import fs from 'node:fs/promises'; import path from 'node:path'; import assert from 'node:assert/strict'; import { pathToFileURL } from 'node:url'; const out = path.resolve('docs/workshop-argument-quality-20261111'); const raw = JSON.parse(await fs.readFile(path.join(out,'example-fixture.json'),'utf8')); const empty = structuredClone(raw.artifact[0]); empty.objectId='no-quotes-preview';empty.report.evidence={}; empty.report.chapters=[{id:'product-proposal',title:'旧报告未保存引文',sections:[{kind:'para',text:'这是旧报告正文,不是原始引文。'}]}]; raw.artifact.push(empty); const origin='http://127.0.0.1:4181'; const {chromium}=await import(pathToFileURL(path.resolve('work/workshop-manual-tools/node_modules/playwright/index.mjs')).href); const browser=await chromium.launch({headless:true,executablePath:'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe'}); const checks=[],errors=[],actions=[]; try { const context=await browser.newContext({viewport:{width:1280,height:900}}); await context.route('**/*',route=>{ const request=route.request(),url=new URL(request.url()); if(url.pathname==='/api/functions'||url.pathname==='/__dev/member-groups') { const body=request.postDataJSON();actions.push(body?.path); const data=body?.path==='/ksfGetPublicBoard'?raw:body?.path==='/ksfGetPublicMemberGroups'?{schema:'ksf.workshop-member-groups.v1',groups:[],members:[]}:null; if(!data){errors.push('Unexpected API action: '+body?.path);return route.abort();} return route.fulfill({contentType:'application/json',body:JSON.stringify({result:{ok:true,data}})}); } if(url.origin===origin&&request.method()==='GET')return route.continue(); errors.push('Unexpected network request: '+url.origin+url.pathname);return route.abort(); }); const page=await context.newPage();page.on('pageerror',error=>errors.push(error.message)); for(const width of [1280,390]) { await page.setViewportSize({width,height:1000}); await page.goto(origin+'/report/argument-preview?mode=sample'); await page.locator('[data-evidence-reading]').waitFor(); assert.equal(await page.locator('.finding .evidence-excerpt').count(),3); assert.equal(await page.locator('[data-evidence-reading] .evidence-excerpt').count(),3); const allText=await page.locator('[data-evidence-reading]').innerText(); for(const evidence of Object.values(raw.artifact[0].report.evidence))assert.ok(allText.includes(evidence.quotes[0].text)); for(const link of await page.locator('.evidence-caption a').all())assert.match(await link.getAttribute('href'),/^https:\/\//); assert.equal(await page.locator('.evidence-more').evaluate(el=>el.open),false); const dimensions=await page.evaluate(()=>({width:innerWidth,scrollWidth:document.documentElement.scrollWidth})); assert.ok(dimensions.scrollWidth<=dimensions.width,JSON.stringify(dimensions)); await page.locator('.finding').first().scrollIntoViewIfNeeded(); await page.screenshot({path:path.join(out,'report-evidence-'+width+'.png')}); checks.push(width+'px:关键判断旁原句、全部依据、原始链接和窄屏布局'); } await page.locator('.finding .evidence-caption button').first().click(); await page.locator('kf-drawer .panel').waitFor(); const sourceText=await page.locator('kf-drawer .panel').innerText(); assert.ok(sourceText.replace(/\s/g,'').includes('R05京东三味六碗商品评价'),sourceText); await page.keyboard.press('Escape'); await page.locator('kf-drawer .panel').waitFor({state:'hidden'}); checks.push('原句旁来源记录按钮可打开对应记录并返回'); await page.locator('.finding [data-anchor]').first().click(); const drawer=page.locator('kf-drawer .panel'); await drawer.waitFor(); assert.ok((await drawer.innerText()).includes(raw.artifact[0].report.evidence.R05.quotes[0].text)); checks.push('证据链仍可打开,对应原句与正文一致'); await page.goto(origin+'/report/no-quotes-preview?mode=sample'); await page.locator('[data-evidence-reading]').waitFor(); assert.equal(await page.locator('.evidence-excerpt').count(),0); assert.ok((await page.locator('[data-evidence-reading]').innerText()).includes('尚未保存')); checks.push('无引文旧报告明确缺失,不由摘要补造证言'); await page.goto(origin+'/docs/workshop-argument-quality-20261111/'+encodeURIComponent('报告内容修订示例.html')); for(const source of raw.source)assert.ok((await page.locator('body').innerText()).includes(source.rawText)); for(const link of await page.locator('a[href$=".json"]').all()) { const response=await page.request.get(new URL(await link.getAttribute('href'),page.url()).href); assert.equal(response.status(),200); } await page.screenshot({path:path.join(out,'content-example-390.png'),fullPage:true}); checks.push('独立HTML示例保留逐字原句,本地原记录链接可读取'); assert.deepEqual(errors,[]); assert.ok(actions.every(action=>['/ksfGetPublicBoard','/ksfGetPublicMemberGroups'].includes(action))); await fs.writeFile(path.join(out,'browser-verification.json'),JSON.stringify({ok:true,checks,errors,actions,fixtureOnly:true,productionWrites:0},null,2)+'\n'); console.log(JSON.stringify({ok:true,checks,errors,fixtureOnly:true,productionWrites:0})); } finally {await browser.close();}