Add a question-circle SVG mask to Help using the existing sidebar pseudo-element. Preserve shared icon geometry, theme colors and navigation behavior. Add full-app icon regression and correct the previous loading-recovery fixture. Production promotion remains gated by full main QA.
85 lines
4.8 KiB
JavaScript
85 lines
4.8 KiB
JavaScript
import {test,expect} from '@playwright/test';
|
|
|
|
const gateSelector='#sunCloudAuthGateV3';
|
|
|
|
async function startWorkspaceLookup(page,{withoutDecoration=false}={}){
|
|
await page.route('https://**',route=>route.abort());
|
|
if(withoutDecoration)await page.route('**/core/login-signature-v1776.js*',route=>route.abort());
|
|
await page.addInitScript(()=>{
|
|
localStorage.setItem('sunCloudV2Config',JSON.stringify({workspaceId:'startup-company',localWorkspaceId:'startup-company',tenantStorageReady:true,autoSync:false}));
|
|
const recovered=sessionStorage.getItem('testWorkspaceRecovered')==='1';
|
|
window.workspaceLookupCount=0;
|
|
window.supabase={createClient:()=>({
|
|
auth:{onAuthStateChange:()=>({data:{subscription:{unsubscribe(){}}}}),getSession:()=>recovered?Promise.resolve({data:{session:{user:{id:'startup-user',email:'test@example.invalid'}}},error:null}):new Promise(resolve=>{window.finishSessionRestore=resolve}),getUser:async()=>({data:{user:null}})},
|
|
rpc:async name=>{
|
|
if(name==='sun_my_workspaces'){
|
|
window.workspaceLookupCount++;
|
|
if(recovered)return {data:[{id:'startup-company',name:'Test company',role:'admin',is_active:true}],error:null};
|
|
return new Promise(resolve=>{window.finishWorkspaceLookup=resolve});
|
|
}
|
|
return {data:null,error:null};
|
|
},
|
|
channel:()=>({on(){return this},subscribe(){return this}}),removeChannel:()=>{}
|
|
})};
|
|
});
|
|
await page.goto('/index.html',{waitUntil:'domcontentloaded'});
|
|
await page.waitForFunction(()=>Boolean(window.finishSessionRestore));
|
|
await page.evaluate(()=>finishSessionRestore({data:{session:{user:{id:'startup-user',email:'test@example.invalid'}}},error:null}));
|
|
await page.waitForFunction(()=>window.workspaceLookupCount===1);
|
|
await expect(page.locator(gateSelector)).toHaveAttribute('data-auth-state','loading');
|
|
}
|
|
|
|
async function expectQuietLoading(page){
|
|
const gate=page.locator(gateSelector);
|
|
await expect(gate.locator('h2')).toHaveText('Загружаю рабочую базу');
|
|
await expect(gate.locator('button:visible,input:visible,a:visible')).toHaveCount(0);
|
|
await expect(gate.locator('.sun-cloud-auth-actions')).toBeHidden();
|
|
await expect(gate.locator('.sun-cloud-auth-error')).toBeHidden();
|
|
await expect(gate.locator('.hint:visible')).toHaveCount(0);
|
|
await expect(page.locator('body > header')).toBeHidden();
|
|
expect(await gate.locator('.sun-cloud-auth-brand').evaluate(el=>getComputedStyle(el,'::after').animationName)).toBe('caterium-workspace-loading');
|
|
}
|
|
|
|
async function resolveWorkspace(page){
|
|
await page.evaluate(()=>finishWorkspaceLookup({data:[{id:'startup-company',name:'Test company',role:'admin',is_active:true}],error:null}));
|
|
await expect(page.locator(gateSelector)).toHaveCount(0);
|
|
await expect(page.locator('body > header')).toBeVisible();
|
|
}
|
|
|
|
for(const withoutDecoration of [false,true]){
|
|
test(`workspace loading has no recovery controls and opens automatically (decoration blocked: ${withoutDecoration})`,async({page})=>{
|
|
await startWorkspaceLookup(page,{withoutDecoration});
|
|
await expectQuietLoading(page);
|
|
await page.emulateMedia({reducedMotion:'reduce'});
|
|
expect(await page.locator(`${gateSelector} .sun-cloud-auth-brand`).evaluate(el=>getComputedStyle(el,'::after').animationName)).toBe('none');
|
|
await resolveWorkspace(page);
|
|
});
|
|
}
|
|
|
|
test('failed workspace lookup retains recovery controls and retry returns to quiet loading',async({page})=>{
|
|
await startWorkspaceLookup(page);
|
|
await expectQuietLoading(page);
|
|
await page.evaluate(()=>finishWorkspaceLookup({data:null,error:{message:'Workspace lookup failed',code:'TEST_NETWORK'}}));
|
|
const gate=page.locator(gateSelector);
|
|
await expect(gate).toHaveAttribute('data-auth-state','error');
|
|
await expect(page.locator('#sunGateRetryWorkspaceV3')).toBeVisible();
|
|
await expect(page.locator('#sunGateSignOutV3')).toBeVisible();
|
|
await expect(page.locator('#sunGateErrorV3')).toBeVisible();
|
|
// Error markup intentionally has no .sun-cloud-auth-brand loading wrapper.
|
|
expect(await gate.evaluate(el=>[...el.querySelectorAll('*')].some(node=>getComputedStyle(node,'::after').animationName==='caterium-workspace-loading'))).toBe(false);
|
|
await page.locator('#sunGateRetryWorkspaceV3').click();
|
|
await page.waitForFunction(()=>window.workspaceLookupCount===2);
|
|
await expect(gate).toHaveAttribute('data-auth-state','loading');
|
|
await expectQuietLoading(page);
|
|
// A successful recovery deliberately reloads the document. Restore the mock
|
|
// session/workspace on that new document rather than pausing it a second time.
|
|
const reloaded=page.waitForEvent('framenavigated',frame=>frame===page.mainFrame());
|
|
await page.evaluate(()=>{
|
|
sessionStorage.setItem('testWorkspaceRecovered','1');
|
|
finishWorkspaceLookup({data:[{id:'startup-company',name:'Test company',role:'admin',is_active:true}],error:null});
|
|
});
|
|
await reloaded;
|
|
await expect(page.locator(gateSelector)).toHaveCount(0);
|
|
await expect(page.locator('body > header')).toBeVisible();
|
|
});
|