104 lines
3.5 KiB
JavaScript
104 lines
3.5 KiB
JavaScript
"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/);
|
|
});
|