Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support RN 0.62 accessibilityState in toBeDisabled #30

Merged
merged 1 commit into from
Jul 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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