fix(tests): use fetch-mock instead of jest-fetch-mock

This commit is contained in:
Łukasz Mierzwa
2020-05-12 18:01:47 +01:00
committed by Łukasz Mierzwa
parent 01a8f069eb
commit 0f3dabb517
16 changed files with 165 additions and 101 deletions
-34
View File
@@ -8305,24 +8305,6 @@
"warning": "^4.0.3"
}
},
"cross-fetch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz",
"integrity": "sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw==",
"dev": true,
"requires": {
"node-fetch": "2.6.0",
"whatwg-fetch": "3.0.0"
},
"dependencies": {
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
"dev": true
}
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -13351,16 +13333,6 @@
"merge-deep": "^3.0.2"
}
},
"jest-fetch-mock": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz",
"integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==",
"dev": true,
"requires": {
"cross-fetch": "^3.0.4",
"promise-polyfill": "^8.1.3"
}
},
"jest-get-type": {
"version": "25.2.6",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz",
@@ -18215,12 +18187,6 @@
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
},
"promise-polyfill": {
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz",
"integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==",
"dev": true
},
"promise-retry": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
-1
View File
@@ -88,7 +88,6 @@
"fetch-mock": "9.7.0",
"jest-canvas-mock": "2.2.0",
"jest-date-mock": "1.0.8",
"jest-fetch-mock": "3.0.3",
"jest-puppeteer": "4.4.0",
"markdownlint-cli": "0.23.0",
"node-sass": "4.14.1",
+34 -21
View File
@@ -2,12 +2,19 @@ import { CommonOptions, FetchGet, FetchPost, FetchRetryConfig } from "./Fetch";
import merge from "lodash/merge";
import fetchMock from "fetch-mock";
beforeEach(() => {
fetch.resetMocks();
fetchMock.reset();
fetchMock.any({
status: 200,
body: "ok",
});
});
afterEach(() => {
jest.restoreAllMocks();
fetchMock.reset();
});
describe("Fetch", () => {
@@ -23,23 +30,23 @@ describe("Fetch", () => {
for (const [name, func] of Object.entries(tests)) {
it(`${name}: passes '{credentials: include}' to all requests`, async () => {
const request = func("http://example.com", {});
const request = func("http://example.com/", {});
await expect(request).resolves.toMatchObject({ status: 200 });
expect(fetch).toHaveBeenCalledWith(
"http://example.com",
merge({}, CommonOptions, methodOptions[name])
);
expect(fetchMock.lastCall()).toEqual([
"http://example.com/",
merge({}, CommonOptions, methodOptions[name]),
]);
});
it(`${name}: custom keys are merged with defaults`, async () => {
const request = func("http://example.com", {
const request = func("http://example.com/", {
foo: "bar",
});
await expect(request).resolves.toMatchObject({ status: 200 });
expect(fetch).toHaveBeenCalledWith(
"http://example.com",
merge({}, CommonOptions, methodOptions[name], { foo: "bar" })
);
expect(fetchMock.lastCall()).toEqual([
"http://example.com/",
merge({}, CommonOptions, methodOptions[name], { foo: "bar" }),
]);
});
it(`${name}: custom credentials are used when passed`, async () => {
@@ -48,43 +55,49 @@ describe("Fetch", () => {
redirect: "follow",
});
await expect(request).resolves.toMatchObject({ status: 200 });
expect(fetch).toHaveBeenCalledWith(
"http://example.com",
expect(fetchMock.lastCall()).toEqual([
"http://example.com/",
merge({}, CommonOptions, methodOptions[name], {
credentials: "none",
redirect: "follow",
})
);
}),
]);
});
}
it("FetchGet switches to no-cors for the last retry", async () => {
fetch.mockReject(new Error("Fetch error"));
fetchMock.reset();
fetchMock.any({
throws: new Error("Fetch error"),
});
const request = FetchGet("http://example.com", {});
await expect(request).rejects.toThrow("Fetch error");
expect(fetch).toHaveBeenCalledTimes(FetchRetryConfig.retries + 1);
expect(fetch.mock.calls.map((r) => r[1])).toMatchObject(
expect(fetchMock.calls()).toHaveLength(FetchRetryConfig.retries + 1);
expect(fetchMock.calls().map((r) => r[1])).toMatchObject(
Array.from(Array(FetchRetryConfig.retries + 1).keys(), (i) => ({
mode: i < FetchRetryConfig.retries ? "cors" : "no-cors",
credentials: "include",
}))
);
// ensure that the the second to last call was with cors
expect(fetch.mock.calls[fetch.mock.calls.length - 2][1]).toMatchObject({
expect(fetchMock.calls()[fetchMock.calls().length - 2][1]).toMatchObject({
mode: "cors",
credentials: "include",
});
// ensure that the last call was with no-cors
expect(fetch.mock.calls[fetch.mock.calls.length - 1][1]).toMatchObject({
expect(fetchMock.lastCall()[1]).toMatchObject({
mode: "no-cors",
credentials: "include",
});
});
it("FetchGet calls beforeRetry before each retry", async () => {
fetch.mockReject(new Error("Fetch error"));
fetchMock.reset();
fetchMock.any({
throws: new Error("Fetch error"),
});
const beforeRetrySpy = jest.fn();
+8 -1
View File
@@ -2,6 +2,8 @@ import React from "react";
import { mount } from "enzyme";
import fetchMock from "fetch-mock";
import { advanceTo, advanceBy, clear } from "jest-date-mock";
import { EmptyAPIResponse } from "__mocks__/Fetch";
@@ -41,12 +43,17 @@ afterEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
clear();
fetchMock.reset();
});
const MockEmptyAPIResponseWithoutFilters = () => {
const response = EmptyAPIResponse();
response.filters = [];
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
status: 200,
body: JSON.stringify(response),
});
};
const MountedFetcher = () => {
@@ -2,6 +2,8 @@ import React from "react";
import { mount } from "enzyme";
import fetchMock from "fetch-mock";
import toDiffableHtml from "diffable-html";
import { MockThemeContext } from "__mocks__/Theme";
@@ -11,7 +13,11 @@ import { MultiGridConfiguration } from "./MultiGridConfiguration";
let settingsStore;
beforeEach(() => {
fetch.mockResponse(JSON.stringify([]));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify([]),
});
settingsStore = new Settings();
jest.spyOn(React, "useContext").mockImplementation(() => MockThemeContext);
@@ -20,6 +26,7 @@ beforeEach(() => {
afterEach(() => {
jest.restoreAllMocks();
useFetchGet.mockReset();
fetchMock.reset();
});
const FakeConfiguration = () => {
@@ -35,7 +42,6 @@ const ExpandSortLabelSuggestions = () => {
.find("input#react-select-configuration-grid-label-input")
.simulate("change", { target: { value: "a" } });
fetch.resetMocks();
return tree;
};
@@ -2,6 +2,8 @@ import React from "react";
import { mount } from "enzyme";
import fetchMock from "fetch-mock";
import toDiffableHtml from "diffable-html";
import { Settings } from "Stores/Settings";
@@ -13,11 +15,16 @@ import {
import { Configuration } from ".";
beforeEach(() => {
fetch.mockResponse(JSON.stringify([]));
fetchMock.reset();
fetchMock.any({
status: 200,
body: JSON.stringify([]),
});
});
afterEach(() => {
jest.restoreAllMocks();
fetchMock.reset();
});
describe("<Configuration />", () => {
@@ -2,6 +2,8 @@ import React from "react";
import { mount } from "enzyme";
import fetchMock from "fetch-mock";
import toDiffableHtml from "diffable-html";
import { AlertStore } from "Stores/AlertStore";
@@ -21,11 +23,15 @@ beforeEach(() => {
alertStore = new AlertStore([]);
settingsStore = new Settings();
onHide.mockClear();
fetch.mockResponse(JSON.stringify([]));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify([]),
});
});
afterEach(() => {
jest.restoreAllMocks();
fetchMock.reset();
});
const Wrapped = (component) => (
+7 -1
View File
@@ -2,6 +2,8 @@ import React from "react";
import { mount } from "enzyme";
import fetchMock from "fetch-mock";
import { AlertStore } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
import { ThemeContext } from "Components/Theme";
@@ -22,11 +24,15 @@ beforeEach(() => {
alertStore = new AlertStore([]);
settingsStore = new Settings();
fetch.mockResponse(JSON.stringify([]));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify([]),
});
});
afterEach(() => {
jest.restoreAllMocks();
fetchMock.reset();
});
const MountedMainModal = () => {
@@ -18,7 +18,6 @@ beforeEach(() => {
afterEach(() => {
jest.restoreAllMocks();
fetch.resetMocks();
});
const CollapseMock = jest.fn();
@@ -49,7 +49,6 @@ beforeEach(() => {
afterEach(() => {
jest.restoreAllMocks();
fetch.resetMocks();
// reset Date() to current time
clear();
});
@@ -24,7 +24,6 @@ beforeEach(() => {
afterEach(() => {
jest.restoreAllMocks();
fetch.resetMocks();
// reset Date() to current time
clear();
});
@@ -50,7 +50,6 @@ beforeEach(() => {
afterEach(() => {
jest.restoreAllMocks();
fetch.resetMocks();
clear();
});
+5 -1
View File
@@ -3,6 +3,8 @@ import { act } from "react-dom/test-utils";
import { mount } from "enzyme";
import fetchMock from "fetch-mock";
import { ThemeContext } from "Components/Theme";
import {
ReactSelectColors,
@@ -19,7 +21,9 @@ let silenceFormStore;
beforeAll(() => {
jest.useFakeTimers();
fetch.mockResponse(JSON.stringify([]));
fetchMock.any({
body: JSON.stringify([]),
});
});
beforeEach(() => {
+74 -28
View File
@@ -1,5 +1,6 @@
import { EmptyAPIResponse } from "__mocks__/Fetch";
import fetchMock from "fetch-mock";
import { EmptyAPIResponse } from "__mocks__/Fetch";
import {
AlertStore,
AlertStoreStatuses,
@@ -11,11 +12,12 @@ import {
} from "Stores/AlertStore";
beforeEach(() => {
fetch.resetMocks();
fetchMock.reset();
});
afterEach(() => {
jest.restoreAllMocks();
fetchMock.reset();
// wipe REACT_APP_BACKEND_URI env on each run as it's used by some tests
delete process.env.REACT_APP_BACKEND_URI;
});
@@ -426,23 +428,29 @@ describe("AlertStore.fetch", () => {
it("fetch() works with valid response", async () => {
const response = EmptyAPIResponse();
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
const store = new AlertStore(["label=value"]);
await expect(store.fetch()).resolves.toBeUndefined();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(fetchMock.calls()).toHaveLength(1);
expect(store.status.value).toEqual(AlertStoreStatuses.Idle);
expect(store.info.version).toBe("fakeVersion");
});
it("fetch() handles response with error correctly", async () => {
fetch.mockResponse(JSON.stringify({ error: "Fetch error" }));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify({ error: "Fetch error" }),
});
const store = new AlertStore([]);
await expect(store.fetch()).resolves.toBeUndefined();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(fetchMock.calls()).toHaveLength(1);
expect(store.status.value).toEqual(AlertStoreStatuses.Failure);
expect(store.info.version).toBe("unknown");
});
@@ -451,12 +459,15 @@ describe("AlertStore.fetch", () => {
const consoleSpy = jest
.spyOn(console, "trace")
.mockImplementation(() => {});
fetch.mockReject(new Error("Fetch error"));
fetchMock.reset();
fetchMock.any({
throws: new Error("fetch error"),
});
const store = new AlertStore([]);
await expect(store.fetch()).resolves.toHaveProperty("error");
expect(global.fetch).toHaveBeenCalledTimes(10);
expect(fetchMock.calls()).toHaveLength(10);
expect(store.status.value).toEqual(AlertStoreStatuses.Failure);
expect(store.info.version).toBe("unknown");
// there should be a trace of the error
@@ -467,27 +478,43 @@ describe("AlertStore.fetch", () => {
jest.spyOn(console, "trace").mockImplementation(() => {});
const store = new AlertStore([]);
fetch.mockReject(new Error("Fetch error"));
fetchMock.reset();
fetchMock.any({
throws: new Error("fetch error"),
});
await expect(store.fetch()).resolves.toHaveProperty("error");
expect(global.fetch).toHaveBeenCalledTimes(10);
expect(fetchMock.calls()).toHaveLength(10);
});
it("fetch() retry counter is reset after successful fetch", async () => {
jest.spyOn(console, "trace").mockImplementation(() => {});
const store = new AlertStore(["label=value"]);
fetch.mockReject(new Error("Fetch error"));
fetchMock.reset();
fetchMock.any({
throws: new Error("fetch error"),
});
await expect(store.fetch()).resolves.toHaveProperty("error");
expect(global.fetch).toHaveBeenCalledTimes(10);
expect(fetchMock.calls()).toHaveLength(10);
const response = EmptyAPIResponse();
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
await expect(store.fetch()).resolves.toBeUndefined();
expect(global.fetch).toHaveBeenCalledTimes(11);
expect(fetchMock.calls()).toHaveLength(1);
fetchMock.reset();
fetchMock.any({
throws: new Error("fetch error"),
});
fetch.mockReject(new Error("Fetch error"));
await expect(store.fetch()).resolves.toHaveProperty("error");
expect(global.fetch).toHaveBeenCalledTimes(21);
expect(fetchMock.calls()).toHaveLength(10);
});
it("fetch() reloads the page after if auth middleware is detected", async () => {
@@ -513,14 +540,21 @@ describe("AlertStore.fetch", () => {
store.filters.values[0].applied = false;
jest.spyOn(console, "trace").mockImplementation(() => {});
fetch.mockReject(new Error("Fetch error"));
fetchMock.reset();
fetchMock.any({
throws: new Error("fetch error"),
});
await expect(store.fetch()).resolves.toHaveProperty("error");
expect(store.filters.values[0].applied).toBe(true);
});
it("stored settings are updated if needed after fetch", async () => {
const response = EmptyAPIResponse();
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
const store = new AlertStore(["label=value"]);
@@ -546,13 +580,19 @@ describe("AlertStore.fetch", () => {
it("wants to reload page after new version is returned in the API", async () => {
const response = EmptyAPIResponse();
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
const store = new AlertStore(["label=value"]);
await expect(store.fetch()).resolves.toBeUndefined();
expect(store.info.upgradeNeeded).toBe(false);
response.version = "newFakeVersion";
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
await expect(store.fetch()).resolves.toBeUndefined();
expect(store.info.upgradeNeeded).toBe(true);
});
@@ -607,27 +647,33 @@ describe("AlertStore.fetch", () => {
it("uses correct query args with gridSortReverse=false", async () => {
const response = EmptyAPIResponse();
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
const store = new AlertStore(["label=value"]);
await expect(
store.fetch("", false, "sortOrder", "sortLabel", "sortReverse")
).resolves.toBeUndefined();
expect(fetch.mock.calls.length).toEqual(1);
expect(fetch.mock.calls[0][0]).toBe(
"./alerts.json?&gridLabel=&gridSortReverse=0&sortOrder=sortOrder&sortLabel=sortLabel&sortReverse=sortReverse&q=label%3Dvalue"
expect(fetchMock.calls().length).toEqual(1);
expect(fetchMock.calls()[0][0]).toBe(
"/alerts.json?&gridLabel=&gridSortReverse=0&sortOrder=sortOrder&sortLabel=sortLabel&sortReverse=sortReverse&q=label%3Dvalue"
);
});
it("uses correct query args with gridSortReverse=true", async () => {
const response = EmptyAPIResponse();
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
const store = new AlertStore(["label=value"]);
await expect(
store.fetch("cluster", true, "sortOrder", "sortLabel", "sortReverse")
).resolves.toBeUndefined();
expect(fetch.mock.calls.length).toEqual(1);
expect(fetch.mock.calls[0][0]).toBe(
"./alerts.json?&gridLabel=cluster&gridSortReverse=1&sortOrder=sortOrder&sortLabel=sortLabel&sortReverse=sortReverse&q=label%3Dvalue"
expect(fetchMock.calls().length).toEqual(1);
expect(fetchMock.calls()[0][0]).toBe(
"/alerts.json?&gridLabel=cluster&gridSortReverse=1&sortOrder=sortOrder&sortLabel=sortLabel&sortReverse=sortReverse&q=label%3Dvalue"
);
});
});
+14 -2
View File
@@ -1,3 +1,5 @@
import fetchMock from "fetch-mock";
import { EmptyAPIResponse } from "__mocks__/Fetch";
import { DefaultsBase64 } from "__mocks__/Defaults";
import { mockMatchMedia } from "__mocks__/matchMedia";
@@ -31,7 +33,12 @@ it("renders without crashing with missing defaults div", () => {
});
const response = EmptyAPIResponse();
response.filters = [];
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
const Index = require("./index.tsx");
expect(Index).toBeTruthy();
expect(root.innerHTML).toMatch(/data-theme="auto"/);
@@ -52,7 +59,12 @@ it("renders without crashing with defaults present", () => {
});
const response = EmptyAPIResponse();
response.filters = [];
fetch.mockResponse(JSON.stringify(response));
fetchMock.reset();
fetchMock.any({
body: JSON.stringify(response),
});
const Index = require("./index.tsx");
expect(Index).toBeTruthy();
});
-4
View File
@@ -14,10 +14,6 @@ require("jest-canvas-mock");
// used to mock current time since we render moment.fromNow() in some places
require("jest-date-mock");
// fetch is used in multiple places to interact with Go backend
// or upstream Alertmanager API
global.fetch = require("jest-fetch-mock");
// ensure that all console messages throw errors
for (const level of ["error", "warn", "info", "log", "trace"]) {
// https://reactjs.org/blog/2019/08/08/react-v16.9.0.html#new-deprecations