Skip to content

Commit

Permalink
feat: isInaccessible API form RTL/DTL (#1128)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdjastrzebski committed Sep 23, 2022
1 parent 807898e commit fa21fc5
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 8 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
2,
{ "ignore": ["^@theme", "^@docusaurus", "^@generated"] }
],
"react-native-a11y/has-valid-accessibility-ignores-invert-colors": 0,
"react-native/no-color-literals": "off",
"react-native/no-inline-styles": "off",
"react-native-a11y/has-valid-accessibility-descriptors": "off",
"react-native-a11y/has-valid-accessibility-ignores-invert-colors": 0,
"react-native-a11y/has-valid-accessibility-value": "off"
}
}
5 changes: 1 addition & 4 deletions src/fireEvent.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { ReactTestInstance } from 'react-test-renderer';
import act from './act';
import { isHostElement } from './helpers/component-tree';
import { filterNodeByType } from './helpers/filterNodeByType';

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

const isHostElement = (element?: ReactTestInstance) => {
return typeof element?.type === 'string';
};

const isTextInput = (element?: ReactTestInstance) => {
if (!element) {
return false;
Expand Down
165 changes: 165 additions & 0 deletions src/helpers/__tests__/accessiblity.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React from 'react';
import { View, Text, TextInput } from 'react-native';
import { render, isInaccessible } from '../..';

test('returns false for accessible elements', () => {
expect(
isInaccessible(render(<View testID="subject" />).getByTestId('subject'))
).toBe(false);

expect(
isInaccessible(
render(<Text testID="subject">Hello</Text>).getByTestId('subject')
)
).toBe(false);

expect(
isInaccessible(
render(<TextInput testID="subject" />).getByTestId('subject')
)
).toBe(false);
});

test('detects elements with accessibilityElementsHidden prop', () => {
const view = render(<View testID="subject" accessibilityElementsHidden />);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects nested elements with accessibilityElementsHidden prop', () => {
const view = render(
<View accessibilityElementsHidden>
<View testID="subject" />
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects deeply nested elements with accessibilityElementsHidden prop', () => {
const view = render(
<View accessibilityElementsHidden>
<View>
<View>
<View testID="subject" />
</View>
</View>
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects elements with importantForAccessibility="no-hide-descendants" prop', () => {
const view = render(
<View testID="subject" importantForAccessibility="no-hide-descendants" />
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects nested elements with importantForAccessibility="no-hide-descendants" prop', () => {
const view = render(
<View importantForAccessibility="no-hide-descendants">
<View testID="subject" />
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects elements with display=none', () => {
const view = render(<View testID="subject" style={{ display: 'none' }} />);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects nested elements with display=none', () => {
const view = render(
<View style={{ display: 'none' }}>
<View testID="subject" />
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects deeply nested elements with display=none', () => {
const view = render(
<View style={{ display: 'none' }}>
<View>
<View>
<View testID="subject" />
</View>
</View>
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects elements with display=none with complex style', () => {
const view = render(
<View
testID="subject"
style={[{ display: 'flex' }, [{ display: 'flex' }], { display: 'none' }]}
/>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('is not trigged by opacity = 0', () => {
const view = render(<View testID="subject" style={{ opacity: 0 }} />);
expect(isInaccessible(view.getByTestId('subject'))).toBe(false);
});

test('detects siblings of element with accessibilityViewIsModal prop', () => {
const view = render(
<View>
<View accessibilityViewIsModal />
<View testID="subject" />
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('detects deeply nested siblings of element with accessibilityViewIsModal prop', () => {
const view = render(
<View>
<View accessibilityViewIsModal />
<View>
<View>
<View testID="subject" />
</View>
</View>
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(true);
});

test('is not triggered for element with accessibilityViewIsModal prop', () => {
const view = render(
<View>
<View accessibilityViewIsModal testID="subject" />
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(false);
});

test('is not triggered for child of element with accessibilityViewIsModal prop', () => {
const view = render(
<View>
<View accessibilityViewIsModal>
<View testID="subject" />
</View>
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(false);
});

test('is not triggered for descendent of element with accessibilityViewIsModal prop', () => {
const view = render(
<View>
<View accessibilityViewIsModal>
<View>
<View>
<View testID="subject" />
</View>
</View>
</View>
</View>
);
expect(isInaccessible(view.getByTestId('subject'))).toBe(false);
});
202 changes: 202 additions & 0 deletions src/helpers/__tests__/component-tree.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import React from 'react';
import { View, Text, TextInput } from 'react-native';
import { render } from '../..';
import {
getHostChildren,
getHostParent,
getHostSelves,
getHostSiblings,
} from '../component-tree';

function MultipleHostChildren() {
return (
<>
<View testID="child1" />
<View testID="child2" />
<View testID="child3" />
</>
);
}

test('returns host parent for host component', () => {
const view = render(
<View testID="grandparent">
<View testID="parent">
<View testID="subject" />
<View testID="sibling" />
</View>
</View>
);

const hostParent = getHostParent(view.getByTestId('subject'));
expect(hostParent).toBe(view.getByTestId('parent'));

const hostGrandparent = getHostParent(hostParent);
expect(hostGrandparent).toBe(view.getByTestId('grandparent'));

expect(getHostParent(hostGrandparent)).toBe(null);
});

test('returns host parent for composite component', () => {
const view = render(
<View testID="parent">
<MultipleHostChildren />
<View testID="subject" />
</View>
);

const compositeComponent = view.UNSAFE_getByType(MultipleHostChildren);
const hostParent = getHostParent(compositeComponent);
expect(hostParent).toBe(view.getByTestId('parent'));
});

test('returns host children for host component', () => {
const view = render(
<View testID="grandparent">
<View testID="parent">
<View testID="subject" />
<View testID="sibling" />
</View>
</View>
);

const hostSubject = view.getByTestId('subject');
expect(getHostChildren(hostSubject)).toEqual([]);

const hostSibling = view.getByTestId('sibling');
const hostParent = view.getByTestId('parent');
expect(getHostChildren(hostParent)).toEqual([hostSubject, hostSibling]);

const hostGrandparent = view.getByTestId('grandparent');
expect(getHostChildren(hostGrandparent)).toEqual([hostParent]);
});

test('returns host children for composite component', () => {
const view = render(
<View testID="parent">
<MultipleHostChildren />
<View testID="subject" />
<View testID="sibling" />
</View>
);

expect(getHostChildren(view.getByTestId('parent'))).toEqual([
view.getByTestId('child1'),
view.getByTestId('child2'),
view.getByTestId('child3'),
view.getByTestId('subject'),
view.getByTestId('sibling'),
]);
});

test('returns host selves for host components', () => {
const view = render(
<View testID="grandparent">
<View testID="parent">
<View testID="subject" />
<View testID="sibling" />
</View>
</View>
);

const hostSubject = view.getByTestId('subject');
expect(getHostSelves(hostSubject)).toEqual([hostSubject]);

const hostSibling = view.getByTestId('sibling');
expect(getHostSelves(hostSibling)).toEqual([hostSibling]);

const hostParent = view.getByTestId('parent');
expect(getHostSelves(hostParent)).toEqual([hostParent]);

const hostGrandparent = view.getByTestId('grandparent');
expect(getHostSelves(hostGrandparent)).toEqual([hostGrandparent]);
});

test('returns host selves for React Native composite components', () => {
const view = render(
<View testID="parent">
<Text testID="text">Text</Text>
<TextInput
testID="textInput"
defaultValue="TextInputValue"
placeholder="TextInputPlaceholder"
/>
</View>
);

const compositeText = view.getByText('Text');
const hostText = view.getByTestId('text');
expect(getHostSelves(compositeText)).toEqual([hostText]);

const compositeTextInputByValue = view.getByDisplayValue('TextInputValue');
const compositeTextInputByPlaceholder = view.getByPlaceholderText(
'TextInputPlaceholder'
);
const hostTextInput = view.getByTestId('textInput');
expect(getHostSelves(compositeTextInputByValue)).toEqual([hostTextInput]);
expect(getHostSelves(compositeTextInputByPlaceholder)).toEqual([
hostTextInput,
]);
});

test('returns host selves for custom composite components', () => {
const view = render(
<View testID="parent">
<MultipleHostChildren />
<View testID="sibling" />
</View>
);

const compositeComponent = view.UNSAFE_getByType(MultipleHostChildren);
const hostChild1 = view.getByTestId('child1');
const hostChild2 = view.getByTestId('child2');
const hostChild3 = view.getByTestId('child3');
expect(getHostSelves(compositeComponent)).toEqual([
hostChild1,
hostChild2,
hostChild3,
]);
});

test('returns host siblings for host component', () => {
const view = render(
<View testID="grandparent">
<View testID="parent">
<View testID="siblingBefore" />
<View testID="subject" />
<View testID="siblingAfter" />
<MultipleHostChildren />
</View>
</View>
);

const hostSiblings = getHostSiblings(view.getByTestId('subject'));
expect(hostSiblings).toEqual([
view.getByTestId('siblingBefore'),
view.getByTestId('siblingAfter'),
view.getByTestId('child1'),
view.getByTestId('child2'),
view.getByTestId('child3'),
]);
});

test('returns host siblings for composite component', () => {
const view = render(
<View testID="grandparent">
<View testID="parent">
<View testID="siblingBefore" />
<View testID="subject" />
<View testID="siblingAfter" />
<MultipleHostChildren />
</View>
</View>
);

const compositeComponent = view.UNSAFE_getByType(MultipleHostChildren);
const hostSiblings = getHostSiblings(compositeComponent);
expect(hostSiblings).toEqual([
view.getByTestId('siblingBefore'),
view.getByTestId('subject'),
view.getByTestId('siblingAfter'),
]);
});
Loading

0 comments on commit fa21fc5

Please sign in to comment.