"use strict"; const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const test = require("node:test"); const vm = require("node:vm"); const root = path.resolve(__dirname, "..", ".."); const app = fs.readFileSync(path.join( root, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "Web", "app.js"), "utf8"); const nativeWindow = fs.readFileSync(path.join( root, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "MainWindow.xaml.cs"), "utf8"); const nativePlayout = fs.readFileSync(path.join( root, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "MainWindow.Playout.cs"), "utf8"); function loadCutHelpers(rows) { const start = app.indexOf("function legacyCutKey"); const end = app.indexOf("function createCutElement", start); assert.ok(start >= 0 && end > start); const context = { cutList: { children: rows }, result: null }; vm.runInNewContext(` ${app.slice(start, end)} result = { legacyCutKey, focusCutElement, cutKeyboardTarget }; `, context); return context.result; } function row(id) { return { id, tabIndex: -1, focusCount: 0, scrollCount: 0, focus() { this.focusCount += 1; }, scrollIntoView() { this.scrollCount += 1; } }; } test("cut keyboard maps only native ListView keys and uses physical boundaries", () => { const rows = [row(0), row(1), row(2)]; const helpers = loadCutHelpers(rows); assert.equal(helpers.legacyCutKey("ArrowUp"), "arrow-up"); assert.equal(helpers.legacyCutKey("ArrowDown"), "arrow-down"); assert.equal(helpers.legacyCutKey("Home"), "home"); assert.equal(helpers.legacyCutKey("End"), "end"); assert.equal(helpers.legacyCutKey(" "), "space"); assert.equal(helpers.legacyCutKey("PageDown"), null); assert.equal(helpers.cutKeyboardTarget(rows[0], "ArrowUp").id, 0); assert.equal(helpers.cutKeyboardTarget(rows[1], "ArrowDown").id, 2); assert.equal(helpers.cutKeyboardTarget(rows[1], "Home").id, 0); assert.equal(helpers.cutKeyboardTarget(rows[1], "End").id, 2); }); test("cut focus is roving and scrolls only for keyboard navigation", () => { const rows = [row(0), row(1), row(2)]; const helpers = loadCutHelpers(rows); helpers.focusCutElement(rows[1], true); assert.deepEqual(rows.map(item => item.tabIndex), [-1, 0, -1]); assert.equal(rows[1].focusCount, 1); assert.equal(rows[1].scrollCount, 1); }); test("cut key intent preserves Home End bubbling and stable PROGRAM selection", () => { const start = app.indexOf("function createCutElement"); const end = app.indexOf("function renderCuts", start); const factory = app.slice(start, end); assert.match(factory, /element\.addEventListener\("keydown"/); assert.match(factory, /event\.preventDefault\(\)/); assert.match(factory, /if \(!cutSelectionEnabled\) return/); assert.match(factory, /send\("cut-key-down", \{/); assert.match(factory, /control: event\.ctrlKey/); assert.match(factory, /shift: event\.shiftKey/); assert.doesNotMatch(factory, /stopPropagation/); assert.match(nativeWindow, /LegacyCutKeyDownIntent key => _controller\.CutKeyDown/); assert.doesNotMatch(nativePlayout, /LegacyCutKeyDownIntent or/); }); test("rendered cut rows expose authoritative focus without replacing DOM rows", () => { const start = app.indexOf("function renderCuts"); const end = app.indexOf("cutList.addEventListener", start); const render = app.slice(start, end); assert.match(render, /row\.isFocused === true/); assert.match(render, /element\.tabIndex/); assert.doesNotMatch(render, /replaceChildren/); });