125 lines
4.6 KiB
JavaScript
125 lines
4.6 KiB
JavaScript
/**
|
||
* 知识列表翻页的界面验收:点「加载更多」能把 84 条翻到底,不出错。
|
||
*
|
||
* 这条脚本是有来历的:接口的游标字段名写错(`cur.item_id`),第二页直接 500,
|
||
* 但库小于一页时永远碰不到——导入 84 条旧备忘之后用户一点就撞上了。
|
||
*
|
||
* 用法:
|
||
* node web/e2e/knowledge-paging.mjs # 默认打生产
|
||
* WEB=http://127.0.0.1:5180 PHONE=13900000001 node web/e2e/knowledge-paging.mjs
|
||
*/
|
||
import { chromium } from "playwright-core";
|
||
|
||
const WEB = process.env.WEB ?? "http://192.168.8.188:8200";
|
||
const API = process.env.API ?? `${WEB}/api/v1`;
|
||
const PHONE = process.env.PHONE ?? "13605513683";
|
||
const CODE = process.env.CODE ?? "123456";
|
||
const CODE_PIN = process.env.CODE_PIN ?? "123456"; // 生产 pin 就是验证码本身
|
||
const CHROME =
|
||
process.env.CHROME ??
|
||
`${process.env.LOCALAPPDATA}\\ms-playwright\\chromium-1223\\chrome-win64\\chrome.exe`;
|
||
|
||
let pass = 0;
|
||
let fail = 0;
|
||
function check(name, ok, extra = "") {
|
||
if (ok) {
|
||
pass += 1;
|
||
console.log(`PASS ${name}`);
|
||
} else {
|
||
fail += 1;
|
||
console.log(`FAIL ${name} ${extra}`);
|
||
}
|
||
}
|
||
|
||
async function login() {
|
||
const r = await fetch(`${API}/auth/sms/send`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ phone: PHONE }),
|
||
});
|
||
const sent = await r.json();
|
||
if (sent.code !== 0) throw new Error(`发码失败:${sent.message}`);
|
||
const login = await fetch(`${API}/auth/login`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ phone: PHONE, code: CODE_PIN }),
|
||
});
|
||
const body = await login.json();
|
||
if (body.code !== 0) throw new Error(`登录失败:${body.message}`);
|
||
return { tokens: body.data.tokens, workspaces: await workspaces(body.data.tokens) };
|
||
}
|
||
|
||
async function workspaces(tokens) {
|
||
const r = await fetch(`${API}/workspaces`, {
|
||
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
||
});
|
||
const body = await r.json();
|
||
return body.data?.items ?? body.data ?? [];
|
||
}
|
||
|
||
async function main() {
|
||
void CODE;
|
||
const { tokens, workspaces: spaces } = await login();
|
||
if (!spaces.length) throw new Error("这个账号没有工作区");
|
||
const ws = spaces[0];
|
||
|
||
// 先问接口一共有多少条,作为界面上应该数出来的数字
|
||
const head = await fetch(`${API}/workspaces/${ws.id}/knowledge?limit=100`, {
|
||
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
||
});
|
||
const headBody = await head.json();
|
||
const total = (headBody.data?.items ?? []).length;
|
||
|
||
const browser = await chromium.launch({ executablePath: CHROME });
|
||
const context = await browser.newContext({ viewport: { width: 1440, height: 960 } });
|
||
await context.addInitScript(
|
||
([access, refresh]) => {
|
||
localStorage.setItem("dadao.access", access);
|
||
localStorage.setItem("dadao.refresh", refresh);
|
||
},
|
||
[tokens.access_token, tokens.refresh_token],
|
||
);
|
||
const page = await context.newPage();
|
||
const errors = [];
|
||
const bad = [];
|
||
page.on("pageerror", (e) => errors.push(String(e)));
|
||
page.on("response", (r) => {
|
||
if (r.url().includes("/knowledge?") && r.status() >= 400) bad.push(`${r.status()} ${r.url()}`);
|
||
});
|
||
|
||
await page.goto(`${WEB}/w/${ws.id}/knowledge`, { waitUntil: "networkidle" });
|
||
const cards = () => page.locator("li");
|
||
const first = await cards().count();
|
||
check("首屏有列表", first > 0, `count=${first}`);
|
||
|
||
let rounds = 0;
|
||
while (rounds < 20) {
|
||
const more = page.getByRole("button", { name: "加载更多" });
|
||
if ((await more.count()) === 0) break;
|
||
const before = await cards().count();
|
||
await more.first().click();
|
||
const grew = async () => (await cards().count()) > before;
|
||
for (let i = 0; i < 40 && !(await grew()); i += 1) await page.waitForTimeout(100);
|
||
if (!(await grew())) break;
|
||
rounds += 1;
|
||
}
|
||
|
||
const shown = await cards().count();
|
||
check("点「加载更多」能把列表翻到底", shown >= total, `shown=${shown} total=${total}`);
|
||
check("翻页接口没有报错", bad.length === 0, bad.slice(0, 3).join(" | "));
|
||
const toast = await page.locator("text=服务器内部错误").count();
|
||
check("页面上没有「服务器内部错误」", toast === 0);
|
||
check("控制台零报错", errors.length === 0, errors.slice(0, 2).join(" | "));
|
||
|
||
await page.screenshot({ path: "e2e/artifacts/knowledge-paging.png" });
|
||
await browser.close();
|
||
|
||
console.log(`\n${fail === 0 ? "ALL PASS" : "SOME FAILED"} — pass ${pass}, fail ${fail}`);
|
||
process.exit(fail === 0 ? 0 : 1);
|
||
}
|
||
|
||
main().catch((err) => {
|
||
console.error("脚本异常 —", err?.message ?? err);
|
||
process.exit(1);
|
||
});
|