Skip to content

Commit faebd54

Browse files
jaworekmdjastrzebski
authored andcommitted
feat: add toBeDisabled matcher
1 parent 3ff0afb commit faebd54

File tree

5 files changed

+79
-0
lines changed

5 files changed

+79
-0
lines changed

src/matchers/__tests__/to-be-disabled.test.tsx

Whitespace-only changes.

src/matchers/extend-expect.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface JestNativeMatchers<R> {
66
toBeVisible(): R;
77
toHaveDisplayValue(expectedValue: TextMatch, options?: TextMatchOptions): R;
88
toHaveTextContent(expectedText: TextMatch, options?: TextMatchOptions): R;
9+
toBeDisabled(): R;
910
}
1011

1112
// Implicit Jest global `expect`.

src/matchers/extend-expect.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ 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';
89

910
expect.extend({
1011
toBeOnTheScreen,
1112
toBeEmptyElement,
1213
toBeVisible,
1314
toHaveDisplayValue,
1415
toHaveTextContent,
16+
toBeDisabled,
1517
});

src/matchers/to-be-disabled.tsx

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import type { ReactTestInstance } from 'react-test-renderer';
2+
import { matcherHint } from 'jest-matcher-utils';
3+
import { checkHostElement, formatMessage, getType } from './utils';
4+
5+
// Elements that support 'disabled'
6+
const DISABLE_TYPES = [
7+
'Button',
8+
'Slider',
9+
'Switch',
10+
'Text',
11+
'TouchableHighlight',
12+
'TouchableOpacity',
13+
'TouchableWithoutFeedback',
14+
'TouchableNativeFeedback',
15+
'View',
16+
'TextInput',
17+
'Pressable',
18+
];
19+
function isElementDisabled(element: ReactTestInstance) {
20+
if (getType(element) === 'TextInput' && element?.props?.editable === false) {
21+
return true;
22+
}
23+
24+
if (!DISABLE_TYPES.includes(getType(element))) {
25+
return false;
26+
}
27+
28+
return (
29+
!!element?.props?.disabled ||
30+
!!element?.props?.accessibilityState?.disabled ||
31+
!!element?.props?.accessibilityStates?.includes('disabled')
32+
);
33+
}
34+
35+
function isAncestorDisabled(element: ReactTestInstance): boolean {
36+
const parent = element.parent;
37+
return (
38+
parent != null && (isElementDisabled(element) || isAncestorDisabled(parent))
39+
);
40+
}
41+
42+
export function toBeDisabled(
43+
this: jest.MatcherContext,
44+
element: ReactTestInstance
45+
) {
46+
if (element !== null || !this.isNot) {
47+
checkHostElement(element, toBeDisabled, this);
48+
}
49+
50+
const isDisabled = isElementDisabled(element) || isAncestorDisabled(element);
51+
52+
return {
53+
pass: isDisabled,
54+
message: () => {
55+
const is = isDisabled ? 'is' : 'is not';
56+
return [
57+
formatMessage(
58+
matcherHint(
59+
`${this.isNot ? '.not' : ''}.toBeDisabled`,
60+
'element',
61+
''
62+
),
63+
'',
64+
`Received element ${is} disabled:`,
65+
printElement(element),
66+
null
67+
),
68+
].join('\n');
69+
},
70+
};
71+
}

src/matchers/utils.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,8 @@ export function formatMessage(
121121
function formatValue(value: unknown) {
122122
return typeof value === 'string' ? value : stringify(value);
123123
}
124+
125+
export function getType({ type }: ReactTestInstance) {
126+
// @ts-expect-error: ReactTestInstance contains too loose typing
127+
return type.displayName || type.name || type;
128+
}

0 commit comments

Comments
 (0)