test(web): 固定三期视觉与几何场景

This commit is contained in:
tbphp committed 2026-07-29 02:24:31 +08:00
1 parent 0e1d3839d2
commit f70079ffe3
5 files changed
+158 -18

No files matched your search

+65 -11
View File
@@ -1,4 +1,5 @@
import { expect, test } from './fixtures'
import { visualThemes, visualViewports } from './visual-fixtures'
const authKey = process.env.GPT_LOAD_E2E_AUTH_KEY
const upstreamURL = process.env.GPT_LOAD_E2E_UPSTREAM_URL
@@ -453,44 +454,93 @@ test('critical management journey works through the embedded binary', async ({ p
})
await test.step('verify responsive light and dark layouts with table-local overflow', async () => {
const viewports = [
{ width: 375, height: 812 },
{ width: 768, height: 900 },
{ width: 1024, height: 900 },
{ width: 1440, height: 900 },
] as const
for (const theme of ['light', 'dark'] as const) {
for (const theme of visualThemes) {
await page.evaluate((value) => localStorage.setItem('gpt-load.theme', value), theme)
for (const viewport of viewports) {
for (const viewport of visualViewports) {
await page.setViewportSize(viewport)
await page.goto('/')
await expect(page.locator('html')).toHaveAttribute('data-theme', theme)
await expect(page.locator('[data-test="home-usage-requests"]')).toBeVisible()
await expect(page.locator('[data-test="connection-snippet"]')).toContainText(
'GPT_LOAD_API_KEY',
)
const layout = await page.evaluate(() => {
const topbar = document.querySelector('.app-topbar')?.getBoundingClientRect()
const main = document.querySelector('#main-content')?.getBoundingClientRect()
const operational = document
.querySelector('[data-test="home-operational-overview"]')
?.getBoundingClientRect()
const overview = document.querySelector('.home-overview')?.getBoundingClientRect()
const usage = document.querySelector('.usage-summary-section')?.getBoundingClientRect()
const groups = document.querySelector('.groups-section')?.getBoundingClientRect()
const connection = document
.querySelector('[data-test="home-connection"]')
?.getBoundingClientRect()
return {
documentWidth: document.documentElement.scrollWidth,
operationalBottom: operational?.bottom ?? -1,
operationalTop: operational?.top ?? -1,
overviewBottom: overview?.bottom ?? -1,
overviewTop: overview?.top ?? -1,
usageBottom: usage?.bottom ?? -1,
usageTop: usage?.top ?? -1,
groupsBottom: groups?.bottom ?? -1,
groupsTop: groups?.top ?? -1,
connectionTop: connection?.top ?? -1,
mainTop: main?.top ?? -1,
topbarBottom: topbar?.bottom ?? -1,
topbarHeight: topbar?.height ?? -1,
viewportWidth: window.innerWidth,
}
})
expect(layout.documentWidth).toBeLessThanOrEqual(layout.viewportWidth)
expect(layout.usageTop).toBeGreaterThanOrEqual(layout.overviewBottom)
expect(layout.groupsTop).toBeGreaterThanOrEqual(layout.usageBottom)
expect(layout.mainTop).toBeGreaterThanOrEqual(layout.topbarBottom)
expect(layout.topbarHeight).toBeLessThanOrEqual(viewport.width < 640 ? 132 : 80)
expect(layout.overviewTop).toBeGreaterThanOrEqual(layout.operationalTop)
expect(layout.usageTop).toBeGreaterThanOrEqual(layout.overviewTop)
expect(layout.usageBottom).toBeLessThanOrEqual(layout.overviewBottom)
expect(layout.groupsTop).toBeGreaterThanOrEqual(layout.operationalBottom)
expect(layout.connectionTop).toBeGreaterThanOrEqual(layout.groupsBottom)
}
}
const preferences = page.getByRole('button', { name: 'Preferences' })
await preferences.click()
await expect(page.locator('[data-test="preferences-panel"]')).toBeVisible()
await page.keyboard.press('Escape')
await expect(preferences).toBeFocused()
const protocol = page.getByLabel('Protocol', { exact: true })
await protocol.click()
await page.getByRole('option', { name: 'Anthropic' }).click()
await expect(page.locator('[data-test="connection-snippet"]')).toContainText('/v1/messages')
await expect(page.locator('[data-test="connection-snippet"]')).toContainText(
'anthropic-version',
)
await protocol.click()
await page.getByRole('option', { name: 'Gemini' }).click()
await expect(page.locator('[data-test="connection-snippet"]')).toContainText('/v1beta/models/')
await expect(page.locator('[data-test="connection-snippet"]')).toContainText('x-goog-api-key')
await page.setViewportSize({ width: 375, height: 812 })
await page.goto('/settings/model-prices')
await expect(page.locator('[data-test^="builtin-price-card-"]').first()).toBeVisible()
await expect(page.locator('[data-test^="builtin-price-row-"]')).toHaveCount(0)
await page.goto('/access-keys')
await expect(page.locator('[data-test^="access-key-card-"]').first()).toBeVisible()
await expect(page.locator('[data-test^="access-key-row-"]')).toHaveCount(0)
await page.setViewportSize({ width: 768, height: 900 })
await page.goto('/settings/model-prices')
await expect(page.locator('[data-test^="builtin-price-row-"]').first()).toBeVisible()
await expect(page.locator('[data-test^="builtin-price-card-"]')).toHaveCount(0)
const priceTable = page.locator('.data-table__container').first()
await expect(priceTable).toBeVisible()
await expect(priceTable).toHaveAttribute('tabindex', '0')
await priceTable.focus()
await expect(priceTable).toBeFocused()
const overflow = await priceTable.evaluate((table) => ({
client: table.clientWidth,
scroll: table.scrollWidth,
@@ -499,6 +549,10 @@ test('critical management journey works through the embedded binary', async ({ p
}))
expect(overflow.scroll).toBeGreaterThan(overflow.client)
expect(overflow.document).toBeLessThanOrEqual(overflow.viewport)
await page.goto('/access-keys')
await expect(page.locator('[data-test^="access-key-row-"]').first()).toBeVisible()
await expect(page.locator('[data-test^="access-key-card-"]')).toHaveCount(0)
})
await test.step('inspect current route', async () => {
+12 -6
View File
@@ -10,8 +10,12 @@ test('Reka overlays work under production CSP', async ({ page }, testInfo) => {
video: 'off',
})
const errors: string[] = []
const unauthorizedResponses: string[] = []
page.on('console', (message) => message.type() === 'error' && errors.push(message.text()))
page.on('pageerror', (error) => errors.push(error.message))
page.on('response', (response) => {
if (response.status() === 401) unauthorizedResponses.push(response.url())
})
await page.addInitScript(() => {
localStorage.setItem('gpt-load.locale', 'en-US')
const violations: string[] = []
@@ -42,18 +46,20 @@ test('Reka overlays work under production CSP', async ({ page }, testInfo) => {
expect(revealBounds).not.toBeNull()
expect(revealBounds!.width).toBeGreaterThanOrEqual(44)
expect(revealBounds!.height).toBeGreaterThanOrEqual(44)
const languageTrigger = page.getByLabel('Language')
await languageTrigger.click()
const preferencesTrigger = page.getByRole('button', { name: 'Preferences' })
await preferencesTrigger.click()
await expect(page.locator('[data-test="preferences-panel"]')).toBeVisible()
await page.keyboard.press('Escape')
await expect(languageTrigger).toBeFocused()
await languageTrigger.click()
await page.getByRole('option', { name: 'English' }).click()
await page.getByRole('button', { name: 'Use dark theme' }).click()
await expect(preferencesTrigger).toBeFocused()
await preferencesTrigger.click()
await page.getByLabel('English', { exact: true }).check()
await page.getByLabel('Dark', { exact: true }).check()
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark')
const cspViolations = await page.evaluate(
() => (window as Window & { __cspViolations?: string[] }).__cspViolations ?? [],
)
expect(cspViolations).toEqual([])
expect(unauthorizedResponses).toEqual([])
expect(errors).toEqual([])
const recordings = testInfo.attachments.filter(({ name }) => /trace|screenshot|video/i.test(name))
+2 -1
View File
@@ -1,12 +1,13 @@
import { expect, test as base } from '@playwright/test'
import { deterministicUUIDPrefix } from './deterministic-ids'
import { visualClock } from './visual-fixtures'
function deterministicInitScript(uuidPrefix: string): string {
return String.raw`
(() => {
const uuidPrefix = ${JSON.stringify(uuidPrefix)}
const fixedNow = Date.parse('2026-07-28T00:00:00.000Z')
const fixedNow = Date.parse(${JSON.stringify(visualClock)})
const NativeDate = Date
class DeterministicDate extends NativeDate {
constructor(...args) {
+45
View File
@@ -0,0 +1,45 @@
import { describe, expect, it } from 'vitest'
import {
visualClock,
visualFixtureData,
visualLocales,
visualScenarios,
visualThemes,
visualViewports,
} from './visual-fixtures'
describe('deterministic visual fixtures', () => {
it('freezes the approved scenario, viewport, theme, and locale matrix', () => {
expect(visualScenarios).toEqual([
'home-normal',
'home-anomaly',
'access-keys-long',
'model-prices-mixed',
'settings-dirty',
'usage-quality',
'logs-signal-path',
])
expect(visualViewports).toEqual([
{ width: 375, height: 812 },
{ width: 768, height: 900 },
{ width: 1024, height: 900 },
{ width: 1440, height: 900 },
])
expect(visualThemes).toEqual(['light', 'dark'])
expect(visualLocales).toEqual(['en-US', 'zh-CN'])
})
it('uses stable public-safe values without plaintext credentials', () => {
expect(visualClock).toBe('2026-07-29T00:00:00.000Z')
expect(visualFixtureData.requestId).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/,
)
expect(visualFixtureData.accessKeySuffix).toMatch(/^[0-9a-f]{4}$/)
expect(visualFixtureData.longIdentifier.length).toBeGreaterThan(96)
const serialized = JSON.stringify(visualFixtureData)
expect(serialized).not.toMatch(/sk-gl-[0-9a-z]{16,}/i)
expect(serialized).not.toContain('Bearer ')
})
})
+34
View File
@@ -0,0 +1,34 @@
export const visualFixtureVersion = 1
export const visualClock = '2026-07-29T00:00:00.000Z'
export const visualScenarios = [
'home-normal',
'home-anomaly',
'access-keys-long',
'model-prices-mixed',
'settings-dirty',
'usage-quality',
'logs-signal-path',
] as const
export type VisualScenario = (typeof visualScenarios)[number]
export const visualViewports = [
{ width: 375, height: 812 },
{ width: 768, height: 900 },
{ width: 1024, height: 900 },
{ width: 1440, height: 900 },
] as const
export const visualThemes = ['light', 'dark'] as const
export const visualLocales = ['en-US', 'zh-CN'] as const
export const visualFixtureData = Object.freeze({
requestId: '5297f18d-3ca7-42d9-8a5b-2ce08ccd1b01',
accessKeySuffix: 'a11e',
groupName: 'Visual Fixture OpenAI Group',
modelName: 'visual-fixture-model-with-a-stable-long-identifier',
longIdentifier:
'visual-fixture-client-segment-segment-segment-segment-segment-segment-segment-segment-segment-segment-segment-segment',
observedAt: visualClock,
})