/** * DOM shim for the module-split tests. * * Extends the markdown harness shim with a CAPTURING console (so scenarios * can assert on the `[statum]` warnings/errors the seam emits) and a small * element tree builder. Like tests/markdown/shim.js, `document.readyState` * is 'loading' so statum-core.js only registers a DOMContentLoaded listener * instead of booting. */ export function installShim() { var listeners = {}; var log = { errors: [], warns: [], messages: [] }; function makeElement(tag) { return { tagName: (tag || 'div').toUpperCase(), style: {}, dataset: {}, attributes: [], children: [], textContent: '', innerHTML: '', get firstChild() { return this.children[0] || null; }, classList: { add: function () {}, remove: function () {}, contains: function () { return false; } }, contains: function () { return false; }, appendChild: function (c) { this.children.push(c); return c; }, removeChild: function (c) { var i = this.children.indexOf(c); if (i >= 0) this.children.splice(i, 1); return c; }, remove: function () { if (this.parentNode) this.parentNode.removeChild(this); }, replaceWith: function () {}, cloneNode: function () { return makeElement(this.tagName); }, nextElementSibling: null, parentNode: null, addEventListener: function () {}, removeEventListener: function () {}, setAttribute: function (n, v) { this.attributes = this.attributes.filter(function (a) { return a.name !== n; }); this.attributes.push({ name: n, value: String(v) }); }, removeAttribute: function (n) { this.attributes = this.attributes.filter(function (a) { return a.name !== n; }); }, hasAttribute: function (n) { return this.attributes.some(function (a) { return a.name === n; }); }, getAttribute: function (n) { var hit = this.attributes.filter(function (a) { return a.name === n; })[0]; return hit ? hit.value : null; }, querySelectorAll: function () { return []; }, querySelector: function () { return null; } }; } globalThis.__shim = { log: log, makeElement: makeElement, body: makeElement('body') }; globalThis.console = { log: function () { log.messages.push(['log', Array.prototype.slice.call(arguments)]); }, info: function () { log.messages.push(['info', Array.prototype.slice.call(arguments)]); }, debug: function () { log.messages.push(['debug', Array.prototype.slice.call(arguments)]); }, warn: function () { log.warns.push(Array.prototype.slice.call(arguments).join(' ')); log.messages.push(['warn', Array.prototype.slice.call(arguments)]); }, error: function () { log.errors.push(Array.prototype.slice.call(arguments).join(' ')); log.messages.push(['error', Array.prototype.slice.call(arguments)]); } }; globalThis.document = { readyState: 'loading', cookie: '', head: makeElement('head'), body: globalThis.__shim.body, documentElement: makeElement('html'), addEventListener: function (t, f) { (listeners[t] = listeners[t] || []).push(f); }, removeEventListener: function () {}, getElementById: function () { return null; }, querySelectorAll: function () { return []; }, querySelector: function () { return null; }, createElement: function (tag) { return makeElement(tag); }, createTextNode: function (t) { return { nodeType: 3, textContent: t }; } }; globalThis.window = globalThis; globalThis.location = { href: 'http://localhost/', protocol: 'http:', addEventListener: function () {} }; globalThis.navigator = { userAgent: 'qjs' }; globalThis.addEventListener = function (t, f) { (listeners[t] = listeners[t] || []).push(f); }; globalThis.removeEventListener = function () {}; globalThis.__STATUM_TEST__ = true; // Minimal fetch/Headers stub so scenarios can prove core's headless boot // (the full-featured DOM version lives in tests/dom/shim.js). var fetchHandler = null; function Headers() { this._h = {}; } Headers.prototype.set = function (n, v) { this._h[String(n).toLowerCase()] = String(v); }; Headers.prototype.append = function (n, v) { var k = String(n).toLowerCase(); this._h[k] = this._h[k] ? this._h[k] + ', ' + v : String(v); }; Headers.prototype.get = function (n) { var k = Object.keys(this._h).filter(function (x) { return x === String(n).toLowerCase(); })[0]; return k !== undefined ? this._h[k] : null; }; globalThis.Headers = Headers; function okResponse(directives) { return { ok: true, status: 200, headers: { get: function (n) { return String(n).toLowerCase() === 'content-type' ? 'application/vnd.statum+json' : null; } }, json: function () { return Promise.resolve(directives || []); } }; } globalThis.fetch = function (url, init) { var res = fetchHandler ? fetchHandler(String(url), init || {}) : okResponse([]); return Promise.resolve(res); }; globalThis.__shim.setFetch = function (fn) { fetchHandler = fn; }; globalThis.__shim.okResponse = okResponse; }