Skip to content

Commit 49149ff

Browse files
Merge branch 'main' into jb/session_mgt_logout_modal
2 parents a6aa880 + b2d8542 commit 49149ff

File tree

5 files changed

+668
-0
lines changed

5 files changed

+668
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { screen, within, waitFor } from "@testing-library/react";
2+
import AuditLogs from "./page";
3+
import { renderWithUser, RootProviderMock } from "@/app/tests/unit/setup";
4+
import userEvent from "@testing-library/user-event";
5+
6+
const TEST_NAME = "Rocky Balboa";
7+
const TEST_REPORT = "Created Report";
8+
9+
/**
10+
* Creates an enhanced user event with custom helper methods.
11+
* @param user - The user event from renderWithUser
12+
* @returns - The enhanced user event object with custom helpers
13+
*/
14+
const createUserWithHelpers = (user: ReturnType<typeof userEvent.setup>) => ({
15+
...user,
16+
17+
/**
18+
* Selects an option in a dropdown.
19+
* @param label - The label of the dropdown
20+
* @param value - The value of the option to select
21+
*/
22+
async selectDropdownOption(label: string, value: string) {
23+
const select = screen.getByLabelText(label);
24+
await user.selectOptions(select, value);
25+
},
26+
27+
/**
28+
* Inputs text in a field.
29+
* @param placeholder - The placeholder of the input field
30+
* @param text - The text to input
31+
*/
32+
async typeInField(placeholder: string, text: string) {
33+
const input = screen.getByPlaceholderText(placeholder);
34+
await user.type(input, text);
35+
},
36+
});
37+
38+
describe("AuditLogs Component", () => {
39+
let user: ReturnType<typeof createUserWithHelpers>;
40+
41+
beforeEach(() => {
42+
const renderResult = renderWithUser(
43+
<RootProviderMock currentPage="/auditLogs">
44+
<AuditLogs />
45+
</RootProviderMock>,
46+
);
47+
user = createUserWithHelpers(renderResult.user);
48+
});
49+
50+
test("renders the audit logs table", async () => {
51+
await waitFor(() => expect(screen.getByRole("table")).toBeInTheDocument());
52+
});
53+
54+
test("filters by name and clears filter", async () => {
55+
await user.selectDropdownOption("Name(s)", TEST_NAME);
56+
57+
const rows = await screen.findAllByRole("row");
58+
expect(rows.length).toBeGreaterThan(1);
59+
rows.slice(1).forEach((row) => {
60+
expect(within(row).getByText(TEST_NAME)).toBeInTheDocument();
61+
});
62+
63+
await user.selectDropdownOption("Name(s)", "");
64+
});
65+
66+
test("filters by action and clears filter", async () => {
67+
await user.selectDropdownOption("Action(s)", TEST_REPORT);
68+
69+
const rows = await screen.findAllByRole("row");
70+
expect(rows.length).toBeGreaterThan(1);
71+
rows.slice(1).forEach((row) => {
72+
expect(within(row).getByText(TEST_REPORT)).toBeInTheDocument();
73+
});
74+
75+
await user.selectDropdownOption("Action(s)", "");
76+
});
77+
78+
test("filters by partial search", async () => {
79+
await user.typeInField("Search name or action", "Apollo");
80+
81+
const rows = await screen.findAllByRole("row");
82+
expect(rows.length).toBeGreaterThan(1);
83+
rows.slice(1).forEach((row) => {
84+
expect(within(row).getByText(/Apollo/i)).toBeInTheDocument();
85+
});
86+
});
87+
88+
test("pagination updates displayed logs", async () => {
89+
const nextButton = screen.getByLabelText("Next page");
90+
expect(nextButton).toBeInTheDocument();
91+
await user.click(nextButton);
92+
93+
await waitFor(() => {
94+
const pageNumbers = screen.getAllByRole("button", { name: /Page/ });
95+
expect(pageNumbers[1]).toHaveAttribute("aria-current", "page");
96+
});
97+
});
98+
99+
test("pagination allows clicking previous only if it exists", async () => {
100+
const nextButton = screen.getByLabelText("Next page");
101+
expect(nextButton).toBeInTheDocument();
102+
await user.click(nextButton);
103+
104+
// Ensure previous button appears after going to the second page
105+
await waitFor(async () => {
106+
const prevButton = screen.getByLabelText("Previous page");
107+
expect(prevButton).toBeInTheDocument();
108+
await user.click(prevButton);
109+
});
110+
});
111+
112+
test("pagination allows clicking a specific page number", async () => {
113+
const pageTwoButton = screen.getByRole("button", { name: "Page 2" });
114+
115+
await user.click(pageTwoButton);
116+
117+
await waitFor(() => {
118+
expect(pageTwoButton).toHaveAttribute("aria-current", "page");
119+
});
120+
});
121+
122+
test("changing actions per page updates table", async () => {
123+
await user.selectDropdownOption("Actions per page", "25");
124+
125+
await waitFor(() => {
126+
const rows = screen.getAllByRole("row");
127+
expect(rows.length).toBe(26); // 25 logs + 1 header row
128+
});
129+
});
130+
131+
test("clear filters resets empty state", async () => {
132+
await user.selectDropdownOption("Name(s)", "Apollo Creed");
133+
await user.selectDropdownOption("Action(s)", "Created Report");
134+
135+
await waitFor(() => {
136+
expect(
137+
screen.queryByRole("button", { name: "Clear filters" }),
138+
).toBeInTheDocument();
139+
});
140+
141+
const clearFiltersButton = screen.getByRole("button", {
142+
name: "Clear filters",
143+
});
144+
await user.click(clearFiltersButton);
145+
146+
await waitFor(() => {
147+
expect(screen.getByRole("table")).toBeInTheDocument();
148+
});
149+
});
150+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
.mainContainerWider {
2+
max-width: 74rem;
3+
width: 100%;
4+
margin: auto;
5+
margin-bottom: 1.5rem;
6+
margin-top: 1.5rem;
7+
padding: 0 !important;
8+
flex: 1 1 auto;
9+
display: flex;
10+
flex-direction: column;
11+
position: relative;
12+
}
13+
14+
.auditTableContainer {
15+
flex-grow: 1;
16+
justify-content: space-between;
17+
align-items: center;
18+
width: 70rem;
19+
padding-bottom: 1.5rem;
20+
}
21+
22+
.errorText {
23+
color: #d73a49;
24+
font-size: 0.875rem;
25+
margin-top: 0.25rem;
26+
position: fixed;
27+
width: 100%;
28+
top: 27%;
29+
}
30+
31+
.bgErrorLighter {
32+
background-color: #fce8e8;
33+
}
34+
35+
.borderError {
36+
border: 2px solid #d73a49;
37+
}
38+
39+
.searchContainer {
40+
display: flex;
41+
width: 70rem;
42+
align-items: center;
43+
gap: 0.5rem;
44+
}
45+
46+
.inputGroup {
47+
display: flex;
48+
width: 13.0625rem;
49+
flex-direction: column;
50+
align-items: flex-start;
51+
}
52+
53+
54+
.searchField {
55+
display: flex;
56+
width: 25rem;
57+
padding: 0.75rem;
58+
align-items: center;
59+
gap: 0.5rem;
60+
margin-top: 1.75rem;
61+
margin-left: 4.25rem;
62+
background-color: white !important;
63+
}
64+
65+
66+
67+
.tableHeader:first-child {
68+
width: 13rem;
69+
}
70+
71+
.tableHeader:nth-child(2) {
72+
width: 41.5rem;
73+
}
74+
75+
.tableHeader:nth-child(3) {
76+
width: 12.5rem;
77+
78+
}
79+
80+
.tableRows {
81+
height: 4.25rem;
82+
white-space: ellipsis;
83+
}
84+
85+
.tableRows td:nth-child(3) {
86+
white-space: nowrap;
87+
}
88+
89+
90+
.paginationContainerWrapper {
91+
display: flex;
92+
justify-content: center;
93+
width: 100%;
94+
position: absolute;
95+
bottom: 1.5rem;
96+
left: 0;
97+
background: var(--white, #FFF);
98+
padding: 1rem 0;
99+
z-index: 10;
100+
}
101+
102+
.paginationContainer {
103+
display: flex;
104+
width: 100%;
105+
padding: 1rem 1.5rem;
106+
justify-content: space-between;
107+
align-items: center;
108+
border-radius: 0.5rem;
109+
border: 1px solid var(--gray-400, #DFE1E2);
110+
background: var(--white, #FFF);
111+
margin-left: -2rem;
112+
}
113+
114+
.actionsPerPageContainer {
115+
display: flex;
116+
align-items: center;
117+
gap: 0.5rem;
118+
min-width: 0;
119+
}
120+
121+
.actionsPerPageContainer label {
122+
white-space: nowrap;
123+
flex-shrink: 0;
124+
}
125+
126+
.actionsPerPageDropdown {
127+
display: flex;
128+
width: 5.875rem;
129+
flex-direction: column;
130+
align-items: flex-start;
131+
gap: 0.5rem;
132+
border-radius: 0.25rem;
133+
margin-bottom: 0.5rem;
134+
}
135+
136+
.noResultsContainer {
137+
display: flex;
138+
justify-content: center;
139+
background: var(--blue-100, #E8F5FF); padding: 2rem;
140+
border-radius: 0.5rem;
141+
text-align: center;
142+
padding: 7.5rem 0rem;
143+
flex-direction: column;
144+
align-items: center;
145+
gap: 2.5rem;
146+
align-self: stretch;
147+
}

0 commit comments

Comments
 (0)