Merge remote-tracking branch 'origin/master' into worktree-session-hover-menu-fix
# Conflicts: # .agents/notes/implemented/architecture/2026-07-30-client-locale-full-rollout.i18n.yaml
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
|
||||
launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
|
||||
} from './scaffold.ts'
|
||||
import { saveFailureShot } from './support.ts'
|
||||
import { ZH_BROWSER_LOCALE, saveFailureShot } from './support.ts'
|
||||
|
||||
/**
|
||||
* connectFreshWorkspace twin over the product default Chinese locale (the
|
||||
@@ -49,7 +49,7 @@ describe('web e2e: Full access confirmation', () => {
|
||||
browser = await chromium.launch(executablePath === undefined ? {} : { executablePath })
|
||||
// Keep the product default Chinese locale: the golden pins the actual
|
||||
// registered dictionary rather than a test-local translation callback.
|
||||
page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
|
||||
page = await browser.newPage({ viewport: { width: 1680, height: 1000 }, locale: ZH_BROWSER_LOCALE })
|
||||
tripwire = watchConsole(page)
|
||||
await page.goto(scaffold.baseUrl, { waitUntil: 'load' })
|
||||
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
|
||||
launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
|
||||
} from './scaffold.ts'
|
||||
import { saveFailureShot } from './support.ts'
|
||||
import { ZH_BROWSER_LOCALE, saveFailureShot } from './support.ts'
|
||||
|
||||
const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/models-settings', import.meta.url))
|
||||
const EMPTY_EXPECTED = join(SNAPSHOT_DIR, 'empty.expected.md')
|
||||
@@ -37,7 +37,8 @@ describe('web e2e: Models settings page configures a dormant provider', () => {
|
||||
beforeAll(async () => {
|
||||
scaffold = await launchWebScaffold({})
|
||||
browser = await chromium.launch()
|
||||
page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
|
||||
// The scenario asserts the shipped Chinese copy, so the browser asks for it.
|
||||
page = await browser.newPage({ viewport: { width: 1680, height: 1000 }, locale: ZH_BROWSER_LOCALE })
|
||||
tripwire = watchConsole(page)
|
||||
await page.goto(scaffold.baseUrl, { waitUntil: 'load' })
|
||||
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
acknowledgeReloadConnectionLoss, assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
|
||||
launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
|
||||
} from './scaffold.ts'
|
||||
import { saveFailureShot } from './support.ts'
|
||||
import { ZH_BROWSER_LOCALE, saveFailureShot } from './support.ts'
|
||||
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
import {
|
||||
WELCOME_NOTICE_ACK_FIELD, WELCOME_NOTICE_COPY, WELCOME_NOTICE_SETTINGS_NAMESPACE,
|
||||
@@ -34,7 +34,8 @@ describe.skipIf(MODE === 'record')('web e2e: first-run DeepSeek credential setup
|
||||
beforeAll(async () => {
|
||||
scaffold = await launchWebScaffold({ deepSeekMissingCredential: true, welcomeNoticePending: true })
|
||||
browser = await chromium.launch()
|
||||
page = await browser.newPage({ viewport: { width: 1440, height: 960 } })
|
||||
// The scenario asserts the shipped Chinese copy, so the browser asks for it.
|
||||
page = await browser.newPage({ viewport: { width: 1440, height: 960 }, locale: ZH_BROWSER_LOCALE })
|
||||
tripwire = watchConsole(page)
|
||||
page.on('console', message => browserConsole.push(message.text()))
|
||||
await page.goto(scaffold.baseUrl, { waitUntil: 'load' })
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
acknowledgeReloadConnectionLoss, assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
|
||||
launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
|
||||
} from './scaffold.ts'
|
||||
import { saveFailureShot } from './support.ts'
|
||||
import { ZH_BROWSER_LOCALE, saveFailureShot } from './support.ts'
|
||||
|
||||
const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/settings-chrome', import.meta.url))
|
||||
const DIALOG_EXPECTED = join(SNAPSHOT_DIR, 'dialog.expected.md')
|
||||
@@ -33,7 +33,9 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
beforeAll(async () => {
|
||||
scaffold = await launchWebScaffold({})
|
||||
browser = await chromium.launch()
|
||||
page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
|
||||
// Chinese browser: the shared page asserts the localized settings surface
|
||||
// the client derives from it (the English default has its own spec below).
|
||||
page = await browser.newPage({ viewport: { width: 1680, height: 1000 }, locale: ZH_BROWSER_LOCALE })
|
||||
tripwire = watchConsole(page)
|
||||
await page.goto(scaffold.baseUrl, { waitUntil: 'load' })
|
||||
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
|
||||
@@ -215,6 +217,30 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
expect(tripwire.pageErrors).toEqual([])
|
||||
}, 90_000)
|
||||
|
||||
it('opens an English browser in English without any stored preference', async () => {
|
||||
// A second page under a different browser language: nothing is persisted
|
||||
// for it, so the settings surface must follow the browser rather than the
|
||||
// product fallback the shared zh page shows.
|
||||
const enPage = await browser.newPage({ viewport: { width: 1680, height: 1000 }, locale: 'en-US' })
|
||||
const enTripwire = watchConsole(enPage)
|
||||
onTestFailed(() => saveFailureShot(enPage, 'web-e2e-settings-browser-language'))
|
||||
try {
|
||||
await enPage.goto(scaffold.baseUrl, { waitUntil: 'load' })
|
||||
await enPage.waitForSelector('[class*="frame"]', { timeout: 30_000 })
|
||||
expect(await enPage.evaluate(() => localStorage.getItem('dsh.locale'))).toBeNull()
|
||||
await enPage.getByRole('button', { name: 'Settings', exact: true }).click()
|
||||
const dialog = enPage.getByRole('dialog', { name: 'Settings' })
|
||||
await dialog.waitFor({ timeout: 10_000 })
|
||||
await dialog.getByRole('button', { name: 'English' }).waitFor({ timeout: 10_000 })
|
||||
// This page has no closing inventory spec to sweep its console, so the
|
||||
// scenario clears both tripwire channels itself.
|
||||
expect(enTripwire.pageErrors).toEqual([])
|
||||
expect(enTripwire.warnings).toEqual([])
|
||||
} finally {
|
||||
await enPage.close()
|
||||
}
|
||||
}, 90_000)
|
||||
|
||||
it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => {
|
||||
expect(tripwire.warnings).toEqual([])
|
||||
await assertFixtureInventory(SNAPSHOT_DIR, ['dialog.expected.md'])
|
||||
|
||||
@@ -9,11 +9,18 @@ export const DIST_INDEX = fileURLToPath(new URL('../dist/index.html', import.met
|
||||
|
||||
export const REPO_ROOT = fileURLToPath(new URL('../../..', import.meta.url))
|
||||
|
||||
/**
|
||||
* Browser language a page must advertise to boot into the product's Chinese
|
||||
* surface: with no stored preference the client derives its initial locale
|
||||
* from the browser, and Playwright's default browser asks for English.
|
||||
*/
|
||||
export const ZH_BROWSER_LOCALE = 'zh-CN'
|
||||
|
||||
/**
|
||||
* Open the standard browser-test page with English selected before client
|
||||
* boot. This keeps role locators and goldens deterministic across localized
|
||||
* component migrations; the settings locale scenario deliberately bypasses
|
||||
* this helper to cover the product's default Chinese state.
|
||||
* component migrations; the scenarios asserting the Chinese surface bypass
|
||||
* this helper and advertise {@link ZH_BROWSER_LOCALE} instead.
|
||||
* @param browser - Playwright browser owning the page.
|
||||
* @param height - Viewport height; width is fixed to the lane baseline.
|
||||
* @returns the initialized page.
|
||||
|
||||
Reference in New Issue
Block a user