mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 22:31:46 +00:00
fix(tests): resolve URL.createObjectURL and fetch mocking failures on CI
Three interrelated issues caused 4 tests to pass locally but fail on CI: 1. setup.ts only applied the URL.createObjectURL stub when it was undefined, but jsdom already defines it (returning ''). Changed to always override with configurable:true so the predictable 'blob:mock' value is set in every environment. 2. FE-API-013 used Object.defineProperty (non-configurable in jsdom) and MSW to handle a native fetch call. Replaced with vi.spyOn for both URL.createObjectURL/revokeObjectURL and a direct fetch mock, which is more reliable across environments. 3. FE-COMP-AUTHURL-012's vi.spyOn(URL, 'createObjectURL') returned the same vi.fn() instance set in setup.ts, accumulating calls from all prior tests in the file (1+8+7+6=22 instead of 6). Added mockClear() immediately after the spy setup to reset the count.
This commit is contained in:
@@ -334,10 +334,14 @@ describe('API client interceptors', () => {
|
|||||||
// ── backupApi.download ───────────────────────────────────────────────────────
|
// ── backupApi.download ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
it('FE-API-013: backupApi.download creates a temp anchor and clicks it', async () => {
|
it('FE-API-013: backupApi.download creates a temp anchor and clicks it', async () => {
|
||||||
const createObjectURL = vi.fn(() => 'blob:mock-url');
|
// backupApi.download uses native fetch (not axios), so mock it directly to
|
||||||
const revokeObjectURL = vi.fn();
|
// avoid jsdom/MSW interception differences across environments.
|
||||||
Object.defineProperty(URL, 'createObjectURL', { writable: true, value: createObjectURL });
|
const blob = new Blob(['zip-bytes'], { type: 'application/zip' });
|
||||||
Object.defineProperty(URL, 'revokeObjectURL', { writable: true, value: revokeObjectURL });
|
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce(
|
||||||
|
new Response(blob, { status: 200 })
|
||||||
|
);
|
||||||
|
const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock-url');
|
||||||
|
const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {});
|
||||||
|
|
||||||
// Spy on createElement to intercept the anchor click
|
// Spy on createElement to intercept the anchor click
|
||||||
const originalCreate = document.createElement.bind(document);
|
const originalCreate = document.createElement.bind(document);
|
||||||
@@ -350,12 +354,6 @@ describe('API client interceptors', () => {
|
|||||||
return el;
|
return el;
|
||||||
});
|
});
|
||||||
|
|
||||||
server.use(
|
|
||||||
http.get('/api/backup/download/backup.zip', () => {
|
|
||||||
return new HttpResponse(new Blob(['zip-bytes'], { type: 'application/zip' }), { status: 200 });
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(backupApi.download('backup.zip')).resolves.toBeUndefined();
|
await expect(backupApi.download('backup.zip')).resolves.toBeUndefined();
|
||||||
expect(createObjectURL).toHaveBeenCalled();
|
expect(createObjectURL).toHaveBeenCalled();
|
||||||
expect(revokeObjectURL).toHaveBeenCalled();
|
expect(revokeObjectURL).toHaveBeenCalled();
|
||||||
|
|||||||
@@ -59,13 +59,10 @@ globalThis.ResizeObserver = vi.fn().mockImplementation(() => ({
|
|||||||
disconnect: vi.fn(),
|
disconnect: vi.fn(),
|
||||||
})) as unknown as typeof ResizeObserver;
|
})) as unknown as typeof ResizeObserver;
|
||||||
|
|
||||||
// URL.createObjectURL / revokeObjectURL — used by file uploads
|
// URL.createObjectURL / revokeObjectURL — jsdom defines these but returns '' for createObjectURL;
|
||||||
if (typeof URL.createObjectURL === 'undefined') {
|
// always override so tests get a predictable 'blob:mock' string.
|
||||||
Object.defineProperty(URL, 'createObjectURL', { writable: true, value: vi.fn(() => 'blob:mock') });
|
Object.defineProperty(URL, 'createObjectURL', { writable: true, configurable: true, value: vi.fn(() => 'blob:mock') });
|
||||||
}
|
Object.defineProperty(URL, 'revokeObjectURL', { writable: true, configurable: true, value: vi.fn() });
|
||||||
if (typeof URL.revokeObjectURL === 'undefined') {
|
|
||||||
Object.defineProperty(URL, 'revokeObjectURL', { writable: true, value: vi.fn() });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Element.prototype.scrollIntoView — jsdom doesn't implement it
|
// Element.prototype.scrollIntoView — jsdom doesn't implement it
|
||||||
Element.prototype.scrollIntoView = vi.fn();
|
Element.prototype.scrollIntoView = vi.fn();
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ describe('clearImageQueue', () => {
|
|||||||
it('removes queued items so they never execute after active slots drain', async () => {
|
it('removes queued items so they never execute after active slots drain', async () => {
|
||||||
const resolvers: Array<() => void> = [];
|
const resolvers: Array<() => void> = [];
|
||||||
const createObjectURLSpy = vi.spyOn(URL, 'createObjectURL');
|
const createObjectURLSpy = vi.spyOn(URL, 'createObjectURL');
|
||||||
|
createObjectURLSpy.mockClear(); // vi.spyOn returns the same vi.fn() set in setup.ts; reset accumulated calls from prior tests
|
||||||
|
|
||||||
vi.spyOn(globalThis, 'fetch').mockImplementation(async () => {
|
vi.spyOn(globalThis, 'fetch').mockImplementation(async () => {
|
||||||
await new Promise<void>(r => resolvers.push(r));
|
await new Promise<void>(r => resolvers.push(r));
|
||||||
|
|||||||
Reference in New Issue
Block a user