const { test } = require("node:test"); const assert = require("node:assert/strict"); const fs = require("node:fs"); const vm = require("node:vm"); const { loadApplication } = require("./support/application.cjs"); const { createDebugDom } = require("./support/debug-dom.cjs"); const factory = new vm.Script(fs.readFileSync("src/features/debug/state.js", "utf8") + "\ncreateDebugState").runInNewContext(); function storage() { const data = new Map(); return { data, get length() { return data.size; }, key: i => [...data.keys()][i], getItem: key => data.get(key) ?? null, setItem: (key, value) => data.set(key, value), removeItem: key => data.delete(key) }; } test("opportunities clears debug only when sending the first page, preserving pagination", async () => { const localStorage = storage(); const app = loadApplication("content", { localStorage, fetch: async (_url, options) => { if (options.method === "GET") return new Response(JSON.stringify({ items: [{ isDefault: true, savedSearchNumber: "test" }] })); const offset = JSON.parse(options.body).offset; return new Response(JSON.stringify({ items: [{ OptyNumber: String(offset) }], offset, count: 1, hasMore: offset === 0 })); } }); app.debugContentView.renderDebugPanel = () => {}; app.debugContentLog.logRequestStart({ name: "old", method: "GET" }); const range = { startCloseDate: "2026-09-01", endCloseDate: "2026-12-01" }; const query = () => app.fusionOpportunities.requestOpportunitiesPage("test", "Current Quarter", range, 0, [], 0, 1); await query(); assert.equal(app.debugState.generation, 1); assert.deepEqual(Array.from(app.debugState.requestLog, entry => entry.name), ["requestOpportunities", "requestOpportunities"]); await query(); assert.equal(app.debugState.generation, 2); assert.equal(app.debugState.requestLog.length, 2); app.debugState.flush(); assert.equal([...localStorage.data.values()].some(value => value.includes('"name":"old"')), false); }); test("browser timer receiver does not prevent Token Relay from starting or completing", async () => { let calls = 0; const app = loadApplication("content", { localStorage: storage(), AbortController, setTimeout: function(callback, delay) { "use strict"; assert.equal(this, undefined); return setTimeout(callback, delay); }, clearTimeout: function(id) { "use strict"; assert.equal(this, undefined); clearTimeout(id); }, fetch: async () => { calls++; return new Response(JSON.stringify({ access_token: "synthetic" })); } }); app.debugContentView.renderDebugPanel = () => {}; app.authXsrfContent.getXsrfToken = async () => ({ token: "synthetic-xsrf" }); assert.equal(await app.authTokenRelay.requestTokenRelay(), "synthetic"); app.debugState.flush(); assert.equal(calls, 1); assert.equal(app.debugState.requestLog[0].status, "success"); }); test("Token Relay times out and records failure when fetch never completes", async () => { let signal; const app = loadApplication("content", { AbortController, setTimeout: (callback, delay) => setTimeout(callback, delay === 30000 ? 5 : delay), clearTimeout, fetch: (_url, options) => { signal = options.signal; return new Promise(() => {}); } }); app.debugContentView.renderDebugPanel = () => {}; app.authXsrfContent.getXsrfToken = async () => ({ token: "synthetic-xsrf" }); await assert.rejects(app.authTokenRelay.requestTokenRelay(), /timed out/); app.debugState.flush(); assert.equal(signal.aborted, true); assert.equal(app.debugState.requestLog[0].status, "failed"); }); test("debug stores individual complete records, reloads Dates and preserves pending request handles", () => { const localStorage = storage(); const state = factory({ localStorage, crypto: { randomUUID: () => "tab-a" } }); state.reset(); const entry = { id: 1, startedAt: new Date(), completedAt: null, responseBody: "x".repeat(100000), requestHeaders: { Authorization: "synthetic" } }; state.requestLog.push(entry); state.persist(entry); assert.equal(JSON.parse(localStorage.getItem("opportunities-extension-debug:tab-a:1")).responseBody.length, 100000); entry.responseBody = "changed in memory"; state.reload(); assert.equal(state.requestLog[0], entry); assert.equal(entry.responseBody.length, 100000); assert.equal(typeof entry.startedAt.getTime, "function"); }); test("reset clears only this tab's debug records and ignores old request completions", () => { const localStorage = storage(); localStorage.setItem("preferences", "preserved"); localStorage.setItem("opportunities-extension-debug:other:1", "preserved"); const state = factory({ localStorage, crypto: { randomUUID: () => "tab-a" } }); const entry = { id: 1 }; state.requestLog.push(entry); state.persist(entry); state.reset(); state.persist(entry); assert.equal(state.requestLog.length, 0); assert.equal(localStorage.getItem("opportunities-extension-debug:tab-a:1"), null); assert.equal(localStorage.getItem("preferences"), "preserved"); assert.equal(localStorage.getItem("opportunities-extension-debug:other:1"), "preserved"); }); test("quota failure retains complete data in memory without breaking requests", () => { const state = factory({ localStorage: { setItem() { throw new Error("quota"); } } }); const entry = { id: 1, responseBody: "complete" }; state.requestLog.push(entry); assert.doesNotThrow(() => state.persist(entry)); assert.match(state.storageError, /full/); state.reload(); assert.equal(state.requestLog[0].responseBody, "complete"); }); test("hidden debug has no request DOM; opening hydrates summaries and expanding mounts details", () => { const dom = createDebugDom(); dom.panel.hidden = true; const app = loadApplication("content", { document: dom.document, localStorage: storage() }); const entry = app.debugContentLog.logRequestStart({ name: "requestExample", method: "GET", url: "https://example.invalid" }); assert.equal(dom.panel.children.length, 0); app.debugContentView.toggleDebugPanel(); const find = cls => dom.descendants(dom.panel).find(node => node.className === cls); assert.equal(find("opportunities-extension-debug-details"), undefined); const row = dom.descendants(dom.panel).find(node => node.tag === "details"); row.open = true; row.listeners.toggle(); assert.ok(find("opportunities-extension-debug-details")); app.debugContentView.closeDebugPanel(); assert.equal(dom.panel.children.length, 0); app.debugState.reset(); app.debugContentLog.logRequestFailure(entry, "Late failure"); assert.equal(app.debugState.requestLog.length, 0); });