// The kernel's builtin API surface against the pinned Node's (compat/node-<.node-version>.json, // recorded by `node scripts/surface.mjs record`). Every Node path must exist with the same // kind, or be listed in compat/divergences.json with a reason. A divergence the kernel // now satisfies fails too, so the list only shrinks on purpose. // After closing or opening gaps: `node scripts/surface.mjs baseline` rewrites the list, // marking new gaps "untriaged", which fails here until each gets a tagged reason: // todo: implementable here; should land ("todo (high):" for the urgent ones) // refuse: can't work in a page; export the name as a refusal (src/runtime/refuse.ts) // A refusal made with refuse.ts records as "refused: ", never as a working // function, so each one is listed here: as refuse: when the page cannot do it, as // todo: when the kernel just doesn't yet. // internal: a Node implementation detail visible on the object, not documented API // deprecated: deprecated in Node; add only if a real package needs it // accepted: a deliberate difference that code can't observe, or a refused class's members import { test, expect } from "@playwright/test"; import { readFileSync, writeFileSync } from "node:fs"; import { compare, type Divergences, type Manifest } from "../scripts/surface-compare.ts"; import { NODE_VERSION } from "../src/shared/platform"; import { runGuest } from "./guest"; const compat = (name: string): URL => new URL(`../compat/${name}`, import.meta.url); const pinned = readFileSync(new URL("../../../.node-version", import.meta.url), "utf8").trim(); const manifest: Manifest = JSON.parse(readFileSync(compat(`node-${pinned}.json`), "utf8")); const divergences: Divergences = JSON.parse(readFileSync(compat("divergences.json"), "utf8")); const TAGGED = /^(todo|todo \(high\)|refuse|internal|deprecated|accepted): \S/; test("process.version is the pin", () => { expect(NODE_VERSION).toBe(pinned); }); test("every divergence has a tagged reason", () => { expect(Object.entries(divergences).filter(([, why]) => !TAGGED.test(why)).map(([path]) => path)).toEqual([]); }); test(`builtin surface matches Node ${manifest.node} or a listed divergence`, async ({ page }) => { test.setTimeout(120_000); const modules = Object.keys(manifest.surface).filter((p) => !/[.#]/.test(p) && p !== "globalThis"); const res = await page.request.get("/tests/fixtures/guests/surface-walk.cjs"); expect(res.ok()).toBe(true); const out = await runGuest(page, { "/app/surface-walk.cjs": await res.text(), "/app/surface-modules.json": JSON.stringify(modules), }, "/app/surface-walk.cjs"); expect(out.code, out.stderr).toBe(0); const observed = JSON.parse(out.stdout); writeFileSync(compat("observed.json"), JSON.stringify(observed, null, 2) + "\n"); const { unexplained, stale } = compare(manifest.surface, observed, divergences); expect(unexplained.map((g) => `${g.path}: node ${g.expected}, kernel ${g.actual ?? "absent"}`), "gaps not in compat/divergences.json").toEqual([]); expect(stale, "divergences the kernel now satisfies; delete them").toEqual([]); });