"use strict"; const fs = require("node:fs"); const path = require("node:path"); const test = require("node:test"); const assert = require("node:assert/strict"); const workflow = require("../../Web/manual-financial-workflow.js"); const ui = require("../../Web/manual-financial-ui.js"); const rowVersion = "A".repeat(64); const STOCK_RESULT = "stock-search-results"; function quarters() { return [ { quarter: "1Q", value: 10 }, { quarter: "2Q", value: -20 }, { quarter: "3Q", value: 30 }, { quarter: "4Q", value: 40 }, { quarter: "5Q", value: 50 }, { quarter: "6Q", value: 60 } ]; } function records() { return { "revenue-composition": { kind: "revenue-composition", stockName: "Alpha", baseDate: "2026-06", slices: [ { label: "Semiconductor", percentageText: "50.0", percentage: 50 }, { label: "Service", percentageText: "-", percentage: 0 }, null, { label: "Overseas", percentageText: "25", percentage: 25 }, null ] }, "growth-metrics": { kind: "growth-metrics", stockName: "Alpha", salesGrowth: [1, 2.5, null, -4], operatingProfitGrowth: [5, 6, 7, 8], netAssetGrowth: [9, null, 11, 12], netIncomeGrowth: [13, 14, 15, 16], periods: ["1Q", "2Q", "3Q", "4Q"] }, sales: { kind: "sales", stockName: "Alpha", quarters: quarters() }, "operating-profit": { kind: "operating-profit", stockName: "Alpha", quarters: quarters().map(item => ({ ...item, value: item.value * 2 })) } }; } function createHarness() { const posted = []; const entries = []; const toasts = []; const logs = []; let ordinal = 0; const controller = ui.createController({ postNative(type, payload) { posted.push({ type, payload }); }, appendPlaylistEntry(entry) { entries.push(entry); return true; }, isLocked() { return false; }, getSelectedStock() { return { market: "kospi", source: "oracle", name: "Alpha", code: "000001" }; }, showToast(message) { toasts.push(message); }, addLog(message) { logs.push(message); }, createId() { ordinal += 1; return `manual-ui-${ordinal}`; } }); return { controller, posted, entries, toasts, logs }; } test("all four GraphE records round-trip through pure form conversion", () => { for (const [screen, record] of Object.entries(records())) { const form = ui.recordToForm(screen, record); assert.ok(form, screen); assert.deepEqual(ui.formToRecord(screen, form), workflow.normalizeRecord(screen, record)); } const revenue = ui.recordToForm("revenue-composition", records()["revenue-composition"]); assert.equal(ui.formToRecord("revenue-composition", { ...revenue, slice3Label: "Only label", slice3Percentage: "" }), null); const growth = ui.recordToForm("growth-metrics", records()["growth-metrics"]); assert.equal(ui.formToRecord("growth-metrics", { ...growth, salesGrowth2: "not-a-number" }), null); const sales = ui.recordToForm("sales", records().sales); assert.equal(ui.formToRecord("sales", { ...sales, value4: "10.5" }), null); assert.equal(ui.resolveScreen("manual-operating-profit"), "operating-profit"); assert.equal(ui.resolveScreen({ id: "manual-revenue-mix" }), "revenue-composition"); assert.equal(ui.resolveScreen({ screen: "growth-metrics" }), "growth-metrics"); }); test("controller accepts only the pending list correlation and ignores shared stock traffic", () => { const harness = createHarness(); assert.equal(harness.controller.open({ id: "manual-sales" }), true); assert.equal(harness.posted.length, 1); assert.equal(harness.posted[0].type, workflow.bridgeContract.listRequestType); const request = harness.posted[0].payload; assert.deepEqual(request, { requestId: "manual-ui-1", screen: "sales", query: "", maximumResults: workflow.bridgeContract.defaultMaximumResults }); assert.equal(harness.controller.render().pending.list, request.requestId); const payload = { requestId: request.requestId, screen: "sales", query: "", retrievedAt: "2026-07-12T10:00:00+09:00", totalRowCount: 1, truncated: false, items: [{ stockName: "Alpha", rowVersion, record: records().sales }] }; assert.equal(harness.controller.handleMessage( workflow.bridgeContract.listResultType, { ...payload, requestId: "stale-list" }), false); assert.equal(harness.controller.render().pending.list, request.requestId); assert.equal(harness.controller.render().itemCount, 0); assert.equal(harness.controller.handleMessage(workflow.bridgeContract.listResultType, payload), true); assert.equal(harness.controller.render().pending.list, undefined); assert.equal(harness.controller.render().itemCount, 1); assert.equal(harness.controller.render().status, "ready"); assert.equal(harness.controller.handleMessage(STOCK_RESULT, { requestId: "another-component", query: "Alpha", retrievedAt: "2026-07-12T10:01:00+09:00", totalRowCount: 0, truncated: false, results: [] }), false); assert.equal(harness.controller.setLocked(true).locked, true); assert.equal(harness.controller.setLocked(false).locked, false); }); class FakeElement { constructor(tagName, ownerDocument) { this.tagName = tagName.toUpperCase(); this.ownerDocument = ownerDocument; this.children = []; this.attributes = new Map(); this.listeners = new Map(); this.dataset = {}; this.textContent = ""; this.value = ""; this.hidden = false; this.disabled = false; this.readOnly = false; this.className = ""; } append(...values) { for (const value of values) this.appendChild(value); } appendChild(value) { this.children.push(value); return value; } replaceChildren(...values) { this.children = []; this.append(...values); } setAttribute(name, value) { this.attributes.set(name, String(value)); } addEventListener(name, callback) { this.listeners.set(name, callback); } } class FakeDocument { constructor() { this.body = new FakeElement("body", this); } createElement(tagName) { return new FakeElement(tagName, this); } } function findByClass(node, className) { if (String(node.className || "").split(/\s+/).includes(className)) return node; for (const child of node.children || []) { const found = findByClass(child, className); if (found) return found; } return null; } test("mount creates the isolated text-only dialog and exposes exactly the integration methods", () => { const harness = createHarness(); const document = new FakeDocument(); const overlay = harness.controller.mount(document.body); assert.equal(overlay.tagName, "DIV"); assert.equal(overlay.className, "mfui-overlay"); assert.equal(document.body.children.length, 1); assert.deepEqual(Object.keys(harness.controller), [ "mount", "open", "handleMessage", "render", "setLocked" ]); assert.equal(harness.controller.open("revenue-composition"), true); assert.equal(overlay.hidden, false); assert.equal(harness.posted[0].type, workflow.bridgeContract.listRequestType); }); test("fresh load, exact stock revalidation and native selection create one playable entry", () => { const harness = createHarness(); const document = new FakeDocument(); const overlay = harness.controller.mount(document.body); harness.controller.open("sales"); const listRequest = harness.posted.at(-1).payload; assert.equal(harness.controller.handleMessage(workflow.bridgeContract.listResultType, { requestId: listRequest.requestId, screen: "sales", query: "", retrievedAt: "2026-07-12T10:00:00+09:00", totalRowCount: 1, truncated: false, items: [{ stockName: "Alpha", rowVersion, record: records().sales }] }), true); const listRow = findByClass(overlay, "mfui-list-row"); assert.ok(listRow); listRow.listeners.get("click")(); const loadCall = harness.posted.at(-1); assert.equal(loadCall.type, workflow.bridgeContract.loadRequestType); assert.equal(harness.controller.handleMessage(workflow.bridgeContract.loadResultType, { requestId: loadCall.payload.requestId, screen: "sales", retrievedAt: "2026-07-12T10:01:00+09:00", snapshot: { stockName: "Alpha", rowVersion, record: records().sales } }), true); assert.equal(harness.controller.render().hasSnapshot, true); const playlistButton = findByClass(overlay, "mfui-accent"); assert.ok(playlistButton); playlistButton.listeners.get("click")(); const truncatedSearch = harness.posted.at(-1); assert.equal(truncatedSearch.type, "search-stocks"); harness.controller.handleMessage(STOCK_RESULT, { requestId: truncatedSearch.payload.requestId, query: "Alpha", retrievedAt: "2026-07-12T10:02:00+09:00", totalRowCount: 1, truncated: true, results: [{ market: "kospi", source: "oracle", name: "Alpha", code: "000001" }] }); assert.equal(harness.entries.length, 0); assert.equal(harness.controller.render().stockStatus, "error"); playlistButton.listeners.get("click")(); const stockCall = harness.posted.at(-1); harness.controller.handleMessage(STOCK_RESULT, { requestId: stockCall.payload.requestId, query: "Alpha", retrievedAt: "2026-07-12T10:03:00+09:00", totalRowCount: 1, truncated: false, results: [{ market: "kospi", source: "oracle", name: "Alpha", code: "000001" }] }); const selectionCall = harness.posted.at(-1); assert.equal(selectionCall.type, workflow.bridgeContract.selectionRequestType); const profile = workflow.getScreenDefinition("sales"); harness.controller.handleMessage(workflow.bridgeContract.selectionResultType, { requestId: selectionCall.payload.requestId, screen: "sales", stockName: "Alpha", rowVersion, builderKey: profile.builderKey, code: profile.code, aliases: [profile.code], selection: { groupCode: "KOSPI", subject: "Alpha", graphicType: profile.graphicType, subtype: "", dataCode: "" }, currentPage: 1, totalPages: 1 }); assert.equal(harness.entries.length, 1); assert.equal(workflow.isPlaylistEntryPlayable(harness.entries[0]), true); assert.equal(harness.entries[0].code, "5080"); }); test("UI source uses DOM construction and documents the isolated app integration hook", () => { const source = fs.readFileSync( path.join(__dirname, "..", "..", "Web", "manual-financial-ui.js"), "utf8"); assert.match(source, /App integration hook/); assert.match(source, /createElement/); assert.doesNotMatch(source, /\.innerHTML\s*=/); assert.doesNotMatch(source, /insertAdjacentHTML/); }); test("pending writes correlate exactly and OutcomeUnknown latches process quarantine", () => { const coordinator = ui.createPendingCoordinator(); const expected = { requestId: "write-1", screen: "sales", stockName: "Alpha", expectedRowVersion: rowVersion }; coordinator.set("write", expected, { operation: "delete-one" }); const unknown = { requestId: "write-1", operation: "delete-one", screen: "sales", stockName: "Alpha", code: "OUTCOME_UNKNOWN", message: "Commit result is unknown", retryable: false, outcomeUnknown: true }; assert.equal(coordinator.acceptMutationError({ ...unknown, requestId: "stale-write" }).status, "stale"); assert.equal(coordinator.snapshot().write, "write-1"); assert.equal(ui.getProcessWriteQuarantine().active, false); const accepted = coordinator.acceptMutationError(unknown); assert.equal(accepted.status, "accepted"); assert.equal(accepted.value.outcomeUnknown, true); assert.equal(coordinator.snapshot().write, undefined); assert.equal(ui.getProcessWriteQuarantine().active, true); assert.match(ui.getProcessWriteQuarantine().message, /unknown/i); const laterController = createHarness().controller; assert.equal(laterController.render().writeQuarantined, true); assert.equal(laterController.render().canWrite, false); });