17c5b33a5b
Add support for HTTP methods, status codes, body and headers in the fetch mock. Track calls and expose getFetchCalls for assertions. Update fixture interfaces to use string IDs, add ImportResult and ApiError types, and provide standard error constants. Adjust fixture data to match new types.
120 lines
3.1 KiB
TypeScript
120 lines
3.1 KiB
TypeScript
// Mock de fetch() pour les tests — supporte méthodes HTTP et status codes
|
|
|
|
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
|
|
export interface MockRoute {
|
|
method?: HttpMethod;
|
|
status?: number;
|
|
body?: unknown;
|
|
headers?: Record<string, string>;
|
|
}
|
|
|
|
// deno-lint-ignore no-explicit-any
|
|
let _originalFetch: ((input: any, init?: any) => Promise<Response>) | null =
|
|
null;
|
|
let _calls: { url: string; method: string; body?: unknown }[] = [];
|
|
|
|
/**
|
|
* Remplace globalThis.fetch par un mock configurable.
|
|
*
|
|
* Usage simple (GET 200 par défaut) :
|
|
* mockFetch({ "/students": studentsData })
|
|
*
|
|
* Usage avancé (méthode + status) :
|
|
* mockFetch({ "/students": { method: "POST", status: 201, body: newStudent } })
|
|
*/
|
|
export function mockFetch(
|
|
routes: Record<string, unknown | MockRoute>,
|
|
): void {
|
|
_originalFetch = globalThis.fetch;
|
|
_calls = [];
|
|
|
|
globalThis.fetch = async (
|
|
input: string | URL | Request,
|
|
init?: RequestInit,
|
|
): Promise<Response> => {
|
|
const url = typeof input === "string"
|
|
? input
|
|
: input instanceof URL
|
|
? input.toString()
|
|
: input.url;
|
|
const method = (init?.method ?? "GET").toUpperCase();
|
|
|
|
// Parse le body si présent
|
|
let reqBody: unknown = undefined;
|
|
if (init?.body) {
|
|
try {
|
|
reqBody = JSON.parse(init.body as string);
|
|
} catch {
|
|
reqBody = init.body;
|
|
}
|
|
}
|
|
|
|
_calls.push({ url, method, body: reqBody });
|
|
|
|
for (const [pattern, config] of Object.entries(routes)) {
|
|
if (!url.includes(pattern)) continue;
|
|
|
|
// Config simple : la valeur est directement le body de réponse (GET 200)
|
|
if (!isRouteConfig(config)) {
|
|
return new Response(JSON.stringify(config), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
|
|
// Config avancée : vérifier la méthode si spécifiée
|
|
if (config.method && config.method !== method) continue;
|
|
|
|
const status = config.status ?? 200;
|
|
|
|
// 204 : pas de body
|
|
if (status === 204) {
|
|
return new Response(null, { status: 204 });
|
|
}
|
|
|
|
return new Response(
|
|
config.body !== undefined ? JSON.stringify(config.body) : null,
|
|
{
|
|
status,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...config.headers,
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
return new Response(JSON.stringify({ error: "Not Found" }), {
|
|
status: 404,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Restaure le fetch original.
|
|
*/
|
|
export function restoreFetch(): void {
|
|
if (_originalFetch) {
|
|
globalThis.fetch = _originalFetch;
|
|
_originalFetch = null;
|
|
}
|
|
_calls = [];
|
|
}
|
|
|
|
/**
|
|
* Retourne la liste des appels fetch interceptés.
|
|
*/
|
|
export function getFetchCalls(): { url: string; method: string; body?: unknown }[] {
|
|
return [..._calls];
|
|
}
|
|
|
|
function isRouteConfig(value: unknown): value is MockRoute {
|
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
return false;
|
|
}
|
|
const v = value as Record<string, unknown>;
|
|
return "status" in v || "method" in v || "body" in v;
|
|
}
|