Skip to content

Commit

Permalink
Add Mock Service Worker to mock client API responses
Browse files Browse the repository at this point in the history
  • Loading branch information
davelopez committed Jul 30, 2024
1 parent 1c10ce5 commit 92c71c8
Show file tree
Hide file tree
Showing 11 changed files with 421 additions and 142 deletions.
2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@
"jsdom-worker": "^0.3.0",
"json-loader": "^0.5.7",
"mini-css-extract-plugin": "^2.7.6",
"msw": "^2.3.4",
"openapi-msw": "^0.7.0",
"openapi-typescript": "^7.0.2",
"postcss-loader": "^7.3.3",
"prettier": "^2.8.8",
Expand Down
7 changes: 7 additions & 0 deletions client/src/api/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createOpenApiHttp } from "openapi-msw";

import { type GalaxyApiPaths } from "@/api";

const clientMock = createOpenApiHttp<GalaxyApiPaths>();

export { clientMock };
3 changes: 3 additions & 0 deletions client/src/api/__mocks__/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { setupServer } from "msw/node";

export const server = setupServer();
95 changes: 0 additions & 95 deletions client/src/api/schema/__mocks__/fetcher.ts

This file was deleted.

1 change: 0 additions & 1 deletion client/src/api/schema/__mocks__/index.ts

This file was deleted.

94 changes: 94 additions & 0 deletions client/src/api/schema/mockClient.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { client, type HistoryDetailed, type HistorySummary, type MessageException } from "@/api";
import { clientMock } from "@/api/__mocks__";

const TEST_HISTORY_SUMMARY: HistorySummary = {
model_class: "History",
id: "test",
name: "Test History",
archived: false,
deleted: false,
purged: false,
published: false,
update_time: "2021-09-01T00:00:00",
count: 0,
annotation: "Test History Annotation",
tags: [],
url: "/api/histories/test",
};

const TEST_HISTORY_DETAILED: HistoryDetailed = {
...TEST_HISTORY_SUMMARY,
create_time: "2021-09-01T00:00:00",
contents_url: "/api/histories/test/contents",
importable: false,
slug: "testSlug",
size: 0,
user_id: "userID",
username_and_slug: "username/slug",
state: "ok",
empty: true,
hid_counter: 0,
genome_build: null,
state_ids: {},
state_details: {},
};

const EXPECTED_500_ERROR: MessageException = { err_code: 500, err_msg: "Internal Server Error" };

// Mock the API client
// You can do whatever you want with the parameters and return values
// All API schema types must be strictly respected and will be up to date with the OpenAPI schema
clientMock.get("/api/histories/{history_id}", ({ params, query, response }) => {
if (query.get("view") === "detailed") {
return response(200).json(TEST_HISTORY_DETAILED);
}
if (params.history_id === "must-fail") {
return response("5XX").json(EXPECTED_500_ERROR, { status: 500 });
}
return response(200).json(TEST_HISTORY_SUMMARY);
});

describe("clientMock", () => {
it("mocks the API client", async () => {
{
const { data, error } = await client.GET("/api/histories/{history_id}", {
params: {
path: { history_id: "test" },
query: { view: "summary" },
},
});

expect(error).toBeUndefined();

expect(data).toBeDefined();
expect(data).toEqual(TEST_HISTORY_SUMMARY);
}

{
const { data, error } = await client.GET("/api/histories/{history_id}", {
params: {
path: { history_id: "test" },
query: { view: "detailed" },
},
});

expect(error).toBeUndefined();

expect(data).toBeDefined();
expect(data).toEqual(TEST_HISTORY_DETAILED);
}

{
const { data, error } = await client.GET("/api/histories/{history_id}", {
params: {
path: { history_id: "must-fail" },
},
});

expect(error).toBeDefined();
expect(error).toEqual(EXPECTED_500_ERROR);

expect(data).toBeUndefined();
}
});
});
42 changes: 0 additions & 42 deletions client/src/api/schema/mockFetcher.test.ts

This file was deleted.

4 changes: 4 additions & 0 deletions client/tests/jest/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ module.exports = {
modulePathIgnorePatterns: ["<rootDir>/src/.*/__mocks__"],
rootDir: path.join(__dirname, "../../"),
roots: ["<rootDir>/src/", "<rootDir>/tests/jest/standalone/"],
setupFiles: ["<rootDir>/tests/jest/jest.polyfills.js"],
setupFilesAfterEnv: ["<rootDir>/tests/jest/jest.setup.js"],
testEnvironment: "<rootDir>/tests/jest/jest-environment.js",
testEnvironmentOptions: {
customExportConditions: ["msw"],
},
testPathIgnorePatterns: ["/node_modules/", "/dist/"],
transform: {
"^.+\\.js$": "babel-jest",
Expand Down
31 changes: 31 additions & 0 deletions client/tests/jest/jest.polyfills.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @note The block below contains polyfills for Node.js globals
* required for Jest to function when running JSDOM tests.
* These HAVE to be require's and HAVE to be in this exact
* order, since "undici" depends on the "TextEncoder" global API.
*
* Consider migrating to a more modern test runner if
* you don't want to deal with this.
*/

// https://mswjs.io/docs/migrations/1.x-to-2.x#frequent-issues

const { TextDecoder, TextEncoder } = require("node:util");

Object.defineProperties(globalThis, {
TextDecoder: { value: TextDecoder },
TextEncoder: { value: TextEncoder },
});

const { Blob, File } = require("node:buffer");
const { fetch, Headers, FormData, Request, Response } = require("undici");

Object.defineProperties(globalThis, {
fetch: { value: fetch, writable: true },
Blob: { value: Blob },
File: { value: File },
Headers: { value: Headers },
FormData: { value: FormData },
Request: { value: Request },
Response: { value: Response },
});
19 changes: 19 additions & 0 deletions client/tests/jest/jest.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import "@testing-library/jest-dom/jest-globals";

import Vue from "vue";

import { server } from "@/api/__mocks__/node";

// Set Vue to suppress production / devtools / etc. warnings
Vue.config.productionTip = false;
Vue.config.devtools = false;
Expand All @@ -15,3 +17,20 @@ global.setImmediate = global.setTimeout;
// Always mock the following imports
jest.mock("@/composables/hashedUserId");
jest.mock("@/composables/userLocalStorage");

beforeAll(() => {
// Enable API mocking before all the tests.
server.listen();
});

afterEach(() => {
// Reset the request handlers between each test.
// This way the handlers we add on a per-test basis
// do not leak to other, irrelevant tests.
server.resetHandlers();
});

afterAll(() => {
// Finally, disable API mocking after the tests are done.
server.close();
});
Loading

0 comments on commit 92c71c8

Please sign in to comment.