64 lines
4.0 KiB
JavaScript
64 lines
4.0 KiB
JavaScript
|
|
// Mocked SSE and API snapshots: partial text, replay, retry reset and persistence.
|
||
|
|
import { chromium } from "../tools/playwright/node_modules/playwright/index.mjs";
|
||
|
|
import assert from "node:assert/strict";
|
||
|
|
|
||
|
|
const browser = await chromium.launch({ headless: true });
|
||
|
|
try {
|
||
|
|
const page = await browser.newPage();
|
||
|
|
page.setDefaultTimeout(8000);
|
||
|
|
await page.addInitScript(() => {
|
||
|
|
localStorage.setItem("lazyboy.locale", "zh-Hant");
|
||
|
|
window.sources = [];
|
||
|
|
window.EventSource = class {
|
||
|
|
constructor() { window.sources.push(this); }
|
||
|
|
close() {}
|
||
|
|
};
|
||
|
|
});
|
||
|
|
const errors = [];
|
||
|
|
page.on("pageerror", error => errors.push(error.message));
|
||
|
|
let transcript = [{ role: "user", content: "請直接回答" }];
|
||
|
|
const activity = { state: "running", running: true, queued: false, active_task_ids: [], observed_at_ms: Date.now() };
|
||
|
|
await page.route("**/api/**", route => {
|
||
|
|
const path = new URL(route.request().url()).pathname;
|
||
|
|
if (path === "/api/agents") return route.fulfill({ json: { agents: [{ id: "owner", name: "測試" }] } });
|
||
|
|
if (path.endsWith("/activity")) return route.fulfill({ json: activity });
|
||
|
|
if (path.endsWith("/computer")) return route.fulfill({ json: { ready: false, state: "error", error: "fixture" } });
|
||
|
|
if (path === "/api/agents/owner") return route.fulfill({ json: { id: "owner", name: "測試", transcript, activity } });
|
||
|
|
return route.fulfill({ json: {} });
|
||
|
|
});
|
||
|
|
await page.goto(process.env.LAZYBOY_TEST_UI_URL || "http://127.0.0.1:5173");
|
||
|
|
await page.locator(".messages").getByText("請直接回答", { exact: true }).waitFor();
|
||
|
|
await page.waitForFunction(() => window.sources.at(-1)?.onmessage);
|
||
|
|
const emit = events => page.evaluate(events => window.sources.at(-1).onmessage({ data: JSON.stringify({ events }) }), events);
|
||
|
|
const event = (id, type, fields = {}) => ({ id, kind: "runtime", payload: { type, id: "message-1", ...fields } });
|
||
|
|
await emit([event(1, "message_delta", { delta: "正在" })]);
|
||
|
|
await page.locator(".messages").getByText("正在", { exact: true }).waitFor();
|
||
|
|
await emit([event(1, "message_delta", { delta: "正在" }), event(2, "message_delta", { delta: "回答" })]);
|
||
|
|
await page.locator(".messages").getByText("正在回答", { exact: true }).waitFor();
|
||
|
|
assert.equal(await page.locator(".messages").getByText("正在正在回答", { exact: true }).count(), 0);
|
||
|
|
const reconnect = page.waitForResponse(response => new URL(response.url()).pathname === "/api/agents/owner");
|
||
|
|
await page.evaluate(() => window.sources.at(-1).onopen());
|
||
|
|
await reconnect;
|
||
|
|
await page.locator(".messages").getByText("正在回答", { exact: true }).waitFor();
|
||
|
|
await emit([event(3, "message_reset")]);
|
||
|
|
await page.locator(".messages").getByText("正在回答", { exact: true }).waitFor({ state: "hidden" });
|
||
|
|
await emit([event(4, "message_delta", { delta: "完成" })]);
|
||
|
|
await page.locator(".messages").getByText("完成", { exact: true }).waitFor();
|
||
|
|
await emit([event(5, "message_end", { content: "完成回答" })]);
|
||
|
|
await page.locator(".messages").getByText("完成回答", { exact: true }).waitFor();
|
||
|
|
transcript = [...transcript, { role: "assistant", id: "message-1", content: "完成回答" }];
|
||
|
|
const persisted = page.waitForResponse(response => new URL(response.url()).pathname === "/api/agents/owner");
|
||
|
|
await emit([{ id: 6, kind: "reply", payload: { verdict: "answer", message: "完成回答" } }]);
|
||
|
|
await persisted;
|
||
|
|
assert.equal(await page.locator(".messages").getByText("完成回答", { exact: true }).count(), 1);
|
||
|
|
await page.reload();
|
||
|
|
await page.locator(".messages").getByText("完成回答", { exact: true }).waitFor();
|
||
|
|
await page.waitForFunction(() => window.sources.at(-1)?.onmessage);
|
||
|
|
await emit([event(1, "message_delta", { delta: "舊內容" }), event(2, "message_reset")]);
|
||
|
|
assert.equal(await page.locator(".messages").getByText("完成回答", { exact: true }).count(), 1);
|
||
|
|
assert.deepEqual(errors, []);
|
||
|
|
console.log("PASS incremental text appears before completion; replay/reset/reconnect/reload stay consistent");
|
||
|
|
} finally {
|
||
|
|
await browser.close();
|
||
|
|
}
|