Skip to content

Commit 6f9c20c

Browse files
committed
Resolves: MTV-3561 | Refactor StandardPage - Unit tests
Signed-off-by: Jeff Puzzo <[email protected]>
1 parent d4faa79 commit 6f9c20c

File tree

2 files changed

+506
-0
lines changed

2 files changed

+506
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
import { MemoryRouter } from 'react-router-dom-v5-compat';
2+
3+
import type { ResourceField } from '@components/common/utils/types';
4+
import { beforeEach, describe, expect, it } from '@jest/globals';
5+
import { render, screen } from '@testing-library/react';
6+
import userEvent from '@testing-library/user-event';
7+
8+
import StandardPage from '../StandardPage';
9+
10+
const renderWithRouter = (component: React.ReactElement) =>
11+
render(<MemoryRouter>{component}</MemoryRouter>);
12+
13+
describe('StandardPage', () => {
14+
const mockData = [
15+
{ id: '1', name: 'Item 1', status: 'Ready' },
16+
{ id: '2', name: 'Item 2', status: 'NotReady' },
17+
{ id: '3', name: 'Item 3', status: 'Ready' },
18+
];
19+
20+
const fieldsMetadata: ResourceField[] = [
21+
{
22+
resourceFieldId: 'name',
23+
label: 'Name',
24+
isVisible: true,
25+
isIdentity: true,
26+
sortable: true,
27+
},
28+
{
29+
resourceFieldId: 'status',
30+
label: 'Status',
31+
isVisible: true,
32+
isIdentity: false,
33+
filter: {
34+
type: 'enum',
35+
values: [
36+
{ id: 'Ready', label: 'Ready' },
37+
{ id: 'NotReady', label: 'Not Ready' },
38+
],
39+
},
40+
},
41+
];
42+
43+
beforeEach(() => {
44+
window.history.replaceState({}, '', '/');
45+
});
46+
47+
describe('Rendering and Data Display', () => {
48+
it('should render table with data', () => {
49+
renderWithRouter(
50+
<StandardPage
51+
dataSource={[mockData, true, null]}
52+
fieldsMetadata={fieldsMetadata}
53+
namespace="test-ns"
54+
/>,
55+
);
56+
57+
expect(screen.getByText('Item 1')).toBeVisible();
58+
expect(screen.getByText('Item 2')).toBeVisible();
59+
expect(screen.getByText('Item 3')).toBeVisible();
60+
});
61+
62+
it('should show loading state when data is not loaded', () => {
63+
renderWithRouter(
64+
<StandardPage
65+
dataSource={[[], false, null]}
66+
fieldsMetadata={fieldsMetadata}
67+
namespace="test-ns"
68+
/>,
69+
);
70+
71+
expect(screen.getByText('Loading')).toBeVisible();
72+
});
73+
74+
it('should show error state when there is an error', () => {
75+
const error = new Error('Failed to fetch');
76+
renderWithRouter(
77+
<StandardPage
78+
dataSource={[[], true, error]}
79+
fieldsMetadata={fieldsMetadata}
80+
namespace="test-ns"
81+
/>,
82+
);
83+
84+
expect(screen.getByText('Unable to retrieve data')).toBeVisible();
85+
});
86+
87+
it('should show no results message when data is empty', () => {
88+
renderWithRouter(
89+
<StandardPage
90+
dataSource={[[], true, null]}
91+
fieldsMetadata={fieldsMetadata}
92+
namespace="test-ns"
93+
/>,
94+
);
95+
96+
expect(screen.getByText('No results found')).toBeVisible();
97+
});
98+
});
99+
100+
describe('Sorting', () => {
101+
it('should allow sorting by clicking column headers', async () => {
102+
const user = userEvent.setup();
103+
104+
renderWithRouter(
105+
<StandardPage
106+
dataSource={[mockData, true, null]}
107+
fieldsMetadata={fieldsMetadata}
108+
namespace="test-ns"
109+
/>,
110+
);
111+
112+
// Verify sortable column header is rendered as a button
113+
const nameHeader = screen.getByRole('button', { name: /name/i });
114+
expect(nameHeader).toBeInTheDocument();
115+
116+
// Click to sort
117+
await user.click(nameHeader);
118+
119+
// Verify data is still rendered (sorting doesn't break display)
120+
expect(screen.getByText('Item 1')).toBeInTheDocument();
121+
expect(screen.getByText('Item 2')).toBeInTheDocument();
122+
expect(screen.getByText('Item 3')).toBeInTheDocument();
123+
});
124+
});
125+
126+
describe('Filtering', () => {
127+
it('should render filter controls when filter is defined', () => {
128+
renderWithRouter(
129+
<StandardPage
130+
dataSource={[mockData, true, null]}
131+
fieldsMetadata={fieldsMetadata}
132+
namespace="test-ns"
133+
/>,
134+
);
135+
136+
// Verify filter button/toggle is rendered
137+
const filterButton = screen.getByRole('button', { name: /show filters/i });
138+
expect(filterButton).toBeInTheDocument();
139+
140+
// Verify all data is visible without filters
141+
expect(screen.getByText('Item 1')).toBeVisible();
142+
expect(screen.getByText('Item 2')).toBeVisible();
143+
expect(screen.getByText('Item 3')).toBeVisible();
144+
});
145+
146+
it('should show no results message when data is empty after filtering', () => {
147+
// Render with empty data (simulating filtered-out results)
148+
renderWithRouter(
149+
<StandardPage
150+
dataSource={[[], true, null]}
151+
fieldsMetadata={fieldsMetadata}
152+
namespace="test-ns"
153+
/>,
154+
);
155+
156+
expect(screen.getByText(/no results found/i)).toBeVisible();
157+
});
158+
});
159+
160+
describe('Pagination', () => {
161+
const largeDataSet = Array.from({ length: 25 }, (_, i) => ({
162+
id: `${i + 1}`,
163+
name: `Item ${i + 1}`,
164+
status: 'Ready',
165+
}));
166+
167+
it('should render data when pagination is enabled', () => {
168+
renderWithRouter(
169+
<StandardPage
170+
dataSource={[largeDataSet, true, null]}
171+
fieldsMetadata={fieldsMetadata}
172+
namespace="test-ns"
173+
pagination={10}
174+
/>,
175+
);
176+
177+
// Verify data renders correctly with pagination enabled
178+
expect(screen.getByText('Item 1')).toBeVisible();
179+
180+
// Verify not all items are shown at once (only first page)
181+
// With 25 items and pagination=10, we should see first 10
182+
expect(screen.getByText('Item 10')).toBeInTheDocument();
183+
});
184+
185+
it('should not render pagination when data fits on one page', () => {
186+
renderWithRouter(
187+
<StandardPage
188+
dataSource={[mockData, true, null]}
189+
fieldsMetadata={fieldsMetadata}
190+
namespace="test-ns"
191+
pagination={10}
192+
/>,
193+
);
194+
195+
// With only 3 items and pagination=10, pagination should not be shown
196+
expect(screen.queryByText(/of 3/i)).not.toBeInTheDocument();
197+
198+
// Data should still be visible
199+
expect(screen.getByText('Item 1')).toBeVisible();
200+
expect(screen.getByText('Item 3')).toBeVisible();
201+
});
202+
});
203+
204+
describe('Custom Title and Actions', () => {
205+
it('should render custom title', () => {
206+
renderWithRouter(
207+
<StandardPage
208+
dataSource={[mockData, true, null]}
209+
fieldsMetadata={fieldsMetadata}
210+
namespace="test-ns"
211+
title="My Custom Table"
212+
/>,
213+
);
214+
215+
expect(screen.getByText('My Custom Table')).toBeVisible();
216+
});
217+
218+
it('should accept addButton prop without errors', () => {
219+
const addButton = <button>Add New Item</button>;
220+
221+
// Should render without errors when addButton is provided
222+
renderWithRouter(
223+
<StandardPage
224+
dataSource={[mockData, true, null]}
225+
fieldsMetadata={fieldsMetadata}
226+
namespace="test-ns"
227+
addButton={addButton}
228+
/>,
229+
);
230+
231+
// Verify data still renders correctly
232+
expect(screen.getByText('Item 1')).toBeVisible();
233+
expect(screen.getByText('Item 2')).toBeVisible();
234+
});
235+
});
236+
});

0 commit comments

Comments
 (0)