From c2268e40bc6553c313192f79f662ac0c9bfd1f51 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 6 Dec 2024 19:34:07 +0000 Subject: [PATCH 01/10] feat: wip --- site/e2e/api.ts | 33 +++++ .../customRoles/customRoles.spec.ts | 127 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 site/e2e/tests/organizations/customRoles/customRoles.spec.ts diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 0494467799a97..8daf4e254065f 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -90,6 +90,39 @@ export const createOrganizationSyncSettings = async () => { organization_assign_default: true, }); return settings; +} + +export const createCustomRole = async (orgId: string, name: string, displayName: string) => { + const role = await API.createOrganizationRole(orgId, { + name, + display_name: displayName, + organization_id: orgId, + site_permissions: [], + organization_permissions: [ + { + negate: false, + resource_type: "organization_member", + action: "create", + }, + { + negate: false, + resource_type: "organization_member", + action: "delete", + }, + { + negate: false, + resource_type: "organization_member", + action: "read", + }, + { + negate: false, + resource_type: "organization_member", + action: "update", + }, + ], + user_permissions: [], + }); + return role; }; export async function verifyConfigFlagBoolean( diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts new file mode 100644 index 0000000000000..ba33620ef9076 --- /dev/null +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -0,0 +1,127 @@ +import { test, expect } from "@playwright/test"; +import { MockOrganization } from "testHelpers/entities"; +import { + createOrganization, + createCustomRole, + getCurrentOrgId, + setupApiCalls, +} from "../../../api"; +import { requiresLicense } from "../../../helpers"; +import { beforeCoderTest } from "../../../hooks"; + +test.describe("CustomRolesPage", () => { + + test.beforeEach(async ({ page }) => await beforeCoderTest(page)); + + test("create custom role", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); + + const org = await createOrganization(); + const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); + + await page.goto(`/organizations/${org.name}/roles`); + const roleRow = page.getByTestId(`role-${customRole.name}`); + await expect(roleRow.getByText(customRole.display_name)).toBeVisible(); + await expect(roleRow.getByText("organization_member")).toBeVisible(); + + }); + + test("displays built-in role without edit/delete options", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); + + const org = await createOrganization(); + await page.goto(`/organizations/${org.name}/roles`); + + const roleRow = page.getByTestId("role-organization-admin"); + await expect(roleRow).toBeVisible(); + + await expect(roleRow.getByText("Organization Admin")).toBeVisible(); + + // Verify that the more menu (three dots) is not present for built-in roles + await expect(roleRow.getByRole("button", { name: "More options" })).not.toBeVisible(); + }); + + test("can navigate to create custom role", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); + + const org = await createOrganization(); + await page.goto(`/organizations/${org.name}/roles`); + + await page.getByRole("link", { name: "Create custom role" }).first().click(); + await expect(page).toHaveURL(`/organizations/${org.name}/roles/create`); + }); + +// test("delete custom role", async ({ page }) => { +// requiresLicense(); +// await setupApiCalls(page); + +// const org = await createOrganization(); +// const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); +// await page.goto(`/organizations/${org.name}/roles`); + +// // const roleRow = page.getByTestId("role-custom-role-test-1"); +// await page.getByRole("button", { name: "More options" }).click(); + +// // Check menu items +// await expect(page.getByRole("menuitem", { name: "Edit" })).toBeVisible(); +// await expect(page.getByText("Edit")).toBeVisible(); +// // const menu = page.getByRole("menu"); + +// const deleteButton = page.getByRole("menuitem", { name: "Delete…" }); +// await expect(deleteButton).toBeVisible(); +// await deleteButton.click(); + +// const input = page.getByRole("textbox"); +// await input.fill(customRole.name); +// await page.getByRole("button", { name: "Delete" }).click(); + +// await expect(page.getByText("Custom role deleted successfully!")).toBeVisible(); +// }); + +// test("shows delete confirmation dialog", async ({ page }) => { +// // Click delete option +// const roleRow = page.getByTestId("role-custom-role-test-1"); +// await roleRow.getByRole("button", { name: "More options" }).click(); +// await page.getByRole("menuitem", { name: "Delete…" }).click(); + +// // Check dialog content +// await expect(page.getByRole("dialog")).toBeVisible(); +// await expect(page.getByText(/Are you sure you want to delete/)).toBeVisible(); +// await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible(); +// await expect(page.getByRole("button", { name: "Delete" })).toBeVisible(); +// }); + +// test("handles delete role successfully", async ({ page }) => { +// // Mock delete API call +// await page.route("**/api/v2/organizations/*/roles/*", (route) => +// route.fulfill(createMockApiResponse({})) +// ); + +// // Perform delete +// const roleRow = page.getByTestId("role-custom-role-test-1"); +// await roleRow.getByRole("button", { name: "More options" }).click(); +// await page.getByRole("menuitem", { name: "Delete…" }).click(); +// await page.getByRole("button", { name: "Delete" }).click(); + +// // Check success message +// await expect(page.getByText("Custom role deleted successfully!")).toBeVisible(); +// }); + +// test("shows paywall when custom roles not enabled", async ({ page }) => { +// // Mock feature flags to disable custom roles +// await page.route("**/api/v2/features", (route) => +// route.fulfill(createMockApiResponse({ +// custom_roles: false +// })) +// ); + +// await page.reload(); + +// // Check paywall content +// await expect(page.getByText("Upgrade to a premium license to create a custom role")).toBeVisible(); +// await expect(page.getByRole("link", { name: "Create custom role" })).not.toBeVisible(); +// }); +}); From ab4263019d5a94f6ff196bc64cf434eebdb52c5a Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Tue, 10 Dec 2024 15:19:39 +0000 Subject: [PATCH 02/10] feat: add custom roles e2e tests --- .../customRoles/customRoles.spec.ts | 152 +++++++++++------- 1 file changed, 98 insertions(+), 54 deletions(-) diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index ba33620ef9076..6d69755e12e75 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -1,10 +1,9 @@ import { test, expect } from "@playwright/test"; -import { MockOrganization } from "testHelpers/entities"; import { - createOrganization, createCustomRole, - getCurrentOrgId, + createOrganizationWithName, setupApiCalls, + deleteOrganization, } from "../../../api"; import { requiresLicense } from "../../../helpers"; import { beforeCoderTest } from "../../../hooks"; @@ -13,11 +12,40 @@ test.describe("CustomRolesPage", () => { test.beforeEach(async ({ page }) => await beforeCoderTest(page)); - test("create custom role", async ({ page }) => { + test("create custom role and cancel edit changes", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganization(); + const org = await createOrganizationWithName("developers"); + + const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); + + await page.goto(`/organizations/${org.name}/roles`); + const roleRow = page.getByTestId(`role-${customRole.name}`); + await expect(roleRow.getByText(customRole.display_name)).toBeVisible(); + await expect(roleRow.getByText("organization_member")).toBeVisible(); + + await roleRow.getByRole("button", { name: "More options" }).click() + const menu = page.locator("#more-options"); + await menu.getByText("Edit").click(); + + await expect(page).toHaveURL(`/organizations/${org.name}/roles/${customRole.name}`); + + const cancelButton = page.getByRole("button", { name: "Cancel" }).first(); + await expect(cancelButton).toBeVisible(); + await cancelButton.click(); + + await expect(page).toHaveURL(`/organizations/${org.name}/roles`); + + await deleteOrganization("developers"); + }); + + test("create custom role, edit role and save changes", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); + + const org = await createOrganizationWithName("developers"); + const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); await page.goto(`/organizations/${org.name}/roles`); @@ -25,13 +53,40 @@ test.describe("CustomRolesPage", () => { await expect(roleRow.getByText(customRole.display_name)).toBeVisible(); await expect(roleRow.getByText("organization_member")).toBeVisible(); + await page.goto(`/organizations/${org.name}/roles/${customRole.name}`); + + const displayNameInput = page.getByRole("textbox", { name: "Display name" }); + await displayNameInput.fill("Custom Role Test 2 Edited"); + + const groupCheckbox = page.getByTestId("group").getByRole("checkbox"); + await expect(groupCheckbox).toBeVisible(); + await groupCheckbox.click(); + + const organizationMemberCheckbox = page.getByTestId("organization_member").getByRole("checkbox"); + await expect(organizationMemberCheckbox).toBeVisible(); + await organizationMemberCheckbox.click(); + + const saveButton = page.getByRole("button", { name: "Save" }).first(); + await expect(saveButton).toBeVisible(); + await saveButton.click(); + + await expect(roleRow.getByText("Custom Role Test 2 Edited")).toBeVisible(); + + const roleRow2 = page.getByTestId(`role-${customRole.name}`); + await expect(roleRow2.getByText("organization_member")).not.toBeVisible(); + await expect(roleRow2.getByText("group")).toBeVisible(); + + await expect(page).toHaveURL(`/organizations/${org.name}/roles`); + + await deleteOrganization("developers"); }); test("displays built-in role without edit/delete options", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganization(); + const org = await createOrganizationWithName("developers"); + await page.goto(`/organizations/${org.name}/roles`); const roleRow = page.getByTestId("role-organization-admin"); @@ -41,74 +96,63 @@ test.describe("CustomRolesPage", () => { // Verify that the more menu (three dots) is not present for built-in roles await expect(roleRow.getByRole("button", { name: "More options" })).not.toBeVisible(); + + await deleteOrganization("developers"); }); - test("can navigate to create custom role", async ({ page }) => { + test("create custom role with UI", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganization(); + const org = await createOrganizationWithName("developers"); + await page.goto(`/organizations/${org.name}/roles`); await page.getByRole("link", { name: "Create custom role" }).first().click(); + await expect(page).toHaveURL(`/organizations/${org.name}/roles/create`); - }); -// test("delete custom role", async ({ page }) => { -// requiresLicense(); -// await setupApiCalls(page); + const customRoleName = "custom-role-test"; + const roleNameInput = page.getByRole("textbox", { exact: true, name: "Name" }); + await roleNameInput.fill(customRoleName); -// const org = await createOrganization(); -// const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); -// await page.goto(`/organizations/${org.name}/roles`); + const customRoleDisplayName = "Custom Role Test"; + const displayNameInput = page.getByRole("textbox", { exact: true, name: "Display Name" }); + await displayNameInput.fill(customRoleDisplayName); -// // const roleRow = page.getByTestId("role-custom-role-test-1"); -// await page.getByRole("button", { name: "More options" }).click(); + await page.getByRole("button", { name: "Create Role" }).first().click(); -// // Check menu items -// await expect(page.getByRole("menuitem", { name: "Edit" })).toBeVisible(); -// await expect(page.getByText("Edit")).toBeVisible(); -// // const menu = page.getByRole("menu"); + await expect(page).toHaveURL(`/organizations/${org.name}/roles`); -// const deleteButton = page.getByRole("menuitem", { name: "Delete…" }); -// await expect(deleteButton).toBeVisible(); -// await deleteButton.click(); + const roleRow = page.getByTestId(`role-${customRoleName}`); + await expect(roleRow.getByText(customRoleDisplayName)).toBeVisible(); + await expect(roleRow.getByText("None")).toBeVisible(); -// const input = page.getByRole("textbox"); -// await input.fill(customRole.name); -// await page.getByRole("button", { name: "Delete" }).click(); + await deleteOrganization("developers"); + }); -// await expect(page.getByText("Custom role deleted successfully!")).toBeVisible(); -// }); + test("delete custom role", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); -// test("shows delete confirmation dialog", async ({ page }) => { -// // Click delete option -// const roleRow = page.getByTestId("role-custom-role-test-1"); -// await roleRow.getByRole("button", { name: "More options" }).click(); -// await page.getByRole("menuitem", { name: "Delete…" }).click(); - -// // Check dialog content -// await expect(page.getByRole("dialog")).toBeVisible(); -// await expect(page.getByText(/Are you sure you want to delete/)).toBeVisible(); -// await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible(); -// await expect(page.getByRole("button", { name: "Delete" })).toBeVisible(); -// }); + const org = await createOrganizationWithName("developers"); + const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); + await page.goto(`/organizations/${org.name}/roles`); -// test("handles delete role successfully", async ({ page }) => { -// // Mock delete API call -// await page.route("**/api/v2/organizations/*/roles/*", (route) => -// route.fulfill(createMockApiResponse({})) -// ); + const roleRow = page.getByTestId(`role-${customRole.name}`); + await roleRow.getByRole("button", { name: "More options" }).click() -// // Perform delete -// const roleRow = page.getByTestId("role-custom-role-test-1"); -// await roleRow.getByRole("button", { name: "More options" }).click(); -// await page.getByRole("menuitem", { name: "Delete…" }).click(); -// await page.getByRole("button", { name: "Delete" }).click(); + const menu = page.locator("#more-options"); + await menu.getByText("Delete…").click(); -// // Check success message -// await expect(page.getByText("Custom role deleted successfully!")).toBeVisible(); -// }); + const input = page.getByRole("textbox"); + await input.fill(customRole.name); + await page.getByRole("button", { name: "Delete" }).click(); + + await expect(page.getByText("Custom role deleted successfully!")).toBeVisible(); + + await deleteOrganization("developers"); + }); // test("shows paywall when custom roles not enabled", async ({ page }) => { // // Mock feature flags to disable custom roles From 1ef60fc2df6561f4857992f9873da047ae80297e Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Tue, 10 Dec 2024 15:23:28 +0000 Subject: [PATCH 03/10] fix: use different org name for test --- site/e2e/tests/deployment/idpOrgSync.spec.ts | 8 ++++---- .../customRoles/customRoles.spec.ts | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/site/e2e/tests/deployment/idpOrgSync.spec.ts b/site/e2e/tests/deployment/idpOrgSync.spec.ts index 59fccb440400b..5c49459db1195 100644 --- a/site/e2e/tests/deployment/idpOrgSync.spec.ts +++ b/site/e2e/tests/deployment/idpOrgSync.spec.ts @@ -117,7 +117,7 @@ test.describe("IdpOrgSyncPage", () => { requiresLicense(); await setupApiCalls(page); - await createOrganizationWithName("developers"); + await createOrganizationWithName("admins"); await page.goto("/deployment/idp-org-sync", { waitUntil: "domcontentloaded", @@ -135,7 +135,7 @@ test.describe("IdpOrgSyncPage", () => { // Select Coder organization from combobox await orgSelector.click(); - await page.getByRole("option", { name: "developers" }).click(); + await page.getByRole("option", { name: "admins" }).click(); // Add button should now be enabled await expect(addButton).toBeEnabled(); @@ -146,12 +146,12 @@ test.describe("IdpOrgSyncPage", () => { 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(newRow.getByText("admins")).toBeVisible(); await expect( page.getByText("Organization sync settings updated."), ).toBeVisible(); - await deleteOrganization("developers"); + await deleteOrganization("admins"); }); }); diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index 6d69755e12e75..f07368fc48206 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -44,7 +44,7 @@ test.describe("CustomRolesPage", () => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("developers"); + const org = await createOrganizationWithName("users"); const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); @@ -78,14 +78,14 @@ test.describe("CustomRolesPage", () => { await expect(page).toHaveURL(`/organizations/${org.name}/roles`); - await deleteOrganization("developers"); + await deleteOrganization("users"); }); test("displays built-in role without edit/delete options", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("developers"); + const org = await createOrganizationWithName("testers"); await page.goto(`/organizations/${org.name}/roles`); @@ -97,14 +97,14 @@ test.describe("CustomRolesPage", () => { // Verify that the more menu (three dots) is not present for built-in roles await expect(roleRow.getByRole("button", { name: "More options" })).not.toBeVisible(); - await deleteOrganization("developers"); + await deleteOrganization("testers"); }); test("create custom role with UI", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("developers"); + const org = await createOrganizationWithName("contractors"); await page.goto(`/organizations/${org.name}/roles`); @@ -128,14 +128,14 @@ test.describe("CustomRolesPage", () => { await expect(roleRow.getByText(customRoleDisplayName)).toBeVisible(); await expect(roleRow.getByText("None")).toBeVisible(); - await deleteOrganization("developers"); + await deleteOrganization("contractors"); }); test("delete custom role", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("developers"); + const org = await createOrganizationWithName("custom1"); const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); await page.goto(`/organizations/${org.name}/roles`); @@ -151,7 +151,7 @@ test.describe("CustomRolesPage", () => { await expect(page.getByText("Custom role deleted successfully!")).toBeVisible(); - await deleteOrganization("developers"); + await deleteOrganization("custom1"); }); // test("shows paywall when custom roles not enabled", async ({ page }) => { From 1c163578296a5a2d6f880d25f310955e8ac71dac Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Tue, 10 Dec 2024 15:32:05 +0000 Subject: [PATCH 04/10] fix: format --- site/e2e/api.ts | 8 +- .../customRoles/customRoles.spec.ts | 254 ++++++++++-------- 2 files changed, 149 insertions(+), 113 deletions(-) diff --git a/site/e2e/api.ts b/site/e2e/api.ts index 8daf4e254065f..2b4e54429c017 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -90,9 +90,13 @@ export const createOrganizationSyncSettings = async () => { organization_assign_default: true, }); return settings; -} +}; -export const createCustomRole = async (orgId: string, name: string, displayName: string) => { +export const createCustomRole = async ( + orgId: string, + name: string, + displayName: string, +) => { const role = await API.createOrganizationRole(orgId, { name, display_name: displayName, diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index f07368fc48206..c99a2f6730a19 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -1,171 +1,203 @@ -import { test, expect } from "@playwright/test"; +import { expect, test } from "@playwright/test"; import { createCustomRole, createOrganizationWithName, - setupApiCalls, deleteOrganization, + setupApiCalls, } from "../../../api"; import { requiresLicense } from "../../../helpers"; import { beforeCoderTest } from "../../../hooks"; test.describe("CustomRolesPage", () => { + test.beforeEach(async ({ page }) => await beforeCoderTest(page)); - test.beforeEach(async ({ page }) => await beforeCoderTest(page)); - - test("create custom role and cancel edit changes", async ({ page }) => { - requiresLicense(); - await setupApiCalls(page); + test("create custom role and cancel edit changes", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); - const org = await createOrganizationWithName("developers"); + const org = await createOrganizationWithName("developers"); - const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); + const customRole = await createCustomRole( + org.id, + "custom-role-test-1", + "Custom Role Test 1", + ); - await page.goto(`/organizations/${org.name}/roles`); - const roleRow = page.getByTestId(`role-${customRole.name}`); - await expect(roleRow.getByText(customRole.display_name)).toBeVisible(); - await expect(roleRow.getByText("organization_member")).toBeVisible(); + await page.goto(`/organizations/${org.name}/roles`); + const roleRow = page.getByTestId(`role-${customRole.name}`); + await expect(roleRow.getByText(customRole.display_name)).toBeVisible(); + await expect(roleRow.getByText("organization_member")).toBeVisible(); - await roleRow.getByRole("button", { name: "More options" }).click() - const menu = page.locator("#more-options"); - await menu.getByText("Edit").click(); + await roleRow.getByRole("button", { name: "More options" }).click(); + const menu = page.locator("#more-options"); + await menu.getByText("Edit").click(); - await expect(page).toHaveURL(`/organizations/${org.name}/roles/${customRole.name}`); + await expect(page).toHaveURL( + `/organizations/${org.name}/roles/${customRole.name}`, + ); - const cancelButton = page.getByRole("button", { name: "Cancel" }).first(); - await expect(cancelButton).toBeVisible(); - await cancelButton.click(); + const cancelButton = page.getByRole("button", { name: "Cancel" }).first(); + await expect(cancelButton).toBeVisible(); + await cancelButton.click(); - await expect(page).toHaveURL(`/organizations/${org.name}/roles`); + await expect(page).toHaveURL(`/organizations/${org.name}/roles`); - await deleteOrganization("developers"); - }); + await deleteOrganization("developers"); + }); - test("create custom role, edit role and save changes", async ({ page }) => { - requiresLicense(); - await setupApiCalls(page); + test("create custom role, edit role and save changes", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); - const org = await createOrganizationWithName("users"); + const org = await createOrganizationWithName("users"); - const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); + const customRole = await createCustomRole( + org.id, + "custom-role-test-1", + "Custom Role Test 1", + ); - await page.goto(`/organizations/${org.name}/roles`); - const roleRow = page.getByTestId(`role-${customRole.name}`); - await expect(roleRow.getByText(customRole.display_name)).toBeVisible(); - await expect(roleRow.getByText("organization_member")).toBeVisible(); + await page.goto(`/organizations/${org.name}/roles`); + const roleRow = page.getByTestId(`role-${customRole.name}`); + await expect(roleRow.getByText(customRole.display_name)).toBeVisible(); + await expect(roleRow.getByText("organization_member")).toBeVisible(); - await page.goto(`/organizations/${org.name}/roles/${customRole.name}`); + await page.goto(`/organizations/${org.name}/roles/${customRole.name}`); - const displayNameInput = page.getByRole("textbox", { name: "Display name" }); - await displayNameInput.fill("Custom Role Test 2 Edited"); + const displayNameInput = page.getByRole("textbox", { + name: "Display name", + }); + await displayNameInput.fill("Custom Role Test 2 Edited"); - const groupCheckbox = page.getByTestId("group").getByRole("checkbox"); - await expect(groupCheckbox).toBeVisible(); - await groupCheckbox.click(); + const groupCheckbox = page.getByTestId("group").getByRole("checkbox"); + await expect(groupCheckbox).toBeVisible(); + await groupCheckbox.click(); - const organizationMemberCheckbox = page.getByTestId("organization_member").getByRole("checkbox"); - await expect(organizationMemberCheckbox).toBeVisible(); - await organizationMemberCheckbox.click(); + const organizationMemberCheckbox = page + .getByTestId("organization_member") + .getByRole("checkbox"); + await expect(organizationMemberCheckbox).toBeVisible(); + await organizationMemberCheckbox.click(); - const saveButton = page.getByRole("button", { name: "Save" }).first(); - await expect(saveButton).toBeVisible(); - await saveButton.click(); + const saveButton = page.getByRole("button", { name: "Save" }).first(); + await expect(saveButton).toBeVisible(); + await saveButton.click(); - await expect(roleRow.getByText("Custom Role Test 2 Edited")).toBeVisible(); + await expect(roleRow.getByText("Custom Role Test 2 Edited")).toBeVisible(); - const roleRow2 = page.getByTestId(`role-${customRole.name}`); - await expect(roleRow2.getByText("organization_member")).not.toBeVisible(); - await expect(roleRow2.getByText("group")).toBeVisible(); + const roleRow2 = page.getByTestId(`role-${customRole.name}`); + await expect(roleRow2.getByText("organization_member")).not.toBeVisible(); + await expect(roleRow2.getByText("group")).toBeVisible(); - await expect(page).toHaveURL(`/organizations/${org.name}/roles`); + await expect(page).toHaveURL(`/organizations/${org.name}/roles`); - await deleteOrganization("users"); - }); + await deleteOrganization("users"); + }); - test("displays built-in role without edit/delete options", async ({ page }) => { - requiresLicense(); - await setupApiCalls(page); + test("displays built-in role without edit/delete options", async ({ + page, + }) => { + requiresLicense(); + await setupApiCalls(page); - const org = await createOrganizationWithName("testers"); + const org = await createOrganizationWithName("testers"); - await page.goto(`/organizations/${org.name}/roles`); + await page.goto(`/organizations/${org.name}/roles`); - const roleRow = page.getByTestId("role-organization-admin"); - await expect(roleRow).toBeVisible(); + const roleRow = page.getByTestId("role-organization-admin"); + await expect(roleRow).toBeVisible(); - await expect(roleRow.getByText("Organization Admin")).toBeVisible(); + await expect(roleRow.getByText("Organization Admin")).toBeVisible(); - // Verify that the more menu (three dots) is not present for built-in roles - await expect(roleRow.getByRole("button", { name: "More options" })).not.toBeVisible(); + // Verify that the more menu (three dots) is not present for built-in roles + await expect( + roleRow.getByRole("button", { name: "More options" }), + ).not.toBeVisible(); - await deleteOrganization("testers"); - }); + await deleteOrganization("testers"); + }); - test("create custom role with UI", async ({ page }) => { - requiresLicense(); - await setupApiCalls(page); + test("create custom role with UI", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); - const org = await createOrganizationWithName("contractors"); + const org = await createOrganizationWithName("contractors"); - await page.goto(`/organizations/${org.name}/roles`); + await page.goto(`/organizations/${org.name}/roles`); - await page.getByRole("link", { name: "Create custom role" }).first().click(); + await page + .getByRole("link", { name: "Create custom role" }) + .first() + .click(); - await expect(page).toHaveURL(`/organizations/${org.name}/roles/create`); + await expect(page).toHaveURL(`/organizations/${org.name}/roles/create`); - const customRoleName = "custom-role-test"; - const roleNameInput = page.getByRole("textbox", { exact: true, name: "Name" }); - await roleNameInput.fill(customRoleName); + const customRoleName = "custom-role-test"; + const roleNameInput = page.getByRole("textbox", { + exact: true, + name: "Name", + }); + await roleNameInput.fill(customRoleName); - const customRoleDisplayName = "Custom Role Test"; - const displayNameInput = page.getByRole("textbox", { exact: true, name: "Display Name" }); - await displayNameInput.fill(customRoleDisplayName); + const customRoleDisplayName = "Custom Role Test"; + const displayNameInput = page.getByRole("textbox", { + exact: true, + name: "Display Name", + }); + await displayNameInput.fill(customRoleDisplayName); - await page.getByRole("button", { name: "Create Role" }).first().click(); + await page.getByRole("button", { name: "Create Role" }).first().click(); - await expect(page).toHaveURL(`/organizations/${org.name}/roles`); + await expect(page).toHaveURL(`/organizations/${org.name}/roles`); - const roleRow = page.getByTestId(`role-${customRoleName}`); - await expect(roleRow.getByText(customRoleDisplayName)).toBeVisible(); - await expect(roleRow.getByText("None")).toBeVisible(); + const roleRow = page.getByTestId(`role-${customRoleName}`); + await expect(roleRow.getByText(customRoleDisplayName)).toBeVisible(); + await expect(roleRow.getByText("None")).toBeVisible(); - await deleteOrganization("contractors"); - }); + await deleteOrganization("contractors"); + }); - test("delete custom role", async ({ page }) => { - requiresLicense(); - await setupApiCalls(page); + test("delete custom role", async ({ page }) => { + requiresLicense(); + await setupApiCalls(page); - const org = await createOrganizationWithName("custom1"); - const customRole = await createCustomRole(org.id, "custom-role-test-1", "Custom Role Test 1"); - await page.goto(`/organizations/${org.name}/roles`); + const org = await createOrganizationWithName("custom1"); + const customRole = await createCustomRole( + org.id, + "custom-role-test-1", + "Custom Role Test 1", + ); + await page.goto(`/organizations/${org.name}/roles`); - const roleRow = page.getByTestId(`role-${customRole.name}`); - await roleRow.getByRole("button", { name: "More options" }).click() + const roleRow = page.getByTestId(`role-${customRole.name}`); + await roleRow.getByRole("button", { name: "More options" }).click(); - const menu = page.locator("#more-options"); - await menu.getByText("Delete…").click(); + const menu = page.locator("#more-options"); + await menu.getByText("Delete…").click(); - const input = page.getByRole("textbox"); - await input.fill(customRole.name); - await page.getByRole("button", { name: "Delete" }).click(); + const input = page.getByRole("textbox"); + await input.fill(customRole.name); + await page.getByRole("button", { name: "Delete" }).click(); - await expect(page.getByText("Custom role deleted successfully!")).toBeVisible(); + await expect( + page.getByText("Custom role deleted successfully!"), + ).toBeVisible(); - await deleteOrganization("custom1"); - }); + await deleteOrganization("custom1"); + }); -// test("shows paywall when custom roles not enabled", async ({ page }) => { -// // Mock feature flags to disable custom roles -// await page.route("**/api/v2/features", (route) => -// route.fulfill(createMockApiResponse({ -// custom_roles: false -// })) -// ); + // test("shows paywall when custom roles not enabled", async ({ page }) => { + // // Mock feature flags to disable custom roles + // await page.route("**/api/v2/features", (route) => + // route.fulfill(createMockApiResponse({ + // custom_roles: false + // })) + // ); -// await page.reload(); + // await page.reload(); -// // Check paywall content -// await expect(page.getByText("Upgrade to a premium license to create a custom role")).toBeVisible(); -// await expect(page.getByRole("link", { name: "Create custom role" })).not.toBeVisible(); -// }); + // // Check paywall content + // await expect(page.getByText("Upgrade to a premium license to create a custom role")).toBeVisible(); + // await expect(page.getByRole("link", { name: "Create custom role" })).not.toBeVisible(); + // }); }); From eae7fae740c659862eb771dba47b1afde806a391 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Tue, 10 Dec 2024 21:05:33 +0000 Subject: [PATCH 05/10] feat: add test for custom roles disabled --- .../customRoles/customRoles.spec.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index c99a2f6730a19..cb986f7cdd75e 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -185,19 +185,12 @@ test.describe("CustomRolesPage", () => { await deleteOrganization("custom1"); }); +}); - // test("shows paywall when custom roles not enabled", async ({ page }) => { - // // Mock feature flags to disable custom roles - // await page.route("**/api/v2/features", (route) => - // route.fulfill(createMockApiResponse({ - // custom_roles: false - // })) - // ); - - // await page.reload(); +test("custom roles disabled", async ({ page }) => { + await page.goto("/organizations/coder/roles"); + await expect(page).toHaveURL("/organizations/coder/roles"); - // // Check paywall content - // await expect(page.getByText("Upgrade to a premium license to create a custom role")).toBeVisible(); - // await expect(page.getByRole("link", { name: "Create custom role" })).not.toBeVisible(); - // }); + await expect(page.getByText("Upgrade to a premium license to create a custom role")).toBeVisible(); + await expect(page.getByRole("link", { name: "Create custom role" })).not.toBeVisible(); }); From 992c579899e3917b53ef5613f387a0df83c579a4 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Tue, 10 Dec 2024 21:58:12 +0000 Subject: [PATCH 06/10] fix: format --- .../tests/organizations/customRoles/customRoles.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index cb986f7cdd75e..9d6b6683fd917 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -191,6 +191,10 @@ test("custom roles disabled", async ({ page }) => { await page.goto("/organizations/coder/roles"); await expect(page).toHaveURL("/organizations/coder/roles"); - await expect(page.getByText("Upgrade to a premium license to create a custom role")).toBeVisible(); - await expect(page.getByRole("link", { name: "Create custom role" })).not.toBeVisible(); + await expect( + page.getByText("Upgrade to a premium license to create a custom role"), + ).toBeVisible(); + await expect( + page.getByRole("link", { name: "Create custom role" }), + ).not.toBeVisible(); }); From 6330959e4aff5b23463cf020b39faf20ee4f603e Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Tue, 10 Dec 2024 23:25:43 +0000 Subject: [PATCH 07/10] fix: run test only if there is no premium license --- site/e2e/helpers.ts | 4 ++++ site/e2e/tests/organizations/customRoles/customRoles.spec.ts | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index 8f69b90900538..b307e0e9b8c2b 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -48,6 +48,10 @@ export function requiresLicense() { test.skip(!license); } +export function noPremiumLicense() { + test.skip(license.length > 0); +} + /** * requireTerraformProvisioner by default is enabled. */ diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index 9d6b6683fd917..508b951c4cef9 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -5,7 +5,7 @@ import { deleteOrganization, setupApiCalls, } from "../../../api"; -import { requiresLicense } from "../../../helpers"; +import { requiresLicense, noPremiumLicense } from "../../../helpers"; import { beforeCoderTest } from "../../../hooks"; test.describe("CustomRolesPage", () => { @@ -188,6 +188,7 @@ test.describe("CustomRolesPage", () => { }); test("custom roles disabled", async ({ page }) => { + noPremiumLicense(); await page.goto("/organizations/coder/roles"); await expect(page).toHaveURL("/organizations/coder/roles"); From 6723ce89059318b26ed7a25cc83b7a0e011fd83d Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Tue, 10 Dec 2024 23:41:39 +0000 Subject: [PATCH 08/10] fix: format --- site/e2e/tests/organizations/customRoles/customRoles.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index 508b951c4cef9..07c873e357cb8 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -5,7 +5,7 @@ import { deleteOrganization, setupApiCalls, } from "../../../api"; -import { requiresLicense, noPremiumLicense } from "../../../helpers"; +import { noPremiumLicense, requiresLicense } from "../../../helpers"; import { beforeCoderTest } from "../../../hooks"; test.describe("CustomRolesPage", () => { From d5ca1b0124907fdbbf01c3fc5a97ac1cb67cdd99 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Thu, 12 Dec 2024 20:05:27 +0000 Subject: [PATCH 09/10] fix: use randomName --- site/e2e/helpers.ts | 2 +- site/e2e/tests/deployment/idpOrgSync.spec.ts | 12 ++++++---- .../customRoles/customRoles.spec.ts | 24 +++++++++---------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index b307e0e9b8c2b..1300f73fb643b 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -48,7 +48,7 @@ export function requiresLicense() { test.skip(!license); } -export function noPremiumLicense() { +export function requiresUnlicensed() { test.skip(license.length > 0); } diff --git a/site/e2e/tests/deployment/idpOrgSync.spec.ts b/site/e2e/tests/deployment/idpOrgSync.spec.ts index 5c49459db1195..05ae54828fffc 100644 --- a/site/e2e/tests/deployment/idpOrgSync.spec.ts +++ b/site/e2e/tests/deployment/idpOrgSync.spec.ts @@ -5,7 +5,7 @@ import { deleteOrganization, setupApiCalls, } from "../../api"; -import { requiresLicense } from "../../helpers"; +import { randomName, requiresLicense } from "../../helpers"; import { beforeCoderTest } from "../../hooks"; test.describe("IdpOrgSyncPage", () => { @@ -117,7 +117,9 @@ test.describe("IdpOrgSyncPage", () => { requiresLicense(); await setupApiCalls(page); - await createOrganizationWithName("admins"); + const orgName = randomName(); + + await createOrganizationWithName(orgName); await page.goto("/deployment/idp-org-sync", { waitUntil: "domcontentloaded", @@ -135,7 +137,7 @@ test.describe("IdpOrgSyncPage", () => { // Select Coder organization from combobox await orgSelector.click(); - await page.getByRole("option", { name: "admins" }).click(); + await page.getByRole("option", { name: orgName }).click(); // Add button should now be enabled await expect(addButton).toBeEnabled(); @@ -146,12 +148,12 @@ test.describe("IdpOrgSyncPage", () => { 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("admins")).toBeVisible(); + await expect(newRow.getByText(orgName)).toBeVisible(); await expect( page.getByText("Organization sync settings updated."), ).toBeVisible(); - await deleteOrganization("admins"); + await deleteOrganization(orgName); }); }); diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index 07c873e357cb8..79cfda226bb64 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -5,7 +5,7 @@ import { deleteOrganization, setupApiCalls, } from "../../../api"; -import { noPremiumLicense, requiresLicense } from "../../../helpers"; +import { randomName, requiresUnlicensed, requiresLicense } from "../../../helpers"; import { beforeCoderTest } from "../../../hooks"; test.describe("CustomRolesPage", () => { @@ -15,7 +15,7 @@ test.describe("CustomRolesPage", () => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("developers"); + const org = await createOrganizationWithName(randomName()); const customRole = await createCustomRole( org.id, @@ -42,14 +42,14 @@ test.describe("CustomRolesPage", () => { await expect(page).toHaveURL(`/organizations/${org.name}/roles`); - await deleteOrganization("developers"); + await deleteOrganization(org.name); }); test("create custom role, edit role and save changes", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("users"); + const org = await createOrganizationWithName(randomName()); const customRole = await createCustomRole( org.id, @@ -91,7 +91,7 @@ test.describe("CustomRolesPage", () => { await expect(page).toHaveURL(`/organizations/${org.name}/roles`); - await deleteOrganization("users"); + await deleteOrganization(org.name); }); test("displays built-in role without edit/delete options", async ({ @@ -100,7 +100,7 @@ test.describe("CustomRolesPage", () => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("testers"); + const org = await createOrganizationWithName(randomName()); await page.goto(`/organizations/${org.name}/roles`); @@ -114,14 +114,14 @@ test.describe("CustomRolesPage", () => { roleRow.getByRole("button", { name: "More options" }), ).not.toBeVisible(); - await deleteOrganization("testers"); + await deleteOrganization(org.name); }); test("create custom role with UI", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("contractors"); + const org = await createOrganizationWithName(randomName()); await page.goto(`/organizations/${org.name}/roles`); @@ -154,14 +154,14 @@ test.describe("CustomRolesPage", () => { await expect(roleRow.getByText(customRoleDisplayName)).toBeVisible(); await expect(roleRow.getByText("None")).toBeVisible(); - await deleteOrganization("contractors"); + await deleteOrganization(org.name); }); test("delete custom role", async ({ page }) => { requiresLicense(); await setupApiCalls(page); - const org = await createOrganizationWithName("custom1"); + const org = await createOrganizationWithName(randomName()); const customRole = await createCustomRole( org.id, "custom-role-test-1", @@ -183,12 +183,12 @@ test.describe("CustomRolesPage", () => { page.getByText("Custom role deleted successfully!"), ).toBeVisible(); - await deleteOrganization("custom1"); + await deleteOrganization(org.name); }); }); test("custom roles disabled", async ({ page }) => { - noPremiumLicense(); + requiresUnlicensed(); await page.goto("/organizations/coder/roles"); await expect(page).toHaveURL("/organizations/coder/roles"); From 4a8a8a8ba3e57b61b66aaffd14b6733b16b889d0 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Thu, 12 Dec 2024 20:12:57 +0000 Subject: [PATCH 10/10] fix: format --- .../e2e/tests/organizations/customRoles/customRoles.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts index 79cfda226bb64..99f8801de8141 100644 --- a/site/e2e/tests/organizations/customRoles/customRoles.spec.ts +++ b/site/e2e/tests/organizations/customRoles/customRoles.spec.ts @@ -5,7 +5,11 @@ import { deleteOrganization, setupApiCalls, } from "../../../api"; -import { randomName, requiresUnlicensed, requiresLicense } from "../../../helpers"; +import { + randomName, + requiresLicense, + requiresUnlicensed, +} from "../../../helpers"; import { beforeCoderTest } from "../../../hooks"; test.describe("CustomRolesPage", () => {