-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(tests): add tests for provider forms
- Loading branch information
Showing
7 changed files
with
356 additions
and
40 deletions.
There are no files selected for viewing
129 changes: 129 additions & 0 deletions
129
ui/src/pages/providers/ProviderCreate/ProviderCreate.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { screen, waitFor } from "@testing-library/dom"; | ||
import { faker } from "@faker-js/faker"; | ||
import userEvent from "@testing-library/user-event"; | ||
import { Location } from "react-router-dom"; | ||
import MockAdapter from "axios-mock-adapter"; | ||
import * as reactQuery from "@tanstack/react-query"; | ||
|
||
import { renderComponent } from "test/utils"; | ||
import { urls } from "urls"; | ||
import { axiosInstance } from "api/axios"; | ||
|
||
import ProviderCreate from "./ProviderCreate"; | ||
import { ProviderFormLabel } from "../ProviderForm"; | ||
import { Label } from "./types"; | ||
import { initialValues } from "./ProviderCreate"; | ||
import { | ||
NotificationProvider, | ||
NotificationConsumer, | ||
} from "@canonical/react-components"; | ||
import { queryKeys } from "util/queryKeys"; | ||
|
||
vi.mock("@tanstack/react-query", async () => { | ||
const actual = await vi.importActual("@tanstack/react-query"); | ||
return { | ||
...actual, | ||
useQueryClient: vi.fn(), | ||
}; | ||
}); | ||
|
||
const mock = new MockAdapter(axiosInstance); | ||
|
||
beforeEach(() => { | ||
mock.reset(); | ||
vi.spyOn(reactQuery, "useQueryClient").mockReturnValue({ | ||
invalidateQueries: vi.fn(), | ||
} as unknown as reactQuery.QueryClient); | ||
mock.onPost("/idps").reply(200); | ||
}); | ||
|
||
test("can cancel", async () => { | ||
let location: Location | null = null; | ||
renderComponent(<ProviderCreate />, { | ||
url: "/", | ||
setLocation: (newLocation) => { | ||
location = newLocation; | ||
}, | ||
}); | ||
await userEvent.click(screen.getByRole("button", { name: Label.CANCEL })); | ||
expect((location as Location | null)?.pathname).toBe(urls.providers.index); | ||
}); | ||
|
||
test("calls the API on submit", async () => { | ||
const values = { | ||
id: faker.word.sample(), | ||
}; | ||
renderComponent(<ProviderCreate />); | ||
const input = screen.getByRole("textbox", { name: ProviderFormLabel.NAME }); | ||
await userEvent.click(input); | ||
await userEvent.clear(input); | ||
await userEvent.type(input, values.id); | ||
await userEvent.click(screen.getByRole("button", { name: Label.SUBMIT })); | ||
expect(mock.history.post[0].url).toBe("/idps"); | ||
expect(JSON.parse(mock.history.post[0].data as string)).toMatchObject({ | ||
...initialValues, | ||
scope: initialValues.scope.split(","), | ||
...values, | ||
}); | ||
}); | ||
|
||
test("handles API success", async () => { | ||
let location: Location | null = null; | ||
const invalidateQueries = vi.fn(); | ||
vi.spyOn(reactQuery, "useQueryClient").mockReturnValue({ | ||
invalidateQueries, | ||
} as unknown as reactQuery.QueryClient); | ||
mock.onPost("/idps").reply(200); | ||
const values = { | ||
id: faker.word.sample(), | ||
}; | ||
renderComponent( | ||
<NotificationProvider> | ||
<ProviderCreate /> | ||
<NotificationConsumer /> | ||
</NotificationProvider>, | ||
{ | ||
url: "/", | ||
setLocation: (newLocation) => { | ||
location = newLocation; | ||
}, | ||
}, | ||
); | ||
await userEvent.type( | ||
screen.getByRole("textbox", { name: ProviderFormLabel.NAME }), | ||
values.id, | ||
); | ||
await userEvent.click(screen.getByRole("button", { name: Label.SUBMIT })); | ||
await waitFor(() => | ||
expect(invalidateQueries).toHaveBeenCalledWith({ | ||
queryKey: [queryKeys.providers], | ||
}), | ||
); | ||
expect(document.querySelector(".p-notification--positive")).toHaveTextContent( | ||
Label.SUCCESS, | ||
), | ||
expect((location as Location | null)?.pathname).toBe(urls.providers.index); | ||
}); | ||
|
||
test("handles API failure", async () => { | ||
mock.onPost("/idps").reply(400, { | ||
message: "oops", | ||
}); | ||
const values = { | ||
id: faker.word.sample(), | ||
}; | ||
renderComponent( | ||
<NotificationProvider> | ||
<ProviderCreate /> | ||
<NotificationConsumer /> | ||
</NotificationProvider>, | ||
); | ||
await userEvent.type( | ||
screen.getByRole("textbox", { name: ProviderFormLabel.NAME }), | ||
values.id, | ||
); | ||
await userEvent.click(screen.getByRole("button", { name: Label.SUBMIT })); | ||
expect(document.querySelector(".p-notification--negative")).toHaveTextContent( | ||
`${Label.ERROR}oops`, | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export enum Label { | ||
CANCEL = "Cancel", | ||
ERROR = "Provider creation failed", | ||
SUBMIT = "Save", | ||
SUCCESS = "Provider created.", | ||
} |
167 changes: 167 additions & 0 deletions
167
ui/src/pages/providers/ProviderEdit/ProviderEdit.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { screen, waitFor } from "@testing-library/dom"; | ||
import { faker } from "@faker-js/faker"; | ||
import userEvent from "@testing-library/user-event"; | ||
import { Location } from "react-router-dom"; | ||
import MockAdapter from "axios-mock-adapter"; | ||
import * as reactQuery from "@tanstack/react-query"; | ||
|
||
import { renderComponent } from "test/utils"; | ||
import { urls } from "urls"; | ||
import { axiosInstance } from "api/axios"; | ||
|
||
import ProviderEdit from "./ProviderEdit"; | ||
import { ProviderFormLabel } from "../ProviderForm"; | ||
import { Label } from "./types"; | ||
import { | ||
NotificationProvider, | ||
NotificationConsumer, | ||
} from "@canonical/react-components"; | ||
import { queryKeys } from "util/queryKeys"; | ||
import { mockIdentityProvider } from "test/mocks/providers"; | ||
import { IdentityProvider } from "types/provider"; | ||
|
||
vi.mock("@tanstack/react-query", async () => { | ||
const actual = await vi.importActual("@tanstack/react-query"); | ||
return { | ||
...actual, | ||
useQueryClient: vi.fn(), | ||
}; | ||
}); | ||
|
||
const mock = new MockAdapter(axiosInstance); | ||
|
||
let provider: IdentityProvider; | ||
|
||
beforeEach(() => { | ||
vi.spyOn(reactQuery, "useQueryClient").mockReturnValue({ | ||
invalidateQueries: vi.fn(), | ||
} as unknown as reactQuery.QueryClient); | ||
mock.reset(); | ||
provider = mockIdentityProvider({ | ||
id: faker.word.sample(), | ||
apple_private_key: faker.word.sample(), | ||
apple_private_key_id: faker.word.sample(), | ||
apple_team_id: faker.word.sample(), | ||
auth_url: faker.word.sample(), | ||
client_id: faker.word.sample(), | ||
client_secret: faker.word.sample(), | ||
issuer_url: faker.word.sample(), | ||
mapper_url: faker.word.sample(), | ||
microsoft_tenant: faker.word.sample(), | ||
provider: faker.word.sample(), | ||
requested_claims: faker.word.sample(), | ||
subject_source: "userinfo", | ||
token_url: faker.word.sample(), | ||
scope: ["email"], | ||
}); | ||
mock.onGet(`/idps/${provider.id}`).reply(200, { data: [provider] }); | ||
mock.onPatch(`/idps/${provider.id}`).reply(200); | ||
}); | ||
|
||
test("can cancel", async () => { | ||
let location: Location | null = null; | ||
renderComponent(<ProviderEdit />, { | ||
url: `/?id=${provider.id}`, | ||
setLocation: (newLocation) => { | ||
location = newLocation; | ||
}, | ||
}); | ||
await userEvent.click(screen.getByRole("button", { name: Label.CANCEL })); | ||
expect((location as Location | null)?.pathname).toBe("/"); | ||
expect((location as Location | null)?.search).toBe(""); | ||
}); | ||
|
||
test("calls the API on submit", async () => { | ||
const values = { | ||
scope: faker.word.sample(), | ||
}; | ||
renderComponent(<ProviderEdit />, { | ||
url: `/?id=${provider.id}`, | ||
}); | ||
const input = screen.getByRole("textbox", { name: ProviderFormLabel.SCOPES }); | ||
await userEvent.click(input); | ||
await userEvent.clear(input); | ||
await userEvent.type(input, values.scope); | ||
await userEvent.click(screen.getByRole("button", { name: Label.SUBMIT })); | ||
expect(mock.history.patch[0].url).toBe(`/idps/${provider.id}`); | ||
expect(JSON.parse(mock.history.patch[0].data as string)).toMatchObject({ | ||
apple_private_key: provider.apple_private_key, | ||
apple_private_key_id: provider.apple_private_key_id, | ||
apple_team_id: provider.apple_team_id, | ||
auth_url: provider.auth_url, | ||
client_id: provider.client_id, | ||
client_secret: provider.client_secret, | ||
id: provider.id, | ||
issuer_url: provider.issuer_url, | ||
mapper_url: provider.mapper_url, | ||
microsoft_tenant: provider.microsoft_tenant, | ||
provider: provider.provider, | ||
requested_claims: provider.requested_claims, | ||
subject_source: "userinfo", | ||
token_url: provider.token_url, | ||
scope: [values.scope], | ||
}); | ||
}); | ||
|
||
test("handles API success", async () => { | ||
let location: Location | null = null; | ||
const invalidateQueries = vi.fn(); | ||
vi.spyOn(reactQuery, "useQueryClient").mockReturnValue({ | ||
invalidateQueries, | ||
} as unknown as reactQuery.QueryClient); | ||
mock.onPatch(`/idps/${provider.id}`).reply(200); | ||
const values = { | ||
scope: faker.word.sample(), | ||
}; | ||
renderComponent( | ||
<NotificationProvider> | ||
<ProviderEdit /> | ||
<NotificationConsumer /> | ||
</NotificationProvider>, | ||
{ | ||
url: `/?id=${provider.id}`, | ||
setLocation: (newLocation) => { | ||
location = newLocation; | ||
}, | ||
}, | ||
); | ||
await userEvent.type( | ||
screen.getByRole("textbox", { name: ProviderFormLabel.SCOPES }), | ||
values.scope, | ||
); | ||
await userEvent.click(screen.getByRole("button", { name: Label.SUBMIT })); | ||
await waitFor(() => | ||
expect(invalidateQueries).toHaveBeenCalledWith({ | ||
queryKey: [queryKeys.providers], | ||
}), | ||
); | ||
expect(document.querySelector(".p-notification--positive")).toHaveTextContent( | ||
Label.SUCCESS, | ||
); | ||
expect((location as Location | null)?.pathname).toBe("/"); | ||
expect((location as Location | null)?.search).toBe(""); | ||
}); | ||
|
||
test("handles API failure", async () => { | ||
mock.onPatch(`/idps/${provider.id}`).reply(400, { | ||
message: "oops", | ||
}); | ||
const values = { | ||
scope: faker.word.sample(), | ||
}; | ||
renderComponent( | ||
<NotificationProvider> | ||
<ProviderEdit /> | ||
<NotificationConsumer /> | ||
</NotificationProvider>, | ||
{ url: `/?id=${provider.id}` }, | ||
); | ||
await userEvent.type( | ||
screen.getByRole("textbox", { name: ProviderFormLabel.SCOPES }), | ||
values.scope, | ||
); | ||
await userEvent.click(screen.getByRole("button", { name: Label.SUBMIT })); | ||
expect(document.querySelector(".p-notification--negative")).toHaveTextContent( | ||
`${Label.ERROR}oops`, | ||
); | ||
}); |
Oops, something went wrong.