UNPKG

vitest-fetch-mock

Version:
323 lines 12 kB
import { vi as vitest } from 'vitest'; class FetchMockObject { mockedFetch; originalFetch; chainingResultProvider; isMocking = vitest.fn(always(true)); constructor(mockedFetch, originalFetch, chainingResultProvider) { this.mockedFetch = mockedFetch; this.originalFetch = originalFetch; this.chainingResultProvider = chainingResultProvider; } // enable/disable enableMocks() { globalThis.fetch = this.mockedFetch; globalThis.fetchMock = this.chainingResultProvider(); return this.chainingResultProvider(); } disableMocks() { globalThis.fetch = this.originalFetch; return this.chainingResultProvider(); } // reset resetMocks() { this.mockedFetch.mockRestore(); return this.chainingResultProvider(); } // mocking functions mockResponse(responseProvider, params) { this.mockedFetch.mockImplementation((input, requestInit) => { if (!this.isMocking(input, requestInit)) { return this.originalFetch(input, requestInit); } const request = normalizeRequest(input, requestInit); return buildResponse(request, responseProvider, params); }); return this.chainingResultProvider(); } mockResponseOnce(responseProvider, params) { this.mockedFetch.mockImplementationOnce((input, requestInit) => { if (!this.isMocking(input, requestInit)) { return this.originalFetch(input, requestInit); } const request = normalizeRequest(input, requestInit); return buildResponse(request, responseProvider, params); }); return this.chainingResultProvider(); } mockResponseIf(urlOrPredicate, responseProvider, params) { this.mockedFetch.mockImplementation((input, requestInit) => { if (!this.isMocking(input, requestInit)) { return this.originalFetch(input, requestInit); } const request = normalizeRequest(input, requestInit); return requestMatches(request, urlOrPredicate) ? buildResponse(request, responseProvider, params) : this.originalFetch(input, requestInit); }); return this.chainingResultProvider(); } mockResponseOnceIf(urlOrPredicate, responseProvider, params) { this.isMocking.mockImplementationOnce((input, requestInit) => requestMatches(normalizeRequest(input, requestInit), urlOrPredicate)); this.mockedFetch.mockImplementationOnce((input, requestInit) => { if (!this.isMocking(input, requestInit)) { return this.originalFetch(input, requestInit); } const request = normalizeRequest(input, requestInit); return requestMatches(request, urlOrPredicate) ? buildResponse(request, responseProvider, params) : this.originalFetch(input, requestInit); }); return this.chainingResultProvider(); } mockResponses(...responses) { responses.forEach((response) => { if (Array.isArray(response)) { const [body, init] = response; this.mockedFetch.mockImplementationOnce((input) => { if (!this.isMocking(input)) { return this.originalFetch(input); } const request = normalizeRequest(input); return buildResponse(request, body, init); }); } else { this.mockedFetch.mockImplementationOnce((input) => { if (!this.isMocking(input)) { return this.originalFetch(input); } const request = normalizeRequest(input); return buildResponse(request, response); }); } }); return this.chainingResultProvider(); } // abort mockAbort() { this.mockedFetch.mockImplementation(() => abortAsync()); return this.chainingResultProvider(); } mockAbortOnce() { this.mockedFetch.mockImplementationOnce(() => abortAsync()); return this.chainingResultProvider(); } // reject (error) mockReject(error) { this.mockedFetch.mockImplementation((input, requestInit) => normalizeError(normalizeRequest(input, requestInit), error)); return this.chainingResultProvider(); } mockRejectOnce(error) { this.mockedFetch.mockImplementationOnce((input, requestInit) => normalizeError(normalizeRequest(input, requestInit), error)); return this.chainingResultProvider(); } // enable/disable doMock(responseProvider, params) { this.isMocking.mockImplementation(always(true)); if (responseProvider) { this.mockResponse(responseProvider, params); } return this.chainingResultProvider(); } doMockOnce(responseProvider, params) { this.isMocking.mockImplementationOnce(always(true)); if (responseProvider) { this.mockResponseOnce(responseProvider, params); } return this.chainingResultProvider(); } doMockIf(urlOrPredicate, responseProvider, params) { this.isMocking.mockImplementation((input, requestInit) => requestMatches(normalizeRequest(input, requestInit), urlOrPredicate)); if (responseProvider) { this.mockResponse(responseProvider, params); } return this.chainingResultProvider(); } doMockOnceIf(urlOrPredicate, responseProvider, params) { this.isMocking.mockImplementationOnce((input, requestInit) => requestMatches(normalizeRequest(input, requestInit), urlOrPredicate)); if (responseProvider) { this.mockResponseOnce(responseProvider, params); } return this.chainingResultProvider(); } dontMock() { this.isMocking.mockImplementation(always(false)); return this.chainingResultProvider(); } dontMockOnce() { this.isMocking.mockImplementationOnce(always(false)); return this.chainingResultProvider(); } dontMockIf(urlOrPredicate) { this.isMocking.mockImplementation((input, requestInit) => requestNotMatches(normalizeRequest(input, requestInit), urlOrPredicate)); return this.chainingResultProvider(); } dontMockOnceIf(urlOrPredicate) { this.isMocking.mockImplementationOnce((input, requestInit) => requestNotMatches(normalizeRequest(input, requestInit), urlOrPredicate)); return this.chainingResultProvider(); } // recording requests() { return this.mockedFetch.mock.calls .map(([input, requestInit]) => { try { return normalizeRequest(input, requestInit); } catch (e) { return undefined; } }) .filter((it) => it !== undefined); } // aliases /** * alias for mockResponseOnce */ once(responseProvider, params) { return this.mockResponseOnce(responseProvider, params); } /** * alias for doMockOnce */ mockOnce(responseProvider, params) { return this.doMockOnce(responseProvider, params); } /** * alias for doMockIf */ mockIf(urlOrPredicate, responseProvider, params) { return this.doMockIf(urlOrPredicate, responseProvider, params); } /** * alias for doMockOnceIf */ mockOnceIf(urlOrPredicate, responseProvider, params) { return this.doMockOnceIf(urlOrPredicate, responseProvider, params); } } // factory export default function createFetchMock(vi) { const isMocking = vi.fn(always(true)); const originalFetch = globalThis.fetch; const mockedFetch = vi.fn((input, requestInit) => { if (!isMocking(input, requestInit)) { return originalFetch(input, requestInit); } return buildResponse(normalizeRequest(input, requestInit), ''); }); const fetchMock = mockedFetch; const fetchMockObject = new FetchMockObject(mockedFetch, originalFetch, () => fetchMock); copyMethods(fetchMockObject, fetchMock); return mockedFetch; } function requestMatches(request, urlOrPredicate) { if (urlOrPredicate instanceof RegExp) { return urlOrPredicate.test(request.url); } else if (typeof urlOrPredicate === 'string') { return request.url === urlOrPredicate; } else { return urlOrPredicate(request); } } function requestNotMatches(request, urlOrPredicate) { return !requestMatches(request, urlOrPredicate); } // Node 18 does not support URL.canParse() export function canParseURL(url) { try { new URL(url); return true; } catch (err) { return false; } } // Node Requests cannot be relative function resolveInput(input) { if (canParseURL(input)) return input; // Window context if (typeof window !== 'undefined' && typeof window.document !== 'undefined') { return new URL(input, window.document.baseURI).toString(); } // Worker context if (typeof location !== 'undefined') { return new URL(input, location.origin).toString(); } return input; } function normalizeRequest(input, requestInit) { if (input instanceof Request) { if (input.signal && input.signal.aborted) { abort(); } return input; } else if (typeof input === 'string') { if (requestInit && requestInit.signal && requestInit.signal.aborted) { abort(); } return new Request(resolveInput(input), requestInit); } else { if (requestInit && requestInit.signal && requestInit.signal.aborted) { abort(); } return new Request(resolveInput(input.toString()), requestInit); } } async function buildResponse(request, responseProvider, params) { const response = await normalizeResponse(request, responseProvider, params); if (request.signal && request.signal.aborted) { abort(); } return response; } async function normalizeResponse(request, responseProvider, params) { const responseLike = typeof responseProvider === 'function' ? await responseProvider(request) : responseProvider; if (responseLike instanceof Response) { return responseLike; } else if (typeof responseLike === 'string' || responseLike === null || responseLike === undefined) { return new Response(responseLike, params); } else { return patchUrl(new Response(responseLike.body, { ...params, ...responseLike }), responseLike.url ?? params?.url); } } // see: https://stackoverflow.com/questions/70002424/url-is-empty-when-defining-a-response-object function patchUrl(response, url) { if (url) { Object.defineProperty(response, 'url', { value: url }); } return response; } function always(toggle) { return () => toggle; } const normalizeError = async (request, errorOrFunction) => errorOrFunction instanceof Error ? Promise.reject(errorOrFunction) : typeof errorOrFunction === 'function' ? buildResponse(request, errorOrFunction) : Promise.reject(errorOrFunction); function abortError() { return new DOMException('The operation was aborted.', 'AbortError'); } function abort() { throw abortError(); } function abortAsync() { return Promise.reject(abortError()); } function copyMethods(source, target) { Object.getOwnPropertyNames(FetchMockObject.prototype).forEach((propertyName) => { const propertyFromSource = source[propertyName]; if (propertyName !== 'constructor' && typeof propertyFromSource === 'function') { target[propertyName] = propertyFromSource.bind(source); } }); } //# sourceMappingURL=index.js.map