Files
MBN_STOCK_WEBVIEW/tests/LegacyParityWeb/legacy-overseas-ui-parity.test.cjs

117 lines
5.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 root = path.resolve(__dirname, "..", "..");
const webRoot = path.join(root, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "Web");
const app = fs.readFileSync(path.join(webRoot, "app.js"), "utf8");
const styles = fs.readFileSync(path.join(webRoot, "styles.css"), "utf8");
function section(startToken, endToken) {
const start = app.indexOf(startToken);
const end = app.indexOf(endToken, start + startToken.length);
assert.ok(start >= 0 && end > start, `missing ${startToken} section`);
return app.slice(start, end);
}
test("UC5 Web keeps the original top-to-bottom index, industry, stock order", () => {
const render = section("function renderOverseas(state)", "function renderComparison(state)");
const fixed = render.indexOf('fixedHeading.textContent = "해외지수"');
const industry = render.indexOf('searchGroup("industry", "해외 업종지수", "업종 검색"');
const stock = render.indexOf('searchGroup("stock", "해외 종목", "종목 검색"');
assert.ok(fixed >= 0 && fixed < industry && industry < stock);
assert.match(render, /periodButtons\("fixedIndex"\)/);
assert.match(render, /model\.isTruncated === true/);
assert.match(render, /검색 결과가 최대 500건으로 제한되었습니다\./);
assert.match(styles, /\.overseas-truncation/);
assert.match(render, /actionButton\(current, "1열판"\)/);
assert.match(render, /action\.periodDays \+ "일"/);
assert.doesNotMatch(render, /overseas-action-panel|overseas-selected|overseas-note/);
assert.doesNotMatch(render, /document\.createElement\("details"\)/);
for (const selector of [
".overseas-workspace",
".overseas-fixed-body",
".overseas-period-actions",
".overseas-result-area"
]) {
assert.match(styles, new RegExp(selector.replaceAll(".", "\\.")));
}
assert.match(styles, /grid-template-rows:\s*minmax\(176px.*minmax\(184px.*minmax\(210px/u);
});
test("UC5 rows are immediately selected, preserve their DOM on receipts, and keep data private", () => {
const render = section("function renderOverseas(state)", "function renderComparison(state)");
const update = section("function updateOverseasWorkspace", "function markOverseasRowSelected");
const click = section(
'categoryTree.addEventListener("click"',
'categoryTree.addEventListener("change"');
const overseasClick = click.slice(
click.indexOf('const overseasTarget = event.target.closest("button[data-overseas-target-id]");'),
click.indexOf("const comparisonChoice", click.indexOf("const overseasTarget")));
assert.match(app, /function selectOverseasRow\(row\)[\s\S]*markOverseasRowSelected\(row\)/);
assert.match(overseasClick, /selectOverseasRow\(overseasTarget\)/);
assert.match(overseasClick, /selectOverseasRow\(overseasResult\)/);
assert.doesNotMatch(overseasClick, /setTimeout/);
assert.match(render,
/existingWorkspace && structureKey === overseasRenderStructureKey[\s\S]*updateOverseasWorkspace\(existingWorkspace, overseas/);
assert.doesNotMatch(update, /replaceChildren/);
assert.doesNotMatch(render, /nationCode|FOREIGN_|symbol/i);
assert.match(app, /event\.key === "Enter"[\s\S]*overseas-industry-search/);
});
test("UC5 listboxes keep one tab stop and move a single native selection by keyboard", () => {
const helper = section("function moveOverseasListSelection", "function renderOverseas(state)");
const keydown = section(
'categoryTree.addEventListener("keydown"',
'window.addEventListener("pointerup"');
assert.match(helper, /list\.getAttribute\("aria-disabled"\) === "true"/);
assert.match(helper, /querySelectorAll\("button"\)/);
assert.match(helper, /selectOverseasRow\(nextRow\)/);
assert.match(helper, /scrollIntoView\(\{ block: "nearest", inline: "nearest" \}\)/);
assert.match(keydown, /const overseasList = event\.target\.closest\("\[data-overseas-list\]"\)/);
assert.match(keydown, /event\.stopPropagation\(\);[\s\S]*moveOverseasListSelection\(overseasList, event\.key\)/);
assert.match(keydown, /moveOverseasListSelection\(overseasList, event\.key\)/);
assert.match(app, /button\.tabIndex = -1/g);
const rows = [0, 1, 2].map(index => ({
index,
selected: false,
scrolled: 0,
getAttribute(name) { return name === "aria-selected" ? String(this.selected) : null; },
scrollIntoView() { this.scrolled += 1; }
}));
const list = {
busy: false,
getAttribute(name) { return name === "aria-disabled" && this.busy ? "true" : "false"; },
querySelectorAll(selector) { assert.equal(selector, "button"); return rows; }
};
const selected = [];
const move = new Function("selectOverseasRow", `${helper}; return moveOverseasListSelection;`)(row => {
rows.forEach(candidate => { candidate.selected = candidate === row; });
selected.push(row.index);
});
assert.equal(move(list, "ArrowDown"), true);
assert.equal(move(list, "ArrowDown"), true);
assert.equal(move(list, "Home"), true);
assert.equal(move(list, "ArrowUp"), false);
assert.equal(move(list, "End"), true);
assert.deepEqual(selected, [0, 1, 0, 2]);
assert.equal(rows[0].scrolled, 2);
assert.equal(rows[1].scrolled, 1);
assert.equal(rows[2].scrolled, 1);
list.busy = true;
assert.equal(move(list, "ArrowUp"), false);
assert.deepEqual(selected, [0, 1, 0, 2]);
const empty = { getAttribute() { return "false"; }, querySelectorAll() { return []; } };
assert.equal(move(empty, "ArrowDown"), false);
});