Skip to content

Commit c92f480

Browse files
authored
chore: add e2e test for org groups (#15853)
1 parent 50ff06c commit c92f480

File tree

8 files changed

+163
-25
lines changed

8 files changed

+163
-25
lines changed

site/e2e/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ export const getCurrentOrgId = async (): Promise<string> => {
2828
return currentOrgId;
2929
};
3030

31-
export const createUser = async (orgId: string) => {
31+
export const createUser = async (...orgIds: string[]) => {
3232
const name = randomName();
3333
const user = await API.createUser({
3434
email: `${name}@coder.com`,
3535
username: name,
3636
name: name,
3737
password: "s3cure&password!",
3838
login_type: "password",
39-
organization_ids: [orgId],
39+
organization_ids: orgIds,
4040
user_status: null,
4141
});
4242
return user;

site/e2e/helpers.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,3 +1037,25 @@ export async function createUser(
10371037
await page.goto(returnTo, { waitUntil: "domcontentloaded" });
10381038
return { name, username, email, password };
10391039
}
1040+
1041+
export async function createOrganization(page: Page): Promise<{
1042+
name: string;
1043+
displayName: string;
1044+
description: string;
1045+
}> {
1046+
// Create a new organization to test
1047+
await page.goto("/organizations/new", { waitUntil: "domcontentloaded" });
1048+
const name = randomName();
1049+
await page.getByLabel("Slug").fill(name);
1050+
const displayName = `Org ${name}`;
1051+
await page.getByLabel("Display name").fill(displayName);
1052+
const description = `Org description ${name}`;
1053+
await page.getByLabel("Description").fill(description);
1054+
await page.getByLabel("Icon", { exact: true }).fill("/emojis/1f957.png");
1055+
await page.getByRole("button", { name: "Submit" }).click();
1056+
1057+
await expectUrl(page).toHavePathName(`/organizations/${name}`);
1058+
await expect(page.getByText("Organization created.")).toBeVisible();
1059+
1060+
return { name, displayName, description };
1061+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { expect, test } from "@playwright/test";
2+
import {
3+
createGroup,
4+
createOrganization,
5+
createUser,
6+
setupApiCalls,
7+
} from "../api";
8+
import { expectUrl } from "../expectUrl";
9+
import { randomName, requiresLicense } from "../helpers";
10+
import { beforeCoderTest } from "../hooks";
11+
12+
test.beforeEach(async ({ page }) => {
13+
await beforeCoderTest(page);
14+
await setupApiCalls(page);
15+
});
16+
17+
test("create group", async ({ page }) => {
18+
requiresLicense();
19+
20+
// Create a new organization
21+
const org = await createOrganization();
22+
await page.goto(`/organizations/${org.name}`);
23+
24+
// Navigate to groups page
25+
await page.getByText("Groups").click();
26+
await expect(page).toHaveTitle(`Groups - Org ${org.name} - Coder`);
27+
28+
// Create a new group
29+
await page.getByText("Create group").click();
30+
await expect(page).toHaveTitle("Create Group - Coder");
31+
const name = randomName();
32+
await page.getByLabel("Name", { exact: true }).fill(name);
33+
const displayName = `Group ${name}`;
34+
await page.getByLabel("Display Name").fill(displayName);
35+
await page.getByLabel("Avatar URL").fill("/emojis/1f60d.png");
36+
await page.getByRole("button", { name: "Submit" }).click();
37+
38+
await expectUrl(page).toHavePathName(
39+
`/organizations/${org.name}/groups/${name}`,
40+
);
41+
await expect(page).toHaveTitle(`${displayName} - Coder`);
42+
await expect(page.getByText("No members yet")).toBeVisible();
43+
await expect(page.getByText(displayName)).toBeVisible();
44+
45+
// Add a user to the group
46+
const personToAdd = await createUser(org.id);
47+
await page.getByPlaceholder("User email or username").fill(personToAdd.email);
48+
await page.getByRole("option", { name: personToAdd.email }).click();
49+
await page.getByRole("button", { name: "Add user" }).click();
50+
const addedRow = page.locator("tr", { hasText: personToAdd.email });
51+
await expect(addedRow).toBeVisible();
52+
53+
// Ensure we can't add a user who isn't in the org
54+
const otherOrg = await createOrganization();
55+
const personToReject = await createUser(otherOrg.id);
56+
await page
57+
.getByPlaceholder("User email or username")
58+
.fill(personToReject.email);
59+
await expect(page.getByText("No users found")).toBeVisible();
60+
61+
// Remove someone from the group
62+
await addedRow.getByLabel("More options").click();
63+
await page.getByText("Remove").click();
64+
await expect(addedRow).not.toBeVisible();
65+
66+
// Delete the group
67+
await page.getByRole("button", { name: "Delete" }).click();
68+
const dialog = page.getByTestId("dialog");
69+
await dialog.getByLabel("Name of the group to delete").fill(name);
70+
await dialog.getByRole("button", { name: "Delete" }).click();
71+
await expect(page.getByText("Group deleted successfully.")).toBeVisible();
72+
73+
await expectUrl(page).toHavePathName(`/organizations/${org.name}/groups`);
74+
await expect(page).toHaveTitle(`Groups - Org ${org.name} - Coder`);
75+
});
76+
77+
test("change quota settings", async ({ page }) => {
78+
requiresLicense();
79+
80+
// Create a new organization and group
81+
const org = await createOrganization();
82+
const group = await createGroup(org.id);
83+
84+
// Go to settings
85+
await page.goto(`/organizations/${org.name}/groups/${group.name}`);
86+
await page.getByRole("button", { name: "Settings" }).click();
87+
expectUrl(page).toHavePathName(
88+
`/organizations/${org.name}/groups/${group.name}/settings`,
89+
);
90+
91+
// Update Quota
92+
await page.getByLabel("Quota Allowance").fill("100");
93+
await page.getByRole("button", { name: "Submit" }).click();
94+
95+
// We should get sent back to the group page afterwards
96+
expectUrl(page).toHavePathName(
97+
`/organizations/${org.name}/groups/${group.name}`,
98+
);
99+
100+
// ...and that setting should persist if we go back
101+
await page.getByRole("button", { name: "Settings" }).click();
102+
await expect(page.getByLabel("Quota Allowance")).toHaveValue("100");
103+
});

site/e2e/tests/organizationMembers.spec.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { expect, test } from "@playwright/test";
22
import { setupApiCalls } from "../api";
3-
import { expectUrl } from "../expectUrl";
4-
import { createUser, randomName, requiresLicense } from "../helpers";
3+
import { createOrganization, createUser, requiresLicense } from "../helpers";
54
import { beforeCoderTest } from "../hooks";
65

76
test.beforeEach(async ({ page }) => {
@@ -12,19 +11,12 @@ test.beforeEach(async ({ page }) => {
1211
test("add and remove organization member", async ({ page }) => {
1312
requiresLicense();
1413

15-
// Create a new organization to test
16-
await page.goto("/organizations/new", { waitUntil: "domcontentloaded" });
17-
const name = randomName();
18-
await page.getByLabel("Slug").fill(name);
19-
await page.getByLabel("Display name").fill(`Org ${name}`);
20-
await page.getByLabel("Description").fill(`Org description ${name}`);
21-
await page.getByLabel("Icon", { exact: true }).fill("/emojis/1f957.png");
22-
await page.getByRole("button", { name: "Submit" }).click();
14+
// Create a new organization
15+
const { displayName } = await createOrganization(page);
2316

2417
// Navigate to members page
25-
await expectUrl(page).toHavePathName(`/organizations/${name}`);
26-
await expect(page.getByText("Organization created.")).toBeVisible();
2718
await page.getByText("Members").click();
19+
await expect(page).toHaveTitle(`Members - ${displayName} - Coder`);
2820

2921
// Add a user to the org
3022
const personToAdd = await createUser(page);

site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,10 @@ export const GroupPage: FC = () => {
111111
{canUpdateGroup && (
112112
<Stack direction="row" spacing={2}>
113113
<Button
114+
role="button"
115+
component={RouterLink}
114116
startIcon={<SettingsOutlined />}
115117
to="settings"
116-
component={RouterLink}
117118
>
118119
Settings
119120
</Button>

site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ export const GroupsPage: FC = () => {
6969
return (
7070
<>
7171
<Helmet>
72-
<title>{pageTitle("Groups")}</title>
72+
<title>
73+
{pageTitle("Groups", organization.display_name || organization.name)}
74+
</title>
7375
</Helmet>
7476

7577
<Stack

site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import { Stack } from "components/Stack/Stack";
1717
import { useAuthenticated } from "contexts/auth/RequireAuth";
1818
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
1919
import { type FC, useState } from "react";
20+
import { Helmet } from "react-helmet-async";
2021
import { useMutation, useQuery, useQueryClient } from "react-query";
2122
import { useParams } from "react-router-dom";
23+
import { pageTitle } from "utils/page";
2224
import { OrganizationMembersPageView } from "./OrganizationMembersPageView";
2325

2426
const OrganizationMembersPage: FC = () => {
@@ -50,8 +52,7 @@ const OrganizationMembersPage: FC = () => {
5052
updateOrganizationMemberRoles(queryClient, organizationName),
5153
);
5254

53-
const { organizations } = useManagementSettings();
54-
const organization = organizations?.find((o) => o.name === organizationName);
55+
const { organization } = useManagementSettings();
5556
const permissionsQuery = useQuery(organizationPermissions(organization?.id));
5657

5758
const [memberToDelete, setMemberToDelete] =
@@ -62,8 +63,17 @@ const OrganizationMembersPage: FC = () => {
6263
return <Loader />;
6364
}
6465

66+
const helmet = organization && (
67+
<Helmet>
68+
<title>
69+
{pageTitle("Members", organization.display_name || organization.name)}
70+
</title>
71+
</Helmet>
72+
);
73+
6574
return (
6675
<>
76+
{helmet}
6777
<OrganizationMembersPageView
6878
allAvailableRoles={organizationRolesQuery.data}
6979
canEditMembers={permissions.editMembers}

site/src/pages/ManagementSettingsPage/OrganizationProvisionersPage.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { buildInfo } from "api/queries/buildInfo";
22
import { provisionerDaemonGroups } from "api/queries/organizations";
3-
import type { Organization } from "api/typesGenerated";
43
import { EmptyState } from "components/EmptyState/EmptyState";
54
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
65
import { useDashboard } from "modules/dashboard/useDashboard";
76
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
87
import type { FC } from "react";
8+
import { Helmet } from "react-helmet-async";
99
import { useQuery } from "react-query";
1010
import { useParams } from "react-router-dom";
11+
import { pageTitle } from "utils/page";
1112
import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView";
1213

1314
const OrganizationProvisionersPage: FC = () => {
@@ -25,12 +26,19 @@ const OrganizationProvisionersPage: FC = () => {
2526
}
2627

2728
return (
28-
<OrganizationProvisionersPageView
29-
showPaywall={!entitlements.features.multiple_organizations.enabled}
30-
error={provisionersQuery.error}
31-
buildInfo={buildInfoQuery.data}
32-
provisioners={provisionersQuery.data}
33-
/>
29+
<>
30+
<Helmet>
31+
<title>
32+
{pageTitle("Members", organization.display_name || organization.name)}
33+
</title>
34+
</Helmet>
35+
<OrganizationProvisionersPageView
36+
showPaywall={!entitlements.features.multiple_organizations.enabled}
37+
error={provisionersQuery.error}
38+
buildInfo={buildInfoQuery.data}
39+
provisioners={provisionersQuery.data}
40+
/>
41+
</>
3442
);
3543
};
3644

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy