From 92b6cfa4d175abffeb68354ead58b79d3be5bdbb Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Thu, 12 Dec 2024 21:48:22 +0000 Subject: [PATCH 1/3] chore: add e2e test for org groups --- site/e2e/api.ts | 4 +- site/e2e/helpers.ts | 22 ++++++ site/e2e/tests/organizationGroups.spec.ts | 70 +++++++++++++++++++ site/e2e/tests/organizationMembers.spec.ts | 16 ++--- .../GroupsPage/GroupsPage.tsx | 4 +- .../OrganizationMembersPage.tsx | 14 +++- .../OrganizationProvisionersPage.tsx | 22 ++++-- 7 files changed, 128 insertions(+), 24 deletions(-) create mode 100644 site/e2e/tests/organizationGroups.spec.ts diff --git a/site/e2e/api.ts b/site/e2e/api.ts index bca000e5c29c6..f4a95c981a0dc 100644 --- a/site/e2e/api.ts +++ b/site/e2e/api.ts @@ -28,7 +28,7 @@ export const getCurrentOrgId = async (): Promise => { return currentOrgId; }; -export const createUser = async (orgId: string) => { +export const createUser = async (...orgIds: string[]) => { const name = randomName(); const user = await API.createUser({ email: `${name}@coder.com`, @@ -36,7 +36,7 @@ export const createUser = async (orgId: string) => { name: name, password: "s3cure&password!", login_type: "password", - organization_ids: [orgId], + organization_ids: orgIds, user_status: null, }); return user; diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index 34fa03837057f..100e0a3d0bf95 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -1037,3 +1037,25 @@ export async function createUser( await page.goto(returnTo, { waitUntil: "domcontentloaded" }); return { name, username, email, password }; } + +export async function createOrganization(page: Page): Promise<{ + name: string; + displayName: string; + description: string; +}> { + // Create a new organization to test + await page.goto("/organizations/new", { waitUntil: "domcontentloaded" }); + const name = randomName(); + await page.getByLabel("Slug").fill(name); + const displayName = `Org ${name}`; + await page.getByLabel("Display name").fill(displayName); + const description = `Org description ${name}`; + await page.getByLabel("Description").fill(description); + await page.getByLabel("Icon", { exact: true }).fill("/emojis/1f957.png"); + await page.getByRole("button", { name: "Submit" }).click(); + + await expectUrl(page).toHavePathName(`/organizations/${name}`); + await expect(page.getByText("Organization created.")).toBeVisible(); + + return { name, displayName, description }; +} diff --git a/site/e2e/tests/organizationGroups.spec.ts b/site/e2e/tests/organizationGroups.spec.ts new file mode 100644 index 0000000000000..023685d6dfea5 --- /dev/null +++ b/site/e2e/tests/organizationGroups.spec.ts @@ -0,0 +1,70 @@ +import { expect, test } from "@playwright/test"; +import { createOrganization, createUser, setupApiCalls } from "../api"; +import { expectUrl } from "../expectUrl"; +import { randomName, requiresLicense } from "../helpers"; +import { beforeCoderTest } from "../hooks"; + +test.beforeEach(async ({ page }) => { + await beforeCoderTest(page); + await setupApiCalls(page); +}); + +test("create group", async ({ page }) => { + requiresLicense(); + + // Create a new organization + const org = await createOrganization(); + await page.goto(`/organizations/${org.name}`); + + // Navigate to groups page + await page.getByText("Groups").click(); + await expect(page).toHaveTitle(`Groups - Org ${org.name} - Coder`); + + // Create a new group + await page.getByText("Create group").click(); + await expect(page).toHaveTitle("Create Group - Coder"); + const name = randomName(); + await page.getByLabel("Name", { exact: true }).fill(name); + const displayName = `Group ${name}`; + await page.getByLabel("Display Name").fill(displayName); + await page.getByLabel("Avatar URL").fill("/emojis/1f60d.png"); + await page.getByRole("button", { name: "Submit" }).click(); + + await expectUrl(page).toHavePathName( + `/organizations/${org.name}/groups/${name}`, + ); + await expect(page).toHaveTitle(`${displayName} - Coder`); + await expect(page.getByText("No members yet")).toBeVisible(); + await expect(page.getByText(displayName)).toBeVisible(); + + // Add a user to the group + const personToAdd = await createUser(org.id); + await page.getByPlaceholder("User email or username").fill(personToAdd.email); + await page.getByRole("option", { name: personToAdd.email }).click(); + await page.getByRole("button", { name: "Add user" }).click(); + const addedRow = page.locator("tr", { hasText: personToAdd.email }); + await expect(addedRow).toBeVisible(); + + // Ensure we can't add a user who isn't in the org + const otherOrg = await createOrganization(); + const personToReject = await createUser(otherOrg.id); + await page + .getByPlaceholder("User email or username") + .fill(personToReject.email); + await expect(page.getByText("No users found")).toBeVisible(); + + // Remove someone from the group + await addedRow.getByLabel("More options").click(); + await page.getByText("Remove").click(); + await expect(addedRow).not.toBeVisible(); + + // Delete the group + await page.getByRole("button", { name: "Delete" }).click(); + const dialog = page.getByTestId("dialog"); + await dialog.getByLabel("Name of the group to delete").fill(name); + await dialog.getByRole("button", { name: "Delete" }).click(); + await expect(page.getByText("Group deleted successfully.")).toBeVisible(); + + await expectUrl(page).toHavePathName(`/organizations/${org.name}/groups`); + await expect(page).toHaveTitle(`Groups - Org ${org.name} - Coder`); +}); diff --git a/site/e2e/tests/organizationMembers.spec.ts b/site/e2e/tests/organizationMembers.spec.ts index 56fb2dbc4acde..26cf450883378 100644 --- a/site/e2e/tests/organizationMembers.spec.ts +++ b/site/e2e/tests/organizationMembers.spec.ts @@ -1,7 +1,6 @@ import { expect, test } from "@playwright/test"; import { setupApiCalls } from "../api"; -import { expectUrl } from "../expectUrl"; -import { createUser, randomName, requiresLicense } from "../helpers"; +import { createOrganization, createUser, requiresLicense } from "../helpers"; import { beforeCoderTest } from "../hooks"; test.beforeEach(async ({ page }) => { @@ -12,19 +11,12 @@ test.beforeEach(async ({ page }) => { test("add and remove organization member", async ({ page }) => { requiresLicense(); - // Create a new organization to test - await page.goto("/organizations/new", { waitUntil: "domcontentloaded" }); - const name = randomName(); - await page.getByLabel("Slug").fill(name); - await page.getByLabel("Display name").fill(`Org ${name}`); - await page.getByLabel("Description").fill(`Org description ${name}`); - await page.getByLabel("Icon", { exact: true }).fill("/emojis/1f957.png"); - await page.getByRole("button", { name: "Submit" }).click(); + // Create a new organization + const { displayName } = await createOrganization(page); // Navigate to members page - await expectUrl(page).toHavePathName(`/organizations/${name}`); - await expect(page.getByText("Organization created.")).toBeVisible(); await page.getByText("Members").click(); + await expect(page).toHaveTitle(`Members — ${displayName} - Coder`); // Add a user to the org const personToAdd = await createUser(page); diff --git a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx index 774360dc6a6d1..5f9df03f825e0 100644 --- a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx +++ b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx @@ -69,7 +69,9 @@ export const GroupsPage: FC = () => { return ( <> - {pageTitle("Groups")} + + {pageTitle("Groups", organization.display_name || organization.name)} + { @@ -50,8 +52,7 @@ const OrganizationMembersPage: FC = () => { updateOrganizationMemberRoles(queryClient, organizationName), ); - const { organizations } = useManagementSettings(); - const organization = organizations?.find((o) => o.name === organizationName); + const { organization } = useManagementSettings(); const permissionsQuery = useQuery(organizationPermissions(organization?.id)); const [memberToDelete, setMemberToDelete] = @@ -62,8 +63,17 @@ const OrganizationMembersPage: FC = () => { return ; } + const helmet = organization && ( + + + {pageTitle("Members", organization.display_name || organization.name)} + + + ); + return ( <> + {helmet} { @@ -25,12 +26,19 @@ const OrganizationProvisionersPage: FC = () => { } return ( - + <> + + + {pageTitle("Members", organization.display_name || organization.name)} + + + + ); }; From bac9d8fa2a1b9c6d506d184adc65036ab7f0e9dd Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 13 Dec 2024 22:52:10 +0000 Subject: [PATCH 2/3] add a quota test --- site/e2e/tests/organizationGroups.spec.ts | 35 ++++++++++++++++++- .../GroupsPage/GroupPage.tsx | 3 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/site/e2e/tests/organizationGroups.spec.ts b/site/e2e/tests/organizationGroups.spec.ts index 023685d6dfea5..0a049191b97df 100644 --- a/site/e2e/tests/organizationGroups.spec.ts +++ b/site/e2e/tests/organizationGroups.spec.ts @@ -1,5 +1,10 @@ import { expect, test } from "@playwright/test"; -import { createOrganization, createUser, setupApiCalls } from "../api"; +import { + createGroup, + createOrganization, + createUser, + setupApiCalls, +} from "../api"; import { expectUrl } from "../expectUrl"; import { randomName, requiresLicense } from "../helpers"; import { beforeCoderTest } from "../hooks"; @@ -68,3 +73,31 @@ test("create group", async ({ page }) => { await expectUrl(page).toHavePathName(`/organizations/${org.name}/groups`); await expect(page).toHaveTitle(`Groups - Org ${org.name} - Coder`); }); + +test("change quota settings", async ({ page }) => { + requiresLicense(); + + // Create a new organization and group + const org = await createOrganization(); + const group = await createGroup(org.id); + + // Go to settings + await page.goto(`/organizations/${org.name}/groups/${group.name}`); + await page.getByRole("button", { name: "Settings" }).click(); + expectUrl(page).toHavePathName( + `/organizations/${org.name}/groups/${group.name}/settings`, + ); + + // Update Quota + await page.getByLabel("Quota Allowance").fill("100"); + await page.getByRole("button", { name: "Submit" }).click(); + + // We should get sent back to the group page afterwards + expectUrl(page).toHavePathName( + `/organizations/${org.name}/groups/${group.name}`, + ); + + // ...and that setting should persist if we go back + await page.getByRole("button", { name: "Settings" }).click(); + await expect(page.getByLabel("Quota Allowance")).toHaveValue("100"); +}); diff --git a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx index 22841f5d2fbc0..42251f5d5965f 100644 --- a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx +++ b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx @@ -111,9 +111,10 @@ export const GroupPage: FC = () => { {canUpdateGroup && ( From f2ca6639390cf26b07ef4385b28ee8a15371dae6 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 13 Dec 2024 23:04:49 +0000 Subject: [PATCH 3/3] - --- site/e2e/tests/organizationMembers.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/e2e/tests/organizationMembers.spec.ts b/site/e2e/tests/organizationMembers.spec.ts index 26cf450883378..a761f7c723132 100644 --- a/site/e2e/tests/organizationMembers.spec.ts +++ b/site/e2e/tests/organizationMembers.spec.ts @@ -16,7 +16,7 @@ test("add and remove organization member", async ({ page }) => { // Navigate to members page await page.getByText("Members").click(); - await expect(page).toHaveTitle(`Members — ${displayName} - Coder`); + await expect(page).toHaveTitle(`Members - ${displayName} - Coder`); // Add a user to the org const personToAdd = await createUser(page);