Skip to content

Commit 779b712

Browse files
authored
feat: add toBePartiallyPressed matcher (#203) (#692)
1 parent e15f789 commit 779b712

File tree

5 files changed

+230
-6
lines changed

5 files changed

+230
-6
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ clear to read and to maintain.
8282
- [`toHaveRole`](#tohaverole)
8383
- [`toHaveErrorMessage`](#tohaveerrormessage)
8484
- [`toBePressed`](#tobepressed)
85+
- [`toBePartiallyPressed`](#tobepartiallypressed)
8586
- [Deprecated matchers](#deprecated-matchers)
8687
- [`toBeEmpty`](#tobeempty)
8788
- [`toBeInTheDOM`](#tobeinthedom)
@@ -1347,6 +1348,44 @@ screen.getByRole('button', {name: 'Pressed span'}).toBePressed()
13471348
screen.getByRole('button', {name: 'Released span'}).not.toBePressed()
13481349
```
13491350

1351+
<hr />
1352+
1353+
### `toBePartiallyPressed`
1354+
1355+
This allows to check whether given element is partially
1356+
[pressed](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed).
1357+
1358+
It accepts elements with explicit or implicit `button` role and valid
1359+
`aria-pressed` attribute of `mixed`.
1360+
1361+
```typescript
1362+
toBePressed()
1363+
```
1364+
1365+
#### Examples
1366+
1367+
```html
1368+
<button aria-pressed="mixed">Partially pressed</button>
1369+
<input
1370+
type="button"
1371+
aria-pressed="mixed"
1372+
value="Partially pressed input button"
1373+
/>
1374+
<span role="button" aria-pressed="mixed">Partially pressed span</span>
1375+
```
1376+
1377+
##### Using DOM Testing Library
1378+
1379+
```javascript
1380+
screen.getByRole('button', {name: 'Partially pressed'}).toBePartiallyPressed()
1381+
screen
1382+
.getByRole('button', {name: 'Partially pressed input button'})
1383+
.toBePartiallyPressed()
1384+
screen
1385+
.getByRole('button', {name: 'Partially pressed span'})
1386+
.toBePartiallyPressed()
1387+
```
1388+
13501389
## Deprecated matchers
13511390

13521391
### `toBeEmpty`
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {render} from './helpers/test-utils'
2+
3+
describe('.toBePartiallyPressed', () => {
4+
test('handles button element', () => {
5+
const {queryByTestId} = render(`
6+
<button data-testid="button-partially-pressed" aria-pressed="mixed" />
7+
<button data-testid="button-not-pressed" aria-pressed="true" />
8+
<button data-testid="button-pressed" aria-pressed="false" />
9+
<button data-testid="button" />
10+
`)
11+
12+
expect(queryByTestId('button-partially-pressed')).toBePartiallyPressed()
13+
expect(queryByTestId('button-not-pressed')).not.toBePartiallyPressed()
14+
expect(queryByTestId('button-pressed')).not.toBePartiallyPressed()
15+
expect(queryByTestId('button')).not.toBePartiallyPressed()
16+
})
17+
18+
test('handles element with role="button"', () => {
19+
const {queryByTestId} = render(`
20+
<span role="button" aria-pressed="mixed" data-testid="button-partially-pressed" />
21+
<span role="button" aria-pressed="true" data-testid="button-not-pressed" />
22+
<span role="button" aria-pressed="false" data-testid="button-pressed" />
23+
<span role="button" data-testid="button" />
24+
`)
25+
26+
expect(queryByTestId('button-partially-pressed')).toBePartiallyPressed()
27+
expect(queryByTestId('button-not-pressed')).not.toBePartiallyPressed()
28+
expect(queryByTestId('button-pressed')).not.toBePartiallyPressed()
29+
expect(queryByTestId('button')).not.toBePartiallyPressed()
30+
})
31+
32+
test('handles input with button type', () => {
33+
const {queryByTestId} = render(`
34+
<input type="button" aria-pressed="mixed" data-testid="button-partially-pressed" />
35+
<input type="button" aria-pressed="true" data-testid="button-not-pressed" />
36+
<input type="button" aria-pressed="false" data-testid="button-pressed" />
37+
<input type="button" data-testid="button" />
38+
`)
39+
40+
expect(queryByTestId('button-partially-pressed')).toBePartiallyPressed()
41+
expect(queryByTestId('button-not-pressed')).not.toBePartiallyPressed()
42+
expect(queryByTestId('button-pressed')).not.toBePartiallyPressed()
43+
expect(queryByTestId('button')).not.toBePartiallyPressed()
44+
})
45+
46+
test('throw an error when pressable element is not partially pressed but expected to be', () => {
47+
const {queryByTestId} = render(`
48+
<button data-testid="button" aria-pressed="true" />
49+
`)
50+
51+
expect(() => expect(queryByTestId('button')).toBePartiallyPressed())
52+
.toThrowErrorMatchingInlineSnapshot(`
53+
<dim>expect(</><red>element</><dim>).toBePartiallyPressed()</>
54+
55+
Expected element to have:
56+
<green> aria-pressed="mixed"</>
57+
Received:
58+
<red> aria-pressed="true"</>
59+
`)
60+
})
61+
62+
test('throw an error when pressable element is partially pressed but expected not to be', () => {
63+
const {queryByTestId} = render(`
64+
<button data-testid="button" aria-pressed="mixed" />
65+
`)
66+
67+
expect(() => expect(queryByTestId('button')).not.toBePartiallyPressed())
68+
.toThrowErrorMatchingInlineSnapshot(`
69+
<dim>expect(</><red>element</><dim>).not.toBePartiallyPressed()</>
70+
71+
Expected element not to have:
72+
<green> aria-pressed="mixed"</>
73+
Received:
74+
<red> aria-pressed="mixed"</>
75+
`)
76+
})
77+
78+
test('throw an error when pressable element has invalid aria-pressed attribute', () => {
79+
const {queryByTestId} = render(`
80+
<button data-testid="button" aria-pressed="invalid" />
81+
`)
82+
83+
expect(() =>
84+
expect(queryByTestId('button')).toBePartiallyPressed(),
85+
).toThrowErrorMatchingInlineSnapshot(
86+
`Only button or input with type="button" or element with role="button" and a valid aria-pressed attribute can be used with .toBePartiallyPressed()`,
87+
)
88+
})
89+
90+
test('throws when element is not a button', () => {
91+
const {queryByTestId} = render(`
92+
<div data-testid="div" aria-pressed="mixed" />
93+
`)
94+
95+
expect(() =>
96+
expect(queryByTestId('div')).toBePartiallyPressed(),
97+
).toThrowErrorMatchingInlineSnapshot(
98+
`Only button or input with type="button" or element with role="button" and a valid aria-pressed attribute can be used with .toBePartiallyPressed()`,
99+
)
100+
})
101+
102+
test('throws when element do not have aria-pressed attribute', () => {
103+
const {queryByTestId} = render(`<span data-testid="span" />`)
104+
105+
expect(() =>
106+
expect(queryByTestId('span')).toBePartiallyPressed(),
107+
).toThrowErrorMatchingInlineSnapshot(
108+
`Only button or input with type="button" or element with role="button" and a valid aria-pressed attribute can be used with .toBePartiallyPressed()`,
109+
)
110+
})
111+
})

src/matchers.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ export {toHaveDescription} from './to-have-description'
2626
export {toHaveErrorMessage} from './to-have-errormessage'
2727
export {toHaveSelection} from './to-have-selection'
2828
export {toBePressed} from './to-be-pressed'
29+
export {toBePartiallyPressed} from './to-be-partially-pressed'

src/to-be-partially-pressed.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {checkHtmlElement, getMessage} from './utils'
2+
3+
export function toBePartiallyPressed(element) {
4+
checkHtmlElement(element, toBePartiallyPressed, this)
5+
6+
const roles = (element.getAttribute('role') || '')
7+
.split(' ')
8+
.map(role => role.trim())
9+
10+
const isButton =
11+
element.tagName.toLowerCase() === 'button' ||
12+
(element.tagName.toLowerCase() === 'input' && element.type === 'button') ||
13+
roles.includes('button')
14+
15+
const pressedAttribute = element.getAttribute('aria-pressed')
16+
17+
const isValidAriaElement =
18+
pressedAttribute === 'true' ||
19+
pressedAttribute === 'false' ||
20+
pressedAttribute === 'mixed'
21+
22+
if (!isButton || !isValidAriaElement) {
23+
return {
24+
pass: false,
25+
message: () =>
26+
`Only button or input with type="button" or element with role="button" and a valid aria-pressed attribute can be used with .toBePartiallyPressed()`,
27+
}
28+
}
29+
30+
const isPartiallyPressed = pressedAttribute === 'mixed'
31+
32+
return {
33+
pass: isButton && isPartiallyPressed,
34+
35+
message: () => {
36+
const to = this.isNot ? 'not to' : 'to'
37+
38+
const matcher = this.utils.matcherHint(
39+
`${this.isNot ? '.not' : ''}.toBePartiallyPressed`,
40+
'element',
41+
'',
42+
)
43+
44+
return getMessage(
45+
this,
46+
matcher,
47+
`Expected element ${to} have`,
48+
`aria-pressed="mixed"`,
49+
`Received`,
50+
`aria-pressed="${pressedAttribute}"`,
51+
)
52+
},
53+
}
54+
}

types/matchers.d.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -763,34 +763,53 @@ declare namespace matchers {
763763
toHaveSelection(selection?: string): R
764764
/*
765765
* @description
766-
* This allows to check whether given element has been [pressed](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed)
766+
* This allows to check whether given element has been [pressed](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed)
767767
*
768-
* It accepts elements with explicit or implicit `button` role and valid `aria-pressed`
769-
* attribute of `"true"` or `"false"`.
768+
* It accepts elements with explicit or implicit `button` role and valid `aria-pressed`
769+
* attribute of `"true"` or `"false"`.
770770
*
771771
* @example
772772
* <button aria-pressed="true">Pressed</button>
773773
* <button aria-pressed="false">Released</button>
774774
*
775775
* <input type="button" aria-pressed="true" value="Pressed input button" />
776776
* <input type="button" aria-pressed="false" value="Released input button" />
777-
*
777+
*
778778
* <span role="button" aria-pressed="true">Pressed span</span>
779779
* <span role="button" aria-pressed="false">Released span</span>
780780
*
781781
* screen.getByRole('button', { name: 'Pressed' }).toBePressed();
782782
* screen.getByRole('button', { name: 'Released' }).not.toBePressed();
783-
*
783+
*
784784
* screen.getByRole('button', { name: 'Pressed input button' }).toBePressed();
785785
* screen.getByRole('button', { name: 'Released input button' }).not.toBePressed();
786-
*
786+
*
787787
* screen.getByRole('button', { name: 'Pressed span' }).toBePressed();
788788
* screen.getByRole('button', { name: 'Released span' }).not.toBePressed();
789789
*
790790
* @see
791791
* [testing-library/jest-dom#tobepressed](https://github.com/testing-library/jest-dom#tobepressed)
792792
*/
793793
toBePressed(): R
794+
/*
795+
* @description
796+
* This allows to check whether given element has been [partially pressed](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed)
797+
*
798+
* It accepts elements with explicit or implicit `button` role and valid `aria-pressed` of `"mixed"`.
799+
*
800+
* @example
801+
* <button aria-pressed="mixed">Partially Pressed</button>
802+
* <input type="button" aria-pressed="mixed" value="Partially pressed input button" />
803+
* <span role="button" aria-pressed="mixed">Partially pressed span</span>
804+
*
805+
* screen.getByRole('button', { name: 'Partially Pressed' }).toBePartiallyPressed();
806+
* screen.getByRole('button', { name: 'Partially pressed input button' }).toBePartiallyPressed();
807+
* screen.getByRole('button', { name: 'Partially pressed span' }).toBePartiallyPressed();
808+
*
809+
* @See
810+
* [testing-library/jest-dom#tobepartiallypressed](https://github.com/testing-library/jest-dom#tobepartiallypressed)
811+
*/
812+
toBePartiallyPressed(): R
794813
}
795814
}
796815

0 commit comments

Comments
 (0)