const assert=require('node:assert/strict');
const fs=require('node:fs');
const path=require('node:path');
const {chromium}=require('C:/kefu/admin-web/node_modules/playwright');
const out=__dirname;
const customTag=String(process.env.REVIEW_PANEL_TAG||'custom').replace(/[^a-zA-Z0-9_-]/g,'_');
const targets=process.env.REVIEW_PANEL_ROOT ? [[customTag,process.env.REVIEW_PANEL_ROOT.replace(/\\/g,'/')]] : [['production','C:/wechat_rpa'],['source','C:/kefu/wechat_rpa']];
const reportName=process.env.REVIEW_PANEL_ROOT ? `panel-dom-results-${customTag}.json` : 'panel-dom-results.json';
const results=[];
let checks=0;
function check(value,message){assert(value,message);checks++;}
(async()=>{
const browser=await chromium.launch({headless:true,executablePath:'C:/Program Files/Google/Chrome/Application/chrome.exe'});
try{
for(const [tag,root] of targets){
for(const scale of [1,2]){
const context=await browser.newContext({viewport:scale===1?{width:1280,height:1000}:{width:960,height:850},deviceScaleFactor:scale});
await context.route(/^https?:/,route=>route.abort());
await context.addInitScript(()=>{
window.bridgeCalls=[];
window.qtBridge={
refreshReviewAssistant:()=>window.bridgeCalls.push(['refresh']),
chooseReviewAssistant:()=>window.bridgeCalls.push(['choose']),
setReviewAssistantEnabled:value=>window.bridgeCalls.push(['toggle',value]),
};
});
const page=await context.newPage();const errors=[];
page.on('pageerror',error=>errors.push(String(error)));
await page.goto('file:///'+root+'/assets/ui/zhenyang-ai-console.html');
const draw=async(recipient,overrides={})=>{
await page.evaluate(({recipient,overrides})=>{
const settings={supported:true,ready:true,enabled:false,busy:false,
accountId:'168800000000001',accountName:'合成测试托管账号',recipient,
preview:'示例内容,仅用于离线界面检查。',...overrides};
window.applyState({view:'automation',demo:false,ready:true,automation:{transport:'visual',reviewAssistant:settings}});
render('automation');document.documentElement.classList.add('embedded');window.fit();
},{recipient,overrides});
await page.evaluate(()=>new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve))));
};
const fullId='M:168885000012345678901234567890';
for(const [kind,recipient] of [
['named',{id:fullId,name:'微信联系人',displayName:'陈医生(合成测试)',nameKnown:true,label:'私人微信'}],
['unknown',{id:fullId,name:'微信联系人',displayName:'微信联系人',nameKnown:false,label:'私人微信'}],
]){
await draw(recipient);
const card=page.locator('.assistant-recipient-card');
check(await card.getAttribute('aria-label')==='已配置收件人',tag+' configured recipient label');
check(await card.locator('.assistant-recipient-id').innerText()===fullId,tag+' full ID rendered');
const expected=kind==='named'?'陈医生(合成测试)':'昵称未读取,请核对联系编号';
check(await card.locator('.assistant-recipient-name').innerText()===expected,tag+' nickname is explicit');
const metrics=await card.evaluate(el=>{
const id=el.querySelector('.assistant-recipient-id'),name=el.querySelector('.assistant-recipient-name');
const b=el.getBoundingClientRect(),n=name.getBoundingClientRect(),i=id.getBoundingClientRect();
return {card:{left:b.left,right:b.right,width:b.width},nameFont:getComputedStyle(name).fontSize,
idFont:getComputedStyle(id).fontSize,nameInside:n.left>=b.left&&n.right<=b.right,
idInside:i.left>=b.left&&i.right<=b.right,scrollWidth:el.scrollWidth,clientWidth:el.clientWidth,
whiteSpace:getComputedStyle(id).whiteSpace,overflowWrap:getComputedStyle(id).overflowWrap};
});
check(metrics.nameInside&&metrics.idInside&&metrics.scrollWidth<=metrics.clientWidth+1,tag+' card has no clipping');
check(parseFloat(metrics.nameFont)>parseFloat(metrics.idFont),tag+' nickname has stronger hierarchy');
check(metrics.whiteSpace==='normal'&&metrics.overflowWrap==='anywhere',tag+' ID can wrap');
check(await page.locator('[data-action=review-assistant-toggle]').getAttribute('aria-checked')==='false',tag+' enabled state unchanged');
const screenshot=path.join(out,`recipient-${tag}-${kind}-${scale}x.png`);
await card.locator('..').screenshot({path:screenshot});
results.push({tag,scale,kind,screenshot,metrics});
}
// Legacy objects never turn a generic contact placeholder into an apparent name.
await draw({id:fullId,name:'微信联系人'});
check((await page.locator('.assistant-recipient-name').innerText()).includes('昵称未读取'),tag+' legacy generic name is unknown');
await draw({id:fullId,name:'旧字段真实姓名'});
check(await page.locator('.assistant-recipient-name').innerText()==='旧字段真实姓名',tag+' legacy genuine name remains visible');
await draw({id:fullId,displayName:'陈医生',nameKnown:false});
check((await page.locator('.assistant-recipient-name').innerText()).includes('昵称未读取'),tag+' explicit unknown wins');
await draw({});
check(await page.locator('.assistant-recipient-card').getAttribute('aria-label')==='未配置收件人',tag+' empty selection distinct');
check(await page.locator('[data-action=review-assistant-toggle]').isDisabled(),tag+' empty selection cannot enable');
// Use literal hostile strings as data only; neither elements nor attributes may be created.
const hostile='
& "助理"';
const longId='M:'+'0123456789'.repeat(20)+'