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=()=>{};
boxes.push({id:'brand-catalog-fixture',name:'Бокс компании Лист',price:1200,category:0,photo:'',ingredients:[],composition:[]});
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);
});