96 lines
7.0 KiB
JavaScript
96 lines
7.0 KiB
JavaScript
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(); }
|