mirror of https://github.com/coder/coder.git
chore(site): add e2e tests for groups (#12866)
This commit is contained in:
parent
bc9ea61eb4
commit
3fbcdb0ddc
|
@ -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,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,
|
||||
|
@ -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}`);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
|
@ -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();
|
||||
}
|
||||
});
|
|
@ -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();
|
||||
});
|
|
@ -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`);
|
||||
});
|
|
@ -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");
|
||||
});
|
|
@ -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();
|
||||
});
|
|
@ -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);
|
||||
|
|
|
@ -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: [],
|
||||
});
|
||||
|
|
|
@ -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."));
|
||||
|
|
|
@ -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,
|
||||
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue