import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; function createClassList() { const values = new Set(); return { add(name) { values.add(name); }, remove(name) { values.delete(name); }, contains(name) { return values.has(name); }, toggle(name, force) { if (force === undefined ? !values.has(name) : force) values.add(name); else values.delete(name); }, }; } function createButton(attributes = {}) { const listeners = new Map(); return { addEventListener(type, listener) { if (!listeners.has(type)) listeners.set(type, []); listeners.get(type).push(listener); }, getAttribute(name) { return attributes[name] || null; }, listenerCount(type) { return (listeners.get(type) || []).length; }, click() { (listeners.get('click') || []).forEach((listener) => listener()); }, }; } function createModal() { return { hidden: true, dataset: {}, classList: createClassList(), querySelector() { return null; }, querySelectorAll() { return []; }, }; } const autoFunHandlers = new Map(); const body = { classList: createClassList() }; const chargeButton = createButton(); const transferButton = createButton({ 'data-aksrt-transfer-open': 'balance' }); const chargeModal = createModal(); const transferModal = createModal(); let contentInjected = false; const documentStub = { body, querySelector(selector) { if (!contentInjected) return null; if (selector === '[data-aksrt-charge-modal]') return chargeModal; if (selector === '[data-aksrt-transfer-modal]') return transferModal; return null; }, querySelectorAll(selector) { if (!contentInjected) return []; if (selector === '[data-aksrt-charge-open]') return [chargeButton]; if (selector === '[data-aksrt-transfer-open]') return [transferButton]; return []; }, }; globalThis.document = documentStub; globalThis.window = { aksrtData: {}, AksrtRequest: { request() { return Promise.resolve({ ok: false, json: {} }); }, }, }; globalThis.jQuery = function () { return { off(name) { autoFunHandlers.delete(name); return this; }, on(name, handler) { autoFunHandlers.set(name, handler); return this; }, }; }; const testFile = fileURLToPath(new URL('../assets/js/credits.js', import.meta.url)); const source = readFileSync(testFile, 'utf8'); new Function(source)(); assert.ok(autoFunHandlers.has('auto_fun.aksrt-credits'), 'credits.js should own one namespaced auto_fun subscription'); contentInjected = true; const rehydrateCredits = autoFunHandlers.get('auto_fun.aksrt-credits'); rehydrateCredits(); rehydrateCredits(); assert.equal(chargeButton.listenerCount('click'), 1, 'AJAX-injected recharge button should bind exactly once'); assert.equal(transferButton.listenerCount('click'), 1, 'AJAX-injected transfer button should bind exactly once'); chargeButton.click(); assert.equal(chargeModal.hidden, false, 'recharge click should open the AJAX-injected modal'); assert.equal(chargeModal.classList.contains('is-open'), true, 'recharge modal should receive its open state'); transferButton.click(); assert.equal(transferModal.hidden, false, 'transfer click should open the AJAX-injected modal'); assert.equal(transferModal.classList.contains('is-open'), true, 'transfer modal should receive its open state'); console.log('credits AJAX rehydrate regression: ok');