diff --git a/ui/src/pages/providers/ProviderForm/ProviderForm.test.tsx b/ui/src/pages/providers/ProviderForm/ProviderForm.test.tsx
new file mode 100644
index 000000000..ca46b2f07
--- /dev/null
+++ b/ui/src/pages/providers/ProviderForm/ProviderForm.test.tsx
@@ -0,0 +1,188 @@
+import { screen } from "@testing-library/dom";
+import { faker } from "@faker-js/faker";
+import userEvent from "@testing-library/user-event";
+import { Formik } from "formik";
+import MockAdapter from "axios-mock-adapter";
+
+import { axiosInstance } from "api/axios";
+import { renderComponent } from "test/utils";
+
+import ProviderForm from "./ProviderForm";
+import { ProviderFormTypes } from "./ProviderForm";
+import { Label } from "./types";
+
+const mock = new MockAdapter(axiosInstance);
+
+const initialValues = {
+ id: "",
+ client_id: "",
+ client_secret: "",
+ auth_url: "",
+ issuer_url: "",
+ token_url: "",
+ subject_source: "userinfo",
+ microsoft_tenant: "",
+ provider: "",
+ mapper_url: "",
+ scope: "",
+ apple_team_id: "",
+ apple_private_key_id: "",
+ apple_private_key: "",
+ requested_claims: "",
+} as const;
+
+beforeEach(() => {
+ mock.reset();
+});
+
+test("submits field data", async () => {
+ const values = {
+ provider: "auth0",
+ id: faker.word.sample(),
+ client_id: faker.word.sample(),
+ client_secret: faker.word.sample(),
+ scope: faker.word.sample(),
+ requested_claims: faker.word.sample(),
+ mapper_url: faker.word.sample(),
+ };
+ const onSubmit = vi.fn();
+ renderComponent(
+
+ initialValues={initialValues}
+ onSubmit={onSubmit}
+ >
+ {(formik) => (
+ <>
+
+
+ >
+ )}
+ ,
+ );
+ await userEvent.selectOptions(
+ screen.getByRole("combobox", { name: Label.PROVIDER }),
+ values.provider,
+ );
+ await userEvent.type(
+ screen.getByRole("textbox", { name: Label.NAME }),
+ values.id,
+ );
+ await userEvent.type(
+ screen.getByRole("textbox", { name: Label.CLIENT_ID }),
+ values.client_id,
+ );
+ await userEvent.type(
+ screen.getByRole("textbox", { name: Label.CLIENT_SECRET }),
+ values.client_secret,
+ );
+ await userEvent.type(
+ screen.getByRole("textbox", { name: Label.SCOPES }),
+ values.scope,
+ );
+ await userEvent.type(
+ screen.getByRole("textbox", { name: Label.REQUESTED_CLAIMS }),
+ values.requested_claims,
+ );
+ await userEvent.type(
+ screen.getByRole("textbox", { name: Label.MAPPER }),
+ values.mapper_url,
+ );
+ await userEvent.click(screen.getByRole("button"));
+ expect(onSubmit.mock.calls[0][0]).toMatchObject(values);
+});
+
+test("displays fields for apple form", async () => {
+ renderComponent(
+ initialValues={initialValues} onSubmit={vi.fn()}>
+ {(formik) => }
+ ,
+ );
+ await userEvent.selectOptions(
+ screen.getByRole("combobox", { name: Label.PROVIDER }),
+ "apple",
+ );
+ expect(
+ screen.queryByRole("textbox", { name: Label.CLIENT_SECRET }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.getByRole("textbox", { name: Label.PRIVATE_KEY }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("textbox", { name: Label.DEVELOPER_TEAM_ID }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("textbox", { name: Label.PRIVATE_KEY_ID }),
+ ).toBeInTheDocument();
+});
+
+test("displays fields for microsoft form", async () => {
+ renderComponent(
+ initialValues={initialValues} onSubmit={vi.fn()}>
+ {(formik) => }
+ ,
+ );
+ await userEvent.selectOptions(
+ screen.getByRole("combobox", { name: Label.PROVIDER }),
+ "microsoft",
+ );
+ expect(
+ screen.getByRole("textbox", { name: Label.TENANT }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("radio", { name: Label.USERINFO }),
+ ).toBeInTheDocument();
+ expect(screen.getByRole("radio", { name: Label.ME })).toBeInTheDocument();
+});
+
+test("generic form displays auto discovery fields", async () => {
+ renderComponent(
+ initialValues={initialValues} onSubmit={vi.fn()}>
+ {(formik) => }
+ ,
+ );
+ await userEvent.selectOptions(
+ screen.getByRole("combobox", { name: Label.PROVIDER }),
+ "generic",
+ );
+ await userEvent.click(screen.getByRole("radio", { name: Label.YES }));
+ expect(
+ screen.getByRole("textbox", { name: Label.OIDC_SERVER_URL }),
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByRole("textbox", { name: Label.AUTH_URL }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole("textbox", { name: Label.TOKEN_URL }),
+ ).not.toBeInTheDocument();
+});
+
+test("generic form displays non auto discovery fields", async () => {
+ renderComponent(
+ initialValues={initialValues} onSubmit={vi.fn()}>
+ {(formik) => }
+ ,
+ );
+ await userEvent.selectOptions(
+ screen.getByRole("combobox", { name: Label.PROVIDER }),
+ "generic",
+ );
+ await userEvent.click(screen.getByRole("radio", { name: Label.NO }));
+ expect(
+ screen.queryByRole("textbox", { name: Label.OIDC_SERVER_URL }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.getByRole("textbox", { name: Label.AUTH_URL }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("textbox", { name: Label.TOKEN_URL }),
+ ).toBeInTheDocument();
+});
+
+test("disables the mapper URL field when editing", () => {
+ renderComponent(
+ initialValues={initialValues} onSubmit={vi.fn()}>
+ {(formik) => }
+ ,
+ );
+ expect(screen.getByRole("textbox", { name: Label.MAPPER })).toBeDisabled();
+});
diff --git a/ui/src/pages/providers/ProviderForm/ProviderForm.tsx b/ui/src/pages/providers/ProviderForm/ProviderForm.tsx
index 6bbf1e04a..66f200303 100644
--- a/ui/src/pages/providers/ProviderForm/ProviderForm.tsx
+++ b/ui/src/pages/providers/ProviderForm/ProviderForm.tsx
@@ -1,6 +1,7 @@
import { FC, useState } from "react";
import { Form, Input, Select, Textarea } from "@canonical/react-components";
import { FormikProps } from "formik";
+import { Label } from "./types";
const providerOptions = [
{
@@ -120,14 +121,14 @@ const ProviderForm: FC = ({ formik, isEdit = false }) => {
{...formik.getFieldProps("provider")}
id="provider"
options={providerOptions}
- label="Provider"
+ label={Label.PROVIDER}
error={formik.touched.provider ? formik.errors.provider : null}
/>
@@ -135,7 +136,7 @@ const ProviderForm: FC = ({ formik, isEdit = false }) => {
{...formik.getFieldProps("client_id")}
id="client_id"
type="text"
- label="Client ID"
+ label={Label.CLIENT_ID}
error={formik.touched.client_id ? formik.errors.client_id : null}
disabled={isEdit}
/>
@@ -144,7 +145,7 @@ const ProviderForm: FC = ({ formik, isEdit = false }) => {
{...formik.getFieldProps("client_secret")}
id="client_secret"
type="text"
- label="Client secret"
+ label={Label.CLIENT_SECRET}
error={
formik.touched.client_secret ? formik.errors.client_secret : null
}
@@ -159,7 +160,7 @@ const ProviderForm: FC = ({ formik, isEdit = false }) => {
{...formik.getFieldProps("microsoft_tenant")}
id="microsoft_tenant"
type="text"
- label="Tenant"
+ label={Label.TENANT}
help={
<>
The Azure AD Tenant to use for authentication. Can either be{" "}
@@ -178,7 +179,7 @@ const ProviderForm: FC = ({ formik, isEdit = false }) => {
@@ -188,7 +189,7 @@ const ProviderForm: FC = ({ formik, isEdit = false }) => {
The id
field of https://graph.microsoft.com/v1.0/me
@@ -208,7 +209,7 @@ const ProviderForm: FC = ({ formik, isEdit = false }) => {