Keep notification-read state personal to user/company, preserve server read-only sections during staff synchronization, and respect separate order-create/edit/delete rights. Add visible mobile header session actions and a sticky logout that survives profile RPC failure; scope asynchronous profile/branding to user and workspace. Targeted browser suites and isolated SQL recovery tests passed in run 35506145405, iPhone screenshots reviewed. Full main QA remains required before production promotion. Workspace branding RPC migration is included but has NOT been applied to production Supabase; older servers retain safe owner-only fallback. No live membership/business-data repair is claimed without identifying the reported employee.
42 lines
3.6 KiB
JavaScript
42 lines
3.6 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs/promises';
|
|
import {createHash} from 'node:crypto';
|
|
import {chromium,expect} from '@playwright/test';
|
|
import {trainingInit} from './training-catalog-fixture.mjs';
|
|
|
|
// Read only public application assets. Use synthetic SDK responses; no live
|
|
// customer credentials, database reads, writes, or session termination.
|
|
const base=new URL(process.env.TIMEWEB_BASE_URL||'https://app.caterium.ru');
|
|
assert.equal(base.protocol,'https:');
|
|
const output='production-ui-results';await fs.mkdir(output,{recursive:true});
|
|
const files=['core/account-center-v1780.js','core/company-branding.js','core/sun-safe.js','app-runtime.js','index.html','service-worker.js'];
|
|
for(const file of files){
|
|
const response=await fetch(new URL(file+'?employee_check='+Date.now(),base),{signal:AbortSignal.timeout(20000),headers:{'Cache-Control':'no-cache'}});
|
|
assert(response.ok,'Published asset unavailable: '+file);
|
|
const actual=Buffer.from(await response.arrayBuffer()),expected=await fs.readFile('public/'+file);
|
|
assert.equal(createHash('sha256').update(actual).digest('hex'),createHash('sha256').update(expected).digest('hex'),'Published revision differs: '+file);
|
|
}
|
|
const browser=await chromium.launch();
|
|
try{
|
|
const context=await browser.newContext({viewport:{width:390,height:844},isMobile:true,hasTouch:true,serviceWorkers:'block'});
|
|
await context.route('**/*',route=>{const req=route.request(),url=new URL(req.url());return req.method()==='GET'&&url.origin===base.origin&&!url.pathname.startsWith('/api/')?route.continue():route.abort()});
|
|
await context.addInitScript(trainingInit);
|
|
const page=await context.newPage();await page.goto(base.href,{waitUntil:'domcontentloaded',timeout:60000});
|
|
await page.waitForFunction(()=>window.SunCloudV2?.getWorkspace()?.id&&window.CateriumAccountCenterV1780);
|
|
await expect(page.locator('#sunCloudAuthGateV3')).toHaveCount(0,{timeout:20000});
|
|
await expect(page.locator('#cateriumMobileLogout')).toBeVisible();await expect(page.locator('#cateriumMobileLogout')).toHaveText('Выйти');
|
|
await expect(page.locator('#cateriumMobileAccountButton')).toBeVisible();
|
|
await page.evaluate(()=>{window.exitChecks=0;SunCloudV2.signOut=async()=>{exitChecks++};const c=SunCloudV2.getClient(),rpc=c.rpc;c.rpc=(n,a)=>n==='caterium_account_snapshot'?Promise.resolve({error:{message:'Synthetic offline'}}):rpc(n,a)});
|
|
await page.screenshot({path:output+'/employee-mobile-header.png'});
|
|
await page.locator('#cateriumMobileAccountButton').click();
|
|
await expect(page.locator('#cateriumAccountCenterBody')).toContainText('Выход из аккаунта доступен');
|
|
await expect(page.locator('#cacHeaderLogout')).toBeVisible();await page.locator('#cacHeaderLogout').click();
|
|
assert.equal(await page.evaluate(()=>exitChecks),1);
|
|
await page.screenshot({path:output+'/employee-profile-offline.png'});
|
|
await page.locator('#cateriumAccountCenter .cac-close').click();await page.locator('#cateriumMobileLogout').click();assert.equal(await page.evaluate(()=>exitChecks),2);
|
|
assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth+1),false);
|
|
await fs.writeFile(output+'/employee-session.json',JSON.stringify({checkedAt:new Date().toISOString(),base:base.href,assetHashes:'match',mobileLogout:'pass',profileRpcFailure:'exit remains usable',backend:'synthetic, blocked',liveMembership:'not inspected',sqlMigration:'not deployed by frontend workflow'},null,2));
|
|
console.log('PASS published employee session: exact assets, visible mobile logout, profile unavailable fallback; synthetic backend only');
|
|
await context.close();
|
|
}finally{await browser.close()}
|