/** * Sprint 16 visual / authenticated-страницы: * - /dashboard (с виджетами + chart) * - /catalog/products (table) * - /catalog/counterparties * - /catalog/products/new (form) * - /purchases/supplies * - /purchases/supplies/new * - /sales/retail * - /sales/retail/new * - /inventory/stock * - /reports/sales * - /reports/stock * - /settings/organization * * 12 страниц × 2 темы = 24 snapshot'a per project (desktop+mobile = 48 total). * Чтобы держать прогон под 15 мин, делаем один org per worker — фабрика * вызывается в beforeAll. */ import { expect, test } from '@playwright/test' import { OrgFactory, type BuiltOrg } from '../factories/OrgFactory.js' import { attachSession } from '../lib/ui.js' import { applyTheme } from './_helper.js' const pages = [ { path: '/dashboard', name: 'dashboard' }, { path: '/catalog/products', name: 'products-list' }, { path: '/catalog/counterparties', name: 'counterparties' }, { path: '/catalog/products/new', name: 'product-new' }, { path: '/purchases/supplies', name: 'supplies-list' }, { path: '/purchases/supplies/new', name: 'supply-new' }, { path: '/sales/retail', name: 'retail-list' }, { path: '/sales/retail/new', name: 'retail-new' }, { path: '/inventory/stock', name: 'stock' }, { path: '/reports/sales', name: 'reports-sales' }, { path: '/reports/stock', name: 'reports-stock' }, { path: '/settings/organization', name: 'org-settings' }, ] // Один org на весь файл — 12 страниц × 2 темы = 24 snapshot'a одной сессией. let built: BuiltOrg test.beforeAll(async () => { built = await OrgFactory.for('visual') .withProducts(3) .withCounterparties(2) .withSupplies(1) .build() }) /** Маски для динамического контента (артикулы с Date.now, KPI'ы с * текущей датой, текущее время). Без масок 0.2% threshold завышает * diff'ы по «гуляющему» контенту между прогонами. */ function masks(page: import('@playwright/test').Page) { return [ // Артикулы в таблицах товаров (содержат Date.now()). page.locator('table td:nth-child(2)'), // KPI-блоки на dashboard. page.locator('[data-kpi]'), // delta-стрелки «+12%». page.locator('[data-delta]'), ] } for (const p of pages) { test(`${p.name} light`, async ({ page }) => { await attachSession(page, built.session, p.path) await page.waitForLoadState('networkidle') await applyTheme(page, 'light') // На пути /reports/* картинки чарта мокаются ленивым chunk'ом — // подождать дополнительно. if (p.path.includes('/reports')) await page.waitForTimeout(500) await expect(page).toHaveScreenshot(`${p.name}-light.png`, { mask: masks(page) }) }) test(`${p.name} dark`, async ({ page }) => { await attachSession(page, built.session, p.path) await page.waitForLoadState('networkidle') await applyTheme(page, 'dark') if (p.path.includes('/reports')) await page.waitForTimeout(500) await expect(page).toHaveScreenshot(`${p.name}-dark.png`, { mask: masks(page) }) }) }