From 423af28f1630a90a5d05decc5af626ae9a46b76a Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 17 Mar 2025 21:17:05 +0000 Subject: [PATCH 1/8] ok well this seems to work --- site/e2e/api.ts | 29 +++-- site/e2e/tests/organizationGroups.spec.ts | 8 +- .../e2e/tests/organizations/auditLogs.spec.ts | 100 ++++++++++++++++++ site/e2e/tests/roles.spec.ts | 16 ++- 4 files changed, 137 insertions(+), 16 deletions(-) create mode 100644 site/e2e/tests/organizations/auditLogs.spec.ts diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 0dc9e46831708..5e3fd2de06802 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -38,15 +38,25 @@ export const createUser = async (...orgIds: string[]) => { return user; }; -export const createOrganizationMember = async ( - orgRoles: Record, -): Promise => { +type CreateOrganizationMemberOptions = { + username?: string; + email?: string; + password?: string; + orgRoles: Record; +}; + +export const createOrganizationMember = async ({ + username = randomName(), + email = `${username}@coder.com`, + password = defaultPassword, + orgRoles, +}: CreateOrganizationMemberOptions): Promise => { const name = randomName(); const user = await API.createUser({ - email: `${name}@coder.com`, - username: name, - name: name, - password: defaultPassword, + email, + username, + name: username, + password, login_type: "password", organization_ids: Object.keys(orgRoles), user_status: null, @@ -59,7 +69,7 @@ export const createOrganizationMember = async ( return { username: user.username, email: user.email, - password: defaultPassword, + password, }; }; @@ -74,8 +84,7 @@ export const createGroup = async (orgId: string) => { return group; }; -export const createOrganization = async () => { - const name = randomName(); +export const createOrganization = async (name = randomName()) => { const org = await API.createOrganization({ name, display_name: `Org ${name}`, diff --git a/site/e2e/tests/organizationGroups.spec.ts b/site/e2e/tests/organizationGroups.spec.ts index 9b3ea986aa580..08768d4bbae11 100644 --- a/site/e2e/tests/organizationGroups.spec.ts +++ b/site/e2e/tests/organizationGroups.spec.ts @@ -34,7 +34,9 @@ test("create group", async ({ page }) => { // Create a new organization const org = await createOrganization(); const orgUserAdmin = await createOrganizationMember({ - [org.id]: ["organization-user-admin"], + orgRoles: { + [org.id]: ["organization-user-admin"], + }, }); await login(page, orgUserAdmin); @@ -99,7 +101,9 @@ test("change quota settings", async ({ page }) => { const org = await createOrganization(); const group = await createGroup(org.id); const orgUserAdmin = await createOrganizationMember({ - [org.id]: ["organization-user-admin"], + orgRoles: { + [org.id]: ["organization-user-admin"], + }, }); // Go to settings diff --git a/site/e2e/tests/organizations/auditLogs.spec.ts b/site/e2e/tests/organizations/auditLogs.spec.ts new file mode 100644 index 0000000000000..1be64d53eae78 --- /dev/null +++ b/site/e2e/tests/organizations/auditLogs.spec.ts @@ -0,0 +1,100 @@ +import { type Page, chromium, expect, test } from "@playwright/test"; +import { + createOrganization, + createOrganizationMember, + createUser, + setupApiCalls, +} from "../../api"; +import { defaultPassword, users } from "../../constants"; +import { + createTemplate, + createWorkspace, + currentUser, + login, + randomName, + requiresLicense, +} from "../../helpers"; +import { beforeCoderTest } from "../../hooks"; + +test.describe.configure({ mode: "parallel" }); + +const orgName = randomName(); + +const orgAuditor = { + username: `org-auditor-${orgName}`, + password: defaultPassword, + email: `org-auditor-${orgName}@coder.com`, +}; + +test.beforeEach(({ page }) => { + beforeCoderTest(page); +}); + +test.describe("organization scoped audit logs", () => { + requiresLicense(); + + test.beforeAll(async ({ browser }) => { + const context = await browser.newContext(); + const page = await context.newPage(); + + await login(page); + await setupApiCalls(page); + + const org = await createOrganization(orgName); + await createOrganizationMember({ + ...orgAuditor, + orgRoles: { + [org.id]: ["organization-auditor"], + }, + }); + + await context.close(); + }); + + test("organization auditors cannot see logins", async ({ page }) => { + // Go to the audit history + await login(page, orgAuditor); + await page.goto("/audit"); + const username = orgAuditor.username; + + const loginMessage = `${username} logged in`; + // Make sure those things we did all actually show up + await expect(page.getByText(loginMessage).first()).not.toBeVisible(); + }); + + test("creating organization is logged", async ({ page }) => { + await login(page, orgAuditor); + + // Go to the audit history + await page.goto("/audit", { waitUntil: "domcontentloaded" }); + + const auditLogText = `${users.owner.username} created organization ${orgName}`; + const org = page.locator(".MuiTableRow-root", { + hasText: auditLogText, + }); + await org.scrollIntoViewIfNeeded(); + await expect(org).toBeVisible(); + + await org.getByLabel("open-dropdown").click(); + await expect(org.getByText(`icon: "/emojis/1f957.png"`)).toBeVisible(); + }); + + test("assigning an organization role is logged", async ({ page }) => { + await login(page, orgAuditor); + + // Go to the audit history + await page.goto("/audit", { waitUntil: "domcontentloaded" }); + + const auditLogText = `${users.owner.username} updated organization member ${orgAuditor.username}`; + const member = page.locator(".MuiTableRow-root", { + hasText: auditLogText, + }); + await member.scrollIntoViewIfNeeded(); + await expect(member).toBeVisible(); + + await member.getByLabel("open-dropdown").click(); + await expect( + member.getByText(`roles: ["organization-auditor"]`), + ).toBeVisible(); + }); +}); diff --git a/site/e2e/tests/roles.spec.ts b/site/e2e/tests/roles.spec.ts index 484e6294de7a1..e6b92bd944ba0 100644 --- a/site/e2e/tests/roles.spec.ts +++ b/site/e2e/tests/roles.spec.ts @@ -106,7 +106,9 @@ test.describe("org-scoped roles admin settings access", () => { test("org template admin can see admin settings", async ({ page }) => { const org = await createOrganization(); const orgTemplateAdmin = await createOrganizationMember({ - [org.id]: ["organization-template-admin"], + orgRoles: { + [org.id]: ["organization-template-admin"], + }, }); await login(page, orgTemplateAdmin); @@ -118,7 +120,9 @@ test.describe("org-scoped roles admin settings access", () => { test("org user admin can see admin settings", async ({ page }) => { const org = await createOrganization(); const orgUserAdmin = await createOrganizationMember({ - [org.id]: ["organization-user-admin"], + orgRoles: { + [org.id]: ["organization-user-admin"], + }, }); await login(page, orgUserAdmin); @@ -130,7 +134,9 @@ test.describe("org-scoped roles admin settings access", () => { test("org auditor can see admin settings", async ({ page }) => { const org = await createOrganization(); const orgAuditor = await createOrganizationMember({ - [org.id]: ["organization-auditor"], + orgRoles: { + [org.id]: ["organization-auditor"], + }, }); await login(page, orgAuditor); @@ -142,7 +148,9 @@ test.describe("org-scoped roles admin settings access", () => { test("org admin can see admin settings", async ({ page }) => { const org = await createOrganization(); const orgAdmin = await createOrganizationMember({ - [org.id]: ["organization-admin"], + orgRoles: { + [org.id]: ["organization-admin"], + }, }); await login(page, orgAdmin); From afc61cdcc100dbd6a35c8e44bf8923e2533ce4f0 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 17 Mar 2025 21:21:17 +0000 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/e2e/tests/organizations/auditLogs.spec.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/site/e2e/tests/organizations/auditLogs.spec.ts b/site/e2e/tests/organizations/auditLogs.spec.ts index 1be64d53eae78..3044d9da2d7ca 100644 --- a/site/e2e/tests/organizations/auditLogs.spec.ts +++ b/site/e2e/tests/organizations/auditLogs.spec.ts @@ -1,19 +1,11 @@ -import { type Page, chromium, expect, test } from "@playwright/test"; +import { type Page, expect, test } from "@playwright/test"; import { createOrganization, createOrganizationMember, - createUser, setupApiCalls, } from "../../api"; import { defaultPassword, users } from "../../constants"; -import { - createTemplate, - createWorkspace, - currentUser, - login, - randomName, - requiresLicense, -} from "../../helpers"; +import { login, randomName, requiresLicense } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.describe.configure({ mode: "parallel" }); From e7293e4215a62ae6ec8c178084ff34cc1e91fb8e Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 17 Mar 2025 22:07:40 +0000 Subject: [PATCH 3/8] use user admin and template admin for a bunch more tests --- site/e2e/tests/auditLogs.spec.ts | 179 ++++++++++-------- site/e2e/tests/groups/addMembers.spec.ts | 7 +- .../groups/addUsersToDefaultGroup.spec.ts | 7 +- site/e2e/tests/groups/createGroup.spec.ts | 7 +- site/e2e/tests/groups/removeGroup.spec.ts | 7 +- site/e2e/tests/groups/removeMember.spec.ts | 7 +- site/e2e/tests/organizationGroups.spec.ts | 8 +- .../e2e/tests/organizations/auditLogs.spec.ts | 92 --------- site/e2e/tests/roles.spec.ts | 16 +- .../e2e/tests/templates/listTemplates.spec.ts | 3 +- .../templates/updateTemplateSchedule.spec.ts | 3 +- site/e2e/tests/updateTemplate.spec.ts | 11 +- 12 files changed, 128 insertions(+), 219 deletions(-) delete mode 100644 site/e2e/tests/organizations/auditLogs.spec.ts diff --git a/site/e2e/tests/auditLogs.spec.ts b/site/e2e/tests/auditLogs.spec.ts index 31d3208c636fa..c25a828eedb64 100644 --- a/site/e2e/tests/auditLogs.spec.ts +++ b/site/e2e/tests/auditLogs.spec.ts @@ -1,10 +1,11 @@ import { type Page, expect, test } from "@playwright/test"; -import { users } from "../constants"; +import { defaultPassword, users } from "../constants"; import { createTemplate, + createUser, createWorkspace, - currentUser, login, + randomName, requiresLicense, } from "../helpers"; import { beforeCoderTest } from "../hooks"; @@ -15,6 +16,14 @@ test.beforeEach(async ({ page }) => { beforeCoderTest(page); }); +const name = randomName(); +const userToAudit = { + username: `peep-${name}`, + password: defaultPassword, + email: `peep-${name}@coder.com`, + roles: ["Template Admin", "User Admin"], +}; + async function resetSearch(page: Page, username: string) { const clearButton = page.getByLabel("Clear search"); if (await clearButton.isVisible()) { @@ -27,92 +36,96 @@ async function resetSearch(page: Page, username: string) { await expect(page.getByText("All users")).not.toBeVisible(); } -test("logins are logged", async ({ page }) => { +test.describe("audit logs", () => { requiresLicense(); - // Go to the audit history - await login(page, users.auditor); - await page.goto("/audit"); - const username = users.auditor.username; - - const loginMessage = `${username} logged in`; - // Make sure those things we did all actually show up - await resetSearch(page, username); - await expect(page.getByText(loginMessage).first()).toBeVisible(); -}); + test.beforeAll(async ({ browser }) => { + const context = await browser.newContext(); + const page = await context.newPage(); + await login(page); + await createUser(page, userToAudit); + }); -test("creating templates and workspaces is logged", async ({ page }) => { - requiresLicense(); + test("logins are logged", async ({ page }) => { + // Go to the audit history + await login(page, users.auditor); + await page.goto("/audit"); - // Do some stuff that should show up in the audit logs - await login(page, users.templateAdmin); - const username = users.templateAdmin.username; - const templateName = await createTemplate(page); - const workspaceName = await createWorkspace(page, templateName); - - // Go to the audit history - await login(page, users.auditor); - await page.goto("/audit"); - - // Make sure those things we did all actually show up - await resetSearch(page, username); - await expect( - page.getByText(`${username} created template ${templateName}`), - ).toBeVisible(); - await expect( - page.getByText(`${username} created workspace ${workspaceName}`), - ).toBeVisible(); - await expect( - page.getByText(`${username} started workspace ${workspaceName}`), - ).toBeVisible(); - - // Make sure we can inspect the details of the log item - const createdWorkspace = page.locator(".MuiTableRow-root", { - hasText: `${username} created workspace ${workspaceName}`, + // Make sure those things we did all actually show up + await resetSearch(page, users.auditor.username); + const loginMessage = `${users.auditor.username} logged in`; + await expect(page.getByText(loginMessage).first()).toBeVisible(); }); - await createdWorkspace.getByLabel("open-dropdown").click(); - await expect( - createdWorkspace.getByText(`automatic_updates: "never"`), - ).toBeVisible(); - await expect( - createdWorkspace.getByText(`name: "${workspaceName}"`), - ).toBeVisible(); -}); -test("inspecting and filtering audit logs", async ({ page }) => { - requiresLicense(); + test("creating templates and workspaces is logged", async ({ page }) => { + // Do some stuff that should show up in the audit logs + await login(page, userToAudit); + const username = userToAudit.username; + const templateName = await createTemplate(page); + const workspaceName = await createWorkspace(page, templateName); + + // Go to the audit history + await login(page, users.auditor); + await page.goto("/audit"); + + // Make sure those things we did all actually show up + await resetSearch(page, username); + await expect( + page.getByText(`${username} created template ${templateName}`), + ).toBeVisible(); + await expect( + page.getByText(`${username} created workspace ${workspaceName}`), + ).toBeVisible(); + await expect( + page.getByText(`${username} started workspace ${workspaceName}`), + ).toBeVisible(); + + // Make sure we can inspect the details of the log item + const createdWorkspace = page.locator(".MuiTableRow-root", { + hasText: `${username} created workspace ${workspaceName}`, + }); + await createdWorkspace.getByLabel("open-dropdown").click(); + await expect( + createdWorkspace.getByText(`automatic_updates: "never"`), + ).toBeVisible(); + await expect( + createdWorkspace.getByText(`name: "${workspaceName}"`), + ).toBeVisible(); + }); - // Do some stuff that should show up in the audit logs - await login(page, users.templateAdmin); - const username = users.templateAdmin.username; - const templateName = await createTemplate(page); - const workspaceName = await createWorkspace(page, templateName); - - // Go to the audit history - await login(page, users.auditor); - await page.goto("/audit"); - const loginMessage = `${username} logged in`; - const startedWorkspaceMessage = `${username} started workspace ${workspaceName}`; - - // Filter by resource type - await resetSearch(page, username); - await page.getByText("All resource types").click(); - const workspaceBuildsOption = page.getByText("Workspace Build"); - await workspaceBuildsOption.scrollIntoViewIfNeeded({ timeout: 5000 }); - await workspaceBuildsOption.click(); - // Our workspace build should be visible - await expect(page.getByText(startedWorkspaceMessage)).toBeVisible(); - // Logins should no longer be visible - await expect(page.getByText(loginMessage)).not.toBeVisible(); - await page.getByLabel("Clear search").click(); - await expect(page.getByText("All resource types")).toBeVisible(); - - // Filter by action type - await resetSearch(page, username); - await page.getByText("All actions").click(); - await page.getByText("Login", { exact: true }).click(); - // Logins should be visible - await expect(page.getByText(loginMessage).first()).toBeVisible(); - // Our workspace build should no longer be visible - await expect(page.getByText(startedWorkspaceMessage)).not.toBeVisible(); + test("inspecting and filtering audit logs", async ({ page }) => { + // Do some stuff that should show up in the audit logs + await login(page, userToAudit); + const username = userToAudit.username; + const templateName = await createTemplate(page); + const workspaceName = await createWorkspace(page, templateName); + + // Go to the audit history + await login(page, users.auditor); + await page.goto("/audit"); + const loginMessage = `${username} logged in`; + const startedWorkspaceMessage = `${username} started workspace ${workspaceName}`; + + // Filter by resource type + await resetSearch(page, username); + await page.getByText("All resource types").click(); + const workspaceBuildsOption = page.getByText("Workspace Build"); + await workspaceBuildsOption.scrollIntoViewIfNeeded({ timeout: 5000 }); + await workspaceBuildsOption.click(); + // Our workspace build should be visible + await expect(page.getByText(startedWorkspaceMessage)).toBeVisible(); + // Logins should no longer be visible + await expect(page.getByText(loginMessage)).not.toBeVisible(); + await page.getByLabel("Clear search").click(); + await expect(page.getByText("All resource types")).toBeVisible(); + + // Filter by action type + await resetSearch(page, username); + await page.getByText("All actions").click(); + await page.getByText("Login", { exact: true }).click(); + // Logins should be visible + await expect(page.getByText(loginMessage).first()).toBeVisible(); + // Our workspace build should no longer be visible + await expect(page.getByText(startedWorkspaceMessage)).not.toBeVisible(); + }); }); diff --git a/site/e2e/tests/groups/addMembers.spec.ts b/site/e2e/tests/groups/addMembers.spec.ts index 7f29f4a536385..d48b8e7beee54 100644 --- a/site/e2e/tests/groups/addMembers.spec.ts +++ b/site/e2e/tests/groups/addMembers.spec.ts @@ -5,14 +5,13 @@ import { getCurrentOrgId, setupApiCalls, } from "../../api"; -import { defaultOrganizationName } from "../../constants"; -import { requiresLicense } from "../../helpers"; -import { login } from "../../helpers"; +import { defaultOrganizationName, users } from "../../constants"; +import { login, requiresLicense } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.userAdmin); await setupApiCalls(page); }); diff --git a/site/e2e/tests/groups/addUsersToDefaultGroup.spec.ts b/site/e2e/tests/groups/addUsersToDefaultGroup.spec.ts index b1ece8705e2c6..e28566f57e73e 100644 --- a/site/e2e/tests/groups/addUsersToDefaultGroup.spec.ts +++ b/site/e2e/tests/groups/addUsersToDefaultGroup.spec.ts @@ -1,13 +1,12 @@ import { expect, test } from "@playwright/test"; import { createUser, getCurrentOrgId, setupApiCalls } from "../../api"; -import { defaultOrganizationName } from "../../constants"; -import { requiresLicense } from "../../helpers"; -import { login } from "../../helpers"; +import { defaultOrganizationName, users } from "../../constants"; +import { login, requiresLicense } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.userAdmin); }); const DEFAULT_GROUP_NAME = "Everyone"; diff --git a/site/e2e/tests/groups/createGroup.spec.ts b/site/e2e/tests/groups/createGroup.spec.ts index 8df1cdbdcc9fb..e5e6e059ebe93 100644 --- a/site/e2e/tests/groups/createGroup.spec.ts +++ b/site/e2e/tests/groups/createGroup.spec.ts @@ -1,12 +1,11 @@ import { expect, test } from "@playwright/test"; -import { defaultOrganizationName } from "../../constants"; -import { randomName, requiresLicense } from "../../helpers"; -import { login } from "../../helpers"; +import { defaultOrganizationName, users } from "../../constants"; +import { login, randomName, requiresLicense } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.userAdmin); }); test("create group", async ({ page, baseURL }) => { diff --git a/site/e2e/tests/groups/removeGroup.spec.ts b/site/e2e/tests/groups/removeGroup.spec.ts index 736b86f7d386d..7caec10d6034c 100644 --- a/site/e2e/tests/groups/removeGroup.spec.ts +++ b/site/e2e/tests/groups/removeGroup.spec.ts @@ -1,13 +1,12 @@ import { expect, test } from "@playwright/test"; import { createGroup, getCurrentOrgId, setupApiCalls } from "../../api"; -import { defaultOrganizationName } from "../../constants"; -import { requiresLicense } from "../../helpers"; -import { login } from "../../helpers"; +import { defaultOrganizationName, users } from "../../constants"; +import { login, requiresLicense } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.userAdmin); await setupApiCalls(page); }); diff --git a/site/e2e/tests/groups/removeMember.spec.ts b/site/e2e/tests/groups/removeMember.spec.ts index 81fb5ee4f4117..856ece95c0b02 100644 --- a/site/e2e/tests/groups/removeMember.spec.ts +++ b/site/e2e/tests/groups/removeMember.spec.ts @@ -6,14 +6,13 @@ import { getCurrentOrgId, setupApiCalls, } from "../../api"; -import { defaultOrganizationName } from "../../constants"; -import { requiresLicense } from "../../helpers"; -import { login } from "../../helpers"; +import { defaultOrganizationName, users } from "../../constants"; +import { login, requiresLicense } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.userAdmin); await setupApiCalls(page); }); diff --git a/site/e2e/tests/organizationGroups.spec.ts b/site/e2e/tests/organizationGroups.spec.ts index 08768d4bbae11..9b3ea986aa580 100644 --- a/site/e2e/tests/organizationGroups.spec.ts +++ b/site/e2e/tests/organizationGroups.spec.ts @@ -34,9 +34,7 @@ test("create group", async ({ page }) => { // Create a new organization const org = await createOrganization(); const orgUserAdmin = await createOrganizationMember({ - orgRoles: { - [org.id]: ["organization-user-admin"], - }, + [org.id]: ["organization-user-admin"], }); await login(page, orgUserAdmin); @@ -101,9 +99,7 @@ test("change quota settings", async ({ page }) => { const org = await createOrganization(); const group = await createGroup(org.id); const orgUserAdmin = await createOrganizationMember({ - orgRoles: { - [org.id]: ["organization-user-admin"], - }, + [org.id]: ["organization-user-admin"], }); // Go to settings diff --git a/site/e2e/tests/organizations/auditLogs.spec.ts b/site/e2e/tests/organizations/auditLogs.spec.ts deleted file mode 100644 index 3044d9da2d7ca..0000000000000 --- a/site/e2e/tests/organizations/auditLogs.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { type Page, expect, test } from "@playwright/test"; -import { - createOrganization, - createOrganizationMember, - setupApiCalls, -} from "../../api"; -import { defaultPassword, users } from "../../constants"; -import { login, randomName, requiresLicense } from "../../helpers"; -import { beforeCoderTest } from "../../hooks"; - -test.describe.configure({ mode: "parallel" }); - -const orgName = randomName(); - -const orgAuditor = { - username: `org-auditor-${orgName}`, - password: defaultPassword, - email: `org-auditor-${orgName}@coder.com`, -}; - -test.beforeEach(({ page }) => { - beforeCoderTest(page); -}); - -test.describe("organization scoped audit logs", () => { - requiresLicense(); - - test.beforeAll(async ({ browser }) => { - const context = await browser.newContext(); - const page = await context.newPage(); - - await login(page); - await setupApiCalls(page); - - const org = await createOrganization(orgName); - await createOrganizationMember({ - ...orgAuditor, - orgRoles: { - [org.id]: ["organization-auditor"], - }, - }); - - await context.close(); - }); - - test("organization auditors cannot see logins", async ({ page }) => { - // Go to the audit history - await login(page, orgAuditor); - await page.goto("/audit"); - const username = orgAuditor.username; - - const loginMessage = `${username} logged in`; - // Make sure those things we did all actually show up - await expect(page.getByText(loginMessage).first()).not.toBeVisible(); - }); - - test("creating organization is logged", async ({ page }) => { - await login(page, orgAuditor); - - // Go to the audit history - await page.goto("/audit", { waitUntil: "domcontentloaded" }); - - const auditLogText = `${users.owner.username} created organization ${orgName}`; - const org = page.locator(".MuiTableRow-root", { - hasText: auditLogText, - }); - await org.scrollIntoViewIfNeeded(); - await expect(org).toBeVisible(); - - await org.getByLabel("open-dropdown").click(); - await expect(org.getByText(`icon: "/emojis/1f957.png"`)).toBeVisible(); - }); - - test("assigning an organization role is logged", async ({ page }) => { - await login(page, orgAuditor); - - // Go to the audit history - await page.goto("/audit", { waitUntil: "domcontentloaded" }); - - const auditLogText = `${users.owner.username} updated organization member ${orgAuditor.username}`; - const member = page.locator(".MuiTableRow-root", { - hasText: auditLogText, - }); - await member.scrollIntoViewIfNeeded(); - await expect(member).toBeVisible(); - - await member.getByLabel("open-dropdown").click(); - await expect( - member.getByText(`roles: ["organization-auditor"]`), - ).toBeVisible(); - }); -}); diff --git a/site/e2e/tests/roles.spec.ts b/site/e2e/tests/roles.spec.ts index e6b92bd944ba0..484e6294de7a1 100644 --- a/site/e2e/tests/roles.spec.ts +++ b/site/e2e/tests/roles.spec.ts @@ -106,9 +106,7 @@ test.describe("org-scoped roles admin settings access", () => { test("org template admin can see admin settings", async ({ page }) => { const org = await createOrganization(); const orgTemplateAdmin = await createOrganizationMember({ - orgRoles: { - [org.id]: ["organization-template-admin"], - }, + [org.id]: ["organization-template-admin"], }); await login(page, orgTemplateAdmin); @@ -120,9 +118,7 @@ test.describe("org-scoped roles admin settings access", () => { test("org user admin can see admin settings", async ({ page }) => { const org = await createOrganization(); const orgUserAdmin = await createOrganizationMember({ - orgRoles: { - [org.id]: ["organization-user-admin"], - }, + [org.id]: ["organization-user-admin"], }); await login(page, orgUserAdmin); @@ -134,9 +130,7 @@ test.describe("org-scoped roles admin settings access", () => { test("org auditor can see admin settings", async ({ page }) => { const org = await createOrganization(); const orgAuditor = await createOrganizationMember({ - orgRoles: { - [org.id]: ["organization-auditor"], - }, + [org.id]: ["organization-auditor"], }); await login(page, orgAuditor); @@ -148,9 +142,7 @@ test.describe("org-scoped roles admin settings access", () => { test("org admin can see admin settings", async ({ page }) => { const org = await createOrganization(); const orgAdmin = await createOrganizationMember({ - orgRoles: { - [org.id]: ["organization-admin"], - }, + [org.id]: ["organization-admin"], }); await login(page, orgAdmin); diff --git a/site/e2e/tests/templates/listTemplates.spec.ts b/site/e2e/tests/templates/listTemplates.spec.ts index 6defbe10f40dd..d844925644881 100644 --- a/site/e2e/tests/templates/listTemplates.spec.ts +++ b/site/e2e/tests/templates/listTemplates.spec.ts @@ -1,10 +1,11 @@ import { expect, test } from "@playwright/test"; +import { users } from "../../constants"; import { login } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.templateAdmin); }); test("list templates", async ({ page, baseURL }) => { diff --git a/site/e2e/tests/templates/updateTemplateSchedule.spec.ts b/site/e2e/tests/templates/updateTemplateSchedule.spec.ts index 8c1f6a87dc2fe..42c758df5db16 100644 --- a/site/e2e/tests/templates/updateTemplateSchedule.spec.ts +++ b/site/e2e/tests/templates/updateTemplateSchedule.spec.ts @@ -1,12 +1,13 @@ import { expect, test } from "@playwright/test"; import { API } from "api/api"; import { getCurrentOrgId, setupApiCalls } from "../../api"; +import { users } from "../../constants"; import { login } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.templateAdmin); await setupApiCalls(page); }); diff --git a/site/e2e/tests/updateTemplate.spec.ts b/site/e2e/tests/updateTemplate.spec.ts index 33e85e40e3b6d..e0bfac03cf036 100644 --- a/site/e2e/tests/updateTemplate.spec.ts +++ b/site/e2e/tests/updateTemplate.spec.ts @@ -1,20 +1,20 @@ import { expect, test } from "@playwright/test"; -import { defaultOrganizationName } from "../constants"; +import { defaultOrganizationName, users } from "../constants"; import { expectUrl } from "../expectUrl"; import { createGroup, createTemplate, + login, requiresLicense, updateTemplateSettings, } from "../helpers"; -import { login } from "../helpers"; import { beforeCoderTest } from "../hooks"; test.describe.configure({ mode: "parallel" }); test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.templateAdmin); }); test("template update with new name redirects on successful submit", async ({ @@ -29,10 +29,13 @@ test("template update with new name redirects on successful submit", async ({ test("add and remove a group", async ({ page }) => { requiresLicense(); + await login(page, users.userAdmin); const orgName = defaultOrganizationName; - const templateName = await createTemplate(page); const groupName = await createGroup(page, orgName); + await login(page, users.templateAdmin); + const templateName = await createTemplate(page); + await page.goto( `/templates/${orgName}/${templateName}/settings/permissions`, { waitUntil: "domcontentloaded" }, From b74acdd8fe9a1a76cd7da1af5cf6db5936027083 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 17 Mar 2025 22:11:49 +0000 Subject: [PATCH 4/8] use user admin for IdP sync tests as well --- site/e2e/tests/auditLogs.spec.ts | 2 +- site/e2e/tests/deployment/idpOrgSync.spec.ts | 22 +++++++------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/site/e2e/tests/auditLogs.spec.ts b/site/e2e/tests/auditLogs.spec.ts index c25a828eedb64..56a27f94ad3c2 100644 --- a/site/e2e/tests/auditLogs.spec.ts +++ b/site/e2e/tests/auditLogs.spec.ts @@ -1,4 +1,4 @@ -import { type Page, expect, test } from "@playwright/test"; +import { expect, type Page, test } from "@playwright/test"; import { defaultPassword, users } from "../constants"; import { createTemplate, diff --git a/site/e2e/tests/deployment/idpOrgSync.spec.ts b/site/e2e/tests/deployment/idpOrgSync.spec.ts index d77ddb1593fd3..20a1f843c2af4 100644 --- a/site/e2e/tests/deployment/idpOrgSync.spec.ts +++ b/site/e2e/tests/deployment/idpOrgSync.spec.ts @@ -5,23 +5,24 @@ import { deleteOrganization, setupApiCalls, } from "../../api"; -import { randomName, requiresLicense } from "../../helpers"; -import { login } from "../../helpers"; +import { users } from "../../constants"; +import { login, randomName, requiresLicense } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page); + await login(page, users.userAdmin); await setupApiCalls(page); }); -test.describe("IdpOrgSyncPage", () => { +test.describe("IdP organization sync", () => { + requiresLicense(); + test.describe.configure({ retries: 1 }); test("show empty table when no org mappings are present", async ({ page, }) => { - requiresLicense(); await page.goto("/deployment/idp-org-sync", { waitUntil: "domcontentloaded", }); @@ -35,8 +36,6 @@ test.describe("IdpOrgSyncPage", () => { }); test("add new IdP organization mapping with API", async ({ page }) => { - requiresLicense(); - await createOrganizationSyncSettings(); await page.goto("/deployment/idp-org-sync", { @@ -59,7 +58,6 @@ test.describe("IdpOrgSyncPage", () => { }); test("delete a IdP org to coder org mapping row", async ({ page }) => { - requiresLicense(); await createOrganizationSyncSettings(); await page.goto("/deployment/idp-org-sync", { waitUntil: "domcontentloaded", @@ -77,7 +75,6 @@ test.describe("IdpOrgSyncPage", () => { }); test("update sync field", async ({ page }) => { - requiresLicense(); await page.goto("/deployment/idp-org-sync", { waitUntil: "domcontentloaded", }); @@ -100,7 +97,6 @@ test.describe("IdpOrgSyncPage", () => { }); test("toggle off default organization assignment", async ({ page }) => { - requiresLicense(); await page.goto("/deployment/idp-org-sync", { waitUntil: "domcontentloaded", }); @@ -126,8 +122,6 @@ test.describe("IdpOrgSyncPage", () => { test("export policy button is enabled when sync settings are present", async ({ page, }) => { - requiresLicense(); - await page.goto("/deployment/idp-org-sync", { waitUntil: "domcontentloaded", }); @@ -140,8 +134,6 @@ test.describe("IdpOrgSyncPage", () => { }); test("add new IdP organization mapping with UI", async ({ page }) => { - requiresLicense(); - const orgName = randomName(); await createOrganizationWithName(orgName); @@ -172,7 +164,7 @@ test.describe("IdpOrgSyncPage", () => { await orgSelector.click(); await page.waitForTimeout(1000); - const option = await page.getByRole("option", { name: orgName }); + const option = page.getByRole("option", { name: orgName }); await expect(option).toBeAttached({ timeout: 30000 }); await expect(option).toBeVisible(); await option.click(); From 10c1722d3c4713ee27af27321e3b403222596662 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 17 Mar 2025 22:26:34 +0000 Subject: [PATCH 5/8] jk --- site/e2e/tests/deployment/idpOrgSync.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/site/e2e/tests/deployment/idpOrgSync.spec.ts b/site/e2e/tests/deployment/idpOrgSync.spec.ts index 20a1f843c2af4..a693e70007d4d 100644 --- a/site/e2e/tests/deployment/idpOrgSync.spec.ts +++ b/site/e2e/tests/deployment/idpOrgSync.spec.ts @@ -11,7 +11,7 @@ import { beforeCoderTest } from "../../hooks"; test.beforeEach(async ({ page }) => { beforeCoderTest(page); - await login(page, users.userAdmin); + await login(page); await setupApiCalls(page); }); @@ -135,7 +135,6 @@ test.describe("IdP organization sync", () => { test("add new IdP organization mapping with UI", async ({ page }) => { const orgName = randomName(); - await createOrganizationWithName(orgName); await page.goto("/deployment/idp-org-sync", { From 08286a107d2b379549207582a891b95709aa2214 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 17 Mar 2025 22:34:06 +0000 Subject: [PATCH 6/8] :| --- site/e2e/tests/auditLogs.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/e2e/tests/auditLogs.spec.ts b/site/e2e/tests/auditLogs.spec.ts index 56a27f94ad3c2..c25a828eedb64 100644 --- a/site/e2e/tests/auditLogs.spec.ts +++ b/site/e2e/tests/auditLogs.spec.ts @@ -1,4 +1,4 @@ -import { expect, type Page, test } from "@playwright/test"; +import { type Page, expect, test } from "@playwright/test"; import { defaultPassword, users } from "../constants"; import { createTemplate, From 1702c62341a3ba26a17b34d683fd64087480b911 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 17 Mar 2025 22:38:52 +0000 Subject: [PATCH 7/8] oh --- site/e2e/api.ts | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 5e3fd2de06802..0dc9e46831708 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -38,25 +38,15 @@ export const createUser = async (...orgIds: string[]) => { return user; }; -type CreateOrganizationMemberOptions = { - username?: string; - email?: string; - password?: string; - orgRoles: Record; -}; - -export const createOrganizationMember = async ({ - username = randomName(), - email = `${username}@coder.com`, - password = defaultPassword, - orgRoles, -}: CreateOrganizationMemberOptions): Promise => { +export const createOrganizationMember = async ( + orgRoles: Record, +): Promise => { const name = randomName(); const user = await API.createUser({ - email, - username, - name: username, - password, + email: `${name}@coder.com`, + username: name, + name: name, + password: defaultPassword, login_type: "password", organization_ids: Object.keys(orgRoles), user_status: null, @@ -69,7 +59,7 @@ export const createOrganizationMember = async ({ return { username: user.username, email: user.email, - password, + password: defaultPassword, }; }; @@ -84,7 +74,8 @@ export const createGroup = async (orgId: string) => { return group; }; -export const createOrganization = async (name = randomName()) => { +export const createOrganization = async () => { + const name = randomName(); const org = await API.createOrganization({ name, display_name: `Org ${name}`, From 81535bc4ae17e150c6254f7b4b40cc32e0443206 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 17 Mar 2025 23:09:35 +0000 Subject: [PATCH 8/8] :) --- site/e2e/helpers.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index 3ab726f245c54..e99de6e97e1bc 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -267,9 +267,8 @@ export const createTemplate = async ( ); } - // picker is disabled if only one org is available + // The organization picker will be disabled if there is only one option. const pickerIsDisabled = await orgPicker.isDisabled(); - if (!pickerIsDisabled) { await orgPicker.click(); await page.getByText(orgName, { exact: true }).click(); @@ -1094,8 +1093,12 @@ export async function createUser( const orgPicker = page.getByLabel("Organization *"); const organizationsEnabled = await orgPicker.isVisible(); if (organizationsEnabled) { - await orgPicker.click(); - await page.getByText(orgName, { exact: true }).click(); + // The organization picker will be disabled if there is only one option. + const pickerIsDisabled = await orgPicker.isDisabled(); + if (!pickerIsDisabled) { + await orgPicker.click(); + await page.getByText(orgName, { exact: true }).click(); + } } await page.getByLabel("Login Type").click();