import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { render, screen, fireEvent, waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import SearchPage from "./page"; const ORIGINAL_ENV = process.env; beforeEach(() => { process.env = { ...ORIGINAL_ENV, NEXT_PUBLIC_MAIL_TENANT_SLUG: "acme" }; }); afterEach(() => { process.env = ORIGINAL_ENV; vi.restoreAllMocks(); }); // SRC-06: SearchPage ruft jetzt sowohl /api/search als auch /api/facets // auf (Promise.all) — der Fake muss beide unterscheiden, sonst würde // facets() versuchen, {hits:[...]} als FacetsResponse zu lesen. function mockSearchResponse(hits: unknown[], facetValues: Record = {}) { vi.stubGlobal( "fetch", vi.fn().mockImplementation((url: string) => { if (url.includes("/api/facets")) { return Promise.resolve(new Response(JSON.stringify({ values: facetValues }), { status: 200 })); } return Promise.resolve(new Response(JSON.stringify({ hits }), { status: 200 })); }) ); } // Akzeptanzkriterium 1: Eingabe liefert Trefferliste mit hervorgehobenen // Suchbegriffen im Snippet. describe("SearchPage — Trefferliste mit Hervorhebung", () => { it("rendert Treffer mit um den hervorgehobenen Suchbegriff", async () => { mockSearchResponse([ { messageId: "msg-1", subjectSnippet: "Quartalsbericht ⦃⦃Umsatz⦄⦄", bodySnippet: "hoher ⦃⦃Umsatz⦄⦄ im dritten Quartal", score: 100, }, ]); render(); fireEvent.change(screen.getByLabelText("Suchbegriff"), { target: { value: "Umsatz" } }); fireEvent.submit(screen.getByLabelText("Suchbegriff").closest("form")!); await waitFor(() => { const marks = document.querySelectorAll("mark"); expect(marks.length).toBeGreaterThan(0); }); const marks = Array.from(document.querySelectorAll("mark")).map((m) => m.textContent); expect(marks).toContain("Umsatz"); }); // Akzeptanzkriterium 3: leere Ergebnisse zeigen verständlichen Hinweis // statt leerer Fläche. it("zeigt bei leerem Ergebnis einen verständlichen Hinweis", async () => { mockSearchResponse([]); render(); fireEvent.change(screen.getByLabelText("Suchbegriff"), { target: { value: "nichts-vorhanden" } }); fireEvent.submit(screen.getByLabelText("Suchbegriff").closest("form")!); await waitFor(() => { expect(screen.getByRole("status")).toHaveTextContent("Keine Treffer"); }); }); // Pflichtprüfung 3: Sonderzeichen in der Suchanfrage bringen die Anzeige // nicht zum Absturz. it("wirft bei Sonderzeichen in der Suchanfrage keinen Fehler", async () => { mockSearchResponse([ { messageId: "msg-2", subjectSnippet: `⦃⦃⦄⦄ & "Zitat" 日本語`, bodySnippet: "", score: 1, }, ]); render(); fireEvent.change(screen.getByLabelText("Suchbegriff"), { target: { value: '"