test(web): await command and settings readiness before assertions
This commit is contained in:
1 parent
e807447288
commit
58144ccf78
2 files changed
+40
-18
No files matched your search
@@ -15,7 +15,7 @@ import { fileURLToPath } from 'node:url'
|
||||
import { join } from 'node:path'
|
||||
import type { Browser, Page, WebSocketRoute } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed, onTestFinished } from 'vitest'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import {
|
||||
acknowledgeReloadConnectionLoss, assertFixtureInventory, captureExpandedTurnProcessAria,
|
||||
@@ -72,10 +72,17 @@ describe('web e2e: lifecycle & chrome (workspace flow / reload / dark mode)', ()
|
||||
|
||||
it.skipIf(MODE === 'record')('opens the shared slash menu from plus with only Command candidates', async () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-command-menu-launcher'))
|
||||
const input = page.locator('[data-composer-input]').first()
|
||||
onTestFinished(async () => {
|
||||
await input.press('Escape')
|
||||
await writeComposerDraft(page, input, '')
|
||||
await page.getByRole('listbox', { name: 'Trigger suggestions' }).waitFor({ state: 'hidden' })
|
||||
})
|
||||
const launcher = page.getByRole('button', { name: 'Commands' })
|
||||
await launcher.click()
|
||||
const menu = page.getByRole('listbox', { name: 'Trigger suggestions' })
|
||||
await menu.waitFor({ timeout: 10_000 })
|
||||
await menu.getByRole('option').first().waitFor({ timeout: 10_000 })
|
||||
await menu.getByRole('status').waitFor({ state: 'hidden', timeout: 10_000 })
|
||||
const snapshot = await captureStableAria(page, '[role="listbox"]', scaffold.workspaceCwd)
|
||||
await compareOrRefreshGolden(COMMAND_MENU_EXPECTED, snapshot, MODE)
|
||||
expect(snapshot).toContain('text: Commands')
|
||||
@@ -84,9 +91,9 @@ describe('web e2e: lifecycle & chrome (workspace flow / reload / dark mode)', ()
|
||||
const launchedBox = await menu.boundingBox()
|
||||
await page.locator('[data-composer-input]').first().press('Escape')
|
||||
await expect.poll(() => menu.count()).toBe(0)
|
||||
const input = page.locator('[data-composer-input]').first()
|
||||
await writeComposerDraft(page, input, '/')
|
||||
await menu.waitFor({ timeout: 10_000 })
|
||||
await menu.getByRole('option').first().waitFor({ timeout: 10_000 })
|
||||
await menu.getByRole('status').waitFor({ state: 'hidden', timeout: 10_000 })
|
||||
const typedBox = await menu.boundingBox()
|
||||
expect(launchedBox).not.toBeNull()
|
||||
expect(typedBox).not.toBeNull()
|
||||
@@ -114,7 +121,8 @@ describe('web e2e: lifecycle & chrome (workspace flow / reload / dark mode)', ()
|
||||
const launcher = zhPage.getByRole('button', { name: '指令' })
|
||||
await launcher.click()
|
||||
const menu = zhPage.getByRole('listbox', { name: '触发候选建议' })
|
||||
await menu.waitFor({ timeout: 10_000 })
|
||||
await menu.getByRole('option').first().waitFor({ timeout: 10_000 })
|
||||
await menu.getByRole('status').waitFor({ state: 'hidden', timeout: 10_000 })
|
||||
const snapshot = await captureStableAria(zhPage, '[role="listbox"]', scaffold.workspaceCwd)
|
||||
await compareOrRefreshGolden(COMMAND_MENU_ZH_EXPECTED, snapshot, MODE)
|
||||
expect(zhTripwire.pageErrors).toEqual([])
|
||||
|
||||
@@ -12,7 +12,7 @@ import { readFile } from 'node:fs/promises'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import type { Browser, Locator, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed, onTestFinished } from 'vitest'
|
||||
import { join } from 'node:path'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import {
|
||||
@@ -378,6 +378,10 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
|
||||
it('steps the content font size, applies it to body, and persists across reload', async () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-settings-font-size'))
|
||||
onTestFinished(async () => {
|
||||
await page.keyboard.press('Escape')
|
||||
await page.getByRole('dialog', { name: '设置', exact: true }).waitFor({ state: 'hidden' })
|
||||
})
|
||||
const readFontSize = async (target: Page = page): Promise<string> => await target.evaluate(
|
||||
() => document.body.style.getPropertyValue('--dsh-content-font-size'),
|
||||
)
|
||||
@@ -392,6 +396,24 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
probe.remove()
|
||||
return size
|
||||
})
|
||||
// The displayed value is optimistic; wait for the write before the next step.
|
||||
const stepFontSize = async (button: Locator, px: number): Promise<void> => {
|
||||
const [response] = await Promise.all([
|
||||
page.waitForResponse((reply) => {
|
||||
if (new URL(reply.url()).pathname !== '/api/settings/mutate' || reply.request().method() !== 'POST') return false
|
||||
const request = reply.request().postDataJSON() as { payload: { args: { ns: string } } }
|
||||
return request.payload.args.ns === 'ui-theme'
|
||||
}),
|
||||
button.click(),
|
||||
])
|
||||
expect(await response.finished()).toBeNull()
|
||||
const envelope = await response.json() as { result: { ok: boolean } }
|
||||
expect(envelope.result.ok).toBe(true)
|
||||
await expect.poll(async () => readFile(join(scaffold.harnessHome, 'settings.yaml'), 'utf8'), { timeout: 5_000 })
|
||||
.toMatch(new RegExp(`ui-theme:\n(?:\\s+\\w+: .*\n)*?\\s+fontSize: ${px}`))
|
||||
await page.getByRole('dialog', { name: '设置' }).getByText(String(px), { exact: true }).waitFor({ timeout: 5_000 })
|
||||
await expect.poll(readFontSize, { timeout: 5_000 }).toBe(`${px}px`)
|
||||
}
|
||||
expect(await readFontSize()).toBe('14px')
|
||||
expect(await readSecondaryFontSize()).toBe('13px')
|
||||
await page.getByRole('button', { name: '设置', exact: true }).click()
|
||||
@@ -400,17 +422,12 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
// The stepper reveals its arrows on hover; the up arrow steps 14 → 15 → 16.
|
||||
await dialog.getByText('14', { exact: true }).hover()
|
||||
const increase = dialog.getByRole('button', { name: '增大字号' })
|
||||
await increase.click()
|
||||
await dialog.getByText('15', { exact: true }).waitFor({ timeout: 5_000 })
|
||||
await stepFontSize(increase, 15)
|
||||
// 15 is the piecewise boundary: the secondary tier holds at 13px (−2)
|
||||
// where the ≤14 branch would have given 14px (−1).
|
||||
await expect.poll(readSecondaryFontSize, { timeout: 5_000 }).toBe('13px')
|
||||
await increase.click()
|
||||
await dialog.getByText('16', { exact: true }).waitFor({ timeout: 5_000 })
|
||||
await expect.poll(readFontSize, { timeout: 5_000 }).toBe('16px')
|
||||
await stepFontSize(increase, 16)
|
||||
await expect.poll(readSecondaryFontSize, { timeout: 5_000 }).toBe('14px')
|
||||
await expect.poll(async () => readFile(join(scaffold.harnessHome, 'settings.yaml'), 'utf8'), { timeout: 5_000 })
|
||||
.toMatch(/ui-theme:\n(?:\s+\w+: .*\n)*?\s+fontSize: 16/)
|
||||
await page.keyboard.press('Escape')
|
||||
|
||||
// Reload: the boot script embeds the durable size and ThemeRuntime seeds
|
||||
@@ -429,11 +446,8 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
await restored.waitFor({ timeout: 10_000 })
|
||||
await restored.getByText('16', { exact: true }).hover()
|
||||
const decrease = restored.getByRole('button', { name: '减小字号' })
|
||||
await decrease.click()
|
||||
await restored.getByText('15', { exact: true }).waitFor({ timeout: 5_000 })
|
||||
await decrease.click()
|
||||
await restored.getByText('14', { exact: true }).waitFor({ timeout: 5_000 })
|
||||
await expect.poll(readFontSize, { timeout: 5_000 }).toBe('14px')
|
||||
await stepFontSize(decrease, 15)
|
||||
await stepFontSize(decrease, 14)
|
||||
await page.keyboard.press('Escape')
|
||||
expect(tripwire.pageErrors).toEqual([])
|
||||
}, 90_000)
|
||||
|
||||
Reference in new issue
Block a user