docs(dashboard): record Phase 8 acceptance and QA handoff
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
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(); }
|
||||
});
|
||||
@@ -4,5 +4,5 @@ import path from 'node:path';
|
||||
|
||||
const cwd = path.resolve(__dirname, '../../../../../job-tracker-ui');
|
||||
const servers = Array.isArray(base.webServer) ? base.webServer : [base.webServer!];
|
||||
export default defineConfig({ ...base, testDir: __dirname, testMatch: 'sizes.spec.ts',
|
||||
export default defineConfig({ ...base, testDir: __dirname, testMatch: ['sizes.spec.ts', 'performance.spec.ts'],
|
||||
webServer: servers.map(server => ({ ...server, cwd })) });
|
||||
|
||||
Reference in New Issue
Block a user