Make workspace loading a calm progress-only screen (#29)
* Make workspace loading a calm progress-only screen * Cover quiet workspace loading and error recovery on desktop and mobile * Run workspace loading regressions in desktop, mobile and WebKit QA
This commit is contained in:
parent
43219c49a1
commit
b2bcb0f4db
@ -18,3 +18,16 @@ body.sun-cloud-auth-required{overflow:hidden!important;background:#f5f0e7!import
|
||||
|
||||
#sunCloudAuthGateV3 .sun-auth-eye{position:absolute;border:0;cursor:pointer;font-size:17px}
|
||||
#sunCloudAuthGateV3 .sun-auth-switch button{cursor:pointer}
|
||||
|
||||
/* Waiting for workspace access is normal progress, not a recovery screen.
|
||||
Keep these rules in the synchronously loaded stylesheet to avoid a button
|
||||
flash even when the optional login decoration script is delayed or blocked.
|
||||
Error, missing-company and signed-out states keep their existing controls. */
|
||||
#sunCloudAuthGateV3[data-auth-state="loading"] .sun-cloud-auth-actions,
|
||||
#sunCloudAuthGateV3[data-auth-state="loading"] .sun-cloud-auth-card>p.hint,
|
||||
#sunCloudAuthGateV3[data-auth-state="loading"] .sun-cloud-auth-brand .hint,
|
||||
#sunCloudAuthGateV3[data-auth-state="loading"] .sun-cloud-auth-error,
|
||||
#sunCloudAuthGateV3[data-auth-state="loading"] .ct-help-login{display:none!important}
|
||||
#sunCloudAuthGateV3[data-auth-state="loading"] .sun-cloud-auth-brand:after{content:'';display:block;width:28px;height:28px;margin-top:30px;border:3px solid rgba(211,164,60,.2);border-top-color:#d3a43c;border-radius:50%;animation:caterium-workspace-loading .9s linear infinite}
|
||||
@keyframes caterium-workspace-loading{to{transform:rotate(360deg)}}
|
||||
@media(prefers-reduced-motion:reduce){#sunCloudAuthGateV3[data-auth-state="loading"] .sun-cloud-auth-brand:after{animation:none}}
|
||||
|
||||
@ -2,13 +2,13 @@ import { defineConfig, devices } from '@playwright/test';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
export default defineConfig({
|
||||
testDir:'.',
|
||||
testMatch:['ui-stability.spec.mjs','help-center.spec.mjs','app.spec.mjs','theme-startup.spec.mjs','company-branding.spec.mjs','order-import.spec.mjs','account-access.spec.mjs','banquet-menu.spec.mjs','calendar-print.spec.mjs','login-recovery.spec.mjs','trial-demo.spec.mjs','proposal-quality.spec.mjs'],
|
||||
testMatch:['ui-stability.spec.mjs','help-center.spec.mjs','app.spec.mjs','theme-startup.spec.mjs','company-branding.spec.mjs','order-import.spec.mjs','account-access.spec.mjs','banquet-menu.spec.mjs','calendar-print.spec.mjs','login-recovery.spec.mjs','workspace-loading.spec.mjs','trial-demo.spec.mjs','proposal-quality.spec.mjs'],
|
||||
timeout:30000,
|
||||
use:{baseURL:'http://127.0.0.1:4173'},
|
||||
webServer:{command:'npx http-server public -p 4173 -c-1',cwd:fileURLToPath(new URL('../',import.meta.url)),port:4173,reuseExistingServer:true},
|
||||
projects:[
|
||||
{name:'iphone-pdf',testMatch:['proposal-quality.spec.mjs'],grep:/all six selections|transparent wide|an actual offer downloads/,use:{...devices['iPhone 13'],serviceWorkers:'block'}},
|
||||
{name:'iphone-webkit',testMatch:['ui-stability.spec.mjs','help-center.spec.mjs','login-recovery.spec.mjs','account-access.spec.mjs','calendar-print.spec.mjs'],use:{...devices['iPhone 13'],serviceWorkers:'block'}},
|
||||
{name:'iphone-webkit',testMatch:['ui-stability.spec.mjs','help-center.spec.mjs','login-recovery.spec.mjs','workspace-loading.spec.mjs','account-access.spec.mjs','calendar-print.spec.mjs'],use:{...devices['iPhone 13'],serviceWorkers:'block'}},
|
||||
{name:'desktop',use:{...devices['Desktop Chrome']}},
|
||||
{name:'mobile-390',use:{viewport:{width:390,height:844},isMobile:true,hasTouch:true}}
|
||||
]
|
||||
|
||||
72
tests/workspace-loading.spec.mjs
Normal file
72
tests/workspace-loading.spec.mjs
Normal file
@ -0,0 +1,72 @@
|
||||
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}));
|
||||
window.workspaceLookupCount=0;
|
||||
window.supabase={createClient:()=>({
|
||||
auth:{onAuthStateChange:()=>({data:{subscription:{unsubscribe(){}}}}),getSession:()=>new Promise(resolve=>{window.finishSessionRestore=resolve}),getUser:async()=>({data:{user:null}})},
|
||||
rpc:async name=>{
|
||||
if(name==='sun_my_workspaces'){
|
||||
window.workspaceLookupCount++;
|
||||
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();
|
||||
expect(await gate.locator('.sun-cloud-auth-brand').evaluate(el=>getComputedStyle(el,'::after').content)).toBe('none');
|
||||
await page.locator('#sunGateRetryWorkspaceV3').click();
|
||||
await page.waitForFunction(()=>window.workspaceLookupCount===2);
|
||||
await expect(gate).toHaveAttribute('data-auth-state','loading');
|
||||
await expectQuietLoading(page);
|
||||
await resolveWorkspace(page);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user