Skip to content

Commit 70d5867

Browse files
committed
add test for NewFileOrFolder
1 parent df742de commit 70d5867

File tree

2 files changed

+182
-4
lines changed

2 files changed

+182
-4
lines changed

frontend/pages/sites/$siteId/storage/NewFileOrFolder.jsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ const NewFileOrFolder = ({ onUpload, onCreateFolder }) => {
1111
const [errorMessage, setErrorMessage] = useState('');
1212

1313
const handleCreateFolder = async () => {
14-
if (!folderName.trim()) {
15-
setErrorMessage('Folder name cannot be empty.');
14+
const trimmedName = folderName.trim();
15+
16+
if (trimmedName.length < 1) {
1617
return;
1718
}
1819

1920
setCreatingFolder(true);
2021
setErrorMessage('');
2122

2223
try {
23-
await onCreateFolder(folderName);
24+
await onCreateFolder(trimmedName);
2425
setFolderName('');
2526
setShowFolderNameField(false);
2627
} catch (error) {
@@ -50,11 +51,12 @@ const NewFileOrFolder = ({ onUpload, onCreateFolder }) => {
5051
disabled={creatingFolder}
5152
className="usa-input grid-col flex-fill margin-top-0"
5253
/>
54+
5355
<button
5456
type="button"
5557
className="usa-button grid-col flex-auto margin-left-1"
5658
onClick={handleCreateFolder}
57-
disabled={creatingFolder || folderName.length < 1}
59+
disabled={creatingFolder || folderName.trim().length < 1}
5860
>
5961
Create folder
6062
</button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import React from 'react';
2+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
4+
import userEvent from '@testing-library/user-event';
5+
import NewFileOrFolder from './NewFileOrFolder';
6+
import prettyBytes from 'pretty-bytes';
7+
8+
jest.mock('pretty-bytes', () => jest.fn());
9+
const mockOnUpload = jest.fn();
10+
const mockOnCreateFolder = jest.fn();
11+
12+
const renderComponent = (props = {}) => {
13+
return render(
14+
<NewFileOrFolder
15+
onUpload={mockOnUpload}
16+
onCreateFolder={mockOnCreateFolder}
17+
{...props}
18+
/>,
19+
);
20+
};
21+
22+
describe('NewFileOrFolder Component', () => {
23+
beforeEach(() => {
24+
jest.clearAllMocks();
25+
jest.restoreAllMocks();
26+
prettyBytes.mockReturnValue('4 KB');
27+
});
28+
29+
afterEach(() => {
30+
jest.restoreAllMocks();
31+
});
32+
33+
test('renders initial state with "Upload files" and "New folder" buttons', () => {
34+
renderComponent();
35+
expect(screen.getByRole('button', { name: 'Upload files' })).toBeInTheDocument();
36+
expect(screen.getByRole('button', { name: 'New folder' })).toBeInTheDocument();
37+
});
38+
39+
test('shows file upload UI when "Upload files" is clicked', async () => {
40+
renderComponent();
41+
42+
fireEvent.click(screen.getByRole('button', { name: 'Upload files' }));
43+
44+
await waitFor(() => {
45+
expect(screen.getByLabelText('Upload files')).toBeInTheDocument();
46+
});
47+
});
48+
49+
test('shows folder input when "New folder" is clicked', async () => {
50+
renderComponent();
51+
52+
fireEvent.click(screen.getByRole('button', { name: 'New folder' }));
53+
54+
await waitFor(() => {
55+
expect(screen.getByPlaceholderText('Enter folder name')).toBeInTheDocument();
56+
});
57+
await waitFor(() => {
58+
expect(screen.getByRole('button', { name: 'Create folder' })).toBeInTheDocument();
59+
});
60+
await waitFor(() => {
61+
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
62+
});
63+
});
64+
65+
test('calls onCreateFolder when user names and creates a folder', async () => {
66+
renderComponent();
67+
68+
fireEvent.click(screen.getByRole('button', { name: 'New folder' }));
69+
70+
const input = screen.getByPlaceholderText('Enter folder name');
71+
await userEvent.type(input, 'New Folder');
72+
73+
fireEvent.click(screen.getByRole('button', { name: 'Create folder' }));
74+
75+
await waitFor(() => {
76+
expect(mockOnCreateFolder).toHaveBeenCalledWith('New Folder');
77+
});
78+
79+
expect(screen.queryByPlaceholderText('Enter folder name')).not.toBeInTheDocument();
80+
});
81+
82+
test('prevents naming a folder as " " (only whitespace)', async () => {
83+
renderComponent();
84+
85+
fireEvent.click(screen.getByRole('button', { name: 'New folder' }));
86+
87+
const input = screen.getByPlaceholderText('Enter folder name');
88+
await userEvent.type(input, ' ');
89+
90+
expect(screen.getByRole('button', { name: 'Create folder' })).toBeDisabled();
91+
});
92+
93+
test('shows an error when backend erros that folder already exists', async () => {
94+
const backendError = new Error('A folder with this name already exists.');
95+
mockOnCreateFolder.mockRejectedValueOnce(backendError);
96+
97+
renderComponent();
98+
99+
fireEvent.click(screen.getByRole('button', { name: 'New folder' }));
100+
101+
const input = screen.getByPlaceholderText('Enter folder name');
102+
await userEvent.type(input, 'Existing Folder');
103+
104+
fireEvent.click(screen.getByRole('button', { name: 'Create folder' }));
105+
106+
await waitFor(() => {
107+
expect(
108+
screen.getByText(/A folder with this name already exists/i),
109+
).toBeInTheDocument();
110+
});
111+
112+
expect(mockOnCreateFolder).toHaveBeenCalledWith('Existing Folder');
113+
});
114+
115+
test('shows an error message when folder creation fails in backend', async () => {
116+
const mockError = new Error('Failed to create folder.');
117+
mockOnCreateFolder.mockRejectedValueOnce(mockError);
118+
119+
renderComponent();
120+
121+
fireEvent.click(screen.getByRole('button', { name: 'New folder' }));
122+
123+
const input = screen.getByPlaceholderText('Enter folder name');
124+
await userEvent.type(input, 'Bad Folder');
125+
126+
fireEvent.click(screen.getByRole('button', { name: 'Create folder' }));
127+
128+
await waitFor(() => {
129+
expect(screen.getByText('Failed to create folder.')).toBeInTheDocument();
130+
});
131+
132+
expect(mockOnCreateFolder).toHaveBeenCalledWith('Bad Folder');
133+
});
134+
135+
test('cancels folder creation when "Cancel" is clicked', async () => {
136+
renderComponent();
137+
138+
fireEvent.click(screen.getByRole('button', { name: 'New folder' }));
139+
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
140+
141+
await waitFor(() => {
142+
expect(screen.queryByPlaceholderText('Folder name')).not.toBeInTheDocument();
143+
});
144+
});
145+
test('hides file drop zone when canceling file upload', async () => {
146+
renderComponent();
147+
148+
fireEvent.click(screen.getByRole('button', { name: 'Upload files' }));
149+
// show the file uploader
150+
await waitFor(() => {
151+
expect(screen.getByRole('button', { name: 'Cancel upload' })).toBeInTheDocument();
152+
});
153+
await waitFor(() => {
154+
expect(screen.getByLabelText('Upload files')).toBeInTheDocument();
155+
});
156+
await waitFor(() => {
157+
expect(
158+
screen.queryByRole('button', { name: 'Upload files' }),
159+
).not.toBeInTheDocument();
160+
});
161+
162+
fireEvent.click(screen.getByRole('button', { name: 'Cancel upload' }));
163+
// hide the file uploader
164+
await waitFor(() => {
165+
expect(
166+
screen.queryByRole('button', { name: 'Cancel upload' }),
167+
).not.toBeInTheDocument();
168+
});
169+
await waitFor(() => {
170+
expect(screen.queryByLabelText('Upload files')).not.toBeInTheDocument();
171+
});
172+
await waitFor(() => {
173+
expect(screen.getByRole('button', { name: 'Upload files' })).toBeInTheDocument();
174+
});
175+
});
176+
});

0 commit comments

Comments
 (0)