fix(ui): clarify document errors and retain readable actions
This commit is contained in:
@@ -99,10 +99,43 @@ test('[P7-07 Documents] initial503 Retry and incompatible held query expose no s
|
||||
|
||||
test('[P7-07 Documents] stale CV opens the existing missing-source error and Back plus explicit Refresh removes its metadata', async ({ page, session }, info) => {
|
||||
test.setTimeout(120_000); const variant = await cv(page.request, session.owner, 'Removed current CV — æøå'); await enter(page, session.owner); const item = (await readIndex(page.request)).items[0]; await expect(row(page, item)).toBeVisible(); const from = indexPath(page); noImplicitSourceWork(session); expect((await page.request.delete(`${apiUrl}/cv/variants/${variant.id}`, { headers: session.owner.headers })).status()).toBe(204);
|
||||
const missing = waitResponse(page, 'GET', `/api/cv/variants/${variant.id}`); await keyboard(page, row(page, item).getByRole('link', { name: copy('documentsOpenSource'), exact: true })); expect((await missing).status()).toBe(404); await expect(page).toHaveURL(new RegExp(`/career/builder/${variant.id}$`)); const sourceError = page.getByRole('main').getByRole('alert'); await expect(sourceError).toBeVisible(); await expect(sourceError).toHaveText('Not Found'); await expect(page.getByRole('heading', { level: 2, name: copy('documentsYourDocuments'), exact: true })).toHaveCount(0); await capture(page, info, desktop, 'existing-missing-source-editor'); await page.goBack(); await expect(results(page)).toBeVisible(); expect(indexPath(page)).toBe(from);
|
||||
const missing = waitResponse(page, 'GET', `/api/cv/variants/${variant.id}`); await keyboard(page, row(page, item).getByRole('link', { name: copy('documentsOpenSource'), exact: true })); expect((await missing).status()).toBe(404); await expect(page).toHaveURL(new RegExp(`/career/builder/${variant.id}$`)); const sourceError = page.getByRole('main').getByRole('alert'); await expect(sourceError).toBeVisible(); await expect(sourceError).toContainText(copy('cvEditorSourceUnavailable')); await expect(sourceError.getByRole('button', { name: copy('retry'), exact: true })).toHaveCount(0); await expect(page.getByRole('heading', { level: 2, name: copy('cvBuilder'), exact: true })).toBeVisible(); await expect(page.getByRole('heading', { level: 2, name: copy('documentsYourDocuments'), exact: true })).toHaveCount(0); await capture(page, info, desktop, 'existing-missing-source-editor'); await page.goBack(); await expect(results(page)).toBeVisible(); expect(indexPath(page)).toBe(from);
|
||||
const refreshed = waitResponse(page, 'GET', '/api/documents'); await keyboard(page, page.getByRole('button', { name: copy('documentsRefresh'), exact: true })); expect(metadata(await json<Documents>(await refreshed)).total).toBe(0); await expect(row(page, item)).toHaveCount(0); await expect(page.getByText(copy('documentsEmpty'), { exact: true })).toBeVisible(); expect(writes(session)).toEqual([]);
|
||||
});
|
||||
|
||||
test('[P7-11 E08 C05] transient private CV entry recovers GET reads and keyboard focus without saving', async ({ page, session }, info) => {
|
||||
const variant = await cv(page.request, session.owner, 'Read-only recovery CV');
|
||||
await enter(page, session.owner);
|
||||
let failed = true;
|
||||
await page.route(`**/api/cv/variants/${variant.id}`, route => failed ? route.fulfill({ status: 503, json: { title: 'Controlled unavailable read' } }) : route.continue());
|
||||
await keyboard(page, row(page, { name: variant.name, type: 'cv' }).getByRole('link', { name: copy('documentsOpenSource'), exact: true }));
|
||||
const alert = page.getByRole('alert').filter({ hasText: copy('cvEditorOpenFailed') });
|
||||
await expect(alert).toBeVisible();
|
||||
failed = false;
|
||||
await keyboard(page, alert.getByRole('button', { name: copy('retry'), exact: true }));
|
||||
const editor = page.getByRole('region', { name: copy('cvEditorEditingRegion'), exact: true });
|
||||
await expect(editor).toBeFocused();
|
||||
await expect(page.getByLabel(copy('cvEditorName'), { exact: true })).toHaveValue(variant.name);
|
||||
expect((await json<CvVariant>(await page.request.get(`${apiUrl}/cv/variants/${variant.id}`))).version).toBe(variant.version);
|
||||
expect(writes(session).every(value => value.method === 'POST' && value.path === `/api/cv/variants/${variant.id}/preview`)).toBe(true);
|
||||
await capture(page, info, desktop, 'recovered-private-cv-entry');
|
||||
});
|
||||
|
||||
test('[P7-11 E08] unknown routes have bilingual contextual headings and preserve browser history', async ({ page, session }, info) => {
|
||||
for (const sample of [desktop, { language: 'nb', theme: 'dark', width: 320, height: 844 }] as Sample[]) {
|
||||
await enter(page, session.owner, sample);
|
||||
const from = indexPath(page);
|
||||
await page.goto('/phase7-unknown-route?keep=context');
|
||||
await expect(page.getByRole('heading', { level: 1, name: copy('notFoundTitle', {}, sample.language), exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { level: 2, name: copy('notFoundTitle', {}, sample.language), exact: true })).toBeVisible();
|
||||
await capture(page, info, sample, 'unknown-route-context');
|
||||
await keyboard(page, page.getByRole('button', { name: copy('goBack', {}, sample.language), exact: true }));
|
||||
await expect(results(page, sample.language)).toBeVisible();
|
||||
expect(indexPath(page)).toBe(from);
|
||||
}
|
||||
expect(writes(session)).toEqual([]);
|
||||
});
|
||||
|
||||
test('[P7-07 Documents] explicit authenticated attachment GET retries exact bytes and a real404 offers only list Refresh', async ({ page, session }, info) => {
|
||||
test.setTimeout(150_000); const parent = await job(page.request, session.owner); const file = await upload(page.request, session.owner, parent); await enter(page, session.owner); const item = (await readIndex(page.request)).items[0]; await expect(row(page, item)).toBeVisible(); noImplicitSourceWork(session); const path = `/api/attachments/download/${file.attachment.id}`; let attempts = 0; await page.route(`${apiUrl}/attachments/download/${file.attachment.id}`, async route => { attempts += 1; if (attempts === 1) return route.fulfill({ status: 503, json: {} }); return route.fulfill({ response: await route.fetch() }); });
|
||||
await keyboard(page, row(page, item).getByRole('button', { name: copy('documentsDownloadAttachment'), exact: true })); const failed = page.getByRole('alert').filter({ hasText: copy('documentsDownloadFailed') }); await expect(failed).toBeVisible(); expect(count(session, 'GET', path)).toHaveLength(1); const listReads = count(session, 'GET', '/api/documents').length; const downloaded = page.waitForEvent('download'); const actual = waitResponse(page, 'GET', path); await keyboard(page, failed.getByRole('button', { name: copy('retry'), exact: true })); const response = await actual; expect(response.status()).toBe(200); expect(response.headers()['content-type']).toContain('text/plain'); const download = await downloaded; expect(download.suggestedFilename()).toBe(file.name); const saved = await download.path(); expect(saved).not.toBeNull(); expect(await readFile(saved!)).toEqual(file.bytes); expect(attempts).toBe(2); expect(count(session, 'GET', '/api/documents')).toHaveLength(listReads); await expect(row(page, item)).toBeVisible();
|
||||
@@ -123,6 +156,25 @@ test('[P7-07 Documents] existing letter source keeps dirty edits on navigation C
|
||||
test('[P7-07 Documents] eight finite bilingual theme widths retain long metadata keyboard actions and complete axe vectors', async ({ page, session }, info) => {
|
||||
test.setTimeout(300_000); const seeded = await seven(page, session.owner, true); const samples: Sample[] = [{ language: 'en', theme: 'light', width: 320, height: 740 }, { language: 'nb', theme: 'dark', width: 360, height: 844 }, { language: 'en', theme: 'dark', width: 390, height: 844 }, { language: 'nb', theme: 'light', width: 768, height: 1024 }, { language: 'en', theme: 'dark', width: 1024, height: 768 }, { language: 'nb', theme: 'light', width: 1280, height: 320 }, { language: 'en', theme: 'light', width: 1440, height: 900 }, { language: 'nb', theme: 'dark', width: 1920, height: 1080 }];
|
||||
for (const sample of samples) {
|
||||
if (sample.width === 1024) {
|
||||
await enter(page, session.owner, sample);
|
||||
const open = page.getByRole('link', { name: copy('documentsOpenCorrespondence', {}, sample.language), exact: true }).first();
|
||||
await expect(open).toBeVisible();
|
||||
const wordLines = await open.evaluate(button => {
|
||||
const walker = document.createTreeWalker(button, NodeFilter.SHOW_TEXT);
|
||||
let node: Node | null;
|
||||
while ((node = walker.nextNode())) {
|
||||
const text = node.textContent?.trim();
|
||||
if (!text) continue;
|
||||
const range = document.createRange();
|
||||
range.setStart(node, (node.textContent ?? '').lastIndexOf(' ') + 1);
|
||||
range.setEnd(node, node.textContent!.length);
|
||||
return range.getClientRects().length;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
expect(wordLines).toBe(1);
|
||||
}
|
||||
await enter(page, session.owner, sample); await expect(rows(page, sample.language)).toHaveCount(7); const control = search(page, sample.language); await control.focus(); await expect(control).toBeFocused(); await page.keyboard.press('Tab'); expect(await page.evaluate(() => document.activeElement !== document.body && document.activeElement?.isConnected)).toBe(true); const frozen = seeded.items.find(value => value.type === 'submitted-package')!; const action = row(page, frozen, sample.language).getByRole('link', { name: copy('documentsOpenSubmissionHistory', {}, sample.language), exact: true }); await action.focus(); await action.scrollIntoViewIfNeeded(); await expect(action).toBeFocused(); await expect(action).toHaveAttribute('href', frozen.sourceHref);
|
||||
const beforeCapture = await action.boundingBox(); expect(beforeCapture).not.toBeNull(); expect(beforeCapture!.y).toBeGreaterThanOrEqual(0); expect(beforeCapture!.y + beforeCapture!.height).toBeLessThanOrEqual(sample.height); await capture(page, info, sample, 'long-metadata-keyboard');
|
||||
await action.scrollIntoViewIfNeeded(); await expect(action).toBeFocused(); const afterCapture = await action.boundingBox(); expect(afterCapture).not.toBeNull(); expect(afterCapture!.y).toBeGreaterThanOrEqual(0); expect(afterCapture!.y + afterCapture!.height).toBeLessThanOrEqual(sample.height); await info.attach(`p707-documents-${sample.language}-${sample.width}-focus-bounds`, { body: JSON.stringify({ beforeFullPageCapture: beforeCapture, afterScrollRestoration: afterCapture, viewport: sample, qualification: 'Full-page screenshot can change scroll. Both actual viewport bounds are asserted after explicit focus/scroll, without relaxing the 0..height requirement.' }), contentType: 'application/json' });
|
||||
|
||||
Reference in New Issue
Block a user