diff --git a/ui/src/Components/MainModal/Configuration/AlertGroupSortConfiguration.test.tsx b/ui/src/Components/MainModal/Configuration/AlertGroupSortConfiguration.test.tsx index 71ac437b7..5fbef0138 100644 --- a/ui/src/Components/MainModal/Configuration/AlertGroupSortConfiguration.test.tsx +++ b/ui/src/Components/MainModal/Configuration/AlertGroupSortConfiguration.test.tsx @@ -5,11 +5,12 @@ import { mount } from "enzyme"; import toDiffableHtml from "diffable-html"; import { MockThemeContext } from "__mocks__/Theme"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { Settings } from "Stores/Settings"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; import { AlertGroupSortConfiguration } from "./AlertGroupSortConfiguration"; let settingsStore: Settings; + beforeEach(() => { settingsStore = new Settings(null); @@ -18,7 +19,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchGet.mockReset(); }); const FakeConfiguration = () => { @@ -51,6 +51,16 @@ describe("", () => { }); it("changing sort order value update settingsStore", () => { + useFetchGetMock.fetch.setMockedData({ + response: null, + error: "fake error", + isLoading: false, + isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), + }); + settingsStore.gridConfig.setSortOrder("label"); expect(settingsStore.gridConfig.config.sortOrder).toBe( settingsStore.gridConfig.options.label.value @@ -119,11 +129,14 @@ describe("", () => { }); it("label select handles fetch errors", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: "fake error", isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = ExpandSortLabelSuggestions(); const options = tree.find("div.react-select__option"); diff --git a/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.tsx b/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.tsx index d8cdd94b7..8a4f0a24c 100644 --- a/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.tsx +++ b/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.tsx @@ -7,8 +7,8 @@ import fetchMock from "fetch-mock"; import toDiffableHtml from "diffable-html"; import { MockThemeContext } from "__mocks__/Theme"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { Settings } from "Stores/Settings"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; import { MultiGridConfiguration } from "./MultiGridConfiguration"; let settingsStore: Settings; @@ -25,8 +25,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchGet.mockReset(); - fetchMock.reset(); }); const FakeConfiguration = () => { @@ -63,11 +61,14 @@ describe("", () => { }); it("label select handles fetch errors", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: "fake error", isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = ExpandSortLabelSuggestions(); const options = tree.find("div.react-select__option"); diff --git a/ui/src/Components/ManagedSilence/DeleteSilence.test.tsx b/ui/src/Components/ManagedSilence/DeleteSilence.test.tsx index 0feed8ca8..c271730c6 100644 --- a/ui/src/Components/ManagedSilence/DeleteSilence.test.tsx +++ b/ui/src/Components/ManagedSilence/DeleteSilence.test.tsx @@ -10,7 +10,6 @@ import { PressKey } from "__mocks__/PressKey"; import { APISilenceT } from "Models/APITypes"; import { AlertStore } from "Stores/AlertStore"; import { SilenceFormStore } from "Stores/SilenceFormStore"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; import { useFetchDelete } from "__mocks__/Hooks/useFetchDelete"; import { DeleteSilence, DeleteSilenceModalContent } from "./DeleteSilence"; @@ -50,7 +49,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchGet.mockReset(); useFetchDelete.mockReset(); clear(); document.body.className = ""; diff --git a/ui/src/Components/NavBar/FilterInput/index.test.tsx b/ui/src/Components/NavBar/FilterInput/index.test.tsx index 44921a5e3..4e1370f4e 100644 --- a/ui/src/Components/NavBar/FilterInput/index.test.tsx +++ b/ui/src/Components/NavBar/FilterInput/index.test.tsx @@ -5,9 +5,10 @@ import { mount, render } from "enzyme"; import toDiffableHtml from "diffable-html"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; +import { useFetchGet } from "Hooks/useFetchGet"; import { FilterInput } from "."; let alertStore: AlertStore; @@ -29,7 +30,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - (useFetchGet as any).mockReset(); global.window.innerWidth = originalInnerWidth; }); @@ -142,8 +142,8 @@ describe("", () => { jest.runOnlyPendingTimers(); }); - expect((useFetchGet as any).fetch.calls).toHaveLength(1); - expect((useFetchGet as any).fetch.calls[0]).toContain( + expect(useFetchGetMock.fetch.calls).toHaveLength(1); + expect(useFetchGetMock.fetch.calls[0]).toContain( "./autocomplete.json?term=cluster" ); }); @@ -154,7 +154,7 @@ describe("", () => { act(() => { jest.runOnlyPendingTimers(); }); - expect((useFetchGet as any).fetch.calls).toHaveLength(0); + expect(useFetchGetMock.fetch.calls).toHaveLength(0); }); it("clicking on a suggestion adds it to filters", async () => { @@ -182,12 +182,14 @@ describe("", () => { }); it("handles failed suggestion fetches", async () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: "fake error", isLoading: false, isRetrying: false, + retryCount: 0, get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedInput(); diff --git a/ui/src/Components/PaginatedAlertList/index.test.tsx b/ui/src/Components/PaginatedAlertList/index.test.tsx index 6675a9e02..0ca0755d4 100644 --- a/ui/src/Components/PaginatedAlertList/index.test.tsx +++ b/ui/src/Components/PaginatedAlertList/index.test.tsx @@ -4,9 +4,10 @@ import { mount } from "enzyme"; import { advanceTo, clear } from "jest-date-mock"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { EmptyAPIResponse } from "__mocks__/Fetch"; import { AlertStore } from "Stores/AlertStore"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; +import { useFetchGet } from "Hooks/useFetchGet"; import { useFetchDelete } from "__mocks__/Hooks/useFetchDelete"; import { PaginatedAlertList } from "."; @@ -40,7 +41,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchGet.mockReset(); useFetchDelete.mockReset(); clear(); document.body.className = ""; @@ -48,11 +48,14 @@ afterEach(() => { describe("", () => { it("renders Placeholder while loading preview", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, - error: false, + error: null, isLoading: true, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = mount( @@ -61,11 +64,14 @@ describe("", () => { }); it("renders Placeholder while response is empty", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, - error: false, + error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = mount( @@ -82,11 +88,14 @@ describe("", () => { }); it("renders empty LabelSetList with empty response", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: EmptyAPIResponse(), - error: false, + error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = mount( @@ -113,11 +122,14 @@ describe("", () => { }); it("handles empty grid response correctly", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: EmptyAPIResponse(), - error: false, + error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = mount( @@ -126,11 +138,14 @@ describe("", () => { }); it("renders FetchError on failed preview fetch", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: "fake error", isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = mount( diff --git a/ui/src/Components/SilenceModal/Browser/index.test.tsx b/ui/src/Components/SilenceModal/Browser/index.test.tsx index df026dc00..c050a9e96 100644 --- a/ui/src/Components/SilenceModal/Browser/index.test.tsx +++ b/ui/src/Components/SilenceModal/Browser/index.test.tsx @@ -7,15 +7,15 @@ import toDiffableHtml from "diffable-html"; import { advanceTo, clear } from "jest-date-mock"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { MockSilence } from "__mocks__/Alerts"; import { MockThemeContext } from "__mocks__/Theme"; import { PressKey } from "__mocks__/PressKey"; -import { APISilenceT } from "Models/APITypes"; +import { APISilenceT, APIManagedSilenceT } from "Models/APITypes"; import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; import { SilenceFormStore } from "Stores/SilenceFormStore"; import { ThemeContext } from "Components/Theme"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; import Browser from "."; let alertStore: AlertStore; @@ -62,14 +62,13 @@ afterEach(() => { jest.restoreAllMocks(); jest.clearAllTimers(); clear(); - useFetchGet.mockReset(); localStorage.setItem("fetchConfig.interval", ""); global.window.innerWidth = 1024; }); -const MockSilenceList = (count: number) => { - const silences = []; +const MockSilenceList = (count: number): APIManagedSilenceT[] => { + const silences: APIManagedSilenceT[] = []; for (let index = 1; index <= count; index++) { const silence = MockSilence(); silence.id = `silence${index}`; @@ -77,6 +76,7 @@ const MockSilenceList = (count: number) => { cluster: cluster, alertCount: 123, silence: silence, + isExpired: false, }); } return silences; @@ -99,7 +99,7 @@ const MountedBrowser = () => { describe("", () => { it("fetches /silences.json on mount", () => { MountedBrowser(); - expect((useFetchGet as any).fetch.calls[0]).toBe( + expect(useFetchGetMock.fetch.calls[0]).toBe( "./silences.json?sortReverse=0&showExpired=0&searchTerm=" ); }); @@ -123,12 +123,12 @@ describe("", () => { jest.runOnlyPendingTimers(); }); - expect((useFetchGet as any).fetch.calls).toHaveLength(4); + expect(useFetchGetMock.fetch.calls).toHaveLength(4); }); it("enabling reverse sort passes sortReverse=1 to the API", () => { const tree = MountedBrowser(); - expect((useFetchGet as any).fetch.calls[0]).toBe( + expect(useFetchGetMock.fetch.calls[0]).toBe( "./silences.json?sortReverse=0&showExpired=0&searchTerm=" ); @@ -136,7 +136,7 @@ describe("", () => { expect(sortOrder.text()).toBe("Sort order"); sortOrder.simulate("click"); - expect((useFetchGet as any).fetch.calls[1]).toBe( + expect(useFetchGetMock.fetch.calls[1]).toBe( "./silences.json?sortReverse=1&showExpired=0&searchTerm=" ); }); @@ -147,7 +147,7 @@ describe("", () => { const expiredCheckbox = tree.find("input[type='checkbox']"); expiredCheckbox.simulate("change", { target: { checked: true } }); - expect((useFetchGet as any).fetch.calls[1]).toBe( + expect(useFetchGetMock.fetch.calls[1]).toBe( "./silences.json?sortReverse=0&showExpired=1&searchTerm=" ); }); @@ -161,21 +161,24 @@ describe("", () => { act(() => { jest.advanceTimersByTime(1000); }); - expect((useFetchGet as any).fetch.calls).toHaveLength(2); - expect((useFetchGet as any).fetch.calls[0]).toBe( + expect(useFetchGetMock.fetch.calls).toHaveLength(2); + expect(useFetchGetMock.fetch.calls[0]).toBe( "./silences.json?sortReverse=0&showExpired=0&searchTerm=" ); - expect((useFetchGet as any).fetch.calls[1]).toBe( + expect(useFetchGetMock.fetch.calls[1]).toBe( "./silences.json?sortReverse=0&showExpired=0&searchTerm=foo" ); }); it("renders loading placeholder before fetch finishes", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: null, isLoading: true, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); expect(tree.find("Placeholder")).toHaveLength(1); @@ -183,11 +186,14 @@ describe("", () => { }); it("renders loading placeholder before fetch finishes", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: null, isLoading: true, isRetrying: true, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); expect(tree.find("Placeholder")).toHaveLength(1); @@ -195,11 +201,14 @@ describe("", () => { }); it("renders empty placeholder after fetch with zero results", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: [], error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); expect(tree.find("Placeholder")).toHaveLength(1); @@ -207,17 +216,21 @@ describe("", () => { }); it("renders silences after successful fetch", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: [ { cluster: cluster, alertCount: 123, silence: silence, + isExpired: false, }, ], error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); expect(tree.find("ManagedSilence")).toHaveLength(1); @@ -225,11 +238,14 @@ describe("", () => { it("renders only first 6 silences on desktop", () => { global.window.innerWidth = 1024; - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: MockSilenceList(7), error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); expect(tree.find("ManagedSilence")).toHaveLength(6); @@ -237,22 +253,28 @@ describe("", () => { it("renders only first 6 silences on mobile", () => { global.window.innerWidth = 500; - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: MockSilenceList(7), error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); expect(tree.find("ManagedSilence")).toHaveLength(4); }); it("renders last silence after page change", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: MockSilenceList(7), error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); @@ -268,11 +290,14 @@ describe("", () => { }); it("renders next/previous page after arrow key press", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: MockSilenceList(13), error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); @@ -314,11 +339,14 @@ describe("", () => { }); it("resets pagination to last page on truncation", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: MockSilenceList(13), error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); @@ -329,22 +357,28 @@ describe("", () => { expect(tree.find("ManagedSilence")).toHaveLength(1); expect(tree.find("li.page-item").at(3).hasClass("active")).toBe(true); - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: MockSilenceList(8), error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); tree.find("button.btn-secondary").simulate("click"); expect(tree.find("ManagedSilence")).toHaveLength(2); expect(tree.find("li.page-item").at(2).hasClass("active")).toBe(true); - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: [], error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); tree.find("button.btn-secondary").simulate("click"); @@ -353,11 +387,14 @@ describe("", () => { }); it("renders error after failed fetch", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: "fake failure", isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedBrowser(); @@ -367,7 +404,7 @@ describe("", () => { it("resets the timer on unmount", () => { const tree = MountedBrowser(); - expect((useFetchGet as any).fetch.calls).toHaveLength(1); + expect(useFetchGetMock.fetch.calls).toHaveLength(1); tree.unmount(); @@ -376,6 +413,6 @@ describe("", () => { jest.runOnlyPendingTimers(); }); - expect((useFetchGet as any).fetch.calls).toHaveLength(1); + expect(useFetchGetMock.fetch.calls).toHaveLength(1); }); }); diff --git a/ui/src/Components/SilenceModal/SilenceMatch/LabelNameInput.test.tsx b/ui/src/Components/SilenceModal/SilenceMatch/LabelNameInput.test.tsx index c1256ae34..e863ab32e 100644 --- a/ui/src/Components/SilenceModal/SilenceMatch/LabelNameInput.test.tsx +++ b/ui/src/Components/SilenceModal/SilenceMatch/LabelNameInput.test.tsx @@ -6,7 +6,8 @@ import toDiffableHtml from "diffable-html"; import { MockThemeContext } from "__mocks__/Theme"; import { NewEmptyMatcher, MatcherWithIDT } from "Stores/SilenceFormStore"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; +import { useFetchGet } from "Hooks/useFetchGet"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { LabelNameInput } from "./LabelNameInput"; let matcher: MatcherWithIDT; @@ -20,7 +21,7 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchGet.mockReset(); + (useFetchGet as jest.MockedFunction).mockReset(); }); const MountedLabelNameInput = (isValid: boolean) => { @@ -70,15 +71,21 @@ describe("", () => { it("populates suggestions on mount", () => { MountedLabelNameInput(true); - expect(useFetchGet.mock.calls[0][0]).toBe("./labelNames.json"); + expect( + (useFetchGet as jest.MockedFunction).mock + .calls[0][0] + ).toBe("./labelNames.json"); }); it("handles fetch errors when populating suggestions", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: "fake error", isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedLabelNameInput(true); tree.find("input").simulate("change", { target: { value: "f" } }); diff --git a/ui/src/Components/SilenceModal/SilenceMatch/LabelValueInput.test.tsx b/ui/src/Components/SilenceModal/SilenceMatch/LabelValueInput.test.tsx index 7c7dbe783..e89846899 100644 --- a/ui/src/Components/SilenceModal/SilenceMatch/LabelValueInput.test.tsx +++ b/ui/src/Components/SilenceModal/SilenceMatch/LabelValueInput.test.tsx @@ -4,6 +4,7 @@ import { mount } from "enzyme"; import toDiffableHtml from "diffable-html"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { MockThemeContext } from "__mocks__/Theme"; import { SilenceFormStore, @@ -11,7 +12,6 @@ import { MatcherWithIDT, } from "Stores/SilenceFormStore"; import { StringToOption } from "Common/Select"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; import { LabelValueInput } from "./LabelValueInput"; let silenceFormStore: SilenceFormStore; @@ -27,7 +27,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchGet.mockReset(); }); const MountedLabelValueInput = (isValid: boolean) => { @@ -49,8 +48,8 @@ describe("", () => { it("fetches suggestions on mount", () => { const tree = MountedLabelValueInput(true); expect(toDiffableHtml(tree.html())).toMatchSnapshot(); - expect((useFetchGet as any).fetch.calls).toHaveLength(1); - expect((useFetchGet as any).fetch.calls[0]).toBe( + expect(useFetchGetMock.fetch.calls).toHaveLength(1); + expect(useFetchGetMock.fetch.calls[0]).toBe( "./labelValues.json?name=cluster" ); }); @@ -58,7 +57,7 @@ describe("", () => { it("doesn't fetch suggestions if name is not set", () => { matcher.name = ""; MountedLabelValueInput(true); - expect((useFetchGet as any).fetch.calls).toHaveLength(0); + expect(useFetchGetMock.fetch.calls).toHaveLength(0); }); it("doesn't renders ValidationError after passed validation", () => { diff --git a/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.tsx b/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.tsx index 70eca6e70..3aa4e8d65 100644 --- a/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.tsx +++ b/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.tsx @@ -4,12 +4,14 @@ import { mount } from "enzyme"; import toDiffableHtml from "diffable-html"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; +import { EmptyAPIResponse } from "__mocks__/Fetch"; import { SilenceFormStore, NewEmptyMatcher, MatcherWithIDT, } from "Stores/SilenceFormStore"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; +import { useFetchGet } from "Hooks/useFetchGet"; import { StringToOption } from "Common/Select"; import { MatchCounter } from "./MatchCounter"; @@ -25,7 +27,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchGet.mockReset(); }); const MountedMatchCounter = () => { @@ -41,11 +42,14 @@ describe("", () => { }); it("renders spinner icon while fetching", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, - error: false, + error: null, isLoading: true, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedMatchCounter(); @@ -54,11 +58,14 @@ describe("", () => { }); it("renders spinner icon with text-danger while retrying fetching", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, - error: false, + error: null, isLoading: true, isRetrying: true, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedMatchCounter(); @@ -66,11 +73,14 @@ describe("", () => { }); it("renders error icon on failed fetch", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: "failed", isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedMatchCounter(); @@ -78,11 +88,16 @@ describe("", () => { }); it("totalAlerts is 0 after mount", () => { - (useFetchGet as any).fetch.setMockedData({ - response: { totalAlerts: 0 }, - error: false, + const r = EmptyAPIResponse(); + r.totalAlerts = 0; + useFetchGetMock.fetch.setMockedData({ + response: r, + error: null, isLoading: false, isRetrying: false, + retryCount: 0, + get: jest.fn(), + cancelGet: jest.fn(), }); const tree = MountedMatchCounter(); @@ -96,15 +111,17 @@ describe("", () => { it("sends correct query string for a 'foo=bar' matcher", () => { MountedMatchCounter(); - expect(useFetchGet.mock.calls[0][0]).toBe("./alerts.json?q=foo%3Dbar"); + expect( + (useFetchGet as jest.MockedFunction).mock.calls[0][0] + ).toBe("./alerts.json?q=foo%3Dbar"); }); it("sends correct query string for a 'foo=~bar' matcher", () => { matcher.isRegex = true; MountedMatchCounter(); - expect(useFetchGet.mock.calls[0][0]).toBe( - "./alerts.json?q=foo%3D~%5Ebar%24" - ); + expect( + (useFetchGet as jest.MockedFunction).mock.calls[0][0] + ).toBe("./alerts.json?q=foo%3D~%5Ebar%24"); }); it("sends correct query string for a 'foo=~(bar|baz)' matcher", () => { @@ -112,9 +129,9 @@ describe("", () => { matcher.isRegex = true; silenceFormStore.data.setAlertmanagers([]); MountedMatchCounter(); - expect(useFetchGet.mock.calls[0][0]).toBe( - "./alerts.json?q=foo%3D~%5E%28bar%7Cbaz%29%24" - ); + expect( + (useFetchGet as jest.MockedFunction).mock.calls[0][0] + ).toBe("./alerts.json?q=foo%3D~%5E%28bar%7Cbaz%29%24"); }); it("selecting one Alertmanager instance appends it to the filters", () => { @@ -125,9 +142,9 @@ describe("", () => { }, ]); MountedMatchCounter(); - expect(useFetchGet.mock.calls[0][0]).toBe( - "./alerts.json?q=foo%3Dbar&q=%40alertmanager%3D~%5E%28am1%29%24" - ); + expect( + (useFetchGet as jest.MockedFunction).mock.calls[0][0] + ).toBe("./alerts.json?q=foo%3Dbar&q=%40alertmanager%3D~%5E%28am1%29%24"); }); it("selecting two Alertmanager instances appends it correctly to the filters", () => { @@ -142,7 +159,9 @@ describe("", () => { }, ]); MountedMatchCounter(); - expect(useFetchGet.mock.calls[0][0]).toBe( + expect( + (useFetchGet as jest.MockedFunction).mock.calls[0][0] + ).toBe( "./alerts.json?q=foo%3Dbar&q=%40alertmanager%3D~%5E%28am1%7Cam2%29%24" ); }); diff --git a/ui/src/Components/SilenceModal/SilencePreview/index.test.tsx b/ui/src/Components/SilenceModal/SilencePreview/index.test.tsx index 98be15190..dad449a1e 100644 --- a/ui/src/Components/SilenceModal/SilencePreview/index.test.tsx +++ b/ui/src/Components/SilenceModal/SilencePreview/index.test.tsx @@ -8,8 +8,9 @@ import { EmptyAPIResponse } from "__mocks__/Fetch"; import { MockAlertGroup, MockAlert } from "__mocks__/Alerts"; import { AlertStore } from "Stores/AlertStore"; import { SilenceFormStore, NewEmptyMatcher } from "Stores/SilenceFormStore"; +import { useFetchGet } from "Hooks/useFetchGet"; import { StringToOption } from "Common/Select"; -import { useFetchGet } from "__mocks__/Hooks/useFetchGet"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { SilencePreview } from "."; let alertStore: AlertStore; @@ -28,7 +29,7 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchGet.mockReset(); + (useFetchGet as jest.MockedFunction).mockReset(); }); const MockAPIResponse = () => { @@ -103,7 +104,7 @@ describe("", () => { }); it("matches snapshot", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: MockAPIResponse(), error: false, isLoading: false, @@ -115,7 +116,7 @@ describe("", () => { }); it("renders Placeholder while loading preview", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: false, isLoading: true, @@ -132,7 +133,7 @@ describe("", () => { }); it("handles empty grid response correctly", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: EmptyAPIResponse(), error: false, isLoading: false, @@ -144,7 +145,7 @@ describe("", () => { }); it("renders FetchError on failed fetch", () => { - (useFetchGet as any).fetch.setMockedData({ + useFetchGetMock.fetch.setMockedData({ response: null, error: "Fetch error", isLoading: false, diff --git a/ui/src/Hooks/useFetchGet.test.tsx b/ui/src/Hooks/useFetchGet.test.tsx index 7ab9d9491..1ab48215b 100644 --- a/ui/src/Hooks/useFetchGet.test.tsx +++ b/ui/src/Hooks/useFetchGet.test.tsx @@ -9,6 +9,8 @@ import fetchMock from "fetch-mock"; import { FetchRetryConfig } from "Common/Fetch"; import { useFetchGet } from "./useFetchGet"; +jest.unmock("./useFetchGet"); + describe("useFetchGet", () => { beforeAll(() => { fetchMock.mock("http://localhost/ok", { diff --git a/ui/src/Hooks/useFetchGet.ts b/ui/src/Hooks/useFetchGet.ts index 22b062a34..9a62e0291 100644 --- a/ui/src/Hooks/useFetchGet.ts +++ b/ui/src/Hooks/useFetchGet.ts @@ -22,10 +22,7 @@ interface ResponseState { retryCount: number; } -const useFetchGet = ( - uri: string, - { autorun = true, deps = [], fetcher = null }: FetchGetOptionsT = {} -): { +export interface FetchGetResultT { response: null | T; error: null | string; isLoading: boolean; @@ -33,7 +30,12 @@ const useFetchGet = ( retryCount: number; get: () => void; cancelGet: () => void; -} => { +} + +const useFetchGet = ( + uri: string, + { autorun = true, deps = [], fetcher = null }: FetchGetOptionsT = {} +): FetchGetResultT => { const [response, setResponse] = useState>({ response: null, error: null, diff --git a/ui/src/__mocks__/Hooks/useFetchGet.ts b/ui/src/__fixtures__/useFetchGet.ts similarity index 68% rename from ui/src/__mocks__/Hooks/useFetchGet.ts rename to ui/src/__fixtures__/useFetchGet.ts index 74dce574a..be7f6475c 100644 --- a/ui/src/__mocks__/Hooks/useFetchGet.ts +++ b/ui/src/__fixtures__/useFetchGet.ts @@ -1,16 +1,35 @@ import { useState, useEffect, useCallback } from "react"; import { MockAPIResponse, MockSilenceResponse } from "__mocks__/Fetch"; +import { FetchGetResultT } from "Hooks/useFetchGet"; +import { APIAlertsResponseT, APIManagedSilenceT } from "Models/APITypes"; + +type responseT = null | string[] | APIAlertsResponseT | APIManagedSilenceT[]; interface mockedDataT { - response: any; - error: undefined | string; + response: undefined | responseT; + error: undefined | null | string; isLoading: undefined | boolean; isRetrying: undefined | boolean; + retryCount: undefined | number; + get: () => void; + cancelGet: () => void; } -const MockFetchStats = { - getCalls: [] as string[], +interface mockFetchStatsT { + getCalls: string[]; + readonly calls: string[]; + wasCalled: (uri: string) => void; + reset: () => void; + mockedData: mockedDataT; + setMockedData: (data: mockedDataT) => void; +} + +const mockGet = jest.fn(); +const mockCancelGet = jest.fn(); + +const MockFetchStats: mockFetchStatsT = { + getCalls: [], get calls() { return this.getCalls; }, @@ -18,12 +37,15 @@ const MockFetchStats = { this.getCalls.push(uri); }, reset() { - this.getCalls = [] as string[]; + this.getCalls = []; this.mockedData = { response: undefined, error: undefined, isLoading: undefined, isRetrying: undefined, + retryCount: undefined, + get: mockGet, + cancelGet: mockCancelGet, }; }, mockedData: { @@ -31,14 +53,20 @@ const MockFetchStats = { error: undefined, isLoading: undefined, isRetrying: undefined, - } as mockedDataT, + retryCount: undefined, + get: mockGet, + cancelGet: mockCancelGet, + }, setMockedData(data: mockedDataT) { this.mockedData = data; }, }; -const Mock = (uri: string, { autorun = true, deps = [] } = {}) => { - const [response, setResponse] = useState(null as null | any); +const useFetchGetMock = ( + uri: string, + { autorun = true, deps = [] } = {} +): FetchGetResultT => { + const [response, setResponse] = useState(null as responseT); const [error] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isRetrying] = useState(false); @@ -123,17 +151,17 @@ const Mock = (uri: string, { autorun = true, deps = [] } = {}) => { MockFetchStats.mockedData.isRetrying !== undefined ? MockFetchStats.mockedData.isRetrying : isRetrying, + retryCount: + MockFetchStats.mockedData.retryCount !== undefined + ? MockFetchStats.mockedData.retryCount + : 0, get, cancelGet, }; }; -const useFetchGet = jest.fn(Mock); -(useFetchGet as any).fetch = MockFetchStats; -(useFetchGet as any).mockReset = () => { - useFetchGet.mockClear(); - useFetchGet.mockImplementation(Mock); - MockFetchStats.reset(); -}; +useFetchGetMock.fetch = MockFetchStats; +useFetchGetMock._mockGet = mockGet; +useFetchGetMock._cancelGet = mockCancelGet; -export { useFetchGet }; +export { useFetchGetMock }; diff --git a/ui/src/__mocks__/Fetch.ts b/ui/src/__mocks__/Fetch.ts index 56f954c3f..7e2cc145b 100644 --- a/ui/src/__mocks__/Fetch.ts +++ b/ui/src/__mocks__/Fetch.ts @@ -1,4 +1,8 @@ -import { APIAlertsResponseT, APILabelCounterT } from "Models/APITypes"; +import { + APIAlertsResponseT, + APILabelCounterT, + APIManagedSilenceT, +} from "Models/APITypes"; import { MockAlert, MockAlertGroup, MockSilence } from "./Alerts"; const EmptyAPIResponse = (): APIAlertsResponseT => ({ @@ -109,7 +113,7 @@ const MockAPIResponse = () => { }; const MockSilenceResponse = (cluster: string, count: number) => { - const silences = []; + const silences: APIManagedSilenceT[] = []; for (let index = 1; index <= count; index++) { const silence = MockSilence(); silence.id = `silence${index}`; diff --git a/ui/src/setupTests.ts b/ui/src/setupTests.ts index 271f3ff24..eb648f0a0 100644 --- a/ui/src/setupTests.ts +++ b/ui/src/setupTests.ts @@ -4,6 +4,11 @@ import Adapter from "enzyme-adapter-react-16"; import { FetchRetryConfig } from "Common/Fetch"; +import { useFetchGetMock } from "__fixtures__/useFetchGet"; +import { useFetchGet } from "Hooks/useFetchGet"; + +jest.mock("Hooks/useFetchGet"); + // https://github.com/airbnb/enzyme Enzyme.configure({ adapter: new Adapter() }); @@ -32,3 +37,11 @@ FetchRetryConfig.maxTimeout = 10; // usePopper uses useLayoutEffect but that fails in enzyme React.useLayoutEffect = React.useEffect; + +beforeEach(() => { + useFetchGetMock.fetch.reset(); + (useFetchGet as jest.MockedFunction).mockRestore(); + (useFetchGet as jest.MockedFunction< + typeof useFetchGetMock + >).mockImplementation(useFetchGetMock); +});