Skip to content

Commit 53859e6

Browse files
authored
Merge pull request #77 from SergiCL/pr/add-modifiers-to-triggered-event
feat: add modifiers to trigger function
2 parents f26e0d4 + 1d5b30a commit 53859e6

File tree

8 files changed

+388
-34
lines changed

8 files changed

+388
-34
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@vue/compiler-sfc": "^3.0.0-beta.2",
2828
"babel-jest": "^25.2.3",
2929
"babel-preset-jest": "^25.2.1",
30+
"dom-event-types": "^1.0.0",
3031
"flush-promises": "^1.0.2",
3132
"husky": "^4.2.3",
3233
"jest": "^25.1.0",

rollup.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ function createEntry(options) {
2626
'lodash/camelCase',
2727
'lodash/upperFirst',
2828
'lodash/kebabCase',
29-
'lodash/flow'
29+
'lodash/flow',
30+
'dom-event-types'
3031
],
3132
plugins: [resolve()],
3233
output: {

src/create-dom-event.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import eventTypes from 'dom-event-types'
2+
3+
interface TriggerOptions {
4+
code?: String
5+
key?: String
6+
keyCode?: Number
7+
[custom: string]: any
8+
}
9+
10+
interface EventParams {
11+
eventType: string
12+
modifier: string
13+
meta: any
14+
options?: TriggerOptions
15+
}
16+
17+
export const keyCodesByKeyName = {
18+
backspace: 8,
19+
tab: 9,
20+
enter: 13,
21+
esc: 27,
22+
space: 32,
23+
pageup: 33,
24+
pagedown: 34,
25+
end: 35,
26+
home: 36,
27+
left: 37,
28+
up: 38,
29+
right: 39,
30+
down: 40,
31+
insert: 45,
32+
delete: 46
33+
}
34+
35+
function getEventProperties(eventParams: EventParams) {
36+
const { modifier, meta, options } = eventParams
37+
const keyCode =
38+
keyCodesByKeyName[modifier] ||
39+
(options && (options.keyCode || options.code))
40+
41+
return {
42+
...options, // What the user passed in as the second argument to #trigger
43+
bubbles: meta.bubbles,
44+
meta: meta.cancelable,
45+
// Any derived options should go here
46+
keyCode,
47+
code: keyCode
48+
}
49+
}
50+
51+
function createEvent(eventParams: EventParams) {
52+
const { eventType, meta } = eventParams
53+
const metaEventInterface = window[meta.eventInterface]
54+
55+
const SupportedEventInterface =
56+
typeof metaEventInterface === 'function' ? metaEventInterface : window.Event
57+
58+
const eventProperties = getEventProperties(eventParams)
59+
60+
const event = new SupportedEventInterface(
61+
eventType,
62+
// event properties can only be added when the event is instantiated
63+
// custom properties must be added after the event has been instantiated
64+
eventProperties
65+
)
66+
67+
return event
68+
}
69+
70+
function createDOMEvent(eventString: String, options?: TriggerOptions) {
71+
const [eventType, modifier] = eventString.split('.')
72+
const meta = eventTypes[eventType] || {
73+
eventInterface: 'Event',
74+
cancelable: true,
75+
bubbles: true
76+
}
77+
78+
const eventParams: EventParams = { eventType, modifier, meta, options }
79+
const event: Event = createEvent(eventParams)
80+
const eventPrototype = Object.getPrototypeOf(event)
81+
82+
options &&
83+
Object.keys(options).forEach((key) => {
84+
const propertyDescriptor = Object.getOwnPropertyDescriptor(
85+
eventPrototype,
86+
key
87+
)
88+
const canSetProperty = !(
89+
propertyDescriptor && propertyDescriptor.set === undefined
90+
)
91+
if (canSetProperty) {
92+
event[key] = options[key]
93+
}
94+
})
95+
return event
96+
}
97+
98+
export { TriggerOptions, createDOMEvent }

src/dom-wrapper.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { nextTick } from 'vue'
33
import { WrapperAPI } from './types'
44
import { ErrorWrapper } from './error-wrapper'
55

6+
import { TriggerOptions, createDOMEvent } from './create-dom-event'
7+
68
export class DOMWrapper<ElementType extends Element> implements WrapperAPI {
79
element: ElementType
810

@@ -155,12 +157,38 @@ export class DOMWrapper<ElementType extends Element> implements WrapperAPI {
155157
return new DOMWrapper(parentElement).trigger('change')
156158
}
157159

158-
async trigger(eventString: string) {
159-
const evt = document.createEvent('Event')
160-
evt.initEvent(eventString)
160+
async trigger(eventString: string, options?: TriggerOptions) {
161+
if (options && options['target']) {
162+
throw Error(
163+
`[vue-test-utils]: you cannot set the target value of an event. See the notes section ` +
164+
`of the docs for more details—` +
165+
`https://vue-test-utils.vuejs.org/api/wrapper/trigger.html`
166+
)
167+
}
168+
169+
const isDisabled = () => {
170+
const validTagsToBeDisabled = [
171+
'BUTTON',
172+
'COMMAND',
173+
'FIELDSET',
174+
'KEYGEN',
175+
'OPTGROUP',
176+
'OPTION',
177+
'SELECT',
178+
'TEXTAREA',
179+
'INPUT'
180+
]
181+
const hasDisabledAttribute = this.attributes().disabled !== undefined
182+
const elementCanBeDisabled = validTagsToBeDisabled.includes(
183+
this.element.tagName
184+
)
185+
186+
return hasDisabledAttribute && elementCanBeDisabled
187+
}
161188

162-
if (this.element) {
163-
this.element.dispatchEvent(evt)
189+
if (this.element && !isDisabled()) {
190+
const event = createDOMEvent(eventString, options)
191+
this.element.dispatchEvent(event)
164192
}
165193

166194
return nextTick

src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ export interface WrapperAPI {
1313
findAll<T extends Element>(selector: string): DOMWrapper<T>[]
1414
html: () => string
1515
text: () => string
16-
trigger: (eventString: string) => Promise<(fn?: () => void) => Promise<void>>
16+
trigger: (
17+
eventString: string,
18+
options?: Object
19+
) => Promise<(fn?: () => void) => Promise<void>>
1720
}
1821

1922
interface RefSelector {

src/vue-wrapper.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
WrapperAPI
99
} from './types'
1010
import { ErrorWrapper } from './error-wrapper'
11+
import { TriggerOptions } from './create-dom-event'
1112
import { find } from './utils/find'
1213

1314
export class VueWrapper<T extends ComponentPublicInstance>
@@ -145,9 +146,9 @@ export class VueWrapper<T extends ComponentPublicInstance>
145146
return nextTick()
146147
}
147148

148-
trigger(eventString: string) {
149+
trigger(eventString: string, options?: TriggerOptions) {
149150
const rootElementWrapper = new DOMWrapper(this.element)
150-
return rootElementWrapper.trigger(eventString)
151+
return rootElementWrapper.trigger(eventString, options)
151152
}
152153

153154
unmount() {

0 commit comments

Comments
 (0)