From 3549cc438e5a4fc40abac9a3f36f90420cbc4f2a Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Thu, 5 Dec 2024 21:10:22 +0000 Subject: [PATCH 1/3] feat: create e2e tests for IDP org sync settings page --- site/e2e/README.md | 2 +- site/e2e/api.ts | 25 +++ site/e2e/tests/deployment/idpOrgSync.spec.ts | 154 +++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 site/e2e/tests/deployment/idpOrgSync.spec.ts 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..d1b83dcfd83e5 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -63,6 +63,31 @@ export const createOrganization = async () => { return org; }; +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..40ecc87e565fb --- /dev/null +++ b/site/e2e/tests/deployment/idpOrgSync.spec.ts @@ -0,0 +1,154 @@ +import { expect, test } from "@playwright/test"; +import { + createOrganizationSyncSettings, + createOrganizationWithName, + 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(); + }); +}); From 9e7f39ca7c6417842da4b6236e2a086ed4e09250 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 6 Dec 2024 20:19:17 +0000 Subject: [PATCH 2/3] fix: delete created org at end of test --- site/e2e/tests/deployment/idpOrgSync.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/e2e/tests/deployment/idpOrgSync.spec.ts b/site/e2e/tests/deployment/idpOrgSync.spec.ts index 40ecc87e565fb..59fccb440400b 100644 --- a/site/e2e/tests/deployment/idpOrgSync.spec.ts +++ b/site/e2e/tests/deployment/idpOrgSync.spec.ts @@ -2,6 +2,7 @@ import { expect, test } from "@playwright/test"; import { createOrganizationSyncSettings, createOrganizationWithName, + deleteOrganization, setupApiCalls, } from "../../api"; import { requiresLicense } from "../../helpers"; @@ -150,5 +151,7 @@ test.describe("IdpOrgSyncPage", () => { await expect( page.getByText("Organization sync settings updated."), ).toBeVisible(); + + await deleteOrganization("developers"); }); }); From 84096aa5d8c083d9182dce75e935b17779035abb Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 6 Dec 2024 20:37:24 +0000 Subject: [PATCH 3/3] fix: add deleteOrganization to e2e test api --- site/e2e/api.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index d1b83dcfd83e5..0494467799a97 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -63,6 +63,10 @@ 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, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy