"use strict"; // Loads the compiled bundle (html/js/app.js) into a jsdom window so the // global-script UI code can be driven from Node's test runner. const fs = require("fs"); const path = require("path"); const vm = require("vm"); const {JSDOM, VirtualConsole} = require("jsdom"); const BUNDLE_PATH = path.join(__dirname, "..", "html", "js", "app.js"); // A trimmed copy of index.html: only the ids the bundle looks up. const SHELL_HTML = ` xTeVe

Connecting...

`; function readBundle() { return fs.readFileSync(BUNDLE_PATH, "utf8"); } class FakeWebSocket { constructor(url) { this.url = url; this.readyState = FakeWebSocket.CONNECTING; this.sent = []; this.onopen = null; this.onmessage = null; this.onerror = null; this.onclose = null; FakeWebSocket.instances.push(this); } send(data) { this.sent.push(data); } close() { this.readyState = FakeWebSocket.CLOSED; } } FakeWebSocket.CONNECTING = 0; FakeWebSocket.OPEN = 1; FakeWebSocket.CLOSING = 2; FakeWebSocket.CLOSED = 3; FakeWebSocket.instances = []; // loadApp evaluates the bundle in a fresh window. Options: // server - object assigned to the global SERVER after the bundle runs // html - alternative document markup function loadApp(options) { options = options || {}; // The UI logs freely; keep that out of the test output but keep jsdom's // own errors (unimplemented APIs, uncaught handler exceptions) visible. const jsdomErrors = []; const virtualConsole = new VirtualConsole(); virtualConsole.on("jsdomError", err => { jsdomErrors.push(err); }); const dom = new JSDOM(options.html || SHELL_HTML, { url: "http://localhost:34400/", runScripts: "dangerously", pretendToBeVisual: true, virtualConsole, }); const window = dom.window; // jsdom does not implement innerText; the UI uses it as a textContent alias. Object.defineProperty(window.HTMLElement.prototype, "innerText", { configurable: true, get() { return this.textContent; }, set(value) { this.textContent = value; }, }); const alerts = []; const confirms = []; window.alert = function(message) { alerts.push(message); }; window.confirm = function(message) { confirms.push(message); return true; }; FakeWebSocket.instances = []; window.WebSocket = FakeWebSocket; const context = dom.getInternalVMContext(); vm.runInContext(readBundle(), context, {filename: "app.js"}); // evalIn runs code inside the window, where top-level class declarations // from the bundle are visible (they are lexical bindings, not window props). function evalIn(code) { return vm.runInContext(code, context); } if (options.server) { window.SERVER = options.server; } return {dom, window, document: window.document, evalIn, alerts, confirms, jsdomErrors, sockets: FakeWebSocket.instances}; } module.exports = {loadApp, readBundle, SHELL_HTML, BUNDLE_PATH};