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

96 lines
7.0 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-v2-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(15000);
const errors=[];
page.on('pageerror',error=>errors.push(error.message));
const login=await fetch('http://127.0.0.1:18866/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,method='GET',body)=>{
const response=await fetch(`http://127.0.0.1:18866/api/v2/knowledge/${path}?account_id=${fixture.account_id}`,{method,headers:{Authorization:`Bearer ${login.access_token}`,'Content-Type':'application/json'},body:body?JSON.stringify(body):undefined});
if (!response.ok) throw new Error(`API ${path}: ${response.status}`);
return response.json();
};
const checks=[];
try {
await page.goto('http://127.0.0.1:18866/#/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('http://127.0.0.1:18866/#/knowledge/center');
await page.getByRole('button',{name:'选择账号开始整理',exact:true}).waitFor();
await page.screenshot({path:`${root}/01-account-guide.png`,fullPage:true});
await page.getByRole('button',{name:'选择账号开始整理',exact:true}).click();
await page.locator('.ant-select-dropdown:visible').getByText('流程验收客服(QA001)',{exact:true}).click();
const wizard=page.getByRole('dialog').filter({has:page.getByText('从聊天整理知识',{exact:true})});
await wizard.waitFor();
await wizard.getByRole('button',{name:'预览整理范围',exact:true}).click();
await wizard.getByText('范围内单聊消息',{exact:false}).waitFor();
const previewText=await wizard.innerText();
if (!previewText.includes('身份明确的有效文本')) throw new Error('Preview not rendered');
await page.screenshot({path:`${root}/02-job-preview.png`,fullPage:true});
checks.push('账号选择引导、来源自动选择、范围预览');
await wizard.getByRole('checkbox',{name:'确认来源账号由客服使用,所选聊天允许用于知识整理。'}).check();
await wizard.getByRole('button',{name:'确认创建任务',exact:true}).click();
await wizard.waitFor({state:'hidden'});
await page.getByText('所选范围已读取完成',{exact:true}).waitFor();
await page.screenshot({path:`${root}/03-job-progress.png`,fullPage:true});
checks.push('创建后台任务、真实分母和完成原因');
await page.getByRole('tab',{name:'知识与审核',exact:true}).click();
const data=await api('items');
if (data.items.length!==3) throw new Error(`Expected 3 drafts, got ${data.items.length}`);
const chosen=data.items.slice(0,2);
for (const item of chosen) {
await page.getByRole('button',{name:item.title,exact:true}).click();
const editor=page.getByRole('dialog').filter({has:page.getByText('知识审核与来源',{exact:true})});
await editor.locator('.ant-form-item').filter({hasText:'适用条件(必填)'}).locator('textarea').fill('适用于一般门店预约咨询;具体安排以客服当天核实为准。');
const saved=page.waitForResponse(response=>response.url().includes(`/knowledge/items/${item.id}`)&&response.request().method()==='PUT'&&response.status()===200);
await editor.getByRole('button',{name:'保存草稿',exact:true}).click();
await saved;
await editor.locator('.ant-modal-close').click();
await editor.waitFor({state:'hidden'});
}
for (const item of chosen) await page.locator('tbody tr').filter({has:page.getByRole('button',{name:item.title,exact:true})}).getByRole('checkbox').check();
await page.getByRole('button',{name:'批量审核',exact:true}).click();
const review=page.getByRole('dialog').filter({has:page.getByText('批量审核通过 · 2 条',{exact:true})});
await review.getByText('可操作 2 条 · 需先处理 0 条',{exact:true}).waitFor();
if (await review.getByRole('button',{name:'确认审核通过 2 条',exact:true}).isEnabled()) throw new Error('Review skipped per-item confirmation');
for (const item of chosen) {
await review.locator('.ant-collapse-header').filter({hasText:item.title}).click();
}
const boxes=review.getByRole('checkbox');
for (let index=0;index<await boxes.count();index++) await boxes.nth(index).check();
await page.screenshot({path:`${root}/04-batch-review.png`,fullPage:true});
await review.getByRole('button',{name:'确认审核通过 2 条',exact:true}).click();
await review.getByText('成功 2 · 失败 0 · 未执行 0',{exact:true}).waitFor();
await review.getByRole('button',{name:/完\s*成/}).click();
checks.push('编辑适用条件、逐条来源确认、批量审核');
for (const item of chosen) await page.locator('tbody tr').filter({has:page.getByRole('button',{name:item.title,exact:true})}).getByRole('checkbox').check();
await page.getByRole('button',{name:'发布选中',exact:true}).click();
const publishing=page.getByRole('dialog').filter({has:page.getByText('批量发布 · 2 条',{exact:true})});
await publishing.getByRole('button',{name:'确认发布 2 条',exact:true}).click();
await publishing.getByText('成功 2 · 失败 0 · 未执行 0',{exact:true}).waitFor();
await publishing.getByRole('button',{name:/完\s*成/}).click();
await page.getByRole('tab',{name:'检索测试',exact:true}).click();
await page.getByPlaceholder('输入一个真实业务问题').fill(chosen[0].question);
await page.getByRole('button',{name:/检\s*索/,exact:true}).click();
await page.getByRole('button',{name:'查看来源',exact:true}).first().waitFor();
await page.screenshot({path:`${root}/05-published-search.png`,fullPage:true});
checks.push('批量发布、已发布知识检索命中');
if(errors.length) throw new Error(JSON.stringify(errors));
writeFileSync(`${root}/browser-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});
console.log((await page.locator('body').innerText()).slice(-6500));
console.log(JSON.stringify({pageErrors:errors}));
throw error;
} finally { await browser.close(); }