Skip to content

chore: add e2e test for org groups #15853

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 3 commits into from
Dec 13, 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
4 changes: 2 additions & 2 deletions site/e2e/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ 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`,
username: name,
name: name,
password: "s3cure&password!",
login_type: "password",
organization_ids: [orgId],
organization_ids: orgIds,
user_status: null,
});
return user;
Expand Down
22 changes: 22 additions & 0 deletions site/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 103 additions & 0 deletions site/e2e/tests/organizationGroups.spec.ts
Original file line number Diff line number Diff line change
@@ -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");
});
16 changes: 4 additions & 12 deletions site/e2e/tests/organizationMembers.spec.ts
Original file line number Diff line number Diff line change
@@ -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 }) => {
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 12 additions & 2 deletions site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand Down Expand Up @@ -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] =
Expand All @@ -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}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = () => {
Expand All @@ -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}
/>
</>
);
};

Expand Down
Loading