diff --git a/site/e2e/README.md b/site/e2e/README.md index 76af72b17d182..b89bad5cf8782 100644 --- a/site/e2e/README.md +++ b/site/e2e/README.md @@ -55,7 +55,7 @@ code . Enterprise tests require a license key to run. ```shell -export CODER_E2E_ENTERPRISE_LICENSE= +export CODER_E2E_LICENSE= ``` # Debugging tests diff --git a/site/e2e/api.ts b/site/e2e/api.ts index da5a57dee007d..0494467799a97 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -63,6 +63,35 @@ export const createOrganization = async () => { return org; }; +export const deleteOrganization = async (orgName: string) => { + await API.deleteOrganization(orgName); +}; + +export const createOrganizationWithName = async (name: string) => { + const org = await API.createOrganization({ + name, + display_name: `${name}`, + description: `Org description ${name}`, + icon: "/emojis/1f957.png", + }); + return org; +}; + +export const createOrganizationSyncSettings = async () => { + const settings = await API.patchOrganizationIdpSyncSettings({ + field: "organization-field-test", + mapping: { + "idp-org-1": [ + "fbd2116a-8961-4954-87ae-e4575bd29ce0", + "13de3eb4-9b4f-49e7-b0f8-0c3728a0d2e2", + ], + "idp-org-2": ["fbd2116a-8961-4954-87ae-e4575bd29ce0"], + }, + organization_assign_default: true, + }); + return settings; +}; + export async function verifyConfigFlagBoolean( page: Page, config: DeploymentConfig, diff --git a/site/e2e/tests/deployment/idpOrgSync.spec.ts b/site/e2e/tests/deployment/idpOrgSync.spec.ts new file mode 100644 index 0000000000000..59fccb440400b --- /dev/null +++ b/site/e2e/tests/deployment/idpOrgSync.spec.ts @@ -0,0 +1,157 @@ +import { expect, test } from "@playwright/test"; +import { + createOrganizationSyncSettings, + createOrganizationWithName, + deleteOrganization, + setupApiCalls, +} from "../../api"; +import { requiresLicense } from "../../helpers"; +import { beforeCoderTest } from "../../hooks"; + +test.describe("IdpOrgSyncPage", () => { + test.beforeEach(async ({ page }) => await beforeCoderTest(page)); + + test("add new IdP organization mapping with API", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); + + await createOrganizationSyncSettings(); + + await page.goto("/deployment/idp-org-sync", { + waitUntil: "domcontentloaded", + }); + + await expect( + page.getByRole("switch", { name: "Assign Default Organization" }), + ).toBeChecked(); + + await expect(page.getByText("idp-org-1")).toBeVisible(); + await expect( + page.getByText("fbd2116a-8961-4954-87ae-e4575bd29ce0").first(), + ).toBeVisible(); + + await expect(page.getByText("idp-org-2")).toBeVisible(); + await expect( + page.getByText("fbd2116a-8961-4954-87ae-e4575bd29ce0").last(), + ).toBeVisible(); + }); + + test("delete a IdP org to coder org mapping row", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); + await createOrganizationSyncSettings(); + await page.goto("/deployment/idp-org-sync", { + waitUntil: "domcontentloaded", + }); + + await expect(page.getByText("idp-org-1")).toBeVisible(); + await page + .getByRole("button", { name: /delete/i }) + .first() + .click(); + await expect(page.getByText("idp-org-1")).not.toBeVisible(); + await expect( + page.getByText("Organization sync settings updated."), + ).toBeVisible(); + }); + + test("update sync field", async ({ page }) => { + requiresLicense(); + await page.goto("/deployment/idp-org-sync", { + waitUntil: "domcontentloaded", + }); + + const syncField = page.getByRole("textbox", { + name: "Organization sync field", + }); + const saveButton = page.getByRole("button", { name: "Save" }).first(); + + await expect(saveButton).toBeDisabled(); + + await syncField.fill("test-field"); + await expect(saveButton).toBeEnabled(); + + await page.getByRole("button", { name: "Save" }).click(); + + await expect( + page.getByText("Organization sync settings updated."), + ).toBeVisible(); + }); + + test("toggle default organization assignment", async ({ page }) => { + requiresLicense(); + await page.goto("/deployment/idp-org-sync", { + waitUntil: "domcontentloaded", + }); + + const toggle = page.getByRole("switch", { + name: "Assign Default Organization", + }); + await toggle.click(); + + await expect( + page.getByText("Organization sync settings updated."), + ).toBeVisible(); + + await expect(toggle).not.toBeChecked(); + }); + + test("export policy button is enabled when sync settings are present", async ({ + page, + }) => { + requiresLicense(); + await setupApiCalls(page); + + await page.goto("/deployment/idp-org-sync", { + waitUntil: "domcontentloaded", + }); + + const exportButton = page.getByRole("button", { name: /Export Policy/i }); + await createOrganizationSyncSettings(); + + await expect(exportButton).toBeEnabled(); + await exportButton.click(); + }); + + test("add new IdP organization mapping with UI", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); + + await createOrganizationWithName("developers"); + + await page.goto("/deployment/idp-org-sync", { + waitUntil: "domcontentloaded", + }); + + const idpOrgInput = page.getByLabel("IdP organization name"); + const orgSelector = page.getByPlaceholder("Select organization"); + const addButton = page.getByRole("button", { + name: /Add IdP organization/i, + }); + + await expect(addButton).toBeDisabled(); + + await idpOrgInput.fill("new-idp-org"); + + // Select Coder organization from combobox + await orgSelector.click(); + await page.getByRole("option", { name: "developers" }).click(); + + // Add button should now be enabled + await expect(addButton).toBeEnabled(); + + await addButton.click(); + + // Verify new mapping appears in table + const newRow = page.getByTestId("idp-org-new-idp-org"); + await expect(newRow).toBeVisible(); + await expect(newRow.getByText("new-idp-org")).toBeVisible(); + await expect(newRow.getByText("developers")).toBeVisible(); + + await expect( + page.getByText("Organization sync settings updated."), + ).toBeVisible(); + + await deleteOrganization("developers"); + }); +});