Skip to content

chore(site): add e2e tests for groups #12866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions site/e2e/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Page } from "@playwright/test";
import * as API from "api/api";
import { coderPort } from "./constants";
import { findSessionToken, randomName } from "./helpers";

let currentOrgId: string;

export const setupApiCalls = async (page: Page) => {
const token = await findSessionToken(page);
API.setSessionToken(token);
API.setHost(`http://127.0.0.1:${coderPort}`);
};

export const getCurrentOrgId = async (): Promise<string> => {
if (currentOrgId) {
return currentOrgId;
}
const currentUser = await API.getAuthenticatedUser();
currentOrgId = currentUser.organization_ids[0];
return currentOrgId;
};

export const createUser = async (orgId: string) => {
const name = randomName();
const user = await API.createUser({
email: `${name}@coder.com`,
username: name,
password: "s3cure&password!",
login_type: "password",
disable_login: false,
organization_id: orgId,
});
return user;
};

export const createGroup = async (orgId: string) => {
const name = randomName();
const group = await API.createGroup(orgId, {
name,
display_name: `Display ${name}`,
avatar_url: "/emojis/1f60d.png",
quota_allowance: 0,
});
return group;
};
7 changes: 0 additions & 7 deletions site/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import capitalize from "lodash/capitalize";
import path from "path";
import * as ssh from "ssh2";
import { Duplex } from "stream";
import * as API from "api/api";
import type {
WorkspaceBuildParameter,
UpdateTemplateMeta,
Expand Down Expand Up @@ -826,9 +825,3 @@ export async function openTerminalWindow(

return terminal;
}

export const setupApiCalls = async (page: Page) => {
const token = await findSessionToken(page);
API.setSessionToken(token);
API.setHost(`http://127.0.0.1:${coderPort}`);
};
34 changes: 34 additions & 0 deletions site/e2e/tests/groups/addMembers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { test, expect } from "@playwright/test";
import {
createGroup,
createUser,
getCurrentOrgId,
setupApiCalls,
} from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";

test.beforeEach(async ({ page }) => await beforeCoderTest(page));

test("add members", async ({ page, baseURL }) => {
requiresEnterpriseLicense();
await setupApiCalls(page);
const orgId = await getCurrentOrgId();
const group = await createGroup(orgId);
const numberOfMembers = 3;
const users = await Promise.all(
Array.from({ length: numberOfMembers }, () => createUser(orgId)),
);

await page.goto(`${baseURL}/groups/${group.id}`, {
waitUntil: "domcontentloaded",
});
await expect(page).toHaveTitle(`${group.display_name} - Coder`);

for (const user of users) {
await page.getByPlaceholder("User email or username").fill(user.username);
await page.getByRole("option", { name: user.email }).click();
await page.getByRole("button", { name: "Add user" }).click();
await expect(page.getByRole("row", { name: user.username })).toBeVisible();
}
});
32 changes: 32 additions & 0 deletions site/e2e/tests/groups/addUsersToDefaultGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { test, expect } from "@playwright/test";
import { createUser, getCurrentOrgId, setupApiCalls } from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";

test.beforeEach(async ({ page }) => await beforeCoderTest(page));

const DEFAULT_GROUP_NAME = "Everyone";

test(`Every user should be automatically added to the default '${DEFAULT_GROUP_NAME}' group upon creation`, async ({
page,
baseURL,
}) => {
requiresEnterpriseLicense();
await setupApiCalls(page);
const orgId = await getCurrentOrgId();
const numberOfMembers = 3;
const users = await Promise.all(
Array.from({ length: numberOfMembers }, () => createUser(orgId)),
);

await page.goto(`${baseURL}/groups`, { waitUntil: "domcontentloaded" });
await expect(page).toHaveTitle("Groups - Coder");

const groupRow = page.getByRole("row", { name: DEFAULT_GROUP_NAME });
await groupRow.click();
await expect(page).toHaveTitle(`${DEFAULT_GROUP_NAME} - Coder`);

for (const user of users) {
await expect(page.getByRole("row", { name: user.username })).toBeVisible();
}
});
30 changes: 30 additions & 0 deletions site/e2e/tests/groups/createGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { test, expect } from "@playwright/test";
import { randomName, requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";

test.beforeEach(async ({ page }) => await beforeCoderTest(page));

test("create group", async ({ page, baseURL }) => {
requiresEnterpriseLicense();
await page.goto(`${baseURL}/groups`, { waitUntil: "domcontentloaded" });
await expect(page).toHaveTitle("Groups - Coder");

await page.getByText("Create group").click();
await expect(page).toHaveTitle("Create Group - Coder");

const name = randomName();
const groupValues = {
name: name,
displayName: `Display Name for ${name}`,
avatarURL: "/emojis/1f60d.png",
};

await page.getByLabel("Name", { exact: true }).fill(groupValues.name);
await page.getByLabel("Display Name").fill(groupValues.displayName);
await page.getByLabel("Avatar URL").fill(groupValues.avatarURL);
await page.getByRole("button", { name: "Submit" }).click();

await expect(page).toHaveTitle(`${groupValues.displayName} - Coder`);
await expect(page.getByText(groupValues.displayName)).toBeVisible();
await expect(page.getByText("No members yet")).toBeVisible();
});
23 changes: 23 additions & 0 deletions site/e2e/tests/groups/navigateToGroupPage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { test, expect } from "@playwright/test";
import { createGroup, getCurrentOrgId, setupApiCalls } from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";

test.beforeEach(async ({ page }) => await beforeCoderTest(page));

test("navigate to group page", async ({ page, baseURL }) => {
requiresEnterpriseLicense();
await setupApiCalls(page);
const orgId = await getCurrentOrgId();
const group = await createGroup(orgId);

await page.goto(`${baseURL}/users`, { waitUntil: "domcontentloaded" });
await expect(page).toHaveTitle("Users - Coder");

await page.getByRole("link", { name: "Groups" }).click();
await expect(page).toHaveTitle("Groups - Coder");

const groupRow = page.getByRole("row", { name: group.display_name });
await groupRow.click();
await expect(page).toHaveTitle(`${group.display_name} - Coder`);
});
26 changes: 26 additions & 0 deletions site/e2e/tests/groups/removeGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { test, expect } from "@playwright/test";
import { createGroup, getCurrentOrgId, setupApiCalls } from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";

test.beforeEach(async ({ page }) => await beforeCoderTest(page));

test("remove group", async ({ page, baseURL }) => {
requiresEnterpriseLicense();
await setupApiCalls(page);
const orgId = await getCurrentOrgId();
const group = await createGroup(orgId);

await page.goto(`${baseURL}/groups/${group.id}`, {
waitUntil: "domcontentloaded",
});
await expect(page).toHaveTitle(`${group.display_name} - Coder`);

await page.getByRole("button", { name: "Delete" }).click();
const dialog = page.getByTestId("dialog");
await dialog.getByLabel("Name of the group to delete").fill(group.name);
await dialog.getByRole("button", { name: "Delete" }).click();
await expect(page.getByText("Group deleted successfully.")).toBeVisible();

await expect(page).toHaveTitle("Groups - Coder");
});
36 changes: 36 additions & 0 deletions site/e2e/tests/groups/removeMember.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { test, expect } from "@playwright/test";
import * as API from "api/api";
import {
createGroup,
createUser,
getCurrentOrgId,
setupApiCalls,
} from "../../api";
import { requiresEnterpriseLicense } from "../../helpers";
import { beforeCoderTest } from "../../hooks";

test.beforeEach(async ({ page }) => await beforeCoderTest(page));

test("remove member", async ({ page, baseURL }) => {
requiresEnterpriseLicense();
await setupApiCalls(page);
const orgId = await getCurrentOrgId();
const [group, member] = await Promise.all([
createGroup(orgId),
createUser(orgId),
]);
await API.addMember(group.id, member.id);

await page.goto(`${baseURL}/groups/${group.id}`, {
waitUntil: "domcontentloaded",
});
await expect(page).toHaveTitle(`${group.display_name} - Coder`);

const userRow = page.getByRole("row", { name: member.username });
await userRow.getByRole("button", { name: "More options" }).click();

const menu = page.locator("#more-options");
await menu.getByText("Remove").click({ timeout: 1_000 });

await expect(page.getByText("Member removed successfully.")).toBeVisible();
});
20 changes: 6 additions & 14 deletions site/e2e/tests/users/removeUser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import { test, expect } from "@playwright/test";
import * as API from "api/api";
import { randomName, setupApiCalls } from "../../helpers";
import { createUser, getCurrentOrgId, setupApiCalls } from "../../api";
import { beforeCoderTest } from "../../hooks";

test.beforeEach(async ({ page }) => await beforeCoderTest(page));

test("remove user", async ({ page, baseURL }) => {
await setupApiCalls(page);
const currentUser = await API.getAuthenticatedUser();
const name = randomName();
const user = await API.createUser({
email: `${name}@coder.com`,
username: name,
password: "s3cure&password!",
login_type: "password",
disable_login: false,
organization_id: currentUser.organization_ids[0],
});
const orgId = await getCurrentOrgId();
const user = await createUser(orgId);

await page.goto(`${baseURL}/users`, { waitUntil: "domcontentloaded" });
await expect(page).toHaveTitle("Users - Coder");

const userRow = page.locator("tr", { hasText: user.email });
const userRow = page.getByRole("row", { name: user.email });
await userRow.getByRole("button", { name: "More options" }).click();
await userRow.getByText("Delete", { exact: false }).click();
const menu = page.locator("#more-options");
await menu.getByText("Delete").click();

const dialog = page.getByTestId("dialog");
await dialog.getByLabel("Name of the user to delete").fill(user.username);
Expand Down
1 change: 0 additions & 1 deletion site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,6 @@ export const patchGroup = async (
export const addMember = async (groupId: string, userId: string) => {
return patchGroup(groupId, {
name: "",
display_name: "",
add_users: [userId],
remove_users: [],
});
Expand Down
1 change: 1 addition & 0 deletions site/src/pages/GroupsPage/GroupPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ export const GroupPage: FC = () => {
onConfirm={async () => {
try {
await deleteGroupMutation.mutateAsync(groupId);
displaySuccess("Group deleted successfully.");
navigate("/groups");
} catch (error) {
displayError(getErrorMessage(error, "Failed to delete group."));
Expand Down
11 changes: 7 additions & 4 deletions site/src/pages/UsersPage/UsersLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import GroupAdd from "@mui/icons-material/GroupAddOutlined";
import PersonAdd from "@mui/icons-material/PersonAddOutlined";
import Button from "@mui/material/Button";
import Link from "@mui/material/Link";
import { type FC, Suspense } from "react";
import {
Link as RouterLink,
Expand Down Expand Up @@ -43,9 +42,13 @@ export const UsersLayout: FC = () => {
</Button>
)}
{canCreateGroup && isTemplateRBACEnabled && (
<Link component={RouterLink} to="/groups/create">
<Button startIcon={<GroupAdd />}>Create group</Button>
</Link>
<Button
component={RouterLink}
startIcon={<GroupAdd />}
to="/groups/create"
>
Create group
</Button>
)}
</>
}
Expand Down