test: Update upload test case (#38282)

This commit is contained in:
hms181231 2022-10-31 10:16:51 +08:00 committed by GitHub
parent 46c846a34c
commit 00e5ca5ee9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 237 deletions

View File

@ -2,12 +2,12 @@
import produce from 'immer'; import produce from 'immer';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import type { UploadRequestOption } from 'rc-upload/lib/interface'; import type { UploadRequestOption } from 'rc-upload/lib/interface';
import React from 'react'; import React, { createRef } from 'react';
import type { RcFile, UploadFile, UploadProps } from '..'; import type { RcFile, UploadFile, UploadProps } from '..';
import Upload from '..'; import Upload from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, sleep, act } from '../../../tests/utils'; import { fireEvent, render, waitFakeTimer, act } from '../../../tests/utils';
import Form from '../../form'; import Form from '../../form';
import { resetWarned } from '../../_util/warning'; import { resetWarned } from '../../_util/warning';
import { getFileItem, isImageUrl, removeFileItem } from '../utils'; import { getFileItem, isImageUrl, removeFileItem } from '../utils';
@ -19,8 +19,17 @@ describe('Upload', () => {
mountTest(Upload); mountTest(Upload);
rtlTest(Upload); rtlTest(Upload);
beforeAll(() => {
jest.useFakeTimers();
});
beforeEach(() => setup()); beforeEach(() => setup());
afterEach(() => teardown()); afterAll(() => {
jest.useRealTimers();
});
afterEach(() => {
jest.clearAllTimers();
return teardown();
});
// Mock for rc-util raf // Mock for rc-util raf
window.requestAnimationFrame = callback => window.setTimeout(callback, 16); window.requestAnimationFrame = callback => window.setTimeout(callback, 16);
@ -29,16 +38,18 @@ describe('Upload', () => {
// https://github.com/react-component/upload/issues/36 // https://github.com/react-component/upload/issues/36
it('should get refs inside Upload in componentDidMount', () => { it('should get refs inside Upload in componentDidMount', () => {
let ref: React.ReactInstance; let ref: React.RefObject<HTMLInputElement>;
class App extends React.Component { class App extends React.Component {
inputRef = createRef<HTMLInputElement>();
componentDidMount() { componentDidMount() {
ref = this.refs.input; ref = this.inputRef;
} }
render() { render() {
return ( return (
<Upload supportServerRender={false}> <Upload supportServerRender={false}>
<input ref="input" /> <input ref={this.inputRef} />
</Upload> </Upload>
); );
} }
@ -48,7 +59,6 @@ describe('Upload', () => {
}); });
it('return promise in beforeUpload', async () => { it('return promise in beforeUpload', async () => {
jest.useFakeTimers();
const data = jest.fn(); const data = jest.fn();
const done = jest.fn(); const done = jest.fn();
const props: UploadProps = { const props: UploadProps = {
@ -74,22 +84,11 @@ describe('Upload', () => {
fireEvent.change(wrapper.querySelector('input')!, { fireEvent.change(wrapper.querySelector('input')!, {
target: { files: [{ file: 'foo.png' }] }, target: { files: [{ file: 'foo.png' }] },
}); });
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
await act(async () => {
for (let i = 0; i < 4; i += 1) {
// eslint-disable-next-line no-await-in-loop
await Promise.resolve();
}
});
expect(done).toHaveBeenCalled(); expect(done).toHaveBeenCalled();
jest.useRealTimers();
}); });
it('beforeUpload can be falsy', async () => { it('beforeUpload can be falsy', async () => {
jest.useFakeTimers();
const done = jest.fn(); const done = jest.fn();
const props: UploadProps = { const props: UploadProps = {
action: 'http://upload.com', action: 'http://upload.com',
@ -110,18 +109,11 @@ describe('Upload', () => {
fireEvent.change(wrapper.querySelector('input')!, { fireEvent.change(wrapper.querySelector('input')!, {
target: { files: [{ file: 'foo.png' }] }, target: { files: [{ file: 'foo.png' }] },
}); });
await act(async () => { await waitFakeTimer();
for (let i = 0; i < 4; i += 1) {
// eslint-disable-next-line no-await-in-loop
await Promise.resolve();
}
});
expect(done).toHaveBeenCalled(); expect(done).toHaveBeenCalled();
jest.useRealTimers();
}); });
it('upload promise return file in beforeUpload', async () => { it('upload promise return file in beforeUpload', async () => {
jest.useFakeTimers();
const done = jest.fn(); const done = jest.fn();
const data = jest.fn(); const data = jest.fn();
const props: UploadProps = { const props: UploadProps = {
@ -153,18 +145,9 @@ describe('Upload', () => {
fireEvent.change(wrapper.querySelector('input')!, { fireEvent.change(wrapper.querySelector('input')!, {
target: { files: [{ file: 'foo.png' }] }, target: { files: [{ file: 'foo.png' }] },
}); });
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
await act(async () => {
for (let i = 0; i < 4; i += 1) {
// eslint-disable-next-line no-await-in-loop
await Promise.resolve();
}
});
expect(done).toHaveBeenCalled(); expect(done).toHaveBeenCalled();
jest.useRealTimers();
}); });
it('should not stop upload when return value of beforeUpload is false', done => { it('should not stop upload when return value of beforeUpload is false', done => {
@ -298,8 +281,7 @@ describe('Upload', () => {
expect(wrapper.querySelectorAll('input#upload').length).toBe(0); expect(wrapper.querySelectorAll('input#upload').length).toBe(0);
}); });
it('should be controlled by fileList', () => { it('should be controlled by fileList', async () => {
jest.useFakeTimers();
const fileList = [ const fileList = [
{ {
uid: '-1', uid: '-1',
@ -312,11 +294,8 @@ describe('Upload', () => {
const { rerender } = render(<Upload ref={ref} />); const { rerender } = render(<Upload ref={ref} />);
expect(ref.current.fileList).toEqual([]); expect(ref.current.fileList).toEqual([]);
rerender(<Upload ref={ref} fileList={fileList as UploadProps['fileList']} />); rerender(<Upload ref={ref} fileList={fileList as UploadProps['fileList']} />);
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
expect(ref.current.fileList).toEqual(fileList); expect(ref.current.fileList).toEqual(fileList);
jest.useRealTimers();
}); });
it('should be able to get uid at first', () => { it('should be able to get uid at first', () => {
@ -451,7 +430,7 @@ describe('Upload', () => {
expect(linkNode?.getAttribute('rel')).toBe('noopener'); expect(linkNode?.getAttribute('rel')).toBe('noopener');
}); });
it('should not stop remove when return value of onRemove is false', done => { it('should stop remove when return value of onRemove is false', async () => {
const mockRemove = jest.fn(() => false); const mockRemove = jest.fn(() => false);
const props: UploadProps = { const props: UploadProps = {
onRemove: mockRemove, onRemove: mockRemove,
@ -469,12 +448,11 @@ describe('Upload', () => {
fireEvent.click(wrapper.querySelector('div.ant-upload-list-item .anticon-delete')!); fireEvent.click(wrapper.querySelector('div.ant-upload-list-item .anticon-delete')!);
setTimeout(() => { await waitFakeTimer();
expect(mockRemove).toHaveBeenCalled();
expect(props.fileList).toHaveLength(1); expect(mockRemove).toHaveBeenCalled();
expect(props.fileList?.[0]?.status).toBe('done'); expect(props.fileList).toHaveLength(1);
done(); expect(props.fileList?.[0]?.status).toBe('done');
});
}); });
// https://github.com/ant-design/ant-design/issues/18902 // https://github.com/ant-design/ant-design/issues/18902
@ -504,13 +482,8 @@ describe('Upload', () => {
); );
fireEvent.click(container.querySelector('div.ant-upload-list-item .anticon-delete')!); fireEvent.click(container.querySelector('div.ant-upload-list-item .anticon-delete')!);
// uploadStart is a batch work which we need wait for react act
await act(async () => {
await Promise.resolve();
});
// Delay return true for remove // Delay return true for remove
await sleep(100); await waitFakeTimer();
await act(async () => { await act(async () => {
await removePromise(true); await removePromise(true);
}); });
@ -519,7 +492,7 @@ describe('Upload', () => {
expect(file.status).toBe('removed'); expect(file.status).toBe('removed');
}); });
it('should not stop download when return use onDownload', done => { it('should not stop download when return use onDownload', async () => {
const mockRemove = jest.fn(() => false); const mockRemove = jest.fn(() => false);
const props: UploadProps = { const props: UploadProps = {
onRemove: mockRemove, onRemove: mockRemove,
@ -540,11 +513,9 @@ describe('Upload', () => {
fireEvent.click(wrapper.querySelector('div.ant-upload-list-item .anticon-download')!); fireEvent.click(wrapper.querySelector('div.ant-upload-list-item .anticon-download')!);
setTimeout(() => { await waitFakeTimer();
expect(props.fileList).toHaveLength(1); expect(props.fileList).toHaveLength(1);
expect(props.fileList?.[0]?.status).toBe('done'); expect(props.fileList?.[0]?.status).toBe('done');
done();
});
}); });
// https://github.com/ant-design/ant-design/issues/14439 // https://github.com/ant-design/ant-design/issues/14439
@ -658,7 +629,6 @@ describe('Upload', () => {
// https://github.com/ant-design/ant-design/issues/26427 // https://github.com/ant-design/ant-design/issues/26427
it('should sync file list with control mode', async () => { it('should sync file list with control mode', async () => {
jest.useFakeTimers();
const done = jest.fn(); const done = jest.fn();
let callTimes = 0; let callTimes = 0;
@ -718,21 +688,9 @@ describe('Upload', () => {
target: { files: [{ file: 'foo.png' }] }, target: { files: [{ file: 'foo.png' }] },
}); });
await act(async () => { await waitFakeTimer();
for (let i = 0; i < 3; i += 1) {
// eslint-disable-next-line no-await-in-loop
await Promise.resolve();
}
});
act(() => {
jest.runAllTimers();
});
await act(async () => {
await Promise.resolve();
});
expect(done).toHaveBeenCalled(); expect(done).toHaveBeenCalled();
jest.useRealTimers();
}); });
describe('maxCount', () => { describe('maxCount', () => {
@ -764,7 +722,7 @@ describe('Upload', () => {
}, },
}); });
await sleep(20); await waitFakeTimer();
expect(onChange.mock.calls[0][0].fileList).toHaveLength(1); expect(onChange.mock.calls[0][0].fileList).toHaveLength(1);
expect(onChange.mock.calls[0][0].fileList[0]).toEqual( expect(onChange.mock.calls[0][0].fileList[0]).toEqual(
@ -807,7 +765,7 @@ describe('Upload', () => {
}, },
}); });
await sleep(20); await waitFakeTimer();
expect(onChange.mock.calls[0][0].fileList).toHaveLength(2); expect(onChange.mock.calls[0][0].fileList).toHaveLength(2);
expect(onChange.mock.calls[0][0].fileList).toEqual([ expect(onChange.mock.calls[0][0].fileList).toEqual([
@ -850,7 +808,7 @@ describe('Upload', () => {
}, },
}); });
await sleep(); await waitFakeTimer();
const { file } = onChange.mock.calls[0][0]; const { file } = onChange.mock.calls[0][0];
const clone = cloneDeep(file); const clone = cloneDeep(file);
@ -875,6 +833,8 @@ describe('Upload', () => {
}, },
]; ];
const image = cloneDeep(fileList[0]);
const frozenFileList = fileList.map(Object.freeze); const frozenFileList = fileList.map(Object.freeze);
const { container: wrapper } = render( const { container: wrapper } = render(
@ -884,11 +844,10 @@ describe('Upload', () => {
fireEvent.click(rmBtn[rmBtn.length - 1]); fireEvent.click(rmBtn[rmBtn.length - 1]);
// Wait for Upload async remove // Wait for Upload async remove
await act(async () => { await waitFakeTimer();
await sleep();
});
});
expect(image).toEqual(frozenFileList[0]);
});
// https://github.com/ant-design/ant-design/issues/30390 // https://github.com/ant-design/ant-design/issues/30390
// IE11 Does not support the File constructor // IE11 Does not support the File constructor
it('should not break in IE if beforeUpload returns false', async () => { it('should not break in IE if beforeUpload returns false', async () => {
@ -906,7 +865,7 @@ describe('Upload', () => {
}); });
// React 18 is async now // React 18 is async now
await sleep(); await waitFakeTimer();
expect(onChange.mock.calls[0][0].fileList).toHaveLength(1); expect(onChange.mock.calls[0][0].fileList).toHaveLength(1);
spyIE.mockRestore(); spyIE.mockRestore();
@ -914,8 +873,6 @@ describe('Upload', () => {
// https://github.com/ant-design/ant-design/issues/33819 // https://github.com/ant-design/ant-design/issues/33819
it('should show the animation of the upload children leaving when the upload children becomes null', async () => { it('should show the animation of the upload children leaving when the upload children becomes null', async () => {
jest.useFakeTimers();
const { container, rerender } = render( const { container, rerender } = render(
<Upload listType="picture-card"> <Upload listType="picture-card">
<button type="button">upload</button> <button type="button">upload</button>
@ -931,16 +888,12 @@ describe('Upload', () => {
}); });
// Motion leave status change: start > active // Motion leave status change: start > active
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
fireEvent.animationEnd(container.querySelector('.ant-upload-select-picture-card')!); fireEvent.animationEnd(container.querySelector('.ant-upload-select-picture-card')!);
expect(container.querySelector('.ant-upload-select-picture-card')).not.toHaveClass( expect(container.querySelector('.ant-upload-select-picture-card')).not.toHaveClass(
'ant-upload-animate-inline-leave-start', 'ant-upload-animate-inline-leave-start',
); );
jest.useRealTimers();
}); });
it('<Upload /> should pass <UploadList /> prefixCls', async () => { it('<Upload /> should pass <UploadList /> prefixCls', async () => {
@ -978,9 +931,8 @@ describe('Upload', () => {
}); });
// React 18 is async now // React 18 is async now
await act(async () => { await waitFakeTimer();
await sleep();
});
onChange.mockReset(); onChange.mockReset();
// Processing // Processing

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import Upload from '..'; import Upload from '..';
import { fireEvent, render, sleep, waitFor, act } from '../../../tests/utils'; import { act, fireEvent, render, waitFakeTimer, waitFor } from '../../../tests/utils';
import Form from '../../form'; import Form from '../../form';
import UploadList from '../UploadList'; import UploadList from '../UploadList';
import { previewImage } from '../utils'; import { previewImage } from '../utils';
@ -54,10 +54,15 @@ describe('Upload List', () => {
// HTMLCanvasElement.prototype // HTMLCanvasElement.prototype
beforeEach(() => setup()); beforeEach(() => {
jest.useFakeTimers();
return setup();
});
afterEach(() => { afterEach(() => {
teardown(); teardown();
drawImageCallback = null; drawImageCallback = null;
jest.clearAllTimers();
jest.useRealTimers();
}); });
let open: jest.MockInstance<any, any[]>; let open: jest.MockInstance<any, any[]>;
@ -109,8 +114,6 @@ describe('Upload List', () => {
// https://github.com/ant-design/ant-design/issues/7269 // https://github.com/ant-design/ant-design/issues/7269
it('should remove correct item when uid is 0', async () => { it('should remove correct item when uid is 0', async () => {
jest.useFakeTimers();
const list = [ const list = [
{ {
uid: '0', uid: '0',
@ -138,36 +141,22 @@ describe('Upload List', () => {
); );
// Upload use Promise to wait remove action. Let's wait this also. // Upload use Promise to wait remove action. Let's wait this also.
await act(async () => { await waitFakeTimer();
for (let i = 0; i < 10; i += 1) {
// eslint-disable-next-line no-await-in-loop
await Promise.resolve();
}
});
// Progress motion to active
act(() => {
jest.runAllTimers();
});
// Progress motion to done // Progress motion to done
// React 17 will reach deadline, so we need check if already done // React 17 will reach deadline, so we need check if already done
if (container.querySelector('.ant-upload-animate-leave-active')) { if (container.querySelector('.ant-upload-animate-leave-active')) {
fireEvent.animationEnd(container.querySelector('.ant-upload-animate-leave-active')!); fireEvent.animationEnd(container.querySelector('.ant-upload-animate-leave-active')!);
} }
act(() => {
jest.runAllTimers(); await waitFakeTimer();
});
expect(container.querySelectorAll('.ant-upload-list-text-container')).toHaveLength(1); expect(container.querySelectorAll('.ant-upload-list-text-container')).toHaveLength(1);
unmount(); unmount();
jest.useRealTimers();
}); });
it('should be uploading when upload a file', async () => { it('should be uploading when upload a file', async () => {
jest.useFakeTimers();
const done = jest.fn(); const done = jest.fn();
let wrapper: ReturnType<typeof render>; let wrapper: ReturnType<typeof render>;
let latestFileList: UploadFile<any>[] | null = null; let latestFileList: UploadFile<any>[] | null = null;
@ -195,23 +184,14 @@ describe('Upload List', () => {
fireEvent.change(wrapper.container.querySelector('input')!, { fireEvent.change(wrapper.container.querySelector('input')!, {
target: { files: [{ name: 'foo.png' }] }, target: { files: [{ name: 'foo.png' }] },
}); });
await act(async () => { await waitFakeTimer();
await Promise.resolve();
await Promise.resolve();
});
act(() => {
jest.runAllTimers();
});
expect(done).toHaveBeenCalled(); expect(done).toHaveBeenCalled();
wrapper.unmount(); wrapper.unmount();
jest.useRealTimers();
}); });
it('handle error', async () => { it('handle error', async () => {
jest.useFakeTimers();
const onChange = jest.fn(); const onChange = jest.fn();
const { const {
@ -231,14 +211,9 @@ describe('Upload List', () => {
target: { files: [{ name: 'foo.png' }] }, target: { files: [{ name: 'foo.png' }] },
}); });
await act(async () => { await waitFakeTimer();
await Promise.resolve();
});
// Wait twice since `errorRequest` also use timeout for mock // Wait twice since `errorRequest` also use timeout for mock
act(() => {
jest.runAllTimers();
});
expect(onChange).toHaveBeenLastCalledWith( expect(onChange).toHaveBeenLastCalledWith(
expect.objectContaining({ expect.objectContaining({
@ -249,21 +224,15 @@ describe('Upload List', () => {
fireEvent.animationEnd(wrapper.querySelector('.ant-upload-animate-appear-active')!); fireEvent.animationEnd(wrapper.querySelector('.ant-upload-animate-appear-active')!);
} }
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
expect(wrapper.firstChild).toMatchSnapshot(); expect(wrapper.firstChild).toMatchSnapshot();
// Error message // Error message
fireEvent.mouseEnter(wrapper.querySelector('.ant-upload-list-item')!); fireEvent.mouseEnter(wrapper.querySelector('.ant-upload-list-item')!);
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
expect(baseElement.querySelector('.ant-tooltip')).not.toHaveClass('.ant-tooltip-hidden'); expect(baseElement.querySelector('.ant-tooltip')).not.toHaveClass('.ant-tooltip-hidden');
jest.useRealTimers();
unmount(); unmount();
}); });
@ -285,7 +254,7 @@ describe('Upload List', () => {
target: { files: [{ name: 'foo.png' }] }, target: { files: [{ name: 'foo.png' }] },
}); });
await sleep(); await waitFakeTimer();
expect(ref.current.fileList.length).toBe(fileList.length + 1); expect(ref.current.fileList.length).toBe(fileList.length + 1);
expect(handleChange.mock.calls[0][0].fileList).toHaveLength(3); expect(handleChange.mock.calls[0][0].fileList).toHaveLength(3);
@ -381,8 +350,8 @@ describe('Upload List', () => {
expect(handleRemove).toHaveBeenCalledWith(fileList[0]); expect(handleRemove).toHaveBeenCalledWith(fileList[0]);
fireEvent.click(wrapper.querySelectorAll('.anticon-delete')[1]); fireEvent.click(wrapper.querySelectorAll('.anticon-delete')[1]);
expect(handleRemove).toHaveBeenCalledWith(fileList[1]); expect(handleRemove).toHaveBeenCalledWith(fileList[1]);
await sleep(); await waitFakeTimer();
expect(handleChange.mock.calls.length).toBe(2); expect(handleChange).toHaveBeenCalledTimes(2);
unmount(); unmount();
}); });
@ -409,6 +378,7 @@ describe('Upload List', () => {
</Upload>, </Upload>,
); );
fireEvent.click(wrapper.querySelectorAll('.anticon-download')[0]); fireEvent.click(wrapper.querySelectorAll('.anticon-download')[0]);
expect(handleDownload).toHaveBeenCalled();
unmount(); unmount();
}); });
@ -467,7 +437,7 @@ describe('Upload List', () => {
<button type="button">upload</button> <button type="button">upload</button>
</Upload>, </Upload>,
); );
await sleep(); await waitFakeTimer();
expect(ref.current.fileList[2].thumbUrl).not.toBe(undefined); expect(ref.current.fileList[2].thumbUrl).not.toBe(undefined);
expect(onDrawImage).toHaveBeenCalled(); expect(onDrawImage).toHaveBeenCalled();
@ -559,8 +529,6 @@ describe('Upload List', () => {
}); });
it('not crash when uploading not provides percent', async () => { it('not crash when uploading not provides percent', async () => {
jest.useFakeTimers();
const { unmount } = render( const { unmount } = render(
<Upload <Upload
listType="picture" listType="picture"
@ -570,13 +538,9 @@ describe('Upload List', () => {
/>, />,
); );
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
unmount(); unmount();
jest.useRealTimers();
}); });
it('should support showRemoveIcon and showPreviewIcon', () => { it('should support showRemoveIcon and showPreviewIcon', () => {
@ -640,8 +604,8 @@ describe('Upload List', () => {
fireEvent.click(wrapper.querySelectorAll('.custom-delete')[1]); fireEvent.click(wrapper.querySelectorAll('.custom-delete')[1]);
expect(handleRemove).toHaveBeenCalledWith(fileList[1]); expect(handleRemove).toHaveBeenCalledWith(fileList[1]);
expect(myClick).toHaveBeenCalled(); expect(myClick).toHaveBeenCalled();
await sleep(); await waitFakeTimer();
expect(handleChange.mock.calls.length).toBe(2); expect(handleChange).toHaveBeenCalledTimes(2);
unmount(); unmount();
}); });
@ -718,11 +682,9 @@ describe('Upload List', () => {
rules={[ rules={[
{ {
required: true, required: true,
validator(_, value, callback) { async validator(_, value) {
if (!value || value.length === 0) { if (!value || value.length === 0) {
callback('file required'); throw new Error('file required');
} else {
callback();
} }
}, },
}, },
@ -739,7 +701,7 @@ describe('Upload List', () => {
const { container, unmount } = render(<TestForm />); const { container, unmount } = render(<TestForm />);
fireEvent.submit(container.querySelector('form')!); fireEvent.submit(container.querySelector('form')!);
await sleep(); await waitFakeTimer();
expect(formRef!.getFieldError(['file'])).toEqual(['file required']); expect(formRef!.getFieldError(['file'])).toEqual(['file required']);
fireEvent.change(container.querySelector('input')!, { fireEvent.change(container.querySelector('input')!, {
@ -747,7 +709,7 @@ describe('Upload List', () => {
}); });
fireEvent.submit(container.querySelector('form')!); fireEvent.submit(container.querySelector('form')!);
await sleep(); await waitFakeTimer();
expect(formRef!.getFieldError(['file'])).toEqual([]); expect(formRef!.getFieldError(['file'])).toEqual([]);
unmount(); unmount();
@ -795,7 +757,8 @@ describe('Upload List', () => {
target: { files: [{ name: 'foo.png' }] }, target: { files: [{ name: 'foo.png' }] },
}); });
await sleep(); await waitFakeTimer();
expect(wrapper.querySelector('.ant-upload-list-item-thumbnail')?.getAttribute('href')).toBe( expect(wrapper.querySelector('.ant-upload-list-item-thumbnail')?.getAttribute('href')).toBe(
null, null,
); );
@ -1005,7 +968,7 @@ describe('Upload List', () => {
</Upload>, </Upload>,
); );
expect(previewFile).toHaveBeenCalledWith(file.originFileObj); expect(previewFile).toHaveBeenCalledWith(file.originFileObj);
await sleep(100); await waitFakeTimer();
expect( expect(
wrapper.querySelector('.ant-upload-list-item-thumbnail img')?.getAttribute('src'), wrapper.querySelector('.ant-upload-list-item-thumbnail img')?.getAttribute('src'),
@ -1039,7 +1002,6 @@ describe('Upload List', () => {
); );
const imgNode = wrapper.querySelectorAll('.ant-upload-list-item-thumbnail img'); const imgNode = wrapper.querySelectorAll('.ant-upload-list-item-thumbnail img');
expect(imgNode.length).toBe(2); expect(imgNode.length).toBe(2);
unmount(); unmount();
}); });
it('should render <img /> when custom imageUrl return true', () => { it('should render <img /> when custom imageUrl return true', () => {
@ -1056,7 +1018,6 @@ describe('Upload List', () => {
const imgNode = wrapper.querySelectorAll('.ant-upload-list-item-thumbnail img'); const imgNode = wrapper.querySelectorAll('.ant-upload-list-item-thumbnail img');
expect(isImageUrl).toHaveBeenCalled(); expect(isImageUrl).toHaveBeenCalled();
expect(imgNode.length).toBe(3); expect(imgNode.length).toBe(3);
unmount(); unmount();
}); });
it('should not render <img /> when custom imageUrl return false', () => { it('should not render <img /> when custom imageUrl return false', () => {
@ -1073,31 +1034,11 @@ describe('Upload List', () => {
const imgNode = wrapper.querySelectorAll('.ant-upload-list-item-thumbnail img'); const imgNode = wrapper.querySelectorAll('.ant-upload-list-item-thumbnail img');
expect(isImageUrl).toHaveBeenCalled(); expect(isImageUrl).toHaveBeenCalled();
expect(imgNode.length).toBe(0); expect(imgNode.length).toBe(0);
unmount(); unmount();
}); });
}); });
describe('thumbUrl support for non-image', () => { describe('thumbUrl support for non-image', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
const nonImageFile = new File([''], 'foo.7z', { type: 'application/x-7z-compressed' }); const nonImageFile = new File([''], 'foo.7z', { type: 'application/x-7z-compressed' });
/** Wait for a long promise since `rc-util` internal has at least 3 promise wait */
async function waitPromise() {
/* eslint-disable no-await-in-loop */
for (let i = 0; i < 10; i += 1) {
await Promise.resolve();
}
/* eslint-enable */
}
it('should render <img /> when upload non-image file and configure thumbUrl in onChange', async () => { it('should render <img /> when upload non-image file and configure thumbUrl in onChange', async () => {
const thumbUrl = const thumbUrl =
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'; 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
@ -1140,20 +1081,12 @@ describe('Upload List', () => {
}); });
// Wait for `rc-upload` process file // Wait for `rc-upload` process file
await waitPromise(); await waitFakeTimer();
// Wait for mock request finish request
act(() => {
jest.runAllTimers();
});
// Basic called times // Basic called times
expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalled();
// Check for images // Check for images
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
const afterImgNode = wrapper.container.querySelectorAll( const afterImgNode = wrapper.container.querySelectorAll(
'.ant-upload-list-item-thumbnail img', '.ant-upload-list-item-thumbnail img',
); );
@ -1162,29 +1095,25 @@ describe('Upload List', () => {
wrapper.unmount(); wrapper.unmount();
}); });
it('should not render <img /> when upload non-image file without thumbUrl in onChange', done => { it('should not render <img /> when upload non-image file without thumbUrl in onChange', async () => {
(global as any).testName = (global as any).testName =
'should not render <img /> when upload non-image file without thumbUrl in onChange'; 'should not render <img /> when upload non-image file without thumbUrl in onChange';
let wrapper: ReturnType<typeof render>; let wrapper: ReturnType<typeof render>;
const onChange: UploadProps['onChange'] = async ({ fileList: files }) => { const onChange = jest.fn<void, Record<'fileList', UploadProps['fileList']>[]>(
wrapper.rerender( ({ fileList: files }) => {
<Upload wrapper.rerender(
action="http://jsonplaceholder.typicode.com/posts/" <Upload
listType="picture-card" action="http://jsonplaceholder.typicode.com/posts/"
fileList={files} listType="picture-card"
onChange={onChange} fileList={files}
customRequest={successRequest} onChange={onChange}
> customRequest={successRequest}
<button type="button">upload</button> >
</Upload>, <button type="button">upload</button>
); </Upload>,
);
await sleep(); },
const imgNode = wrapper.container.querySelectorAll('.ant-upload-list-item-thumbnail img'); );
expect(imgNode.length).toBe(0);
done();
};
wrapper = render( wrapper = render(
<Upload <Upload
action="http://jsonplaceholder.typicode.com/posts/" action="http://jsonplaceholder.typicode.com/posts/"
@ -1201,10 +1130,17 @@ describe('Upload List', () => {
fireEvent.change(wrapper.container.querySelector('input')!, { fireEvent.change(wrapper.container.querySelector('input')!, {
target: { files: [nonImageFile] }, target: { files: [nonImageFile] },
}); });
await waitFakeTimer();
expect(onChange).toHaveBeenCalled();
expect(wrapper.container.querySelectorAll('.ant-upload-list-item-thumbnail img').length).toBe(
0,
);
}); });
}); });
it('[deprecated] should support transformFile', done => { it('[deprecated] should support transformFile', done => {
jest.useRealTimers();
let wrapper: ReturnType<typeof render>; let wrapper: ReturnType<typeof render>;
let lastFile: UploadFile; let lastFile: UploadFile;
@ -1281,8 +1217,6 @@ describe('Upload List', () => {
// https://github.com/ant-design/ant-design/issues/26536 // https://github.com/ant-design/ant-design/issues/26536
it('multiple file upload should keep the internal fileList async', async () => { it('multiple file upload should keep the internal fileList async', async () => {
jest.useFakeTimers();
const uploadRef = React.createRef<any>(); const uploadRef = React.createRef<any>();
const MyUpload: React.FC = () => { const MyUpload: React.FC = () => {
@ -1320,14 +1254,10 @@ describe('Upload List', () => {
expect(uploadRef.current.fileList).toHaveLength(fileNames.length); expect(uploadRef.current.fileList).toHaveLength(fileNames.length);
act(() => { await waitFakeTimer();
jest.runAllTimers();
});
expect(uploadRef.current.fileList).toHaveLength(fileNames.length); expect(uploadRef.current.fileList).toHaveLength(fileNames.length);
unmount(); unmount();
jest.useRealTimers();
}); });
it('itemRender', () => { it('itemRender', () => {
@ -1384,13 +1314,11 @@ describe('Upload List', () => {
const beforeUpload = jest.fn(() => Upload.LIST_IGNORE); const beforeUpload = jest.fn(() => Upload.LIST_IGNORE);
const { container: wrapper, unmount } = render(<Upload beforeUpload={beforeUpload} />); const { container: wrapper, unmount } = render(<Upload beforeUpload={beforeUpload} />);
await act(() => { fireEvent.change(wrapper.querySelector('input')!, {
fireEvent.change(wrapper.querySelector('input')!, { target: { files: [{ file: 'foo.png' }] },
target: { files: [{ file: 'foo.png' }] },
});
}); });
await sleep(); await waitFakeTimer();
expect(beforeUpload).toHaveBeenCalled(); expect(beforeUpload).toHaveBeenCalled();
expect(wrapper.querySelectorAll('.ant-upload-list-text-container')).toHaveLength(0); expect(wrapper.querySelectorAll('.ant-upload-list-text-container')).toHaveLength(0);
@ -1585,8 +1513,6 @@ describe('Upload List', () => {
// https://github.com/ant-design/ant-design/issues/36286 // https://github.com/ant-design/ant-design/issues/36286
it('remove should keep origin className', async () => { it('remove should keep origin className', async () => {
jest.useFakeTimers();
const onChange = jest.fn(); const onChange = jest.fn();
const list = [ const list = [
{ {
@ -1605,15 +1531,7 @@ describe('Upload List', () => {
fireEvent.click(container.querySelector('.anticon-delete')!); fireEvent.click(container.querySelector('.anticon-delete')!);
// Wait for Upload sync await waitFakeTimer();
for (let i = 0; i < 10; i += 1) {
// eslint-disable-next-line no-await-in-loop
await Promise.resolve();
}
act(() => {
jest.runAllTimers();
});
expect(onChange).toHaveBeenCalledWith( expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
@ -1622,7 +1540,5 @@ describe('Upload List', () => {
); );
expect(container.querySelector('.ant-upload-list-item-error')).toBeTruthy(); expect(container.querySelector('.ant-upload-list-item-error')).toBeTruthy();
jest.useRealTimers();
}); });
}); });