AtlatestRepositorysigil-web-client

sigil-web-client / tree / test / integrationtest-web-client-drift.mjs

1#!/usr/bin/env node
2
3import fs from "node:fs";
4import path from "node:path";
5import { spawnSync } from "node:child_process";
6import { fileURLToPath } from "node:url";
7import { WASI } from "node:wasi";
8
9const repo = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
10const app = path.join(repo, "test/integration/apps/web-client-drift");
11const build = path.join(app, "build/web");
12const sigil = process.env.SIGIL;
13const redirects = process.env.SIGIL_REDIRECTS;
14if (!sigil || !redirects) throw new Error("set SIGIL and SIGIL_REDIRECTS");
16const built = process.env.P5_SKIP_BUILD === "1" ? { status: 0, stdout: "", stderr: "" } : spawnSync(sigil, ["build", "--config", "web", "--force", "--no-bundle", "--redirects", redirects], {
17 cwd: app,
18 encoding: "utf8",
19 env: { ...process.env, SIGIL_BACKEND: "native" },
20});
21process.stdout.write(built.stdout || "");
22process.stderr.write(built.stderr || "");
23if (built.status !== 0) throw new Error(`fixture build failed (${built.status})`);
25class Node {
26 constructor(tag = "") {
27 this.tagName = tag.toUpperCase();
28 this.childNodes = [];
29 this.parentNode = null;
30 this.attributes = new Map();
31 this.className = "";
32 }
33 appendChild(child) { return this.insertBefore(child, null); }
34 insertBefore(child, ref) {
35 if (child.parentNode) child.parentNode.removeChild(child);
36 const index = ref === null ? this.childNodes.length : this.childNodes.indexOf(ref);
37 if (index < 0) throw new Error("reference is not a child");
38 this.childNodes.splice(index, 0, child);
39 child.parentNode = this;
40 return child;
41 }
42 removeChild(child) {
43 const index = this.childNodes.indexOf(child);
44 if (index < 0) throw new Error("node is not a child");
45 this.childNodes.splice(index, 1);
46 child.parentNode = null;
47 return child;
48 }
49 replaceChildren(...children) {
50 for (const child of [...this.childNodes]) this.removeChild(child);
51 for (const child of children) this.appendChild(child);
52 }
53 setAttribute(name, value) {
54 this.attributes.set(name, String(value));
55 if (name === "class") this.className = String(value);
56 }
57 removeAttribute(name) { this.attributes.delete(name); }
58 addEventListener() {}
59 removeEventListener() {}
62const root = new Node("div");
63const statusRoot = new Node("div");
64function walk(node, predicate) {
65 if (predicate(node)) return node;
66 for (const child of node.childNodes) {
67 const found = walk(child, predicate);
68 if (found) return found;
69 }
70 return null;
72globalThis.document = {
73 querySelector(selector) {
74 if (selector === "#p5-root") return root;
75 if (selector === "#p5-status") return statusRoot;
76 if (selector === ".p5-child" || selector === ".p5-replacement") {
77 return root.childNodes[0] || null;
78 }
79 if (selector.startsWith(".")) {
80 const wanted = selector.slice(1);
81 return walk(root, node => node.className.split(/\s+/).includes(wanted)) ||
82 walk(statusRoot, node => node.className.split(/\s+/).includes(wanted));
83 }
84 return null;
85 },
86 createElement(tag) { return new Node(tag); },
87 createTextNode(text) { const node = new Node(); node.textContent = String(text); return node; },
88};
90await import(path.join(build, "assets/sigil-wasm-dom.js"));
91const module = await WebAssembly.compile(fs.readFileSync(path.join(build, "web-client-drift.wasm")));
92const wasi = new WASI({ version: "preview1" });
93const imports = {
94 ...wasi.getImportObject(),
95 ...SigilWasmDom.createImports(),
96};
97for (const entry of WebAssembly.Module.imports(module)) {
98 imports[entry.module] ||= {};
99 if (!(entry.name in imports[entry.module]) && entry.kind === "function") {
100 imports[entry.module][entry.name] = () => 0;
101 }
103const instance = await WebAssembly.instantiate(module, imports);
104SigilWasmDom.setInstance(instance);
105wasi.start(instance);
106const init = instance.exports.sigil_wasm_init();
107if (init !== 0) throw new Error(`sigil_wasm_init returned ${init}`);
108const start = instance.exports.sigil_wasm_start();
109if (start !== 0) throw new Error(`sigil_wasm_start returned ${start}`);
110if (root.childNodes.length !== 0) throw new Error("refused drift render mutated the root");
111if (statusRoot.childNodes.length !== 1) throw new Error("fixture did not reach its completion marker");
112console.log("P5-GATE: PASS fresh=#t undrifted=#t drifted=#f mutated=no");