Skip to content

Commit 3c5eecb

Browse files
committed
Create toHaveEmittedOnRoot
1 parent 70536ae commit 3c5eecb

File tree

4 files changed

+235
-3
lines changed

4 files changed

+235
-3
lines changed

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import toShow from "./matchers/toShow";
55
import toHide from "./matchers/toHide";
66
import toEmit from "./matchers/toEmit";
77
import toHaveEmitted from "./matchers/toHaveEmitted";
8+
import toHaveEmittedOnRoot from "./matchers/toHaveEmittedOnRoot";
89
import toDispatch from "./matchers/toDispatch";
910
import toHaveDispatched from "./matchers/toHaveDispatched";
1011
import toRequireProp from "./matchers/toRequireProp";
@@ -19,6 +20,7 @@ const matchers = {
1920
toHide,
2021
toEmit,
2122
toHaveEmitted,
23+
toHaveEmittedOnRoot,
2224
toDispatch,
2325
toHaveDispatched,
2426
toBeValidProp,
@@ -34,6 +36,7 @@ export {
3436
toHide,
3537
toEmit,
3638
toHaveEmitted,
39+
toHaveEmittedOnRoot,
3740
toDispatch,
3841
toHaveDispatched,
3942
toBeValidProp,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import Vue from "vue";
2+
import { Wrapper, createWrapper } from "@vue/test-utils";
3+
import diff from "jest-diff";
4+
import { equals } from "expect/build/jasmineUtils";
5+
6+
import { MatcherResult } from "../utils";
7+
8+
declare global {
9+
namespace jest {
10+
interface Matchers<R, T> {
11+
/**
12+
* Asserts that the emitted event on $root with payload
13+
* @param {string} eventName - The event's name
14+
* @param payload - The payload of the event (optional)
15+
* @example
16+
* expect(wrapper).toHaveEmittedOnRoot("input")
17+
* expect(wrapper).toHaveEmittedOnRoot("input", "value")
18+
* expect(wrapper).toHaveEmittedOnRoot("input", "value", ["more"], "arguments")
19+
*/
20+
toHaveEmittedOnRoot (eventName: string, ...payloads: any[]): R;
21+
}
22+
}
23+
}
24+
25+
export default function<V extends Vue> (
26+
wrapper: Wrapper<V>,
27+
eventName: string,
28+
...payloads: any[]
29+
): MatcherResult {
30+
const emittedOnRoot = createWrapper(wrapper.vm.$root).emitted()[eventName] || [];
31+
32+
let pass: boolean;
33+
let message: () => string;
34+
35+
if (arguments.length >= 3) {
36+
pass = emittedOnRoot.some((event) => {
37+
return payloads.length === event.length &&
38+
payloads.every((payload, index) => {
39+
return equals(event[index], payload);
40+
});
41+
});
42+
message = pass ?
43+
() => `The "${eventName}" event was emitted on $root with the expected payload` :
44+
emittedOnRoot.length > 0 ?
45+
() => {
46+
const diffs = emittedOnRoot.map((event, i): string | null => {
47+
return `'${eventName}' event #${i} payloads:\n\n${diff(payloads, event, { bAnnotation: "Emitted" })}`;
48+
}).join("\n\n");
49+
return `The "${eventName}" event was emitted on $root but the payload is not matched\n\n${diffs}`
50+
} :
51+
() => `The "${eventName}" event was never emitted on $root`;
52+
} else {
53+
pass = emittedOnRoot.length > 0;
54+
message = pass ?
55+
() => `The "${eventName}" event was emitted on $root` :
56+
() => `The "${eventName}" event was never emitted on $root`;
57+
}
58+
59+
return {
60+
message,
61+
pass
62+
}
63+
}

test/emit.test.ts

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {
22
config,
33
toEmit,
4-
toHaveEmitted
4+
toHaveEmitted,
5+
toHaveEmittedOnRoot
56
} from "@/index";
67

78
import Component from "./fixtures/event.vue";
@@ -10,7 +11,8 @@ import { MatcherResult } from '@/utils';
1011

1112
expect.extend({
1213
toEmit,
13-
toHaveEmitted
14+
toHaveEmitted,
15+
toHaveEmittedOnRoot
1416
});
1517

1618
config({
@@ -21,6 +23,10 @@ const emitEvent = (wrapper, eventName, payload) => {
2123
(wrapper.vm as any).emitEventWithPayload(eventName, payload);
2224
};
2325

26+
const emitEventOnRoot = (wrapper, eventName, payload) => {
27+
(wrapper.vm as any).emitEventOnRootWithPayload(eventName, payload);
28+
};
29+
2430
const doAsyncronously = (callback: Function): Promise<any> => {
2531
return new Promise((resolve) => {
2632
callback();
@@ -178,6 +184,156 @@ describe("toHaveEmitted with payload", () => {
178184
});
179185
});
180186

187+
describe("toHaveEmittedOnRoot", () => {
188+
describe("as a function which is registered to jest", () => {
189+
it("returns true if the event is emitted on $root", () => {
190+
const wrapper = shallowMount(Component);
191+
wrapper.trigger("blur");
192+
const result = toHaveEmittedOnRoot(wrapper, "blured");
193+
expect(result.pass).toBe(true);
194+
expect(result.message()).toBe('The "blured" event was emitted on $root');
195+
});
196+
197+
it("returns false if the event is not emitted on $root", () => {
198+
const wrapper = shallowMount(Component);
199+
const result = toHaveEmittedOnRoot(wrapper, "blured");
200+
expect(result.pass).toBe(false);
201+
expect(result.message()).toBe('The "blured" event was never emitted on $root');
202+
});
203+
204+
it("returns false if the another event is emitted on $root", () => {
205+
const wrapper = shallowMount(Component);
206+
wrapper.vm.$root.$emit("another");
207+
const result = toHaveEmittedOnRoot(wrapper, "blured");
208+
expect(result.pass).toBe(false);
209+
expect(result.message()).toBe('The "blured" event was never emitted on $root');
210+
});
211+
});
212+
213+
describe("actual use", () => {
214+
it("doesn't claim for positive expectation when expected event happens", () => {
215+
const wrapper = shallowMount(Component);
216+
wrapper.trigger("blur");
217+
expect(wrapper).toHaveEmittedOnRoot("blured");
218+
});
219+
220+
it("doesn't claim for negative expectation when expected event doesn't happen", () => {
221+
const wrapper = shallowMount(Component);
222+
expect(wrapper).not.toHaveEmittedOnRoot("blured");
223+
});
224+
225+
it("doesn't claim for negative expectation when unintentional event happens", () => {
226+
const wrapper = shallowMount(Component);
227+
wrapper.vm.$root.$emit("another");
228+
expect(wrapper).not.toHaveEmittedOnRoot("blured");
229+
});
230+
});
231+
});
232+
233+
describe("toHaveEmittedOnRoot with payload", () => {
234+
describe("as a function which is registered to jest", () => {
235+
it("returns true if the event is emitted with the expected payload", () => {
236+
const wrapper = shallowMount(Component);
237+
emitEventOnRoot(wrapper, "rooty", { value: "something" });
238+
const result = toHaveEmittedOnRoot(wrapper, "rooty", { value: "something" });
239+
expect(result.pass).toBe(true);
240+
expect(result.message()).toBe('The "rooty" event was emitted on $root with the expected payload');
241+
});
242+
243+
it("returns false if the event is not emitted", () => {
244+
const wrapper = shallowMount(Component);
245+
const result = toHaveEmittedOnRoot(wrapper, "rooty", "something");
246+
expect(result.pass).toBe(false);
247+
expect(result.message()).toBe('The "rooty" event was never emitted on $root');
248+
});
249+
250+
it("returns false if the another event is emitted", () => {
251+
const wrapper = shallowMount(Component);
252+
emitEventOnRoot(wrapper, "another", { value: "something" });
253+
const result = toHaveEmittedOnRoot(wrapper, "rooty", { value: "something" });
254+
expect(result.pass).toBe(false);
255+
expect(result.message()).toBe('The "rooty" event was never emitted on $root');
256+
});
257+
258+
describe("when the event is emitted but the payload is not matched", () => {
259+
const subject = () => {
260+
const wrapper = shallowMount(Component);
261+
emitEventOnRoot(wrapper, "rooty", { value: "anything" });
262+
return toHaveEmittedOnRoot(wrapper, "rooty", "some text", { value: "something" });
263+
};
264+
265+
it("returns false", () => {
266+
expect(subject().pass).toBe(false);
267+
});
268+
269+
it("tells the reason", () => {
270+
expect(subject().message()).toContain('The "rooty" event was emitted on $root but the payload is not matched');
271+
});
272+
273+
it("shows the diff of payloads", () => {
274+
const message = subject().message();
275+
expect(message).toContain("'rooty' event #0 payloads:");
276+
expect(message).toContain("- Expected");
277+
expect(message).toContain("+ Emitted");
278+
expect(message).toContain('- "some text"');
279+
expect(message).toContain('- "value": "something"');
280+
expect(message).toContain('+ "value": "anything"');
281+
});
282+
});
283+
});
284+
285+
describe("actual use", () => {
286+
it("passes for expected case positively", () => {
287+
const wrapper = shallowMount(Component);
288+
emitEventOnRoot(wrapper, "rooty", { value: "actual life" });
289+
expect(wrapper).toHaveEmittedOnRoot("rooty", { value: "actual life" });
290+
});
291+
292+
it("passes negatively when any event was not emitted", () => {
293+
const wrapper = shallowMount(Component);
294+
expect(wrapper).not.toHaveEmittedOnRoot("rooty", { value: "unusual life" });
295+
});
296+
297+
it("passes negatively when unintentional event was emitted", () => {
298+
const wrapper = shallowMount(Component);
299+
emitEventOnRoot(wrapper, "another", { value: "actual life" });
300+
expect(wrapper).not.toHaveEmittedOnRoot("rooty", { value: "unusual life" });
301+
});
302+
303+
it("passes negatively when the expected event is emitted but the payload is not matched", () => {
304+
const wrapper = shallowMount(Component);
305+
emitEventOnRoot(wrapper, "rooty", { value: "actual life" });
306+
expect(wrapper).not.toHaveEmitted("rooty", { value: "exciting life" });
307+
});
308+
309+
describe("multiple payloads", () => {
310+
it("passes positively when the expected event is emitted with the multiple payloads by the function", () => {
311+
const wrapper = shallowMount(Component);
312+
(wrapper.vm as any).emitEventOnRootWithMultiplePayload("multiOnRoot!");
313+
expect(wrapper).toHaveEmittedOnRoot("multiOnRoot!", ["D"], 3, "e");
314+
});
315+
316+
it("passes positively for expect helper to assert with 'anything'", () => {
317+
const wrapper = shallowMount(Component);
318+
(wrapper.vm as any).emitEventOnRootWithMultiplePayload("multiOnRoot!!");
319+
expect(wrapper).toHaveEmittedOnRoot("multiOnRoot!!", expect.arrayContaining(["D"]), expect.anything(), "e");
320+
});
321+
322+
it("passes negatively when the number of paylod is shorter than actual (even they are matching)", () => {
323+
const wrapper = shallowMount(Component);
324+
(wrapper.vm as any).emitEventOnRootWithMultiplePayload("multiOnRoot!");
325+
expect(wrapper).not.toHaveEmittedOnRoot("multiOnRoot!", ["D"], 3);
326+
});
327+
328+
it("passes negatively when the number of paylod is longer", () => {
329+
const wrapper = shallowMount(Component);
330+
(wrapper.vm as any).emitEventOnRootWithMultiplePayload("multiOnRoot!");
331+
expect(wrapper).not.toHaveEmittedOnRoot("multiOnRoot!", ["D"], 3, "e", "additional");
332+
});
333+
});
334+
});
335+
});
336+
181337
describe("toEmit", () => {
182338
describe("as a function which is registered to jest", () => {
183339
describe("returns true if the event is emitted", () => {

test/fixtures/event.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<template>
22
<div
3-
@click="emitEvent">
3+
@click="emitEvent"
4+
@blur="emitRootEvent">
45
Click Me
56
</div>
67
</template>
@@ -11,11 +12,20 @@ module.exports = {
1112
emitEvent (e) {
1213
this.$emit("special", e);
1314
},
15+
emitRootEvent (e) {
16+
this.$root.$emit("blured", e);
17+
},
1418
emitEventWithPayload (eventName, payload) {
1519
this.$emit(eventName, payload);
1620
},
21+
emitEventOnRootWithPayload (eventName, payload) {
22+
this.$root.$emit(eventName, payload);
23+
},
1724
emitEventWithMultiplePayload (eventName) {
1825
this.$emit(eventName, "a", 5, ["C"]);
26+
},
27+
emitEventOnRootWithMultiplePayload (eventName) {
28+
this.$root.$emit(eventName, ["D"], 3, "e");
1929
}
2030
}
2131
}

0 commit comments

Comments
 (0)