Skip to content

Commit 202f7f7

Browse files
authored
feat: create e2e tests for organization custom roles page (coder#15814)
Adds 5 premium tests and 1 non-premium test.
1 parent 4c939a6 commit 202f7f7

File tree

4 files changed

+253
-5
lines changed

4 files changed

+253
-5
lines changed

site/e2e/api.ts

+37
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,43 @@ export const createOrganizationSyncSettings = async () => {
9393
return settings;
9494
};
9595

96+
export const createCustomRole = async (
97+
orgId: string,
98+
name: string,
99+
displayName: string,
100+
) => {
101+
const role = await API.createOrganizationRole(orgId, {
102+
name,
103+
display_name: displayName,
104+
organization_id: orgId,
105+
site_permissions: [],
106+
organization_permissions: [
107+
{
108+
negate: false,
109+
resource_type: "organization_member",
110+
action: "create",
111+
},
112+
{
113+
negate: false,
114+
resource_type: "organization_member",
115+
action: "delete",
116+
},
117+
{
118+
negate: false,
119+
resource_type: "organization_member",
120+
action: "read",
121+
},
122+
{
123+
negate: false,
124+
resource_type: "organization_member",
125+
action: "update",
126+
},
127+
],
128+
user_permissions: [],
129+
});
130+
return role;
131+
};
132+
96133
export async function verifyConfigFlagBoolean(
97134
page: Page,
98135
config: DeploymentConfig,

site/e2e/helpers.ts

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ export function requiresLicense() {
4949
test.skip(!license);
5050
}
5151

52+
export function requiresUnlicensed() {
53+
test.skip(license.length > 0);
54+
}
55+
5256
/**
5357
* requireTerraformProvisioner by default is enabled.
5458
*/

site/e2e/tests/deployment/idpOrgSync.spec.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
deleteOrganization,
66
setupApiCalls,
77
} from "../../api";
8-
import { requiresLicense } from "../../helpers";
8+
import { randomName, requiresLicense } from "../../helpers";
99
import { beforeCoderTest } from "../../hooks";
1010

1111
test.describe("IdpOrgSyncPage", () => {
@@ -123,7 +123,9 @@ test.describe("IdpOrgSyncPage", () => {
123123
requiresLicense();
124124
await setupApiCalls(page);
125125

126-
await createOrganizationWithName("developers");
126+
const orgName = randomName();
127+
128+
await createOrganizationWithName(orgName);
127129

128130
await page.goto("/deployment/idp-org-sync", {
129131
waitUntil: "domcontentloaded",
@@ -141,7 +143,7 @@ test.describe("IdpOrgSyncPage", () => {
141143

142144
// Select Coder organization from combobox
143145
await orgSelector.click();
144-
await page.getByRole("option", { name: "developers" }).click();
146+
await page.getByRole("option", { name: orgName }).click();
145147

146148
// Add button should now be enabled
147149
await expect(addButton).toBeEnabled();
@@ -152,12 +154,12 @@ test.describe("IdpOrgSyncPage", () => {
152154
const newRow = page.getByTestId("idp-org-new-idp-org");
153155
await expect(newRow).toBeVisible();
154156
await expect(newRow.getByText("new-idp-org")).toBeVisible();
155-
await expect(newRow.getByText("developers")).toBeVisible();
157+
await expect(newRow.getByText(orgName)).toBeVisible();
156158

157159
await expect(
158160
page.getByText("Organization sync settings updated."),
159161
).toBeVisible();
160162

161-
await deleteOrganization("developers");
163+
await deleteOrganization(orgName);
162164
});
163165
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import { expect, test } from "@playwright/test";
2+
import {
3+
createCustomRole,
4+
createOrganizationWithName,
5+
deleteOrganization,
6+
setupApiCalls,
7+
} from "../../../api";
8+
import {
9+
randomName,
10+
requiresLicense,
11+
requiresUnlicensed,
12+
} from "../../../helpers";
13+
import { beforeCoderTest } from "../../../hooks";
14+
15+
test.describe("CustomRolesPage", () => {
16+
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
17+
18+
test("create custom role and cancel edit changes", async ({ page }) => {
19+
requiresLicense();
20+
await setupApiCalls(page);
21+
22+
const org = await createOrganizationWithName(randomName());
23+
24+
const customRole = await createCustomRole(
25+
org.id,
26+
"custom-role-test-1",
27+
"Custom Role Test 1",
28+
);
29+
30+
await page.goto(`/organizations/${org.name}/roles`);
31+
const roleRow = page.getByTestId(`role-${customRole.name}`);
32+
await expect(roleRow.getByText(customRole.display_name)).toBeVisible();
33+
await expect(roleRow.getByText("organization_member")).toBeVisible();
34+
35+
await roleRow.getByRole("button", { name: "More options" }).click();
36+
const menu = page.locator("#more-options");
37+
await menu.getByText("Edit").click();
38+
39+
await expect(page).toHaveURL(
40+
`/organizations/${org.name}/roles/${customRole.name}`,
41+
);
42+
43+
const cancelButton = page.getByRole("button", { name: "Cancel" }).first();
44+
await expect(cancelButton).toBeVisible();
45+
await cancelButton.click();
46+
47+
await expect(page).toHaveURL(`/organizations/${org.name}/roles`);
48+
49+
await deleteOrganization(org.name);
50+
});
51+
52+
test("create custom role, edit role and save changes", async ({ page }) => {
53+
requiresLicense();
54+
await setupApiCalls(page);
55+
56+
const org = await createOrganizationWithName(randomName());
57+
58+
const customRole = await createCustomRole(
59+
org.id,
60+
"custom-role-test-1",
61+
"Custom Role Test 1",
62+
);
63+
64+
await page.goto(`/organizations/${org.name}/roles`);
65+
const roleRow = page.getByTestId(`role-${customRole.name}`);
66+
await expect(roleRow.getByText(customRole.display_name)).toBeVisible();
67+
await expect(roleRow.getByText("organization_member")).toBeVisible();
68+
69+
await page.goto(`/organizations/${org.name}/roles/${customRole.name}`);
70+
71+
const displayNameInput = page.getByRole("textbox", {
72+
name: "Display name",
73+
});
74+
await displayNameInput.fill("Custom Role Test 2 Edited");
75+
76+
const groupCheckbox = page.getByTestId("group").getByRole("checkbox");
77+
await expect(groupCheckbox).toBeVisible();
78+
await groupCheckbox.click();
79+
80+
const organizationMemberCheckbox = page
81+
.getByTestId("organization_member")
82+
.getByRole("checkbox");
83+
await expect(organizationMemberCheckbox).toBeVisible();
84+
await organizationMemberCheckbox.click();
85+
86+
const saveButton = page.getByRole("button", { name: "Save" }).first();
87+
await expect(saveButton).toBeVisible();
88+
await saveButton.click();
89+
90+
await expect(roleRow.getByText("Custom Role Test 2 Edited")).toBeVisible();
91+
92+
const roleRow2 = page.getByTestId(`role-${customRole.name}`);
93+
await expect(roleRow2.getByText("organization_member")).not.toBeVisible();
94+
await expect(roleRow2.getByText("group")).toBeVisible();
95+
96+
await expect(page).toHaveURL(`/organizations/${org.name}/roles`);
97+
98+
await deleteOrganization(org.name);
99+
});
100+
101+
test("displays built-in role without edit/delete options", async ({
102+
page,
103+
}) => {
104+
requiresLicense();
105+
await setupApiCalls(page);
106+
107+
const org = await createOrganizationWithName(randomName());
108+
109+
await page.goto(`/organizations/${org.name}/roles`);
110+
111+
const roleRow = page.getByTestId("role-organization-admin");
112+
await expect(roleRow).toBeVisible();
113+
114+
await expect(roleRow.getByText("Organization Admin")).toBeVisible();
115+
116+
// Verify that the more menu (three dots) is not present for built-in roles
117+
await expect(
118+
roleRow.getByRole("button", { name: "More options" }),
119+
).not.toBeVisible();
120+
121+
await deleteOrganization(org.name);
122+
});
123+
124+
test("create custom role with UI", async ({ page }) => {
125+
requiresLicense();
126+
await setupApiCalls(page);
127+
128+
const org = await createOrganizationWithName(randomName());
129+
130+
await page.goto(`/organizations/${org.name}/roles`);
131+
132+
await page
133+
.getByRole("link", { name: "Create custom role" })
134+
.first()
135+
.click();
136+
137+
await expect(page).toHaveURL(`/organizations/${org.name}/roles/create`);
138+
139+
const customRoleName = "custom-role-test";
140+
const roleNameInput = page.getByRole("textbox", {
141+
exact: true,
142+
name: "Name",
143+
});
144+
await roleNameInput.fill(customRoleName);
145+
146+
const customRoleDisplayName = "Custom Role Test";
147+
const displayNameInput = page.getByRole("textbox", {
148+
exact: true,
149+
name: "Display Name",
150+
});
151+
await displayNameInput.fill(customRoleDisplayName);
152+
153+
await page.getByRole("button", { name: "Create Role" }).first().click();
154+
155+
await expect(page).toHaveURL(`/organizations/${org.name}/roles`);
156+
157+
const roleRow = page.getByTestId(`role-${customRoleName}`);
158+
await expect(roleRow.getByText(customRoleDisplayName)).toBeVisible();
159+
await expect(roleRow.getByText("None")).toBeVisible();
160+
161+
await deleteOrganization(org.name);
162+
});
163+
164+
test("delete custom role", async ({ page }) => {
165+
requiresLicense();
166+
await setupApiCalls(page);
167+
168+
const org = await createOrganizationWithName(randomName());
169+
const customRole = await createCustomRole(
170+
org.id,
171+
"custom-role-test-1",
172+
"Custom Role Test 1",
173+
);
174+
await page.goto(`/organizations/${org.name}/roles`);
175+
176+
const roleRow = page.getByTestId(`role-${customRole.name}`);
177+
await roleRow.getByRole("button", { name: "More options" }).click();
178+
179+
const menu = page.locator("#more-options");
180+
await menu.getByText("Delete…").click();
181+
182+
const input = page.getByRole("textbox");
183+
await input.fill(customRole.name);
184+
await page.getByRole("button", { name: "Delete" }).click();
185+
186+
await expect(
187+
page.getByText("Custom role deleted successfully!"),
188+
).toBeVisible();
189+
190+
await deleteOrganization(org.name);
191+
});
192+
});
193+
194+
test("custom roles disabled", async ({ page }) => {
195+
requiresUnlicensed();
196+
await page.goto("/organizations/coder/roles");
197+
await expect(page).toHaveURL("/organizations/coder/roles");
198+
199+
await expect(
200+
page.getByText("Upgrade to a premium license to create a custom role"),
201+
).toBeVisible();
202+
await expect(
203+
page.getByRole("link", { name: "Create custom role" }),
204+
).not.toBeVisible();
205+
});

0 commit comments

Comments
 (0)