From 4efc79adedc7274bba8a7ca0757bd170f55722d9 Mon Sep 17 00:00:00 2001 From: madocto Date: Fri, 21 Mar 2025 16:15:34 +0800 Subject: [PATCH 1/5] feat: support paste upload file --- docs/demo/paste.md | 8 ++ docs/demo/pasteDirectory.md | 8 ++ docs/examples/paste.tsx | 43 ++++++++ docs/examples/pasteDirectory.tsx | 44 ++++++++ src/AjaxUploader.tsx | 65 ++++++++---- tests/uploader.spec.tsx | 173 ++++++++++++++++++++++++++++--- 6 files changed, 307 insertions(+), 34 deletions(-) create mode 100644 docs/demo/paste.md create mode 100644 docs/demo/pasteDirectory.md create mode 100644 docs/examples/paste.tsx create mode 100644 docs/examples/pasteDirectory.tsx diff --git a/docs/demo/paste.md b/docs/demo/paste.md new file mode 100644 index 00000000..51574df3 --- /dev/null +++ b/docs/demo/paste.md @@ -0,0 +1,8 @@ +--- +title: paste +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/demo/pasteDirectory.md b/docs/demo/pasteDirectory.md new file mode 100644 index 00000000..0731d52b --- /dev/null +++ b/docs/demo/pasteDirectory.md @@ -0,0 +1,8 @@ +--- +title: pasteDirectory +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/paste.tsx b/docs/examples/paste.tsx new file mode 100644 index 00000000..05e41f0a --- /dev/null +++ b/docs/examples/paste.tsx @@ -0,0 +1,43 @@ +/* eslint no-console:0 */ +import React from 'react'; +import Upload from 'rc-upload'; + +const props = { + action: '/upload.do', + type: 'drag', + accept: '.png', + beforeUpload(file) { + console.log('beforeUpload', file.name); + }, + onStart: file => { + console.log('onStart', file.name); + }, + onSuccess(file) { + console.log('onSuccess', file); + }, + onProgress(step, file) { + console.log('onProgress', Math.round(step.percent), file.name); + }, + onError(err) { + console.log('onError', err); + }, + style: { display: 'inline-block', width: 200, height: 200, background: '#eee' }, +}; + +const Test = () => { + return ( +
+
+ + 开始上传 + +
+
+ ); +}; + +export default Test; diff --git a/docs/examples/pasteDirectory.tsx b/docs/examples/pasteDirectory.tsx new file mode 100644 index 00000000..61a875e3 --- /dev/null +++ b/docs/examples/pasteDirectory.tsx @@ -0,0 +1,44 @@ +/* eslint no-console:0 */ +import React from 'react'; +import Upload from 'rc-upload'; + +const props = { + action: '/upload.do', + type: 'drag', + accept: '.png', + directory: true, + beforeUpload(file) { + console.log('beforeUpload', file.name); + }, + onStart: file => { + console.log('onStart', file.name); + }, + onSuccess(file) { + console.log('onSuccess', file); + }, + onProgress(step, file) { + console.log('onProgress', Math.round(step.percent), file.name); + }, + onError(err) { + console.log('onError', err); + }, + style: { display: 'inline-block', width: 200, height: 200, background: '#eee' }, +}; + +const Test = () => { + return ( +
+
+ + 开始上传 + +
+
+ ); +}; + +export default Test; diff --git a/src/AjaxUploader.tsx b/src/AjaxUploader.tsx index 383d10f3..d9cbe893 100644 --- a/src/AjaxUploader.tsx +++ b/src/AjaxUploader.tsx @@ -28,6 +28,8 @@ class AjaxUploader extends Component { private fileInput: HTMLInputElement; + private isMouseEnter: boolean; + private _isMounted: boolean; onChange = (e: React.ChangeEvent) => { @@ -66,41 +68,58 @@ class AjaxUploader extends Component { } }; - onFileDrop = async (e: React.DragEvent) => { - const { multiple } = this.props; - + onFileDropOrPaste = async (e: React.DragEvent | ClipboardEvent) => { e.preventDefault(); if (e.type === 'dragover') { return; } - if (this.props.directory) { - const files = await traverseFileTree( - Array.prototype.slice.call(e.dataTransfer.items), - (_file: RcFile) => attrAccept(_file, this.props.accept), + const { multiple, accept, directory } = this.props; + let items: DataTransferItem[] = []; + let files: File[] = []; + + if (e.type === 'drop') { + const dataTransfer = (e as React.DragEvent).dataTransfer; + items = [...(dataTransfer.items || [])]; + files = [...(dataTransfer.files || [])]; + } else if (e.type === 'paste') { + const clipboardData = (e as ClipboardEvent).clipboardData; + items = [...(clipboardData.items || [])]; + files = [...(clipboardData.files || [])]; + } + + if (directory) { + files = await traverseFileTree(Array.prototype.slice.call(items), (_file: RcFile) => + attrAccept(_file, this.props.accept), ); this.uploadFiles(files); } else { - let files = [...e.dataTransfer.files].filter((file: RcFile) => - attrAccept(file, this.props.accept), - ); + let acceptFiles = [...files].filter((file: RcFile) => attrAccept(file, accept)); if (multiple === false) { - files = files.slice(0, 1); + acceptFiles = files.slice(0, 1); } - this.uploadFiles(files); + this.uploadFiles(acceptFiles); } }; + onPrePaste(e: ClipboardEvent) { + if (this.isMouseEnter) { + this.onFileDropOrPaste(e); + } + } + componentDidMount() { this._isMounted = true; + document.addEventListener('paste', this.onPrePaste.bind(this)); } componentWillUnmount() { this._isMounted = false; this.abort(); + document.removeEventListener('paste', this.onPrePaste.bind(this)); } uploadFiles = (files: File[]) => { @@ -261,6 +280,18 @@ class AjaxUploader extends Component { this.fileInput = node; }; + handleMouseEnter = (e: React.MouseEvent) => { + this.isMouseEnter = true; + + this.props.onMouseEnter?.(e); + }; + + handleMouseLeave = (e: React.MouseEvent) => { + this.isMouseEnter = false; + + this.props.onMouseLeave?.(e); + }; + render() { const { component: Tag, @@ -278,8 +309,6 @@ class AjaxUploader extends Component { children, directory, openFileDialogOnClick, - onMouseEnter, - onMouseLeave, hasControlInside, ...otherProps } = this.props; @@ -297,10 +326,10 @@ class AjaxUploader extends Component { : { onClick: openFileDialogOnClick ? this.onClick : () => {}, onKeyDown: openFileDialogOnClick ? this.onKeyDown : () => {}, - onMouseEnter, - onMouseLeave, - onDrop: this.onFileDrop, - onDragOver: this.onFileDrop, + onMouseEnter: this.handleMouseEnter, + onMouseLeave: this.handleMouseLeave, + onDrop: this.onFileDropOrPaste, + onDragOver: this.onFileDropOrPaste, tabIndex: hasControlInside ? undefined : '0', }; return ( diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index 344a61d2..8efaef89 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -50,13 +50,13 @@ const makeFileSystemEntryAsync = item => { return { async readEntries(handle, error) { await sleep(100); - + if (!first) { return handle([]); } if (item.error && first) { - return error && error(new Error('read file error')) + return error && error(new Error('read file error')); } first = false; @@ -349,6 +349,106 @@ describe('uploader', () => { }, 100); }); + it('paste to upload', done => { + const rcUpload = uploader.container.querySelector('.rc-upload')!; + const input = uploader.container.querySelector('input')!; + + const files = [ + { + name: 'success.png', + toString() { + return this.name; + }, + }, + ]; + (files as any).item = (i: number) => files[i]; + + handlers.onSuccess = (ret, file) => { + expect(ret[1]).toEqual(file.name); + expect(file).toHaveProperty('uid'); + done(); + }; + + handlers.onError = err => { + done(err); + }; + + fireEvent.mouseEnter(rcUpload); + fireEvent.paste(input, { + clipboardData: { files }, + }); + + setTimeout(() => { + requests[0].respond(200, {}, `["","${files[0].name}"]`); + }, 100); + }); + + it('paste unaccepted type files to upload will not trigger onStart', done => { + const input = uploader.container.querySelector('input')!; + const files = [ + { + name: 'success.jpg', + toString() { + return this.name; + }, + }, + ]; + (files as any).item = (i: number) => files[i]; + + fireEvent.paste(input, { + clipboardData: { files }, + }); + const mockStart = jest.fn(); + handlers.onStart = mockStart; + setTimeout(() => { + expect(mockStart.mock.calls.length).toBe(0); + done(); + }, 100); + }); + + it('paste files with multiple false', done => { + const { container } = render(); + const rcUpload = container.querySelector('.rc-upload')!; + const input = container.querySelector('input')!; + const files = [ + new File([''], 'success.png', { type: 'image/png' }), + new File([''], 'filtered.png', { type: 'image/png' }), + ]; + Object.defineProperty(files, 'item', { + value: i => files[i], + }); + + // Only can trigger once + let triggerTimes = 0; + handlers.onStart = () => { + triggerTimes += 1; + }; + handlers.onSuccess = (ret, file) => { + try { + expect(ret[1]).toEqual(file.name); + expect(file).toHaveProperty('uid'); + expect(triggerTimes).toEqual(1); + done(); + } catch (error) { + done(error); + } + }; + handlers.onError = error => { + done(error); + }; + + Object.defineProperty(input, 'files', { + value: files, + }); + + fireEvent.mouseEnter(rcUpload); + fireEvent.paste(input, { clipboardData: { files } }); + + setTimeout(() => { + handlers.onSuccess!(['', files[0].name] as any, files[0] as any, null!); + }, 100); + }); + it('support action and data is function returns Promise', async () => { const action: any = () => { return new Promise(resolve => { @@ -377,16 +477,18 @@ describe('uploader', () => { }); it('should pass file to request', done => { - const fakeRequest = jest.fn((file) => { - expect(file).toEqual(expect.objectContaining({ - filename: 'file', // <= https://github.com/react-component/upload/pull/574 - file: expect.any(File), - method: 'post', - onError: expect.any(Function), - onProgress: expect.any(Function), - onSuccess: expect.any(Function), - data: expect.anything(), - })); + const fakeRequest = jest.fn(file => { + expect(file).toEqual( + expect.objectContaining({ + filename: 'file', // <= https://github.com/react-component/upload/pull/574 + file: expect.any(File), + method: 'post', + onError: expect.any(Function), + onProgress: expect.any(Function), + onSuccess: expect.any(Function), + data: expect.anything(), + }), + ); done(); }); @@ -400,6 +502,21 @@ describe('uploader', () => { fireEvent.change(input, { target: { files } }); }); + + it('support onMouseEnter and onMouseLeave', async () => { + const onMouseEnter = jest.fn(); + const onMouseLeave = jest.fn(); + + const { container } = render( + , + ); + const rcUpload = container.querySelector('.rc-upload')!; + + fireEvent.mouseEnter(rcUpload); + fireEvent.mouseLeave(rcUpload); + expect(onMouseEnter).toHaveBeenCalled(); + expect(onMouseLeave).toHaveBeenCalled(); + }); }); describe('directory uploader', () => { @@ -441,6 +558,7 @@ describe('uploader', () => { beforeEach(() => { uploader = render(); }); + it('beforeUpload should run after all children files are parsed', done => { const props = { action: '/test', directory: true, accept: '.png' }; const mockBeforeUpload = jest.fn(); @@ -489,6 +607,7 @@ describe('uploader', () => { done(); }, 100); }); + it('unaccepted type files to upload will not trigger onStart', done => { const input = uploader.container.querySelector('input')!; const files = { @@ -563,14 +682,14 @@ describe('uploader', () => { fireEvent.drop(input, { dataTransfer: { items: [makeDataTransferItemAsync(files)] } }); const mockStart = jest.fn(); handlers.onStart = mockStart; - + setTimeout(() => { expect(mockStart.mock.calls.length).toBe(2); done(); }, 1000); }); - it('dragging and dropping files to upload through asynchronous file reading with some readEntries method throw error', (done) => { + it('dragging and dropping files to upload through asynchronous file reading with some readEntries method throw error', done => { const input = uploader.container.querySelector('input')!; const files = { @@ -593,7 +712,7 @@ describe('uploader', () => { name: '8.png', }, ], - } + }, ], }, { @@ -612,7 +731,7 @@ describe('uploader', () => { fireEvent.drop(input, { dataTransfer: { items: [makeDataTransferItemAsync(files)] } }); const mockStart = jest.fn(); handlers.onStart = mockStart; - + setTimeout(() => { expect(mockStart.mock.calls.length).toBe(1); done(); @@ -662,6 +781,28 @@ describe('uploader', () => { done(); }, 100); }); + + it('paste directory', done => { + const rcUpload = uploader.container.querySelector('.rc-upload')!; + const files = { + name: 'foo', + children: [ + { + name: '1.png', + }, + ], + }; + + fireEvent.mouseEnter(rcUpload); + fireEvent.paste(rcUpload, { clipboardData: { items: [makeDataTransferItem(files)] } }); + const mockStart = jest.fn(); + handlers.onStart = mockStart; + + setTimeout(() => { + expect(mockStart.mock.calls.length).toBe(1); + done(); + }, 100); + }); }); describe('accept', () => { From 1ad93d6cbd594546788fd2688b0257ac56071d83 Mon Sep 17 00:00:00 2001 From: madocto Date: Wed, 9 Apr 2025 17:43:26 +0800 Subject: [PATCH 2/5] feat: allowPasteUpload --- README.md | 1 + src/AjaxUploader.tsx | 31 +++++++++++++------------------ src/interface.tsx | 1 + tests/uploader.spec.tsx | 12 +++++------- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 50cfcb08..fcc27037 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ React.render(, container); |customRequest | function | null | provide an override for the default xhr behavior for additional customization| |withCredentials | boolean | false | ajax upload with cookie send | |openFileDialogOnClick | boolean | true | useful for drag only upload as it does not trigger on enter key or click event | +|allowPasteUpload | boolean | false | support paste upload | #### onError arguments diff --git a/src/AjaxUploader.tsx b/src/AjaxUploader.tsx index d9cbe893..9e46b14f 100644 --- a/src/AjaxUploader.tsx +++ b/src/AjaxUploader.tsx @@ -28,8 +28,6 @@ class AjaxUploader extends Component { private fileInput: HTMLInputElement; - private isMouseEnter: boolean; - private _isMounted: boolean; onChange = (e: React.ChangeEvent) => { @@ -106,14 +104,21 @@ class AjaxUploader extends Component { }; onPrePaste(e: ClipboardEvent) { - if (this.isMouseEnter) { + const { allowPasteUpload } = this.props; + + if (allowPasteUpload) { this.onFileDropOrPaste(e); } } componentDidMount() { this._isMounted = true; - document.addEventListener('paste', this.onPrePaste.bind(this)); + + const { allowPasteUpload } = this.props; + + if (allowPasteUpload) { + document.addEventListener('paste', this.onPrePaste.bind(this)); + } } componentWillUnmount() { @@ -280,18 +285,6 @@ class AjaxUploader extends Component { this.fileInput = node; }; - handleMouseEnter = (e: React.MouseEvent) => { - this.isMouseEnter = true; - - this.props.onMouseEnter?.(e); - }; - - handleMouseLeave = (e: React.MouseEvent) => { - this.isMouseEnter = false; - - this.props.onMouseLeave?.(e); - }; - render() { const { component: Tag, @@ -309,6 +302,8 @@ class AjaxUploader extends Component { children, directory, openFileDialogOnClick, + onMouseEnter, + onMouseLeave, hasControlInside, ...otherProps } = this.props; @@ -326,8 +321,8 @@ class AjaxUploader extends Component { : { onClick: openFileDialogOnClick ? this.onClick : () => {}, onKeyDown: openFileDialogOnClick ? this.onKeyDown : () => {}, - onMouseEnter: this.handleMouseEnter, - onMouseLeave: this.handleMouseLeave, + onMouseEnter, + onMouseLeave, onDrop: this.onFileDropOrPaste, onDragOver: this.onFileDropOrPaste, tabIndex: hasControlInside ? undefined : '0', diff --git a/src/interface.tsx b/src/interface.tsx index 1c6e1d5e..42c053a7 100644 --- a/src/interface.tsx +++ b/src/interface.tsx @@ -44,6 +44,7 @@ export interface UploadProps input?: React.CSSProperties; }; hasControlInside?: boolean; + allowPasteUpload?: boolean; } export interface UploadProgressEvent extends Partial { diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index 8efaef89..e63edeed 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -350,8 +350,8 @@ describe('uploader', () => { }); it('paste to upload', done => { - const rcUpload = uploader.container.querySelector('.rc-upload')!; - const input = uploader.container.querySelector('input')!; + const { container } = render(); + const input = container.querySelector('input')!; const files = [ { @@ -373,7 +373,6 @@ describe('uploader', () => { done(err); }; - fireEvent.mouseEnter(rcUpload); fireEvent.paste(input, { clipboardData: { files }, }); @@ -407,8 +406,7 @@ describe('uploader', () => { }); it('paste files with multiple false', done => { - const { container } = render(); - const rcUpload = container.querySelector('.rc-upload')!; + const { container } = render(); const input = container.querySelector('input')!; const files = [ new File([''], 'success.png', { type: 'image/png' }), @@ -441,7 +439,6 @@ describe('uploader', () => { value: files, }); - fireEvent.mouseEnter(rcUpload); fireEvent.paste(input, { clipboardData: { files } }); setTimeout(() => { @@ -783,7 +780,8 @@ describe('uploader', () => { }); it('paste directory', done => { - const rcUpload = uploader.container.querySelector('.rc-upload')!; + const { container } = render(); + const rcUpload = container.querySelector('.rc-upload')!; const files = { name: 'foo', children: [ From f62c96e2e8274b21d426241400a111f671144557 Mon Sep 17 00:00:00 2001 From: madocto Date: Wed, 9 Apr 2025 17:46:58 +0800 Subject: [PATCH 3/5] docs: update demo --- docs/examples/paste.tsx | 1 + docs/examples/pasteDirectory.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/examples/paste.tsx b/docs/examples/paste.tsx index 05e41f0a..1faeff24 100644 --- a/docs/examples/paste.tsx +++ b/docs/examples/paste.tsx @@ -6,6 +6,7 @@ const props = { action: '/upload.do', type: 'drag', accept: '.png', + allowPasteUpload: true, beforeUpload(file) { console.log('beforeUpload', file.name); }, diff --git a/docs/examples/pasteDirectory.tsx b/docs/examples/pasteDirectory.tsx index 61a875e3..d3badcc8 100644 --- a/docs/examples/pasteDirectory.tsx +++ b/docs/examples/pasteDirectory.tsx @@ -7,6 +7,7 @@ const props = { type: 'drag', accept: '.png', directory: true, + allowPasteUpload: true, beforeUpload(file) { console.log('beforeUpload', file.name); }, From b45b53341478846df59067a73487bf097fc231dd Mon Sep 17 00:00:00 2001 From: madocto Date: Wed, 9 Apr 2025 18:08:26 +0800 Subject: [PATCH 4/5] feat: rename `allowPasteUpload` to `pastable` --- README.md | 2 +- docs/examples/paste.tsx | 2 +- docs/examples/pasteDirectory.tsx | 2 +- src/AjaxUploader.tsx | 26 ++++++++++++++++++-------- src/interface.tsx | 2 +- tests/uploader.spec.tsx | 6 +++--- 6 files changed, 25 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index fcc27037..26a217d9 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ React.render(, container); |customRequest | function | null | provide an override for the default xhr behavior for additional customization| |withCredentials | boolean | false | ajax upload with cookie send | |openFileDialogOnClick | boolean | true | useful for drag only upload as it does not trigger on enter key or click event | -|allowPasteUpload | boolean | false | support paste upload | +|pastable | boolean | false | support paste upload | #### onError arguments diff --git a/docs/examples/paste.tsx b/docs/examples/paste.tsx index 1faeff24..b7c95065 100644 --- a/docs/examples/paste.tsx +++ b/docs/examples/paste.tsx @@ -6,7 +6,7 @@ const props = { action: '/upload.do', type: 'drag', accept: '.png', - allowPasteUpload: true, + pastable: true, beforeUpload(file) { console.log('beforeUpload', file.name); }, diff --git a/docs/examples/pasteDirectory.tsx b/docs/examples/pasteDirectory.tsx index d3badcc8..3bb7aefc 100644 --- a/docs/examples/pasteDirectory.tsx +++ b/docs/examples/pasteDirectory.tsx @@ -7,7 +7,7 @@ const props = { type: 'drag', accept: '.png', directory: true, - allowPasteUpload: true, + pastable: true, beforeUpload(file) { console.log('beforeUpload', file.name); }, diff --git a/src/AjaxUploader.tsx b/src/AjaxUploader.tsx index 9e46b14f..7d10882e 100644 --- a/src/AjaxUploader.tsx +++ b/src/AjaxUploader.tsx @@ -103,28 +103,38 @@ class AjaxUploader extends Component { } }; - onPrePaste(e: ClipboardEvent) { - const { allowPasteUpload } = this.props; + onPrePaste = (e: ClipboardEvent) => { + const { pastable } = this.props; - if (allowPasteUpload) { + if (pastable) { this.onFileDropOrPaste(e); } - } + }; componentDidMount() { this._isMounted = true; - const { allowPasteUpload } = this.props; + const { pastable } = this.props; - if (allowPasteUpload) { - document.addEventListener('paste', this.onPrePaste.bind(this)); + if (pastable) { + document.addEventListener('paste', this.onPrePaste); } } componentWillUnmount() { this._isMounted = false; this.abort(); - document.removeEventListener('paste', this.onPrePaste.bind(this)); + document.removeEventListener('paste', this.onPrePaste); + } + + componentDidUpdate(prevProps: UploadProps) { + const { pastable } = this.props; + + if (pastable && !prevProps.pastable) { + document.addEventListener('paste', this.onPrePaste); + } else if (!pastable && prevProps.pastable) { + document.removeEventListener('paste', this.onPrePaste); + } } uploadFiles = (files: File[]) => { diff --git a/src/interface.tsx b/src/interface.tsx index 42c053a7..2f182ffd 100644 --- a/src/interface.tsx +++ b/src/interface.tsx @@ -44,7 +44,7 @@ export interface UploadProps input?: React.CSSProperties; }; hasControlInside?: boolean; - allowPasteUpload?: boolean; + pastable?: boolean; } export interface UploadProgressEvent extends Partial { diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index e63edeed..5f41f1d9 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -350,7 +350,7 @@ describe('uploader', () => { }); it('paste to upload', done => { - const { container } = render(); + const { container } = render(); const input = container.querySelector('input')!; const files = [ @@ -406,7 +406,7 @@ describe('uploader', () => { }); it('paste files with multiple false', done => { - const { container } = render(); + const { container } = render(); const input = container.querySelector('input')!; const files = [ new File([''], 'success.png', { type: 'image/png' }), @@ -780,7 +780,7 @@ describe('uploader', () => { }); it('paste directory', done => { - const { container } = render(); + const { container } = render(); const rcUpload = container.querySelector('.rc-upload')!; const files = { name: 'foo', From 8b5d9c723db93258ea391d607967bc778d87397e Mon Sep 17 00:00:00 2001 From: madocto Date: Wed, 9 Apr 2025 19:57:19 +0800 Subject: [PATCH 5/5] test: optimize --- tests/uploader.spec.tsx | 62 +++++++++++------------------------------ 1 file changed, 17 insertions(+), 45 deletions(-) diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index 5f41f1d9..a1c22fa7 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -349,7 +349,7 @@ describe('uploader', () => { }, 100); }); - it('paste to upload', done => { + it('paste to upload', async () => { const { container } = render(); const input = container.querySelector('input')!; @@ -366,23 +366,21 @@ describe('uploader', () => { handlers.onSuccess = (ret, file) => { expect(ret[1]).toEqual(file.name); expect(file).toHaveProperty('uid'); - done(); }; handlers.onError = err => { - done(err); + throw err; }; fireEvent.paste(input, { clipboardData: { files }, }); - setTimeout(() => { - requests[0].respond(200, {}, `["","${files[0].name}"]`); - }, 100); + await sleep(100); + requests[0].respond(200, {}, `["","${files[0].name}"]`); }); - it('paste unaccepted type files to upload will not trigger onStart', done => { + it('paste unaccepted type files to upload will not trigger onStart', () => { const input = uploader.container.querySelector('input')!; const files = [ { @@ -399,13 +397,11 @@ describe('uploader', () => { }); const mockStart = jest.fn(); handlers.onStart = mockStart; - setTimeout(() => { - expect(mockStart.mock.calls.length).toBe(0); - done(); - }, 100); + + expect(mockStart.mock.calls.length).toBe(0); }); - it('paste files with multiple false', done => { + it('paste files with multiple false', async () => { const { container } = render(); const input = container.querySelector('input')!; const files = [ @@ -422,28 +418,21 @@ describe('uploader', () => { triggerTimes += 1; }; handlers.onSuccess = (ret, file) => { - try { - expect(ret[1]).toEqual(file.name); - expect(file).toHaveProperty('uid'); - expect(triggerTimes).toEqual(1); - done(); - } catch (error) { - done(error); - } + expect(ret[1]).toEqual(file.name); + expect(file).toHaveProperty('uid'); + expect(triggerTimes).toEqual(1); }; handlers.onError = error => { - done(error); + throw error; }; - Object.defineProperty(input, 'files', { value: files, }); fireEvent.paste(input, { clipboardData: { files } }); - setTimeout(() => { - handlers.onSuccess!(['', files[0].name] as any, files[0] as any, null!); - }, 100); + await sleep(100); + handlers.onSuccess!(['', files[0].name] as any, files[0] as any, null!); }); it('support action and data is function returns Promise', async () => { @@ -499,21 +488,6 @@ describe('uploader', () => { fireEvent.change(input, { target: { files } }); }); - - it('support onMouseEnter and onMouseLeave', async () => { - const onMouseEnter = jest.fn(); - const onMouseLeave = jest.fn(); - - const { container } = render( - , - ); - const rcUpload = container.querySelector('.rc-upload')!; - - fireEvent.mouseEnter(rcUpload); - fireEvent.mouseLeave(rcUpload); - expect(onMouseEnter).toHaveBeenCalled(); - expect(onMouseLeave).toHaveBeenCalled(); - }); }); describe('directory uploader', () => { @@ -779,7 +753,7 @@ describe('uploader', () => { }, 100); }); - it('paste directory', done => { + it('paste directory', async () => { const { container } = render(); const rcUpload = container.querySelector('.rc-upload')!; const files = { @@ -796,10 +770,8 @@ describe('uploader', () => { const mockStart = jest.fn(); handlers.onStart = mockStart; - setTimeout(() => { - expect(mockStart.mock.calls.length).toBe(1); - done(); - }, 100); + await sleep(100); + expect(mockStart.mock.calls.length).toBe(1); }); });