// ============================================================ // Test harness — load js/app.js into a sandboxed VM context // ============================================================ // // app.js is a ~12k-line browser monofile with no module exports. It runs // only one top-level statement (a DOMContentLoaded listener); everything // else is function/const declarations. We load it in a node:vm context with // browser globals stubbed out so init() never fires, then reach the internals // we want to test through an appended export epilogue. // // Why the epilogue: in vm.runInContext, top-level `function`/`var` declarations // attach to the context's global object, but top-level `const`/`let` (like // `state`, `API`, `HASH_ALGO_V2`) do NOT. So we append a line that copies the // symbols we care about onto globalThis.__test, giving tests a stable handle. // // Reassignable seams for the merge tests: `api`, `loadEntries`, `loadFolders`, // `encryptImportEntry`, etc. are `function` declarations → global properties, // so a test can override `ctx.api = fake` and the free-variable lookup inside // applyRemoteSnapshot will pick up the fake. `state` is a `const` (lexical), // so it can't be replaced — but it CAN be mutated, and applyRemoteSnapshot // closes over that same object, so mutating ctx.__test.state is visible to it. const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const { webcrypto } = require('node:crypto'); // The frontend is split into ordered classic-script files (§3.1). In the // browser they share one global lexical environment; node:vm does NOT share // top-level const/let across separate runInContext calls, so we CONCATENATE // the app.* parts (in