"use strict"; const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const test = require("node:test"); const repositoryRoot = path.resolve(__dirname, "..", ".."); const webRoot = path.join(repositoryRoot, "src", "MBN_STOCK_WEBVIEW.LegacyParityApp", "Web"); const markup = fs.readFileSync(path.join(webRoot, "index.html"), "utf8"); const styles = fs.readFileSync(path.join(webRoot, "styles.css"), "utf8"); const navigation = fs.readFileSync(path.join(webRoot, "workspace-navigation.js"), "utf8"); test("operator shell keeps one switchable workspace beside an always-present schedule", () => { for (const id of [ "workspace-region", "workspace-navigation", "workspace-nav-toggle", "workspace-stock-tab", "workspace-catalog-tab", "workspace-title", "stock-workspace", "catalog-workspace", "playlist-drop-zone" ]) { assert.match(markup, new RegExp(`id="${id}"`)); } const workspaceStart = markup.indexOf('id="workspace-region"'); const stockStart = markup.indexOf('id="stock-workspace"'); const catalogStart = markup.indexOf('id="catalog-workspace"'); const scheduleStart = markup.indexOf('class="playlist-panel"'); assert.ok(workspaceStart >= 0 && stockStart > workspaceStart); assert.ok(catalogStart > stockStart); assert.ok(scheduleStart > catalogStart); assert.match(markup, /<\/section>\s*<\/div>\s*<\/section>\s*<\/section>\s*
]*hidden inert/); assert.match(markup, /항상 표시<\/small>

송출 스케줄<\/h2>/); }); test("layout uses a two-to-one work and schedule split with compact schedule columns", () => { assert.match(styles, /grid-template-columns:\s*minmax\(900px, 2fr\) minmax\(600px, 1fr\)/); assert.match(styles, /\.workspace-region\s*\{[^}]*grid-template-columns:\s*var\(--workspace-nav-width\) minmax\(0, 1fr\)/); assert.match(styles, /\.workspace-region\.nav-collapsed/); assert.match(styles, /--workspace-nav-collapsed:\s*62px/); assert.match(styles, /\.workspace-nav-item:focus-visible\s*\{[\s\S]*?outline:\s*2px solid var\(--workspace-accent\)/); assert.match(styles, /\.playlist-toolbar\s*\{[^}]*flex-wrap:\s*wrap/); assert.match(styles, /\.playlist-row\s*\{[^}]*min-width:\s*600px/); }); test("workspace navigation is local presentation state and preserves native contracts", () => { assert.match(navigation, /entry\.view\.hidden = !isCurrent/); assert.match(navigation, /entry\.view\.toggleAttribute\("inert", !isCurrent\)/); assert.match(navigation, /if \(focusWillBeHidden\) selected\.tab\.focus\(\)/); assert.match(navigation, /setAttribute\("aria-current", isCurrent \? "page" : "false"\)/); assert.match(navigation, /region\.classList\.toggle\("nav-collapsed", !isExpanded\)/); assert.match(navigation, /event\.key !== "ArrowUp" && event\.key !== "ArrowDown"/); assert.match(navigation, /event\.key === "Enter" \|\| event\.key === " "/); assert.match(navigation, /setWorkspace\(menuButton === stockTab \? "stock" : "catalog"\)/); assert.match(navigation, /marketTabs\.addEventListener\("click"/); assert.doesNotMatch(navigation, /postMessage|localStorage|sessionStorage|send\s*\(/); }); test("workspace navigation loads before the native-authority application bridge", () => { const navigationScript = markup.indexOf(''); const applicationScript = markup.indexOf(''); assert.ok(navigationScript >= 0 && applicationScript > navigationScript); });