Skip to content

Commit

Permalink
refactor: ♻️ setup test server for mirage and msw to be used in test …
Browse files Browse the repository at this point in the history
…cases
  • Loading branch information
singhAmandeep007 committed Jul 4, 2024
1 parent 830bc0b commit ff60f01
Show file tree
Hide file tree
Showing 24 changed files with 313 additions and 120 deletions.
3 changes: 3 additions & 0 deletions .testing.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
REACT_APP_NODE_ENV="test"
REACT_APP_PUBLIC_URL = "http://localhost"
REACT_APP_API_URL = "http://localhost/api/v1/"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"build:msw": "msw init ./build --no-save",
"analyze": "source-map-explorer 'build/static/js/*.js'",
"TEST": "------------------------------------------------------------------------",
"test": "npm run test:unit",
"test": "env-cmd -f ./.testing.env npm run test:unit",
"test:unit": "npm run tsc:check && node scripts/test.js --watchAll=false",
"test:unit:coverage": "npm run test:unit -- --coverage",
"LINT": "------------------------------------------------------------------------",
Expand Down
6 changes: 4 additions & 2 deletions src/hooks/useOnClickOutside.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ describe("useOnClickOutside", () => {

return {
result: renderHook(() => useOutsideClick({ ref, handler }), {
config: {},
config: {
withToaster: false,
},
}),
handler,
ref,
Expand Down Expand Up @@ -85,7 +87,7 @@ describe("useOnClickOutside", () => {

setup();

expect(addEventListenerSpy).toHaveBeenCalledTimes(2);
expect(addEventListenerSpy).toHaveBeenCalledTimes(1);
expect(addEventListenerSpy).toHaveBeenCalledWith("mousedown", expect.any(Function));

addEventListenerSpy.mockRestore();
Expand Down
3 changes: 3 additions & 0 deletions src/services/mocker/mirage/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export * from "./server";
export * from "./scenarios";
export * from "./utils";
export * from "./routes";
2 changes: 1 addition & 1 deletion src/services/mocker/mirage/routes/reminder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { REMINDER_STATE, TReminder } from "types";

import { TAppMockServer } from "../types";

import { urlPrefix, resourceNotFoundResponse } from "./utils";
import { urlPrefix, resourceNotFoundResponse } from "../utils";

export function reminderRoutes(this: TAppMockServer) {
this.get(urlPrefix("/reminders"), (schema, request) => {
Expand Down
2 changes: 1 addition & 1 deletion src/services/mocker/mirage/routes/reminderGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TReminderGroup } from "types";

import { TAppMockServer } from "../types";

import { urlPrefix, resourceNotFoundResponse } from "./utils";
import { urlPrefix, resourceNotFoundResponse } from "../utils";

export function reminderGroupRoutes(this: TAppMockServer) {
this.get(urlPrefix("/reminder-groups"), (schema) => {
Expand Down
16 changes: 5 additions & 11 deletions src/services/mocker/mirage/scenarios/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import { TNonEmptyArray } from "types";

import { TAppMockServer } from "../types";

import { TScenariosBuilder } from "../../types";

export function buildScenarios(server: TAppMockServer) {
const builder = {
const builder: TScenariosBuilder = {
// create reminders without any group
withReminders: (n: number = 10) => {
withReminders: (n = 10) => {
server.createList("reminder", n);
return builder;
},
// create reminders with groups
withReminderGroups: ({
reminderGroups = ["Work", "Home", "Personal"],
remindersPerGroup = 10,
}: {
reminderGroups?: TNonEmptyArray<string>;
remindersPerGroup?: number;
}) => {
withReminderGroups: ({ reminderGroups = ["Work", "Home", "Personal"], remindersPerGroup = 10 }) => {
reminderGroups.forEach((groupName) => {
const group = server.create("reminderGroup", { name: groupName });
server.createList("reminder", remindersPerGroup, { group });
Expand Down
15 changes: 12 additions & 3 deletions src/services/mocker/mirage/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ import { createServer } from "miragejs";
import { createRoutes } from "./routes";
import * as models from "./models";
import * as factories from "./factories";
import { buildScenarios } from "./scenarios";

import { TAppMockServer } from "./types";
import { IdentityManager } from "./identityManager";
import { buildScenarios } from "./scenarios";

export type TRunMirageServerConfig = {
environment?: string;
logging?: boolean;
timing?: number;
trackRequests?: boolean;
withDefaultScenario?: boolean;
};

export function runServer(config: TRunMirageServerConfig = {}): TAppMockServer {
return createServer({
const server = createServer({
logging: config.logging || true,
trackRequests: config?.trackRequests || false,
environment: config?.environment || "development",
models,
factories,
Expand All @@ -25,7 +28,9 @@ export function runServer(config: TRunMirageServerConfig = {}): TAppMockServer {
},
// mirage's seeds are loaded on initialization
seeds(server) {
buildScenarios(server).withReminders(5).withReminderGroups({ remindersPerGroup: 2 });
if (config?.withDefaultScenario) {
buildScenarios(server).withReminders(5).withReminderGroups({ remindersPerGroup: 2 });
}
},

routes() {
Expand All @@ -36,4 +41,8 @@ export function runServer(config: TRunMirageServerConfig = {}): TAppMockServer {
this.passthrough();
},
});

return server;
}

export type TServer = ReturnType<typeof runServer>;
File renamed without changes.
30 changes: 0 additions & 30 deletions src/services/mocker/msw/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,6 @@ export const db = factory({

export type TDb = typeof db;

export const buildScenarios = (db: TDb) => {
const builder = {
withReminders: (n: number = 10) => {
for (let i = 0; i < n; i++) {
db.reminder.create();
}

return builder;
},
withReminderGroups: ({
reminderGroups = ["Work", "Home", "Personal"],
remindersPerGroup = 10,
}: {
reminderGroups?: string[];
remindersPerGroup?: number;
}) => {
reminderGroups.forEach((groupName) => {
const group = db.reminderGroup.create({ name: groupName });

for (let i = 0; i < remindersPerGroup; i++) {
db.reminder.create({ group });
}
});

return builder;
},
};
return builder;
};

export const dropDb = (db: TDb) => {
drop(db);
};
37 changes: 33 additions & 4 deletions src/services/mocker/msw/server.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { setupWorker } from "msw/browser";

import { db, buildScenarios } from "./db";
import { TScenariosBuilder } from "../types";

import { db, TDb } from "./db";

import { setupHandlers } from "./handlers";

const PUBLIC_URL = process.env.REACT_APP_PUBLIC_URL;

export const runServer = () => {
// NOTE: seed data
buildScenarios(db).withReminders(5).withReminderGroups({ remindersPerGroup: 2 });
export const runServer = (config?: { withDefaultScenario?: boolean }) => {
if (config?.withDefaultScenario) {
buildScenarios(db)
.withReminders(5)
.withReminderGroups({ reminderGroups: ["Work", "Home", "Personal"], remindersPerGroup: 2 });
}

const handlers = setupHandlers(db);

Expand All @@ -21,3 +26,27 @@ export const runServer = () => {
},
});
};

export const buildScenarios = (db: TDb) => {
const builder: TScenariosBuilder = {
withReminders: (n = 10) => {
for (let i = 0; i < n; i++) {
db.reminder.create();
}

return builder;
},
withReminderGroups: ({ reminderGroups = ["Work", "Home", "Personal"], remindersPerGroup = 10 }) => {
reminderGroups.forEach((groupName) => {
const group = db.reminderGroup.create({ name: groupName });

for (let i = 0; i < remindersPerGroup; i++) {
db.reminder.create({ group });
}
});

return builder;
},
};
return builder;
};
13 changes: 5 additions & 8 deletions src/services/mocker/setupMocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ export const MOCKER_TYPE = {

export type TMocker = (typeof MOCKER_TYPE)[keyof typeof MOCKER_TYPE] | undefined;

const initServer = (module: { runServer: () => void }) => {
const { runServer } = module;
const server = runServer();

return server;
};

export const setupMocker = async ({ type = undefined }: { type: TMocker }) => {
if (MOCKER_TYPE[type!]) {
return import(`./${type}`).then(initServer);
const { runServer } = await import(`./${type}/server.ts`);

return runServer({
withDefaultScenario: true,
});
}

return Promise.resolve();
Expand Down
6 changes: 6 additions & 0 deletions src/services/mocker/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { TNonEmptyArray } from "types";

export type TScenariosBuilder = {
withReminders: (n: number) => any;
withReminderGroups: (options: { reminderGroups: TNonEmptyArray<string>; remindersPerGroup: number }) => any;
};
7 changes: 4 additions & 3 deletions src/tests/jest.setupBeforeEnv.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import "./jest.polyfills";

// mocking the environment variables
// NOTE: alternative to .testing.env file
// mocking the environment variables
// @ts-ignore
process.env.REACT_APP_PUBLIC_URL = "http://localhost";
// process.env.REACT_APP_PUBLIC_URL = "http://localhost";
// @ts-ignore
process.env.REACT_APP_API_URL = "http://localhost/api/v1/";
// process.env.REACT_APP_API_URL = "http://localhost/api/v1/";
1 change: 1 addition & 0 deletions src/tests/utils/Wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const Wrapper: FC<PropsWithChildren<TWrapperProps>> = ({
config = {
withI18n: true,
withStore: true,
preloadedState: undefined,
withRouter: true,
withToaster: true,
},
Expand Down
2 changes: 1 addition & 1 deletion src/tests/utils/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const render = (ui: ReactNode, options: TRenderProps = {}) => {
const OuterWrapper = () => {
const InnerWrapper = renderOptions?.wrapper;

return <Wrapper {...config}>{InnerWrapper ? <InnerWrapper>{ui}</InnerWrapper> : ui}</Wrapper>;
return <Wrapper config={config}>{InnerWrapper ? <InnerWrapper>{ui}</InnerWrapper> : ui}</Wrapper>;
};

return _render(ui, { wrapper: OuterWrapper, ...renderOptions });
Expand Down
2 changes: 1 addition & 1 deletion src/tests/utils/renderHook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const renderHook = <Props, Result>(

const InnerWrapper = options?.wrapper;

return <Wrapper {...config}>{InnerWrapper ? <InnerWrapper {...props} /> : children}</Wrapper>;
return <Wrapper config={config}>{InnerWrapper ? <InnerWrapper {...props} /> : children}</Wrapper>;
};

return _renderHook(render, { ...options, wrapper: OuterWrapper });
Expand Down
86 changes: 63 additions & 23 deletions src/tests/utils/testServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,72 @@ import { setupServer } from "msw/node";

import { beforeAll, afterEach, afterAll } from "@jest/globals";

import { runServer, createRoutes } from "services/mocker/mirage";

import { setupHandlers, db, dropDb } from "services/mocker/msw";

export const testServer = setupServer(...setupHandlers(db));
export const createTestMswServer = (logging: boolean = false) => {
const testMswServer = setupServer(...setupHandlers(db));

beforeAll(() => {
testMswServer.listen({
onUnhandledRequest(request) {
// eslint-disable-next-line no-console
console.log("Unhandled %s %s", request.method, request.url);
},
});
});

afterEach(() => {
testMswServer.resetHandlers();
dropDb(db);
});

beforeAll(() =>
testServer.listen({
onUnhandledRequest(request) {
afterAll(() => {
testMswServer.close();
});

if (logging) {
// NOTE: simple outgoing request listener logger
testMswServer.events.on("request:start", ({ request }) => {
// eslint-disable-next-line no-console
console.log("Unhandled %s %s", request.method, request.url);
},
})
);

afterEach(() => {
testServer.resetHandlers();
dropDb(db);
});

afterAll(() => {
testServer.close();
});

// NOTE: simple outgoing request listener logger
testServer.events.on("request:start", ({ request }) => {
// eslint-disable-next-line no-console
console.log("MSW intercepted:", request.method, request.url);
});
console.log("MSW intercepted:", request.method, request.url);
});
}

return testMswServer;
};

export const createTestMirageServer = () => {
const testMirageServer = runServer({
environment: "test",
trackRequests: true,
logging: false,
});

afterEach(() => {
createRoutes.call(testMirageServer);
testMirageServer.db.emptyData();
});

afterAll(() => {
testMirageServer.shutdown();
});

return testMirageServer;
};

export { HttpResponse, http } from "msw";

// NOTE: Alternative
// let testMirageServer: TAppMockServer;

// beforeEach(() => {
// testMirageServer = runServer({
// environment: "test",
// });
// });

// afterEach(() => {
// testMirageServer.shutdown();
// });
Loading

0 comments on commit ff60f01

Please sign in to comment.