fix(frontend): binder le fetch global à globalThis (pairing Firefox) (#13)

Firefox exige que fetch soit appelé sur Window : passer la référence nue
`fetch` en dépendance déclenchait « 'fetch' called on an object that does
not implement interface Window » au pairing. Nouveau helper defaultFetch()
qui binde globalThis.fetch, utilisé par webSession.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 08:47:00 +02:00
parent c9ce3d7c4e
commit de9c90966d
4 changed files with 141 additions and 7 deletions

View File

@ -91,6 +91,44 @@ describe("WebSession unauthorized handling", () => {
});
});
describe("WebSession default fetch receiver", () => {
/**
* Regression (live Firefox, ticket #13): the default global `fetch` was stored
* unbound, so `this.fetchImpl(…)` called it with the WebSession as receiver ⇒
* `TypeError: 'fetch' called on an object that does not implement interface
* Window` at pairing. jsdom does not enforce the receiver, so assert it here.
*/
async function pairWithRecordingGlobalFetch(): Promise<unknown[]> {
const original = globalThis.fetch;
const receivers: unknown[] = [];
globalThis.fetch = function (this: unknown) {
receivers.push(this);
return Promise.resolve({
ok: true,
status: 200,
json: async () => ({ ok: true }),
text: async () => "{}",
});
} as unknown as typeof globalThis.fetch;
try {
// No `fetchImpl` injected ⇒ the global fallback is used.
const session = new WebSession({ baseUrl: "https://host", store: memStore() });
await session.pair("4821-93");
} finally {
globalThis.fetch = original;
}
return receivers;
}
it("pairs using the global fetch bound to globalThis, not the session", async () => {
const receivers = await pairWithRecordingGlobalFetch();
expect(receivers).toHaveLength(1);
expect(receivers[0]).toBe(globalThis);
expect(receivers[0]).not.toBeInstanceOf(WebSession);
});
});
describe("WebSession logout", () => {
it("POSTs /api/logout same-origin and clears the flag on success", async () => {
const store = memStore();