Skip to content

Commit

Permalink
feat: support RN 0.62 accessibilityState in toBeDisabled (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
brunohkbx authored Jul 29, 2020
1 parent db5ccb3 commit 7667e16
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 95 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">
<h1>jest-native</h1>

<a href="https://www.joypixels.com/emoji/1f985">
<img
height="80"
Expand All @@ -9,7 +9,7 @@
src="https://raw.githubusercontent.com/testing-library/jest-native/master/other/eagle.png"
/>
</a>

<p>Custom jest matchers to test the state of React Native.</p>
</div>

Expand Down Expand Up @@ -117,15 +117,18 @@ toBeDisabled();
Check whether or not an element is disabled from a user perspective.

This matcher will check if the element or its parent has a `disabled` prop, or if it has
`accessibilityStates={['disabled']}`.
`accessibilityState={{disabled: true]}.

It also works with `accessibilityStates={['disabled']}` for now. However, this prop is deprecated in
React Native [0.62](https://reactnative.dev/blog/2020/03/26/version-0.62#breaking-changes)

#### Examples

```javascript
const { getByTestId } = render(
<View>
<Button disabled testID="button" title="submit" onPress={(e) => e} />
<TextInput accessibilityStates={['disabled']} testID="input" value="text" />
<Button disabled testID="button" title="submit" onPress={e => e} />
<TextInput accessibilityState={{ disabled: true }} testID="input" value="text" />
</View>,
);

Expand All @@ -148,7 +151,7 @@ Works similarly to `expect().not.toBeDisabled()`.
```javascript
const { getByTestId } = render(
<View>
<Button testID="button" title="submit" onPress={(e) => e} />
<Button testID="button" title="submit" onPress={e => e} />
<TextInput testID="input" value="text" />
</View>,
);
Expand Down
154 changes: 69 additions & 85 deletions src/__tests__/to-be-disabled.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import {
Button,
Text,
TouchableHighlight,
TouchableOpacity,
TouchableWithoutFeedback,
Expand All @@ -10,92 +9,77 @@ import {
} from 'react-native';
import { render } from '@testing-library/react-native';

test('.toBeDisabled', () => {
const { queryByTestId, queryByText, queryByTitle, queryByDisplayValue } = render(
<View disabled testID="view">
<Button disabled testID="button" title="button" />
<TextInput editable={false} testID="textInput" value="textInput" />
<TouchableHighlight disabled testID="highlight">
<Text>highlight</Text>
</TouchableHighlight>
<TouchableOpacity disabled testID="opacity">
<Text>opacity</Text>
</TouchableOpacity>
<TouchableWithoutFeedback disabled testID="without">
<Text>without</Text>
</TouchableWithoutFeedback>
</View>,
);

expect(queryByTestId('view')).toBeDisabled();
expect(() => expect(queryByTestId('view')).not.toBeDisabled()).toThrowError();

expect(queryByTitle('button')).toBeDisabled();
expect(() => expect(queryByTitle('button')).not.toBeDisabled()).toThrowError();

expect(queryByTestId('textInput')).toBeDisabled();
expect(queryByDisplayValue('textInput')).toBeDisabled();
expect(() => expect(queryByTestId('textInput')).not.toBeDisabled()).toThrowError();
expect(() => expect(queryByDisplayValue('textInput')).not.toBeDisabled()).toThrowError();

expect(queryByTestId('highlight')).toBeDisabled();
expect(queryByText('highlight')).toBeDisabled();
expect(() => expect(queryByTestId('highlight')).not.toBeDisabled()).toThrowError();
expect(() => expect(queryByText('highlight')).not.toBeDisabled()).toThrowError();

expect(queryByTestId('opacity')).toBeDisabled();
expect(queryByText('opacity')).toBeDisabled();
expect(() => expect(queryByTestId('opacity')).not.toBeDisabled()).toThrowError();
expect(() => expect(queryByText('opacity')).not.toBeDisabled()).toThrowError();

expect(queryByTestId('without')).toBeDisabled();
expect(queryByText('without')).toBeDisabled();
expect(() => expect(queryByTestId('without')).not.toBeDisabled()).toThrowError();
expect(() => expect(queryByText('without')).not.toBeDisabled()).toThrowError();
const ALLOWED_COMPONENTS = {
View,
Button,
TextInput,
TouchableHighlight,
TouchableOpacity,
TouchableWithoutFeedback,
};

describe('.toBeDisabled', () => {
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled prop for element ${name}`, () => {
const { queryByTestId } = render(<Component disabled testID={name} />);

expect(queryByTestId(name)).toBeDisabled();
expect(() => expect(queryByTestId(name)).not.toBeDisabled()).toThrowError();
});
});

Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled in accessibilityStates for element ${name}`, () => {
const { queryByTestId } = render(
<Component accessibilityStates={['disabled']} testID={name} />,
);

expect(queryByTestId(name)).toBeDisabled();
expect(() => expect(queryByTestId(name)).not.toBeDisabled()).toThrowError();
});
});

Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled in accessibilityState for element ${name}`, () => {
const { queryByTestId } = render(
<Component accessibilityState={{ disabled: true }} testID={name} />,
);

expect(queryByTestId(name)).toBeDisabled();
expect(() => expect(queryByTestId(name)).not.toBeDisabled()).toThrowError();
});
});
});

test('.toBeEnabled', () => {
const { queryByTestId, queryByText, queryByTitle, queryByDisplayValue } = render(
<View testID="view">
<Button title="button" />
<TextInput testID="textInput" value="textInput" />
<TouchableHighlight testID="highlight">
<Text>highlight</Text>
</TouchableHighlight>
<TouchableOpacity testID="opacity">
<Text>opacity</Text>
</TouchableOpacity>
<TouchableWithoutFeedback testID="without">
<Text>without</Text>
</TouchableWithoutFeedback>
</View>,
);

expect(queryByTestId('view')).toBeEnabled();
expect(() => expect(queryByTestId('view')).not.toBeEnabled()).toThrowError();

expect(queryByTitle('button')).toBeEnabled();
expect(() => expect(queryByTitle('button')).not.toBeEnabled()).toThrowError();

expect(queryByTestId('textInput')).toBeEnabled();
expect(queryByDisplayValue('textInput')).toBeEnabled();
expect(() => expect(queryByTestId('textInput')).not.toBeEnabled()).toThrowError();
expect(() => expect(queryByDisplayValue('textInput')).not.toBeEnabled()).toThrowError();

expect(queryByTestId('highlight')).toBeEnabled();
expect(queryByText('highlight')).toBeEnabled();
expect(() => expect(queryByTestId('highlight')).not.toBeEnabled()).toThrowError();
expect(() => expect(queryByText('highlight')).not.toBeEnabled()).toThrowError();

expect(queryByTestId('opacity')).toBeEnabled();
expect(queryByText('opacity')).toBeEnabled();
expect(() => expect(queryByTestId('opacity')).not.toBeEnabled()).toThrowError();
expect(() => expect(queryByText('opacity')).not.toBeEnabled()).toThrowError();

expect(queryByTestId('without')).toBeEnabled();
expect(queryByText('without')).toBeEnabled();
expect(() => expect(queryByTestId('without')).not.toBeEnabled()).toThrowError();
expect(() => expect(queryByText('without')).not.toBeEnabled()).toThrowError();
describe('.toBeEnabled', () => {
Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled prop for element ${name} when undefined`, () => {
const { queryByTestId } = render(<Component testID={name} />);

expect(queryByTestId(name)).toBeEnabled();
expect(() => expect(queryByTestId(name)).not.toBeEnabled()).toThrowError();
});
});

Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled in accessibilityStates for element ${name} when not included`, () => {
const { queryByTestId } = render(<Component accessibilityStates={[]} testID={name} />);

expect(queryByTestId(name)).toBeEnabled();
expect(() => expect(queryByTestId(name)).not.toBeEnabled()).toThrowError();
});
});

Object.entries(ALLOWED_COMPONENTS).forEach(([name, Component]) => {
test(`handle disabled in accessibilityState for element ${name} when false`, () => {
const { queryByTestId } = render(
<Component accessibilityState={{ disabled: false }} testID={name} />,
);

expect(queryByTestId(name)).toBeEnabled();
expect(() => expect(queryByTestId(name)).not.toBeEnabled()).toThrowError();
});
});
});

test('matcher misses', () => {
Expand Down
14 changes: 10 additions & 4 deletions src/to-be-disabled.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { compose, defaultTo, includes, path } from 'ramda';
import { compose, defaultTo, includes, path, propEq, anyPass } from 'ramda';
import { matcherHint } from 'jest-matcher-utils';

import { checkReactElement, getType, printElement } from './utils';
Expand All @@ -22,13 +22,19 @@ function isElementDisabledByParent(parent) {

function isElementDisabled(element) {
const propDisabled = path(['props', 'disabled'], element);
const stateDisabled = compose(
const hasStatesDisabled = compose(
includes('disabled'),
defaultTo([]),
path(['props', 'accessibilityStates']),
)(element);
);
const hasStateDisabled = compose(
propEq('disabled', true),
defaultTo({}),
path(['props', 'accessibilityState']),
);
const stateDisabled = anyPass([hasStatesDisabled, hasStateDisabled])(element);

return Boolean(DISABLE_TYPES.includes(getType(element)) && (propDisabled || stateDisabled));
return DISABLE_TYPES.includes(getType(element)) && (Boolean(propDisabled) || stateDisabled);
}

function isAncestorDisabled(element) {
Expand Down

0 comments on commit 7667e16

Please sign in to comment.