import { test, expect, type APIRequestContext } from '../../../../../job-tracker-ui/node_modules/@playwright/test'; const api = 'http://localhost:5302/api'; const password = 'E2ePassword123!'; async function login(request: APIRequestContext, email: string) { expect((await request.post(`${api}/auth/login`, { data: { email, password } })).status()).toBe(200); const me = await (await request.get(`${api}/auth/me`)).json(); const csrf = (await request.storageState()).cookies.find(cookie => cookie.name === 'XSRF-TOKEN')!.value; return { 'X-CSRF-TOKEN': csrf, 'X-Session-Context': me.sessionContext as string }; } test('Finite 250-job dashboard load and edits preserve report request counts', async ({ page, playwright }, info) => { test.setTimeout(120000); const admin = await playwright.request.newContext(); try { const adminHeaders = await login(admin, 'e2e@example.test'); const email = `p8-performance-${Date.now()}@example.test`; expect((await admin.post(`${api}/users`, { headers: adminHeaders, data: { email, password, roles: [] } })).status()).toBe(200); const headers = await login(page.request, email); const company = await page.request.post(`${api}/companies`, { headers, data: { name: 'Finite dashboard performance fixture' } }); expect(company.status()).toBe(201); const companyId = (await company.json()).id; for (let index = 0; index < 250; index++) { expect((await page.request.post(`${api}/jobapplications`, { headers, data: { companyId, jobTitle: `Dashboard ${index}`, status: 'Applied', dateApplied: new Date().toISOString(), tags: 'TypeScript', salaryMin: 600000, salaryMax: 850000, salaryCurrency: 'NOK', salaryPeriod: 'year' } })).status()).toBe(201); } await page.route('**/*', route => ['localhost', '127.0.0.1'].includes(new URL(route.request().url()).hostname) ? route.continue() : route.abort()); const reports = ['stats', 'analytics-overview', 'reminders', 'analytics', 'tags', 'tag-trends']; const counts: Record = {}; page.on('request', request => { const name = new URL(request.url()).pathname.replace('/api/jobapplications/', ''); if (request.method() === 'GET' && reports.includes(name)) counts[name] = (counts[name] ?? 0) + 1; }); const observations = []; for (const width of [1440, 320]) { await page.setViewportSize({ width, height: 900 }); const beforeLoad = { ...counts }; const started = Date.now(); await page.goto('/dashboard'); await expect(page.getByRole('link', { name: 'Tracked jobs: 250', exact: true })).toBeVisible(); await expect(page.locator('[data-dashboard-widget]')).toHaveCount(9); // The existing shell badge and dashboard independently read reminders on entry. await expect.poll(() => reports.every(name => counts[name] === (beforeLoad[name] ?? 0) + (name === 'reminders' ? 2 : 1))).toBe(true); const loadMs = Date.now() - started; const beforeEdits = { ...counts }; await page.getByRole('button', { name: 'Customize dashboard', exact: true }).click(); const editor = page.getByRole('region', { name: 'Editing dashboard', exact: true }); const editStarted = Date.now(); await editor.getByRole('checkbox', { name: 'Job tags', exact: true }).uncheck(); await editor.getByRole('checkbox', { name: 'Job tags', exact: true }).check(); const row = editor.locator('[data-layout-row="funnel"]'); await row.locator('select').selectOption(width === 1440 ? 'full' : 'standard'); await row.getByRole('button', { name: /earlier/ }).click(); const editMs = Date.now() - editStarted; const saveStarted = Date.now(); const write = page.waitForResponse(response => response.url() === `${api}/auth/preferences/dashboard` && response.request().method() === 'PUT'); await editor.getByRole('button', { name: 'Save layout', exact: true }).click(); expect((await write).status()).toBe(200); await expect(editor).toHaveCount(0); expect(counts).toEqual(beforeEdits); observations.push({ width, fixtureJobs: 250, loadMs, editSequenceMs: editMs, acknowledgedSaveMs: Date.now() - saveStarted, reportRequests: Object.fromEntries(reports.map(name => [name, counts[name] - (beforeLoad[name] ?? 0)])), renderer: await page.evaluate(() => ({ domElements: document.querySelectorAll('*').length, heapBytes: (performance as Performance & { memory?: { usedJSHeapSize: number } }).memory?.usedJSHeapSize ?? null })) }); } await info.attach('finite-dashboard-performance', { body: JSON.stringify({ observations, limitation: 'Two local unthrottled Chromium viewport observations; no Phase7 timing comparison, capacity/SLA, physical mobile resource or sustained drag-frame guarantee.' }, null, 2), contentType: 'application/json' }); } finally { await admin.dispose(); } });