1109 lines
39 KiB
JavaScript
1109 lines
39 KiB
JavaScript
import React from 'react'
|
|
import { mount, render } from 'enzyme'
|
|
import { spy, stub } from 'sinon'
|
|
import { onDocumentDragOver } from './utils'
|
|
|
|
const Dropzone = require(process.env.JEST_TARGET ? process.env.JEST_TARGET : './index') // eslint-disable-line import/no-dynamic-require
|
|
const DummyChildComponent = () => null
|
|
|
|
let files
|
|
let images
|
|
|
|
const rejectColor = 'red'
|
|
const acceptColor = 'green'
|
|
|
|
const rejectStyle = {
|
|
color: rejectColor,
|
|
borderColor: 'black'
|
|
}
|
|
|
|
const acceptStyle = {
|
|
color: acceptColor,
|
|
borderWidth: '5px'
|
|
}
|
|
|
|
describe('Dropzone', () => {
|
|
beforeEach(() => {
|
|
files = [
|
|
{
|
|
name: 'file1.pdf',
|
|
size: 1111,
|
|
type: 'application/pdf'
|
|
}
|
|
]
|
|
|
|
images = [
|
|
{
|
|
name: 'cats.gif',
|
|
size: 1234,
|
|
type: 'image/gif'
|
|
},
|
|
{
|
|
name: 'dogs.jpg',
|
|
size: 2345,
|
|
type: 'image/jpeg'
|
|
}
|
|
]
|
|
})
|
|
|
|
describe('basics', () => {
|
|
it('should render children', () => {
|
|
const dropzone = mount(
|
|
<Dropzone>
|
|
<p>some content</p>
|
|
</Dropzone>
|
|
)
|
|
expect(dropzone.html()).toMatchSnapshot()
|
|
})
|
|
|
|
it('should render an input HTML element', () => {
|
|
const dropzone = mount(
|
|
<Dropzone>
|
|
<p>some content</p>
|
|
</Dropzone>
|
|
)
|
|
expect(dropzone.find('input').length).toEqual(1)
|
|
})
|
|
|
|
it('sets ref properly', () => {
|
|
const dropzone = mount(<Dropzone />)
|
|
expect(dropzone.instance().fileInputEl).not.toBeUndefined()
|
|
expect(dropzone.instance().fileInputEl.tagName).toEqual('INPUT')
|
|
})
|
|
|
|
it('renders dynamic props on the root element', () => {
|
|
const component = mount(<Dropzone hidden aria-hidden title="Dropzone" />)
|
|
expect(component.html()).toContain('aria-hidden="true"')
|
|
expect(component.html()).toContain('hidden=""')
|
|
expect(component.html()).toContain('title="Dropzone"')
|
|
})
|
|
|
|
it('renders dynamic props on the input element', () => {
|
|
const component = mount(<Dropzone inputProps={{ id: 'hiddenFileInput' }} />)
|
|
expect(component.find('input').html()).toContain('id="hiddenFileInput"')
|
|
})
|
|
|
|
it('applies the accept prop to the child input', () => {
|
|
const component = render(<Dropzone className="my-dropzone" accept="image/jpeg" />)
|
|
expect(component.find('.my-dropzone').attr()).not.toContain('accept')
|
|
expect(Object.keys(component.find('input').attr())).toContain('accept')
|
|
expect(component.find('input').attr('accept')).toEqual('image/jpeg')
|
|
})
|
|
|
|
it('applies the name prop to the child input', () => {
|
|
const component = render(<Dropzone className="my-dropzone" name="test-file-input" />)
|
|
expect(component.find('.my-dropzone').attr()).not.toContain('name')
|
|
expect(Object.keys(component.find('input').attr())).toContain('name')
|
|
expect(component.find('input').attr('name')).toEqual('test-file-input')
|
|
})
|
|
|
|
it('should render children function', () => {
|
|
const content = <p>some content</p>
|
|
const dropzone = mount(<Dropzone>{content}</Dropzone>)
|
|
const dropzoneWithFunction = mount(<Dropzone>{() => content}</Dropzone>)
|
|
expect(dropzoneWithFunction.html()).toEqual(dropzone.html())
|
|
})
|
|
})
|
|
|
|
describe('document drop protection', () => {
|
|
let dropzone
|
|
let addEventCalls
|
|
let savedAddEventListener
|
|
let savedRemoveEventListener
|
|
|
|
beforeEach(() => {
|
|
savedAddEventListener = document.addEventListener
|
|
savedRemoveEventListener = document.removeEventListener
|
|
document.addEventListener = spy()
|
|
document.removeEventListener = spy()
|
|
})
|
|
|
|
afterEach(() => {
|
|
document.addEventListener = savedAddEventListener
|
|
document.removeEventListener = savedRemoveEventListener
|
|
})
|
|
|
|
// Collect the list of addEventListener/removeEventListener spy calls into an object keyed by event name.
|
|
function collectEventListenerCalls(calls) {
|
|
return calls.reduce((acc, [eventName, ...rest]) => {
|
|
acc[eventName] = rest // eslint-disable-line no-param-reassign
|
|
return acc
|
|
}, {})
|
|
}
|
|
|
|
it('installs hooks to prevent stray drops from taking over the browser window', () => {
|
|
dropzone = mount(
|
|
<Dropzone>
|
|
<p>Content</p>
|
|
</Dropzone>
|
|
)
|
|
expect(dropzone.html()).toMatchSnapshot()
|
|
expect(document.addEventListener.callCount).toEqual(2)
|
|
addEventCalls = collectEventListenerCalls(document.addEventListener.args)
|
|
Object.keys(addEventCalls).forEach(eventName => {
|
|
expect(addEventCalls[eventName][0]).toBeDefined()
|
|
expect(addEventCalls[eventName][1]).toBe(false)
|
|
})
|
|
})
|
|
|
|
it('terminates drags and drops on elements outside our dropzone', () => {
|
|
const event = { preventDefault: spy() }
|
|
onDocumentDragOver(event)
|
|
expect(event.preventDefault.callCount).toEqual(1)
|
|
event.preventDefault.reset()
|
|
|
|
dropzone.getNode().onDocumentDrop(event)
|
|
expect(event.preventDefault.callCount).toEqual(1)
|
|
})
|
|
|
|
it('permits drags and drops on elements inside our dropzone', () => {
|
|
const instanceEvent = {
|
|
preventDefault: spy(),
|
|
target: dropzone.getDOMNode()
|
|
}
|
|
dropzone.getNode().onDocumentDrop(instanceEvent)
|
|
expect(instanceEvent.preventDefault.callCount).toEqual(0)
|
|
})
|
|
|
|
it('removes document hooks when unmounted', () => {
|
|
dropzone.unmount()
|
|
expect(document.removeEventListener.callCount).toEqual(2)
|
|
const removeEventCalls = collectEventListenerCalls(document.removeEventListener.args)
|
|
Object.keys(addEventCalls).forEach(eventName => {
|
|
expect(removeEventCalls[eventName][0]).toEqual(addEventCalls[eventName][0])
|
|
})
|
|
})
|
|
|
|
it('does not prevent stray drops when preventDropOnDocument is false', () => {
|
|
dropzone = mount(<Dropzone preventDropOnDocument={false} />)
|
|
expect(dropzone.html()).toMatchSnapshot()
|
|
expect(document.addEventListener.callCount).toEqual(0)
|
|
|
|
dropzone.unmount()
|
|
expect(document.removeEventListener.callCount).toEqual(0)
|
|
})
|
|
})
|
|
|
|
describe('onClick', () => {
|
|
it('should call `open` method', done => {
|
|
const dropzone = mount(<Dropzone />)
|
|
spy(dropzone.instance(), 'open')
|
|
dropzone.simulate('click')
|
|
setTimeout(() => {
|
|
expect(dropzone.instance().open.callCount).toEqual(1)
|
|
done()
|
|
}, 0)
|
|
})
|
|
|
|
it('should not call `open` if disableClick prop is true', () => {
|
|
const dropzone = mount(<Dropzone disableClick />)
|
|
spy(dropzone.instance(), 'open')
|
|
dropzone.simulate('click')
|
|
expect(dropzone.instance().open.callCount).toEqual(0)
|
|
})
|
|
|
|
it('should call `onClick` callback if provided', done => {
|
|
const clickSpy = spy()
|
|
const dropzone = mount(<Dropzone onClick={clickSpy} />)
|
|
spy(dropzone.instance(), 'open')
|
|
dropzone.simulate('click')
|
|
setTimeout(() => {
|
|
expect(dropzone.instance().open.callCount).toEqual(1)
|
|
expect(clickSpy.callCount).toEqual(1)
|
|
done()
|
|
}, 0)
|
|
})
|
|
|
|
it('should reset the value of input', () => {
|
|
const dropzone = mount(<Dropzone />)
|
|
expect(
|
|
dropzone
|
|
.render()
|
|
.find('input')
|
|
.attr('value')
|
|
).toBeUndefined()
|
|
expect(
|
|
dropzone
|
|
.render()
|
|
.find('input')
|
|
.attr('value', 10)
|
|
).not.toBeUndefined()
|
|
dropzone.simulate('click')
|
|
expect(
|
|
dropzone
|
|
.render()
|
|
.find('input')
|
|
.attr('value')
|
|
).toBeUndefined()
|
|
})
|
|
|
|
it('should trigger click even on the input', done => {
|
|
const dropzone = mount(<Dropzone />)
|
|
const clickSpy = spy(dropzone.instance().fileInputEl, 'click')
|
|
dropzone.simulate('click')
|
|
dropzone.simulate('click')
|
|
setTimeout(() => {
|
|
expect(clickSpy.callCount).toEqual(2)
|
|
done()
|
|
}, 0)
|
|
})
|
|
|
|
it('should not invoke onClick on the wrapper', () => {
|
|
const onClickOuterSpy = spy()
|
|
const onClickInnerSpy = spy()
|
|
const component = mount(
|
|
<div onClick={onClickOuterSpy}>
|
|
<Dropzone onClick={onClickInnerSpy} />
|
|
</div>
|
|
)
|
|
|
|
component.simulate('click')
|
|
expect(onClickOuterSpy.callCount).toEqual(1)
|
|
expect(onClickInnerSpy.callCount).toEqual(0)
|
|
|
|
onClickOuterSpy.reset()
|
|
onClickInnerSpy.reset()
|
|
|
|
component.find('Dropzone').simulate('click')
|
|
expect(onClickOuterSpy.callCount).toEqual(0)
|
|
expect(onClickInnerSpy.callCount).toEqual(1)
|
|
})
|
|
|
|
it('should invoke onClick on the wrapper if disableClick is set', () => {
|
|
const onClickOuterSpy = spy()
|
|
const component = mount(
|
|
<div onClick={onClickOuterSpy}>
|
|
<Dropzone disableClick />
|
|
</div>
|
|
)
|
|
|
|
component.find('Dropzone').simulate('click')
|
|
expect(onClickOuterSpy.callCount).toEqual(1)
|
|
})
|
|
|
|
it('should invoke inputProps onClick if provided', done => {
|
|
const inputPropsClickSpy = spy()
|
|
const component = mount(<Dropzone inputProps={{ onClick: inputPropsClickSpy }} />)
|
|
|
|
component.find('Dropzone').simulate('click')
|
|
setTimeout(() => {
|
|
expect(inputPropsClickSpy.callCount).toEqual(1)
|
|
done()
|
|
}, 0)
|
|
})
|
|
})
|
|
|
|
describe('drag-n-drop', () => {
|
|
it('should override onDrag* methods', () => {
|
|
const dragStartSpy = spy()
|
|
const dragEnterSpy = spy()
|
|
const dragOverSpy = spy()
|
|
const dragLeaveSpy = spy()
|
|
const component = mount(
|
|
<Dropzone
|
|
onDragStart={dragStartSpy}
|
|
onDragEnter={dragEnterSpy}
|
|
onDragOver={dragOverSpy}
|
|
onDragLeave={dragLeaveSpy}
|
|
/>
|
|
)
|
|
component.simulate('dragStart')
|
|
component.simulate('dragEnter', { dataTransfer: { items: files } })
|
|
component.simulate('dragOver', { dataTransfer: { items: files } })
|
|
component.simulate('dragLeave', { dataTransfer: { items: files } })
|
|
expect(dragStartSpy.callCount).toEqual(1)
|
|
expect(dragEnterSpy.callCount).toEqual(1)
|
|
expect(dragOverSpy.callCount).toEqual(1)
|
|
expect(dragLeaveSpy.callCount).toEqual(1)
|
|
})
|
|
|
|
it('should guard dropEffect in onDragOver for IE', () => {
|
|
const dragStartSpy = spy()
|
|
const dragEnterSpy = spy()
|
|
const dragLeaveSpy = spy()
|
|
const component = mount(
|
|
<Dropzone
|
|
onDragStart={dragStartSpy}
|
|
onDragEnter={dragEnterSpy}
|
|
onDragLeave={dragLeaveSpy}
|
|
/>
|
|
)
|
|
|
|
// Using Proxy we'll emulate IE throwing when setting dataTransfer.dropEffect
|
|
const eventProxy = new Proxy(
|
|
{},
|
|
{
|
|
get: (target, prop) => {
|
|
switch (prop) {
|
|
case 'dataTransfer':
|
|
throw new Error('IE does not support rrror')
|
|
default:
|
|
return function noop() {}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
// And using then we'll call the onDragOver with the proxy instead of event
|
|
const dragOverSpy = stub(component.instance(), 'onDragOver').callsFake(
|
|
component.instance().onDragOver(eventProxy)
|
|
)
|
|
|
|
component.simulate('dragStart', { dataTransfer: { items: files } })
|
|
component.simulate('dragEnter', { dataTransfer: { items: files } })
|
|
component.simulate('dragOver', { dataTransfer: { items: files } })
|
|
component.simulate('dragLeave', { dataTransfer: { items: files } })
|
|
expect(dragStartSpy.callCount).toEqual(1)
|
|
expect(dragEnterSpy.callCount).toEqual(1)
|
|
expect(dragLeaveSpy.callCount).toEqual(1)
|
|
// It should not throw the error
|
|
expect(dragOverSpy).not.toThrow()
|
|
dragOverSpy.restore()
|
|
})
|
|
|
|
it('should set proper dragActive state on dragEnter', () => {
|
|
const dropzone = mount(<Dropzone>{props => <DummyChildComponent {...props} />}</Dropzone>)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files } })
|
|
expect(child).toHaveProp('isDragActive', true)
|
|
expect(child).toHaveProp('isDragAccept', true)
|
|
expect(child).toHaveProp('isDragReject', false)
|
|
})
|
|
|
|
it('should set proper dragReject state on dragEnter', () => {
|
|
const dropzone = mount(
|
|
<Dropzone accept="image/*">{props => <DummyChildComponent {...props} />}</Dropzone>
|
|
)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', {
|
|
dataTransfer: { files: files.concat(images) }
|
|
})
|
|
expect(child).toHaveProp('isDragActive', true)
|
|
expect(child).toHaveProp('isDragAccept', false)
|
|
expect(child).toHaveProp('isDragReject', true)
|
|
})
|
|
|
|
it('should set proper dragAccept state if multiple is false', () => {
|
|
const dropzone = mount(
|
|
<Dropzone accept="image/*" multiple={false}>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files } })
|
|
expect(child).toHaveProp('isDragActive', true)
|
|
expect(child).toHaveProp('isDragAccept', false)
|
|
expect(child).toHaveProp('isDragReject', true)
|
|
})
|
|
|
|
it('should set proper dragAccept state if multiple is false', () => {
|
|
const dropzone = mount(
|
|
<Dropzone accept="image/*" multiple={false}>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: images } })
|
|
expect(child).toHaveProp('isDragActive', true)
|
|
expect(child).toHaveProp('isDragAccept', true)
|
|
expect(child).toHaveProp('isDragReject', true)
|
|
})
|
|
|
|
it('should set activeClassName properly', () => {
|
|
const dropzone = mount(
|
|
<Dropzone accept="image/*" activeClassName="👏" multiple={false}>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: images } })
|
|
expect(child).toHaveProp('isDragActive', true)
|
|
expect(dropzone.hasClass('👏')).toBe(true)
|
|
})
|
|
|
|
it('should set rejectClassName properly', () => {
|
|
const dropzone = mount(
|
|
<Dropzone accept="image/*" rejectClassName="👎" multiple={false}>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: images } })
|
|
expect(child).toHaveProp('isDragReject', true)
|
|
expect(dropzone.hasClass('👎')).toBe(true)
|
|
})
|
|
|
|
it('should set acceptClassName properly', () => {
|
|
const dropzone = mount(
|
|
<Dropzone accept="image/*" acceptClassName="👍" className="foo" multiple={false}>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: images } })
|
|
expect(child).toHaveProp('isDragAccept', true)
|
|
expect(dropzone.hasClass('👍')).toBe(true)
|
|
})
|
|
|
|
it('should set disabledClassName properly', () => {
|
|
const dropzone = mount(
|
|
<Dropzone disabled disabledClassName="🤐">
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
expect(dropzone.hasClass('🤐')).toBe(true)
|
|
})
|
|
|
|
it('should keep dragging active when leaving from arbitrary node', () => {
|
|
const arbitraryOverlay = mount(<div />)
|
|
const dropzone = mount(<Dropzone>{props => <DummyChildComponent {...props} />}</Dropzone>)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: images } })
|
|
dropzone.simulate('dragLeave', { target: arbitraryOverlay })
|
|
expect(dropzone.state('isDragActive')).toBe(true)
|
|
expect(dropzone.state('draggedFiles').length > 0).toBe(true)
|
|
})
|
|
|
|
it('should apply acceptStyle if multiple is false and single file', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
accept="image/*"
|
|
multiple={false}
|
|
acceptStyle={acceptStyle}
|
|
rejectStyle={rejectStyle}
|
|
>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: [images[0]] } })
|
|
const mainDiv = dropzone.find('div').at(0)
|
|
expect(mainDiv).toHaveProp('style', acceptStyle)
|
|
})
|
|
|
|
it('should apply rejectStyle if multiple is false and single bad file type', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
accept="image/*"
|
|
multiple={false}
|
|
acceptStyle={acceptStyle}
|
|
rejectStyle={rejectStyle}
|
|
>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: [files[0]] } })
|
|
const mainDiv = dropzone.find('div').at(0)
|
|
expect(mainDiv).toHaveProp('style', rejectStyle)
|
|
})
|
|
|
|
it('should apply acceptStyle + rejectStyle if multiple is false and multiple good file types', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
accept="image/*"
|
|
multiple={false}
|
|
acceptStyle={acceptStyle}
|
|
rejectStyle={rejectStyle}
|
|
>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: images } })
|
|
const mainDiv = dropzone.find('div').at(0)
|
|
const expectedStyle = {
|
|
...acceptStyle,
|
|
...rejectStyle
|
|
}
|
|
expect(mainDiv).toHaveProp('style', expectedStyle)
|
|
})
|
|
|
|
it('should set proper dragActive state if accept prop changes mid-drag', () => {
|
|
const dropzone = mount(
|
|
<Dropzone accept="image/*">{props => <DummyChildComponent {...props} />}</Dropzone>
|
|
)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: images } })
|
|
expect(child).toHaveProp('isDragActive', true)
|
|
expect(child).toHaveProp('isDragAccept', true)
|
|
expect(child).toHaveProp('isDragReject', false)
|
|
|
|
dropzone.setProps({ accept: 'text/*' })
|
|
expect(child).toHaveProp('isDragActive', true)
|
|
expect(child).toHaveProp('isDragAccept', false)
|
|
expect(child).toHaveProp('isDragReject', true)
|
|
})
|
|
|
|
it('should expose state to children', () => {
|
|
const dropzone = mount(
|
|
<Dropzone accept="image/*">
|
|
{({ isDragActive, isDragAccept, isDragReject }) => {
|
|
if (isDragReject) {
|
|
return `${isDragActive && 'Active but'} Reject`
|
|
}
|
|
if (isDragAccept) {
|
|
return `${isDragActive && 'Active and'} Accept`
|
|
}
|
|
return 'Empty'
|
|
}}
|
|
</Dropzone>
|
|
)
|
|
expect(dropzone.text()).toEqual('Empty')
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files: images } })
|
|
expect(dropzone.text()).toEqual('Active and Accept')
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files } })
|
|
expect(dropzone.text()).toEqual('Active but Reject')
|
|
})
|
|
|
|
it('should reset the dragAccept/dragReject state when leaving after a child goes away', () => {
|
|
const DragActiveComponent = () => <p>Accept</p>
|
|
const ChildComponent = () => <p>Child component content</p>
|
|
const dropzone = mount(
|
|
<Dropzone>
|
|
{({ isDragAccept, isDragReject }) => {
|
|
if (isDragReject) {
|
|
return 'Rejected'
|
|
}
|
|
if (isDragAccept) {
|
|
return <DragActiveComponent isDragAccept={isDragAccept} isDragReject={isDragReject} />
|
|
}
|
|
return <ChildComponent isDragAccept={isDragAccept} isDragReject={isDragReject} />
|
|
}}
|
|
</Dropzone>
|
|
)
|
|
const child = dropzone.find(ChildComponent)
|
|
child.simulate('dragEnter', { dataTransfer: { files } })
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files } })
|
|
// make sure we handle any duplicate dragEnter events that the browser may send us
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files } })
|
|
const dragActiveChild = dropzone.find(DragActiveComponent)
|
|
expect(dragActiveChild).toBePresent()
|
|
expect(dragActiveChild).toHaveProp('isDragAccept', true)
|
|
expect(dragActiveChild).toHaveProp('isDragReject', false)
|
|
|
|
dropzone.simulate('dragLeave', { dataTransfer: { files } })
|
|
expect(dropzone.find(DragActiveComponent)).toBeEmpty()
|
|
expect(child).toHaveProp('isDragAccept', false)
|
|
expect(child).toHaveProp('isDragReject', false)
|
|
})
|
|
})
|
|
|
|
describe('onDrop', () => {
|
|
let dropSpy
|
|
let dropAcceptedSpy
|
|
let dropRejectedSpy
|
|
|
|
beforeEach(() => {
|
|
dropSpy = spy()
|
|
dropAcceptedSpy = spy()
|
|
dropRejectedSpy = spy()
|
|
})
|
|
|
|
afterEach(() => {
|
|
dropSpy.reset()
|
|
dropAcceptedSpy.reset()
|
|
dropRejectedSpy.reset()
|
|
})
|
|
|
|
it('should reset the dragActive/dragReject state', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
>
|
|
{props => <DummyChildComponent {...props} />}
|
|
</Dropzone>
|
|
)
|
|
const child = dropzone.find(DummyChildComponent)
|
|
dropzone.simulate('dragEnter', { dataTransfer: { files } })
|
|
expect(child).toHaveProp('isDragActive', true)
|
|
expect(child).toHaveProp('isDragReject', false)
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(child).toHaveProp('isDragActive', false)
|
|
expect(child).toHaveProp('isDragReject', false)
|
|
})
|
|
|
|
it('should add valid files to rejected files on a multple drop when multiple false', () => {
|
|
const dropzone = mount(<Dropzone accept="image/*" onDrop={dropSpy} multiple={false} />)
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
const rejected = dropSpy.firstCall.args[0]
|
|
expect(rejected.length).toEqual(1)
|
|
})
|
|
|
|
it('should add invalid files to rejected when multiple is false', () => {
|
|
const dropzone = mount(<Dropzone accept="image/*" onDrop={dropSpy} multiple={false} />)
|
|
dropzone.simulate('drop', {
|
|
dataTransfer: { files: images.concat(files) }
|
|
})
|
|
const rejected = dropSpy.firstCall.args[1]
|
|
expect(rejected.length).toEqual(2)
|
|
})
|
|
|
|
it('should allow single files to be dropped if multiple is false', () => {
|
|
const dropzone = mount(<Dropzone accept="image/*" onDrop={dropSpy} multiple={false} />)
|
|
|
|
dropzone.simulate('drop', { dataTransfer: { files: [images[0]] } })
|
|
const [accepted, rejected] = dropSpy.firstCall.args
|
|
expect(accepted.length).toEqual(1)
|
|
expect(rejected.length).toEqual(0)
|
|
})
|
|
|
|
it('should take all dropped files if multiple is true', () => {
|
|
const dropzone = mount(<Dropzone onDrop={dropSpy} multiple />)
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(dropSpy.firstCall.args[0][0].name).toEqual(images[0].name)
|
|
expect(dropSpy.firstCall.args[0][1].name).toEqual(images[1].name)
|
|
})
|
|
|
|
it('should set this.isFileDialogActive to false', () => {
|
|
const dropzone = mount(<Dropzone />)
|
|
dropzone.instance().isFileDialogActive = true
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(dropzone.instance().isFileDialogActive).toEqual(false)
|
|
})
|
|
|
|
it('should always call onDrop callback with accepted and rejected arguments', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
accept="image/*"
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toEqual([], [...files])
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
expect(dropSpy.callCount).toEqual(2)
|
|
expect(dropSpy.lastCall.args[0]).toEqual([...images], [])
|
|
dropzone.simulate('drop', {
|
|
dataTransfer: { files: files.concat(images) }
|
|
})
|
|
expect(dropSpy.callCount).toEqual(3)
|
|
expect(dropSpy.lastCall.args[0]).toEqual([...images], [...files])
|
|
})
|
|
|
|
it('should call onDropAccepted callback if some files were accepted', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
accept="image/*"
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(dropAcceptedSpy.callCount).toEqual(0)
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
expect(dropAcceptedSpy.callCount).toEqual(1)
|
|
expect(dropAcceptedSpy.lastCall.args[0]).toEqual([...images])
|
|
dropzone.simulate('drop', {
|
|
dataTransfer: { files: files.concat(images) }
|
|
})
|
|
expect(dropAcceptedSpy.callCount).toEqual(2)
|
|
expect(dropAcceptedSpy.lastCall.args[0]).toEqual([...images])
|
|
})
|
|
|
|
it('should call onDropRejected callback if some files were rejected', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
accept="image/*"
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(dropRejectedSpy.callCount).toEqual(1)
|
|
expect(dropRejectedSpy.lastCall.args[0]).toEqual([...files])
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
expect(dropRejectedSpy.callCount).toEqual(1)
|
|
dropzone.simulate('drop', {
|
|
dataTransfer: { files: files.concat(images) }
|
|
})
|
|
expect(dropRejectedSpy.callCount).toEqual(2)
|
|
expect(dropRejectedSpy.lastCall.args[0]).toEqual([...files])
|
|
})
|
|
|
|
it('applies the accept prop to the dropped files', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
accept="image/*"
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(0)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(1)
|
|
expect(dropAcceptedSpy.callCount).toEqual(0)
|
|
expect(dropRejectedSpy.callCount).toEqual(1)
|
|
expect(dropRejectedSpy.firstCall.args[0]).toHaveLength(1)
|
|
})
|
|
|
|
it('applies the accept prop to the dropped images', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
accept="image/*"
|
|
/>
|
|
)
|
|
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(0)
|
|
expect(dropAcceptedSpy.callCount).toEqual(1)
|
|
expect(dropAcceptedSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(dropRejectedSpy.callCount).toEqual(0)
|
|
})
|
|
|
|
it('accepts a dropped image when Firefox provides a bogus file type', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
accept="image/*"
|
|
/>
|
|
)
|
|
const bogusImages = [
|
|
{
|
|
name: 'bogus.gif',
|
|
size: 1234,
|
|
type: 'application/x-moz-file'
|
|
}
|
|
]
|
|
|
|
dropzone.simulate('drop', { dataTransfer: { files: bogusImages } })
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(1)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(0)
|
|
expect(dropAcceptedSpy.callCount).toEqual(1)
|
|
expect(dropAcceptedSpy.firstCall.args[0]).toHaveLength(1)
|
|
expect(dropRejectedSpy.callCount).toEqual(0)
|
|
})
|
|
|
|
it('accepts all dropped files and images when no accept prop is specified', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', {
|
|
dataTransfer: { files: files.concat(images) }
|
|
})
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(3)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(0)
|
|
expect(dropAcceptedSpy.callCount).toEqual(1)
|
|
expect(dropAcceptedSpy.firstCall.args[0]).toHaveLength(3)
|
|
expect(dropRejectedSpy.callCount).toEqual(0)
|
|
})
|
|
|
|
it('applies the maxSize prop to the dropped files', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
maxSize={1111}
|
|
/>
|
|
)
|
|
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(1)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(0)
|
|
expect(dropAcceptedSpy.callCount).toEqual(1)
|
|
expect(dropAcceptedSpy.firstCall.args[0]).toHaveLength(1)
|
|
expect(dropRejectedSpy.callCount).toEqual(0)
|
|
})
|
|
|
|
it('applies the maxSize prop to the dropped images', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
maxSize={1111}
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(0)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(2)
|
|
expect(dropAcceptedSpy.callCount).toEqual(0)
|
|
expect(dropRejectedSpy.callCount).toEqual(1)
|
|
expect(dropRejectedSpy.firstCall.args[0]).toHaveLength(2)
|
|
})
|
|
|
|
it('applies the minSize prop to the dropped files', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
minSize={1112}
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(0)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(1)
|
|
expect(dropAcceptedSpy.callCount).toEqual(0)
|
|
expect(dropRejectedSpy.callCount).toEqual(1)
|
|
expect(dropRejectedSpy.firstCall.args[0]).toHaveLength(1)
|
|
})
|
|
|
|
it('applies the minSize prop to the dropped images', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
minSize={1112}
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(0)
|
|
expect(dropAcceptedSpy.callCount).toEqual(1)
|
|
expect(dropAcceptedSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(dropRejectedSpy.callCount).toEqual(0)
|
|
})
|
|
|
|
it('accepts all dropped files and images when no size prop is specified', () => {
|
|
const dropzone = mount(
|
|
<Dropzone
|
|
onDrop={dropSpy}
|
|
onDropAccepted={dropAcceptedSpy}
|
|
onDropRejected={dropRejectedSpy}
|
|
/>
|
|
)
|
|
dropzone.simulate('drop', {
|
|
dataTransfer: { files: files.concat(images) }
|
|
})
|
|
expect(dropSpy.callCount).toEqual(1)
|
|
expect(dropSpy.firstCall.args[0]).toHaveLength(3)
|
|
expect(dropSpy.firstCall.args[1]).toHaveLength(0)
|
|
expect(dropAcceptedSpy.callCount).toEqual(1)
|
|
expect(dropAcceptedSpy.firstCall.args[0]).toHaveLength(3)
|
|
expect(dropRejectedSpy.callCount).toEqual(0)
|
|
})
|
|
})
|
|
|
|
describe('preview', () => {
|
|
it('should generate previews for non-images', () => {
|
|
const dropSpy = spy()
|
|
const dropzone = mount(<Dropzone onDrop={dropSpy} />)
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(Object.keys(dropSpy.firstCall.args[0][0])).toContain('preview')
|
|
expect(dropSpy.firstCall.args[0][0].preview).toContain('data://file1.pdf')
|
|
})
|
|
|
|
it('should generate previews for images', () => {
|
|
const dropSpy = spy()
|
|
const dropzone = mount(<Dropzone onDrop={dropSpy} />)
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
expect(Object.keys(dropSpy.firstCall.args[0][0])).toContain('preview')
|
|
expect(dropSpy.firstCall.args[0][0].preview).toContain('data://cats.gif')
|
|
})
|
|
|
|
it('should not throw error when preview cannot be created', () => {
|
|
const dropSpy = spy()
|
|
const dropzone = mount(<Dropzone onDrop={dropSpy} />)
|
|
|
|
dropzone.simulate('drop', { dataTransfer: { files: ['bad_val'] } })
|
|
|
|
expect(Object.keys(dropSpy.firstCall.args[1][0])).not.toContain('preview')
|
|
})
|
|
|
|
it('should not generate previews if disablePreview is true', () => {
|
|
const dropSpy = spy()
|
|
const dropzone = mount(<Dropzone disablePreview onDrop={dropSpy} />)
|
|
dropzone.simulate('drop', { dataTransfer: { files: images } })
|
|
dropzone.simulate('drop', { dataTransfer: { files } })
|
|
expect(dropSpy.callCount).toEqual(2)
|
|
expect(Object.keys(dropSpy.firstCall.args[0][0])).not.toContain('preview')
|
|
expect(Object.keys(dropSpy.lastCall.args[0][0])).not.toContain('preview')
|
|
})
|
|
})
|
|
|
|
describe('onClick', () => {})
|
|
|
|
describe('onCancel', () => {
|
|
it('should not invoke onFileDialogCancel everytime window receives focus', done => {
|
|
const onCancelSpy = spy()
|
|
mount(<Dropzone id="on-cancel-example" onFileDialogCancel={onCancelSpy} />)
|
|
// Simulated DOM event - onfocus
|
|
document.body.addEventListener('focus', () => {})
|
|
const evt = document.createEvent('HTMLEvents')
|
|
evt.initEvent('focus', false, true)
|
|
document.body.dispatchEvent(evt)
|
|
// setTimeout to match the event callback from actual Component
|
|
setTimeout(() => {
|
|
expect(onCancelSpy.callCount).toEqual(0)
|
|
done()
|
|
}, 300)
|
|
})
|
|
|
|
it('should invoke onFileDialogCancel when window receives focus via cancel button', done => {
|
|
const onCancelSpy = spy()
|
|
const component = mount(
|
|
<Dropzone className="dropzone-content" onFileDialogCancel={onCancelSpy} />
|
|
)
|
|
|
|
// Test / invoke the click event
|
|
spy(component.instance(), 'open')
|
|
component.simulate('click')
|
|
|
|
setTimeout(() => {
|
|
expect(component.instance().open.callCount).toEqual(1)
|
|
|
|
// Simulated DOM event - onfocus
|
|
document.body.addEventListener('focus', () => {})
|
|
const evt = document.createEvent('HTMLEvents')
|
|
evt.initEvent('focus', false, true)
|
|
document.body.dispatchEvent(evt)
|
|
|
|
// setTimeout to match the event callback from actual Component
|
|
setTimeout(() => {
|
|
expect(onCancelSpy.callCount).toEqual(1)
|
|
done()
|
|
}, 300)
|
|
}, 0)
|
|
})
|
|
})
|
|
|
|
describe('nested Dropzone component behavior', () => {
|
|
let outerDropzone
|
|
let innerDropzone
|
|
let outerDropSpy
|
|
let outerDropAcceptedSpy
|
|
let outerDropRejectedSpy
|
|
let innerDropSpy
|
|
let innerDropAcceptedSpy
|
|
let innerDropRejectedSpy
|
|
|
|
const InnerDragAccepted = () => <p>Accepted</p>
|
|
const InnerDragRejected = () => <p>Rejected</p>
|
|
const InnerDropzone = () => (
|
|
<Dropzone
|
|
onDrop={innerDropSpy}
|
|
onDropAccepted={innerDropAcceptedSpy}
|
|
onDropRejected={innerDropRejectedSpy}
|
|
accept="image/*"
|
|
>
|
|
{({ isDragActive, isDragReject }) => {
|
|
if (isDragReject) return <InnerDragRejected />
|
|
if (isDragActive) return <InnerDragAccepted />
|
|
return <p>No drag</p>
|
|
}}
|
|
</Dropzone>
|
|
)
|
|
|
|
describe('dropping on the inner dropzone', () => {
|
|
it('mounts both dropzones', () => {
|
|
outerDropSpy = spy()
|
|
outerDropAcceptedSpy = spy()
|
|
outerDropRejectedSpy = spy()
|
|
innerDropSpy = spy()
|
|
innerDropAcceptedSpy = spy()
|
|
innerDropRejectedSpy = spy()
|
|
outerDropzone = mount(
|
|
<Dropzone
|
|
onDrop={outerDropSpy}
|
|
onDropAccepted={outerDropAcceptedSpy}
|
|
onDropRejected={outerDropRejectedSpy}
|
|
accept="image/*"
|
|
>
|
|
{props => <InnerDropzone {...props} />}
|
|
</Dropzone>
|
|
)
|
|
innerDropzone = outerDropzone.find(InnerDropzone)
|
|
})
|
|
|
|
it('does dragEnter on both dropzones', () => {
|
|
innerDropzone.simulate('dragEnter', {
|
|
dataTransfer: { files: images }
|
|
})
|
|
expect(innerDropzone).toHaveProp('isDragActive', true)
|
|
expect(innerDropzone).toHaveProp('isDragReject', false)
|
|
expect(innerDropzone.find(InnerDragAccepted).exists()).toEqual(true)
|
|
expect(innerDropzone.find(InnerDragRejected).exists()).toEqual(false)
|
|
})
|
|
|
|
it('drops on the child dropzone', () => {
|
|
innerDropzone.simulate('drop', {
|
|
dataTransfer: { files: files.concat(images) }
|
|
})
|
|
})
|
|
|
|
it('accepts the drop on the inner dropzone', () => {
|
|
expect(innerDropSpy.callCount).toEqual(1)
|
|
expect(innerDropSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(innerDropSpy.firstCall.args[1]).toHaveLength(1)
|
|
expect(innerDropAcceptedSpy.callCount).toEqual(1)
|
|
expect(innerDropAcceptedSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(innerDropRejectedSpy.callCount).toEqual(1)
|
|
expect(innerDropRejectedSpy.firstCall.args[0]).toHaveLength(1)
|
|
expect(innerDropzone.find(InnerDragAccepted).exists()).toEqual(false)
|
|
expect(innerDropzone.find(InnerDragRejected).exists()).toEqual(false)
|
|
})
|
|
|
|
it('also accepts the drop on the outer dropzone', () => {
|
|
expect(outerDropSpy.callCount).toEqual(1)
|
|
expect(outerDropSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(outerDropSpy.firstCall.args[1]).toHaveLength(1)
|
|
expect(outerDropAcceptedSpy.callCount).toEqual(1)
|
|
expect(outerDropAcceptedSpy.firstCall.args[0]).toHaveLength(2)
|
|
expect(outerDropRejectedSpy.callCount).toEqual(1)
|
|
expect(outerDropRejectedSpy.firstCall.args[0]).toHaveLength(1)
|
|
expect(innerDropzone).toHaveProp('isDragActive', false)
|
|
expect(innerDropzone).toHaveProp('isDragReject', false)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('behavior', () => {
|
|
it('does not throw an error when html is dropped instead of files and multiple is false', () => {
|
|
const dropzone = mount(<Dropzone multiple={false} />)
|
|
|
|
const fn = () => dropzone.simulate('drop', { dataTransfer: { files: [] } })
|
|
expect(fn).not.toThrow()
|
|
})
|
|
|
|
it('does not allow actions when disabled props is true', done => {
|
|
const dropzone = mount(<Dropzone disabled />)
|
|
|
|
spy(dropzone.instance(), 'open')
|
|
dropzone.simulate('click')
|
|
setTimeout(() => {
|
|
expect(dropzone.instance().open.callCount).toEqual(0)
|
|
done()
|
|
}, 0)
|
|
})
|
|
|
|
it('when toggle disabled props, Dropzone works as expected', done => {
|
|
const dropzone = mount(<Dropzone disabled />)
|
|
spy(dropzone.instance(), 'open')
|
|
|
|
dropzone.setProps({ disabled: false })
|
|
|
|
dropzone.simulate('click')
|
|
setTimeout(() => {
|
|
expect(dropzone.instance().open.callCount).toEqual(1)
|
|
done()
|
|
}, 0)
|
|
})
|
|
})
|
|
})
|