caterium-app/tests/app.spec.mjs
pavlov346346-source ce6fa974aa
Caterium v17.6.3 — fix developer gate and SaaS settings
Fix platform developer gate during workspace loading and prevent SaaS click events from reaching Developer Console as DOM buttons. Add v17.6.3 regression tests and PWA cache bump.
2026-09-07 19:31:00 +03:00

99 lines
6.9 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import { test, expect } from '@playwright/test';
async function loadModule(page,file,globalName){
await page.goto('/index.html', { waitUntil:'domcontentloaded' });
const content=fs.readFileSync(path.join(process.cwd(),'public','core',file),'utf8');
await page.addScriptTag({content});
expect(await page.evaluate(name=>Boolean(window[name]),globalName)).toBeTruthy();
}
test('legacy localStorage payload cannot execute XSS', async ({ page }) => {
const pageErrors=[]; page.on('pageerror',e=>pageErrors.push(String(e)));
await page.addInitScript(() => {
localStorage.setItem('sunBoxes', JSON.stringify([{id:'1',name:'<img src=x onerror="window.__cateriumXss=1">',price:100,photo:'javascript:alert(1)',ingredients:[['<svg onload="window.__cateriumXss=2">',1,'шт.']]}]));
localStorage.setItem('sunOrders', JSON.stringify([{id:1,event:'<img src=x onerror="window.__cateriumXss=3">',date:'2026-09-07',time:'12:00',address:'<svg onload="window.__cateriumXss=4">',status:'Новый',lines:[{id:'1',qty:1}]}]));
});
await page.goto('/index.html', { waitUntil:'domcontentloaded' }); await page.waitForTimeout(300);
expect(await page.evaluate(()=>window.__cateriumXss)).toBeUndefined();
expect(pageErrors.filter(x=>!x.includes('supabase'))).toEqual([]);
});
test('safe insert helper handles foreign reference node', async ({ page }) => {
await loadModule(page,'sun-safe.js','SunSafe');
const result=await page.evaluate(()=>{const a=document.createElement('div'),b=document.createElement('div'),n=document.createElement('span'),foreign=document.createElement('i');a.appendChild(foreign);document.body.append(a,b);try{window.SunSafe.insertBefore(b,n,foreign);return {ok:true,parent:n.parentNode===b}}catch(e){return {ok:false,error:String(e)}}});
expect(result).toEqual({ok:true,parent:true});
});
test('shared PDF engine produces A4 PDF blob', async ({ page }) => {
await loadModule(page,'pdf-engine.js','SunPdfEngine');
const result=await page.evaluate(async()=>{const fake=new Uint8Array([255,216,255,217]);const blob=window.SunPdfEngine.fromJpegs([{width:1000,height:1414,bytes:fake}]);const head=new TextDecoder().decode(new Uint8Array(await blob.arrayBuffer()).slice(0,8));return {type:blob.type,size:blob.size,head,w:window.SunPdfEngine.PAGE_W,h:window.SunPdfEngine.PAGE_H}});
expect(result.type).toBe('application/pdf'); expect(result.size).toBeGreaterThan(150); expect(result.head.startsWith('%PDF-1.4')).toBeTruthy(); expect(result.w/result.h).toBeCloseTo(1/Math.sqrt(2),3);
});
test('chat photo guard compresses a large camera image', async ({ page }) => {
await loadModule(page,'performance.js','SunAttachmentGuard');
const result=await page.evaluate(async()=>{
const c=document.createElement('canvas');c.width=3000;c.height=2200;const x=c.getContext('2d');
const g=x.createLinearGradient(0,0,c.width,c.height);g.addColorStop(0,'#172a3a');g.addColorStop(.5,'#d79a4a');g.addColorStop(1,'#f1e3ca');x.fillStyle=g;x.fillRect(0,0,c.width,c.height);
for(let i=0;i<1200;i++){x.fillStyle=`rgba(${i%255},${(i*7)%255},${(i*13)%255},.55)`;x.fillRect((i*31)%3000,(i*47)%2200,90,55)}
const blob=await new Promise(r=>c.toBlob(r,'image/jpeg',1));const input=new File([blob],'camera-photo.jpg',{type:'image/jpeg'});const out=await window.SunAttachmentGuard.prepareAttachment(input);const bm=await createImageBitmap(out.file);const data={original:input.size,size:out.file.size,width:bm.width,height:bm.height,type:out.file.type,name:out.file.name};bm.close();return data;
});
expect(Math.max(result.width,result.height)).toBeLessThanOrEqual(2048);expect(result.size).toBeLessThan(result.original);expect(result.size).toBeLessThanOrEqual(15*1024*1024);expect(result.type).toBe('image/jpeg');expect(result.name.endsWith('.jpg')).toBeTruthy();
});
test('v17.6.2 operations UX boots with menu and route features', async ({ page }) => {
await page.goto('/index.html', { waitUntil:'domcontentloaded' });
await page.evaluate(()=>{
if(!document.querySelector('header nav')){
const header=document.createElement('header');
const nav=document.createElement('nav');
header.appendChild(nav);
document.body.appendChild(header);
}
window.SunCloudV2={
getSupportMode:()=>null,
isSupportMode:()=>false,
hasPermission:()=>true,
getSession:()=>({user:{id:'e2e'}})
};
window.editBox=()=>{};
});
const content=fs.readFileSync(path.join(process.cwd(),'public','core','ops-ux-v1762.js'),'utf8');
await page.addScriptTag({content});
await page.waitForFunction(()=>Boolean(window.SunOpsUXV1762),null,{timeout:5000});
const checks=await page.evaluate(()=>window.SunOpsUXV1762.checks());
expect(checks.version).toBe('17.6.2');
expect(checks.menuPage).toBeTruthy();
expect(checks.routeStartKey).toBe('sunRouteBaseV1');
expect(checks.routeOrderPopup).toBeTruthy();
expect(checks.calendarMore).toBeTruthy();
expect(await page.locator('header nav button', {hasText:'Меню'}).count()).toBeGreaterThan(0);
});
test('v17.6.3 developer gate bypasses workspace loading and SaaS click is safe', async ({ page }) => {
await page.goto('/index.html', { waitUntil:'domcontentloaded' });
await page.evaluate(()=>{
document.body.innerHTML='<div id="sunCloudAuthGateV3"><div class="sun-cloud-auth-card"><h2>Загружаю рабочую базу</h2></div></div><button type="button" data-saas-admin>Управление SaaS</button>';
window.__devOpenArgs=[];
window.SunCloudV2={
getSession:()=>({user:{id:'dev',email:'developer@example.com'}}),
getWorkspace:()=>null,
status:()=>({membershipsLoading:true,membershipsLoaded:false}),
signOut:()=>{}
};
window.SunDeveloperV22={
open:arg=>{window.__devOpenArgs.push(arg===null?'null':arg?.constructor?.name||typeof arg)},
isPlatformAdmin:()=>true,
checkPlatformAdmin:async()=>true
};
});
const content=fs.readFileSync(path.join(process.cwd(),'public','core','hotfix-v1763.js'),'utf8');
await page.addScriptTag({content});
await page.waitForFunction(()=>document.querySelector('[data-open-dev-v1763]')&&Boolean(window.SunHotfixV1763),null,{timeout:5000});
expect(await page.locator('#sunCloudAuthGateV3 h2').textContent()).toBe('Аккаунт разработчика');
await page.locator('[data-saas-admin]').click();
await page.locator('[data-open-dev-v1763]').click();
const args=await page.evaluate(()=>window.__devOpenArgs);
expect(args).toEqual(['null','null']);
});
test('mobile body does not overflow viewport', async ({ page }, testInfo) => {
test.skip(testInfo.project.name!=='mobile-390'); await page.goto('/index.html', { waitUntil:'domcontentloaded' }); await page.waitForTimeout(500);
const dims=await page.evaluate(()=>({innerWidth,scrollWidth:document.documentElement.scrollWidth,bodyWidth:document.body.scrollWidth}));
expect(dims.scrollWidth).toBeLessThanOrEqual(dims.innerWidth+2); expect(dims.bodyWidth).toBeLessThanOrEqual(dims.innerWidth+2);
});