122 lines
6.5 KiB
JavaScript
122 lines
6.5 KiB
JavaScript
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);
|
|
});
|