-
Notifications
You must be signed in to change notification settings - Fork 406
/
Copy pathto-have-class.js
107 lines (98 loc) · 3.11 KB
/
to-have-class.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import {checkHtmlElement, getMessage} from './utils'
function getExpectedClassNamesAndOptions(params) {
const lastParam = params.pop()
let expectedClassNames, options
if (typeof lastParam === 'object' && !(lastParam instanceof RegExp)) {
expectedClassNames = params
options = lastParam
} else {
expectedClassNames = params.concat(lastParam)
options = {exact: false}
}
return {expectedClassNames, options}
}
function splitClassNames(str) {
if (!str) return []
return str.split(/\s+/).filter(s => s.length > 0)
}
function isSubset(subset, superset) {
return subset.every(strOrRegexp =>
typeof strOrRegexp === 'string'
? superset.includes(strOrRegexp)
: superset.some(className => strOrRegexp.test(className)),
)
}
export function toHaveClass(htmlElement, ...params) {
checkHtmlElement(htmlElement, toHaveClass, this)
const {expectedClassNames, options} = getExpectedClassNamesAndOptions(params)
const received = splitClassNames(htmlElement.getAttribute('class'))
const expected = expectedClassNames.reduce(
(acc, className) =>
acc.concat(
typeof className === 'string' || !className
? splitClassNames(className)
: className,
),
[],
)
const hasRegExp = expected.some(className => className instanceof RegExp)
if (options.exact && hasRegExp) {
throw new Error('Exact option does not support RegExp expected class names')
}
if (options.exact) {
return {
pass: isSubset(expected, received) && expected.length === received.length,
message: () => {
const to = this.isNot ? 'not to' : 'to'
return getMessage(
this,
this.utils.matcherHint(
`${this.isNot ? '.not' : ''}.toHaveClass`,
'element',
this.utils.printExpected(expected.join(' ')),
),
`Expected the element ${to} have EXACTLY defined classes`,
expected.join(' '),
'Received',
received.join(' '),
)
},
}
}
return expected.length > 0
? {
pass: isSubset(expected, received),
message: () => {
const to = this.isNot ? 'not to' : 'to'
return getMessage(
this,
this.utils.matcherHint(
`${this.isNot ? '.not' : ''}.toHaveClass`,
'element',
this.utils.printExpected(expected.join(' ')),
),
`Expected the element ${to} have class`,
expected.join(' '),
'Received',
received.join(' '),
)
},
}
: {
pass: this.isNot ? received.length > 0 : false,
message: () =>
this.isNot
? getMessage(
this,
this.utils.matcherHint('.not.toHaveClass', 'element', ''),
'Expected the element to have classes',
'(none)',
'Received',
received.join(' '),
)
: [
this.utils.matcherHint(`.toHaveClass`, 'element'),
'At least one expected class must be provided.',
].join('\n'),
}
}