fix(cv): bind letter proposals to current sources and preserve document choices
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { expect, test, type APIRequestContext } from '@playwright/test';
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { createHash } from 'node:crypto';
|
||||
@@ -35,6 +36,7 @@ test('cover letter native preview/PDF fidelity records four independent physical
|
||||
const email = `letter-fidelity-${Date.now()}@example.test`;
|
||||
try {
|
||||
expect((await admin.post(`${apiUrl}/users`, { headers: adminHeaders, data: { email, password, roles: [] } })).status()).toBe(200);
|
||||
expect((await admin.post(`${apiUrl}/jobapplications/2147483647/ai/history/2147483647/validate-source`, { headers: adminHeaders })).status()).toBe(404);
|
||||
} finally { await admin.dispose(); }
|
||||
const headers = await login(page.request, email);
|
||||
const sourceBefore = await (await page.request.get(`${apiUrl}/career/profile`)).json();
|
||||
@@ -51,7 +53,7 @@ test('cover letter native preview/PDF fidelity records four independent physical
|
||||
: 'Documented reliable workflows and accessible navigation using reviewed source evidence. ';
|
||||
const text = Array.from({ length: 24 }, (_, index) => `B${String(index + 1).padStart(2, '0')} ${sentence.repeat(4)}`)
|
||||
.concat(['- S01 **Reviewed achievement** — æøå.', '- S02 *Verified collaboration* — ÆØÅ.', 'R99 Final reviewed letter content — Åse Østby.']).join('\n\n');
|
||||
const documentSettings = { schemaVersion: 1, senderName: 'R01 Åse Østby', senderTitle: 'P01 Platform engineer',
|
||||
const documentSettings = { schemaVersion: 1, language: language === 'nb' ? 'nb-NO' : 'en', senderName: 'R01 Åse Østby', senderTitle: 'P01 Platform engineer',
|
||||
senderEmail: 'ase@example.test', senderPhone: '+47 12345678', senderLocation: 'Oslo, Norge',
|
||||
recipient: 'R03 Hiring team\nNordlys Synthetic AS\nOslo, Norge', subject: language === 'nb' ? 'R02 Søknad om plattformutvikler' : 'R02 Application for platform engineer',
|
||||
date: '2026-10-25', themeId: 'minimal', accentColor: '#126b55', bodyFont: 'Arial, Helvetica, sans-serif',
|
||||
@@ -71,6 +73,7 @@ test('cover letter native preview/PDF fidelity records four independent physical
|
||||
const preview = await page.request.post(`${letterUrl}/preview`, { headers, data: { text: saved.text, documentSettings: saved.documentSettings } });
|
||||
expect(preview.status()).toBe(200);
|
||||
const rendered = await preview.json();
|
||||
expect(rendered.html).toContain(`<html lang="${documentSettings.language}">`);
|
||||
expect(rendered.html).toContain('class="page letter-paper"');
|
||||
const pdf = await page.request.post(`${letterUrl}/export-pdf?expectedVersion=${saved.revision}`, { headers });
|
||||
expect(pdf.status()).toBe(200);
|
||||
@@ -89,6 +92,7 @@ test('cover letter native preview/PDF fidelity records four independent physical
|
||||
const previewWorkspace = frame.locator('..').locator('..').locator('..');
|
||||
await expect(frame).toBeVisible();
|
||||
await expect.poll(() => frame.evaluate(element => (element as HTMLIFrameElement).contentDocument?.documentElement.dataset.cvPreviewReady)).toBe('true');
|
||||
expect(await frame.evaluate(element => (element as HTMLIFrameElement).contentDocument?.documentElement.lang)).toBe(documentSettings.language);
|
||||
const measured = await frame.evaluate(async element => {
|
||||
const doc = (element as HTMLIFrameElement).contentDocument!;
|
||||
const win = (element as HTMLIFrameElement).contentWindow!;
|
||||
@@ -171,6 +175,7 @@ test('cover letter workspace preserves keyboard drafts and localised controls ac
|
||||
const created = await context.request.post(`${apiUrl}/jobapplications`, { headers, data: { companyId: company.id, jobTitle: 'Responsive reviewed letter', status: 'Saved' } });
|
||||
expect(created.status()).toBe(201);
|
||||
const job = await created.json();
|
||||
expect((await context.request.post(`${apiUrl}/jobapplications/${job.id}/ai/history/2147483647/validate-source`, { headers })).status()).toBe(403);
|
||||
const letterUrl = `${apiUrl}/jobapplications/${job.id}/cover-letter`;
|
||||
const original = await (await context.request.get(letterUrl)).json();
|
||||
const savedResponse = await context.request.put(letterUrl, { headers, data: { text: 'Original reviewed letter — æøå.', source: 'manual', expectedVersion: original.revision } });
|
||||
@@ -199,6 +204,10 @@ test('cover letter workspace preserves keyboard drafts and localised controls ac
|
||||
const workspace = page.getByRole('group', { name: t.workspaceCoverLetter, exact: true });
|
||||
const body = workspace.getByRole('textbox', { name: t.jobDetailsCoverLetter, exact: true });
|
||||
await expect(body).toHaveValue(saved.text);
|
||||
const documentLanguage = workspace.getByRole('combobox', { name: t.letterDocumentLanguage });
|
||||
await expect(documentLanguage).toBeVisible();
|
||||
await documentLanguage.click();
|
||||
await page.getByRole('option', { name: language === 'nb' ? t.languageNorwegianBokmal : t.languageEnglish, exact: true }).click();
|
||||
const draft = `${width} ${language === 'nb' ? 'Ulagret søknadsbrev med gjennomgåtte fakta' : 'Unsaved letter with reviewed facts'} — æøå.`;
|
||||
await body.focus(); await body.press('ControlOrMeta+A'); await body.pressSequentially(draft);
|
||||
await expect(body).toHaveValue(draft);
|
||||
@@ -220,6 +229,15 @@ test('cover letter workspace preserves keyboard drafts and localised controls ac
|
||||
expect(geometry.root).toBeLessThanOrEqual(1); expect(geometry.app).toBeLessThanOrEqual(1);
|
||||
const writePane = workspace.locator('[id$="-write-panel"]');
|
||||
const previewPane = workspace.locator('[id$="-preview-panel"]');
|
||||
const audit = async (state: string) => {
|
||||
await workspace.evaluate(node => node.setAttribute('data-cv-ux-axe', 'letter'));
|
||||
const result = await new AxeBuilder({ page }).include('[data-cv-ux-axe="letter"]').withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']).analyze();
|
||||
const reportPath = info.outputPath(`letter-${width}-${state}-axe.json`);
|
||||
await fs.writeFile(reportPath, JSON.stringify({ violations: result.violations, incomplete: result.incomplete }, null, 2));
|
||||
await info.attach(`letter-${width}-${state}-axe`, { path: reportPath, contentType: 'application/json' });
|
||||
expect(result.violations).toEqual([]);
|
||||
};
|
||||
if (width === 320 || width === 390 || width === 1440) await audit('write');
|
||||
if (width < 1024) {
|
||||
const tabs = workspace.getByRole('tablist', { name: t.assetsLetterWorkspace, exact: true });
|
||||
const write = tabs.getByRole('tab', { name: t.assetsLetterWrite, exact: true });
|
||||
@@ -228,6 +246,7 @@ test('cover letter workspace preserves keyboard drafts and localised controls ac
|
||||
await write.focus(); await page.keyboard.press('ArrowRight'); await expect(preview).toBeFocused(); await page.keyboard.press('Enter');
|
||||
await expect(preview).toHaveAttribute('aria-selected', 'true');
|
||||
await expect(writePane).not.toBeVisible(); await expect(previewPane).toBeVisible();
|
||||
if (width === 320 || width === 390) await audit('preview');
|
||||
await expect(workspace.getByRole('textbox', { name: t.jobDetailsCoverLetter, exact: true })).toHaveCount(0);
|
||||
for (let step = 0; step < 8; step++) {
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
Reference in New Issue
Block a user