Files
MBN_STOCK_WEBVIEW/tests/Web/cut-keyboard-app-integration.test.cjs

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 honors the mutation gate", () => {
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 \(playlistMutationLocked\) 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.match(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/);
});