返回 DeepSeek-Reasonix
protocol-recovery.mjs
根目录 / desktop / frontend / bench / protocol-recovery.mjs
1 import { createServer } from 'vite';
2 import { chromium } from 'playwright';
3 import path from 'node:path';
4 import { fileURLToPath } from 'node:url';
5 const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
6 const server=await createServer({root,server:{host:'127.0.0.1',port:0},logLevel:'error'});
7 await server.listen();
8 let browser;
9 try {
10 browser=await chromium.launch({headless:true,channel:'chrome'});
11 const page=await browser.newPage({viewport:{width:1280,height:850}});
12 const errors=[];page.on('pageerror',e=>errors.push(String(e)));
13 const url=server.resolvedUrls.local[0];
14 page.setDefaultTimeout(15000);
15 await page.goto(url+'?mock=fresh',{waitUntil:'domcontentloaded'});
16 const later=page.getByRole('button',{name:/稍后设置|Set up later/});
17 try { await later.waitFor({state:'visible',timeout:3000}); await later.click(); } catch {}
18 const composer=page.locator('#composer-input');await composer.waitFor({state:'visible',timeout:60000});
19 const send=async text=>{await composer.fill(text);await page.locator('.composer__btn--send').click();};
20 await send('/mock-protocol-recovery'); console.log('sent mock failure');
21 const recover=page.getByRole('button',{name:/从有效历史恢复|Recover from valid history/});
22 await recover.waitFor({state:'visible'});
23 if(await recover.count()!==1)throw Error('duplicate recovery cards');
24 await recover.click();
25 const stop=page.locator('.composer__btn--stop');await stop.waitFor({state:'visible'});await stop.click();
26 await stop.waitFor({state:'hidden'});
27 await page.waitForTimeout(1700);
28 if(await page.locator('.tool__search-summary').count())throw Error('late response after cancellation committed');
29 await send('/mock-protocol-recovery');await recover.waitFor({state:'visible'});await recover.click();
30 const note=page.getByText(/搜索已完成,供应商未提供可用的结构化来源|Search completed; the provider/).first();
31 await note.waitFor({state:'visible',timeout:15000});
32 const summary=page.getByText('搜索摘要仍然可读。');
33 const header=page.locator('.tool__head').last();
34 if(await header.count()===0)await page.locator('.turn-collapse__label').last().click();
35 if(await header.getAttribute('aria-expanded')==='false')await header.click();
36 await summary.waitFor({state:'visible'});
37 await summary.scrollIntoViewIfNeeded();
38 await page.waitForTimeout(300);
39 const summaryBox=await summary.boundingBox();
40 const composerBox=await page.locator('.composer').boundingBox();
41 if(!summaryBox || !composerBox || summaryBox.y+summaryBox.height>composerBox.y || summaryBox.y<0)throw Error('search summary is outside the readable transcript viewport');
42 await page.screenshot({path:'/tmp/reasonix-protocol-recovery-browser.png',fullPage:true});
43 if(errors.length)throw Error(errors.join('\n'));
44 console.log('PASS: one recovery button, click, stop, no late search, source-free successful search and summary; no page errors');
45 } catch(error) { console.error(error); if(browser){const p=browser.contexts()[0]?.pages()[0]; if(p){await p.screenshot({path:'/tmp/reasonix-protocol-browser-failure.png'});console.error((await p.locator('body').innerText()).slice(-4500));}} throw error; } finally {await browser?.close();await server.close();}
46
46 lines Plain Text