mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 13:33:30 +01:00
109 lines
3.1 KiB
TypeScript
109 lines
3.1 KiB
TypeScript
import type { ReactElement } from "react";
|
|
import { act } from "react-dom/test-utils";
|
|
import { createRoot } from "react-dom/client";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import HeaderInput from "../components/HeaderInput";
|
|
|
|
type RenderResult = {
|
|
container: HTMLDivElement;
|
|
};
|
|
|
|
const cleanups: Array<() => void> = [];
|
|
|
|
const render = (ui: ReactElement): RenderResult => {
|
|
const container = document.createElement("div");
|
|
document.body.appendChild(container);
|
|
const root = createRoot(container);
|
|
|
|
act(() => {
|
|
root.render(ui);
|
|
});
|
|
|
|
cleanups.push(() => {
|
|
act(() => {
|
|
root.unmount();
|
|
});
|
|
container.remove();
|
|
});
|
|
|
|
return { container };
|
|
};
|
|
|
|
const getTextarea = (container: HTMLElement): HTMLTextAreaElement => {
|
|
const textarea = container.querySelector("textarea");
|
|
if (!textarea) {
|
|
throw new Error("Expected textarea to be rendered.");
|
|
}
|
|
return textarea as HTMLTextAreaElement;
|
|
};
|
|
|
|
const getAlert = (container: HTMLElement): HTMLElement | null =>
|
|
container.querySelector('[role="alert"]');
|
|
|
|
afterEach(() => {
|
|
while (cleanups.length > 0) {
|
|
const cleanup = cleanups.pop();
|
|
if (cleanup) {
|
|
cleanup();
|
|
}
|
|
}
|
|
});
|
|
|
|
describe("HeaderInput", () => {
|
|
it("renders a textarea for SMTP headers", () => {
|
|
const { container } = render(<HeaderInput value="" onChange={() => undefined} />);
|
|
expect(getTextarea(container)).toBeTruthy();
|
|
});
|
|
|
|
it("accepts pasted header text", () => {
|
|
const handleChange = vi.fn();
|
|
const { container } = render(<HeaderInput value="" onChange={handleChange} />);
|
|
const textarea = getTextarea(container);
|
|
const pasted = "Received: from mail.example.com by mx.example.net";
|
|
|
|
act(() => {
|
|
const pasteEvent = new Event("paste", { bubbles: true });
|
|
Object.defineProperty(pasteEvent, "clipboardData", {
|
|
value: {
|
|
getData: () => pasted,
|
|
},
|
|
});
|
|
textarea.value = pasted;
|
|
textarea.dispatchEvent(pasteEvent);
|
|
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
});
|
|
|
|
expect(handleChange).toHaveBeenCalledWith(pasted);
|
|
});
|
|
|
|
it("shows a validation error when left empty", () => {
|
|
const { container } = render(<HeaderInput value="" onChange={() => undefined} />);
|
|
const textarea = getTextarea(container);
|
|
|
|
act(() => {
|
|
textarea.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
});
|
|
|
|
const alert = getAlert(container);
|
|
expect(alert).not.toBeNull();
|
|
expect(alert?.textContent ?? "").toMatch(/empty|required/i);
|
|
expect(textarea.getAttribute("aria-invalid")).toBe("true");
|
|
});
|
|
|
|
it("shows a validation error when input exceeds 1 MB", () => {
|
|
const oversized = "a".repeat(1024 * 1024 + 1);
|
|
const { container } = render(<HeaderInput value={oversized} onChange={() => undefined} />);
|
|
const textarea = getTextarea(container);
|
|
|
|
act(() => {
|
|
textarea.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
});
|
|
|
|
const alert = getAlert(container);
|
|
expect(alert).not.toBeNull();
|
|
expect(alert?.textContent ?? "").toMatch(/1\s?mb/i);
|
|
expect(textarea.getAttribute("aria-invalid")).toBe("true");
|
|
});
|
|
});
|