chore: add e2e test for org groups (#15853)

This commit is contained in:
ケイラ
2024-12-13 15:14:12 -08:00
committed by GitHub
parent 50ff06cc3c
commit c92f480fcf
8 changed files with 163 additions and 25 deletions
+2 -2
View File
@@ -28,7 +28,7 @@ export const getCurrentOrgId = async (): Promise<string> => {
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;
+22
View File
@@ -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 };
}
+103
View File
@@ -0,0 +1,103 @@
import { expect, test } from "@playwright/test";
import {
createGroup,
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`);
});
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");
});
+4 -12
View File
@@ -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);
@@ -111,9 +111,10 @@ export const GroupPage: FC = () => {
{canUpdateGroup && (
<Stack direction="row" spacing={2}>
<Button
role="button"
component={RouterLink}
startIcon={<SettingsOutlined />}
to="settings"
component={RouterLink}
>
Settings
</Button>
@@ -69,7 +69,9 @@ export const GroupsPage: FC = () => {
return (
<>
<Helmet>
<title>{pageTitle("Groups")}</title>
<title>
{pageTitle("Groups", organization.display_name || organization.name)}
</title>
</Helmet>
<Stack
@@ -17,8 +17,10 @@ import { Stack } from "components/Stack/Stack";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import { type FC, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { OrganizationMembersPageView } from "./OrganizationMembersPageView";
const OrganizationMembersPage: FC = () => {
@@ -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 <Loader />;
}
const helmet = organization && (
<Helmet>
<title>
{pageTitle("Members", organization.display_name || organization.name)}
</title>
</Helmet>
);
return (
<>
{helmet}
<OrganizationMembersPageView
allAvailableRoles={organizationRolesQuery.data}
canEditMembers={permissions.editMembers}
@@ -1,13 +1,14 @@
import { buildInfo } from "api/queries/buildInfo";
import { provisionerDaemonGroups } from "api/queries/organizations";
import type { Organization } from "api/typesGenerated";
import { EmptyState } from "components/EmptyState/EmptyState";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { useDashboard } from "modules/dashboard/useDashboard";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useQuery } from "react-query";
import { useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView";
const OrganizationProvisionersPage: FC = () => {
@@ -25,12 +26,19 @@ const OrganizationProvisionersPage: FC = () => {
}
return (
<OrganizationProvisionersPageView
showPaywall={!entitlements.features.multiple_organizations.enabled}
error={provisionersQuery.error}
buildInfo={buildInfoQuery.data}
provisioners={provisionersQuery.data}
/>
<>
<Helmet>
<title>
{pageTitle("Members", organization.display_name || organization.name)}
</title>
</Helmet>
<OrganizationProvisionersPageView
showPaywall={!entitlements.features.multiple_organizations.enabled}
error={provisionersQuery.error}
buildInfo={buildInfoQuery.data}
provisioners={provisionersQuery.data}
/>
</>
);
};