From 76feeece55d72d4a903ff9ee8b42c06a08a42a29 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Mon, 10 Aug 2026 16:37:01 +0800 Subject: [PATCH] test(web): pin the assembled snapshot lane's locale through the browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The assembled Web snapshot lane selected its locale with a `dsh.locale` localStorage key. That key stopped selecting anything once the locale preference moved to the Host settings document, so the image-display scenario's Chinese expectations met the English default and failed. Pin the navigator languages the boot env already documents, and state the image-display expectations in the lane's English copy — the fixture session title stays Chinese because it is fixture data, not product copy. --- apps/web/tests/assembled-boot.ts | 12 +++++++++++- apps/web/tests/image-display.snapshot.ts | 18 ++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/apps/web/tests/assembled-boot.ts b/apps/web/tests/assembled-boot.ts index 631196c652..ac0ec80fcb 100644 --- a/apps/web/tests/assembled-boot.ts +++ b/apps/web/tests/assembled-boot.ts @@ -70,7 +70,12 @@ let unmount: (() => void) | undefined export function installAssembledBootEnv(): void { beforeEach(() => { localStorage.clear() - localStorage.setItem('dsh.locale', 'en') + // The locale service derives its provisional locale from the browser and + // takes an explicit choice only from Host settings, which this lane's + // fixture transport does not serve; pinning the navigator is what selects + // English here. + Object.defineProperty(navigator, 'languages', { value: ['en-US'], configurable: true }) + Object.defineProperty(navigator, 'language', { value: 'en-US', configurable: true }) document.title = 'DeepSeek Harness' vi.stubGlobal('ResizeObserver', ResizeObserverStub) vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => @@ -88,6 +93,11 @@ export function installAssembledBootEnv(): void { document.head.querySelectorAll('style[data-plugin]').forEach((style) => { style.remove() }) document.title = '' history.replaceState(null, '', '/') + // Deleting the own properties uncovers jsdom's own accessors again + // (Navigator declares both readonly, hence the erased receiver). + const ownNavigator = navigator as unknown as Record + delete ownNavigator.languages + delete ownNavigator.language vi.unstubAllGlobals() }) } diff --git a/apps/web/tests/image-display.snapshot.ts b/apps/web/tests/image-display.snapshot.ts index 6546a79f1b..df286ec1c3 100644 --- a/apps/web/tests/image-display.snapshot.ts +++ b/apps/web/tests/image-display.snapshot.ts @@ -14,7 +14,7 @@ installAssembledBootEnv() /** Open the fixture history session (the alpha log carrying the turn-72 image pair) and wait for its gallery. */ async function openFixtureSession(): Promise { - const tree = await screen.findByRole('tree', { name: '会话' }, { timeout: 10_000 }) + const tree = await screen.findByRole('tree', { name: 'Sessions' }, { timeout: 10_000 }) const group = (await within(tree).findAllByText('fixture')) .map(el => el.closest('[role="treeitem"]')) .find(el => el?.getAttribute('aria-expanded') !== null) @@ -33,7 +33,6 @@ async function openFixtureSession(): Promise { } it('renders the history image pair through the authorized attachment route and opens the lightbox', async () => { - localStorage.setItem('dsh.locale', 'zh') mountAssembledApp() await openFixtureSession() @@ -73,24 +72,23 @@ it('renders the history image pair through the authorized attachment route and o fireEvent.doubleClick(frame) const lightbox = await screen.findByRole('dialog') expect(within(lightbox).getByRole('img').getAttribute('src')?.split(':')[0]).toBe('blob') - fireEvent.click(within(lightbox).getByRole('button', { name: /关闭/ })) + fireEvent.click(within(lightbox).getByRole('button', { name: /Close/ })) await waitFor(() => { expect(screen.queryByRole('dialog')).toBeNull() }) }) it('accepts pasted images into the composer rail in order and removes them', async () => { - localStorage.setItem('dsh.locale', 'zh') mountAssembledApp() - const tree = await screen.findByRole('tree', { name: '会话' }, { timeout: 10_000 }) - const start = tree.querySelector('button[aria-label="在“fixture”中新建会话"]') + const tree = await screen.findByRole('tree', { name: 'Sessions' }, { timeout: 10_000 }) + const start = tree.querySelector('button[aria-label="New session in fixture"]') if (start === null) throw new Error('fixture Workspace new-session action missing') fireEvent.click(start) // Image-only send arming is pinned at package level (input-bar.spec.tsx); // this assembled lane pins the intake chain over the built graph. - const textarea = await screen.findByPlaceholderText('描述你想要构建的内容', {}, { timeout: 10_000 }) + const textarea = await screen.findByPlaceholderText('Describe what you want to build', {}, { timeout: 10_000 }) const image = new File([new Uint8Array([137, 80, 78, 71])], 'pasted.png', { type: 'image/png' }) fireEvent.paste(textarea, { clipboardData: { @@ -102,7 +100,7 @@ it('accepts pasted images into the composer rail in order and removes them', asy // The rail is an accessible group holding the draft thumbnail (queried via // DOM: jsdom's a11y-visibility computation hides the composer subtree). const rail = await waitFor(() => { - const el = document.querySelector('[role="group"][aria-label="待发送图片"]') + const el = document.querySelector('[role="group"][aria-label="Pending images"]') if (el === null) throw new Error('attachment rail missing') return el }, { timeout: 5_000 }) @@ -129,10 +127,10 @@ it('accepts pasted images into the composer rail in order and removes them', asy .toEqual(['pasted.png', 'second.png']) }) - const remove = [...rail.querySelectorAll('button[aria-label^="移除图片"]')] + const remove = [...rail.querySelectorAll('button[aria-label^="Remove image"]')] if (remove.length !== 2) throw new Error('remove buttons missing') for (const button of remove) fireEvent.click(button) await waitFor(() => { - expect(document.querySelector('[role="group"][aria-label="待发送图片"]')).toBeNull() + expect(document.querySelector('[role="group"][aria-label="Pending images"]')).toBeNull() }) })