diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 9bb8d62719..c100c16d36 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -1,4 +1,5 @@ import type { Page } from "@playwright/test"; +import { expect } from "@playwright/test"; import * as API from "api/api"; import { coderPort } from "./constants"; import { findSessionToken, randomName } from "./helpers"; @@ -47,3 +48,68 @@ export const createGroup = async (orgId: string) => { }); return group; }; + +export async function verifyConfigFlag( + page: Page, + config: API.DeploymentConfig, + flag: string, +) { + const opt = config.options.find((option) => option.flag === flag); + if (opt === undefined) { + // must be undefined as `false` is expected + throw new Error(`Option with env ${flag} has undefined value.`); + } + + // Map option type to test class name. + let type: string; + let value = opt.value; + + if (typeof value === "boolean") { + // Boolean options map to string (Enabled/Disabled). + type = value ? "option-enabled" : "option-disabled"; + value = value ? "Enabled" : "Disabled"; + } else if (typeof value === "number") { + type = "option-value-number"; + value = String(value); + } else if (!value || value.length === 0) { + type = "option-value-empty"; + } else if (typeof value === "string") { + type = "option-value-string"; + } else if (typeof value === "object") { + type = "option-array"; + } else { + type = "option-value-json"; + } + + // Special cases + if (opt.flag === "strict-transport-security" && opt.value === 0) { + type = "option-value-string"; + value = "Disabled"; // Display "Disabled" instead of zero seconds. + } + + const configOption = page.locator( + `div.options-table .option-${flag} .${type}`, + ); + + // Verify array of options with green marks. + if (typeof value === "object" && !Array.isArray(value)) { + Object.entries(value) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(async ([item]) => { + await expect( + configOption.locator(`.option-array-item-${item}.option-enabled`, { + hasText: item, + }), + ).toBeVisible(); + }); + return; + } + // Verify array of options with simmple dots + if (Array.isArray(value)) { + for (const item of value) { + await expect(configOption.locator("li", { hasText: item })).toBeVisible(); + } + return; + } + await expect(configOption).toHaveText(String(value)); +} diff --git a/site/e2e/playwright.config.ts b/site/e2e/playwright.config.ts index 2fe84b17a2..bb1929a062 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -111,6 +111,15 @@ export default defineConfig({ ), CODER_PPROF_ADDRESS: "127.0.0.1:" + coderdPProfPort, CODER_EXPERIMENTS: e2eFakeExperiment1 + "," + e2eFakeExperiment2, + + // Tests for Deployment / User Authentication / OIDC + CODER_OIDC_ISSUER_URL: "https://accounts.google.com", + CODER_OIDC_EMAIL_DOMAIN: "coder.com", + CODER_OIDC_CLIENT_ID: "1234567890", // FIXME: https://github.com/coder/coder/issues/12585 + CODER_OIDC_CLIENT_SECRET: "1234567890Secret", + CODER_OIDC_ALLOW_SIGNUPS: "false", + CODER_OIDC_SIGN_IN_TEXT: "Hello", + CODER_OIDC_ICON_URL: "/icon/google.svg", }, reuseExistingServer: false, }, diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts index fe88dcfba2..4aa81f109a 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -1,11 +1,10 @@ -import { expect, test, type Page } from "@playwright/test"; -import * as API from "api/api"; -import { setupApiCalls } from "../../api"; +import { test } from "@playwright/test"; +import { getDeploymentConfig } from "api/api"; +import { setupApiCalls, verifyConfigFlag } from "../../api"; test("enabled security settings", async ({ page }) => { await setupApiCalls(page); - - const config = await API.getDeploymentConfig(); + const config = await getDeploymentConfig(); await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); @@ -27,45 +26,3 @@ test("enabled security settings", async ({ page }) => { await verifyConfigFlag(page, config, flag); } }); - -const verifyConfigFlag = async ( - page: Page, - config: API.DeploymentConfig, - flag: string, -) => { - const opt = config.options.find((option) => option.flag === flag); - if (opt === undefined) { - throw new Error(`Option with env ${flag} has undefined value.`); - } - - // Map option type to test class name. - let type = "", - value = opt.value; - if (typeof value === "boolean") { - // Boolean options map to string (Enabled/Disabled). - type = value ? "option-enabled" : "option-disabled"; - value = value ? "Enabled" : "Disabled"; - } else if (typeof value === "number") { - type = "option-value-number"; - value = String(value); - } else if (!value || value.length === 0) { - type = "option-value-empty"; - } else if (typeof value === "string") { - type = "option-value-string"; - } else if (typeof value === "object") { - type = "object-array"; - } else { - type = "option-value-json"; - } - - // Special cases - if (opt.flag === "strict-transport-security" && opt.value === 0) { - type = "option-value-string"; - value = "Disabled"; // Display "Disabled" instead of zero seconds. - } - - const configOption = page.locator( - `div.options-table .option-${flag} .${type}`, - ); - await expect(configOption).toHaveText(String(value)); -}; diff --git a/site/e2e/tests/deployment/userAuth.spec.ts b/site/e2e/tests/deployment/userAuth.spec.ts new file mode 100644 index 0000000000..68e28fe6b7 --- /dev/null +++ b/site/e2e/tests/deployment/userAuth.spec.ts @@ -0,0 +1,33 @@ +import { test } from "@playwright/test"; +import { getDeploymentConfig } from "api/api"; +import { setupApiCalls, verifyConfigFlag } from "../../api"; + +test("login with OIDC", async ({ page }) => { + await setupApiCalls(page); + const config = await getDeploymentConfig(); + + await page.goto("/deployment/userauth", { waitUntil: "domcontentloaded" }); + + const flags = [ + "oidc-group-auto-create", + "oidc-allow-signups", + "oidc-auth-url-params", + "oidc-client-id", + "oidc-email-domain", + "oidc-email-field", + "oidc-group-mapping", + "oidc-ignore-email-verified", + "oidc-ignore-userinfo", + "oidc-issuer-url", + "oidc-group-regex-filter", + "oidc-scopes", + "oidc-user-role-mapping", + "oidc-username-field", + "oidc-sign-in-text", + "oidc-icon-url", + ]; + + for (const flag of flags) { + await verifyConfigFlag(page, config, flag); + } +});