muse-agent-example/web/tests/旅程/规则改进.spec.ts
zizi ae02fd1732 前端:工作台页面、事件标签与旅程用例
- 上下文清单、任务列表与详情、恢复控制、运行观察、创作对话与消息流、正式变更决策面板、正文写作与候选审阅、资料研究等页面同步本轮后端能力。
- 事件标签表补齐后端事件类型;查询键与生成客户端随 HTTP 契约重新生成。
- 依赖调整后 lockfile 同步;交互用例与浏览器旅程 spec 一并更新。
- 证据:make 前端检查(lint + typecheck + Pi 工具桥 tsc)exit 0;make 前端测试 37 文件 / 120 用例通过。
2026-09-18 01:15:07 +08:00

149 lines
5.7 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 { execFileSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
import { expect, test } from "@playwright/test";
// @muse-case {"case_id":"NC-browser-rule-improvement","environment":"隔离PG/合成HTTP及浏览器","given":"固定请求、来源与作者;按用例环境准备独立测试输入","when":"通过实际公开入口执行并核对拒绝、状态及持久结果","then":["NC-browser-rule-improvement:反馈提案经实际验证后明确启停并读回诊断"],"contract":"docs/系统架构/新版设计/模块设计/B07-作者经验.md#有来源的运行观察"}
test("NC-browser-rule-improvement:反馈提案经实际验证后明确启停并读回诊断", async ({
page,
}, info) => {
const env = process.env;
const password = env.MUSE_AUTHOR_PASSWORD_FILE;
const feedback = env.MUSE_FEEDBACK_ID;
const python = env.MUSE_TEST_PYTHON;
const production = env.MUSE_PRODUCTION_CONFIG;
const evaluation = env.MUSE_EVALUATION_CONFIG;
const maintenance = env.MUSE_MAINTENANCE_CONFIG;
const dataset = env.MUSE_RULE_DATASET_ID;
const target = env.MUSE_RULE_BODY_TARGET;
if (
!password ||
!feedback ||
!python ||
!production ||
!evaluation ||
!maintenance ||
!dataset ||
!target
)
throw new Error("需要独立测试配置、反馈、确切规则样本及正文");
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/");
await page.getByLabel("访问口令").fill(readFileSync(password, "utf8").trim());
await page.getByRole("button", { name: "进入工作台" }).click();
await page.goto(`/improvements/new?feedback=${feedback}`);
await page.getByRole("combobox", { name: "改进目标", exact: true }).selectOption("rule");
await page.getByLabel("改进标题").fill("提示语规则的反馈验证");
await page
.getByLabel("需要验证的改变")
.fill("识别提示语,同时保留人物有意的口头禅。");
await page.getByLabel("具体规则版本").selectOption("l002");
const created = page.waitForResponse(
(response) =>
response.url().endsWith("/api/v1/rule-improvements") &&
response.request().method() === "POST",
);
await page.getByRole("button", { name: "保存规则改进提案" }).click();
const response = await created;
expect(response.status()).toBe(201);
const row = await response.json();
await expect(
page.getByRole("heading", { name: "提示语规则的反馈验证", exact: true }),
).toBeVisible();
await expect(page.getByText("本提案规则尚未启用。")).toBeVisible();
await expect(
page.getByRole("button", { name: "核对凭据并打开启用审阅" }),
).toBeDisabled();
// 评测用途由测试驱动显式调用真实CLI,作者服务不持有或推导评测凭据。
const validationFile = info.outputPath("验证请求.json");
writeFileSync(
validationFile,
JSON.stringify({
command_id: "browser-rule-improvement-validate",
selection: {
improvement_id: row.improvement_id,
revision: row.revision,
content_hash: row.content_hash,
},
request: {
dataset_version_id: dataset,
rule_id: row.payload.rule_id,
rule_version: row.payload.rule_version,
rule_hash: row.payload.rule_hash,
},
}),
);
const result = JSON.parse(
execFileSync(
python,
[
"-I",
"-m",
"muse",
"经验",
production,
"申请规则验证",
validationFile,
"--评测配置",
evaluation,
],
{ encoding: "utf8", timeout: 30000 },
),
);
const exportFile = info.outputPath("导出请求.json");
writeFileSync(
exportFile,
JSON.stringify({
run_id: result.receipt.run_id,
approval_ref: "fixture-rule-improvement-browser",
valid_until: new Date(Date.now() + 3600000).toISOString(),
}),
);
const proof = JSON.parse(
execFileSync(
python,
["-I", "-m", "muse", "评测", maintenance, "导出规则凭据", exportFile],
{ encoding: "utf8", timeout: 30000 },
),
);
await page.reload();
await expect(
page.getByText("实际验证结果已关联", { exact: true }),
).toBeVisible();
await page.getByLabel("规则验证凭据编号").fill(proof.receipt_id);
await page.getByRole("button", { name: "核对凭据并打开启用审阅" }).click();
await expect(page.getByText("本提案规则尚未启用。")).toBeVisible();
await page.getByRole("button", { name: "确认启用此规则版本" }).click();
await expect(
page.getByText("本提案第1版已启用,可用于只读诊断。"),
).toBeVisible();
const diagnosis = await page.evaluate(async (body) => {
const response = await fetch("/api/v1/diagnoses", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ target: JSON.parse(body) }),
});
if (response.status !== 201) throw new Error(await response.text());
return response.json();
}, target);
await page.reload();
await expect(
page.getByRole("link", { name: `查看诊断 ${diagnosis.diagnosis_id}` }),
).toBeVisible();
await page.screenshot({
path: info.outputPath("规则改进与实际诊断.png"),
fullPage: true,
});
await page.getByRole("button", { name: "核对停用依据" }).click();
await page.getByRole("button", { name: "确认停用此规则版本" }).click();
await expect(
page.getByText("本提案规则已停用。", { exact: true }),
).toBeVisible();
await page.reload();
await expect(
page.getByText("本提案规则已停用。", { exact: true }),
).toBeVisible();
expect(errors).toEqual([]);
});