mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
chore: add e2e test for org groups (#15853)
This commit is contained in:
+2
-2
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
@@ -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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user