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();} }); }