mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-02 23:56:24 +08:00
test(webui): restore browser globals after tests
This commit is contained in:
@@ -30,22 +30,28 @@ import {
|
||||
} from "./api";
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
const originalSessionStorage = globalThis.sessionStorage;
|
||||
const restoreBrowserGlobals: Array<() => void> = [];
|
||||
|
||||
function installSessionToken(token: string) {
|
||||
replaceBrowserGlobal(
|
||||
"sessionStorage",
|
||||
createMemoryStorage(token ? { ironclaw_token: token } : {}),
|
||||
restoreBrowserGlobals.push(
|
||||
replaceBrowserGlobal(
|
||||
"sessionStorage",
|
||||
createMemoryStorage(token ? { ironclaw_token: token } : {}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function installWindowOrigin(origin: string) {
|
||||
replaceBrowserGlobal("window", { location: { origin } });
|
||||
restoreBrowserGlobals.push(
|
||||
replaceBrowserGlobal("window", { location: { origin } }),
|
||||
);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
globalThis.sessionStorage = originalSessionStorage;
|
||||
while (restoreBrowserGlobals.length > 0) {
|
||||
restoreBrowserGlobals.pop()?.();
|
||||
}
|
||||
});
|
||||
|
||||
function withCryptoGlobal(replacement, run) {
|
||||
@@ -185,6 +191,7 @@ test("listThreads passes pagination and candidate-thread filters", async () => {
|
||||
const calls = [];
|
||||
const controller = new AbortController();
|
||||
installSessionToken("token-1");
|
||||
installWindowOrigin("http://localhost");
|
||||
globalThis.fetch = async (path, options) => {
|
||||
calls.push({ path, options });
|
||||
return new Response(JSON.stringify({ threads: [] }), {
|
||||
@@ -484,15 +491,18 @@ test("fetchAttachmentDataUrl returns a data URL and never mints a blob URL", asy
|
||||
globalThis.URL.createObjectURL = () => {
|
||||
throw new Error("blob: URLs violate the SPA CSP img-src 'self' data:");
|
||||
};
|
||||
replaceBrowserGlobal("FileReader", class FakeFileReader {
|
||||
result = "";
|
||||
onload: (() => void) | null = null;
|
||||
const restoreFileReader = replaceBrowserGlobal(
|
||||
"FileReader",
|
||||
class FakeFileReader {
|
||||
result = "";
|
||||
onload: (() => void) | null = null;
|
||||
|
||||
readAsDataURL() {
|
||||
this.result = "data:image/png;base64,AQIDBA==";
|
||||
if (this.onload) this.onload();
|
||||
}
|
||||
});
|
||||
readAsDataURL() {
|
||||
this.result = "data:image/png;base64,AQIDBA==";
|
||||
if (this.onload) this.onload();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
const url = await fetchAttachmentDataUrl(
|
||||
@@ -501,6 +511,7 @@ test("fetchAttachmentDataUrl returns a data URL and never mints a blob URL", asy
|
||||
assert.ok(typeof url === "string" && url.startsWith("data:"), `expected a data URL, got ${url}`);
|
||||
} finally {
|
||||
globalThis.URL.createObjectURL = priorCreateObjectURL;
|
||||
restoreFileReader();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// asset bundle, so this file is never served to the browser.
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { beforeEach, test } from "vitest";
|
||||
import { afterEach, beforeEach, test } from "vitest";
|
||||
import {
|
||||
createMemoryStorage,
|
||||
replaceBrowserGlobal,
|
||||
@@ -23,9 +23,11 @@ import {
|
||||
|
||||
// Minimal localStorage stub. The store reads `window.localStorage` lazily, so
|
||||
// installing it on the global before the calls is enough.
|
||||
let restoreWindow: (() => void) | null = null;
|
||||
|
||||
function installStorage() {
|
||||
const storage = createMemoryStorage();
|
||||
replaceBrowserGlobal("window", { localStorage: storage });
|
||||
restoreWindow = replaceBrowserGlobal("window", { localStorage: storage });
|
||||
return storage;
|
||||
}
|
||||
|
||||
@@ -36,6 +38,11 @@ beforeEach(() => {
|
||||
clearAllPins();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreWindow?.();
|
||||
restoreWindow = null;
|
||||
});
|
||||
|
||||
test("togglePin round-trips with isPinned", () => {
|
||||
assert.equal(isPinned("t1"), false);
|
||||
togglePin("t1");
|
||||
@@ -96,7 +103,8 @@ test("clearAllPins resets the set and removes pin keys but leaves others", () =>
|
||||
});
|
||||
|
||||
test("storage failures are swallowed (in-memory still works)", () => {
|
||||
replaceBrowserGlobal("window", {
|
||||
restoreWindow?.();
|
||||
restoreWindow = replaceBrowserGlobal("window", {
|
||||
localStorage: {
|
||||
getItem: () => {
|
||||
throw new Error("quota / private mode");
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// carries. Everything else is pure.
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { beforeAll as before, test } from "vitest";
|
||||
import { afterAll as after, beforeAll as before, test } from "vitest";
|
||||
|
||||
import { replaceBrowserGlobal } from "../../../test-support/browser-mocks";
|
||||
|
||||
@@ -21,6 +21,8 @@ import {
|
||||
toWireAttachment,
|
||||
} from "./attachments";
|
||||
|
||||
let restoreFileReader: (() => void) | null = null;
|
||||
|
||||
before(() => {
|
||||
class FakeFileReader {
|
||||
result: string | ArrayBuffer | null = null;
|
||||
@@ -33,7 +35,12 @@ before(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
replaceBrowserGlobal("FileReader", FakeFileReader);
|
||||
restoreFileReader = replaceBrowserGlobal("FileReader", FakeFileReader);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
restoreFileReader?.();
|
||||
restoreFileReader = null;
|
||||
});
|
||||
|
||||
// A fake `File` carrying its own data URL so the stubbed reader is
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// asset bundle, so this file is never served to the browser.
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { beforeEach, test } from "vitest";
|
||||
import { afterEach, beforeEach, test } from "vitest";
|
||||
import { setAuthScope } from "../../../lib/auth-scope";
|
||||
import {
|
||||
createMemoryStorage,
|
||||
@@ -24,9 +24,11 @@ import {
|
||||
// Minimal localStorage stub — the store reads `window.localStorage` lazily
|
||||
// inside each function, so installing it on the global before the calls is
|
||||
// enough (the module never touches storage at import time).
|
||||
let restoreWindow: (() => void) | null = null;
|
||||
|
||||
function installStorage() {
|
||||
const storage = createMemoryStorage();
|
||||
replaceBrowserGlobal("window", { localStorage: storage });
|
||||
restoreWindow = replaceBrowserGlobal("window", { localStorage: storage });
|
||||
return storage;
|
||||
}
|
||||
|
||||
@@ -35,6 +37,11 @@ beforeEach(() => {
|
||||
setAuthScope(null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreWindow?.();
|
||||
restoreWindow = null;
|
||||
});
|
||||
|
||||
test("drafts are isolated per authenticated user across a session switch", () => {
|
||||
// Regression: signing out and a different user signing into the same tab
|
||||
// must not restore the previous user's unsent draft (the new-conversation
|
||||
@@ -118,7 +125,8 @@ test("storage failures are swallowed (best-effort persistence)", () => {
|
||||
storage.removeItem = () => {
|
||||
throw new Error("quota / private mode");
|
||||
};
|
||||
replaceBrowserGlobal("window", { localStorage: storage });
|
||||
restoreWindow?.();
|
||||
restoreWindow = replaceBrowserGlobal("window", { localStorage: storage });
|
||||
assert.doesNotThrow(() => setDraft("thread-1", "x"));
|
||||
assert.equal(getDraft("thread-1"), "");
|
||||
assert.doesNotThrow(() => clearDraft("thread-1"));
|
||||
|
||||
@@ -33,12 +33,12 @@ function jsonResponse(body, status = 200) {
|
||||
|
||||
function installFetch(handler) {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const originalSessionStorage = globalThis.sessionStorage;
|
||||
const originalWindow = globalThis.window;
|
||||
const calls = [];
|
||||
|
||||
replaceBrowserGlobal("window", { location: { origin: "http://localhost" } });
|
||||
replaceBrowserGlobal(
|
||||
const restoreWindow = replaceBrowserGlobal("window", {
|
||||
location: { origin: "http://localhost" },
|
||||
});
|
||||
const restoreSessionStorage = replaceBrowserGlobal(
|
||||
"sessionStorage",
|
||||
createMemoryStorage({ ironclaw_token: "token-1" }),
|
||||
);
|
||||
@@ -51,8 +51,8 @@ function installFetch(handler) {
|
||||
calls,
|
||||
restore() {
|
||||
globalThis.fetch = originalFetch;
|
||||
globalThis.sessionStorage = originalSessionStorage;
|
||||
globalThis.window = originalWindow;
|
||||
restoreSessionStorage();
|
||||
restoreWindow();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user