diff --git a/__tests__/vue/dblclick.js b/__tests__/vue/dblclick.js new file mode 100644 index 00000000..4010d3d3 --- /dev/null +++ b/__tests__/vue/dblclick.js @@ -0,0 +1,125 @@ +import { render, cleanup } from "@testing-library/vue"; +import "@testing-library/jest-dom/extend-expect"; +import userEvent from "../../src"; + +afterEach(cleanup); + +describe("userEvent.dblClick", () => { + it.each(["input", "textarea"])( + "should fire the correct events for <%s>", + type => { + const events = []; + const eventsHandler = jest.fn(evt => events.push(evt.type)); + const { getByTestId } = render({ + render: function(h) { + return h(type, { + attrs: { + "data-testid": "element" + }, + on: { + mouseover: eventsHandler, + mousemove: eventsHandler, + mousedown: eventsHandler, + focus: eventsHandler, + mouseup: eventsHandler, + click: eventsHandler, + dblclick: eventsHandler + } + }); + } + }); + + userEvent.dblClick(getByTestId("element")); + + expect(events).toEqual([ + "mouseover", + "mousemove", + "mousedown", + "focus", + "mouseup", + "click", + "mousedown", + "mouseup", + "click", + "dblclick" + ]); + } + ); + + it('should fire the correct events for ', () => { + const events = []; + const eventsHandler = jest.fn(evt => events.push(evt.type)); + + const { getByTestId } = render({ + render: function(h) { + return h("input", { + attrs: { + type: "checkbox", + "data-testid": "element" + }, + on: { + mouseover: eventsHandler, + mousemove: eventsHandler, + mousedown: eventsHandler, + focus: eventsHandler, + mouseup: eventsHandler, + click: eventsHandler, + change: eventsHandler + } + }); + } + }); + + userEvent.dblClick(getByTestId("element")); + + expect(events).toEqual([ + "mouseover", + "mousemove", + "mousedown", + "mouseup", + "click", + "change", + "mousedown", + "mouseup", + "click", + "change" + ]); + + expect(getByTestId("element")).toHaveProperty("checked", false); + }); + + it("should fire the correct events for
", () => { + const events = []; + const eventsHandler = jest.fn(evt => events.push(evt.type)); + const { getByTestId } = render({ + render: function(h) { + return h("div", { + attrs: { + "data-testid": "div" + }, + on: { + mouseover: eventsHandler, + mousemove: eventsHandler, + mousedown: eventsHandler, + focus: eventsHandler, + mouseup: eventsHandler, + click: eventsHandler, + change: eventsHandler + } + }); + } + }); + + userEvent.dblClick(getByTestId("div")); + expect(events).toEqual([ + "mouseover", + "mousemove", + "mousedown", + "mouseup", + "click", + "mousedown", + "mouseup", + "click" + ]); + }); +}); diff --git a/__tests__/vue/selectoptions.js b/__tests__/vue/selectoptions.js new file mode 100644 index 00000000..0d178ef8 --- /dev/null +++ b/__tests__/vue/selectoptions.js @@ -0,0 +1,229 @@ +import { render, cleanup } from "@testing-library/vue"; +import "@testing-library/jest-dom/extend-expect"; +import userEvent from "../../src"; + +afterEach(cleanup); + +describe("userEvent.selectOptions", () => { + it.each(["select", "select multiple"])( + "should fire the correct events for <%s>", + type => { + const events = []; + const eventsHandler = jest.fn(evt => events.push(evt.type)); + const multiple = type === "select multiple"; + const eventHandlers = { + mouseover: eventsHandler, + mousemove: eventsHandler, + mousedown: eventsHandler, + focus: eventsHandler, + mouseup: eventsHandler, + click: eventsHandler + }; + + const { getByTestId } = render({ + render: function(h) { + return h( + "select", + { + attrs: { + "data-testid": "element", + ...(multiple && { multiple: true }) + }, + on: eventHandlers + }, + [ + h("option", { attrs: { value: "1" } }, "1"), + h("option", { attrs: { value: "2" } }, "2"), + h("option", { attrs: { value: "3" } }, "3") + ] + ); + } + }); + + userEvent.selectOptions(getByTestId("element"), "1"); + + expect(events).toEqual([ + "mouseover", + "mousemove", + "mousedown", + "focus", + "mouseup", + "click", + "mouseover", // The events repeat because we click on the child OPTION too + "mousemove", // But these specifically are the events bubbling up to the ", () => { + function handleEvent(evt) { + const optValue = parseInt(evt.target.value); + events[optValue] = [...(events[optValue] || []), evt.type]; + } + + const events = []; + const eventsHandler = jest.fn(handleEvent); + const eventHandlers = { + mouseover: eventsHandler, + mousemove: eventsHandler, + mousedown: eventsHandler, + focus: eventsHandler, + mouseup: eventsHandler, + click: eventsHandler + }; + + const { getByTestId } = render({ + render: function(h) { + return h( + "select", + { + attrs: { + "data-testid": "element" + } + }, + [ + h("option", { attrs: { value: "1" }, on: eventHandlers }, "1"), + h("option", { attrs: { value: "2" }, on: eventHandlers }, "2"), + h("option", { attrs: { value: "3" }, on: eventHandlers }, "3") + ] + ); + } + }); + + userEvent.selectOptions(getByTestId("element"), ["2"]); + + expect(events[1]).toBe(undefined); + expect(events[3]).toBe(undefined); + expect(events[2]).toEqual([ + "mouseover", + "mousemove", + "mousedown", + "focus", + "mouseup", + "click" + ]); + }); + + it("should fire the correct events on selected OPTION children with + + + + + ` + }); + + userEvent.selectOptions(getByTestId("element"), ["1", "3"]); + + expect(getByTestId("val1").selected).toBe(true); + expect(getByTestId("val2").selected).toBe(false); + expect(getByTestId("val3").selected).toBe(true); + }); + + it("sets the selected prop on the selected OPTION using htmlFor", () => { + const { getByTestId } = render({ + template: ` +
+ + +
` + }); + + userEvent.selectOptions(getByTestId("element"), "2"); + + expect(getByTestId("val1").selected).toBe(false); + expect(getByTestId("val2").selected).toBe(true); + expect(getByTestId("val3").selected).toBe(false); + }); + + it("sets the selected prop on the selected OPTION using nested SELECT", () => { + const { getByTestId } = render({ + template: ` +
+ +
` + }); + + userEvent.selectOptions(getByTestId("element"), "2"); + + expect(getByTestId("val1").selected).toBe(false); + expect(getByTestId("val2").selected).toBe(true); + expect(getByTestId("val3").selected).toBe(false); + }); +}); diff --git a/__tests__/vue/type.js b/__tests__/vue/type.js new file mode 100644 index 00000000..6f758ee9 --- /dev/null +++ b/__tests__/vue/type.js @@ -0,0 +1,99 @@ +import { cleanup, render, wait, fireEvent } from "@testing-library/vue"; +import "@testing-library/jest-dom/extend-expect"; +import userEvent from "../../src"; + +afterEach(cleanup); + +const renderComponent = (type, events = {}) => + render({ + render: function(h) { + return h(type, { + attrs: { "data-testid": "input" }, + on: events + }); + } + }); + +describe("userEvent.type", () => { + it.each(["input", "textarea"])("should type text in <%s>", type => { + const input = jest.fn(); + + const { getByTestId } = renderComponent(type, { input }); + + const text = "Hello, world!"; + userEvent.type(getByTestId("input"), text); + + expect(input).toHaveBeenCalledTimes(text.length); + expect(getByTestId("input")).toHaveProperty("value", text); + }); + + it("should not type when event.preventDefault() is called", () => { + const input = jest.fn(); + const change = jest.fn(); + const keydown = jest + .fn() + .mockImplementation(event => event.preventDefault()); + + const { getByTestId } = renderComponent("input", { + input, + keydown, + change + }); + + const text = "Hello, world!"; + userEvent.type(getByTestId("input"), text); + expect(keydown).toHaveBeenCalledTimes(text.length); + expect(change).toHaveBeenCalledTimes(0); + expect(input).toHaveBeenCalledTimes(0); + expect(getByTestId("input")).not.toHaveProperty("value", text); + }); + + it("should delay the typing when opts.delay is not 0", async () => { + jest.useFakeTimers(); + const change = jest.fn(); + const input = jest.fn(); + const { getByTestId } = renderComponent("input", { change, input }); + const text = "Hello, world!"; + const delay = 10; + + userEvent.type(getByTestId("input"), text, { + delay + }); + + expect(input).not.toHaveBeenCalled(); + expect(getByTestId("input")).not.toHaveProperty("value", text); + + for (let i = 0; i < text.length; i++) { + jest.advanceTimersByTime(delay); + + await wait(() => expect(input).toHaveBeenCalledTimes(i + 1)); + + expect(getByTestId("input")).toHaveProperty( + "value", + text.slice(0, i + 1) + ); + } + + // Vue's change event is not emitted until blurring the input + expect(change).not.toHaveBeenCalled(); + fireEvent.blur(getByTestId("input")); + await wait(() => expect(change).toHaveBeenCalledTimes(1)); + }); + + it.each(["input", "textarea"])( + "should type text in <%s> all at once", + type => { + const input = jest.fn(); + + const { getByTestId } = renderComponent(type, { input }); + const text = "Hello, world!"; + + userEvent.type(getByTestId("input"), text, { + allAtOnce: true + }); + + expect(getByTestId("input")).toHaveProperty("value", text); + expect(input).toHaveBeenCalledTimes(1); + } + ); +}); diff --git a/package-lock.json b/package-lock.json index 8e926657..1cdfe2fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2382,15 +2382,43 @@ } }, "@testing-library/vue": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@testing-library/vue/-/vue-1.1.0.tgz", - "integrity": "sha512-7y3Y0wv8QBGem3c1LZAkWJFK0QS7LTg38eIqgDZ2cRwFyEhsTkIIeKJ0A4C9iMZ7lZKtPuisWmb+Joc+rCzEtQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/vue/-/vue-2.0.0.tgz", + "integrity": "sha512-6hsQQ89QQPQCUak9N2DJXcWR2MeVQYdrt4S6N0YEf4UYDJf30SWYfHho8dHCsypCo34HHdqDXPdEQ7ktTCsxqA==", "dev": true, "requires": { - "@testing-library/dom": "^5.2.0", - "@vue/test-utils": "^1.0.0-beta.29", - "vue": "^2.6.10", - "vue-template-compiler": "^2.6.10" + "@testing-library/dom": "^6.0.0", + "@vue/test-utils": "^1.0.0-beta.29" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", + "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "@testing-library/dom": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-6.0.0.tgz", + "integrity": "sha512-B5XTz3uMsbqbdR9CZlnwpZjTE3fCWuqRkz/zvDc2Ej/vuHmTM0Ur2v0XPwr7usWfGIBsahEK5HL1E91+4IFiBg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "@sheerun/mutationobserver-shim": "^0.3.2", + "aria-query": "3.0.0", + "pretty-format": "^24.8.0", + "wait-for-expect": "^1.3.0" + } + }, + "wait-for-expect": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-1.3.0.tgz", + "integrity": "sha512-8fJU7jiA96HfGPt+P/UilelSAZfhMBJ52YhKzlmZQvKEZU2EcD1GQ0yqGB6liLdHjYtYAoGVigYwdxr5rktvzA==", + "dev": true + } } }, "@types/babel__core": { diff --git a/package.json b/package.json index 0d324e6b..55076e83 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@testing-library/dom": "5.6.0", "@testing-library/jest-dom": "4.0.0", "@testing-library/react": "8.0.5", - "@testing-library/vue": "1.1.0", + "@testing-library/vue": "^2.0.0", "all-contributors-cli": "6.8.1", "babel-core": "7.0.0-bridge.0", "babel-jest": "24.8.0", @@ -52,7 +52,9 @@ "react-dom": "16.8.6", "regenerator-runtime": "0.13.2", "semantic-release": "15.13.18", - "travis-deploy-once": "5.0.11" + "travis-deploy-once": "5.0.11", + "vue": "^2.6.10", + "vue-template-compiler": "^2.6.10" }, "peerDependencies": { "@testing-library/dom": ">=5"