Skip to content

Commit 9d25396

Browse files
jaworekmdjastrzebski
authored andcommitted
add tests and toBeEnabled matcher
1 parent faebd54 commit 9d25396

File tree

4 files changed

+202
-4
lines changed

4 files changed

+202
-4
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import React from 'react';
2+
import {
3+
Button,
4+
Pressable,
5+
TextInput,
6+
TouchableHighlight,
7+
TouchableNativeFeedback,
8+
TouchableOpacity,
9+
TouchableWithoutFeedback,
10+
View,
11+
} from 'react-native';
12+
import { render } from '../..';
13+
import '../extend-expect';
14+
15+
const ALLOWED_COMPONENTS = {
16+
View,
17+
TextInput,
18+
TouchableHighlight,
19+
TouchableOpacity,
20+
TouchableWithoutFeedback,
21+
TouchableNativeFeedback,
22+
Pressable,
23+
};
24+
25+
describe('.toBeDisabled', () => {
26+
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
27+
test(`handle disabled prop for element ${name}`, () => {
28+
const { queryByTestId } = render(
29+
//@ts-expect-error JSX element type 'Component' does not have any construct or call signatures.ts(2604)
30+
<Component disabled testID={name}>
31+
<TextInput />
32+
</Component>
33+
);
34+
35+
expect(queryByTestId(name)).toBeDisabled();
36+
expect(() => expect(queryByTestId(name)).not.toBeDisabled()).toThrow();
37+
});
38+
});
39+
40+
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
41+
test(`handle disabled in accessibilityState for element ${name}`, () => {
42+
const { queryByTestId } = render(
43+
//@ts-expect-error JSX element type 'Component' does not have any construct or call signatures.ts(2604)
44+
<Component accessibilityState={{ disabled: true }} testID={name}>
45+
<TextInput />
46+
</Component>
47+
);
48+
49+
expect(queryByTestId(name)).toBeDisabled();
50+
expect(() => expect(queryByTestId(name)).not.toBeDisabled()).toThrow();
51+
});
52+
});
53+
54+
test('handle editable prop for TextInput', () => {
55+
const { getByTestId, getByPlaceholderText } = render(
56+
<View>
57+
<TextInput testID="disabled" placeholder="disabled" editable={false} />
58+
<TextInput
59+
testID="enabled-by-default"
60+
placeholder="enabled-by-default"
61+
/>
62+
<TextInput testID="enabled" placeholder="enabled" editable />
63+
</View>
64+
);
65+
66+
// Check host TextInput
67+
expect(getByTestId('disabled')).toBeDisabled();
68+
expect(getByTestId('enabled-by-default')).not.toBeDisabled();
69+
expect(getByTestId('enabled')).not.toBeDisabled();
70+
71+
// Check composite TextInput
72+
expect(getByPlaceholderText('disabled')).toBeDisabled();
73+
expect(getByPlaceholderText('enabled-by-default')).not.toBeDisabled();
74+
expect(getByPlaceholderText('enabled')).not.toBeDisabled();
75+
});
76+
});
77+
78+
describe('.toBeEnabled', () => {
79+
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
80+
test(`handle disabled prop for element ${name} when undefined`, () => {
81+
const { queryByTestId } = render(
82+
//@ts-expect-error JSX element type 'Component' does not have any construct or call signatures.ts(2604)
83+
<Component testID={name}>
84+
<TextInput />
85+
</Component>
86+
);
87+
88+
expect(queryByTestId(name)).toBeEnabled();
89+
expect(() => expect(queryByTestId(name)).not.toBeEnabled()).toThrow();
90+
});
91+
});
92+
93+
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
94+
test(`handle disabled in accessibilityState for element ${name} when false`, () => {
95+
const { queryByTestId } = render(
96+
//@ts-expect-error JSX element type 'Component' does not have any construct or call signatures.ts(2604)
97+
<Component accessibilityState={{ disabled: false }} testID={name}>
98+
<TextInput />
99+
</Component>
100+
);
101+
102+
expect(queryByTestId(name)).toBeEnabled();
103+
expect(() => expect(queryByTestId(name)).not.toBeEnabled()).toThrow();
104+
});
105+
});
106+
107+
test('handle editable prop for TextInput', () => {
108+
const { getByTestId, getByPlaceholderText } = render(
109+
<View>
110+
<TextInput
111+
testID="enabled-by-default"
112+
placeholder="enabled-by-default"
113+
/>
114+
<TextInput testID="enabled" placeholder="enabled" editable />
115+
<TextInput testID="disabled" placeholder="disabled" editable={false} />
116+
</View>
117+
);
118+
119+
// Check host TextInput
120+
expect(getByTestId('enabled-by-default')).toBeEnabled();
121+
expect(getByTestId('enabled')).toBeEnabled();
122+
expect(getByTestId('disabled')).not.toBeEnabled();
123+
124+
// Check composite TextInput
125+
expect(getByPlaceholderText('enabled-by-default')).toBeEnabled();
126+
expect(getByPlaceholderText('enabled')).toBeEnabled();
127+
expect(getByPlaceholderText('disabled')).not.toBeEnabled();
128+
});
129+
});
130+
131+
describe('for .toBeEnabled/Disabled Button', () => {
132+
test('handles disabled prop for button', () => {
133+
const { queryByTestId } = render(
134+
<View>
135+
<Button testID="enabled" title="enabled" />
136+
<Button disabled testID="disabled" title="disabled" />
137+
</View>
138+
);
139+
140+
expect(queryByTestId('enabled')).toBeEnabled();
141+
expect(queryByTestId('disabled')).toBeDisabled();
142+
});
143+
144+
test('handles button a11y state', () => {
145+
const { queryByTestId } = render(
146+
<View>
147+
<Button
148+
accessibilityState={{ disabled: false }}
149+
testID="enabled"
150+
title="enabled"
151+
/>
152+
<Button
153+
accessibilityState={{ disabled: true }}
154+
testID="disabled"
155+
title="disabled"
156+
/>
157+
</View>
158+
);
159+
160+
expect(queryByTestId('enabled')).toBeEnabled();
161+
expect(queryByTestId('disabled')).toBeDisabled();
162+
});
163+
164+
test('Errors when matcher misses', () => {
165+
const { queryByTestId, queryByText } = render(
166+
<View>
167+
<Button testID="enabled" title="enabled" />
168+
<Button disabled testID="disabled" title="disabled" />
169+
</View>
170+
);
171+
172+
expect(() => expect(queryByTestId('enabled')).toBeDisabled()).toThrow();
173+
expect(() => expect(queryByText('disabled')).toBeEnabled()).toThrow();
174+
});
175+
});

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
toHaveDisplayValue(expectedValue: TextMatch, options?: TextMatchOptions): R;
88
toHaveTextContent(expectedText: TextMatch, options?: TextMatchOptions): R;
99
toBeDisabled(): R;
10+
toBeEnabled(): R;
1011
}
1112

1213
// Implicit Jest global `expect`.

src/matchers/extend-expect.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { toBeEmptyElement } from './to-be-empty-element';
55
import { toBeVisible } from './to-be-visible';
66
import { toHaveDisplayValue } from './to-have-display-value';
77
import { toHaveTextContent } from './to-have-text-content';
8-
import { toBeDisabled } from './to-be-disabled';
8+
import { toBeDisabled, toBeEnabled } from './to-be-disabled';
99

1010
expect.extend({
1111
toBeOnTheScreen,
@@ -14,4 +14,5 @@ expect.extend({
1414
toHaveDisplayValue,
1515
toHaveTextContent,
1616
toBeDisabled,
17+
toBeEnabled,
1718
});

src/matchers/to-be-disabled.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const DISABLE_TYPES = [
1616
'TextInput',
1717
'Pressable',
1818
];
19+
1920
function isElementDisabled(element: ReactTestInstance) {
2021
if (getType(element) === 'TextInput' && element?.props?.editable === false) {
2122
return true;
@@ -43,9 +44,7 @@ export function toBeDisabled(
4344
this: jest.MatcherContext,
4445
element: ReactTestInstance
4546
) {
46-
if (element !== null || !this.isNot) {
47-
checkHostElement(element, toBeDisabled, this);
48-
}
47+
checkHostElement(element, toBeDisabled, this);
4948

5049
const isDisabled = isElementDisabled(element) || isAncestorDisabled(element);
5150

@@ -69,3 +68,25 @@ export function toBeDisabled(
6968
},
7069
};
7170
}
71+
72+
export function toBeEnabled(
73+
this: jest.MatcherContext,
74+
element: ReactTestInstance
75+
) {
76+
checkHostElement(element, toBeEnabled, this);
77+
78+
const isEnabled = !isElementDisabled(element) && !isAncestorDisabled(element);
79+
80+
return {
81+
pass: isEnabled,
82+
message: () => {
83+
const is = isEnabled ? 'is' : 'is not';
84+
return [
85+
matcherHint(`${this.isNot ? '.not' : ''}.toBeEnabled`, 'element', ''),
86+
'',
87+
`Received element ${is} enabled:`,
88+
printElement(element),
89+
].join('\n');
90+
},
91+
};
92+
}

0 commit comments

Comments
 (0)