Files
jobtrackingapp/docs/redesign/phase-9/p9-remed-02/representative/long-skills.spec.ts
T

58 lines
5.4 KiB
TypeScript

import { expect,test } from '../../../../../job-tracker-ui/node_modules/@playwright/test';
import fs from 'node:fs/promises';
import path from 'node:path';
const api='http://localhost:5302/api';
for(const [themeId,language] of [['minimal','en'],['technical','nb']] as const){
test(`portable fonts preserve long skills, headings and empty sections ${themeId}/${language}`,async({page,context,browser})=>{
test.setTimeout(180_000);
await context.addInitScript(()=>localStorage.setItem('uiLanguage','en'));
await context.route('**/*',route=>{
const url=new URL(route.request().url());
return ['localhost','127.0.0.1'].includes(url.hostname)||['data:','blob:','about:'].includes(url.protocol)?route.continue():route.abort();
});
expect((await page.request.post(`${api}/auth/login`,{data:{email:'e2e@example.test',password:'E2ePassword123!'}})).status()).toBe(200);
const me=await(await page.request.get(`${api}/auth/me`)).json();
const csrf=(await context.storageState()).cookies.find(cookie=>cookie.name==='XSRF-TOKEN')!.value;
const headers={'X-CSRF-TOKEN':csrf,'X-Session-Context':me.sessionContext};
const skills=Array.from({length:64},(_,i)=>`S${String(i+1).padStart(2,'0')} ${language==='nb'?'Tilgjengelige og pålitelige løsninger for norske arbeidssøkere æøå':'Accessible reliable engineering workflows with verified outcomes'} ${i+1}`);
const current=await(await page.request.get(`${api}/career/profile`)).json();
expect((await page.request.put(`${api}/career/profile`,{headers,data:{expectedVersion:current.revision,profile:{version:'1',contact:{fullName:'Åse Østby æøå',headline:'R01 Verified portable document heading — reliable accessible engineering'},summary:[],jobs:[],education:[],projects:[],skills}}})).status()).toBe(200);
const source=await(await page.request.get(`${api}/career/profile`)).json();
expect(source.profile.skills).toEqual(skills);
const created=await page.request.post(`${api}/cv/variants`,{headers,data:{name:`Supplemental ${themeId}/${language}`,settings:{themeId,language,skillsStyle:'bullets',sections:[{key:'skills',title:'R02 Long heading for verified professional skills and documented engineering capabilities — æøå'}]}}});
expect(created.status()).toBe(200);const variant=await created.json();
await page.goto(`/career/builder/${variant.id}`);
const frame=page.getByTitle('CV preview',{exact:true});
await expect.poll(()=>frame.evaluate(element=>(element as HTMLIFrameElement).contentDocument?.documentElement.dataset.cvPreviewReady)).toBe('true');
const measured=await frame.evaluate(element=>{
const doc=(element as HTMLIFrameElement).contentDocument!,win=doc.defaultView!,width=Number(doc.documentElement.dataset.cvPreviewWidth),height=Number(doc.documentElement.dataset.cvPreviewHeight);
const tokens=[];const walker=doc.createTreeWalker(doc.body,NodeFilter.SHOW_TEXT);let node;
while((node=walker.nextNode()))for(const match of(node.textContent||'').matchAll(/\b[SEBPR]\d{2}\b/g)){
const range=doc.createRange();range.setStart(node,match.index!);range.setEnd(node,match.index!+match[0].length);const rect=range.getClientRects()[0],style=win.getComputedStyle(node.parentElement!);const index=Math.floor(rect.x/width);
tokens.push({token:match[0],x:rect.x-index*width,y:rect.y+index*height,fontSize:style.fontSize,color:style.color});
}
return{tokens,effectiveMargin:win.getComputedStyle(doc.documentElement).getPropertyValue('--cv-page-margin'),text:doc.body.innerText};
});
for(const skill of skills)expect(measured.text).toContain(skill);
const pages=await page.locator('[data-cv-page]').count();expect(pages).toBeGreaterThan(1);
const root=path.join(process.env.CV_FIDELITY_OUTPUT!,`long-skills-${themeId}-${language}`);await fs.mkdir(root,{recursive:true});
const rendered=await(await page.request.get(`${api}/cv/variants/${variant.id}/preview`)).json();
const download=await page.request.post(`${api}/cv/variants/${variant.id}/export-pdf`,{headers});expect(download.status()).toBe(200);
await fs.writeFile(path.join(root,'render.html'),rendered.html);await fs.writeFile(path.join(root,'export.pdf'),await download.body());
for(let i=1;i<=pages;i++){
const srcDoc=await page.getByTitle(`CV preview page ${i}`,{exact:true}).getAttribute('srcdoc');
const capture=await browser.newPage({viewport:{width:794,height:1123}});
try{await capture.setContent(srcDoc!);await expect.poll(()=>capture.evaluate(()=>document.documentElement.dataset.cvPreviewReady)).toBe('true');await capture.screenshot({path:path.join(root,`preview-${i}.png`)});}finally{await capture.close();}
}
await fs.writeFile(path.join(root,'measurements.json'),JSON.stringify({browserVersion:browser.version(),fixtures:[{fixture:path.basename(root),settings:variant.settings,previewPages:pages,measured}]},null,2));
const independent=await browser.newContext();
try{
expect((await independent.request.post(`${api}/auth/login`,{data:{email:'e2e@example.test',password:'E2ePassword123!'}})).status()).toBe(200);
const persisted=await(await independent.request.get(`${api}/career/profile`)).json();
expect(persisted.profile).toEqual(source.profile);expect(persisted.revision).toBe(source.revision);
expect((await(await independent.request.get(`${api}/cv/variants/${variant.id}`)).json()).version).toBe(variant.version);
}finally{await independent.close();}
});
}