hmsk / jest-matcher-vue-test-utils

✨ Cute jest matchers to test Vue components with vue-test-utils
33 stars 3 forks source link
jest jest-matchers vue vue-test-utils

jest-matcher-vue-test-utils

npm GitHub Workflow Status

Cute matchers for Jest to test Vue components with Vue Test Utils.

You can write tests for Vue component/store intuitively ⚡️

it("Emits 'select' event by clicking PrimaryButton", () => {
  const wrapper = shallowMount(Component);

  expect(wrapper.emitted().select).toBeUndefined();
  wrapper.find(PrimaryButton).vm.$emit("click");
  expect(wrapper.emitted().select[0]).toBeTruthy();
});

becomes

it("Emits 'select' event by clicking PrimaryButton", () => {
  const wrapper = shallowMount(Component);

  expect(() => {
    wrapper.find(PrimaryButton).vm.$emit("click");
  }).toEmit(wrapper, "select");
});

And all matchers have type definition and doc 💇‍♂️

190607_jest_matcher_infer

Installation

Get from npm:

$ npm install -D jest-matcher-vue-test-utils

Then, register matchers on your jest process:

import vueTestUtilMatchers from "jest-matcher-vue-test-utils";
expect.extend({ ...vueTestUtilMatchers });

Provided Matchers

Existence on Wrapper

toShow

Assert the function shows a content on Wrapper of vue-test-utils ```js // error-message.vue ... data: function () { return { isError: false } }, methods: { showError () { this.isError = true; } } ``` ```js import Component from "./error-message.vue"; it("show error by showError", async () => { return expect(async () => { wrapper.vm.showError(); await wrapper.vm.$nextTick(); }).toShow(wrapper, "p.error"); // Passes }); ```

toHide

Assert the function hides a content on Wrapper of vue-test-utils ```js // error-message.vue ... data: function () { return { isError: true } }, methods: { hideError () { this.isError = false; } } ``` ```js import Component from "./error-message.vue"; it("show error by showError", async () => { return expect(async () => { wrapper.vm.hideError(); await wrapper.vm.$nextTick(); }).toHide(wrapper, "p.error"); // Passes }); ```

Events on Wrapper

toEmit / toEmitOnRoot

Assert the action emits the event (with the payload optionally) on Wrapper of vue-test-utils ```js // event.vue ``` ```js import Component from "./event.vue"; it("emits special event by click", () => { const wrapper = shallowMount(Component); expect(() => wrapper.trigger("click")).toEmit(wrapper, "special"); // Passes expect(() => wrapper.trigger("click")).toEmit(wrapper, "special", "clicked"); // Passes }); ``` Async function is supported as well. ```js it("emits special event by click", async () => { const wrapper = shallowMount(Component); return expect(async () => triggersEventAsynchronously()).toEmit(wrapper, "special", "clicked"); // Passes }); ``` `toEmitOnRoot` inspects whether the event is emitted on `$root` of Vue instance.

toHaveEmitted / toHaveEmittedOnRoot

Assert the event is emitted (with the payload optionally) on Wrapper of vue-test-utils ```js // event.vue ``` ```js import Component from "./event.vue"; it("emits special event by click", () => { const wrapper = shallowMount(Component); wrapper.trigger("click"); expect(wrapper).toHaveEmitted("special"); // Passes expect(wrapper).toHaveEmitted("special", "clicked"); // Passes }); ``` `toHaveEmittedOnRoot` inspects whether the event is emitted on `$root` of Vue instance.

Vuex actions/mutations

toDispatch

Assert the function dispatches Vuex action on the component ```js // click-store.vue ``` ```js import Component from "./click-store.vue"; it("Dispatches the action on store by click", () => { const wrapper = shallowMount(Component); expect(() => { wrapper.trigger("click"); }).toDispatch(wrapper, "awesomeAction"); // Passes expect(() => { wrapper.trigger("click"); }).toDispatch(wrapper, "awesomeAction", 'click'); // Passes }); ``` Async function is supported as well. ```js it("dispatches the action on store by click", async () => { return expect(async () => { dispatchEventAsynchronosly(); }).toDispatch(wrapper, "awesomeAction", 'click'); // Passes }); ```

toCommit (TBD)

Assert the store mutation is committed ```js // click-store.vue ``` ```js import Component from "./click-store.vue"; it("Commits the mutation on store by click", () => { const wrapper = shallowMount(Component); expect(() => { wrapper.trigger("click"); }).toCommit(wrapper, "importantMutation"); // Passes expect(() => { wrapper.trigger("click"); }).toCommit(wrapper, "importantMutation", 'click'); // Passes }); ```

toHaveDispatched

Assert a component has dispatched Vuex action ```js // click-store.vue ``` ```js import Component from "./click-store.vue"; import { vuexPlugin } from "jest-matcher-vue-test-utils"; it("Dispatches the action on store by click", () => { const store = new Vuex.Store({ actions: dispatchStore() {}, plugins: [vuexPlugin()] // Requires adding plugin to use `toHaveDispatched` matcher }); const wrapper = shallowMount(Component, { store }) wrapper.trigger("click"); expect(wrapper).toHaveDispatched("awesomeAction"); // Passes expect(wrapper).toHaveDispatched("awesomeAction", "click"); // Passes }); ```

Prop Validations

toBeValidProps

Assert that a prop set is valid for a component ```js // name-require-and-fullname-is-validated-component.vue props: { name: { type: String, required: true } fullname: { validator: function (val) { return !!val && val.match(/.+\s.+/); } } } ``` ```js import Component from "./name-require-and-fullname-is-validated-component.vue"; it("component validates props", () => { expect(Component).toBeValidProps({ name: "required name", fullName: "Kengo Hamasaki" }); // Passes expect(Component).toBeValidProps({ fullName: "Kengo Hamasaki" }); // Fails expect(Component).toBeValidProps({ name: "required name", fullName: "Kengo" }); // Fails }); ```

toBeValidProp

Assert that a single prop is valid for a component ```js // name-require-component.vue props: { name: { type: String, required: true } } ``` ```js import Component from "./name-require-component.vue"; it("component validates props", () => { expect(Component).toBeValidProp("name", "Required Name"); // Passes expect(Component).toBeValidProp("name", null); // Fails as required expect(Component).toBeValidProp("name", 123}); // Fails as typecheck }); ```

toRequireProp

Assert that a component requires a prop ```js // name-require-component.vue props: { name: { type: String, required: true } } ``` ```js import Component from "./name-require-component.vue"; it("component requires name prop", () => { expect(Component).toRequireProp("name"); // Passes expect(Component).toRequireProp("birthday"); // Fails }); ```

toHaveDefaultProp

Assert that a component gives default to a prop ```js // default-address-component.vue props: { address: { type: String, default: "Kitakyushu, Japan" } } ``` ```js import Component from "./default-address-component.vue"; it("component gives default value for address prop", () => { expect(Component).toHaveDefaultProp("address", "Kitakyushu, Japan"); // Passes expect(Component).toHaveDefaultProp("address", "San Francisco, US"); // Fails }); ```

toBeValidPropWithTypeCheck

Assert that a component validates a prop with type ```js // takes-zipcode-component.vue props: { zipcode: { type: String } } ``` ```js import Component from "./takes-zipcode-component.vue"; it("component validates zipcode prop", () => { expect(Component).toBeValidPropWithTypeCheck("zipcode", "94103"); // Passes expect(Component).toBeValidPropWithTypeCheck("zipcode", 94103); // Fails }); ```

toBeValidPropWithCustomValidator

Assert that a component validates a prop with custom validator ```js // fullname-is-validated-component.vue props: { fullname: { validator: function (val) { return !!val && val.match(/.+\s.+/); } } } ``` ```js import Component from "./fullname-is-validated-component.vue"; it("component validates fullname prop", () => { expect(Component).toBeValidPropWithCustomValidator("fullname", "Kengo Hamasaki"); // Passes expect(Component).toBeValidPropWithCustomValidator("fullname", "Kengo"); // Fails }); ```

Config

We can configure the matchers. Currently accepting mountOptions property to give options for shallowMount which is running in inside of matchers.

import vueTestUtilMatchers, { config } from "jest-matcher-vue-test-utils";
import { createLocalVue } from "@vue/test-utils";

config({
  mountOptions: { localVue: createLocalVue() }
});

License

MIT, Copyright (c) 2018- Kengo Hamasaki