diff --git a/ui/package-lock.json b/ui/package-lock.json index 840a32954..a5b95c58a 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -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", diff --git a/ui/package.json b/ui/package.json index 36a4abed4..a1e99dd14 100644 --- a/ui/package.json +++ b/ui/package.json @@ -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", diff --git a/ui/src/Common/Fetch.test.js b/ui/src/Common/Fetch.test.js index 060ef0cdf..ab0ae1a0d 100644 --- a/ui/src/Common/Fetch.test.js +++ b/ui/src/Common/Fetch.test.js @@ -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(); diff --git a/ui/src/Components/Fetcher/index.test.js b/ui/src/Components/Fetcher/index.test.js index 9edf28173..49f93d306 100644 --- a/ui/src/Components/Fetcher/index.test.js +++ b/ui/src/Components/Fetcher/index.test.js @@ -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 = () => { diff --git a/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.js b/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.js index 096733b2c..ccca21ddd 100644 --- a/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.js +++ b/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.js @@ -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; }; diff --git a/ui/src/Components/MainModal/Configuration/index.test.js b/ui/src/Components/MainModal/Configuration/index.test.js index 16a385b94..073598902 100644 --- a/ui/src/Components/MainModal/Configuration/index.test.js +++ b/ui/src/Components/MainModal/Configuration/index.test.js @@ -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("", () => { diff --git a/ui/src/Components/MainModal/MainModalContent.test.js b/ui/src/Components/MainModal/MainModalContent.test.js index bef81c36b..0de285694 100644 --- a/ui/src/Components/MainModal/MainModalContent.test.js +++ b/ui/src/Components/MainModal/MainModalContent.test.js @@ -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) => ( diff --git a/ui/src/Components/MainModal/index.test.js b/ui/src/Components/MainModal/index.test.js index fc4d83f71..d8af64b87 100644 --- a/ui/src/Components/MainModal/index.test.js +++ b/ui/src/Components/MainModal/index.test.js @@ -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 = () => { diff --git a/ui/src/Components/ManagedSilence/SilenceComment.test.js b/ui/src/Components/ManagedSilence/SilenceComment.test.js index d1e1af5e3..0d7690c71 100644 --- a/ui/src/Components/ManagedSilence/SilenceComment.test.js +++ b/ui/src/Components/ManagedSilence/SilenceComment.test.js @@ -18,7 +18,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - fetch.resetMocks(); }); const CollapseMock = jest.fn(); diff --git a/ui/src/Components/ManagedSilence/SilenceDetails.test.js b/ui/src/Components/ManagedSilence/SilenceDetails.test.js index 3ee87e7e3..b4d3cbdd2 100644 --- a/ui/src/Components/ManagedSilence/SilenceDetails.test.js +++ b/ui/src/Components/ManagedSilence/SilenceDetails.test.js @@ -49,7 +49,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - fetch.resetMocks(); // reset Date() to current time clear(); }); diff --git a/ui/src/Components/ManagedSilence/SilenceProgress.test.js b/ui/src/Components/ManagedSilence/SilenceProgress.test.js index b41b738ea..20a15b702 100644 --- a/ui/src/Components/ManagedSilence/SilenceProgress.test.js +++ b/ui/src/Components/ManagedSilence/SilenceProgress.test.js @@ -24,7 +24,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - fetch.resetMocks(); // reset Date() to current time clear(); }); diff --git a/ui/src/Components/ManagedSilence/index.test.js b/ui/src/Components/ManagedSilence/index.test.js index d2d3bf643..1a86ff9b7 100644 --- a/ui/src/Components/ManagedSilence/index.test.js +++ b/ui/src/Components/ManagedSilence/index.test.js @@ -50,7 +50,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - fetch.resetMocks(); clear(); }); diff --git a/ui/src/Components/SilenceModal/index.test.js b/ui/src/Components/SilenceModal/index.test.js index d2d0108da..d1a003d86 100644 --- a/ui/src/Components/SilenceModal/index.test.js +++ b/ui/src/Components/SilenceModal/index.test.js @@ -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(() => { diff --git a/ui/src/Stores/AlertStore.test.js b/ui/src/Stores/AlertStore.test.js index 13f6658d3..b114e9f1b 100644 --- a/ui/src/Stores/AlertStore.test.js +++ b/ui/src/Stores/AlertStore.test.js @@ -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" ); }); }); diff --git a/ui/src/index.test.js b/ui/src/index.test.js index 24b59bd6a..a72c9a5ea 100644 --- a/ui/src/index.test.js +++ b/ui/src/index.test.js @@ -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(); }); diff --git a/ui/src/setupTests.js b/ui/src/setupTests.js index 21bdd7fb5..5ac41c311 100644 --- a/ui/src/setupTests.js +++ b/ui/src/setupTests.js @@ -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