Skip to content

Commit 46a468f

Browse files
committed
tests
1 parent 840e073 commit 46a468f

File tree

7 files changed

+281
-29
lines changed

7 files changed

+281
-29
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { fireEvent, screen, within } from "@testing-library/react";
2+
import userEvent from "@testing-library/user-event";
3+
import { HttpResponse, http } from "msw";
4+
import type { SlimRole } from "api/typesGenerated";
5+
import {
6+
MockUser,
7+
MockUser2,
8+
MockOrganizationAuditorRole,
9+
} from "testHelpers/entities";
10+
import {
11+
renderWithTemplateSettingsLayout,
12+
waitForLoaderToBeRemoved,
13+
} from "testHelpers/renderHelpers";
14+
import { server } from "testHelpers/server";
15+
import OrganizationMembersPage from "./OrganizationMembersPage";
16+
17+
jest.spyOn(console, "error").mockImplementation(() => {});
18+
19+
const renderPage = async () => {
20+
renderWithTemplateSettingsLayout(<OrganizationMembersPage />, {
21+
route: `/organizations/my-organization/members`,
22+
path: `/organizations/:organization/members`,
23+
});
24+
await waitForLoaderToBeRemoved();
25+
};
26+
27+
const removeMember = async () => {
28+
const user = userEvent.setup();
29+
// Click on the "More options" button to display the "Remove" option
30+
const moreButtons = await screen.findAllByLabelText("More options");
31+
// get MockUser2
32+
const selectedMoreButton = moreButtons[0];
33+
34+
await user.click(selectedMoreButton);
35+
36+
const removeButton = screen.getByText(/Remove/);
37+
await user.click(removeButton);
38+
};
39+
40+
const updateUserRole = async (role: SlimRole) => {
41+
// Get the first user in the table
42+
const users = await screen.findAllByText(/.*@coder.com/);
43+
const userRow = users[0].closest("tr");
44+
if (!userRow) {
45+
throw new Error("Error on get the first user row");
46+
}
47+
48+
// Click on the "edit icon" to display the role options
49+
const editButton = within(userRow).getByTitle("Edit user roles");
50+
fireEvent.click(editButton);
51+
52+
// Click on the role option
53+
const fieldset = await screen.findByTitle("Available roles");
54+
const roleOption = within(fieldset).getByText(role.display_name);
55+
fireEvent.click(roleOption);
56+
57+
return {
58+
userRow,
59+
};
60+
};
61+
62+
describe("OrganizationMembersPage", () => {
63+
describe("remove member", () => {
64+
describe("when it is success", () => {
65+
it("shows a success message", async () => {
66+
server.use(
67+
http.delete(
68+
`/api/v2/organizations/:organizationId/members/${MockUser2.id}`,
69+
async () => {
70+
return new HttpResponse(null, { status: 204 });
71+
},
72+
),
73+
);
74+
75+
await renderPage();
76+
await removeMember();
77+
await screen.findByText("Member removed.");
78+
});
79+
});
80+
});
81+
82+
describe("Update user role", () => {
83+
describe("when it is success", () => {
84+
it("updates the roles", async () => {
85+
server.use(
86+
http.put(
87+
`/api/v2/organizations/:organizationId/members/${MockUser.id}/roles`,
88+
async () => {
89+
return HttpResponse.json({
90+
...MockUser,
91+
roles: [...MockUser.roles, MockOrganizationAuditorRole],
92+
});
93+
},
94+
),
95+
);
96+
97+
await renderPage();
98+
await updateUserRole(MockOrganizationAuditorRole);
99+
await screen.findByText("Roles updated successfully.");
100+
});
101+
});
102+
103+
describe("when it fails", () => {
104+
it("shows an error message", async () => {
105+
server.use(
106+
http.put(
107+
`/api/v2/organizations/:organizationId/members/${MockUser.id}/roles`,
108+
() => {
109+
return HttpResponse.json(
110+
{ message: "Error on updating the user roles." },
111+
{ status: 400 },
112+
);
113+
},
114+
),
115+
);
116+
117+
await renderPage();
118+
await updateUserRole(MockOrganizationAuditorRole);
119+
await screen.findByText("Error on updating the user roles.");
120+
});
121+
});
122+
});
123+
});

site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx

+14-4
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,20 @@ const OrganizationMembersPage: FC = () => {
136136
<MoreMenuItem
137137
danger
138138
onClick={async () => {
139-
await removeMemberMutation.mutateAsync(
140-
member.user_id,
141-
);
142-
void membersQuery.refetch();
139+
try {
140+
await removeMemberMutation.mutateAsync(
141+
member.user_id,
142+
);
143+
void membersQuery.refetch();
144+
displaySuccess("Member removed.");
145+
} catch (e) {
146+
displayError(
147+
getErrorMessage(
148+
e,
149+
"Failed to remove member.",
150+
),
151+
);
152+
}
143153
}}
144154
>
145155
Remove

site/src/pages/UsersPage/UsersPage.test.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import {
1212
import { renderWithAuth } from "testHelpers/renderHelpers";
1313
import { server } from "testHelpers/server";
1414
import { Language as ResetPasswordDialogLanguage } from "./ResetPasswordDialog";
15-
import { UsersPage } from "./UsersPage";
15+
import UsersPage from "./UsersPage";
16+
17+
jest.spyOn(console, "error").mockImplementation(() => {});
1618

1719
const renderPage = () => {
1820
return renderWithAuth(<UsersPage />);
@@ -116,16 +118,14 @@ const updateUserRole = async (role: SlimRole) => {
116118

117119
// Click on the role option
118120
const fieldset = await screen.findByTitle("Available roles");
119-
const auditorOption = within(fieldset).getByText(role.display_name);
120-
fireEvent.click(auditorOption);
121+
const roleOption = within(fieldset).getByText(role.display_name);
122+
fireEvent.click(roleOption);
121123

122124
return {
123125
userRow,
124126
};
125127
};
126128

127-
jest.spyOn(console, "error").mockImplementation(() => {});
128-
129129
describe("UsersPage", () => {
130130
describe("suspend user", () => {
131131
describe("when it is success", () => {

site/src/pages/UsersPage/UsersPage.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { ResetPasswordDialog } from "./ResetPasswordDialog";
3030
import { useStatusFilterMenu } from "./UsersFilter";
3131
import { UsersPageView } from "./UsersPageView";
3232

33-
export const UsersPage: FC = () => {
33+
const UsersPage: FC = () => {
3434
const queryClient = useQueryClient();
3535
const navigate = useNavigate();
3636

site/src/testHelpers/entities.ts

+81-18
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { FileTree } from "utils/filetree";
1212
import type { TemplateVersionFiles } from "utils/templateVersion";
1313

1414
export const MockOrganization: TypesGen.Organization = {
15-
id: "fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0",
15+
id: "my-organization-id",
1616
name: "my-organization",
1717
display_name: "My Organization",
1818
description: "An organization that gets used for stuff.",
@@ -27,6 +27,17 @@ export const MockDefaultOrganization: TypesGen.Organization = {
2727
is_default: true,
2828
};
2929

30+
export const MockOrganization2: TypesGen.Organization = {
31+
id: "my-organization-2-id",
32+
name: "my-organization-2",
33+
display_name: "My Organization 2",
34+
description: "Another organization that gets used for stuff.",
35+
icon: "/emojis/1f957.png",
36+
created_at: "",
37+
updated_at: "",
38+
is_default: false,
39+
};
40+
3041
export const MockTemplateDAUResponse: TypesGen.DAUsResponse = {
3142
tz_hour_offset: 0,
3243
entries: [
@@ -265,18 +276,54 @@ export const MockTemplateAdminRole: TypesGen.Role = {
265276
organization_id: "",
266277
};
267278

279+
export const MockAuditorRole: TypesGen.Role = {
280+
name: "auditor",
281+
display_name: "Auditor",
282+
site_permissions: [],
283+
organization_permissions: [],
284+
user_permissions: [],
285+
organization_id: "",
286+
};
287+
268288
export const MockMemberRole: TypesGen.SlimRole = {
269289
name: "member",
270290
display_name: "Member",
271291
};
272292

273-
export const MockAuditorRole: TypesGen.Role = {
274-
name: "auditor",
275-
display_name: "Auditor",
293+
export const MockOrganizationAdminRole: TypesGen.Role = {
294+
name: "organization-admin",
295+
display_name: "Organization Admin",
276296
site_permissions: [],
277297
organization_permissions: [],
278298
user_permissions: [],
279-
organization_id: "",
299+
organization_id: MockOrganization.id,
300+
};
301+
302+
export const MockOrganizationUserAdminRole: TypesGen.Role = {
303+
name: "organization-user-admin",
304+
display_name: "Organization User Admin",
305+
site_permissions: [],
306+
organization_permissions: [],
307+
user_permissions: [],
308+
organization_id: MockOrganization.id,
309+
};
310+
311+
export const MockOrganizationTemplateAdminRole: TypesGen.Role = {
312+
name: "organization-template-admin",
313+
display_name: "Organization Template Admin",
314+
site_permissions: [],
315+
organization_permissions: [],
316+
user_permissions: [],
317+
organization_id: MockOrganization.id,
318+
};
319+
320+
export const MockOrganizationAuditorRole: TypesGen.Role = {
321+
name: "organization-auditor",
322+
display_name: "Organization Auditor",
323+
site_permissions: [],
324+
organization_permissions: [],
325+
user_permissions: [],
326+
organization_id: MockOrganization.id,
280327
};
281328

282329
// assignableRole takes a role and a boolean. The boolean implies if the
@@ -319,19 +366,8 @@ export const MockUser: TypesGen.User = {
319366
};
320367

321368
export const MockUserAdmin: TypesGen.User = {
322-
id: "test-user",
323-
username: "TestUser",
324-
email: "test@coder.com",
325-
created_at: "",
326-
updated_at: "",
327-
status: "active",
328-
organization_ids: [MockOrganization.id],
369+
...MockUser,
329370
roles: [MockUserAdminRole],
330-
avatar_url: "",
331-
last_seen_at: "",
332-
login_type: "password",
333-
theme_preference: "",
334-
name: "",
335371
};
336372

337373
export const MockUser2: TypesGen.User = {
@@ -366,6 +402,33 @@ export const SuspendedMockUser: TypesGen.User = {
366402
name: "",
367403
};
368404

405+
export const MockOrganizationMember: TypesGen.OrganizationMemberWithUserData = {
406+
organization_id: MockOrganization.id,
407+
user_id: MockUser.id,
408+
username: MockUser.username,
409+
email: MockUser.email,
410+
created_at: "",
411+
updated_at: "",
412+
name: MockUser.name,
413+
avatar_url: MockUser.avatar_url,
414+
global_roles: MockUser.roles,
415+
roles: [],
416+
};
417+
418+
export const MockOrganizationMember2: TypesGen.OrganizationMemberWithUserData =
419+
{
420+
organization_id: MockOrganization.id,
421+
user_id: MockUser2.id,
422+
username: MockUser2.username,
423+
email: MockUser2.email,
424+
created_at: "",
425+
updated_at: "",
426+
name: MockUser2.name,
427+
avatar_url: MockUser2.avatar_url,
428+
global_roles: MockUser2.roles,
429+
roles: [],
430+
};
431+
369432
export const MockProvisioner: TypesGen.ProvisionerDaemon = {
370433
created_at: "2022-05-17T17:39:01.382927298Z",
371434
id: "test-provisioner",
@@ -2146,7 +2209,7 @@ export const MockEntitlementsWithUserLimit: TypesGen.Entitlements = {
21462209
}),
21472210
};
21482211

2149-
export const MockExperiments: TypesGen.Experiment[] = [];
2212+
export const MockExperiments: TypesGen.Experiment[] = ["multi-organization"];
21502213

21512214
export const MockAuditLog: TypesGen.AuditLog = {
21522215
id: "fbd2116a-8961-4954-87ae-e4575bd29ce0",

site/src/testHelpers/handlers.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@ export const handlers = [
4141
}),
4242

4343
// organizations
44+
http.get("/api/v2/organizations", () => {
45+
console.log(" fuck and piss ================================== ");
46+
return HttpResponse.json([M.MockDefaultOrganization, M.MockOrganization2]);
47+
}),
4448
http.get("/api/v2/organizations/:organizationId", () => {
4549
return HttpResponse.json(M.MockOrganization);
4650
}),
47-
http.get("api/v2/organizations/:organizationId/templates/examples", () => {
51+
http.get("/api/v2/organizations/:organizationId/templates/examples", () => {
4852
return HttpResponse.json([M.MockTemplateExample, M.MockTemplateExample2]);
4953
}),
5054
http.get(
@@ -56,6 +60,20 @@ export const handlers = [
5660
http.get("/api/v2/organizations/:organizationId/templates", () => {
5761
return HttpResponse.json([M.MockTemplate]);
5862
}),
63+
http.get("/api/v2/organizations/:organizationId/members/roles", () => {
64+
return HttpResponse.json([
65+
M.MockOrganizationAdminRole,
66+
M.MockOrganizationUserAdminRole,
67+
M.MockOrganizationTemplateAdminRole,
68+
M.MockOrganizationAuditorRole,
69+
]);
70+
}),
71+
http.get("/api/v2/organizations/:organizationId/members", () => {
72+
return HttpResponse.json([
73+
M.MockOrganizationMember,
74+
M.MockOrganizationMember2,
75+
]);
76+
}),
5977

6078
// templates
6179
http.get("/api/v2/templates/:templateId", () => {

0 commit comments

Comments
 (0)