-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathFileUpload.test.jsx
More file actions
135 lines (115 loc) · 5.15 KB
/
FileUpload.test.jsx
File metadata and controls
135 lines (115 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { screen, fireEvent, waitFor } from '@testing-library/react';
import { renderWithProviders } from './test-utils';
import FileUpload from '../components/FileUpload';
const defaultProps = {
uploadApiEndpoint: '/api/upload/',
token: 'test-token',
csrfToken: 'csrf-token',
onUploadSuccess: vi.fn(),
onUploadError: vi.fn(),
};
describe('FileUpload', () => {
beforeEach(() => {
defaultProps.onUploadSuccess.mockClear();
defaultProps.onUploadError.mockClear();
});
it('renders the upload button with the default label', () => {
renderWithProviders(<FileUpload {...defaultProps} />);
expect(screen.getByText('Upload File')).toBeInTheDocument();
});
it('renders a custom upload label', () => {
renderWithProviders(<FileUpload {...defaultProps} uploadLabel="Attach Document" />);
expect(screen.getByText('Attach Document')).toBeInTheDocument();
});
it('renders helper text when provided', () => {
renderWithProviders(<FileUpload {...defaultProps} helperText="Max size 5 MB" />);
expect(screen.getByText('Max size 5 MB')).toBeInTheDocument();
});
it('shows "Uploading…" while the request is in-flight', async () => {
let resolveUpload;
global.fetch.mockReturnValue(
new Promise((resolve) => {
resolveUpload = resolve;
})
);
const { container } = renderWithProviders(<FileUpload {...defaultProps} />);
const input = container.querySelector('input[type="file"]');
fireEvent.change(input, { target: { files: [new File(['x'], 'doc.pdf', { type: 'application/pdf' })] } });
await waitFor(() => expect(screen.getByText('Uploading...')).toBeInTheDocument());
// Settle the promise so act() can clean up
resolveUpload({ ok: true, json: () => Promise.resolve({ file_name: 'doc.pdf' }) });
});
it('shows the filename and remove button after a successful upload', async () => {
global.fetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ file_name: 'report.pdf' }),
});
const onUploadSuccess = vi.fn();
const { container } = renderWithProviders(
<FileUpload {...defaultProps} onUploadSuccess={onUploadSuccess} />
);
fireEvent.change(container.querySelector('input[type="file"]'), {
target: { files: [new File(['x'], 'report.pdf', { type: 'application/pdf' })] },
});
await waitFor(() => expect(screen.getByText('report.pdf')).toBeInTheDocument());
expect(screen.getByText('Remove File')).toBeInTheDocument();
expect(onUploadSuccess).toHaveBeenCalledWith({ file_name: 'report.pdf' });
});
it('uses a custom remove label', async () => {
global.fetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ file_name: 'doc.pdf' }),
});
const { container } = renderWithProviders(
<FileUpload {...defaultProps} removeLabel="Delete attachment" />
);
fireEvent.change(container.querySelector('input[type="file"]'), {
target: { files: [new File(['x'], 'doc.pdf', { type: 'application/pdf' })] },
});
await waitFor(() => expect(screen.getByText('Delete attachment')).toBeInTheDocument());
});
it('shows an error alert when the upload fails', async () => {
global.fetch.mockResolvedValue({
ok: false,
json: () => Promise.resolve({ error: 'File too large' }),
});
const { container } = renderWithProviders(<FileUpload {...defaultProps} />);
fireEvent.change(container.querySelector('input[type="file"]'), {
target: { files: [new File(['x'], 'big.pdf', { type: 'application/pdf' })] },
});
await waitFor(() => expect(screen.getByText('File too large')).toBeInTheDocument());
});
it('clears the uploaded file when remove is clicked', async () => {
global.fetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ file_name: 'doc.pdf' }),
});
const onUploadSuccess = vi.fn();
const { container } = renderWithProviders(
<FileUpload {...defaultProps} onUploadSuccess={onUploadSuccess} />
);
fireEvent.change(container.querySelector('input[type="file"]'), {
target: { files: [new File(['x'], 'doc.pdf', { type: 'application/pdf' })] },
});
await waitFor(() => expect(screen.getByText('doc.pdf')).toBeInTheDocument());
fireEvent.click(screen.getByText('Remove File'));
expect(screen.queryByText('doc.pdf')).not.toBeInTheDocument();
expect(onUploadSuccess).toHaveBeenLastCalledWith({ file_path: null, file_name: null });
});
it('sends the CSRF token and bearer token in the request', async () => {
global.fetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ file_name: 'f.pdf' }),
});
const { container } = renderWithProviders(<FileUpload {...defaultProps} />);
fireEvent.change(container.querySelector('input[type="file"]'), {
target: { files: [new File(['x'], 'f.pdf', { type: 'application/pdf' })] },
});
await waitFor(() => expect(global.fetch).toHaveBeenCalledOnce());
const [url, options] = global.fetch.mock.calls[0];
expect(url).toBe('/api/upload/');
expect(options.headers['X-CSRFToken']).toBe('csrf-token');
expect(options.method).toBe('POST');
});
});