From e5b94f6995773fb4804c70c43e699e6757b2a8ed Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 17 Apr 2024 12:15:42 +0200 Subject: [PATCH 1/4] refactor --- site/e2e/api.ts | 131 +++++++++++++-------- site/e2e/tests/deployment/userAuth.spec.ts | 46 ++++---- 2 files changed, 108 insertions(+), 69 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index c100c16d3687f..18b3346c22a36 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -1,6 +1,7 @@ import type { Page } from "@playwright/test"; import { expect } from "@playwright/test"; import * as API from "api/api"; +import type { SerpentOption } from "api/typesGenerated"; import { coderPort } from "./constants"; import { findSessionToken, randomName } from "./helpers"; @@ -49,67 +50,105 @@ export const createGroup = async (orgId: string) => { return group; }; -export async function verifyConfigFlag( +export async function verifyConfigFlagBoolean( 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.`); - } + const opt = findConfigOption(config, flag); + const type = opt.value ? "option-enabled" : "option-disabled"; + const value = opt.value ? "Enabled" : "Disabled"; - // 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"; - } + const configOption = page.locator( + `div.options-table .option-${flag} .${type}`, + ); + await expect(configOption).toHaveText(value); +} + +export async function verifyConfigFlagNumber( + page: Page, + config: API.DeploymentConfig, + flag: string, +) { + const opt = findConfigOption(config, flag); + const type = "option-value-number"; + + const configOption = page.locator( + `div.options-table .option-${flag} .${type}`, + ); + await expect(configOption).toHaveText(String(opt.value)); +} + +export async function verifyConfigFlagString( + page: Page, + config: API.DeploymentConfig, + flag: string, +) { + const opt = findConfigOption(config, flag); + const type = "option-value-string"; // Special cases - if (opt.flag === "strict-transport-security" && opt.value === 0) { + /*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(opt.value); +} + +export async function verifyConfigFlagArray( + page: Page, + config: API.DeploymentConfig, + flag: string, +) { + const opt = findConfigOption(config, flag); + const type = "option-array"; + + const configOption = page.locator( + `div.options-table .option-${flag} .${type}`, + ); + + // Verify array of options with simple dots + for (const item of opt.value) { + await expect(configOption.locator("li", { hasText: item })).toBeVisible(); } +} + +export async function verifyConfigFlagEntries( + page: Page, + config: API.DeploymentConfig, + flag: string, +) { + const opt = findConfigOption(config, flag); + const type = "option-array"; 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; + Object.entries(opt.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(); + }); +} + +function findConfigOption( + config: API.DeploymentConfig, + flag: string, +): SerpentOption { + 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.`); } - await expect(configOption).toHaveText(String(value)); + return opt; } diff --git a/site/e2e/tests/deployment/userAuth.spec.ts b/site/e2e/tests/deployment/userAuth.spec.ts index 68e28fe6b78f5..cf656c99fae3f 100644 --- a/site/e2e/tests/deployment/userAuth.spec.ts +++ b/site/e2e/tests/deployment/userAuth.spec.ts @@ -1,6 +1,12 @@ import { test } from "@playwright/test"; import { getDeploymentConfig } from "api/api"; -import { setupApiCalls, verifyConfigFlag } from "../../api"; +import { + setupApiCalls, + verifyConfigFlagArray, + verifyConfigFlagBoolean, + verifyConfigFlagEntries, + verifyConfigFlagString, +} from "../../api"; test("login with OIDC", async ({ page }) => { await setupApiCalls(page); @@ -8,26 +14,20 @@ test("login with OIDC", async ({ page }) => { 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); - } + await verifyConfigFlagBoolean(page, config, "oidc-group-auto-create"); + await verifyConfigFlagBoolean(page, config, "oidc-allow-signups"); + await verifyConfigFlagEntries(page, config, "oidc-auth-url-params"); + await verifyConfigFlagString(page, config, "oidc-client-id"); + await verifyConfigFlagArray(page, config, "oidc-email-domain"); + await verifyConfigFlagString(page, config, "oidc-email-field"); + await verifyConfigFlagEntries(page, config, "oidc-group-mapping"); + await verifyConfigFlagBoolean(page, config, "oidc-ignore-email-verified"); + await verifyConfigFlagBoolean(page, config, "oidc-ignore-userinfo"); + await verifyConfigFlagString(page, config, "oidc-issuer-url"); + await verifyConfigFlagString(page, config, "oidc-group-regex-filter"); + await verifyConfigFlagArray(page, config, "oidc-scopes"); + await verifyConfigFlagEntries(page, config, "oidc-user-role-mapping"); + await verifyConfigFlagString(page, config, "oidc-username-field"); + await verifyConfigFlagString(page, config, "oidc-sign-in-text"); + await verifyConfigFlagString(page, config, "oidc-icon-url"); }); From f4f081427d6b5265df008d7049cbe1a8532df01d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 17 Apr 2024 13:11:36 +0200 Subject: [PATCH 2/4] done --- site/e2e/api.ts | 23 ++-------- site/e2e/playwright.config.ts | 2 +- site/e2e/tests/deployment/security.spec.ts | 53 +++++++++++++++------- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 18b3346c22a36..940dcf11ab93d 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -71,10 +71,8 @@ export async function verifyConfigFlagNumber( flag: string, ) { const opt = findConfigOption(config, flag); - const type = "option-value-number"; - const configOption = page.locator( - `div.options-table .option-${flag} .${type}`, + `div.options-table .option-${flag} .option-value-number`, ); await expect(configOption).toHaveText(String(opt.value)); } @@ -85,16 +83,9 @@ export async function verifyConfigFlagString( flag: string, ) { const opt = findConfigOption(config, flag); - const type = "option-value-string"; - - // 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}`, + `div.options-table .option-${flag} .option-value-string`, ); await expect(configOption).toHaveText(opt.value); } @@ -105,10 +96,8 @@ export async function verifyConfigFlagArray( flag: string, ) { const opt = findConfigOption(config, flag); - const type = "option-array"; - const configOption = page.locator( - `div.options-table .option-${flag} .${type}`, + `div.options-table .option-${flag} .option-array`, ); // Verify array of options with simple dots @@ -123,10 +112,8 @@ export async function verifyConfigFlagEntries( flag: string, ) { const opt = findConfigOption(config, flag); - const type = "option-array"; - const configOption = page.locator( - `div.options-table .option-${flag} .${type}`, + `div.options-table .option-${flag} .option-array`, ); // Verify array of options with green marks. @@ -141,7 +128,7 @@ export async function verifyConfigFlagEntries( }); } -function findConfigOption( +export function findConfigOption( config: API.DeploymentConfig, flag: string, ): SerpentOption { diff --git a/site/e2e/playwright.config.ts b/site/e2e/playwright.config.ts index bb1929a0622cc..9525270e352af 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -32,7 +32,7 @@ export default defineConfig({ timeout: 50_000, }, ], - reporter: [["./reporter.ts"]], + //reporter: [["./reporter.ts"]], use: { baseURL: `http://localhost:${coderPort}`, video: "retain-on-failure", diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts index 4aa81f109a1f4..4c66398898729 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -1,6 +1,14 @@ -import { test } from "@playwright/test"; +import type { Page } from "@playwright/test"; +import { expect, test } from "@playwright/test"; +import type * as API from "api/api"; import { getDeploymentConfig } from "api/api"; -import { setupApiCalls, verifyConfigFlag } from "../../api"; +import { + findConfigOption, + setupApiCalls, + verifyConfigFlagBoolean, + verifyConfigFlagNumber, + verifyConfigFlagString, +} from "../../api"; test("enabled security settings", async ({ page }) => { await setupApiCalls(page); @@ -8,21 +16,32 @@ test("enabled security settings", async ({ page }) => { await page.goto("/deployment/security", { waitUntil: "domcontentloaded" }); - const flags = [ - "ssh-keygen-algorithm", - "secure-auth-cookie", - "disable-owner-workspace-access", + await verifyConfigFlagString(page, config, "ssh-keygen-algorithm"); + await verifyConfigFlagBoolean(page, config, "secure-auth-cookie"); + await verifyConfigFlagBoolean(page, config, "disable-owner-workspace-access"); - "tls-redirect-http-to-https", - "strict-transport-security", - "tls-address", - "tls-allow-insecure-ciphers", - "tls-client-auth", - "tls-enable", - "tls-min-version", - ]; + await verifyConfigFlagBoolean(page, config, "tls-redirect-http-to-https"); + await verifyStrictTransportSecurity(page, config); + await verifyConfigFlagString(page, config, "tls-address"); + await verifyConfigFlagBoolean(page, config, "tls-allow-insecure-ciphers"); + await verifyConfigFlagString(page, config, "tls-client-auth"); + await verifyConfigFlagBoolean(page, config, "tls-enable"); + await verifyConfigFlagString(page, config, "tls-min-version"); +}); - for (const flag of flags) { - await verifyConfigFlag(page, config, flag); +async function verifyStrictTransportSecurity( + page: Page, + config: API.DeploymentConfig, +) { + const flag = "strict-transport-security"; + const opt = findConfigOption(config, flag); + if (opt.value !== 0) { + await verifyConfigFlagNumber(page, config, flag); + return; // Make sure the return statement is inside the if block } -}); + + const configOption = page.locator( + `div.options-table .option-${flag} .option-value-string`, + ); + await expect(configOption).toHaveText("Disabled"); +} From c2babacd8d436bc7bbf0431b355f2ab2711cc5bf Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 17 Apr 2024 13:12:06 +0200 Subject: [PATCH 3/4] fix --- site/e2e/tests/deployment/security.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/e2e/tests/deployment/security.spec.ts b/site/e2e/tests/deployment/security.spec.ts index 4c66398898729..ede966260ca44 100644 --- a/site/e2e/tests/deployment/security.spec.ts +++ b/site/e2e/tests/deployment/security.spec.ts @@ -37,7 +37,7 @@ async function verifyStrictTransportSecurity( const opt = findConfigOption(config, flag); if (opt.value !== 0) { await verifyConfigFlagNumber(page, config, flag); - return; // Make sure the return statement is inside the if block + return; } const configOption = page.locator( From cb5129d2838bb96488074c42f0ac3b833eb78a56 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 17 Apr 2024 13:45:32 +0200 Subject: [PATCH 4/4] reporter back --- 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 9525270e352af..bb1929a0622cc 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -32,7 +32,7 @@ export default defineConfig({ timeout: 50_000, }, ], - //reporter: [["./reporter.ts"]], + reporter: [["./reporter.ts"]], use: { baseURL: `http://localhost:${coderPort}`, video: "retain-on-failure",