From 065812bc390dbf619e9b5fc2c0a19c826c13db31 Mon Sep 17 00:00:00 2001 From: Hare Date: Wed, 30 Sep 2026 09:09:24 +0900 Subject: [PATCH] fix(web): cover deep document headings --- .../memory_document_fixture_server.ts | 2 +- .../memory_document_shell_test.ts | 15 ++++++++++++++ .../markdown/DocumentMarkdown.browser.spec.ts | 20 +++++++++++++++---- .../markdown/DocumentMarkdown.svelte | 8 ++++++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/tools/web-ux/browser-tests/memory_document_fixture_server.ts b/tools/web-ux/browser-tests/memory_document_fixture_server.ts index 3d9c44183..ba1c24614 100644 --- a/tools/web-ux/browser-tests/memory_document_fixture_server.ts +++ b/tools/web-ux/browser-tests/memory_document_fixture_server.ts @@ -25,7 +25,7 @@ const paragraphs = Array.from( const bodyMd = `# Workspace Memory\n\nThis document is read-only.\n\n| Very wide heading | Another heading | Decision |\n| --- | --- | --- |\n| ${ "wide-value-".repeat(16) - } | stable | keep scrolling local |\n\n\`\`\`rust\nfn example() { println!("${ + } | stable | keep scrolling local |\n\n##### Deep document heading\n\n###### Deepest document heading\n\n\`\`\`rust\nfn example() { println!("${ "wide-code-".repeat(18) }"); }\n\`\`\`\n\n[Safe link](https://example.com) [Unsafe link](javascript:alert(1))\n\n\n\n${paragraphs}\n\n## T-661 UNIQUE END MARKER`; diff --git a/tools/web-ux/browser-tests/memory_document_shell_test.ts b/tools/web-ux/browser-tests/memory_document_shell_test.ts index 75f13de3d..54c8d403f 100644 --- a/tools/web-ux/browser-tests/memory_document_shell_test.ts +++ b/tools/web-ux/browser-tests/memory_document_shell_test.ts @@ -165,6 +165,21 @@ Deno.test("production Memory shell renders safe Markdown and scrolls to its end ); assertEquals(await page.locator("blockquote").count() > 0, true); assertEquals(await page.locator("ul li").count() > 0, true); + for ( + const heading of [ + { selector: "h5", text: "Deep document heading" }, + { selector: "h6", text: "Deepest document heading" }, + ] + ) { + const element = page.locator(heading.selector, { hasText: heading.text }); + assertEquals(await element.count(), 1); + assert( + await element.evaluate((node) => + Number.parseFloat((globalThis as any).getComputedStyle(node).fontSize) >= 12 + ), + `${heading.selector} fell below the design language minimum text size`, + ); + } assertEquals(await page.locator('a[href="https://example.com"]').count(), 1); assertEquals(await page.locator('a[href^="javascript:"]').count(), 0); assertEquals(await page.locator("article img, article script").count(), 0); diff --git a/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.browser.spec.ts b/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.browser.spec.ts index 16b1c5084..0c1fe7808 100644 --- a/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.browser.spec.ts +++ b/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.browser.spec.ts @@ -10,6 +10,10 @@ test("renders document Markdown semantics through the shared safe policy", async const source = [ "# Memory heading", "", + "##### Deep heading", + "", + "###### Deepest heading", + "", "A paragraph with **strong context** and `inline-code`.", "", "- first item", @@ -36,10 +40,15 @@ test("renders document Markdown semantics through the shared safe policy", async expect(container.querySelector("table")).not.toBeNull(); }); + expect(container.querySelector("h5")?.textContent).toBe("Deep heading"); + expect(container.querySelector("h6")?.textContent).toBe("Deepest heading"); expect(container.querySelector("strong")?.textContent).toBe("strong context"); expect(container.querySelectorAll("li")).toHaveLength(2); - expect(container.querySelector("blockquote")?.textContent).toContain("durable quote"); - expect(container.querySelector('a[href="https://example.com"]')).not.toBeNull(); + expect(container.querySelector("blockquote")?.textContent).toContain( + "durable quote", + ); + expect(container.querySelector('a[href="https://example.com"]')).not + .toBeNull(); expect(container.querySelectorAll("a")).toHaveLength(1); expect(container.querySelector("img")).toBeNull(); expect(container.querySelector("script")).toBeNull(); @@ -52,13 +61,16 @@ test("renders document Markdown semantics through the shared safe policy", async ); expect(tableRegion?.tabIndex).toBe(0); expect(codeRegion?.tabIndex).toBe(0); - expect(codeRegion?.querySelector("pre > code")?.textContent).toContain("println!"); + expect(codeRegion?.querySelector("pre > code")?.textContent).toContain( + "println!", + ); }); test("wraps long prose and identifiers without turning them into document-level controls", async () => { const longIdentifier = "memory-identifier-".repeat(40); const { container } = render(DocumentMarkdown, { - text: `Long value: ${longIdentifier}\n\n
unsafe HTML
`, + text: + `Long value: ${longIdentifier}\n\n
unsafe HTML
`, }); await waitFor(() => expect(container.textContent).toContain(longIdentifier)); diff --git a/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.svelte b/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.svelte index 6eeef04c2..77add1dce 100644 --- a/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.svelte +++ b/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.svelte @@ -41,7 +41,9 @@ :global(.document-markdown h2), :global(.document-markdown h3), - :global(.document-markdown h4) { + :global(.document-markdown h4), + :global(.document-markdown h5), + :global(.document-markdown h6) { margin: var(--space-6) 0 var(--space-2); color: var(--text-strong); font-size: var(--font-size-body); @@ -53,7 +55,9 @@ } :global(.document-markdown h3), - :global(.document-markdown h4) { + :global(.document-markdown h4), + :global(.document-markdown h5), + :global(.document-markdown h6) { font-weight: 600; }