From 4ef18a45d6555b8ef0d3365997623925524ff062 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 7 Apr 2026 11:27:45 +0200 Subject: [PATCH] fix(web): flush async shiki highlighter in snapshot tests The useShikiHighlighter() hook loads asynchronously via useEffect, so snapshots were sometimes taken before syntax highlighting completed. Await getHighlighter() inside act() after Story.run() to ensure deterministic snapshots with highlighted content. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../__snapshots__/file-viewer.test.tsx.snap | 786 +++++++++++++----- .../src/components/code/file-viewer.test.tsx | 8 + .../src/components/content/markdown.test.tsx | 8 + 3 files changed, 611 insertions(+), 191 deletions(-) diff --git a/webui2/src/components/code/__snapshots__/file-viewer.test.tsx.snap b/webui2/src/components/code/__snapshots__/file-viewer.test.tsx.snap index 2ef830d0438dda2f7df1c8804bbd9b18fc06db79..54b61d3b1f5a263d014ee6bd852b8d10787723da 100644 --- a/webui2/src/components/code/__snapshots__/file-viewer.test.tsx.snap +++ b/webui2/src/components/code/__snapshots__/file-viewer.test.tsx.snap @@ -216,203 +216,607 @@ exports[`FileViewer/TypeScriptFile matches snapshot 1`] = ` class="border-border overflow-hidden rounded-md border" >
-
+ + 18 + lines ยท + 312 B + +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+          
+            
+              
+                1
+              
+              
+                import
+              
+              
+                 { useState } 
+              
+              
+                from
+              
+              
+                 "react"
+              
+              
+                ;
+              
+              
+
+            
+            
+              
+                2
+              
+              
+
+            
+            
+              
+                3
+              
+              
+                interface
+              
+              
+                 CounterProps
+              
+              
+                 {
+              
+              
+
+            
+            
+              
+                4
+              
+              
+                  initial
+              
+              
+                ?:
+              
+              
+                 number
+              
+              
+                ;
+              
+              
+
+            
+            
+              
+                5
+              
+              
+                }
+              
+              
+
+            
+            
+              
+                6
+              
+              
+
+            
+            
+              
+                7
+              
+              
+                export
+              
+              
+                 function
+              
+              
+                 Counter
+              
+              
+                ({ 
+              
+              
+                initial
+              
+              
+                 =
+              
+              
+                 0
+              
+              
+                 }
+              
+              
+                :
+              
+              
+                 CounterProps
+              
+              
+                ) {
+              
+              
+
+            
+            
+              
+                8
+              
+              
+                  const
+              
+              
+                 [
+              
+              
+                count
+              
+              
+                , 
+              
+              
+                setCount
+              
+              
+                ] 
+              
+              
+                =
+              
+              
+                 useState
+              
+              
+                (initial);
+              
+              
+
+            
+            
+              
+                9
+              
+              
+
+            
+            
+              
+                10
+              
+              
+                  return
+              
+              
+                 (
+              
+              
+
+            
+            
+              
+                11
+              
+              
+                    <
+              
+              
+                div
+              
+              
+                >
+              
+              
+
+            
+            
+              
+                12
+              
+              
+                      <
+              
+              
+                p
+              
+              
+                >Count: {count}</
+              
+              
+                p
+              
+              
+                >
+              
+              
+
+            
+            
+              
+                13
+              
+              
+                      <
+              
+              
+                button
+              
+              
+                 onClick
+              
+              
+                =
+              
+              
+                {() 
+              
+              
+                =>
+              
+              
+                 setCount
+              
+              
+                (
+              
+              
+                c
+              
+              
+                 =>
+              
+              
+                 c 
+              
+              
+                +
+              
+              
+                 1
+              
+              
+                )}>
+              
+              
+
+            
+            
+              
+                14
+              
+              
+                        Increment
+              
+              
+
+            
+            
+              
+                15
+              
+              
+                      </
+              
+              
+                button
+              
+              
+                >
+              
+              
+
+            
+            
+              
+                16
+              
+              
+                    </
+              
+              
+                div
+              
+              
+                >
+              
+              
+
+            
+            
+              
+                17
+              
+              
+                  );
+              
+              
+
+            
+            
+              
+                18
+              
+              
+                }
+              
+              
+
+            
+          
+        
diff --git a/webui2/src/components/code/file-viewer.test.tsx b/webui2/src/components/code/file-viewer.test.tsx index 5f828a50aadf515ee4621debba9a3fbb1203a383..eb864859109bb57137d212d07af1d414d1f6d837 100644 --- a/webui2/src/components/code/file-viewer.test.tsx +++ b/webui2/src/components/code/file-viewer.test.tsx @@ -1,6 +1,9 @@ import { composeStories } from "@storybook/react-vite"; +import { act } from "react"; import { expect, test } from "vitest"; +import { getHighlighter } from "@/lib/shiki"; + import * as stories from "./file-viewer.stories"; const composed = composeStories(stories); @@ -8,6 +11,11 @@ const composed = composeStories(stories); for (const [name, Story] of Object.entries(composed)) { test(`FileViewer/${name} matches snapshot`, async () => { await Story.run(); + // Flush the async shiki highlighter so syntax-highlighted + // content is included deterministically in the snapshot. + await act(async () => { + await getHighlighter(); + }); expect(document.body.firstChild).toMatchSnapshot(); }); } diff --git a/webui2/src/components/content/markdown.test.tsx b/webui2/src/components/content/markdown.test.tsx index e50cbac3fa8a2f822987d0444dd95935446f0ba9..012ed4faade986d45a155e52da106e4ff831632d 100644 --- a/webui2/src/components/content/markdown.test.tsx +++ b/webui2/src/components/content/markdown.test.tsx @@ -1,6 +1,9 @@ import { composeStories } from "@storybook/react-vite"; +import { act } from "react"; import { expect, test } from "vitest"; +import { getHighlighter } from "@/lib/shiki"; + import * as stories from "./markdown.stories"; const composed = composeStories(stories); @@ -8,6 +11,11 @@ const composed = composeStories(stories); for (const [name, Story] of Object.entries(composed)) { test(`Markdown/${name} matches snapshot`, async () => { await Story.run(); + // Flush the async shiki highlighter so syntax-highlighted + // code blocks are included deterministically in the snapshot. + await act(async () => { + await getHighlighter(); + }); expect(document.body.firstChild).toMatchSnapshot(); }); }