New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(upload): Add upload API #279
Changes from 1 commit
083d954
b2b7341
dbc5675
d3c5e06
d3600f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import React from "react"; | ||
import { cleanup, render, fireEvent } from "@testing-library/react"; | ||
import "@testing-library/jest-dom/extend-expect"; | ||
import userEvent from "../../src"; | ||
|
||
afterEach(cleanup); | ||
|
||
describe("userEvent.upload", () => { | ||
it("should fire the correct events for input", () => { | ||
const file = new File(["hello"], "hello.png", { type: "image/png" }); | ||
const events = []; | ||
const eventsHandler = jest.fn((evt) => events.push(evt.type)); | ||
const eventHandlers = { | ||
onMouseOver: eventsHandler, | ||
onMouseMove: eventsHandler, | ||
onMouseDown: eventsHandler, | ||
onFocus: eventsHandler, | ||
onMouseUp: eventsHandler, | ||
onClick: eventsHandler, | ||
}; | ||
|
||
const { getByTestId } = render( | ||
<input type="file" data-testid="element" {...eventHandlers} /> | ||
); | ||
|
||
userEvent.upload(getByTestId("element"), file); | ||
|
||
expect(events).toEqual([ | ||
"mouseover", | ||
"mousemove", | ||
"mousedown", | ||
"focus", | ||
"mouseup", | ||
"click", | ||
]); | ||
}); | ||
|
||
it("should fire the correct events with label", () => { | ||
const file = new File(["hello"], "hello.png", { type: "image/png" }); | ||
|
||
const inputEvents = []; | ||
const labelEvents = []; | ||
const eventsHandler = (events) => jest.fn((evt) => events.push(evt.type)); | ||
|
||
const getEventHandlers = (events) => ({ | ||
onMouseOver: eventsHandler(events), | ||
onMouseMove: eventsHandler(events), | ||
onMouseDown: eventsHandler(events), | ||
onFocus: eventsHandler(events), | ||
onMouseUp: eventsHandler(events), | ||
onClick: eventsHandler(events), | ||
}); | ||
|
||
const { getByTestId } = render( | ||
<> | ||
<label | ||
htmlFor="element" | ||
data-testid="label" | ||
{...getEventHandlers(labelEvents)} | ||
> | ||
Element | ||
</label> | ||
<input type="file" id="element" {...getEventHandlers(inputEvents)} /> | ||
</> | ||
); | ||
|
||
userEvent.upload(getByTestId("label"), file); | ||
|
||
expect(inputEvents).toEqual(["focus", "click"]); | ||
expect(labelEvents).toEqual([ | ||
"mouseover", | ||
"mousemove", | ||
"mousedown", | ||
"mouseup", | ||
"click", | ||
]); | ||
}); | ||
|
||
it("should upload the file", () => { | ||
const file = new File(["hello"], "hello.png", { type: "image/png" }); | ||
const { getByTestId } = render(<input type="file" data-testid="element" />); | ||
const input = getByTestId("element"); | ||
|
||
userEvent.upload(input, file); | ||
|
||
expect(input.files[0]).toStrictEqual(file); | ||
expect(input.files.item(0)).toStrictEqual(file); | ||
expect(input.files).toHaveLength(1); | ||
|
||
fireEvent.change(input, { | ||
target: { files: { item: () => {}, length: 0 } }, | ||
}); | ||
|
||
expect(input.files[0]).toBeUndefined(); | ||
expect(input.files.item[0]).toBeUndefined(); | ||
expect(input.files).toHaveLength(0); | ||
}); | ||
|
||
it("should upload multiple files", () => { | ||
const files = [ | ||
new File(["hello"], "hello.png", { type: "image/png" }), | ||
new File(["there"], "there.png", { type: "image/png" }), | ||
]; | ||
const { getByTestId } = render( | ||
<input type="file" multiple data-testid="element" /> | ||
); | ||
const input = getByTestId("element"); | ||
|
||
userEvent.upload(input, files); | ||
|
||
expect(input.files[0]).toStrictEqual(files[0]); | ||
expect(input.files.item(0)).toStrictEqual(files[0]); | ||
expect(input.files[1]).toStrictEqual(files[1]); | ||
expect(input.files.item(1)).toStrictEqual(files[1]); | ||
expect(input.files).toHaveLength(2); | ||
|
||
fireEvent.change(input, { | ||
target: { files: { item: () => {}, length: 0 } }, | ||
}); | ||
|
||
expect(input.files[0]).toBeUndefined(); | ||
expect(input.files.item[0]).toBeUndefined(); | ||
expect(input.files).toHaveLength(0); | ||
}); | ||
|
||
it("should not upload when is disabled", () => { | ||
const file = new File(["hello"], "hello.png", { type: "image/png" }); | ||
const { getByTestId } = render( | ||
<input type="file" data-testid="element" disabled /> | ||
); | ||
|
||
const input = getByTestId("element"); | ||
|
||
userEvent.upload(input, file); | ||
|
||
expect(input.files[0]).toBeUndefined(); | ||
expect(input.files.item[0]).toBeUndefined(); | ||
expect(input.files).toHaveLength(0); | ||
}); | ||
|
||
it("should not fire blur on current element if is the same as previous", () => { | ||
const file = new File(["hello"], "hello.png", { type: "image/png" }); | ||
const onBlur = jest.fn(); | ||
const { getByTestId } = render( | ||
<input type="file" data-testid="element" onBlur={onBlur} /> | ||
); | ||
const input = getByTestId("element"); | ||
|
||
userEvent.upload(input, file); | ||
|
||
expect(onBlur).not.toHaveBeenCalled(); | ||
|
||
userEvent.upload(input, file); | ||
|
||
expect(onBlur).not.toHaveBeenCalled(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -223,6 +223,13 @@ const userEvent = { | |||||
clear(element) { | ||||||
if (element.disabled) return; | ||||||
|
||||||
if (element.type === "file") { | ||||||
fireEvent.change(element, { | ||||||
target: { files: { item: () => {}, length: 0 } }, | ||||||
}); | ||||||
return; | ||||||
} | ||||||
|
||||||
selectAll(element); | ||||||
backspace(element); | ||||||
element.addEventListener("blur", fireChangeEvent); | ||||||
|
@@ -294,6 +301,34 @@ const userEvent = { | |||||
element.addEventListener("blur", fireChangeEvent); | ||||||
}, | ||||||
|
||||||
upload(element, fileOrFiles, init) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it not make sense to allow configurability of both events?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, looks reasonable but here I'm only following There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now I've catched it, thanks for guiding 😄 |
||||||
if (element.disabled) return; | ||||||
const focusedElement = element.ownerDocument.activeElement; | ||||||
|
||||||
let files; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||||||
|
||||||
if (element.tagName === "LABEL") { | ||||||
clickLabel(element); | ||||||
const inputElement = element.htmlFor | ||||||
? document.getElementById(element.htmlFor) | ||||||
: querySelector("input"); | ||||||
files = inputElement.multiple ? fileOrFiles : [fileOrFiles]; | ||||||
} else { | ||||||
files = element.multiple ? fileOrFiles : [fileOrFiles]; | ||||||
clickElement(element, focusedElement, init); | ||||||
} | ||||||
|
||||||
fireEvent.change(element, { | ||||||
target: { | ||||||
files: { | ||||||
length: files.length, | ||||||
item: (index) => files[index], | ||||||
...{ ...files }, | ||||||
vadimshvetsov marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
}, | ||||||
}, | ||||||
}); | ||||||
}, | ||||||
|
||||||
tab({ shift = false, focusTrap = document } = {}) { | ||||||
const focusableElements = focusTrap.querySelectorAll( | ||||||
"input, button, select, textarea, a[href], [tabindex]" | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second argument might be single file or array of files. I'm not sure about that, may be second arg must be array only? But personally I think it's inconvenient to always remember about wrapping file to an array.