Files
diegosouzapw--omniroute/tests/unit/ui/search-tools-scrape-result.test.tsx
2026-07-13 13:39:12 +08:00

209 lines
8.1 KiB
TypeScript

// @vitest-environment jsdom
import React from "react";
import { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
// ── Mocks ─────────────────────────────────────────────────────────────────────
vi.mock("next-intl", () => ({
useTranslations: () => (key: string) => key,
}));
// Lazy import of MarkdownMessage via ScrapeResult's dynamic import — mock it
vi.mock(
"../../../src/app/(dashboard)/dashboard/playground/components/MarkdownMessage",
() => ({
default: ({ content }: { content: string }) =>
React.createElement("div", { "data-testid": "markdown-render" }, content),
}),
);
// ── Fixtures ──────────────────────────────────────────────────────────────────
const CONTENT_256KB_EXACT = "x".repeat(256 * 1024);
const CONTENT_UNDER_CAP = "# Hello\nThis is markdown content";
const CONTENT_OVER_CAP = "y".repeat(256 * 1024 + 100);
function makeScrapeResult(content: string) {
return {
provider: "firecrawl",
url: "https://example.com",
content,
links: ["https://example.com/link1"],
metadata: { title: "Test Page", description: "A test page" },
screenshot_url: null,
};
}
// ── Import component after mocks ──────────────────────────────────────────────
const { default: ScrapeResult } = await import(
"../../../src/app/(dashboard)/dashboard/search-tools/components/ScrapeResult"
);
// ── Helpers ───────────────────────────────────────────────────────────────────
const containers: Array<{ root: ReturnType<typeof createRoot>; el: HTMLDivElement }> = [];
function renderScrapeResult(content: string, latencyMs?: number): HTMLDivElement {
const el = document.createElement("div");
document.body.appendChild(el);
const root = createRoot(el);
act(() => {
root.render(
React.createElement(ScrapeResult, { result: makeScrapeResult(content), latencyMs }),
);
});
containers.push({ root, el });
return el;
}
// ── Tests ─────────────────────────────────────────────────────────────────────
describe("ScrapeResult", () => {
beforeEach(() => {
(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
});
afterEach(() => {
for (const { root, el } of containers.splice(0)) {
act(() => root.unmount());
el.remove();
}
document.body.innerHTML = "";
});
it("renders scrape-result data-testid", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP);
expect(el.querySelector("[data-testid='scrape-result']")).toBeTruthy();
});
it("shows metadata (title and url)", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP);
expect(el.textContent).toContain("Test Page");
expect(el.textContent).toContain("https://example.com");
});
it("shows latency when provided", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP, 142);
expect(el.textContent).toContain("142");
});
it("shows provider name", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP);
expect(el.textContent).toContain("firecrawl");
});
it("shows links count", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP);
// 1 link in fixture
expect(el.textContent).toContain("1");
});
it("defaults to markdown preview mode", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP);
const markdownToggle = el.querySelector("[data-testid='toggle-markdown']");
expect(markdownToggle?.classList.contains("text-primary")).toBe(true);
// Markdown render area should exist
const markdownPreview = el.querySelector("[data-testid='markdown-preview']");
expect(markdownPreview).toBeTruthy();
});
it("switches to raw mode when Raw toggle is clicked", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP);
const rawToggle = el.querySelector("[data-testid='toggle-raw']") as HTMLButtonElement;
act(() => {
rawToggle.click();
});
const rawContent = el.querySelector("[data-testid='raw-content']");
expect(rawContent).toBeTruthy();
// Markdown preview should be gone
const markdownPreview = el.querySelector("[data-testid='markdown-preview']");
expect(markdownPreview).toBeNull();
});
it("switches back to markdown when Preview toggle is clicked", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP);
const rawToggle = el.querySelector("[data-testid='toggle-raw']") as HTMLButtonElement;
act(() => {
rawToggle.click();
});
const markdownToggle = el.querySelector("[data-testid='toggle-markdown']") as HTMLButtonElement;
act(() => {
markdownToggle.click();
});
const markdownPreview = el.querySelector("[data-testid='markdown-preview']");
expect(markdownPreview).toBeTruthy();
});
it("does NOT show truncation warning for content under 256KB", () => {
const el = renderScrapeResult(CONTENT_UNDER_CAP);
const warning = el.querySelector("[data-testid='truncation-warning']");
expect(warning).toBeNull();
});
it("D21 — shows truncation warning for content exactly at 256KB", () => {
const el = renderScrapeResult(CONTENT_256KB_EXACT);
// Exactly at 256KB → NOT truncated (> cap, not >=)
// 256*1024 bytes — the cap is > so 256*1024 == cap is at boundary
// Per implementation: isTruncated = contentSize > CONTENT_CAP_BYTES
// CONTENT_CAP_BYTES = 256*1024 so exactly at cap is NOT truncated
const warning = el.querySelector("[data-testid='truncation-warning']");
expect(warning).toBeNull();
});
it("D21 — shows truncation warning for content over 256KB", () => {
const el = renderScrapeResult(CONTENT_OVER_CAP);
const warning = el.querySelector("[data-testid='truncation-warning']");
expect(warning).toBeTruthy();
expect(warning?.textContent).toContain("256 KB");
});
it("D21 — 'View raw' button opens raw modal for truncated content", () => {
const el = renderScrapeResult(CONTENT_OVER_CAP);
const viewRawBtn = el.querySelector("[data-testid='view-raw-button']") as HTMLButtonElement;
expect(viewRawBtn).toBeTruthy();
act(() => {
viewRawBtn.click();
});
const rawModal = el.querySelector("[data-testid='raw-modal']");
expect(rawModal).toBeTruthy();
});
it("D21 — raw modal shows full content", () => {
const longContent = "z".repeat(256 * 1024 + 500);
const el = renderScrapeResult(longContent);
const viewRawBtn = el.querySelector("[data-testid='view-raw-button']") as HTMLButtonElement;
act(() => {
viewRawBtn.click();
});
const rawModalContent = el.querySelector("[data-testid='raw-modal-content']") as HTMLTextAreaElement;
expect(rawModalContent).toBeTruthy();
// Full content is in the modal
expect(rawModalContent.value.length).toBe(longContent.length);
});
it("D21 — raw modal can be closed", () => {
const el = renderScrapeResult(CONTENT_OVER_CAP);
const viewRawBtn = el.querySelector("[data-testid='view-raw-button']") as HTMLButtonElement;
act(() => {
viewRawBtn.click();
});
expect(el.querySelector("[data-testid='raw-modal']")).toBeTruthy();
const closeBtn = el
.querySelector("[data-testid='raw-modal']")
?.querySelector("button[aria-label='Close raw content modal']") as HTMLButtonElement;
act(() => {
closeBtn.click();
});
expect(el.querySelector("[data-testid='raw-modal']")).toBeNull();
});
it("size display in meta bar shows KB for large content", () => {
const el = renderScrapeResult("x".repeat(2048));
expect(el.textContent).toMatch(/\d+(\.\d+)? KB/);
});
});