收口 MNote P0 P1 P2 审查尾项
- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目 - 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线 - 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径 验证: - cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1 - cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1 - git diff --check - git diff --cached --check - codegraph index . --force && codegraph status . - codegraph sync . && codegraph status .
This commit is contained in:
@@ -137,6 +137,10 @@ async function main() {
|
||||
const screenshots = {};
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "mnote-task490-"));
|
||||
const relativePath = "RuntimeSurfaces.md";
|
||||
const capturedPageAi = {
|
||||
runs: [],
|
||||
aborts: [],
|
||||
};
|
||||
writeWorkspaceManifest(root, "mnote-e2e");
|
||||
fs.writeFileSync(
|
||||
path.join(root, relativePath),
|
||||
@@ -146,6 +150,106 @@ async function main() {
|
||||
let caughtError = null;
|
||||
|
||||
try {
|
||||
await page.route("**/api/hermes/client/gateway/health**", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
ok: true,
|
||||
gateway: { ok: true, status: "mocked" },
|
||||
profile: { name: "reasonix", modelConfigured: true, apiKeyConfigured: true },
|
||||
suggestions: [],
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/hermes/client/tools**", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ ok: true, tools: [] }),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/hermes/client/profiles**", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
ok: true,
|
||||
active: "mnoteai",
|
||||
profiles: [{ name: "mnoteai", label: "MNote AI", modelConfigured: true, apiKeyConfigured: true }],
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/ai/agent-profiles**", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
ok: true,
|
||||
agentId: "reasonix",
|
||||
profiles: [],
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/hermes/client/skills**", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ ok: true, categories: [], archived: [] }),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/hermes/client/sessions", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
ok: true,
|
||||
sessionId: "mnote_task490",
|
||||
title: "task490",
|
||||
traceId: "trace_task490",
|
||||
persistence: "local_ai_session_jsonl",
|
||||
sessionStorage: "local_private",
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/hermes/client/runs", async (route) => {
|
||||
capturedPageAi.runs.push(route.request().postData() || "");
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
ok: true,
|
||||
sessionId: "mnote_task490",
|
||||
runId: "run_task490_stop",
|
||||
upstream: { run_id: "run_task490_stop", trace_id: "trace_task490_stop" },
|
||||
traceId: "trace_task490_stop",
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.route("**/api/hermes/client/events/run_task490_stop", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "text/event-stream; charset=utf-8" },
|
||||
body: `data: ${JSON.stringify({ event: "message.delta", run_id: "run_task490_stop", delta: "Task490 streaming" })}\n\n`,
|
||||
});
|
||||
});
|
||||
await page.route("**/api/hermes/client/runs/run_task490_stop/abort", async (route) => {
|
||||
capturedPageAi.aborts.push(route.request().postData() || "");
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
ok: true,
|
||||
status: "aborted",
|
||||
runtime: { runId: "run_task490_stop", status: "aborted", queueLength: 0 },
|
||||
events: [
|
||||
{ event: "abort.started", runId: "run_task490_stop" },
|
||||
{ event: "abort.completed", runId: "run_task490_stop" },
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await ensureAuthenticated(page, context.request);
|
||||
|
||||
const url = documentUrl(BASE_URL, root, relativePath);
|
||||
@@ -153,7 +257,6 @@ async function main() {
|
||||
assert(response, "文档页没有返回响应");
|
||||
assert(response.status() === 200, `文档页状态码异常: ${response.status()}`);
|
||||
const editor = await waitForRuntimeIsland(page, UI_TIMEOUT_MS);
|
||||
await setBlockHandleFixture(page, UI_TIMEOUT_MS);
|
||||
|
||||
const moreButton = page.locator('[data-testid="wolai-page-settings-trigger"]').first();
|
||||
await moreButton.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||
@@ -175,7 +278,58 @@ async function main() {
|
||||
assert(await page.locator("[data-page-ai-input]").first().isVisible(), "Page AI drawer 必须显示输入框");
|
||||
screenshots.pageAi = await saveScreenshot(page, "02-page-ai");
|
||||
await closePageAiIfOpen(page);
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const drawer = document.querySelector('[data-testid="wolai-page-ai-drawer"]');
|
||||
const trigger = document.querySelector('[data-testid="wolai-floating-ai"]');
|
||||
return drawer instanceof HTMLElement
|
||||
&& drawer.hidden === true
|
||||
&& trigger instanceof HTMLElement
|
||||
&& trigger.getAttribute("data-state") === "closed"
|
||||
&& trigger.getAttribute("aria-expanded") === "false";
|
||||
},
|
||||
null,
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
);
|
||||
|
||||
await aiButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await aiDrawer.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||
await page.locator("[data-page-ai-input]").fill("Task490 stop smoke", { timeout: UI_TIMEOUT_MS });
|
||||
await page.locator('[data-page-ai-action="send"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForFunction(
|
||||
() => document.documentElement.getAttribute("data-mnote-page-ai-run-status") === "running"
|
||||
&& document.documentElement.getAttribute("data-mnote-page-ai-run-id") === "run_task490_stop",
|
||||
null,
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
);
|
||||
const stopButton = page.locator('[data-page-ai-action="stop-run"]').first();
|
||||
await stopButton.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const stop = document.querySelector('[data-page-ai-action="stop-run"]');
|
||||
return stop instanceof HTMLButtonElement && stop.disabled === false && stop.getAttribute("aria-disabled") === "false";
|
||||
},
|
||||
null,
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
);
|
||||
await stopButton.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.waitForFunction(
|
||||
() => document.documentElement.getAttribute("data-mnote-page-ai-run-status") === "aborted",
|
||||
null,
|
||||
{ timeout: UI_TIMEOUT_MS },
|
||||
);
|
||||
await page.locator('[data-page-ai-conversation]').getByText("已请求停止当前 AI run。").waitFor({
|
||||
state: "visible",
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
});
|
||||
assert(capturedPageAi.runs.length === 1, "Page AI stop smoke 应先发起一个 run");
|
||||
assert(capturedPageAi.aborts.length === 1, "点击停止应调用 abort API 一次");
|
||||
const abortBody = JSON.parse(capturedPageAi.aborts[0] || "{}");
|
||||
assert(abortBody.reason === "page_ai_user_stop", "abort API 应记录用户停止原因");
|
||||
screenshots.pageAiStopped = await saveScreenshot(page, "02b-page-ai-stopped");
|
||||
await closePageAiIfOpen(page);
|
||||
|
||||
await setBlockHandleFixture(page, UI_TIMEOUT_MS);
|
||||
await editor.click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.keyboard.type("/");
|
||||
const slashMenu = page.locator('[data-testid="mnote-leptos-tiptap-slash-menu"]').first();
|
||||
|
||||
Reference in New Issue
Block a user