-
Notifications
You must be signed in to change notification settings - Fork 405
/
Copy pathto-have-form-values.js
87 lines (82 loc) · 2.79 KB
/
to-have-form-values.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import isEqualWith from 'lodash/isEqualWith.js'
import escape from 'css.escape'
import {
checkHtmlElement,
getSingleElementValue,
compareArraysAsSet,
} from './utils'
// Returns the combined value of several elements that have the same name
// e.g. radio buttons or groups of checkboxes
function getMultiElementValue(elements) {
const types = [...new Set(elements.map(element => element.type))]
if (types.length !== 1) {
throw new Error(
'Multiple form elements with the same name must be of the same type',
)
}
switch (types[0]) {
case 'radio': {
const theChosenOne = elements.find(radio => radio.checked)
return theChosenOne ? theChosenOne.value : undefined
}
case 'checkbox':
return elements
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.value)
default:
// NOTE: Not even sure this is a valid use case, but just in case...
return elements.map(element => element.value)
}
}
function getFormValue(container, name) {
const elements = [...container.querySelectorAll(`[name="${escape(name)}"]`)]
/* istanbul ignore if */
if (elements.length === 0) {
return undefined // shouldn't happen, but just in case
}
switch (elements.length) {
case 1:
return getSingleElementValue(elements[0])
default:
return getMultiElementValue(elements)
}
}
// Strips the `[]` suffix off a form value name
function getPureName(name) {
return /\[\]$/.test(name) ? name.slice(0, -2) : name
}
function getAllFormValues(container) {
const names = Array.from(container.elements).map(element => element.name)
return names.reduce(
(obj, name) => ({
...obj,
[getPureName(name)]: getFormValue(container, name),
}),
{},
)
}
export function toHaveFormValues(formElement, expectedValues) {
checkHtmlElement(formElement, toHaveFormValues, this)
if (!formElement.elements) {
// TODO: Change condition to use instanceof against the appropriate element classes instead
throw new Error('toHaveFormValues must be called on a form or a fieldset')
}
const formValues = getAllFormValues(formElement)
return {
pass: Object.entries(expectedValues).every(([name, expectedValue]) =>
isEqualWith(formValues[name], expectedValue, compareArraysAsSet),
),
message: () => {
const to = this.isNot ? 'not to' : 'to'
const matcher = `${this.isNot ? '.not' : ''}.toHaveFormValues`
const commonKeyValues = Object.keys(formValues)
.filter(key => expectedValues.hasOwnProperty(key))
.reduce((obj, key) => ({...obj, [key]: formValues[key]}), {})
return [
this.utils.matcherHint(matcher, 'element', ''),
`Expected the element ${to} have form values`,
this.utils.diff(expectedValues, commonKeyValues),
].join('\n\n')
},
}
}