56 lines
2.9 KiB
JavaScript
56 lines
2.9 KiB
JavaScript
"use strict";
|
|
|
|
const test = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
|
|
const root = path.resolve(__dirname, "../..");
|
|
const app = fs.readFileSync(path.join(root, "Web/app.js"), "utf8");
|
|
const index = fs.readFileSync(path.join(root, "Web/index.html"), "utf8");
|
|
const styles = fs.readFileSync(path.join(root, "Web/styles.css"), "utf8");
|
|
const moduleSource = fs.readFileSync(path.join(root, "Web/market-nav-reorder.js"), "utf8");
|
|
|
|
test("market reorder module loads before app and binds only to marketNav", () => {
|
|
const moduleIndex = index.indexOf('<script src="market-nav-reorder.js"></script>');
|
|
const appIndex = index.indexOf('<script src="app.js"></script>');
|
|
assert.ok(moduleIndex >= 0 && appIndex > moduleIndex);
|
|
assert.match(app, /const marketNavReorder = globalThis\.MbnMarketNavReorder;/u);
|
|
assert.match(app, /marketNavReorder\.createController\(\{\s*nav: marketNav,/u);
|
|
assert.match(app,
|
|
/isBlocked: \(\) => hasOpenOperatorModal\(\) \|\| isPlaylistSnapshotLocked\(\)/u);
|
|
assert.match(app, /if \(!marketNavReorderController\.mount\(\)\)/u);
|
|
});
|
|
|
|
test("dashboard remains first and exactly ten original data-market identities reorder", () => {
|
|
const nav = index.match(/<nav id="marketNav"[\s\S]*?<\/nav>/u)?.[0] || "";
|
|
const markets = [...nav.matchAll(/data-market="([^"]+)"/gu)].map(match => match[1]);
|
|
assert.deepEqual(markets, [
|
|
"dashboard", "overseas", "exchange", "index", "kospi", "kosdaq",
|
|
"compare", "theme", "foreign-stock", "expert", "halted"
|
|
]);
|
|
assert.match(moduleSource, /const fixedMarket = "dashboard";/u);
|
|
assert.match(moduleSource, /button\.draggable = reorderable;/u);
|
|
assert.match(moduleSource, /"aria-keyshortcuts", "Alt\+ArrowUp Alt\+ArrowDown"/u);
|
|
assert.match(moduleSource, /"드래그 또는 Alt\+화살표로 업무 탭 순서 변경"/u);
|
|
});
|
|
|
|
test("reorder is DOM/session-only and cannot alter view, DB, or playlist state", () => {
|
|
assert.doesNotMatch(moduleSource,
|
|
/localStorage|sessionStorage|requestMarketData|state\.|playlist|postNative/u);
|
|
assert.match(moduleSource, /nav\.prepend\(dashboard\)/u);
|
|
assert.match(moduleSource, /for \(const market of normalized\) nav\.append/u);
|
|
|
|
const clickStart = app.indexOf('marketNav.addEventListener("click"');
|
|
const clickEnd = app.indexOf("elements.catalogSearch.addEventListener", clickStart);
|
|
const clickHandler = app.slice(clickStart, clickEnd);
|
|
assert.match(clickHandler, /state\.activeMarket = button\.dataset\.market;/u);
|
|
assert.doesNotMatch(clickHandler, /children\[|childNodes\[|selectedIndex/u);
|
|
assert.match(clickHandler, /requestMarketData\(state\.activeMarket\)/u);
|
|
});
|
|
|
|
test("drag affordance is visible without changing dashboard markup semantics", () => {
|
|
assert.match(styles, /\.nav-item\[draggable="true"\] \{ cursor: grab; \}/u);
|
|
assert.match(styles, /\.nav-item\.dragging \{ opacity: \.55; cursor: grabbing; \}/u);
|
|
});
|