import { afterEach, describe, expect, it, vi } from "vitest"; import { googleSearch, parseGoogleResults } from "../google"; // Reduced Google WML structure: nested result cards, linked titles, snippet spans. function card( url: string, title = "Node & Rust", snippet = "A nested snippet 🦀.", ) { return `
${title}
${snippet}
`; } const html = `${card("/url?q=https%3A%2F%2Fnodejs.org%2Fapi%2Ffs.html&sa=U")}${card("https://rust-lang.org/")}`; afterEach(() => vi.restoreAllMocks()); describe("Google WML", () => { it("extracts titles, redirects, nested snippets, and entities", () => { expect(parseGoogleResults(html)).toEqual([ { title: "Node & Rust", url: "https://nodejs.org/api/fs.html", snippet: "A nested snippet 🦀.", }, { title: "Node & Rust", url: "https://rust-lang.org/", snippet: "A nested snippet 🦀.", }, ]); }); it("deduplicates and ignores unsafe, malformed, and navigation links", () => { const text = [ "https://example.com/", "https://example.com/", "javascript:alert(1)", "file:///etc/passwd", "https://user:pass@example.com/", "https://www.google.com/preferences", "/url?q=not-a-url", ] .map((url) => card(url)) .join(""); expect(parseGoogleResults(text)).toHaveLength(1); }); it("does not cap snippets", () => { const snippet = "x".repeat(30_000); expect( parseGoogleResults(card("https://example.com", "Title", snippet))[0] .snippet, ).toBe(snippet); }); it("handles malformed numeric entities without throwing", () => { expect( parseGoogleResults( card("https://example.com", "� �"), )[0].title, ).toBe("� �"); }); it.each([ "Our systems have detected unusual traffic", "
", "Before you continue to Google", "Please enable JavaScript", "A new incompatible layout", ])("fails closed for challenges or changed markup: %s", (body) => { expect(() => parseGoogleResults(body)).toThrow(/Google WML/); }); it("recognizes genuine no-results pages", () => { expect( parseGoogleResults("Your search did not match any documents."), ).toEqual([]); }); it("uses Nokia WML through the real HTTP path, with manual redirects and count", async () => { const network = vi .spyOn(globalThis, "fetch") .mockResolvedValue(new Response(html)); const result = await googleSearch("Node & Rust", undefined, 1); expect(result).toHaveLength(1); const [url, init] = network.mock.calls[0]; expect(new URL(String(url)).pathname).toBe("/wml/search"); expect(new URL(String(url)).searchParams.get("q")).toBe("Node & Rust"); expect(init?.headers).toMatchObject({ "User-Agent": expect.stringContaining("Nokia7610"), Cookie: "CONSENT=YES+", }); expect(init?.redirect).toBe("manual"); }); it("aborts without network I/O", async () => { const network = vi.spyOn(globalThis, "fetch"); await expect( googleSearch("docs", AbortSignal.abort(new Error("cancelled"))), ).rejects.toThrow("cancelled"); expect(network).not.toHaveBeenCalled(); }); it("enforces deadline while waiting for the throttle", async () => { const network = vi.spyOn(globalThis, "fetch"); // The previous request reserved a slot; force the clock behind that slot. vi.spyOn(Date, "now").mockReturnValue(Date.now() - 5000); await expect( googleSearch("docs", new AbortController().signal, 5, 10), ).rejects.toThrow(/abort/i); expect(network).not.toHaveBeenCalled(); }); });