Skip to content

Commit 1592d35

Browse files
feat: implement toBeSelected matcher (#1488)
* feat: implement toBeSelected * refactor: tweaks * refactor: tweak tests * refactor: finishing touches --------- Co-authored-by: Maciej Jastrzębski <[email protected]>
1 parent 8373d6a commit 1592d35

File tree

6 files changed

+131
-0
lines changed

6 files changed

+131
-0
lines changed

src/helpers/accessiblity.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ export function getAccessibilityCheckedState(
176176
return ariaChecked ?? accessibilityState?.checked;
177177
}
178178

179+
export function getAccessibilitySelectedState(
180+
element: ReactTestInstance
181+
): NonNullable<AccessibilityState['selected']> {
182+
const { accessibilityState, 'aria-selected': ariaSelected } = element.props;
183+
return ariaSelected ?? accessibilityState?.selected ?? false;
184+
}
185+
179186
export function getAccessibilityValue(
180187
element: ReactTestInstance
181188
): AccessibilityValue | undefined {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as React from 'react';
2+
import { View } from 'react-native';
3+
import { render, screen } from '../..';
4+
import '../extend-expect';
5+
6+
test('.toBeSelected() basic case', () => {
7+
render(
8+
<>
9+
<View testID="selected" accessibilityState={{ selected: true }} />
10+
<View testID="selected-aria" aria-selected />
11+
<View testID="not-selected" accessibilityState={{ selected: false }} />
12+
<View testID="not-selected-aria" aria-selected={false} />
13+
<View testID="default" />
14+
</>
15+
);
16+
17+
expect(screen.getByTestId('selected')).toBeSelected();
18+
expect(screen.getByTestId('selected-aria')).toBeSelected();
19+
expect(screen.getByTestId('not-selected')).not.toBeSelected();
20+
expect(screen.getByTestId('not-selected-aria')).not.toBeSelected();
21+
expect(screen.getByTestId('default')).not.toBeSelected();
22+
});
23+
24+
test('.toBeSelected() error messages', () => {
25+
render(
26+
<>
27+
<View testID="selected" accessibilityState={{ selected: true }} />
28+
<View testID="selected-aria" aria-selected />
29+
<View testID="not-selected" accessibilityState={{ selected: false }} />
30+
<View testID="not-selected-aria" aria-selected={false} />
31+
<View testID="default" />
32+
</>
33+
);
34+
35+
expect(() => expect(screen.getByTestId('selected')).not.toBeSelected())
36+
.toThrowErrorMatchingInlineSnapshot(`
37+
"expect(element).not.toBeSelected()
38+
39+
Received element is selected
40+
<View
41+
accessibilityState={
42+
{
43+
"selected": true,
44+
}
45+
}
46+
testID="selected"
47+
/>"
48+
`);
49+
50+
expect(() => expect(screen.getByTestId('selected-aria')).not.toBeSelected())
51+
.toThrowErrorMatchingInlineSnapshot(`
52+
"expect(element).not.toBeSelected()
53+
54+
Received element is selected
55+
<View
56+
aria-selected={true}
57+
testID="selected-aria"
58+
/>"
59+
`);
60+
61+
expect(() => expect(screen.getByTestId('not-selected')).toBeSelected())
62+
.toThrowErrorMatchingInlineSnapshot(`
63+
"expect(element).toBeSelected()
64+
65+
Received element is not selected
66+
<View
67+
accessibilityState={
68+
{
69+
"selected": false,
70+
}
71+
}
72+
testID="not-selected"
73+
/>"
74+
`);
75+
76+
expect(() => expect(screen.getByTestId('not-selected-aria')).toBeSelected())
77+
.toThrowErrorMatchingInlineSnapshot(`
78+
"expect(element).toBeSelected()
79+
80+
Received element is not selected
81+
<View
82+
aria-selected={false}
83+
testID="not-selected-aria"
84+
/>"
85+
`);
86+
87+
expect(() => expect(screen.getByTestId('default')).toBeSelected())
88+
.toThrowErrorMatchingInlineSnapshot(`
89+
"expect(element).toBeSelected()
90+
91+
Received element is not selected
92+
<View
93+
testID="default"
94+
/>"
95+
`);
96+
});

src/matchers/extend-expect.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export interface JestNativeMatchers<R> {
77
toBeEmptyElement(): R;
88
toBeEnabled(): R;
99
toBePartiallyChecked(): R;
10+
toBeSelected(): R;
1011
toBeVisible(): R;
1112
toHaveDisplayValue(expectedValue: TextMatch, options?: TextMatchOptions): R;
1213
toHaveProp(name: string, expectedValue?: unknown): R;

src/matchers/extend-expect.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { toBeChecked } from './to-be-checked';
55
import { toBeDisabled, toBeEnabled } from './to-be-disabled';
66
import { toBeEmptyElement } from './to-be-empty-element';
77
import { toBePartiallyChecked } from './to-be-partially-checked';
8+
import { toBeSelected } from './to-be-selected';
89
import { toBeVisible } from './to-be-visible';
910
import { toHaveDisplayValue } from './to-have-display-value';
1011
import { toHaveProp } from './to-have-prop';
@@ -17,6 +18,7 @@ expect.extend({
1718
toBeEmptyElement,
1819
toBeEnabled,
1920
toBePartiallyChecked,
21+
toBeSelected,
2022
toBeVisible,
2123
toHaveDisplayValue,
2224
toHaveProp,

src/matchers/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export { toBeVisible } from './to-be-visible';
77
export { toHaveDisplayValue } from './to-have-display-value';
88
export { toHaveProp } from './to-have-prop';
99
export { toHaveTextContent } from './to-have-text-content';
10+
export { toBeSelected } from './to-be-selected';

src/matchers/to-be-selected.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ReactTestInstance } from 'react-test-renderer';
2+
import { matcherHint } from 'jest-matcher-utils';
3+
import { getAccessibilitySelectedState } from '../helpers/accessiblity';
4+
import { checkHostElement, formatElement } from './utils';
5+
6+
export function toBeSelected(
7+
this: jest.MatcherContext,
8+
element: ReactTestInstance
9+
) {
10+
checkHostElement(element, toBeSelected, this);
11+
12+
return {
13+
pass: getAccessibilitySelectedState(element),
14+
message: () => {
15+
const is = this.isNot ? 'is' : 'is not';
16+
return [
17+
matcherHint(`${this.isNot ? '.not' : ''}.toBeSelected`, 'element', ''),
18+
'',
19+
`Received element ${is} selected`,
20+
formatElement(element),
21+
].join('\n');
22+
},
23+
};
24+
}

0 commit comments

Comments
 (0)