diff --git a/client/tests/setup.ts b/client/tests/setup.ts index 0d6373e3..d801eee1 100644 --- a/client/tests/setup.ts +++ b/client/tests/setup.ts @@ -59,10 +59,11 @@ globalThis.ResizeObserver = vi.fn().mockImplementation(() => ({ disconnect: vi.fn(), })) as unknown as typeof ResizeObserver; -// URL.createObjectURL / revokeObjectURL — jsdom defines these but returns '' for createObjectURL; -// always override so tests get a predictable 'blob:mock' string. -Object.defineProperty(URL, 'createObjectURL', { writable: true, configurable: true, value: vi.fn(() => 'blob:mock') }); -Object.defineProperty(URL, 'revokeObjectURL', { writable: true, configurable: true, value: vi.fn() }); +// URL.createObjectURL / revokeObjectURL — in jsdom, modules access globals +// through window.URL (not globalThis.URL — these are different objects on CI). +// Targeting window.URL is required so the mock is visible to source modules. +Object.defineProperty(window.URL, 'createObjectURL', { writable: true, configurable: true, value: vi.fn(() => 'blob:mock') }); +Object.defineProperty(window.URL, 'revokeObjectURL', { writable: true, configurable: true, value: vi.fn() }); // Element.prototype.scrollIntoView — jsdom doesn't implement it Element.prototype.scrollIntoView = vi.fn(); diff --git a/client/tests/unit/api/authUrl.test.ts b/client/tests/unit/api/authUrl.test.ts index c0906b2b..c7e68362 100644 --- a/client/tests/unit/api/authUrl.test.ts +++ b/client/tests/unit/api/authUrl.test.ts @@ -8,19 +8,9 @@ const flushPromises = () => new Promise(r => setTimeout(r, 10)); beforeEach(() => { clearImageQueue(); - vi.restoreAllMocks(); // restore any vi.spyOn() wrappers from the previous test - vi.unstubAllGlobals(); // restore any vi.stubGlobal() replacements from the previous test - - // jsdom's URL.createObjectURL is non-writable/non-configurable in some CI - // environments — direct assignment and Object.defineProperty both fail - // silently. vi.stubGlobal replaces globalThis.URL entirely, which always - // works. We extend the real URL so all URL parsing behaviour is preserved. - const OriginalURL = URL; - class TestURL extends OriginalURL { - static createObjectURL = vi.fn(() => 'blob:mock'); - static revokeObjectURL = vi.fn(); - } - vi.stubGlobal('URL', TestURL); + vi.restoreAllMocks(); // remove vi.spyOn() wrappers, restoring to the setup.ts vi.fn() + vi.clearAllMocks(); // reset accumulated call counts on window.URL mocks from setup.ts + vi.unstubAllGlobals(); }); // ── getAuthUrl ───────────────────────────────────────────────────────────────── @@ -204,7 +194,7 @@ describe('clearImageQueue', () => { describe('FE-COMP-AUTHURL-012: clearImageQueue discards pending entries', () => { it('removes queued items so they never execute after active slots drain', async () => { const resolvers: Array<() => void> = []; - const createObjectURLSpy = vi.spyOn(URL, 'createObjectURL'); + const createObjectURLSpy = vi.spyOn(window.URL, 'createObjectURL'); vi.spyOn(globalThis, 'fetch').mockImplementation(async () => { await new Promise(r => resolvers.push(r));