diff --git a/tests/ui-stability.spec.mjs b/tests/ui-stability.spec.mjs index b2d2418..7a5dc84 100644 --- a/tests/ui-stability.spec.mjs +++ b/tests/ui-stability.spec.mjs @@ -120,30 +120,41 @@ test('a delayed menu editor cannot move back into a section that was already lef test('full app stays stable across settings, menu, calendar and delayed background refreshes',async({page})=>{ const errors=[];page.on('pageerror',e=>errors.push(String(e))); + const workspaceId='22222222-2222-4222-8222-222222222222',servedRpcs=new Set(); const expires=Math.floor(Date.now()/1000)+3600,user={id:'11111111-1111-4111-8111-111111111111',aud:'authenticated',role:'authenticated',email:'ui-test@example.invalid',app_metadata:{provider:'email'},user_metadata:{}}; const token=[{alg:'HS256',typ:'JWT'},{sub:user.id,role:'authenticated',aud:'authenticated',exp:expires,iat:expires-3600,aal:'aal1'},'test'].map(x=>typeof x==='string'?x:Buffer.from(JSON.stringify(x)).toString('base64url')).join('.'); const session={access_token:token,refresh_token:'test-refresh',token_type:'bearer',expires_in:3600,expires_at:expires,user}; - await page.addInitScript(value=>localStorage.setItem('sb-usfjwhztqoopzzfmfbis-auth-token',JSON.stringify(value)),session); + await page.addInitScript(({session,workspaceId})=>{ + localStorage.setItem('sb-usfjwhztqoopzzfmfbis-auth-token',JSON.stringify(session)); + // This test starts inside an existing workspace. Onboarding/reload is covered + // separately; do not abort a background request by switching tenants here. + localStorage.setItem('sunCloudV2Config',JSON.stringify({workspaceId,localWorkspaceId:workspaceId,tenantStorageReady:true})); + },{session,workspaceId}); await page.route('https://**',r=>r.abort()); const handle=async route=>{ const url=new URL(route.request().url()),path=url.searchParams.get('__caterium_path')||url.pathname; - const headers={'access-control-allow-origin':'*','access-control-allow-headers':'authorization,apikey,content-type,x-client-info','access-control-allow-methods':'GET,POST,OPTIONS'}; + const requestHeaders=await route.request().allHeaders(); + // Test-only CORS response: acknowledge the headers actually sent by the SDK, + // including metadata headers added by new SDK versions and browser preflights. + const headers={'access-control-allow-origin':requestHeaders.origin||'http://127.0.0.1:4173','access-control-allow-credentials':'true','access-control-allow-headers':requestHeaders['access-control-request-headers']||Object.keys(requestHeaders).join(','),'access-control-allow-methods':'GET,POST,OPTIONS'}; if(route.request().method()==='OPTIONS')return route.fulfill({status:204,headers}); let data=null; if(path==='/auth/v1/user')data=user; - else if(path.endsWith('/sun_my_workspaces'))data=[{id:'22222222-2222-4222-8222-222222222222',name:'Тестовая компания',role:'admin',is_active:true,permissions:{}}]; + else if(path.endsWith('/sun_my_workspaces'))data=[{id:workspaceId,name:'Тестовая компания',role:'admin',is_active:true,permissions:{}}]; else if(path.endsWith('/sun_is_platform_admin'))data=false; + else if(path.endsWith('/caterium_trial_demo_status'))data={canInstall:false,canUpgrade:false}; else if(path.endsWith('/sun_subscription_snapshot'))data={plan_id:'full',plan_name:'Полный',status:'active',access_mode:'full',features:Object.fromEntries('orders calendar clients catalog_view catalog_edit production shopping stock routes mailings money stats_basic stats_advanced team suppliers print settings branding client_offers offer_templates backups audit users_manage'.split(' ').map(k=>[k,true]))}; else if(path.endsWith('/sun_fetch_app_state'))data=url.searchParams.get('select')==='revision'?{revision:1}:[{revision:1,payload:{format:'sun-cloud-v2',version:2,storage:{sunOrders:{t:'j',v:[]},sunBoxes:{t:'j',v:[]}}}}]; - return route.fulfill({contentType:'application/json',body:JSON.stringify(data),headers}); + await route.fulfill({contentType:'application/json',body:JSON.stringify(data),headers}); + if(path.includes('/rpc/'))servedRpcs.add(path.split('/').pop()); }; - // The real transport can use either configured proxy while the first document - // reloads after workspace discovery. Both belong to this local server fixture. + // Both configured transports stay within the mocked backend. await page.route('**://api.caterium.ru/**',handle); await page.route('**/api/index.php?**',handle); await page.goto('/index.html',{waitUntil:'domcontentloaded'}); await expect(page.locator('body > header')).toBeVisible({timeout:20000}); await page.waitForFunction(()=>window.SunOpsUXV1762&&window.SunSaaSV16?.getSnapshot()); + await expect.poll(()=>servedRpcs.has('caterium_trial_demo_status')).toBe(true); const nav=page.locator('header nav'); for(const name of ['Настройки','Меню','Календарь','Заказы','Настройки']){ await nav.getByRole('button',{name,exact:true}).click();