diff --git a/tests/mocks/mock-fetch.ts b/tests/mocks/mock-fetch.ts index 8a16bf19..22a67a1f 100644 --- a/tests/mocks/mock-fetch.ts +++ b/tests/mocks/mock-fetch.ts @@ -27,7 +27,13 @@ let capturedRequests: CapturedRequest[] = []; * afterEach(() => restoreFetch()); */ export function mockFetch(handlers: MockFetchHandler[]): void { - // Store original if not already mocked + // Restore previous mock if exists (prevents spy leak on double-call) + if (mockInstance) { + mockInstance.mockRestore(); + mockInstance = null; + } + + // Store original if not already stored if (!originalFetch) { originalFetch = globalThis.fetch; } @@ -64,12 +70,20 @@ export function mockFetch(handlers: MockFetchHandler[]): void { } } - // Extract body + // Extract body with type differentiation if (init?.body) { if (typeof init.body === 'string') { captured.body = init.body; } else if (init.body instanceof FormData) { captured.body = '[FormData]'; + } else if (init.body instanceof URLSearchParams) { + captured.body = '[URLSearchParams]'; + } else if (init.body instanceof ArrayBuffer || ArrayBuffer.isView(init.body)) { + captured.body = '[ArrayBuffer]'; + } else if (init.body instanceof Blob) { + captured.body = '[Blob]'; + } else if (typeof init.body === 'object' && 'getReader' in init.body) { + captured.body = '[ReadableStream]'; } else { captured.body = '[Binary]'; } diff --git a/tests/mocks/mock-http-server.ts b/tests/mocks/mock-http-server.ts index 2bde41f3..35ad0859 100644 --- a/tests/mocks/mock-http-server.ts +++ b/tests/mocks/mock-http-server.ts @@ -55,12 +55,18 @@ export function createMockHttpServer(config: MockHttpServerConfig): MockHttpServ // Try pattern matching for routes with wildcards for (const [key, response] of Object.entries(routes)) { - const [routeMethod, routePath] = key.split(' ', 2); + // Split on first space only to preserve spaces in path + const spaceIndex = key.indexOf(' '); + if (spaceIndex === -1) continue; + const routeMethod = key.slice(0, spaceIndex); + const routePath = key.slice(spaceIndex + 1); if (routeMethod !== method) continue; // Check if route path is a pattern (contains *) if (routePath.includes('*')) { - const pattern = routePath.replace(/\*/g, '.*'); + // Escape regex special chars, then replace * with .* + const escaped = routePath.replace(/[.+?^${}()|[\]\\]/g, '\\$&'); + const pattern = escaped.replace(/\*/g, '.*'); const regex = new RegExp(`^${pattern}$`); if (regex.test(path)) { return response; diff --git a/tests/mocks/types.ts b/tests/mocks/types.ts index 78046cf7..febf0b53 100644 --- a/tests/mocks/types.ts +++ b/tests/mocks/types.ts @@ -21,23 +21,11 @@ export interface MockResponse { headers?: Record; /** Delay in ms before responding (for timeout testing) */ delay?: number; - /** Throw error instead of responding */ - error?: Error; } /** Route key format: "METHOD /path" */ export type RouteKey = `${HttpMethod} ${string}`; -/** Mock route configuration */ -export interface MockRoute { - /** HTTP method */ - method: HttpMethod; - /** URL path pattern (string or regex) */ - path: string | RegExp; - /** Response to return */ - response: MockResponse; -} - /** Configuration for createMockHttpServer */ export interface MockHttpServerConfig { /** Route definitions - key format: "METHOD /path" */