import {test,expect} from '@playwright/test'; async function fixture(page){ await page.route('**/index.html',r=>r.fulfill({contentType:'text/html',body:'
'})); await page.goto('/index.html'); } async function logo(page,color='#146b3d'){ return page.evaluate(async color=>{ const c=document.createElement('canvas');c.width=48;c.height=24;const x=c.getContext('2d');x.fillStyle=color;x.fillRect(8,4,32,16); const blob=await new Promise(r=>c.toBlob(r,'image/png')); return window.CateriumBranding.saveLogo(new File([blob],'logo.png',{type:'image/png'})); },color); } test('company logo preserves transparency, persists and clears without changing product brand',async({page})=>{ await fixture(page);const data=await logo(page); await page.evaluate(()=>window.CateriumBranding.saveName('Кухня <Гости> & Ко')); const result=await page.evaluate(async()=>{ const b=window.CateriumBranding,img=new Image();img.src=b.documentImage();await img.decode();const c=document.createElement('canvas');c.width=48;c.height=24;const x=c.getContext('2d');x.drawImage(img,0,0); return {name:b.documentName(),escaped:b.nameHTML(),alpha:x.getImageData(0,0,1,1).data[3],body:document.querySelector('header').textContent}; }); expect(result).toEqual({name:'Кухня <Гости> & Ко',escaped:'Кухня <Гости> & Ко',alpha:0,body:'Caterium'}); await page.reload();expect(await page.evaluate(()=>window.CateriumBranding.documentLogo())).toBe(data); await page.evaluate(()=>{localStorage.setItem('sunCompanyProfileV1',JSON.stringify({logo:window.CateriumBranding.documentLogo()}));window.CateriumBranding.clearLogo()}); expect(await page.evaluate(()=>window.CateriumBranding.documentLogo())).toBe(''); expect(await page.evaluate(async()=>{const i=new Image();i.src=window.CateriumBranding.documentImage();await i.decode();return i.naturalWidth})).toBe(1); }); test('exclusive sidebar is server assigned, resets on account changes and ignores stale replies',async({page})=>{ await fixture(page); await page.evaluate(async()=>{ window.__user='owner';window.__calls=[]; window.SunCloudV2={getSession:()=>window.__user?{user:{id:window.__user}}:null,getClient:()=>({rpc:async name=>{window.__calls.push(name);return {data:{variant:window.__user==='owner'?'solnce':'caterium'}}}})}; await window.CateriumBranding.refreshSidebar(); }); await expect(page.locator('header .brand')).toHaveText('Солнце Кейтеринг'); await logo(page);await page.evaluate(()=>window.CateriumBranding.saveName('Другой бренд документов')); await expect(page.locator('header .brand')).toHaveText('Солнце Кейтеринг'); await page.evaluate(async()=>{window.__user='employee';await window.CateriumBranding.refreshSidebar()}); await expect(page.locator('header .brand')).toHaveText('Caterium'); await page.evaluate(async()=>{ window.__user='owner';window.SunCloudV2.getClient=()=>({rpc:()=>new Promise(r=>window.__reply=r)}); const pending=window.CateriumBranding.refreshSidebar();window.__user=null;window.CateriumBranding.resetSidebar();window.__reply({data:{variant:'solnce'}});await pending; }); await expect(page.locator('header .brand')).toHaveText('Caterium'); }); test('document branding follows workspace storage without leaking the previous company',async({page})=>{ await fixture(page);const first=await logo(page); await page.evaluate(()=>window.CateriumBranding.saveName('Компания А')); const saved=await page.evaluate(()=>({...localStorage})); await page.evaluate(()=>{localStorage.clear();localStorage.setItem('sunCompanyProfileV1',JSON.stringify({name:'Компания Б'}));window.dispatchEvent(new Event('sun:cloud-state-applied'))}); expect(await page.evaluate(()=>window.CateriumBranding.identity())).toMatchObject({name:'Компания Б',logo:''}); const second=await logo(page,'#801599');expect(second).not.toBe(first); await page.evaluate(saved=>{localStorage.clear();for(const [k,v] of Object.entries(saved))localStorage.setItem(k,v);window.dispatchEvent(new Event('sun:cloud-state-applied'))},saved); expect(await page.evaluate(()=>window.CateriumBranding.identity())).toMatchObject({name:'Компания А',logo:first}); }); test('every classic and signature PDF uses the supplied company logo and no legacy company text',async({page})=>{ test.setTimeout(60000);await fixture(page); for(const module of ['classic-offer-pdf-v1767.js','signature-offer-pdf-v18.js'])await page.addScriptTag({url:'/core/'+module}); const data=await logo(page); const result=await page.evaluate(async data=>{ const texts=[],images=[],proto=CanvasRenderingContext2D.prototype,fill=proto.fillText,draw=proto.drawImage; proto.fillText=function(t,...a){texts.push(String(t));return fill.call(this,t,...a)}; proto.drawImage=function(i,...a){images.push(i.src);return draw.call(this,i,...a)}; const b={brandName:'Компания Лист',brandCity:'Казань',logo:data,client:'Анна',event:'Обед',items:[],pricing:{total:15000},guests:10}; const counts=[]; try{for(const api of [window.SunClassicOfferPDFV1767,window.SunSignatureOfferPDFV18])for(const id of api.CLASSIC_IDS||api.SIGNATURE_IDS){const before=images.length;await api.renderCover({...b,offerTemplateId:id},id);counts.push({id,logo:images.slice(before).includes(data)})}}finally{proto.fillText=fill;proto.drawImage=draw} return {counts,legacy:texts.filter(t=>/солнц|SUN \/ CATERING|САНКТ-ПЕТЕРБУРГ/i.test(t))}; },data); expect(result.counts).toHaveLength(18);expect(result.counts.filter(x=>!x.logo)).toEqual([]);expect(result.legacy).toEqual([]); }); test('real order blank, receipt and preparation share uploaded company identity',async({page})=>{ await page.goto('/index.html',{waitUntil:'domcontentloaded'}); await page.waitForFunction(()=>Boolean(window.sunShowOrderBlankV5)); const data=await logo(page); await page.evaluate(()=>window.CateriumBranding.saveName('Компания <Лист>')); const result=await page.evaluate(()=>{ const out=[];for(const fn of ['sunShowOrderBlankV5','sunShowConfiguredReceipt','openPreview']){ window[fn]();const root=document.querySelector('#preview .dialog');out.push({fn,text:root.textContent,logos:[...root.querySelectorAll('.sun-doc-brand img,.prep-doc-brand img')].map(x=>x.src),unsafe:!!root.querySelector('лист')}); }return out; }); for(const row of result){expect(row.text).toContain('Компания <Лист>');expect(row.text).not.toMatch(/Солнце|Моя компания/);expect(row.logos).toEqual([data]);expect(row.unsafe).toBe(false)} await page.evaluate(()=>document.querySelector('header nav button[data-nav-label="Настройки"]').click()); await expect(page.locator('#sunPdfBrandCard')).toHaveAttribute('data-settings-tab','documents'); await page.locator('#sunPdfBrandLogoFile').setInputFiles({name:'company.png',mimeType:'image/png',buffer:Buffer.from(data.split(',')[1],'base64')}); await expect.poll(()=>page.evaluate(()=>window.CateriumBranding.documentLogo())).toBe(data); }); test('catalog, route sheet and full PDF pages retain the company brand',async({page})=>{ test.setTimeout(60000); await page.goto('/index.html',{waitUntil:'domcontentloaded'}); await page.waitForFunction(()=>Boolean(window.sunClientOfferDebugPdfPages)); const data=await logo(page); const result=await page.evaluate(async data=>{ const brand=window.CateriumBranding;brand.saveName('Лист');window.print=()=>{}; const texts=[],images=[],proto=CanvasRenderingContext2D.prototype,fill=proto.fillText,draw=proto.drawImage; proto.fillText=function(t,...a){texts.push(String(t));return fill.call(this,t,...a)}; proto.drawImage=function(i,...a){images.push(i.src);return draw.call(this,i,...a)}; const allIds=[...window.SunClassicOfferPDFV1767.CLASSIC_IDS,...window.SunClassicOfferPDFV1767.ARCHIVE_IDS,...window.SunSignatureOfferPDFV18.SIGNATURE_IDS],counts=[]; try{ for(const id of allIds){const pages=await window.sunClientOfferDebugPdfPages({offerTemplateId:id,brandName:'Лист',brandCity:'Казань',logo:data,event:'Обед',items:[],pricing:{total:1000},guests:10});counts.push(pages.length)} await window.sunCatalogDebugPdfPages(0,1); }finally{proto.fillText=fill;proto.drawImage=draw} await window.sunPrintLiveCatalog(); const catalog=document.querySelector('#sunLiveCatalogPrintRoot');const catalogOK=catalog.textContent.includes('ЛИСТ')&&[...catalog.querySelectorAll('.sun-live-catalog-logo')].every(i=>i.src===data); window.sunOpenRoutes();await document.getElementById('routePrint').onclick(); const route=document.querySelector('#preview .dialog'); const html=window.sunClientOfferDebugHtml({brandName:'Лист',brandCity:'Казань',logo:'',items:[],pricing:{},event:'Обед'}); return {counts,catalogOK,routeName:route.querySelector('.sun-custom-brand')?.textContent,routeLogo:route.querySelector('.sun-custom-brand img')?.src,logoDrawn:images.includes(data),legacy:texts.filter(t=>/солнц|SUN \/ CATERING|САНКТ-ПЕТЕРБУРГ/i.test(t)),neutralHeader:html.includes('Лист')}; },data); expect(result.counts).toHaveLength(21);expect(result.counts.every(n=>n>0)).toBe(true); expect(result.catalogOK).toBe(true);expect(result.routeName).toContain('Лист');expect(result.routeLogo).toBe(data); expect(result.logoDrawn).toBe(true);expect(result.legacy).toEqual([]);expect(result.neutralHeader).toBe(true); });