Files
kefu/deploy/knowledge-v3-field-fix/qa/browser.mjs
T
2026-09-21 10:34:06 +08:00

70 lines
5.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createRequire } from 'node:module';
import { readFileSync, writeFileSync } from 'node:fs';
const require=createRequire('C:/kefu/admin-web/package.json');
const { chromium }=require('playwright');
const root='C:/kefu/deploy/knowledge-v3-field-fix/qa';
const fixture=JSON.parse(readFileSync(`${root}/fixture.json`,'utf8'));
const browser=await chromium.launch({executablePath:'C:/Program Files/Google/Chrome/Application/chrome.exe',headless:true,args:['--no-proxy-server']});
const context=await browser.newContext({viewport:{width:1600,height:1040},locale:'zh-CN'});
await context.route('**/*', route=>new URL(route.request().url()).hostname==='127.0.0.1'?route.continue():route.abort());
const page=await context.newPage(); page.setDefaultTimeout(20000);
const errors=[]; const checks=[];
page.on('pageerror',error=>errors.push(error.message));
const origin='http://127.0.0.1:18868';
const login=await fetch(`${origin}/api/v2/auth/login`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:'admin',password:'KnowledgeQA@2026'})}).then(r=>r.json());
const api=async(path)=>{
const r=await fetch(`${origin}/api/v2/knowledge/${path}?account_id=${fixture.account_id}`,{headers:{Authorization:`Bearer ${login.access_token}`}});
if(!r.ok) throw new Error(`API ${path}: ${r.status}`); return r.json();
};
const dialog=(title)=>page.getByRole('dialog').filter({has:page.getByText(title,{exact:true})});
const select=async(container,label,value)=>{
await container.locator('.ant-form-item').filter({has:page.locator('label').getByText(label,{exact:true})}).locator('.ant-select-selector').click();
await page.locator('.ant-select-dropdown:visible').getByText(value,{exact:true}).click();
};
try {
await page.goto(`${origin}/#/auth/login`);
await page.getByPlaceholder('用户名',{exact:true}).fill('admin');
await page.getByPlaceholder('密码',{exact:true}).fill('KnowledgeQA@2026');
await page.getByRole('button',{name:'login',exact:true}).click();
await page.waitForURL(url=>!url.hash.includes('/auth/login'));
await page.goto(`${origin}/#/knowledge/center`);
await page.locator('.ant-card-extra .ant-select-selector').click();
await page.locator('.ant-select-dropdown:visible').getByText('流程验收客服(QA001)',{exact:true}).click();
await page.getByRole('button',{name:'从聊天整理知识',exact:true}).click();
const wizard=dialog('从聊天整理知识'); await wizard.waitFor();
await select(wizard,'整理方式','模型整理:脱敏后调用指定模型');
await select(wizard,'整理模型','alpha · openai(默认)');
await wizard.locator('.ant-form-item').filter({hasText:'单次等待时间(秒)'}).locator('input').fill('60');
await wizard.getByRole('button',{name:'预览整理范围',exact:true}).click();
await wizard.getByRole('checkbox',{name:'确认来源账号由客服使用,所选聊天允许用于知识整理。'}).check();
await page.screenshot({path:`${root}/01-model-selection.png`,fullPage:true});
await wizard.getByRole('button',{name:'确认创建任务',exact:true}).click();
await wizard.waitFor({state:'hidden'});
await page.getByText('所选范围已读取完成',{exact:true}).waitFor();
const jobs=await api('jobs'); const job=jobs.items[0];
if(job.status!=='completed' || job.created_items!==3 || job.model_calls!==3 || job.options.model_format_fallbacks!==2) throw new Error('Malformed response interrupted job or lost results');
await page.getByText('模型格式异常 2 条,已保留原文待审。',{exact:false}).waitFor();
await page.screenshot({path:`${root}/02-completed-with-format-warnings.png`,fullPage:true});
checks.push('缺失辅助字段、空答案和超长答案均未卡住任务;显示格式异常数量');
await page.getByRole('tab',{name:'知识与审核',exact:true}).click();
const data=await api('items');
const fallback=data.items.find(item=>item.flags.some(flag=>flag.includes('模型格式异常')));
if(!fallback || fallback.conditions) throw new Error('Fallback review gate missing');
await page.getByRole('button',{name:fallback.title,exact:true}).click();
const editor=dialog('知识审核与来源');
await editor.getByText('已核对客服身份、事实、适用条件和脱敏;内容可作为通用经验。涉及医疗内容已完成专业审核。',{exact:true}).click();
if(await editor.getByRole('button',{name:'审核通过',exact:true}).isEnabled()) throw new Error('Missing conditions can be approved');
if(!(await editor.innerText()).includes('不能直接发布')) throw new Error('Fallback warning not shown');
const detail=await api('items/'+fallback.id);
if(detail.sources.length!==2) throw new Error('Original evidence lost');
await page.screenshot({path:`${root}/03-original-draft-review.png`,fullPage:true});
checks.push('异常输出保留原文和完整来源,明确警示,未补条件不能审核');
if(errors.length) throw new Error(JSON.stringify(errors));
writeFileSync(`${root}/result.json`,JSON.stringify({passed:true,checks,pageErrors:errors},null,2));
console.log(JSON.stringify({passed:true,checks,pageErrors:errors}));
} catch(error) {
await page.screenshot({path:`${root}/failure.png`,fullPage:true});
writeFileSync(`${root}/failure.txt`,String(error)+'\n'+await page.locator('body').innerText());
throw error;
} finally { await browser.close(); }