// 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";