const { test } = require('node:test'); const assert = require('node:assert'); const fs = require('node:fs'); const path = require('node:path'); const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8'); function extractFunc(src, name, asyncFn) { const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{'); const m = re.exec(src); if (!m) throw new Error('function ' + name + ' not found'); let i = m.index + m[0].length - 1; let depth = 0; for (; i < src.length; i++) { if (src[i] === '{') depth++; else if (src[i] === '}') { depth--; if (depth === 0) break; } } return (asyncFn ? 'async ' : '') + src.slice(m.index, i + 1); } const ASYNC_FNS = ['ramBatchFetch', 'ramBatchStart', 'ramBatchStop', 'ramBatchClear']; let code = ''; for (const fn of ['ramIsBatchOp', 'ramBatchPanelVisible', 'ramBatchMergedState', 'ramBatchTitle', 'ramBatchModalTitle', 'ramBatchModalOpen', 'ramBatchModalRender', 'ramBatchStepsText', 'ramBatchAttemptsHtml', 'ramBatchRender', 'ramBatchApplyStatus', 'ramBatchFetch', 'ramBatchStart', 'ramBatchStop', 'ramBatchClear', 'ramOpProgressText', 'ramOpModalClose', 'esc']) { code += extractFunc(html, fn, ASYNC_FNS.indexOf(fn) >= 0) + '\n'; } const RAM_BATCH_FETCH_STALE_MS = parseInt( /const RAM_BATCH_FETCH_STALE_MS = (\d+);/.exec(html)[1], 10); const els = {}; function setEl(id, value) { els[id] = { id: id, value: value === undefined ? '' : value, textContent: '', innerHTML: '', disabled: false, style: {}, classes: {}, classList: { add(c) { els[id].classes[c] = true; }, remove(c) { delete els[id].classes[c]; }, toggle(c, on) { if (on) els[id].classes[c] = true; else delete els[id].classes[c]; }, contains(c) { return !!els[id].classes[c]; }, }, }; if (id === 'ram-batch-attempts') { // like a browser: replacing the content resets the scroll position Object.defineProperty(els[id], 'innerHTML', { get() { return this._html || ''; }, set(v) { this._html = v; this.scrollTop = 0; }, }); } return els[id]; } function clearEls() { for (const k of Object.keys(els)) delete els[k]; } // The modal chrome and the batch panel: the element sets the modal tests need. function setModalEls() { for (const id of ['ram-op-modal', 'ram-op-title', 'ram-op-status', 'ram-op-bar', 'ram-op-steps', 'ram-op-spin', 'ram-op-warn', 'ram-op-close', 'ram-op-batch-info', 'ram-op-batch-stop', 'ram-op-batch-clear']) setEl(id, ''); } function setPanelEls() { for (const id of ['ram-op', 'ram-batch-panel', 'ram-batch-start', 'ram-batch-stop', 'ram-batch-clear', 'ram-batch-show', 'ram-batch-spin', 'ram-batch-state', 'ram-batch-notice', 'ram-batch-counts', 'ram-batch-attempts']) setEl(id, ''); els['ram-op'].value = 'batch'; } const app = new Function('els', ` var sandbox = { fetched: [], replies: {}, fetchError: null, alerts: [], visibility: true }; var document = { getElementById: id => els[id] || null }; var t = k => k; var isViewVisible = () => sandbox.visibility; var alert = m => sandbox.alerts.push(String(m)); var pysimAlert = m => alert(m); var pysimConfirm = async () => true; // not used by the batch flow var pysimPrompt = async () => ''; // not used by the batch flow var pysimFetch = async (p) => { sandbox.fetched.push(p); if (sandbox.hangs) return new Promise(() => {}); // a stalled connection return sandbox.replies[p] || null; }; var pysimFetchOk = async (p, body) => { sandbox.fetched.push(p); sandbox.bodies = sandbox.bodies || {}; sandbox.bodies[p] = body; if (sandbox.fetchError) throw new Error(sandbox.fetchError); return sandbox.replies[p] || { ok: true }; }; var capGateOk = () => sandbox.capGate !== false; var ramReadFileHex = async () => 'AABB'; var ramInstallCapFields = () => ({ sd_aid: '', install_params: '', ram_format: 'auto' }); var ramInstallParamsCompose = () => ({ install_params: 'C900', stk_params: '' }); var updateRcPriv = () => {}; var updateStkParamsHex = () => {}; var pysimApplyAvailability = () => {}; var _ramOpActive = false; var _ramBatchModalShown = false; var _ramBatchLastKind = null; var _ramBatchLast = null; var _ramBatchFetching = false; var _ramBatchFetchAt = 0; var _ramBatchSummary = null; var _ramBatchProgress = null; var RAM_BATCH_FETCH_STALE_MS = ${RAM_BATCH_FETCH_STALE_MS}; ${code} sandbox.getLast = () => _ramBatchLast; sandbox.setLast = v => { _ramBatchLast = v; }; sandbox.getSummary = () => _ramBatchSummary; sandbox.setSummary = v => { _ramBatchSummary = v; }; sandbox.isFetching = () => _ramBatchFetching; sandbox.setFetchingStale = ms => { _ramBatchFetching = true; _ramBatchFetchAt = Date.now() - ms; }; sandbox.resetFetching = () => { _ramBatchFetching = false; }; sandbox.getProgress = () => _ramBatchProgress; sandbox.ramBatchPanelVisible = ramBatchPanelVisible; sandbox.ramBatchAttemptsHtml = ramBatchAttemptsHtml; sandbox.ramBatchStepsText = ramBatchStepsText; sandbox.ramBatchModalOpen = ramBatchModalOpen; sandbox.ramOpModalClose = ramOpModalClose; sandbox.isBatchModalShown = () => _ramBatchModalShown; sandbox.resetBatchModal = () => { _ramBatchModalShown = false; }; sandbox.resetKind = () => { _ramBatchLastKind = null; }; sandbox.setProgress = v => { _ramBatchProgress = v; }; sandbox.ramBatchRender = ramBatchRender; sandbox.ramBatchApplyStatus = ramBatchApplyStatus; sandbox.ramBatchFetch = ramBatchFetch; sandbox.ramBatchStart = ramBatchStart; sandbox.ramBatchStop = ramBatchStop; sandbox.ramBatchClear = ramBatchClear; return sandbox; `)(els); test('the RAM view wires the batch operation and the panel', () => { assert.match(html, /