feat: v4.0.0 (#1016)

Co-authored-by: Denys Konovalov <kontakt@denyskon.de>
Co-authored-by: Mathieu COSYNS <64072917+Mathieu-COSYNS@users.noreply.github.com>
This commit is contained in:
Daniel Lautzenheiser
2024-01-03 15:14:09 -05:00
committed by GitHub
parent 682576ffc4
commit 799c7e6936
732 changed files with 48477 additions and 10886 deletions

View File

@ -1,3 +1,5 @@
import { isNotNullish } from '@staticcms/core/lib/util/null.util';
export const createMockRequest = <T>(
status: number,
data: {
@ -35,44 +37,87 @@ export const createMockRequest = <T>(
} as Response;
};
export type FetchMethod = 'GET' | 'POST' | 'PUT' | 'HEAD';
export type QueryCheckFunc = (query: URLSearchParams) => boolean;
export interface RequestData {
query: string | true | QueryCheckFunc;
response: MockResponse<unknown> | MockResponseFunc<unknown>;
limit?: number;
used?: number;
}
export type ReplyFunc = <T>(response: MockResponse<T> | MockResponseFunc<T>) => void;
export type RepeatFunc = (limit: number) => { reply: ReplyFunc };
export interface MockFetch {
baseUrl: string;
mocks: Record<string, Response>;
when: (url: string) => {
reply: <T>(
status: number,
data: {
json?: T;
text?: string;
},
options?: {
contentType?: string;
headers?: Record<string, string>;
},
) => void;
mocks: Record<string, Partial<Record<FetchMethod, RequestData[]>>>;
when: (
method: FetchMethod,
url: string,
) => {
query: (query: string | true | QueryCheckFunc) => { reply: ReplyFunc; repeat: RepeatFunc };
repeat: RepeatFunc;
reply: ReplyFunc;
};
reset: () => void;
}
export interface MockResponse<T> {
status: number;
json?: T;
text?: string;
contentType?: string;
headers?: Record<string, string>;
}
export type MockResponseFunc<T> = (url: string) => MockResponse<T> | Promise<MockResponse<T>>;
const mockFetch = (baseUrl: string): MockFetch => {
const mockedFetch: MockFetch = {
baseUrl,
mocks: {},
when(this: MockFetch, url: string) {
// eslint-disable-next-line object-shorthand
when: function (this: MockFetch, method: FetchMethod, url: string) {
const reply =
(query: string | true | QueryCheckFunc = '') =>
(limit?: number) =>
<T>(response: MockResponse<T> | MockResponseFunc<T>) => {
const fullUrl = `${baseUrl}${url}`;
if (!(fullUrl in this.mocks)) {
this.mocks[fullUrl] = {};
}
if (!(method in this.mocks[fullUrl])) {
this.mocks[fullUrl][method] = [];
}
this.mocks[fullUrl][method]?.push({
query,
response,
limit,
});
};
const repeat =
(query: string | true | QueryCheckFunc = '') =>
(limit: number) => {
return {
reply: reply(query)(limit),
};
};
return {
reply: <T>(
status: number,
data: {
json?: T;
text?: string;
},
options?: {
contentType?: string;
headers?: Record<string, string>;
},
) => {
this.mocks[`${baseUrl}${url}`] = createMockRequest(status, data, options);
query: (query: string | true | QueryCheckFunc) => {
return {
repeat: repeat(query),
reply: reply(query)(),
};
},
repeat: repeat(),
reply: reply()(),
};
},
reset(this: MockFetch) {
@ -80,9 +125,66 @@ const mockFetch = (baseUrl: string): MockFetch => {
},
};
global.fetch = jest.fn().mockImplementation((url: string) => {
return Promise.resolve(mockedFetch.mocks[url.split('?')[0]]);
});
global.fetch = jest
.fn()
.mockImplementation(async (fullUrl: string, { method = 'GET' }: { method: FetchMethod }) => {
const [url, ...rest] = fullUrl.split('?');
const query = rest.length > 0 ? rest[0] : '';
const mockResponses = [...(mockedFetch.mocks[url]?.[method] ?? [])];
if (!mockResponses) {
return Promise.resolve(undefined);
}
for (let i = 0; i < mockResponses.length; i++) {
const mockResponse = mockResponses[i];
const limit = mockResponse.limit;
const used = mockResponse.used ?? 0;
if (isNotNullish(limit)) {
if (used >= limit) {
continue;
}
}
if (isNotNullish(mockResponse.query) && mockResponse.query !== true) {
if (typeof mockResponse.query === 'string') {
if (mockResponse.query !== query) {
continue;
}
} else if (!mockResponse.query(new URLSearchParams(query))) {
continue;
}
}
let responseData = mockResponse.response;
if (typeof responseData === 'function') {
responseData = await responseData(fullUrl);
}
const response = createMockRequest(
responseData.status,
{
json: responseData.json,
text: responseData.text,
},
{
contentType: responseData.contentType,
headers: responseData.headers,
},
);
mockedFetch.mocks[url][method]![i] = {
...mockResponse,
used: used + 1,
};
return Promise.resolve(response);
}
return Promise.resolve(undefined);
});
return mockedFetch;
};