"use strict"; const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const test = require("node:test"); const manualFinancialUiModule = require("../../Web/manual-financial-ui.js"); const manualFinancialWorkflow = require("../../Web/manual-financial-workflow.js"); const manualListsUiModule = require("../../Web/manual-lists-ui.js"); const manualListsWorkflow = require("../../Web/manual-lists-workflow.js"); const repositoryRoot = path.resolve(__dirname, "..", ".."); const read = relativePath => fs.readFileSync(path.join(repositoryRoot, relativePath), "utf8"); const appHost = read("App.xaml.cs"); const app = read("Web/app.js"); const index = read("Web/index.html"); const styles = read("Web/styles.css"); const manualFinancialUi = read("Web/manual-financial-ui.js"); const mainWindow = read("MainWindow.xaml.cs"); const closeConfirmationBridge = read("MainWindow.CloseConfirmation.cs"); const manualFinancialBridge = read("MainWindow.ManualFinancial.cs"); const manualListsBridge = read("MainWindow.ManualLists.cs"); const namedPlaylistBridge = read("MainWindow.NamedPlaylists.cs"); const operatorCatalogBridge = read("MainWindow.OperatorCatalogs.cs"); const playoutBridge = read("MainWindow.Playout.cs"); const operatorMutationGate = read("src/MBN_STOCK_WEBVIEW.Core/Playout/OperatorMutationGate.cs"); function functionBody(source, name, nextName) { const start = source.indexOf(`function ${name}(`); assert.ok(start >= 0, `${name} must exist`); const end = nextName ? source.indexOf(`function ${nextName}(`, start + 1) : source.length; assert.ok(end > start, `${name} must precede ${nextName || "the end of the file"}`); return source.slice(start, end); } function sliceBetween(source, startMarker, endMarker) { const start = source.indexOf(startMarker); const end = source.indexOf(endMarker, start + startMarker.length); assert.ok(start >= 0, `${startMarker} must exist`); assert.ok(end > start, `${endMarker} must follow ${startMarker}`); return source.slice(start, end); } function assertOrdered(source, values) { let previous = -1; for (const value of values) { const offset = source.indexOf(value); assert.ok(offset >= 0, `${value} must be loaded`); assert.ok(offset > previous, `${value} must preserve dependency order`); assert.equal(source.indexOf(value, offset + value.length), -1, `${value} must be loaded once`); previous = offset; } } test("operator styles and scripts load once in dependency order before app.js", () => { assertOrdered(index, [ 'href="manual-lists-ui.css"', 'href="manual-financial-ui.css"', 'href="operator-catalog-ui.css"' ]); assertOrdered(index, [ 'src="manual-lists-workflow.js"', 'src="manual-financial-workflow.js"', 'src="operator-catalog-workflow.js"', 'src="manual-lists-ui.js"', 'src="manual-financial-ui.js"', 'src="operator-catalog-ui.js"', 'src="app.js"' ]); }); test("the packaged app registers one primary instance before constructing any runtime", () => { const register = appHost.indexOf("AppInstance.FindOrRegisterForKey(MainInstanceKey)"); const currentCheck = appHost.indexOf("if (!registeredInstance.IsCurrent)", register); const redirect = appHost.indexOf("RedirectActivationToAsync(activation)", currentCheck); const secondaryExit = appHost.indexOf("Exit();", redirect); const windowConstruction = appHost.indexOf("_window = new MainWindow();", secondaryExit); assert.ok(register >= 0); assert.ok(currentCheck > register); assert.ok(redirect > currentCheck); assert.ok(secondaryExit > redirect); assert.ok(windowConstruction > secondaryExit); assert.match(appHost, /_mainInstance\.Activated \+= OnMainInstanceActivated/u); assert.match(appHost, /OnMainInstanceActivated\([\s\S]*DispatcherQueue\.TryEnqueue\(window\.Activate\)/u); assert.doesNotMatch(appHost, /GetProcessesByName|Process\.Kill|Environment\.Exit/u); }); test("legacy GraphE, FSell, VIList, ThemeA and EList controls open their real controllers", () => { assert.match(index, /id="themeCatalogManageButton"/u); assert.match(index, /id="expertCatalogManageButton"/u); const fixedManual = functionBody(app, "openFixedManualAction", "addFixedAction"); assert.match(fixedManual, /action\.builderKey === "s5025"[\s\S]*manualListsUi\?\.openNetSell\(action\.selection\?\.groupCode\)/u); assert.match(fixedManual, /action\.builderKey === "s5074"[\s\S]*action\.selection\?\.groupCode === "PAGED_VI"[\s\S]*manualListsUi\?\.openVi\(\)/u); const stockRender = functionBody(app, "renderStockWorkflow", "requestStockSearch"); assert.match(stockRender, /for \(const action of operatorWorkflow\.manualStockActions\)/u); assert.match(stockRender, /button\.disabled = !search\.selected \|\| operatorUiLocked\(\)/u); assert.match(stockRender, /manualFinancialUi\?\.open\(action\)/u); assert.match(app, /themeCatalogManageButton\.addEventListener\("click", \(\) => \{[\s\S]*operatorCatalogUi\?\.openTheme\(context \|\| undefined\)/u); assert.match(app, /expertCatalogManageButton\.addEventListener\("click", \(\) => \{[\s\S]*operatorCatalogUi\?\.openExpert\(context \|\| undefined\)/u); assert.match(app, /themeCatalogManageButton\.disabled = operatorUiLocked\(\)/u); assert.match(app, /expertCatalogManageButton\.disabled = operatorUiLocked\(\)/u); }); test("the original stock search and 31-cut surface stays independent of every business tab", () => { const stockPanelStart = index.indexOf('
'); const stockWorkflow = index.indexOf('id="stockWorkflow"', stockPanelStart); const catalogPanelStart = index.indexOf('
', stockWorkflow); assert.ok(stockPanelStart >= 0); assert.ok(stockWorkflow > stockPanelStart); assert.ok(catalogPanelStart > stockWorkflow); assert.match(styles, /\.console-grid\s*\{[^}]*grid-template-columns:\s*minmax\([^;]*minmax\([^;]*minmax\([^;]*minmax\(/u); assert.match(styles, /\.stock-panel \.stock-workflow\s*\{[^}]*flex:\s*1 1 auto/u); const render = functionBody(app, "renderStockWorkflow", "requestStockSearch"); assert.match(render, /elements\.stockWorkflow\.hidden = false/u); assert.doesNotMatch(render, /state\.activeMarket/u); const binding = functionBody(app, "bindEvents", null); assert.doesNotMatch(binding, /industryWorkflow\.markets\.includes\(state\.activeMarket\)[\s\S]{0,160}state\.stockWorkflowCollapsed = true/u); }); test("the original first overseas tab is the one and only initial market-data view", () => { assert.match(index, /