Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat / typeIndexHelpers happy path tests with mocked fetch #22

Merged
merged 3 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions jest.config.js

This file was deleted.

11 changes: 11 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { JestConfigWithTsJest } from "ts-jest";

const jestConfig: JestConfigWithTsJest = {
preset: "ts-jest",
// automock: true,
testRegex: "\\.test\\.ts$",
testEnvironment: "jsdom",
setupFilesAfterEnv: ["<rootDir>/test/setup.ts"],
};

export default jestConfig;
126 changes: 126 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"jest-environment-jsdom": "^29.7.0",
"jsdom": "^22.1.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
},
"dependencies": {
Expand Down
167 changes: 167 additions & 0 deletions src/StubFetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { EventEmitter } from 'events';

class StubHeaders implements Headers {
public static make(data: Record<string, string>): StubHeaders {
return new StubHeaders(data);
}

private data: Record<string, string>;

private constructor(data: Record<string, string>) {
this.data = {};

for (const [name, value] of Object.entries(data)) {
this.set(name, value);
}
}
// TODO: to make ts compiler happy
public getSetCookie(): string[] {
throw new Error("Method not implemented.");
}

public [Symbol.iterator](): IterableIterator<[string, string]> {
throw new Error("Method not implemented.");
}

public entries(): IterableIterator<[string, string]> {
throw new Error("Method not implemented.");
}

public keys(): IterableIterator<string> {
throw new Error("Method not implemented.");
}

public values(): IterableIterator<string> {
throw new Error("Method not implemented.");
}

public append(name: string, value: string): void {
this.data[this.normalizeHeader(name)] = value;
}

public delete(name: string): void {
delete this.data[this.normalizeHeader(name)];
}

public get(name: string): string | null {
return this.data[this.normalizeHeader(name)] ?? null;
}

public has(name: string): boolean {
return this.normalizeHeader(name) in this.data;
}

public set(name: string, value: string): void {
this.data[this.normalizeHeader(name)] = value;
}

public forEach(
callbackfn: (value: string, name: string, parent: Headers) => void
): void {
for (const [name, value] of Object.entries(this.data)) {
callbackfn(value, name, this);
}
}

private normalizeHeader(name: string): string {
return name.toLowerCase();
}
}


class StubResponse implements Response {

public static make(
content: string = '',
headers: Record<string, string> = {},
status: number = 200,
): StubResponse {
return new StubResponse(status, content, headers);
}

public static notFound(): StubResponse {
return new StubResponse(404);
}

private content: string;

public readonly body!: ReadableStream<Uint8Array> | null;
public readonly bodyUsed!: boolean;
public readonly headers: StubHeaders;
public readonly ok!: boolean;
public readonly redirected!: boolean;
public readonly status: number;
public readonly statusText!: string;
public readonly trailer!: Promise<Headers>;
public readonly type!: ResponseType;
public readonly url!: string;

private constructor(status: number, content: string = '', headers: Record<string, string> = {}) {
this.status = status;
this.content = content;
this.headers = StubHeaders.make(headers);
}

public async arrayBuffer(): Promise<ArrayBuffer> {
throw new Error('StubResponse.arrayBuffer is not implemented');
}

public async blob(): Promise<Blob> {
throw new Error('StubResponse.blob is not implemented');
}

public async formData(): Promise<FormData> {
throw new Error('StubResponse.formData is not implemented');
}

public async json(): Promise<unknown> {
return JSON.parse(this.content);
}

public async text(): Promise<string> {
return this.content;
}

public clone(): Response {
return { ...this };
}

}

class StubFetcher extends EventEmitter {

public fetchSpy!: jest.SpyInstance<Promise<Response>, [RequestInfo, RequestInit?]>;

private fetchResponses: Response[] = [];

public reset(): void {
this.fetchResponses = [];

this.fetchSpy.mockClear();
}

public addFetchNotFoundResponse(): void {
this.fetchResponses.push(StubResponse.notFound());
}

public addFetchResponse(content: string = '', headers: Record<string, string> = {}, status: number = 200): void {
this.fetchResponses.push(StubResponse.make(content, headers, status));
}

public async fetch(_: RequestInfo, __?: RequestInit): Promise<Response> {
const response = this.fetchResponses.shift();

if (!response) {
return new Promise((_, reject) => reject());
}

return response;
}

}

const stubFetcher = new StubFetcher();

// stubFetcher.fetchSpy = jest.spyOn(stubFetcher, 'fetch');

export default stubFetcher;
Loading