WFDASH-WEBAPP/src/app/scan/page.test.tsx

120 lines
5.1 KiB
TypeScript

import { StrictMode } from "react";
import { renderToString } from "react-dom/server";
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import ScanPage from "./page";
import * as qrDetect from "@/lib/scan/qr-detect";
afterEach(() => {
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
describe("ScanPage", () => {
it("keeps the latest scanned note when an earlier lookup finishes late", async () => {
vi.stubGlobal("isSecureContext", true);
vi.stubGlobal("navigator", {
mediaDevices: { getUserMedia: vi.fn().mockResolvedValue({ getTracks: () => [] }) },
});
vi.spyOn(HTMLMediaElement.prototype, "play").mockResolvedValue();
let onScan: ((raw: string) => void) | undefined;
vi.spyOn(qrDetect, "scanVideoFrames").mockImplementation((_video, _canvas, onFound) => {
onScan = onFound;
return new Promise(() => {});
});
const pending = new Map<string, (response: Response) => void>();
vi.stubGlobal("fetch", (path: string) => new Promise<Response>((resolve) => {
pending.set(new URL(path, "http://localhost").searchParams.get("number")!, resolve);
}));
const resolved = (number: string) => new Response(JSON.stringify({
id: number,
delivery_note_number: number,
status: "draft",
customer_name: "Customer",
company_name: "Company",
customer_po: "",
delivery_date: "",
signed_copy: "",
item_count: 1,
}), { status: 200 });
render(<ScanPage />);
fireEvent.click(screen.getByRole("button", { name: "Start camera" }));
await waitFor(() => expect(onScan).toBeDefined());
act(() => onScan!("DN26100401"));
act(() => onScan!("DN26100402"));
expect(screen.getByRole("status")).toHaveTextContent("Delivery note recognised");
await act(async () => pending.get("DN26100402")!(resolved("DN26100402")));
expect(screen.getByTestId("scan-note")).toHaveTextContent("DN26100402");
expect(screen.queryByRole("status")).not.toBeInTheDocument();
await act(async () => pending.get("DN26100401")!(resolved("DN26100401")));
expect(screen.getByTestId("scan-note")).toHaveTextContent("DN26100402");
expect(screen.getByTestId("scan-note")).not.toHaveTextContent("DN26100401");
});
it("requests a sharp rear-camera feed instead of relying on low-resolution defaults", async () => {
vi.stubGlobal("isSecureContext", true);
const getUserMedia = vi.fn().mockRejectedValue(new Error("Camera unavailable in test"));
vi.stubGlobal("navigator", { mediaDevices: { getUserMedia } });
render(<ScanPage />);
fireEvent.click(screen.getByRole("button", { name: "Start camera" }));
await waitFor(() => expect(screen.getByTestId("scan-toast")).toHaveTextContent("Camera unavailable in test"));
const constraints = getUserMedia.mock.calls[0][0];
expect(constraints.video.facingMode).toEqual({ ideal: "environment" });
expect(constraints.video.width.ideal).toBeGreaterThanOrEqual(1920);
expect(constraints.video.height.ideal).toBeGreaterThanOrEqual(1080);
expect(constraints.audio).toBe(false);
});
it("renders camera controls without a render loop and remains usable after typing", () => {
vi.stubGlobal("isSecureContext", true);
vi.stubGlobal("navigator", {
mediaDevices: { getUserMedia: vi.fn() },
});
render(<StrictMode><ScanPage /></StrictMode>);
expect(screen.getByRole("button", { name: "Start camera" })).toBeEnabled();
expect(screen.getByRole("button", { name: "Go" })).toBeDisabled();
fireEvent.change(screen.getByLabelText("Or type the delivery note number"), {
target: { value: "dn26100401" },
});
expect(screen.getByLabelText("Or type the delivery note number")).toHaveValue("DN26100401");
expect(screen.getByRole("button", { name: "Go" })).toBeEnabled();
expect(screen.getByRole("button", { name: "Start camera" })).toBeEnabled();
});
it("keeps manual entry available on an insecure address without a camera", () => {
vi.stubGlobal("isSecureContext", false);
vi.stubGlobal("navigator", {});
render(<ScanPage />);
expect(screen.getByTestId("scan-insecure-warning")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Start camera" })).toBeDisabled();
fireEvent.change(screen.getByLabelText("Or type the delivery note number"), {
target: { value: "DN26100401" },
});
expect(screen.getByRole("button", { name: "Go" })).toBeEnabled();
});
it("hydrates the server page and enables the browser's camera controls", () => {
vi.stubGlobal("isSecureContext", true);
vi.stubGlobal("navigator", {
mediaDevices: { getUserMedia: vi.fn() },
});
const container = document.createElement("div");
container.innerHTML = renderToString(<ScanPage />);
document.body.appendChild(container);
expect(container.querySelector("button")).toBeDisabled();
render(<ScanPage />, { container, hydrate: true });
expect(screen.getByRole("button", { name: "Start camera" })).toBeEnabled();
expect(screen.getByLabelText("Or type the delivery note number")).toBeInTheDocument();
});
});