| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 |
- const assert = require('node:assert/strict');
- const fs = require('node:fs');
- const path = require('node:path');
- const {chromium} = require('playwright');
- const root = path.resolve(__dirname, '../html');
- (async () => {
- const browser = await chromium.launch({headless:true,channel:'msedge'});
- try {
- const page = await browser.newPage();
- await page.route('**/*', route => {
- const url = new URL(route.request().url());
- if (url.pathname === '/api/data-quality') return route.fulfill({json:{data_version:'v',total:0,datasets:{}}});
- const file = path.join(root, url.pathname === '/' ? 'index.html' : url.pathname);
- if (!file.startsWith(root) || !fs.existsSync(file)) return route.fulfill({status:404,body:''});
- return route.fulfill({body:fs.readFileSync(file),contentType:file.endsWith('.js') ? 'application/javascript' : file.endsWith('.css') ? 'text/css' : 'text/html'});
- });
- await page.goto('http://qa.test/index.html');
- await page.evaluate(() => {
- appendMessage('assistant', '# 标题\n\n## 统计\n\n- **重点**\n- 第二项\n\n| 项目 | 人数 |\n| --- | --- |\n| 示例 | 2 |\n\n```text\n# 代码原样\n```');
- appendMessage('user', '# 用户输入');
- });
- assert.equal(await page.locator('.markdown-body h1').innerText(),'标题');
- assert.equal(await page.locator('.markdown-body h2').innerText(),'统计');
- assert.equal(await page.locator('.markdown-body li').count(),2);
- assert.equal(await page.locator('.markdown-body table td').count(),2);
- assert.equal(await page.locator('pre code').innerText(),'# 代码原样\n');
- assert.equal(await page.locator('.message.user').innerText(),'# 用户输入');
- await page.evaluate(() => appendMessage('assistant','<img src=x onerror="window.bad=1"><script>window.bad=1</script>[危险](javascript:alert(1))\n\n[来源](https://example.com)'));
- assert.equal(await page.locator('.markdown-body script, .markdown-body img, .markdown-body [onerror], .markdown-body a[href^="javascript:"]').count(),0);
- assert.equal(await page.evaluate(() => window.bad),undefined);
- // Send encoded SSE bytes in small chunks, including splits inside Chinese characters.
- await page.evaluate(async () => {
- const events = [['answer_chunk',{text:'# 分'}],['answer_chunk',{text:'块标题\n\n**粗'}],['answer_chunk',{text:'体**'}],['done',{answer:'# 最终标题\n\n**完整答案**'}]];
- const bytes = new TextEncoder().encode(events.map(([event,data]) => `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`).join(''));
- const original = window.fetch;
- window.fetch = async () => new Response(new ReadableStream({start(c) {for(let i=0;i<bytes.length;i+=7)c.enqueue(bytes.slice(i,i+7)); c.close();}}));
- try { await askStream('测试',true,false); } finally { window.fetch=original; }
- });
- assert.equal(await page.locator('.answer-text h1').innerText(),'最终标题');
- assert.equal(await page.locator('.answer-text strong').innerText(),'完整答案');
- await page.evaluate(async () => {
- const original=window.fetch;
- window.fetch=async () => new Response(JSON.stringify({status:'ok',answer:'### 确认后的回答\n\n1. 第一项'}));
- try { await resumeThread('确认'); } finally { window.fetch=original; }
- });
- assert.equal(await page.locator('.markdown-body h3').innerText(),'确认后的回答');
- await page.evaluate(() => showConfirm('请选择', ['1. 水电工', '2. 维修电工', '都不是'], 'clarify_value'));
- await page.locator('#confirmOptions .option-chip').nth(0).click();
- await page.locator('#confirmOptions .option-chip').nth(1).click();
- assert.equal(await page.locator('#confirmOptions .option-chip.selected').count(),2);
- assert.equal(await page.locator('#confirmReply').inputValue(),'1. 水电工,2. 维修电工');
- await page.locator('#confirmOptions .option-chip').nth(2).click();
- assert.equal(await page.locator('#confirmOptions .option-chip.selected').count(),1);
- assert.equal(await page.locator('#confirmReply').inputValue(),'都不是');
- await page.evaluate(() => showConfirm('普通确认', ['确认', '修改'], 'confirm_plan'));
- await page.locator('#confirmOptions .option-chip').nth(0).click();
- await page.locator('#confirmOptions .option-chip').nth(1).click();
- assert.equal(await page.locator('#confirmReply').inputValue(),'修改');
- await page.setViewportSize({width:390,height:844});
- assert.ok(await page.locator('.markdown-body h1').first().isVisible());
- console.log('PASS: headings, lists, table, code, user text, XSS filtering, SSE UTF-8 chunks/final replacement, resume, candidate multi-select and mobile');
- } finally { await browser.close(); }
- })().catch(e => {console.error(e);process.exitCode=1;});
|