// Async context stage 1 (DESIGN.md), for the two cases that can't be a builtin-als-* // golden: an http server's request handler and events need the host to drive a // request over the SW bridge (there is no http client to do it from inside the // guest, as http-exchange.spec.ts's own tests also show), and reading the store // after an await is Node's truth only in stage 2 (not built), so the pinned Node's // own output for that case is not what the kernel must match. import { test, expect, type Page } from "@playwright/test"; import { runGuest } from "./guest"; test("an http server's request handler and req 'end' both see the frame current at listen(), not at construction", async ({ page }) => { const source = await (await page.request.get("/tests/fixtures/guests/async-context-http.cjs")).text(); await page.goto("/demo/fixture.html"); await page.waitForFunction(() => window.__runtime !== undefined, undefined, { timeout: 20_000 }); const base = await page.evaluate(async (source) => { const runtime = window.__runtime!; runtime.writeTree({ "/app/async-context-http.cjs": source }); runtime.exec("/app/async-context-http.cjs"); return await runtime.waitForPort(3102); }, source); const body = await page.evaluate(async (base) => (await fetch(base)).text(), base); expect(JSON.parse(body)).toEqual({ handler: "listen-call", end: "listen-call" }); }); test("fs.watch captures at creation, against the kernel's own (synthetic, immediate) delivery — not a Node golden, fs-watch.spec.ts's own reasoning: FSEvents timing isn't reproducible from a recording", async ({ page }) => { const res = await runGuest(page, { "/app/main.cjs": [ "const fs = require('node:fs');", "const { AsyncLocalStorage } = require('node:async_hooks');", "const als = new AsyncLocalStorage();", "fs.mkdirSync('w');", "const watcher = als.run('create', () => fs.watch('w', () => {", " console.log('change', als.getStore());", " watcher.close();", "}));", "als.run('other', () => setTimeout(() => fs.writeFileSync('w/a.txt', ''), 10));", ].join("\n"), }, "/app/main.cjs"); expect(res.stdout).toBe("change create\n"); expect(res.code).toBe(0); }); test("the store is undefined after an await in stage 1 (Node's truth only in stage 2 — DESIGN.md)", async ({ page }) => { const source = await (await page.request.get("/tests/fixtures/guests/async-context-await.cjs")).text(); await page.goto("/demo/fixture.html"); await page.waitForFunction(() => window.__runtime !== undefined, undefined, { timeout: 20_000 }); const lines = await page.evaluate(async (source) => { const runtime = window.__runtime!; runtime.writeTree({ "/app/async-context-await.cjs": source }); const proc = runtime.exec("/app/async-context-await.cjs"); const dec = new TextDecoder(); let stdout = ""; await proc.stdout.pipeTo(new WritableStream({ write(c) { stdout += dec.decode(c); } })); await proc.exited; return stdout.trimEnd().split("\n"); }, source); expect(lines).toEqual(["before await: store", "after await: undefined"]); });