/** * Render-cost eval harness — the objective guard against the markdown * re-render/re-parse "storm". * * It mounts a list of memoized markdown rows and asserts how many real * normalize passes (cache misses) and per-row re-renders happen under each * update pattern. Wave 1 owns the parse-count assertions; Wave 2/3 extend the * row-render assertions once callbacks + store identity are stabilized. */ import { createContext, memo, useState } from "react"; import { act, fireEvent, render } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("react-markdown", () => ({ default: ({ children }: { children: string }) =>
{children}
, })); vi.mock("@/components/shared/markdown-components", () => ({ MarkdownFileLinkContext: createContext({}), MarkdownTaskContext: createContext(null), markdownComponents: {}, rehypePlugins: [], remarkPlugins: [], })); import { MemoizedMarkdown } from "@/components/shared/memoized-markdown"; import { __markdownParseCount, __resetMarkdownCounters } from "@/lib/markdown/normalize-cache"; import { createMessageTextAnchor, getMessageCommentDecorations, } from "@/lib/chat/agent-message-comments"; type Row = { id: string; content: string }; const rowRenderCounts = new Map(); const MarkdownRow = memo(function MarkdownRow({ id, content }: Row) { rowRenderCounts.set(id, (rowRenderCounts.get(id) ?? 0) + 1); return ; }); function MessageList({ rows, panel }: { rows: Row[]; panel: boolean }) { return (
{rows.map((r) => ( ))}
); } function makeRows(n: number): Row[] { return Array.from({ length: n }, (_, i) => ({ id: `m${i}`, content: `# Message ${i}` })); } /** Deep clone into fresh object refs but identical content (simulates a refetch). */ function refetch(rows: Row[]): Row[] { return rows.map((r) => ({ id: r.id, content: r.content })); } function totalRowRenders(): number { let total = 0; for (const count of rowRenderCounts.values()) total += count; return total; } describe("chat render-cost evals", () => { beforeEach(() => { __resetMarkdownCounters(); rowRenderCounts.clear(); }); it("scenario 1: no-op refetch causes 0 parses and 0 row re-renders", () => { const rows = makeRows(20); const { rerender } = render(); const parsesAfterMount = __markdownParseCount(); rowRenderCounts.clear(); rerender(); expect(__markdownParseCount() - parsesAfterMount).toBe(0); expect(totalRowRenders()).toBe(0); }); it("scenario 2: one changed message causes exactly 1 new parse", () => { const rows = makeRows(20); const { rerender } = render(); const parsesAfterMount = __markdownParseCount(); rowRenderCounts.clear(); const next = refetch(rows); next[5] = { id: next[5].id, content: "# Message 5 (edited)" }; rerender(); expect(__markdownParseCount() - parsesAfterMount).toBe(1); expect(rowRenderCounts.get("m5")).toBe(1); }); it("scenario 3: unrelated parent re-render causes 0 parses", () => { const rows = makeRows(20); function Harness() { const [panel, setPanel] = useState(false); return (
); } const { getByText } = render(); const parsesAfterMount = __markdownParseCount(); rowRenderCounts.clear(); act(() => { fireEvent.click(getByText("toggle")); }); expect(__markdownParseCount() - parsesAfterMount).toBe(0); expect(totalRowRenders()).toBe(0); }); it("scenario 4: identical content across two rows parses once (cache hit)", () => { const rows: Row[] = [ { id: "a", content: "# Same content" }, { id: "b", content: "# Same content" }, ]; render(); expect(__markdownParseCount()).toBe(1); }); it("scenario 5: restoring many inline highlights does not reparse markdown rows", () => { const content = Array.from({ length: 20 }, (_, index) => `reply-${index}`).join(" "); const root = document.createElement("div"); root.textContent = content; const comments = Array.from({ length: 20 }, (_, index) => { const selectedText = `reply-${index}`; const start = content.indexOf(selectedText); return { id: `comment-${index}`, sessionId: "session-1", source: "agent-message" as const, messageId: "reply-1", selectedText, text: "feedback", createdAt: "2026-07-20T00:00:00Z", status: "pending" as const, anchor: createMessageTextAnchor("reply-1", content, start, start + selectedText.length), }; }); const parsesBefore = __markdownParseCount(); expect(getMessageCommentDecorations(root, comments)).toHaveLength(20); expect(root.querySelectorAll("mark[data-agent-message-comment-id]")).toHaveLength(0); expect(__markdownParseCount() - parsesBefore).toBe(0); }); });