diff --git a/ui/src/Components/ManagedSilence/DeleteSilence.test.tsx b/ui/src/Components/ManagedSilence/DeleteSilence.test.tsx index c2d9e0d7d..67265de66 100644 --- a/ui/src/Components/ManagedSilence/DeleteSilence.test.tsx +++ b/ui/src/Components/ManagedSilence/DeleteSilence.test.tsx @@ -7,12 +7,14 @@ import { advanceTo, clear } from "jest-date-mock"; import { MockSilence } from "__fixtures__/Alerts"; import { PressKey } from "__fixtures__/PressKey"; +import { useFetchDelete } from "Hooks/useFetchDelete"; import { APISilenceT } from "Models/APITypes"; import { AlertStore } from "Stores/AlertStore"; import { SilenceFormStore } from "Stores/SilenceFormStore"; -import { useFetchDelete } from "__mocks__/Hooks/useFetchDelete"; import { DeleteSilence, DeleteSilenceModalContent } from "./DeleteSilence"; +jest.mock("Hooks/useFetchDelete"); + let alertStore: AlertStore; let silenceFormStore: SilenceFormStore; let cluster: string; @@ -48,8 +50,9 @@ beforeEach(() => { }); afterEach(() => { + (useFetchDelete as jest.MockedFunction).mockClear(); + jest.restoreAllMocks(); - useFetchDelete.mockReset(); clear(); document.body.className = ""; }); @@ -142,19 +145,33 @@ describe("", () => { }); it("sends a DELETE request after clicking 'Confirm' button", () => { + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: "success", error: null, isDeleting: false }); + const tree = MountedDeleteSilenceModalContent(); tree.find(".btn-danger").simulate("click"); - expect(useFetchDelete.mock.calls[0][0]).toBe( + expect( + (useFetchDelete as jest.MockedFunction).mock + .calls[0][0] + ).toBe( "http://localhost:9093/api/v2/silence/04d37636-2350-4878-b382-e0b50353230f" ); - expect(useFetchDelete.mock.calls[0][1]).toMatchObject({ + expect( + (useFetchDelete as jest.MockedFunction).mock + .calls[0][1] + ).toMatchObject({ headers: {}, credentials: "include", }); }); it("sends headers from alertmanager config", () => { + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: "success", error: null, isDeleting: false }); + alertStore.data.upstreams.instances[0].headers = { Authorization: "Basic ***", }; @@ -162,31 +179,62 @@ describe("", () => { const tree = MountedDeleteSilenceModalContent(); tree.find(".btn-danger").simulate("click"); - expect(useFetchDelete.mock.calls[0][0]).toBe( + expect( + (useFetchDelete as jest.MockedFunction).mock + .calls[0][0] + ).toBe( "http://localhost:9093/api/v2/silence/04d37636-2350-4878-b382-e0b50353230f" ); - expect(useFetchDelete.mock.calls[0][1]).toMatchObject({ + expect( + (useFetchDelete as jest.MockedFunction).mock + .calls[0][1] + ).toMatchObject({ credentials: "include", headers: { Authorization: "Basic ***" }, }); }); it("uses CORS credentials from alertmanager config", () => { + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: "success", error: null, isDeleting: false }); + alertStore.data.upstreams.instances[0].corsCredentials = "omit"; const tree = MountedDeleteSilenceModalContent(); tree.find(".btn-danger").simulate("click"); - expect(useFetchDelete.mock.calls[0][0]).toBe( + expect( + (useFetchDelete as jest.MockedFunction).mock + .calls[0][0] + ).toBe( "http://localhost:9093/api/v2/silence/04d37636-2350-4878-b382-e0b50353230f" ); - expect(useFetchDelete.mock.calls[0][1]).toMatchObject({ + expect( + (useFetchDelete as jest.MockedFunction).mock + .calls[0][1] + ).toMatchObject({ credentials: "omit", headers: {}, }); }); + it("renders ProgressMessage while awaiting response status", () => { + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: null, error: null, isDeleting: true }); + + const tree = MountedDeleteSilenceModalContent(); + tree.find(".btn-danger").simulate("click"); + + expect(tree.find("ProgressMessage")).toHaveLength(1); + }); + it("renders SuccessMessage on successful response status", () => { + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: "success", error: null, isDeleting: false }); + const tree = MountedDeleteSilenceModalContent(); tree.find(".btn-danger").simulate("click"); @@ -194,7 +242,9 @@ describe("", () => { }); it("renders ErrorMessage on failed delete fetch request", () => { - useFetchDelete.mockReturnValue({ + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: null, error: "failed", isDeleting: false, @@ -207,7 +257,9 @@ describe("", () => { }); it("'Retry' button is present after failed delete", () => { - useFetchDelete.mockReturnValue({ + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: null, error: "fake error", isDeleting: false, @@ -220,6 +272,10 @@ describe("", () => { }); it("'Retry' button is not present after successful delete", () => { + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: "success", error: null, isDeleting: false }); + const tree = MountedDeleteSilenceModalContent(); tree.find(".btn-danger").simulate("click"); @@ -227,7 +283,9 @@ describe("", () => { }); it("Clicking 'Retry' button triggers new delete", () => { - useFetchDelete.mockReturnValue({ + (useFetchDelete as jest.MockedFunction< + typeof useFetchDelete + >).mockReturnValue({ response: null, error: "fake error", isDeleting: false, diff --git a/ui/src/Components/PaginatedAlertList/index.test.tsx b/ui/src/Components/PaginatedAlertList/index.test.tsx index 077923e3e..d67fcc9f4 100644 --- a/ui/src/Components/PaginatedAlertList/index.test.tsx +++ b/ui/src/Components/PaginatedAlertList/index.test.tsx @@ -8,7 +8,6 @@ import { useFetchGetMock } from "__fixtures__/useFetchGet"; import { EmptyAPIResponse } from "__fixtures__/Fetch"; import { AlertStore } from "Stores/AlertStore"; import { useFetchGet } from "Hooks/useFetchGet"; -import { useFetchDelete } from "__mocks__/Hooks/useFetchDelete"; import { PaginatedAlertList } from "."; let alertStore: AlertStore; @@ -41,7 +40,6 @@ beforeEach(() => { afterEach(() => { jest.restoreAllMocks(); - useFetchDelete.mockReset(); clear(); document.body.className = ""; }); diff --git a/ui/src/Hooks/useFetchDelete.ts b/ui/src/Hooks/useFetchDelete.ts index f97442bbd..c3a1eb570 100644 --- a/ui/src/Hooks/useFetchDelete.ts +++ b/ui/src/Hooks/useFetchDelete.ts @@ -4,17 +4,19 @@ import merge from "lodash.merge"; import { CommonOptions } from "Common/Fetch"; -type useFetchDeleteDepsT = string[] | number[]; +export type useFetchDeleteDepsT = string[] | number[]; + +export interface ResponseStateT { + response: null | string; + error: null | string; + isDeleting: boolean; +} const useFetchDelete = ( uri: string, options: RequestInit, deps: useFetchDeleteDepsT = [] -): { - response: null | string; - error: null | string; - isDeleting: boolean; -} => { +): ResponseStateT => { const [response, setResponse] = useState(null); const [error, setError] = useState(null); const [isDeleting, setIsDeleting] = useState(true); diff --git a/ui/src/Hooks/useFetchGet.test.tsx b/ui/src/Hooks/useFetchGet.test.tsx index 1ab48215b..62ea95f03 100644 --- a/ui/src/Hooks/useFetchGet.test.tsx +++ b/ui/src/Hooks/useFetchGet.test.tsx @@ -375,7 +375,7 @@ describe("useFetchGet", () => { FetchRetryConfig.retries = 0; let tree: any = false; - const fetcher = jest.fn(() => + const fetcher = (): Promise => Promise.resolve({ headers: { get: () => "text/plain", @@ -384,8 +384,7 @@ describe("useFetchGet", () => { tree.unmount(); return "ok"; }, - }) - ); + } as any); jest.useRealTimers(); const Component = () => { diff --git a/ui/src/Hooks/useFetchGet.ts b/ui/src/Hooks/useFetchGet.ts index 9a62e0291..93a513418 100644 --- a/ui/src/Hooks/useFetchGet.ts +++ b/ui/src/Hooks/useFetchGet.ts @@ -6,7 +6,7 @@ import promiseRetry from "promise-retry"; import { CommonOptions, FetchRetryConfig } from "Common/Fetch"; -type FetchFunctionT = (request: RequestInfo) => Promise; +export type FetchFunctionT = (request: RequestInfo) => Promise; export interface FetchGetOptionsT { autorun?: boolean; diff --git a/ui/src/__mocks__/Hooks/useFetchDelete.ts b/ui/src/__mocks__/Hooks/useFetchDelete.ts deleted file mode 100644 index b908974a4..000000000 --- a/ui/src/__mocks__/Hooks/useFetchDelete.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useState, useEffect } from "react"; - -const Mock = (uri: string, options: RequestInit, deps: any[] = []) => { - const [response, setResponse] = useState(null as null | string); - const [error] = useState(null as null | string); - const [isDeleting, setIsDeleting] = useState(true); - - useEffect(() => { - setResponse("success"); - setIsDeleting(false); - // eslint doesn't like ...deps - // eslint-disable-next-line - }, [uri, options, ...deps]); - - return { response, error, isDeleting }; -}; - -const useFetchDelete = jest.fn(Mock); -(useFetchDelete as any).mockReset = () => { - useFetchDelete.mockClear(); - useFetchDelete.mockImplementation(Mock); -}; - -export { useFetchDelete };