const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const test = require("node:test"); const app = fs.readFileSync(path.join( __dirname, "../../src/MBN_STOCK_WEBVIEW.LegacyParityApp/Web/app.js"), "utf8"); function section(start, end) { const startIndex = app.indexOf(start); const endIndex = app.indexOf(end, startIndex); assert.ok(startIndex >= 0 && endIndex > startIndex, `Missing or out-of-order source markers: ${start} -> ${end}`); return app.slice(startIndex, endIndex); } function timingHarness() { let now = 0; let nextTimer = 1; const pending = new Map(); const windowObject = { setTimeout(callback, milliseconds) { const timer = nextTimer++; pending.set(timer, { callback, due: now + milliseconds }); return timer; }, clearTimeout(timer) { pending.delete(timer); } }; const source = section( "const fallbackDoubleClickTimeMilliseconds", "function isRovingListNavigationKey"); const api = new Function("window", ` ${source} return { update: updateDoubleClickTime, defer: deferLegacySingleClick, clear: clearDelayedClick }; `)(windowObject); return { ...api, pendingCount: () => pending.size, advance(milliseconds) { now += milliseconds; const due = Array.from(pending.entries()) .filter(([, timer]) => timer.due <= now) .sort((left, right) => left[1].due - right[1].due); due.forEach(([id, timer]) => { if (!pending.delete(id)) return; timer.callback(); }); } }; } test("a 350ms Windows double-click cancels the pending single action", () => { const harness = timingHarness(); const actions = []; harness.update({ interactionMetrics: { doubleClickTimeMilliseconds: 500 } }); const timer = harness.defer(() => actions.push("single")); harness.advance(350); assert.deepEqual(actions, [], "the first click must not commit before click two"); assert.equal(harness.pendingCount(), 1); harness.clear(timer); actions.push("double"); harness.advance(1000); assert.deepEqual(actions, ["double"]); assert.equal(harness.pendingCount(), 0); }); test("ordinary single-click follows the live Windows interval plus a race margin", () => { const harness = timingHarness(); let count = 0; harness.update({ interactionMetrics: { doubleClickTimeMilliseconds: 800 } }); harness.defer(() => { count += 1; }); harness.advance(849); assert.equal(count, 0); harness.advance(1); assert.equal(count, 1); harness.update({ interactionMetrics: { doubleClickTimeMilliseconds: 5001 } }); harness.defer(() => { count += 1; }); harness.advance(549); assert.equal(count, 1, "invalid native data must use the 500ms fallback"); harness.advance(1); assert.equal(count, 2); }); test("rerender-prone rows share system timing while GraphE candidates remain double-click only", () => { assert.doesNotMatch(app, /manualStockResultClickTimer|queueManualFinancialStockSelection/u); const manualCandidateClick = section( "const stock = event.target.closest(\"button[data-manual-stock-result-id]\")", "if (event.target.closest(\"button[data-manual-save]\"))"); assert.match(manualCandidateClick, /focusManualFinancialStock\(stock\)/u); assert.match(manualCandidateClick, /selectManualFinancialStock\(stock\)/u); assert.match(app, /manualViResultClickTimer = deferLegacySingleClick\(function \(\) \{/u); assert.match(app, /manualResultClickTimer = deferLegacySingleClick\(function \(\) \{/u); assert.equal( (app.match(/comparisonTargetClickTimer = deferLegacySingleClick\(function \(\) \{/gu) || []) .length, 2); assert.match(app, /themeResultClickTimer = deferLegacySingleClick\(function \(\) \{/u); assert.doesNotMatch(app, /\}, 250\);/u); const operatorCatalog = section( "operatorCatalogStockResults.addEventListener(\"dblclick\"", "operatorCatalogAddStock.addEventListener(\"click\""); assert.match(operatorCatalog, /if \(\(event\.detail \|\| 1\) > 1\) return;/u); assert.doesNotMatch(operatorCatalog, /deferLegacySingleClick|setTimeout/u); const namedPlaylist = section( "namedPlaylistDefinitions.addEventListener(\"dblclick\"", "namedPlaylistNewTitle.addEventListener(\"input\""); assert.match(namedPlaylist, /namedPlaylistSelection\.isPending\(\)/u); assert.match(namedPlaylist, /namedPlaylistSelection\.defer\(definitionId/u); assert.doesNotMatch(namedPlaylist, /setTimeout|250/u); }); test("each native state refreshes timing before rerendering clickable catalogs", () => { const render = section("function render(state)", "Object.keys(operatorFolderControls)"); const timing = render.indexOf("updateDoubleClickTime(state);"); const firstCatalog = render.indexOf("renderFixedCatalog(state);"); const manual = render.indexOf("renderManualFinancial(state);"); assert.ok(timing >= 0 && firstCatalog > timing && manual > timing); });