Skip to content

Commit 04e25de

Browse files
committed
Merge branch 'main' into feature/images_dataset
2 parents 2a7051d + a631bc8 commit 04e25de

11 files changed

Lines changed: 1154 additions & 481 deletions
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
import { render, screen, waitFor } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
4+
import { DatasetListPage } from './dataset-list-page';
5+
import * as actions from '@/actions';
6+
import { MemoryRouter, useLocation, useNavigate } from 'react-router-dom';
7+
import { TestProvider } from '@/test/helpers/test-provider';
8+
9+
vi.mock('@/actions');
10+
vi.mock('react-router-dom', async () => {
11+
const originalModule = await vi.importActual('react-router-dom');
12+
return {
13+
...originalModule,
14+
useNavigate: vi.fn(),
15+
useLocation: vi.fn(),
16+
};
17+
});
18+
19+
import * as ActualToastHook from '@/hooks/use-toast';
20+
vi.mock('@/hooks/use-toast', async (importOriginal) => {
21+
const actual = await importOriginal<typeof ActualToastHook>();
22+
return {
23+
...actual,
24+
useToast: vi.fn(() => ({
25+
toast: vi.fn(),
26+
})),
27+
};
28+
});
29+
30+
31+
describe('DatasetListPage Search Functionality', () => {
32+
const sampleDatasets = [
33+
{ id: 'ds1', name: 'Dataset Alpha', description: 'First one', type: 'csv' },
34+
{ id: 'ds2', name: 'Dataset Beta', description: 'Second one', type: 'list' },
35+
{ id: 'ds3', name: 'Gamma Dataset', description: 'Third one', type: 'csv' },
36+
];
37+
38+
beforeEach(async () => {
39+
vi.resetAllMocks();
40+
41+
(actions.getDatasets as Mock).mockResolvedValue({
42+
datasets: sampleDatasets,
43+
});
44+
(actions.deleteDataset as Mock).mockResolvedValue({});
45+
(actions.createDataset as Mock).mockResolvedValue({});
46+
(actions.updateDataset as Mock).mockResolvedValue({});
47+
48+
49+
(useNavigate as Mock).mockReturnValue(vi.fn());
50+
(useLocation as Mock).mockReturnValue({
51+
key: 'testKey',
52+
pathname: '/datasets',
53+
search: '',
54+
hash: '',
55+
state: null,
56+
});
57+
58+
render(
59+
<MemoryRouter>
60+
<TestProvider>
61+
<DatasetListPage />
62+
</TestProvider>
63+
</MemoryRouter>
64+
);
65+
66+
await waitFor(() => expect(screen.getByText('Dataset Alpha')).toBeInTheDocument());
67+
await waitFor(() => expect(screen.getByText('Dataset Beta')).toBeInTheDocument());
68+
await waitFor(() => expect(screen.getByText('Gamma Dataset')).toBeInTheDocument());
69+
});
70+
71+
it('should render all datasets initially', () => {
72+
expect(screen.getByText('Dataset Alpha')).toBeInTheDocument();
73+
expect(screen.getByText('Dataset Beta')).toBeInTheDocument();
74+
expect(screen.getByText('Gamma Dataset')).toBeInTheDocument();
75+
});
76+
77+
it('should filter datasets based on search query', async () => {
78+
const searchInput = screen.getByPlaceholderText('Search datasets...');
79+
await userEvent.type(searchInput, 'Alpha');
80+
81+
await waitFor(() => {
82+
expect(screen.getByText('Dataset Alpha')).toBeInTheDocument();
83+
expect(screen.queryByText('Dataset Beta')).not.toBeInTheDocument();
84+
expect(screen.queryByText('Gamma Dataset')).not.toBeInTheDocument();
85+
});
86+
});
87+
88+
it('should be case-insensitive', async () => {
89+
const searchInput = screen.getByPlaceholderText('Search datasets...');
90+
await userEvent.type(searchInput, 'gamma');
91+
92+
await waitFor(() => {
93+
expect(screen.queryByText('Dataset Alpha')).not.toBeInTheDocument();
94+
expect(screen.queryByText('Dataset Beta')).not.toBeInTheDocument();
95+
expect(screen.getByText('Gamma Dataset')).toBeInTheDocument();
96+
});
97+
});
98+
99+
it('should show no results message if search matches nothing', async () => {
100+
const searchInput = screen.getByPlaceholderText('Search datasets...');
101+
await userEvent.type(searchInput, 'NonExistentDataset');
102+
103+
// The component currently does not implement a specific "no results" message.
104+
// It would render an empty list.
105+
await waitFor(() => {
106+
expect(screen.queryByText('Dataset Alpha')).not.toBeInTheDocument();
107+
expect(screen.queryByText('Dataset Beta')).not.toBeInTheDocument();
108+
expect(screen.queryByText('Gamma Dataset')).not.toBeInTheDocument();
109+
});
110+
// If a "No datasets found..." message were implemented, we'd assert its presence here.
111+
});
112+
113+
it('should show all datasets when search query is cleared', async () => {
114+
const searchInput = screen.getByPlaceholderText('Search datasets...');
115+
await userEvent.type(searchInput, 'Alpha');
116+
117+
await waitFor(() => expect(screen.getByText('Dataset Alpha')).toBeInTheDocument());
118+
await waitFor(() => expect(screen.queryByText('Dataset Beta')).not.toBeInTheDocument());
119+
120+
await userEvent.clear(searchInput);
121+
122+
await waitFor(() => {
123+
expect(screen.getByText('Dataset Alpha')).toBeInTheDocument();
124+
expect(screen.getByText('Dataset Beta')).toBeInTheDocument();
125+
expect(screen.getByText('Gamma Dataset')).toBeInTheDocument();
126+
});
127+
});
128+
});
129+
130+
vi.mock('@/components/dialog/dataset/dataset', () => ({
131+
CreateDatasetDialog: vi.fn(({ isOpen, onClose, onCreate, dataset }: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
132+
if (!isOpen) return null;
133+
return (
134+
<div data-testid="mock-create-dataset-dialog">
135+
<button
136+
data-testid="mock-create-dataset-submit"
137+
onClick={() => {
138+
const type = dataset?.type || 'csv';
139+
onCreate({
140+
name: dataset?.name || 'New Mocked Dataset',
141+
description: dataset?.description || 'Mocked description',
142+
type: type,
143+
files: type === 'csv' ? [new File([''], 'mock.csv', { type: 'text/csv' })] : undefined,
144+
options: type === 'list' ? ['opt1'] : undefined,
145+
});
146+
}}
147+
>
148+
Create/Update
149+
</button>
150+
<button data-testid="mock-create-dataset-close" onClick={onClose}>Close</button>
151+
</div>
152+
);
153+
}),
154+
}));
155+
vi.mock('@/components/dialog/dataset/info', () => ({
156+
DatasetInfoDialog: vi.fn(() => <div data-testid="mock-dataset-info-dialog" />),
157+
}));
158+
vi.mock('@/components/dialog/dataset/preview', () => ({
159+
DatasetPreviewDialog: vi.fn(() => <div data-testid="mock-dataset-preview-dialog" />),
160+
}));
161+
162+
vi.mock('@/components/ui/common-card', () => ({
163+
CommonCard: vi.fn(({ name, children, onDelete, onEdit, onClick, badgeText }: {
164+
name: string;
165+
children: React.ReactNode;
166+
onDelete?: () => void;
167+
onEdit?: () => void;
168+
onClick: () => void;
169+
badgeText?: string;
170+
}) => (
171+
<div data-testid={`common-card-${name.replace(/\s+/g, "-")}`}>
172+
<button onClick={onClick} data-testid={`view-${name.replace(/\s+/g, "-")}`}>{name}</button>
173+
<div>{children}</div>
174+
{badgeText && <span>{badgeText}</span>}
175+
{onDelete && <button onClick={onDelete} data-testid={`delete-${name.replace(/\s+/g, "-")}`}>Delete</button>}
176+
{onEdit && <button onClick={onEdit} data-testid={`edit-${name.replace(/\s+/g, "-")}`}>Edit</button>}
177+
</div>
178+
)),
179+
}));
180+
181+
182+
describe('DatasetListPage Create, Delete and Refresh', () => {
183+
const initialDatasets = [
184+
{ id: '1', name: 'Dataset Alpha', description: 'First one', type: 'csv' as const },
185+
{ id: '2', name: 'Dataset Beta', description: 'Second one', type: 'list' as const },
186+
];
187+
188+
const newDataset = { id: '3', name: 'New Mocked Dataset', description: 'Mocked description', type: 'csv' as const };
189+
190+
const mockGetDatasets = actions.getDatasets as Mock;
191+
const mockCreateDataset = actions.createDataset as Mock;
192+
const mockDeleteDataset = actions.deleteDataset as Mock;
193+
194+
beforeEach(() => {
195+
vi.resetAllMocks();
196+
(useNavigate as Mock).mockReturnValue(vi.fn());
197+
(useLocation as Mock).mockReturnValue({
198+
key: 'testKeyCUD',
199+
pathname: '/datasets',
200+
search: '',
201+
hash: '',
202+
state: null,
203+
});
204+
205+
mockCreateDataset.mockResolvedValue({ ...newDataset });
206+
mockDeleteDataset.mockResolvedValue(undefined);
207+
});
208+
209+
it('should refresh the list after creating a new dataset', async () => {
210+
mockGetDatasets
211+
.mockResolvedValueOnce({ datasets: initialDatasets })
212+
.mockResolvedValueOnce({ datasets: [...initialDatasets, newDataset] });
213+
214+
render(
215+
<MemoryRouter>
216+
<TestProvider>
217+
<DatasetListPage />
218+
</TestProvider>
219+
</MemoryRouter>
220+
);
221+
222+
await waitFor(() => expect(screen.getByText('Dataset Alpha')).toBeInTheDocument());
223+
expect(screen.getByText('Dataset Beta')).toBeInTheDocument();
224+
225+
const addNewButton = screen.getByRole('button', { name: /Add New Dataset/i });
226+
await userEvent.click(addNewButton);
227+
228+
await waitFor(() => expect(screen.getByTestId('mock-create-dataset-dialog')).toBeInTheDocument());
229+
230+
const submitButton = screen.getByTestId('mock-create-dataset-submit');
231+
await userEvent.click(submitButton);
232+
233+
await waitFor(() => expect(screen.getByText('New Mocked Dataset')).toBeInTheDocument(), { timeout: 2000 });
234+
235+
expect(mockCreateDataset).toHaveBeenCalledTimes(1);
236+
await waitFor(() => expect(mockGetDatasets).toHaveBeenCalledTimes(2)); // Initial load + load after create
237+
});
238+
239+
it('should refresh the list after deleting a dataset', async () => {
240+
mockGetDatasets
241+
.mockResolvedValueOnce({ datasets: initialDatasets })
242+
.mockResolvedValueOnce({ datasets: [initialDatasets[1]] }); // Dataset Alpha (id: '1') is removed
243+
244+
render(
245+
<MemoryRouter>
246+
<TestProvider>
247+
<DatasetListPage />
248+
</TestProvider>
249+
</MemoryRouter>
250+
);
251+
252+
await waitFor(() => expect(screen.getByText('Dataset Alpha')).toBeInTheDocument());
253+
expect(screen.getByText('Dataset Beta')).toBeInTheDocument();
254+
255+
const deleteButtonAlpha = screen.getByTestId('delete-Dataset-Alpha');
256+
await userEvent.click(deleteButtonAlpha);
257+
// Note: Mocked CommonCard's onDelete is called directly, no confirmation dialog step here.
258+
259+
await waitFor(() => expect(screen.queryByText('Dataset Alpha')).not.toBeInTheDocument(), { timeout: 2000 });
260+
expect(screen.getByText('Dataset Beta')).toBeInTheDocument();
261+
262+
expect(mockDeleteDataset).toHaveBeenCalledWith('1');
263+
await waitFor(() => expect(mockGetDatasets).toHaveBeenCalledTimes(2)); // Initial load + load after delete
264+
});
265+
});

0 commit comments

Comments
 (0)