Files
jobtrackingapp/docs/redesign/phase-8/p8-04/diagnostics/performance.spec.ts
T

69 lines
4.8 KiB
TypeScript

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<string, number> = {};
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(); }
});