fix(ui): wait for rendered browser state during CI navigation
This commit is contained in:
@@ -17,10 +17,12 @@ export async function verifyBrandUI(base: string) {
|
||||
const browser = await chromium.launch({ executablePath: process.env.CHROMIUM_PATH || '/usr/bin/chromium', headless: true, args: ['--no-sandbox', '--disable-dev-shm-usage'] });
|
||||
try {
|
||||
const page = await browser.newPage({ viewport: { width: 1440, height: 960 } });
|
||||
page.setDefaultTimeout(60000);page.setDefaultNavigationTimeout(60000);
|
||||
const visit = async (path: string, theme: string) => {
|
||||
await page.goto(base + path, { waitUntil: 'commit' });
|
||||
await page.locator('button[data-theme-ready="true"]').first().waitFor();
|
||||
await page.waitForFunction(expected => document.documentElement.dataset.theme === expected, theme);
|
||||
console.log('Brand UI route ready:', path, theme);
|
||||
};
|
||||
const background = (selector: string) => page.locator(selector).first().evaluate(el => getComputedStyle(el).backgroundColor);
|
||||
await visit('/', 'home-light');
|
||||
|
||||
@@ -21,8 +21,8 @@ export async function verifyCanvasAgentUI(base:string){
|
||||
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));
|
||||
mark('open landing');await page.goto(base+'/',{waitUntil:'domcontentloaded'});
|
||||
page.setDefaultTimeout(60000);page.setDefaultNavigationTimeout(60000);page.on('pageerror',e=>errors.push(e.message));
|
||||
mark('open landing');await page.goto(base+'/',{waitUntil:'commit'});
|
||||
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]);
|
||||
@@ -97,7 +97,7 @@ export async function verifyCanvasAgentUI(base:string){
|
||||
assert.equal((await fetch(base+root,{headers:{authorization:second.authorization}})).status,404);
|
||||
await page.setViewportSize({width:390,height:844});
|
||||
const bounds=await panel.boundingBox();assert.ok(bounds&&bounds.x>=0&&bounds.x+bounds.width<=390,'Agent panel stays within the mobile viewport');
|
||||
await page.reload({waitUntil:'domcontentloaded'});
|
||||
await page.reload({waitUntil:'commit'});
|
||||
await page.getByRole('link',{name:email,exact:true,includeHidden:true}).waitFor({state:'attached'});
|
||||
await page.getByRole('button',{name:'Agent',exact:true}).click();
|
||||
const reopened=page.getByRole('region',{name:'Agent 连接',exact:true});
|
||||
|
||||
@@ -35,7 +35,7 @@ export async function verifyGuidancePublishing(base:string){
|
||||
// The document discovery list hydrates from the real API; the homepage is the product landing page.
|
||||
const browser=await chromium.launch({executablePath:process.env.CHROMIUM_PATH||'/usr/bin/chromium',headless:true,args:['--no-sandbox','--disable-dev-shm-usage']});
|
||||
try{
|
||||
const page=await browser.newPage();
|
||||
const page=await browser.newPage();page.setDefaultTimeout(60000);page.setDefaultNavigationTimeout(60000);
|
||||
const navigationDiagnostics:string[]=[],inflight=new Set<string>();
|
||||
const record=(value:string)=>{navigationDiagnostics.push(value.slice(0,1800));if(navigationDiagnostics.length>24)navigationDiagnostics.shift();};
|
||||
page.on('pageerror',error=>record('pageerror: '+error.message));
|
||||
@@ -46,30 +46,30 @@ export async function verifyGuidancePublishing(base:string){
|
||||
page.on('response',response=>{const path=new URL(response.url()).pathname;if(path.startsWith('/api/')||path.includes('/_next/data/'))void response.text().then(body=>record(`data ${response.status()} ${path}: ${body.slice(0,1400)}`)).catch(()=>{});else if(response.status()>=400||response.request().resourceType()==='script')record(`resource ${response.status()} ${path}`);});
|
||||
try{
|
||||
const link=`a[href="/documents/${httpDoc.id}?revision=1"]`;
|
||||
const navigation=await page.goto(base+'/documents',{waitUntil:'domcontentloaded'});record('directory HTTP '+navigation?.status());
|
||||
const navigation=await page.goto(base+'/documents',{waitUntil:'commit'});record('directory HTTP '+navigation?.status());
|
||||
await page.locator(link).waitFor({state:'visible'});assert.equal(await page.locator(link).getByRole('heading').textContent(),httpDoc.title);
|
||||
// Published matches must not inherit the examples' empty state; submitted filters survive reload.
|
||||
const search=page.getByRole('textbox',{name:'搜索文档'});
|
||||
await search.fill(httpDoc.title);await search.press('Enter');
|
||||
await page.waitForURL(url=>url.searchParams.get('q')===httpDoc.title);
|
||||
await page.waitForURL(url=>url.searchParams.get('q')===httpDoc.title,{waitUntil:'commit'});
|
||||
await page.getByRole('status').filter({hasText:'找到 2 篇文档'}).waitFor();
|
||||
assert.equal(await page.getByText('没有匹配文档',{exact:false}).count(),0);
|
||||
await page.reload({waitUntil:'domcontentloaded'});
|
||||
await page.reload({waitUntil:'commit'});
|
||||
await page.getByRole('status').filter({hasText:'找到 2 篇文档'}).waitFor();
|
||||
assert.equal(await search.inputValue(),httpDoc.title);
|
||||
await page.getByRole('tab',{name:/事实依据/}).click();
|
||||
await page.waitForURL(url=>url.searchParams.get('kind')==='evidence');
|
||||
await page.waitForURL(url=>url.searchParams.get('kind')==='evidence',{waitUntil:'commit'});
|
||||
await page.getByRole('heading',{name:'没有匹配文档',exact:true}).waitFor();
|
||||
await page.getByRole('button',{name:'清除所有筛选'}).click();
|
||||
await page.waitForURL(url=>url.search==='');
|
||||
await page.waitForURL(url=>url.search==='',{waitUntil:'commit'});
|
||||
await page.locator(link).waitFor({state:'visible'});
|
||||
assert.equal(await search.inputValue(),'');
|
||||
await page.getByRole('navigation',{name:'主导航',exact:true}).getByRole('link',{name:'社区文档',exact:true}).click();await page.locator(link).waitFor({state:'visible'});
|
||||
await page.locator(link).click();
|
||||
await page.waitForURL(url=>url.pathname==='/documents/'+httpDoc.id&&url.searchParams.get('revision')==='1');
|
||||
await page.waitForURL(url=>url.pathname==='/documents/'+httpDoc.id&&url.searchParams.get('revision')==='1',{waitUntil:'commit'});
|
||||
await page.getByRole('heading',{name:httpDoc.title,exact:true,level:1}).waitFor();
|
||||
assert.equal(await page.locator('h1').textContent(),httpDoc.title);
|
||||
await page.goto(base+'/write',{waitUntil:'domcontentloaded'});assert.equal(await page.locator('h1').textContent(),'发布独立文档');
|
||||
await page.goto(base+'/write',{waitUntil:'commit'});await page.getByRole('heading',{name:'发布独立文档',exact:true,level:1}).waitFor();assert.equal(await page.locator('h1').textContent(),'发布独立文档');
|
||||
}catch(error){console.error('Document navigation diagnostics',JSON.stringify({url:page.url(),headings:await page.locator('h1').allTextContents(),body:(await page.locator('body').innerText()).slice(0,2200),inflight:[...inflight].map(url=>new URL(url).pathname),events:navigationDiagnostics}));throw error;}
|
||||
await page.close();
|
||||
await verifyGuidanceWorkspaceUI(base,browser);
|
||||
|
||||
@@ -89,7 +89,8 @@ export async function verifyGuidanceWorkspaceUI(base: string, browser: Browser)
|
||||
route.fulfill({ json: { document: authored } }),
|
||||
);
|
||||
const page = await context.newPage();
|
||||
page.setDefaultTimeout(10000);
|
||||
page.setDefaultTimeout(60000);
|
||||
page.setDefaultNavigationTimeout(60000);
|
||||
page.on("pageerror", (error) => errors.push(error.message));
|
||||
page.on("crash", () => errors.push("Browser renderer crashed"));
|
||||
const pending = new Set<string>(), network: string[] = [];
|
||||
@@ -99,7 +100,7 @@ export async function verifyGuidanceWorkspaceUI(base: string, browser: Browser)
|
||||
async function navigate(url: string, reload = false) {
|
||||
const previousSessionReplies = sessionReplies;
|
||||
try {
|
||||
const navigation = await (reload ? page.reload({waitUntil:"domcontentloaded"}) : page.goto(url,{waitUntil:"domcontentloaded"}));
|
||||
const navigation = await (reload ? page.reload({waitUntil:"commit"}) : page.goto(url,{waitUntil:"commit"}));
|
||||
assert.ok(navigation?.ok(), "Navigation returns a successful document");
|
||||
await page.getByText(signedIn ? "editor@example.com" : "登录你的账户",{exact:true}).first().waitFor({state:"attached"});
|
||||
assert.ok(sessionReplies > previousSessionReplies, "The new document requests its account fixture and renders the resulting account state");
|
||||
|
||||
@@ -23,6 +23,13 @@ import {verifyFlatNoteMigration} from './flat-note-migration';
|
||||
import {staticEntries} from '../src/lib/explore';
|
||||
import {publishLearningSeries} from '../src/lib/publish-learning-series';
|
||||
|
||||
// Browser acceptance gets its own deadline; it must not consume the existing business-flow budget.
|
||||
test('Endoleap icons, persisted themes and artwork isolation in the real browser', {timeout:300000,skip:!process.env.PROMPT_TEST_BASE_URL}, async()=>{
|
||||
assert.equal(process.env.PROMPT_INTEGRATION_TEST,'1');
|
||||
const base=process.env.PROMPT_TEST_BASE_URL;assert.ok(base);
|
||||
await verifyBrandUI(base);
|
||||
});
|
||||
|
||||
test('real PostgreSQL + S3 notes, flat tags, resource sharing and video processing', {timeout:600000}, async()=>{
|
||||
assert.equal(process.env.PROMPT_INTEGRATION_TEST,'1','Run only against an explicitly isolated test database');
|
||||
const socket=createServer();socket.listen(0,'127.0.0.1');await once(socket,'listening');const address=socket.address();assert.ok(address&&typeof address!=='string');const port=address.port;await new Promise<void>(r=>socket.close(()=>r()));
|
||||
@@ -34,7 +41,7 @@ test('real PostgreSQL + S3 notes, flat tags, resource sharing and video processi
|
||||
try{
|
||||
let ready=false;for(let i=0;i<60;i++){try{if((await fetch(base+'/api/health')).ok){ready=true;break;}}catch{}await setTimeout(250);}assert.ok(ready);
|
||||
for(const route of ['/','/images','/videos','/audio','/articles','/motion','/library/import','/tools/doodle','/tools/skills','/tools/skills/street-people','/tools/skills/shadow-puppet'])assert.equal((await fetch(base+route)).status,200,route);
|
||||
await verifyBrandUI(base);
|
||||
if(!external)await verifyBrandUI(base);
|
||||
await verifyEmailAccounts();
|
||||
await verifyCanvasWorkspace(base);
|
||||
if(process.env.VERIFY_CANVAS_UI==='1')await verifyCanvasAgentUI(base);
|
||||
|
||||
Reference in New Issue
Block a user