muse-agent-example/web/tests/旅程/评测报告.spec.ts
zizi 9e6f1c4481 R2 改造交付:新版模块化单体全量成果
- src/muse 新版全模块(装配/共享/上下文/任务运行/作品规划/故事世界/正文写作/审校修订/知识方法/作者经验/效果评测/交付连载/资料研究/正式变更/元数据/接入/基础设施/编排)+ 测试树(单元/契约/集成/架构/迁移/端到端/夹具)
- 129 项功能全部实现与自动验证(功能覆盖.json/矩阵),含 W31 补齐的规则与代价/节奏安排/伏笔与承诺
- 旧实现按处置清单退出(702 条中 324 删,保护合同与未迁移条目留存有据);web/app.py 旧工作台退役,新工作台为唯一写入口
- 数据库/旧库迁移:真实旧库内容批次迁移链(端点守卫/PG作品正文映射/质量资产缺省投影)
- 运行手册 docs/运行手册.md;W30 本机服务阶段一已运行(infra PG 为正式内容权威)
- R2 执行证据与私有运行材料在 .agents.local/改造/R2-20260909/(不入库)
2026-09-15 12:47:42 +08:00

273 lines
10 KiB
TypeScript
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 { readFileSync, writeFileSync } from "node:fs";
import { expect, test } from "@playwright/test";
import type { 实验报告 } from "../../src/功能/效果评测/数据";
test("NC-browser-evaluation-report:隔离报告与未知费用真实读回", async ({
page,
}, info) => {
const password = process.env.MUSE_AUTHOR_PASSWORD_FILE;
const id = process.env.MUSE_EVALUATION_ID;
const sample = process.env.MUSE_EVALUATION_SAMPLE;
if (!password || !id || !sample)
throw new Error("需要隔离评测服务、实验、样本与口令位置");
const errors: string[] = [];
page.on("pageerror", (e) => errors.push(e.message));
await page.goto("/");
await page.getByLabel("访问口令").fill(readFileSync(password, "utf8").trim());
await page.getByRole("button", { name: "进入工作台" }).click();
await page.getByRole("link", { name: /效果评测/ }).click();
await page.getByLabel("实验编号").fill(id);
const received = page.waitForResponse((r) =>
r.url().endsWith(`/experiments/${id}/report`),
);
await page.getByRole("button", { name: "查看报告" }).click();
const response = await received;
expect(response.status()).toBe(200);
let report: 实验报告 = await response.json();
const samplePanel = page
.getByLabel("逐例结果")
.getByRole("listitem")
.filter({
has: page
.getByRole("heading", { level: 3 })
.filter({ hasText: `${sample} · ` }),
});
await expect(samplePanel).toHaveCount(1);
if (report.confounders) {
const panel = page.getByLabel("混淆项观察");
await panel.getByText(`${sample} · 五类观察`, { exact: true }).click();
const table = panel.getByRole("table", { name: `${sample}混淆项` });
await expect(table.getByRole("row")).toHaveCount(6);
await expect(table).toContainText("疑似漏报");
await expect(table).toContainText("疑似误报");
await expect(table).toContainText("输入泄漏审计");
await expect(table).toContainText("评委不稳定");
await expect(table).toContainText("声明新角色缺卡");
for (const row of Object.values(report.confounders.samples[sample])) {
if (row.finding_count == null)
await expect(table).toContainText("未计算");
}
}
const completed = process.env.MUSE_EVALUATION_COMPLETED === "1";
if (completed) {
expect(report.state).toBe("completed");
const sampleReport = report.samples.find(
(row: { sample_id: string }) => row.sample_id === sample,
);
if (!sampleReport) throw new Error("报告缺少指定样本");
const decision = sampleReport.decisions[0];
if (!decision?.judgment) throw new Error("完成报告缺少实际判断");
await samplePanel
.getByText(`${decision.pair.join(" / ")} · ${decision.model}:`, {
exact: false,
})
.click();
await expect(
samplePanel
.getByText(decision.judgment.rationale, { exact: true })
.first(),
).toBeVisible();
for (const evidence of decision.literary
? []
: decision.judgment.dimensions[0].evidence) {
await expect(
samplePanel
.getByText(`${decision.sides[evidence.side]} 原文`, {
exact: true,
})
.first(),
).toBeVisible();
await expect(
samplePanel
.locator("blockquote")
.filter({ hasText: evidence.quote })
.first(),
).toBeVisible();
}
if (sampleReport.literary?.["control:treatment"]) {
const literary = samplePanel.getByLabel("control:treatment文学评分");
await expect(literary.getByRole("heading")).toContainText(
sampleReport.literary["control:treatment"].status === "stable_report"
? "双评稳定"
: "第三评已收敛",
);
await expect(literary.getByRole("row")).toHaveCount(11);
await expect(literary).toContainText(
sampleReport.literary["control:treatment"].status === "stable_report"
? "初评分数分歧 0 项"
: "初评分数分歧 10 项",
);
const scores = samplePanel.getByLabel("评委原始评分").first();
await expect(scores).toBeVisible();
await scores
.getByText("设定与实体保真:", { exact: false })
.first()
.click();
await expect(scores.locator("blockquote").first()).toBeVisible();
}
} else {
await expect(
page.getByRole("heading", { name: "费用待核对" }),
).toBeVisible();
await expect(
page.locator("dt").filter({ hasText: "实际总费用" }).locator("+ dd"),
).toHaveText("待核对");
await expect(
page.getByRole("heading", { name: `${sample} · 比较未完成` }),
).toBeVisible();
}
await expect(
page.getByText(`主比较:${report.primary_pair.join(" / ")}`, {
exact: true,
}),
).toBeVisible();
await page.getByText("实验条件与版本", { exact: true }).click();
await expect(
page.getByLabel("实验概况").getByText(/配置合同验证/),
).toBeVisible();
await samplePanel.getByText("执行与费用记录", { exact: true }).click();
if (report.calibration) {
const calibration = page.getByLabel("标定结果");
await expect(calibration).toContainText("与本批金标准一致性达标");
const updated = page.waitForResponse((r) =>
r.url().endsWith(`/experiments/${id}/report`),
);
const saved = page.waitForResponse((r) =>
r.url().endsWith(`/experiments/${id}/calibration/seal`),
);
await calibration.getByRole("button", { name: "保存标定记录" }).click();
expect((await saved).status()).toBe(200);
report = await (await updated).json();
if (!report.calibration) throw new Error("保存后报告丢失标定记录");
expect(report.calibration.receipt_id).toBeTruthy();
await expect(calibration).toContainText("标定记录已保存。");
await calibration.getByText("逐项标定偏差", { exact: true }).click();
await expect(calibration.getByRole("row")).toHaveCount(
report.calibration.result.observations.length + 1,
);
}
if (report.calibration_use) {
const panel = page.getByLabel("本次评委的标定依据");
await expect(panel).toContainText("下表保留对应的来源和版本");
const reviewers = Object.values(report.calibration_use.binding.reviewers);
await expect(panel.getByRole("row")).toHaveCount(reviewers.length + 1);
for (const reviewer of reviewers) {
await expect(
panel.getByText(reviewer.actual_model, { exact: true }),
).toBeVisible();
await expect(
panel
.locator(`a[href="/evaluations/${reviewer.experiment_id}"]`)
.first(),
).toBeVisible();
}
}
if (report.effect) {
const panel = page.getByLabel("效果判据");
await expect(
panel.getByRole("heading", { name: "执行与约束:通过" }),
).toBeVisible();
await expect(
panel.getByRole("heading", { name: "效果阈值:通过" }),
).toBeVisible();
await expect(panel.getByLabel("主比较效果").getByRole("row")).toHaveCount(
6,
);
await panel.getByText("场景与角色分层", { exact: true }).click();
await expect(panel.getByText("老角色回归", { exact: true })).toBeVisible();
const refreshed = page.waitForResponse((r) =>
r.url().endsWith(`/experiments/${id}/report`),
);
const saved = page.waitForResponse((r) =>
r.url().endsWith(`/experiments/${id}/effect/seal`),
);
await panel.getByRole("button", { name: "保存效果判据" }).click();
expect((await saved).status()).toBe(200);
report = await (await refreshed).json();
expect(report.effect?.receipt_id).toBeTruthy();
await expect(
panel.getByText("效果判据已保存。", { exact: true }),
).toBeVisible();
if (!report.effect?.receipt_id) throw new Error("保存后缺少效果凭据");
await panel.getByText("判据与目标记录", { exact: true }).click();
await expect(
panel.getByText(report.effect.receipt_id, { exact: true }),
).toBeVisible();
await expect(
panel.getByText(report.effect.assessment.evidence_hash, { exact: true }),
).toBeVisible();
}
for (const sampleReport of report.samples) {
const panel = page.getByLabel(`${sampleReport.sample_id}语义检测`);
if (sampleReport.detection_complete != null) {
await expect(panel).toContainText("检测齐全");
if (
!sampleReport.detections ||
!Object.keys(sampleReport.detections).length
)
throw new Error("已检测报告缺少实际检测结果");
for (const [arm, detection] of Object.entries(sampleReport.detections)) {
await panel
.getByText(
`${arm} · ${detection.report.status === "failed" ? "发现冲突" : "未发现冲突"}`,
{ exact: true },
)
.click();
for (const finding of detection.report.findings) {
await expect(
panel.getByText(finding.message, { exact: true }),
).toBeVisible();
await expect(
panel
.locator("blockquote")
.filter({ hasText: finding.candidate_quote }),
).toBeVisible();
}
}
} else {
await expect(panel).toContainText("未配置检测器");
}
}
if (!completed)
await expect(page.getByText("费用未结算", { exact: false })).toBeVisible();
for (const sampleReport of report.samples) {
for (const unit of sampleReport.units) {
if (unit.rejections?.length) {
await expect(
page.getByText(`已保留 ${unit.rejections.length} 次判断拒绝。`, {
exact: true,
}),
).toBeVisible();
await expect(page.getByLabel("引文校验记录")).toContainText(
unit.rejections[0].call_id,
);
}
}
}
if (report.effect) {
await page
.getByLabel("效果判据")
.screenshot({ path: `${info.outputDir}/评测报告.png` });
} else {
await page.screenshot({
path: `${info.outputDir}/评测报告.png`,
fullPage: true,
});
}
if (report.confounders) {
await page
.getByLabel("混淆项观察")
.screenshot({ path: `${info.outputDir}/混淆观察.png` });
}
const refreshed = page.waitForResponse((r) =>
r.url().endsWith(`/experiments/${id}/report`),
);
await page.getByRole("button", { name: "刷新报告" }).click();
expect((await (await refreshed).json()).report_hash).toBe(report.report_hash);
writeFileSync(
`${info.outputDir}/报告回执.json`,
JSON.stringify({ experiment_id: id, report_hash: report.report_hash }),
);
expect(errors).toEqual([]);
});