import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs/promises'; import path from 'node:path'; import vm from 'node:vm'; import { fileURLToPath } from 'node:url'; const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))); async function read(relativePath) { return fs.readFile(path.join(root, relativePath), 'utf8'); } function fakeElement(id = '') { const listeners = {}; const attributes = new Map(); const classes = new Set(); const node = { id, nodeType: 1, tagName: '', listeners, attributes, children: [], className: '', textContent: '', value: '', checked: false, disabled: false, open: false, options: [], selectedOptions: [], dataset: {}, classList: { add: (...names) => names.forEach((name) => classes.add(name)), remove: (...names) => names.forEach((name) => classes.delete(name)), contains: (name) => classes.has(name), toggle: (name, force) => { const active = force === undefined ? !classes.has(name) : force; if (active) classes.add(name); else classes.delete(name); return active; } }, addEventListener: (type, handler) => { listeners[type] = handler; }, append(...children) { this.children.push(...children.flat()); }, replaceChildren(...children) { this.children = children.flat(); }, querySelectorAll: () => [], removeAttribute: (name) => attributes.delete(name), getAttribute: (name) => attributes.get(name) || null, setAttribute: (name, value) => attributes.set(name, value), showModal() { this.open = true; }, close() { this.open = false; listeners.close?.(); }, click() { return listeners.click?.({ preventDefault() {}, stopPropagation() {}, target: this }); } }; return node; } function deferred() { let resolve; let reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; } function nodeText(node) { if (typeof node === 'string' || typeof node === 'number') return String(node); return `${node?.textContent || ''}${(node?.children || []).map(nodeText).join('')}`; } async function executeApp({ whatsappState = 'READY', statusResponses = [], qrResponses = [], get, post } = {}) { const elements = new Map(); const timers = []; const gets = []; const posts = []; const element = (id) => { if (!elements.has(id)) elements.set(id, fakeElement(id)); return elements.get(id); }; const apiGet = async (url) => { gets.push(url); if (url === '/api/auth/me') return { user: { username: 'teste', role: 'admin' } }; if (url === '/api/settings') return { settings: {} }; if (url === '/api/outreach/templates') return { templates: [] }; if (url === '/api/outreach/status') return { enviadosHoje: 0, mensagensPorDia: 10, ultimos: [] }; if (url === '/api/outreach/opt-outs') return { optOuts: [] }; if (url === '/api/whatsapp/status') return statusResponses.length ? statusResponses.shift() : { state: whatsappState }; if (url === '/api/whatsapp/qr') return qrResponses.length ? qrResponses.shift() : { qr: 'data:image/png;base64,qr', expiresAt: null }; if (get) return get(url); throw new Error(`GET inesperado: ${url}`); }; const context = { API: { get: apiGet, post: async (url, body) => { posts.push({ url, body }); if (post) return post(url, body); if (url === '/api/outreach/enviar-manual') return { ok: true, status: 'aceito' }; return {}; }, put: async () => ({}), delete: async () => ({}) }, Captura: { init: async () => {} }, Tratamento: { bindEvents() {}, ativar() {} }, Campanhas: { bindEvents() {}, ativar() {} }, // Mirrors public/js/api.js's comCarregando — app.js expects it as a // real global (plain