Skip to content

Commit 9c18602

Browse files
author
lh01217311
committed
fix: handle processFile rejection and improve test fixture
1 parent 33a1d04 commit 9c18602

2 files changed

Lines changed: 120 additions & 20 deletions

File tree

src/AjaxUploader.tsx

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,21 @@ class AjaxUploader extends Component<UploadProps> {
179179
});
180180

181181
// Batch upload files
182-
Promise.all(postFiles).then(fileList => {
183-
const { onBatchStart } = this.props;
184-
185-
onBatchStart?.(fileList.map(({ origin, parsedFile }) => ({ file: origin, parsedFile })));
186-
187-
fileList
188-
.filter(file => file.parsedFile !== null)
189-
.forEach(file => {
190-
this.post(file);
191-
});
192-
});
182+
Promise.all(postFiles)
183+
.then(fileList => {
184+
const { onBatchStart } = this.props;
185+
186+
onBatchStart?.(fileList.map(({ origin, parsedFile }) => ({ file: origin, parsedFile })));
187+
188+
fileList
189+
.filter(file => file.parsedFile !== null)
190+
.forEach(file => {
191+
this.post(file);
192+
});
193+
})
194+
.catch(() => {
195+
// Error already handled in processFile, this catch is to prevent unhandled rejection
196+
});
193197
};
194198

195199
/**
@@ -220,20 +224,48 @@ class AjaxUploader extends Component<UploadProps> {
220224
const { action } = this.props;
221225
let mergedAction: string;
222226
if (typeof action === 'function') {
223-
mergedAction = await action(file);
227+
try {
228+
mergedAction = await action(file);
229+
} catch {
230+
transformedFile = false;
231+
}
224232
} else {
225233
mergedAction = action;
226234
}
227235

236+
// Early return if action rejected
237+
if (transformedFile === false) {
238+
return {
239+
origin: file,
240+
parsedFile: null,
241+
action: null,
242+
data: null,
243+
};
244+
}
245+
228246
// Get latest data
229247
const { data } = this.props;
230248
let mergedData: Record<string, unknown>;
231249
if (typeof data === 'function') {
232-
mergedData = await data(file);
250+
try {
251+
mergedData = await data(file);
252+
} catch {
253+
transformedFile = false;
254+
}
233255
} else {
234256
mergedData = data;
235257
}
236258

259+
// Early return if data rejected
260+
if (transformedFile === false) {
261+
return {
262+
origin: file,
263+
parsedFile: null,
264+
action: null,
265+
data: null,
266+
};
267+
}
268+
237269
const parsedData =
238270
// string type is from legacy `transformFile`.
239271
// Not sure if this will work since no related test case works with it
@@ -302,11 +334,15 @@ class AjaxUploader extends Component<UploadProps> {
302334
}
303335

304336
retryUpload = (originFile: RcFile) => {
305-
this.processFile(originFile, [originFile]).then(fileInfo => {
306-
if (fileInfo.parsedFile !== null) {
307-
this.post(fileInfo);
308-
}
309-
});
337+
this.processFile(originFile, [originFile])
338+
.then(fileInfo => {
339+
if (fileInfo.parsedFile) {
340+
this.post(fileInfo);
341+
}
342+
})
343+
.catch(() => {
344+
// Error already handled in processFile for action/data rejection
345+
});
310346
};
311347

312348
reset() {

tests/uploader.spec.tsx

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { fireEvent, render } from '@testing-library/react';
33
import React from 'react';
44
import sinon from 'sinon';
55
import { format } from 'util';
6-
import Upload, { type UploadProps } from '../src';
6+
import Upload, { type UploadProps, type RcFile } from '../src';
77

88
const sleep = (timeout = 500) => new Promise(resolve => setTimeout(resolve, timeout));
99

@@ -258,6 +258,7 @@ describe('uploader', () => {
258258
render(<Upload ref={uploadRef} action="/test" />);
259259

260260
const file = {
261+
uid: 'rc-upload-retry-test',
261262
name: 'retry.png',
262263
toString() {
263264
return this.name;
@@ -268,10 +269,73 @@ describe('uploader', () => {
268269

269270
const initialRequestCount = requests.length;
270271

271-
uploadRef.current.retryUpload(file as any);
272+
uploadRef.current.retryUpload(file as RcFile);
272273

273274
setTimeout(() => {
274275
expect(requests.length).toBe(initialRequestCount + 1);
276+
// Verify the request uses the correct file uid
277+
expect(requests[initialRequestCount].url).toBe('/test');
278+
done();
279+
}, 100);
280+
});
281+
282+
it('retryUpload should not make request when async action rejects', done => {
283+
const uploadRef = React.createRef<any>();
284+
render(
285+
<Upload
286+
ref={uploadRef}
287+
action={async () => {
288+
throw new Error('action error');
289+
}}
290+
/>,
291+
);
292+
293+
const file = {
294+
uid: 'rc-upload-retry-action-reject',
295+
name: 'retry.png',
296+
toString() {
297+
return this.name;
298+
},
299+
};
300+
301+
const initialRequestCount = requests.length;
302+
303+
uploadRef.current.retryUpload(file as RcFile);
304+
305+
setTimeout(() => {
306+
// Should not make a new request when action rejects
307+
expect(requests.length).toBe(initialRequestCount);
308+
done();
309+
}, 100);
310+
});
311+
312+
it('retryUpload should not make request when async data rejects', done => {
313+
const uploadRef = React.createRef<any>();
314+
render(
315+
<Upload
316+
ref={uploadRef}
317+
action="/test"
318+
data={() => {
319+
throw new Error('data error');
320+
}}
321+
/>,
322+
);
323+
324+
const file = {
325+
uid: 'rc-upload-retry-data-reject',
326+
name: 'retry.png',
327+
toString() {
328+
return this.name;
329+
},
330+
};
331+
332+
const initialRequestCount = requests.length;
333+
334+
uploadRef.current.retryUpload(file as RcFile);
335+
336+
setTimeout(() => {
337+
// Should not make a new request when data rejects
338+
expect(requests.length).toBe(initialRequestCount);
275339
done();
276340
}, 100);
277341
});

0 commit comments

Comments
 (0)