Skip to content

Commit f4991cd

Browse files
jgozgnapse
authored andcommitted
fix: toHaveClass behaviour for empty class list (#39)
* Require at least one class in toHaveClass * .not.toHaveClass() asserts there are no received classes * Update readme
1 parent 1beead0 commit f4991cd

File tree

3 files changed

+65
-21
lines changed

3 files changed

+65
-21
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,17 @@ expect(getByTestId(container, 'delete-button')).not.toHaveClass('btn-link')
249249
// ...
250250
```
251251

252+
You must provide at least one class, unless you are asserting that an element
253+
does not have any classes.
254+
255+
```javascript
256+
// ...
257+
// <button data-testid="no-classes">
258+
// Delete item
259+
// </button>
260+
expect(getByTestId(container, 'no-classes')).not.toHaveClass()
261+
```
262+
252263
### `toHaveStyle`
253264

254265
```typescript

src/__tests__/to-have-class.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/* eslint max-statements:off */
2+
13
import {render} from './helpers/test-utils'
24

35
test('.toHaveClass', () => {
@@ -12,10 +14,10 @@ test('.toHaveClass', () => {
1214
<svg data-testid="svg-spinner" class="spinner clockwise">
1315
<path />
1416
</svg>
17+
<div data-testid="no-classes"></div>
1518
</div>
1619
`)
1720

18-
expect(queryByTestId('delete-button')).toHaveClass()
1921
expect(queryByTestId('delete-button')).toHaveClass('btn')
2022
expect(queryByTestId('delete-button')).toHaveClass('btn-danger')
2123
expect(queryByTestId('delete-button')).toHaveClass('extra')
@@ -34,10 +36,9 @@ test('.toHaveClass', () => {
3436
expect(queryByTestId('svg-spinner')).toHaveClass('spinner')
3537
expect(queryByTestId('svg-spinner')).toHaveClass('clockwise')
3638
expect(queryByTestId('svg-spinner')).not.toHaveClass('wise')
39+
expect(queryByTestId('no-classes')).not.toHaveClass()
40+
expect(queryByTestId('no-classes')).not.toHaveClass(' ')
3741

38-
expect(() =>
39-
expect(queryByTestId('delete-button')).not.toHaveClass(),
40-
).toThrowError()
4142
expect(() =>
4243
expect(queryByTestId('delete-button')).not.toHaveClass('btn'),
4344
).toThrowError()
@@ -74,4 +75,19 @@ test('.toHaveClass', () => {
7475
expect(() =>
7576
expect(queryByTestId('svg-spinner')).toHaveClass('wise'),
7677
).toThrowError()
78+
expect(() =>
79+
expect(queryByTestId('delete-button')).toHaveClass(),
80+
).toThrowError(/At least one expected class must be provided/)
81+
expect(() =>
82+
expect(queryByTestId('delete-button')).toHaveClass(''),
83+
).toThrowError(/At least one expected class must be provided/)
84+
expect(() => expect(queryByTestId('no-classes')).toHaveClass()).toThrowError(
85+
/At least one expected class must be provided/,
86+
)
87+
expect(() =>
88+
expect(queryByTestId('delete-button')).not.toHaveClass(),
89+
).toThrowError(/(none)/)
90+
expect(() =>
91+
expect(queryByTestId('delete-button')).not.toHaveClass(' '),
92+
).toThrowError(/(none)/)
7793
})

src/to-have-class.js

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,38 @@ export function toHaveClass(htmlElement, ...expectedClassNames) {
1919
(acc, className) => acc.concat(splitClassNames(className)),
2020
[],
2121
)
22-
return {
23-
pass: isSubset(expected, received),
24-
message: () => {
25-
const to = this.isNot ? 'not to' : 'to'
26-
return getMessage(
27-
matcherHint(
28-
`${this.isNot ? '.not' : ''}.toHaveClass`,
29-
'element',
30-
printExpected(expected.join(' ')),
31-
),
32-
`Expected the element ${to} have class`,
33-
expected.join(' '),
34-
'Received',
35-
received.join(' '),
36-
)
37-
},
38-
}
22+
return expected.length > 0
23+
? {
24+
pass: isSubset(expected, received),
25+
message: () => {
26+
const to = this.isNot ? 'not to' : 'to'
27+
return getMessage(
28+
matcherHint(
29+
`${this.isNot ? '.not' : ''}.toHaveClass`,
30+
'element',
31+
printExpected(expected.join(' ')),
32+
),
33+
`Expected the element ${to} have class`,
34+
expected.join(' '),
35+
'Received',
36+
received.join(' '),
37+
)
38+
},
39+
}
40+
: {
41+
pass: this.isNot ? received.length > 0 : false,
42+
message: () =>
43+
this.isNot
44+
? getMessage(
45+
matcherHint('.not.toHaveClass', 'element', ''),
46+
'Expected the element to have classes',
47+
'(none)',
48+
'Received',
49+
received.join(' '),
50+
)
51+
: [
52+
matcherHint(`.toHaveClass`, 'element'),
53+
'At least one expected class must be provided.',
54+
].join('\n'),
55+
}
3956
}

0 commit comments

Comments
 (0)