test: add frontend visual regression coverage
This commit is contained in:
@@ -0,0 +1,45 @@
|
|||||||
|
import type { APIRequestContext, Page } from '@playwright/test'
|
||||||
|
import { expect, test } from './fixtures'
|
||||||
|
|
||||||
|
async function csrf(request: APIRequestContext) {
|
||||||
|
const state = await request.storageState()
|
||||||
|
return state.cookies.find(cookie => cookie.name === 'dodo_csrf')?.value ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createTask(request: APIRequestContext, baseURL: string, title: string, listId: string) {
|
||||||
|
const response = await request.post('/api/v1/tasks', {
|
||||||
|
data: { title, list_id: listId },
|
||||||
|
headers: { 'x-csrf-token': await csrf(request), origin: baseURL },
|
||||||
|
})
|
||||||
|
expect(response.ok(), await response.text()).toBeTruthy()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openInbox(page: Page) {
|
||||||
|
if ((await page.viewportSize())!.width <= 930) {
|
||||||
|
await page.locator('.topbar').getByRole('button', { name: /展开菜单|收起菜单/ }).click()
|
||||||
|
}
|
||||||
|
await page.locator('.sidebar').getByRole('button', { name: '收集箱', exact: true }).click()
|
||||||
|
}
|
||||||
|
|
||||||
|
test('inbox and task composer match approved visuals', async ({ page, request, baseURL }, testInfo) => {
|
||||||
|
test.skip(testInfo.project.name !== 'mobile-390', 'approved visual baselines are maintained for mobile-390 only')
|
||||||
|
await page.clock.install({ time: new Date('2026-09-19T05:00:00.000Z') })
|
||||||
|
|
||||||
|
const bootstrap = await request.get('/api/v1/bootstrap')
|
||||||
|
expect(bootstrap.ok()).toBeTruthy()
|
||||||
|
const inbox = (await bootstrap.json()).lists.find((item: { is_inbox: boolean }) => item.is_inbox)
|
||||||
|
expect(inbox).toBeTruthy()
|
||||||
|
|
||||||
|
await createTask(request, baseURL!, '整理本周工作记录', inbox.id)
|
||||||
|
await createTask(request, baseURL!, '检查 Dodo 备份', inbox.id)
|
||||||
|
await createTask(request, baseURL!, '周末买水果和牛奶', inbox.id)
|
||||||
|
|
||||||
|
await page.goto('/')
|
||||||
|
await openInbox(page)
|
||||||
|
await expect(page.locator('.task-row')).toHaveCount(3)
|
||||||
|
await expect(page).toHaveScreenshot('inbox.png', { fullPage: true })
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: '添加任务' }).click()
|
||||||
|
await expect(page.getByRole('dialog', { name: /添加任务/ })).toBeVisible()
|
||||||
|
await expect(page).toHaveScreenshot('task-composer.png', { fullPage: true })
|
||||||
|
})
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
@@ -1 +1 @@
|
|||||||
{"name":"dodo-frontend","private":true,"version":"0.1.0","type":"module","packageManager":"[email protected]","scripts":{"dev":"vite --host 0.0.0.0","build":"vue-tsc -b && vite build","test":"vitest run --exclude 'e2e/**'","test:e2e:mobile":"node scripts/playwright-mobile.mjs"},"dependencies":{"@vitejs/plugin-vue":"latest","class-variance-authority":"latest","clsx":"latest","lucide-vue-next":"^0.468.0","markdown-it":"^15.0.2","markdown-it-task-lists":"^2.1.1","reka-ui":"latest","tailwind-merge":"latest","vue":"latest","vue-router":"latest"},"devDependencies":{"@playwright/test":"^1.63.0","@tailwindcss/vite":"latest","@types/markdown-it":"^14.2.0","@types/node":"latest","fflate":"^0.8.3","jsdom":"^30.0.1","tailwindcss":"latest","typescript":"^5.7.2","vite":"latest","vitest":"latest","vue-tsc":"latest"},"pnpm":{"onlyBuiltDependencies":["vue-demi"]}}
|
{"name":"dodo-frontend","private":true,"version":"0.1.0","type":"module","packageManager":"[email protected]","scripts":{"dev":"vite --host 0.0.0.0","build":"vue-tsc -b && vite build","test":"vitest run --exclude 'e2e/**'","test:e2e:mobile":"node scripts/playwright-mobile.mjs","test:e2e:visual":"DODO_E2E_PROJECT=mobile-390 playwright test e2e/visual-regression.spec.ts --project mobile-390"},"dependencies":{"@vitejs/plugin-vue":"latest","class-variance-authority":"latest","clsx":"latest","lucide-vue-next":"^0.468.0","markdown-it":"^15.0.2","markdown-it-task-lists":"^2.1.1","reka-ui":"latest","tailwind-merge":"latest","vue":"latest","vue-router":"latest"},"devDependencies":{"@playwright/test":"^1.63.0","@tailwindcss/vite":"latest","@types/markdown-it":"^14.2.0","@types/node":"latest","fflate":"^0.8.3","jsdom":"^30.0.1","tailwindcss":"latest","typescript":"^5.7.2","vite":"latest","vitest":"latest","vue-tsc":"latest"},"pnpm":{"onlyBuiltDependencies":["vue-demi"]}}
|
||||||
@@ -25,7 +25,15 @@ export default defineConfig({
|
|||||||
fullyParallel: false,
|
fullyParallel: false,
|
||||||
workers: 1,
|
workers: 1,
|
||||||
timeout: 45_000,
|
timeout: 45_000,
|
||||||
expect: { timeout: 8_000 },
|
expect: {
|
||||||
|
timeout: 8_000,
|
||||||
|
toHaveScreenshot: {
|
||||||
|
animations: 'disabled',
|
||||||
|
caret: 'hide',
|
||||||
|
maxDiffPixelRatio: 0.001,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
snapshotPathTemplate: '{testDir}/{testFilePath}-snapshots/{arg}-{projectName}{ext}',
|
||||||
outputDir: path.join(runtimeRoot, 'test-results'),
|
outputDir: path.join(runtimeRoot, 'test-results'),
|
||||||
reporter: [['line'], ['html', { outputFolder: path.join(runtimeRoot, 'report'), open: 'never' }]],
|
reporter: [['line'], ['html', { outputFolder: path.join(runtimeRoot, 'report'), open: 'never' }]],
|
||||||
globalSetup: './e2e/global-setup.ts',
|
globalSetup: './e2e/global-setup.ts',
|
||||||
|
|||||||
Reference in New Issue
Block a user