react-testing-library version: 6.0.1react version: 16.8.1node version: 8.9.4npm version: 5.6.0jest version: 24.0.0Below is a simplified, generic version of my code
export const FileDropZone = props => {
const handleDrop = useCallback(
event => {
event.stopPropagation();
event.preventDefault();
handleFiles(event.dataTransfer.files);
},
[handleFiles]
);
// Rest of code
return (
<div
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
Drag File Here
</div>
);
}
describe('FileDropZone', () => {
test('should handle a file being dropped in the drop zone', () => {
const file = new File(['(⌐□_□)'], 'chucknorris.png', {
type: 'image/png',
})
const {getByText, queryByText} = render(<FileDropZone {...props} />);
expect(queryByText('chucknorris.png')).toBeNull();
const dropZone = getByText('Drag File Here');
act(() => {
fireEvent.drop(dropZone, {dataTransfer: {files: [file]}});
});
expect(queryByText('chucknorris.png')).toBeInTheDocument();
})
})
I am trying to test a drop event that is used for uploading files via drag-and-drop
When I run my unit test, the event is firing, but I get the following error
TypeError: Cannot read property 'files' of undefined
When I do a console.log of the event it shows that event.dataTransfer is undefined.
Unable to provide a link to code repository due to the repo being private
Unable to test file upload via drag-and-drop using react-testing-library
Hi @christopher-schroer,
I'm not sure what this could be. Without a simple reproduction in a codesandbox there's only so much we can do.
@kentcdodds I figured as much. Once I have some free time, I will try to create a repo for sharing. Unfortunately, I'm going to be on vacation until Apr 11.
I managed to put together a basic example
https://github.com/christopher-schroer/react-testing-library-drop-issue
Clone it, run npm install, and then run npm test
Ah, this appears to be an issue with jsdom not supporting dataTransfer. Based on these tests.
You could file an issue against jsdom to see what it would take to contribute support for dataTransfer.
Closing this in favor of: https://github.com/jsdom/jsdom/issues/1568
Thanks.
For anyone that finds their way to this ticket via a search whilst they are stuck on the same problem, I managed to work around the limitation in jsdom using Object.defineProperty like so:
const file = new File([""], "guybrushthreepwood.png", {
type: "image/png",
}
const mockDropEvent = new window.Event("drop")
Object.defineProperty(event, "dataTransfer", {
value: {
files: [file]
}
})
const target = getByText("Drag File Here")
fireEvent(target, mockDropEvent)
I'm not sure how evil this approach is, but it might come in handy :)
Seems legit. Would be cool to get this added to the examples: https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples
@andypearson I wasn't able to get that exact code to work, but I got a very similar version working:
const fileDropzone = getByText('Drag and Drop Excel Files Here');
const fileDropEvent = createEvent.drop(fileDropzone);
const fileList = [file];
Object.defineProperty(fileDropEvent, 'dataTransfer', {
value: {
files: {
item: itemIndex => fileList[itemIndex],
length: fileList.length,
},
},
});
fireEvent(fileDropzone, fileDropEvent);
Key differences here:
I'm using the RTL createEvent to create the event. window.Event wasn't working for me.
Rather than return files: [file] I created the item property which mocks the FileItem object which has the item property that looks roughly like the function I defined for it. Using an actual FileItem object is a major headache, but if someone else wanted to look into it more here is a pretty in depth post that goes into it: https://github.com/jsdom/jsdom/issues/1272#issuecomment-486088445
@kentcdodds I'll see what I can do!
For anyone testing drag and drop and wants to test setData and the above two approach are not working
Using
const mockEv = new window.Event('dragstart')
//Object.defineProperty ...
fireEvent(draggableNode, mockEv)
doesn't fire event, resulting in handleDragStart() not being called
Second approach with using createEvent fires the desired event, but, Object.defineProperty(event, 'dataTransfer', { setData: jest.fn() }) doesn't actually defines the desired 'dataTransfer' property.
A quick workaround for me was to use Object.assign(event, {dataTransfer: {setData: jest.fn()}})
Full working example as of testing-library/[email protected]
`test('on drag start sets dataTransfer property correctly', () => {
const setData = jest.fn()
const ev = {
dataTransfer: {
setData
}
}
const {
getByTestId
} = render(
const draggableOpNode = getByTestId('ops-bucket-item');
const mockDropEvent = createEvent.dragStart(draggableOpNode);
Object.assign(mockDropEvent, ev);
fireEvent(draggableOpNode, mockDropEvent)
expect(setData).toHaveBeenCalledTimes(1)
expect(setData).toHaveBeenCalledWith({ /*your correct data*/ })`
Most helpful comment
For anyone that finds their way to this ticket via a search whilst they are stuck on the same problem, I managed to work around the limitation in jsdom using
Object.definePropertylike so:I'm not sure how evil this approach is, but it might come in handy :)