import fs from 'node:fs'; import {test,expect} from '@playwright/test'; test('retired device PIN cannot lock an authenticated account or replace email login',async({page})=>{ await page.addInitScript(()=>{localStorage.setItem('sunCloudQuickPinV3:pin-test','legacy-device-pin');localStorage.setItem('unrelated-preference','keep')}); await page.route('https://**',r=>r.abort()); await page.goto('/index.html',{waitUntil:'domcontentloaded'}); await expect(page.locator('#sunGateEmailV3')).toBeVisible(); await expect(page.locator('#sunGatePasswordV3')).toBeVisible(); await page.evaluate(()=>{ window.SunCloudV2={getSession:()=>({user:{id:'pin-test',email:'pin@example.invalid'}}),getWorkspace:()=>({id:'pin-company',role:'admin'}),status:()=>({connected:true,signedIn:true,membershipsLoaded:true}),hasPermission:()=>true,getClient:()=>null}; window.dispatchEvent(new Event('sun:cloud-permissions-changed')); }); await expect(page.locator('body > header')).toBeVisible(); await page.getByRole('button',{name:'Настройки',exact:true}).click(); await expect(page.locator('#sunCloudPinLockV3,#sunCloudQuickPinCardV3,#sunSetQuickPinV3')).toHaveCount(0); expect(await page.evaluate(()=>({pin:localStorage.getItem('sunCloudQuickPinV3:pin-test'),preference:localStorage.getItem('unrelated-preference')}))).toEqual({pin:null,preference:'keep'}); }); test('anonymous login stays closed with an old local-mode preference and an unavailable backend',async({page})=>{ await page.addInitScript(()=>localStorage.setItem('sunLocalOnlyModeV1','1')); await page.route('**://api.caterium.ru/**',r=>r.abort()); await page.route('**://*.supabase.co/**',r=>r.abort()); await page.goto('/index.html',{waitUntil:'domcontentloaded'}); await expect(page.locator('#sunGateEmailV3')).toBeVisible(); await expect(page.locator('#sunGatePasswordV3')).toBeVisible(); await expect(page.locator('#sunGateLocalOnlyV1')).toHaveCount(0); await expect(page.locator('body > header')).toBeHidden(); expect(await page.evaluate(()=>localStorage.getItem('sunLocalOnlyModeV1'))).toBeNull(); expect(await page.evaluate(()=>{localStorage.setItem('sunLocalOnlyModeV1','1');return window.SunAdminRBACV3.hasPermission('orders.create')})).toBe(false); }); test('workspace changes isolate client caches and ignore delayed responses from the previous company',async({page})=>{ await page.route('**/index.html',r=>r.fulfill({contentType:'text/html',body:''})); await page.goto('/index.html'); await page.addScriptTag({url:'/core/sun-safe.js'}); await page.addScriptTag({url:'/core/cloud-transport.js'}); const runtime=fs.readFileSync('public/app-runtime.js','utf8'); let cloud=runtime.slice(runtime.indexOf('/* ===== MODULE: cloud-sync-v2.js'),runtime.indexOf('/* ===== MODULE: admin-rbac-v3.js')); cloud=cloud.replace('async function boot(){','async function boot(){return;'); cloud=cloud.replace('window.SunCloudV2={',`window.SunCloudV2={ testSwitch:async id=>{workspace=id?{id}:null;return switchTenantLocal(id)}, testRefresh:refreshRuntime,`); await page.addScriptTag({content:'var orders=[];var boxes=[];'+cloud}); expect(await page.evaluate(async()=>{ localStorage.setItem('cateriumClientsV1772','{"old-anonymous":{"name":"Old anonymous customer"}}'); localStorage.setItem('sunBoxes','[{"id":"old-anonymous-box"}]'); await window.SunCloudV2.testSwitch('company-a'); return {clients:localStorage.getItem('cateriumClientsV1772'),catalog:localStorage.getItem('sunBoxes')}; })).toEqual({clients:null,catalog:null}); await page.evaluate(async()=>{ await window.SunCloudV2.adoptCurrentLocalAsWorkspace('company-a'); await window.SunCloudV2.testSwitch('company-a'); orders=[{id:1,contact:'Клиент A',phone:'79990000001',address:'Адрес A',total:1234}]; boxes=[{id:'a',name:'Бокс A',price:1234,photo:'private-photo-a'}]; localStorage.setItem('sunOrders',JSON.stringify(orders));localStorage.setItem('sunBoxes',JSON.stringify(boxes)); localStorage.setItem('sunClientCommunicationV1','{}'); localStorage.setItem('cateriumClientsV1772',JSON.stringify({'p:79990000002':{key:'p:79990000002',name:'Ручной клиент A',phone:'79990000002',latestAddress:'Ещё адрес A'}})); localStorage.setItem('sunTelegramImportArchiveV1','{"messages":[{"text":"Private A"}]}'); window.pendingRpc=[];window.rpcCalls=[]; window.SunCloudV2.getSession=()=>({user:{id:'test-user'}}); window.SunCloudV2.hasPermission=()=>true; window.SunCloudV2.getClient=()=>({rpc:(name,args)=>{window.rpcCalls.push({name,workspace:args.p_workspace});return new Promise(resolve=>window.pendingRpc.push(()=>resolve({data:name.includes('snapshot')?[{client_key:'old-server-a',name:'Сервер A'}]:{version:99}})))}}); }); await page.addScriptTag({url:'/core/data-layer-v1773.js'}); const result=await page.evaluate(async()=>{ const c=window.SunCloudV2,d=window.CateriumDataV1773; const refresh=d.clients.refreshServer({force:true}),push=d.clients.pushAll(); await c.testSwitch('company-b');c.testRefresh(); window.pendingRpc.splice(0).forEach(resolve=>resolve()); const staleResults=[await refresh,await push]; await new Promise(resolve=>setTimeout(resolve,300)); const empty={clients:d.clients.list().length,orders:d.orders.list().length,boxes:d.catalog.list().length,archive:localStorage.getItem('sunTelegramImportArchiveV1'),cache:localStorage.getItem('cateriumClientsV1772'),server:localStorage.getItem('cateriumClientsServerV1773')}; await c.testSwitch('company-a');c.testRefresh(); return {empty,staleResults,calls:window.rpcCalls,restored:{clients:d.clients.list().map(c=>c.name).sort(),orders:d.orders.list().length,boxes:d.catalog.list(),archive:localStorage.getItem('sunTelegramImportArchiveV1')}}; }); expect(result.empty).toEqual({clients:0,orders:0,boxes:0,archive:null,cache:null,server:null}); expect(result.staleResults[0].status).toBe('stale'); expect(result.staleResults[1]).toHaveLength(1); expect(result.calls.every(c=>c.workspace==='company-a')).toBe(true); expect(result.restored.clients).toEqual(['Клиент A','Ручной клиент A']); expect(result.restored.orders).toBe(1); expect(result.restored.boxes[0].photo).toBe('private-photo-a'); expect(result.restored.archive).toContain('Private A'); }); test('a developer with a personal company opens the ordinary app before choosing developer tools',async({page})=>{ await page.route('**://api.caterium.ru/**',r=>r.abort()); await page.route('**://*.supabase.co/**',r=>r.abort()); await page.goto('/index.html',{waitUntil:'domcontentloaded'}); await expect(page.locator('#sunGateEmailV3')).toBeVisible(); await page.evaluate(()=>{ window.SunCloudV2={getSession:()=>({user:{id:'developer-test',email:'developer@example.invalid'}}),getWorkspace:()=>({id:'developer-workspace',name:'Тестовая компания',role:'admin'}),status:()=>({connected:true,signedIn:true,membershipsLoaded:true}),hasPermission:()=>true,getClient:()=>null}; window.dispatchEvent(new Event('sun:cloud-permissions-changed')); }); await expect(page.locator('#sunCloudAuthGateV3')).toHaveCount(0); await expect(page.locator('body > header')).toBeVisible(); await expect(page.locator('#sun-developer-console-v22.on')).toHaveCount(0); expect(await page.evaluate(()=>({orders:orders.length,boxes:boxes.length}))).toEqual({orders:0,boxes:0}); });