From 9cb7984f0b868117adec150e6741911d8c0fbb03 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 15 Oct 2026 20:52:11 +0200 Subject: [PATCH] test(markdown): assert linear HTML boundary work without timing --- docs/agent-stream-performance.md | 2 +- .../src/components/markdown/renderer.test.ts | 38 +++++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/docs/agent-stream-performance.md b/docs/agent-stream-performance.md index 4f6dc6de2e..f0d5e2cb12 100644 --- a/docs/agent-stream-performance.md +++ b/docs/agent-stream-performance.md @@ -39,7 +39,7 @@ So arrival sets a _target_ and the reveal rate is derived from the backlog inste memo boundary each coalesced tick re-rendered every mounted row (about 50 on a phone, 100–250 ms of JS per tick). `layoutStream` keeps a layout item's identity when nothing about it changed, `useRevisedHistoryRows` hands a fresh item identity to rows whose tool-call group, expanded state, - or breakpoint changed, and `HistoryStreamRow` memoizes on both. Every viewport runs its history + or breakpoint changed, and `StreamRow` memoizes on both. Every viewport runs its history through that hook; the web viewport once skipped it and history hosts of a live tool group went stale. A new field on `StreamLayoutItem` must be added to `areLayoutItemsEquivalent`, or sharing silently stops. diff --git a/packages/app/src/components/markdown/renderer.test.ts b/packages/app/src/components/markdown/renderer.test.ts index a3d4224b05..4f4dcccf2a 100644 --- a/packages/app/src/components/markdown/renderer.test.ts +++ b/packages/app/src/components/markdown/renderer.test.ts @@ -403,12 +403,34 @@ it("reserves pending image geometry until decode succeeds and collapses only con ); }); -it("normalizes a large HTML table without repeated prefix scans", () => { +it("normalizes a large HTML table with linear bounded-source slicing", () => { const cell = "cell".repeat(8); - const source = `${``.repeat(8000)}
${cell}${cell}
`; - const started = performance.now(); - const rendered = normalizeMarkdownHtml(source); - expect(performance.now() - started).toBeLessThan(3000); - expect(rendered.split("\n\n")).toHaveLength(8000); - expect(rendered.startsWith(`${cell} ${cell}`)).toBe(true); -}, 20000); + const measure = (rows: number) => { + const source = `${``.repeat(rows)}
${cell}${cell}
`; + const originalSlice = String.prototype.slice; + let inspectedCharacters = 0; + // Observe real slices without changing their results. Repeated source.slice(0, offset) + // prefix scans grow quadratically; inspecting only each new gap grows linearly. + // eslint-disable-next-line no-extend-native -- Test-only observation, restored in finally. + String.prototype.slice = function (start, end) { + const result = originalSlice.call(this, start, end); + if (String(this) === source && end !== undefined) { + inspectedCharacters += result.length; + } + return result; + }; + let rendered: string; + try { + rendered = normalizeMarkdownHtml(source); + } finally { + // eslint-disable-next-line no-extend-native -- Restore the observed built-in unchanged. + String.prototype.slice = originalSlice; + } + expect(rendered.split("\n\n")).toHaveLength(rows); + expect(rendered.startsWith(`${cell} ${cell}`)).toBe(true); + return inspectedCharacters; + }; + const baseline = measure(200); + expect(baseline).toBeGreaterThan(0); + expect(measure(800)).toBeLessThanOrEqual(baseline * 4); +});