From 603964fdefec008e80a06e5ad1db7d5a4f117c86 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 10:58:34 +0200 Subject: [PATCH 01/10] refactor --- site/e2e/api.ts | 41 +++++++++++++++++ site/e2e/playwright.config.ts | 6 +++ site/e2e/tests/deployment/security.spec.ts | 51 ++-------------------- site/e2e/tests/deployment/userAuth.spec.ts | 12 +++++ 4 files changed, 62 insertions(+), 48 deletions(-) create mode 100644 site/e2e/tests/deployment/userAuth.spec.ts diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 9bb8d62719066..e0ef79f6e147c 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,43 @@ export const createGroup = async (orgId: string) => { }); return group; }; + +export async function verifyConfigFlag(page: Page, flag: string) { + const config = await API.getDeploymentConfig(); + + 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/playwright.config.ts b/site/e2e/playwright.config.ts index 2fe84b17a2f83..c0a13426a3ceb 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -111,6 +111,12 @@ export default defineConfig({ ), CODER_PPROF_ADDRESS: "127.0.0.1:" + coderdPProfPort, CODER_EXPERIMENTS: e2eFakeExperiment1 + "," + e2eFakeExperiment2, + + // Tests for Deployment / User Authentication + 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", }, reuseExistingServer: false, }, diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts index fe88dcfba248c..9579fce8dee79 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -1,12 +1,9 @@ -import { expect, test, type Page } from "@playwright/test"; -import * as API from "api/api"; -import { setupApiCalls } from "../../api"; +import { test } from "@playwright/test"; +import { setupApiCalls, verifyConfigFlag } from "../../api"; test("enabled security settings", async ({ page }) => { await setupApiCalls(page); - const config = await API.getDeploymentConfig(); - await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); const flags = [ @@ -24,48 +21,6 @@ test("enabled security settings", async ({ page }) => { ]; for (const flag of flags) { - await verifyConfigFlag(page, config, flag); + await verifyConfigFlag(page, 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 0000000000000..6bf19bee9cf35 --- /dev/null +++ b/site/e2e/tests/deployment/userAuth.spec.ts @@ -0,0 +1,12 @@ +import { test } from "@playwright/test"; +import * as API from "api/api"; +import { setupApiCalls } from "../../api"; + +test("user authentication", async ({ page }) => { + await setupApiCalls(page); + + await API.getDeploymentConfig(); + await page.goto("/deployment/userauth", { waitUntil: "domcontentloaded" }); + + await page.pause(); +}); From 4e6a973a4a15afede6b7c31cbcec2e9122151a68 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 11:12:14 +0200 Subject: [PATCH 02/10] need to fix object array --- site/e2e/api.ts | 4 +-- site/e2e/playwright.config.ts | 5 +++- site/e2e/tests/deployment/security.spec.ts | 4 ++- site/e2e/tests/deployment/userAuth.spec.ts | 31 ++++++++++++++++++---- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index e0ef79f6e147c..98fff88de6f27 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -49,9 +49,7 @@ export const createGroup = async (orgId: string) => { return group; }; -export async function verifyConfigFlag(page: Page, flag: string) { - const config = await API.getDeploymentConfig(); - +export async function verifyConfigFlag(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.`); diff --git a/site/e2e/playwright.config.ts b/site/e2e/playwright.config.ts index c0a13426a3ceb..e374eb2393e1f 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -112,11 +112,14 @@ export default defineConfig({ CODER_PPROF_ADDRESS: "127.0.0.1:" + coderdPProfPort, CODER_EXPERIMENTS: e2eFakeExperiment1 + "," + e2eFakeExperiment2, - // Tests for Deployment / User Authentication + // 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 9579fce8dee79..b3fd2e7216c3c 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -1,8 +1,10 @@ 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 getDeploymentConfig() await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); @@ -21,6 +23,6 @@ test("enabled security settings", async ({ page }) => { ]; for (const flag of flags) { - await verifyConfigFlag(page, flag); + await verifyConfigFlag(page, config, flag); } }); diff --git a/site/e2e/tests/deployment/userAuth.spec.ts b/site/e2e/tests/deployment/userAuth.spec.ts index 6bf19bee9cf35..8f9bb7a3e89a2 100644 --- a/site/e2e/tests/deployment/userAuth.spec.ts +++ b/site/e2e/tests/deployment/userAuth.spec.ts @@ -1,12 +1,33 @@ import { test } from "@playwright/test"; -import * as API from "api/api"; -import { setupApiCalls } from "../../api"; +import { getDeploymentConfig } from "api/api"; +import { setupApiCalls, verifyConfigFlag } from "../../api"; -test("user authentication", async ({ page }) => { +test("login with OIDC", async ({ page }) => { await setupApiCalls(page); + const config = await getDeploymentConfig(); - await API.getDeploymentConfig(); await page.goto("/deployment/userauth", { waitUntil: "domcontentloaded" }); - await page.pause(); + 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); + } }); From 45c3131e4276e5928f17a23463d5d6409a836c1d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 11:18:06 +0200 Subject: [PATCH 03/10] fix --- site/e2e/playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/e2e/playwright.config.ts b/site/e2e/playwright.config.ts index e374eb2393e1f..bb1929a0622cc 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -117,7 +117,7 @@ export default defineConfig({ 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_ALLOW_SIGNUPS: "false", CODER_OIDC_SIGN_IN_TEXT: "Hello", CODER_OIDC_ICON_URL: "/icon/google.svg", }, From fbc70a6d0be27c6d647a5faa164e5c02b6128e9c Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 11:24:27 +0200 Subject: [PATCH 04/10] fmt --- site/e2e/api.ts | 6 +++++- site/e2e/tests/deployment/security.spec.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 98fff88de6f27..b1163269345d0 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -49,7 +49,11 @@ export const createGroup = async (orgId: string) => { return group; }; -export async function verifyConfigFlag(page: Page, config: API.DeploymentConfig, flag: string) { +export async function verifyConfigFlag( + 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.`); diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts index b3fd2e7216c3c..4aa81f109a1f4 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -4,7 +4,7 @@ import { setupApiCalls, verifyConfigFlag } from "../../api"; test("enabled security settings", async ({ page }) => { await setupApiCalls(page); - const config = await getDeploymentConfig() + const config = await getDeploymentConfig(); await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); From ec060bb5a391d0cbdccd71a1055aaac47f83342a Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 11:41:14 +0200 Subject: [PATCH 05/10] oidc-auth-url-params --- site/e2e/api.ts | 12 +++++++++++- site/e2e/tests/deployment/userAuth.spec.ts | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index b1163269345d0..652d1e86e87ab 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -74,7 +74,7 @@ export async function verifyConfigFlag( } else if (typeof value === "string") { type = "option-value-string"; } else if (typeof value === "object") { - type = "object-array"; + type = "option-array"; } else { type = "option-value-json"; } @@ -88,5 +88,15 @@ export async function verifyConfigFlag( const configOption = page.locator( `div.options-table .option-${flag} .${type}`, ); + + if (type === "option-array") { + Object.entries(value) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(async ([item]) => { + await expect(configOption.locator(`.option-array-item-${item}.option-enabled`)).toHaveText(item); + }); + return; + } + await expect(configOption).toHaveText(String(value)); } diff --git a/site/e2e/tests/deployment/userAuth.spec.ts b/site/e2e/tests/deployment/userAuth.spec.ts index 8f9bb7a3e89a2..e9f395f207ac9 100644 --- a/site/e2e/tests/deployment/userAuth.spec.ts +++ b/site/e2e/tests/deployment/userAuth.spec.ts @@ -11,7 +11,7 @@ test("login with OIDC", async ({ page }) => { const flags = [ "oidc-group-auto-create", "oidc-allow-signups", - //"oidc-auth-url-params", + "oidc-auth-url-params", "oidc-client-id", //"oidc-email-domain", "oidc-email-field", From ad4f37030fb5e8be65f1bbee27ba4805619c58c4 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 13:11:02 +0200 Subject: [PATCH 06/10] WIP --- site/e2e/api.ts | 18 +++++++++++++----- site/e2e/tests/deployment/userAuth.spec.ts | 8 ++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 652d1e86e87ab..9a8737d2ba972 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -89,14 +89,22 @@ export async function verifyConfigFlag( `div.options-table .option-${flag} .${type}`, ); - if (type === "option-array") { + 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`)).toHaveText(item); - }); + .map(async ([item]) => + expect( + configOption.locator(`.option-array-item-${item}.option-enabled`, { + hasText: item, + }), + ), + ); + return; + } else if (Array.isArray(value)) { + for (const item of value) { + expect(configOption.locator("li", { hasText: item })); + } return; } - await expect(configOption).toHaveText(String(value)); } diff --git a/site/e2e/tests/deployment/userAuth.spec.ts b/site/e2e/tests/deployment/userAuth.spec.ts index e9f395f207ac9..68e28fe6b78f5 100644 --- a/site/e2e/tests/deployment/userAuth.spec.ts +++ b/site/e2e/tests/deployment/userAuth.spec.ts @@ -13,15 +13,15 @@ test("login with OIDC", async ({ page }) => { "oidc-allow-signups", "oidc-auth-url-params", "oidc-client-id", - //"oidc-email-domain", + "oidc-email-domain", "oidc-email-field", - //"oidc-group-mapping", + "oidc-group-mapping", "oidc-ignore-email-verified", "oidc-ignore-userinfo", "oidc-issuer-url", "oidc-group-regex-filter", - //"oidc-scopes", - //"oidc-user-role-mapping", + "oidc-scopes", + "oidc-user-role-mapping", "oidc-username-field", "oidc-sign-in-text", "oidc-icon-url", From 069518aa8cc36012692901ec4514df6246800918 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 13:20:34 +0200 Subject: [PATCH 07/10] expect --- site/e2e/api.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 9a8737d2ba972..c7544a8716324 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -56,12 +56,14 @@ export async function verifyConfigFlag( ) { 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 = "", - value = opt.value; + let type: string; + let value = opt.value; + if (typeof value === "boolean") { // Boolean options map to string (Enabled/Disabled). type = value ? "option-enabled" : "option-disabled"; @@ -92,15 +94,16 @@ export async function verifyConfigFlag( if (typeof value === "object" && !Array.isArray(value)) { Object.entries(value) .sort((a, b) => a[0].localeCompare(b[0])) - .map(async ([item]) => + .map(async ([item]) => { expect( configOption.locator(`.option-array-item-${item}.option-enabled`, { hasText: item, - }), - ), - ); + }) + ) + }); return; - } else if (Array.isArray(value)) { + } + if (Array.isArray(value)) { for (const item of value) { expect(configOption.locator("li", { hasText: item })); } From f1d80735fcc4162a70ce0b9b7283fa5522cf48a0 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 13:23:51 +0200 Subject: [PATCH 08/10] ok --- site/e2e/api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index c7544a8716324..dc32831df9c64 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -95,17 +95,17 @@ export async function verifyConfigFlag( Object.entries(value) .sort((a, b) => a[0].localeCompare(b[0])) .map(async ([item]) => { - expect( + await expect( configOption.locator(`.option-array-item-${item}.option-enabled`, { hasText: item, }) - ) + ).toBeVisible(); }); return; } if (Array.isArray(value)) { for (const item of value) { - expect(configOption.locator("li", { hasText: item })); + await expect(configOption.locator("li", { hasText: item })).toBeVisible(); } return; } From 60b2d66092fe922a4031f7b1a4257184c5b16e21 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 13:25:30 +0200 Subject: [PATCH 09/10] fmt --- site/e2e/api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index dc32831df9c64..db0d7ee74bd9a 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -98,9 +98,9 @@ export async function verifyConfigFlag( await expect( configOption.locator(`.option-array-item-${item}.option-enabled`, { hasText: item, - }) + }), ).toBeVisible(); - }); + }); return; } if (Array.isArray(value)) { From a0d9055080f4f6f336f3d64a832f3f8cb42953ac Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 16 Apr 2024 13:35:17 +0200 Subject: [PATCH 10/10] comments --- site/e2e/api.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index db0d7ee74bd9a..c100c16d3687f 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -91,6 +91,7 @@ export async function verifyConfigFlag( `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])) @@ -103,6 +104,7 @@ export async function verifyConfigFlag( }); 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();