diff --git a/src/canvas-host/server.test.ts b/src/canvas-host/server.test.ts
index 616c6a902b7..db4dc13354f 100644
--- a/src/canvas-host/server.test.ts
+++ b/src/canvas-host/server.test.ts
@@ -18,6 +18,10 @@ const chokidarMockState = vi.hoisted(() => ({
}>,
}));
+const CANVAS_WS_OPEN_TIMEOUT_MS = 2_000;
+const CANVAS_RELOAD_TIMEOUT_MS = 4_000;
+const CANVAS_RELOAD_TEST_TIMEOUT_MS = 12_000;
+
// Tests: avoid chokidar polling/fsevents; trigger "all" events manually.
vi.mock("chokidar", () => {
const createWatcher = () => {
@@ -194,59 +198,69 @@ describe("canvas host", () => {
}
});
- it("serves HTML with injection and broadcasts reload on file changes", async () => {
- const dir = await createCaseDir();
- const index = path.join(dir, "index.html");
- await fs.writeFile(index, "
v1", "utf8");
+ it(
+ "serves HTML with injection and broadcasts reload on file changes",
+ async () => {
+ const dir = await createCaseDir();
+ const index = path.join(dir, "index.html");
+ await fs.writeFile(index, "v1", "utf8");
- const watcherStart = chokidarMockState.watchers.length;
- const server = await startCanvasHost({
- runtime: quietRuntime,
- rootDir: dir,
- port: 0,
- listenHost: "127.0.0.1",
- allowInTests: true,
- });
-
- try {
- const watcher = chokidarMockState.watchers[watcherStart];
- expect(watcher).toBeTruthy();
-
- const res = await fetch(`http://127.0.0.1:${server.port}${CANVAS_HOST_PATH}/`);
- const html = await res.text();
- expect(res.status).toBe(200);
- expect(html).toContain("v1");
- expect(html).toContain(CANVAS_WS_PATH);
-
- const ws = new WebSocket(`ws://127.0.0.1:${server.port}${CANVAS_WS_PATH}`);
- await new Promise((resolve, reject) => {
- const timer = setTimeout(() => reject(new Error("ws open timeout")), 5000);
- ws.on("open", () => {
- clearTimeout(timer);
- resolve();
- });
- ws.on("error", (err) => {
- clearTimeout(timer);
- reject(err);
- });
+ const watcherStart = chokidarMockState.watchers.length;
+ const server = await startCanvasHost({
+ runtime: quietRuntime,
+ rootDir: dir,
+ port: 0,
+ listenHost: "127.0.0.1",
+ allowInTests: true,
});
- const msg = new Promise((resolve, reject) => {
- const timer = setTimeout(() => reject(new Error("reload timeout")), 10_000);
- ws.on("message", (data) => {
- clearTimeout(timer);
- resolve(rawDataToString(data));
- });
- });
+ try {
+ const watcher = chokidarMockState.watchers[watcherStart];
+ expect(watcher).toBeTruthy();
- await fs.writeFile(index, "v2", "utf8");
- watcher.__emit("all", "change", index);
- expect(await msg).toBe("reload");
- ws.close();
- } finally {
- await server.close();
- }
- }, 20_000);
+ const res = await fetch(`http://127.0.0.1:${server.port}${CANVAS_HOST_PATH}/`);
+ const html = await res.text();
+ expect(res.status).toBe(200);
+ expect(html).toContain("v1");
+ expect(html).toContain(CANVAS_WS_PATH);
+
+ const ws = new WebSocket(`ws://127.0.0.1:${server.port}${CANVAS_WS_PATH}`);
+ await new Promise((resolve, reject) => {
+ const timer = setTimeout(
+ () => reject(new Error("ws open timeout")),
+ CANVAS_WS_OPEN_TIMEOUT_MS,
+ );
+ ws.on("open", () => {
+ clearTimeout(timer);
+ resolve();
+ });
+ ws.on("error", (err) => {
+ clearTimeout(timer);
+ reject(err);
+ });
+ });
+
+ const msg = new Promise((resolve, reject) => {
+ const timer = setTimeout(
+ () => reject(new Error("reload timeout")),
+ CANVAS_RELOAD_TIMEOUT_MS,
+ );
+ ws.on("message", (data) => {
+ clearTimeout(timer);
+ resolve(rawDataToString(data));
+ });
+ });
+
+ await fs.writeFile(index, "v2", "utf8");
+ watcher.__emit("all", "change", index);
+ expect(await msg).toBe("reload");
+ ws.close();
+ } finally {
+ await server.close();
+ }
+ },
+ CANVAS_RELOAD_TEST_TIMEOUT_MS,
+ );
it("serves A2UI scaffold and blocks traversal/symlink escapes", async () => {
const dir = await createCaseDir();