// should be excluded from bundles but not from lsp // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// import { fireEvent, waitFor } from "@testing-library/dom"; import { userEvent } from "@testing-library/user-event"; import * as Chai from "chai"; import chaiAsPromised from "chai-as-promised"; import chaiDom from "chai-dom"; import * as Dom from "@hesxenon/prelude/Dom.js"; import { schedulers } from "@hesxenon/prelude/Scheduler.js"; import { toHtml } from "tsx-to-html"; import { pipe } from "@hesxenon/prelude/Function.js"; Chai.use(chaiAsPromised); Chai.use(chaiDom); export const expect = Chai.expect; export const render = (node: JSX.Element) => { document.body.innerHTML = ""; document.body.innerHTML = toHtml(node); return document.body; }; export const renderAsync = async ( table: JSX.Element, scheduler = schedulers.time(10), ) => { const page = render(table); await new Promise((resolve) => scheduler(() => resolve())); return page; }; export const user = userEvent.setup({ // delay: "DEBUG" in window.env ? 100 : 0, }); export const getFormValues = (within: HTMLElement) => pipe( within instanceof HTMLFormElement ? within : Dom.select(within, "form"), (form) => Array.from(new FormData(form).entries()).reduce( (acc, [key, value]) => { if (typeof value !== "string") { return acc; } if (key in acc) { acc[key] = [acc[key]!, value].flat(); } else { acc[key] = value; } return acc; }, {} as Record, ), ); export const waitForElement = ( ...params: Parameters> ) => { return waitFor(() => Dom.select(...params), { timeout: 3000 }); }; export const waitForPredicate = (predicate: () => boolean) => waitFor( () => { if (!predicate()) { throw new Error("predicate didn't match"); } }, { timeout: 3000 }, ); export const sleep = (time: number) => new Promise((resolve) => setTimeout(resolve, time)); export const getElementClientCenter = (element: Element) => { const { left, top, width, height } = element.getBoundingClientRect(); return { x: left + width / 2, y: top + height / 2, }; }; export const drag = async ( element: Element, { delta, steps = 20, duration = 500, }: { delta: { x: number; y: number }; steps?: number; duration?: number }, ) => { const from = getElementClientCenter(element); const to = { x: from.x + delta.x, y: from.y + delta.y, }; const step = { x: (to.x - from.x) / steps, y: (to.y - from.y) / steps, }; const current = { clientX: from.x, clientY: from.y, }; fireEvent.mouseEnter(element, current); fireEvent.mouseOver(element, current); fireEvent.mouseMove(element, current); fireEvent.mouseDown(element, current); for (let i = 0; i < steps; i++) { current.clientX += step.x; current.clientY += step.y; await sleep(duration / steps); fireEvent.mouseMove(element, current); } fireEvent.mouseUp(element, current); }; export const trapEvents = ( on: EventTarget, eventNames: EventName[], ) => { const events = [] as Event[]; eventNames.forEach((event) => on.addEventListener(event, (e) => events.push(e)), ); return { get: () => events, }; }; declare global { interface Window { user: typeof user; fireEvent: typeof fireEvent; drag: typeof drag; } } window.user = user; window.fireEvent = fireEvent; window.drag = drag; export * from "@testing-library/dom"; export * from "nodepup"; export { select, selectAll } from "@hesxenon/prelude/Dom.js"; export * as R from "ramda";