Record navigation timing to diagnose canvas integration failures
creative workspace review checks / verify (pull_request) Failing after 19m40s
creative workspace review checks / verify (pull_request) Failing after 19m40s
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import {randomUUID} from 'node:crypto';
|
||||
import {chromium} from 'playwright-core';
|
||||
import {chromium,type Request} from 'playwright-core';
|
||||
import {db} from '../src/lib/db';
|
||||
import {hashPassword,ACCOUNT_COOKIE} from '../src/lib/account-crypto';
|
||||
|
||||
@@ -10,25 +10,33 @@ export async function verifyCanvasAgentUI(base:string){
|
||||
await db().query("INSERT INTO account_users(id,email,password_hash,verified_at) VALUES($1,$2,$3,now())",[owner,email,await hashPassword(password)]);
|
||||
const browser=await chromium.launch({executablePath:process.env.CHROMIUM_PATH||'/usr/bin/chromium',headless:true,args:['--no-sandbox','--disable-dev-shm-usage']});
|
||||
const context=await browser.newContext({viewport:{width:1440,height:1000}}),errors:string[]=[];
|
||||
const page=await context.newPage(),canvasRequests:string[]=[],pending=new Set<string>(),network:string[]=[];
|
||||
const page=await context.newPage(),canvasRequests:string[]=[],pending=new Set<Request>(),network:string[]=[];
|
||||
const started=Date.now(),navigation:{ms:number;event:string;path:string}[]=[];
|
||||
const mark=(event:string)=>{navigation.push({ms:Date.now()-started,event,path:new URL(page.url()).pathname});if(navigation.length>60)navigation.shift()};
|
||||
page.on('framenavigated',frame=>{if(frame===page.mainFrame())mark('framenavigated')});
|
||||
page.on('domcontentloaded',()=>mark('domcontentloaded'));page.on('load',()=>mark('load'));
|
||||
const requestName=(request:{method:()=>string;url:()=>string})=>request.method()+' '+new URL(request.url()).pathname;
|
||||
page.on('request',request=>pending.add(requestName(request)));
|
||||
page.on('requestfinished',request=>pending.delete(requestName(request)));
|
||||
page.on('requestfailed',request=>{pending.delete(requestName(request));network.push(requestName(request)+': '+request.failure()?.errorText)});
|
||||
page.on('request',request=>pending.add(request));
|
||||
page.on('requestfinished',request=>pending.delete(request));
|
||||
page.on('requestfailed',request=>{pending.delete(request);network.push(requestName(request)+': '+request.failure()?.errorText)});
|
||||
page.on('response',response=>{const url=new URL(response.url());if(url.pathname.startsWith('/api/canvases'))canvasRequests.push(`${response.request().method()} ${url.pathname} ${response.status()}`)});
|
||||
try{
|
||||
page.setDefaultTimeout(20000);page.on('pageerror',e=>errors.push(e.message));
|
||||
await page.goto(base+'/',{waitUntil:'domcontentloaded'});
|
||||
mark('open landing');await page.goto(base+'/',{waitUntil:'domcontentloaded'});
|
||||
await page.getByRole('heading',{name:/想法不该停在对话里/,level:1}).waitFor();
|
||||
const accountSession=page.waitForResponse(response=>new URL(response.url()).pathname==='/api/account/session'&&response.request().method()==='GET');
|
||||
const [,sessionResponse]=await Promise.all([page.getByRole('link',{name:'开始创作',exact:true}).click(),accountSession]);
|
||||
assert.equal(sessionResponse.status(),200);assert.equal((await sessionResponse.json()).user,null);
|
||||
await page.getByRole('heading',{name:'欢迎回来',exact:true,level:2}).waitFor();
|
||||
mark('login form ready');
|
||||
await page.getByLabel('邮箱地址',{exact:true}).fill(email);
|
||||
await page.getByLabel('密码',{exact:true}).fill(password);
|
||||
await page.locator('form').getByRole('button',{name:'登录',exact:true}).click();
|
||||
mark('before login click');await page.locator('form').getByRole('button',{name:'登录',exact:true}).click();
|
||||
mark('after login click');
|
||||
await page.waitForURL(url=>url.pathname==='/workspace',{waitUntil:'commit'});
|
||||
mark('workspace URL accepted');
|
||||
await page.getByRole('heading',{name:'今天,让哪个想法成形?',exact:true}).waitFor();
|
||||
mark('workspace rendered');
|
||||
const session=(await context.cookies()).find(c=>c.name===ACCOUNT_COOKIE);assert.ok(session?.httpOnly,'The actual login response establishes an HTTP-only account session');
|
||||
const ownerHeaders={cookie:ACCOUNT_COOKIE+'='+session.value};
|
||||
await page.getByRole('link',{name:'无限画布',exact:true}).click();
|
||||
@@ -97,11 +105,13 @@ export async function verifyCanvasAgentUI(base:string){
|
||||
await reopened.getByRole('button',{name:'载入在线版本',exact:true}).click();
|
||||
await page.locator('[data-node-id="shot"] [data-node-content]').getByText(generated,{exact:true}).waitFor();
|
||||
assert.deepEqual(errors,[]);
|
||||
console.log('Canvas navigation timeline',JSON.stringify(navigation));
|
||||
console.log('Canvas UI dispatched a real stored task, received an external-client fixture result, rotated/revoked access and restored its online document');
|
||||
}catch(error){
|
||||
const statuses=await page.getByRole('status').allTextContents().catch(()=>[]);
|
||||
const headings=await page.getByRole('heading').allTextContents().catch(()=>[]);
|
||||
console.error('Canvas agent UI diagnostics',JSON.stringify({url:page.url(),browserConnected:browser.isConnected(),headings,statuses,pending:[...pending],network:network.slice(-20),canvasRequests:canvasRequests.slice(-20),errors}));
|
||||
const documentState=await page.evaluate(()=>({readyState:document.readyState,navigation:performance.getEntriesByType('navigation').map(entry=>{const timing=entry as PerformanceNavigationTiming;return {type:timing.type,domContentLoadedEventEnd:timing.domContentLoadedEventEnd,loadEventEnd:timing.loadEventEnd}})})).catch(()=>null);
|
||||
console.error('Canvas agent UI diagnostics',JSON.stringify({url:page.url(),browserConnected:browser.isConnected(),headings,statuses,documentState,navigation,pending:[...pending].map(requestName),network:network.slice(-20),canvasRequests:canvasRequests.slice(-20),errors}));
|
||||
throw error;
|
||||
}finally{await context.close();await browser.close()}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user