Skip to content

Commit fa21fc5

Browse files
feat: isInaccessible API form RTL/DTL (#1128)
1 parent 807898e commit fa21fc5

File tree

8 files changed

+552
-8
lines changed

8 files changed

+552
-8
lines changed

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
2,
1111
{ "ignore": ["^@theme", "^@docusaurus", "^@generated"] }
1212
],
13-
"react-native-a11y/has-valid-accessibility-ignores-invert-colors": 0,
1413
"react-native/no-color-literals": "off",
14+
"react-native/no-inline-styles": "off",
1515
"react-native-a11y/has-valid-accessibility-descriptors": "off",
16+
"react-native-a11y/has-valid-accessibility-ignores-invert-colors": 0,
1617
"react-native-a11y/has-valid-accessibility-value": "off"
1718
}
1819
}

src/fireEvent.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import act from './act';
3+
import { isHostElement } from './helpers/component-tree';
34
import { filterNodeByType } from './helpers/filterNodeByType';
45

56
type EventHandler = (...args: any) => unknown;
67

7-
const isHostElement = (element?: ReactTestInstance) => {
8-
return typeof element?.type === 'string';
9-
};
10-
118
const isTextInput = (element?: ReactTestInstance) => {
129
if (!element) {
1310
return false;
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import React from 'react';
2+
import { View, Text, TextInput } from 'react-native';
3+
import { render, isInaccessible } from '../..';
4+
5+
test('returns false for accessible elements', () => {
6+
expect(
7+
isInaccessible(render(<View testID="subject" />).getByTestId('subject'))
8+
).toBe(false);
9+
10+
expect(
11+
isInaccessible(
12+
render(<Text testID="subject">Hello</Text>).getByTestId('subject')
13+
)
14+
).toBe(false);
15+
16+
expect(
17+
isInaccessible(
18+
render(<TextInput testID="subject" />).getByTestId('subject')
19+
)
20+
).toBe(false);
21+
});
22+
23+
test('detects elements with accessibilityElementsHidden prop', () => {
24+
const view = render(<View testID="subject" accessibilityElementsHidden />);
25+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
26+
});
27+
28+
test('detects nested elements with accessibilityElementsHidden prop', () => {
29+
const view = render(
30+
<View accessibilityElementsHidden>
31+
<View testID="subject" />
32+
</View>
33+
);
34+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
35+
});
36+
37+
test('detects deeply nested elements with accessibilityElementsHidden prop', () => {
38+
const view = render(
39+
<View accessibilityElementsHidden>
40+
<View>
41+
<View>
42+
<View testID="subject" />
43+
</View>
44+
</View>
45+
</View>
46+
);
47+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
48+
});
49+
50+
test('detects elements with importantForAccessibility="no-hide-descendants" prop', () => {
51+
const view = render(
52+
<View testID="subject" importantForAccessibility="no-hide-descendants" />
53+
);
54+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
55+
});
56+
57+
test('detects nested elements with importantForAccessibility="no-hide-descendants" prop', () => {
58+
const view = render(
59+
<View importantForAccessibility="no-hide-descendants">
60+
<View testID="subject" />
61+
</View>
62+
);
63+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
64+
});
65+
66+
test('detects elements with display=none', () => {
67+
const view = render(<View testID="subject" style={{ display: 'none' }} />);
68+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
69+
});
70+
71+
test('detects nested elements with display=none', () => {
72+
const view = render(
73+
<View style={{ display: 'none' }}>
74+
<View testID="subject" />
75+
</View>
76+
);
77+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
78+
});
79+
80+
test('detects deeply nested elements with display=none', () => {
81+
const view = render(
82+
<View style={{ display: 'none' }}>
83+
<View>
84+
<View>
85+
<View testID="subject" />
86+
</View>
87+
</View>
88+
</View>
89+
);
90+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
91+
});
92+
93+
test('detects elements with display=none with complex style', () => {
94+
const view = render(
95+
<View
96+
testID="subject"
97+
style={[{ display: 'flex' }, [{ display: 'flex' }], { display: 'none' }]}
98+
/>
99+
);
100+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
101+
});
102+
103+
test('is not trigged by opacity = 0', () => {
104+
const view = render(<View testID="subject" style={{ opacity: 0 }} />);
105+
expect(isInaccessible(view.getByTestId('subject'))).toBe(false);
106+
});
107+
108+
test('detects siblings of element with accessibilityViewIsModal prop', () => {
109+
const view = render(
110+
<View>
111+
<View accessibilityViewIsModal />
112+
<View testID="subject" />
113+
</View>
114+
);
115+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
116+
});
117+
118+
test('detects deeply nested siblings of element with accessibilityViewIsModal prop', () => {
119+
const view = render(
120+
<View>
121+
<View accessibilityViewIsModal />
122+
<View>
123+
<View>
124+
<View testID="subject" />
125+
</View>
126+
</View>
127+
</View>
128+
);
129+
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
130+
});
131+
132+
test('is not triggered for element with accessibilityViewIsModal prop', () => {
133+
const view = render(
134+
<View>
135+
<View accessibilityViewIsModal testID="subject" />
136+
</View>
137+
);
138+
expect(isInaccessible(view.getByTestId('subject'))).toBe(false);
139+
});
140+
141+
test('is not triggered for child of element with accessibilityViewIsModal prop', () => {
142+
const view = render(
143+
<View>
144+
<View accessibilityViewIsModal>
145+
<View testID="subject" />
146+
</View>
147+
</View>
148+
);
149+
expect(isInaccessible(view.getByTestId('subject'))).toBe(false);
150+
});
151+
152+
test('is not triggered for descendent of element with accessibilityViewIsModal prop', () => {
153+
const view = render(
154+
<View>
155+
<View accessibilityViewIsModal>
156+
<View>
157+
<View>
158+
<View testID="subject" />
159+
</View>
160+
</View>
161+
</View>
162+
</View>
163+
);
164+
expect(isInaccessible(view.getByTestId('subject'))).toBe(false);
165+
});
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import React from 'react';
2+
import { View, Text, TextInput } from 'react-native';
3+
import { render } from '../..';
4+
import {
5+
getHostChildren,
6+
getHostParent,
7+
getHostSelves,
8+
getHostSiblings,
9+
} from '../component-tree';
10+
11+
function MultipleHostChildren() {
12+
return (
13+
<>
14+
<View testID="child1" />
15+
<View testID="child2" />
16+
<View testID="child3" />
17+
</>
18+
);
19+
}
20+
21+
test('returns host parent for host component', () => {
22+
const view = render(
23+
<View testID="grandparent">
24+
<View testID="parent">
25+
<View testID="subject" />
26+
<View testID="sibling" />
27+
</View>
28+
</View>
29+
);
30+
31+
const hostParent = getHostParent(view.getByTestId('subject'));
32+
expect(hostParent).toBe(view.getByTestId('parent'));
33+
34+
const hostGrandparent = getHostParent(hostParent);
35+
expect(hostGrandparent).toBe(view.getByTestId('grandparent'));
36+
37+
expect(getHostParent(hostGrandparent)).toBe(null);
38+
});
39+
40+
test('returns host parent for composite component', () => {
41+
const view = render(
42+
<View testID="parent">
43+
<MultipleHostChildren />
44+
<View testID="subject" />
45+
</View>
46+
);
47+
48+
const compositeComponent = view.UNSAFE_getByType(MultipleHostChildren);
49+
const hostParent = getHostParent(compositeComponent);
50+
expect(hostParent).toBe(view.getByTestId('parent'));
51+
});
52+
53+
test('returns host children for host component', () => {
54+
const view = render(
55+
<View testID="grandparent">
56+
<View testID="parent">
57+
<View testID="subject" />
58+
<View testID="sibling" />
59+
</View>
60+
</View>
61+
);
62+
63+
const hostSubject = view.getByTestId('subject');
64+
expect(getHostChildren(hostSubject)).toEqual([]);
65+
66+
const hostSibling = view.getByTestId('sibling');
67+
const hostParent = view.getByTestId('parent');
68+
expect(getHostChildren(hostParent)).toEqual([hostSubject, hostSibling]);
69+
70+
const hostGrandparent = view.getByTestId('grandparent');
71+
expect(getHostChildren(hostGrandparent)).toEqual([hostParent]);
72+
});
73+
74+
test('returns host children for composite component', () => {
75+
const view = render(
76+
<View testID="parent">
77+
<MultipleHostChildren />
78+
<View testID="subject" />
79+
<View testID="sibling" />
80+
</View>
81+
);
82+
83+
expect(getHostChildren(view.getByTestId('parent'))).toEqual([
84+
view.getByTestId('child1'),
85+
view.getByTestId('child2'),
86+
view.getByTestId('child3'),
87+
view.getByTestId('subject'),
88+
view.getByTestId('sibling'),
89+
]);
90+
});
91+
92+
test('returns host selves for host components', () => {
93+
const view = render(
94+
<View testID="grandparent">
95+
<View testID="parent">
96+
<View testID="subject" />
97+
<View testID="sibling" />
98+
</View>
99+
</View>
100+
);
101+
102+
const hostSubject = view.getByTestId('subject');
103+
expect(getHostSelves(hostSubject)).toEqual([hostSubject]);
104+
105+
const hostSibling = view.getByTestId('sibling');
106+
expect(getHostSelves(hostSibling)).toEqual([hostSibling]);
107+
108+
const hostParent = view.getByTestId('parent');
109+
expect(getHostSelves(hostParent)).toEqual([hostParent]);
110+
111+
const hostGrandparent = view.getByTestId('grandparent');
112+
expect(getHostSelves(hostGrandparent)).toEqual([hostGrandparent]);
113+
});
114+
115+
test('returns host selves for React Native composite components', () => {
116+
const view = render(
117+
<View testID="parent">
118+
<Text testID="text">Text</Text>
119+
<TextInput
120+
testID="textInput"
121+
defaultValue="TextInputValue"
122+
placeholder="TextInputPlaceholder"
123+
/>
124+
</View>
125+
);
126+
127+
const compositeText = view.getByText('Text');
128+
const hostText = view.getByTestId('text');
129+
expect(getHostSelves(compositeText)).toEqual([hostText]);
130+
131+
const compositeTextInputByValue = view.getByDisplayValue('TextInputValue');
132+
const compositeTextInputByPlaceholder = view.getByPlaceholderText(
133+
'TextInputPlaceholder'
134+
);
135+
const hostTextInput = view.getByTestId('textInput');
136+
expect(getHostSelves(compositeTextInputByValue)).toEqual([hostTextInput]);
137+
expect(getHostSelves(compositeTextInputByPlaceholder)).toEqual([
138+
hostTextInput,
139+
]);
140+
});
141+
142+
test('returns host selves for custom composite components', () => {
143+
const view = render(
144+
<View testID="parent">
145+
<MultipleHostChildren />
146+
<View testID="sibling" />
147+
</View>
148+
);
149+
150+
const compositeComponent = view.UNSAFE_getByType(MultipleHostChildren);
151+
const hostChild1 = view.getByTestId('child1');
152+
const hostChild2 = view.getByTestId('child2');
153+
const hostChild3 = view.getByTestId('child3');
154+
expect(getHostSelves(compositeComponent)).toEqual([
155+
hostChild1,
156+
hostChild2,
157+
hostChild3,
158+
]);
159+
});
160+
161+
test('returns host siblings for host component', () => {
162+
const view = render(
163+
<View testID="grandparent">
164+
<View testID="parent">
165+
<View testID="siblingBefore" />
166+
<View testID="subject" />
167+
<View testID="siblingAfter" />
168+
<MultipleHostChildren />
169+
</View>
170+
</View>
171+
);
172+
173+
const hostSiblings = getHostSiblings(view.getByTestId('subject'));
174+
expect(hostSiblings).toEqual([
175+
view.getByTestId('siblingBefore'),
176+
view.getByTestId('siblingAfter'),
177+
view.getByTestId('child1'),
178+
view.getByTestId('child2'),
179+
view.getByTestId('child3'),
180+
]);
181+
});
182+
183+
test('returns host siblings for composite component', () => {
184+
const view = render(
185+
<View testID="grandparent">
186+
<View testID="parent">
187+
<View testID="siblingBefore" />
188+
<View testID="subject" />
189+
<View testID="siblingAfter" />
190+
<MultipleHostChildren />
191+
</View>
192+
</View>
193+
);
194+
195+
const compositeComponent = view.UNSAFE_getByType(MultipleHostChildren);
196+
const hostSiblings = getHostSiblings(compositeComponent);
197+
expect(hostSiblings).toEqual([
198+
view.getByTestId('siblingBefore'),
199+
view.getByTestId('subject'),
200+
view.getByTestId('siblingAfter'),
201+
]);
202+
});

0 commit comments

Comments
 (0)