Skip to content

Commit

Permalink
feat: support Image with alt prop as label (#1665)
Browse files Browse the repository at this point in the history
* refactor: detect Image host component name

* feat: support Image alt prop in *ByLabelText

* feat: support *ByRole queries

* feat: support toHaveAccessibleName matcher

* chore: fix typecheck

* refactor: self code review

* refactor: adjust finding to experiment results

* chore: add missing tests

* refactor: self code review

* refactor: self code review

* refactor: self-code review
  • Loading branch information
mdjastrzebski authored Sep 12, 2024
1 parent f69d1d5 commit 04c7ee4
Show file tree
Hide file tree
Showing 14 changed files with 255 additions and 122 deletions.
2 changes: 2 additions & 0 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ test('resetToDefaults() resets internal config to defaults', () => {
hostComponentNames: {
text: 'A',
textInput: 'A',
image: 'A',
switch: 'A',
scrollView: 'A',
modal: 'A',
Expand All @@ -41,6 +42,7 @@ test('resetToDefaults() resets internal config to defaults', () => {
expect(getConfig().hostComponentNames).toEqual({
text: 'A',
textInput: 'A',
image: 'A',
switch: 'A',
scrollView: 'A',
modal: 'A',
Expand Down
8 changes: 7 additions & 1 deletion src/__tests__/host-component-names.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('getHostComponentNames', () => {
hostComponentNames: {
text: 'banana',
textInput: 'banana',
image: 'banana',
switch: 'banana',
scrollView: 'banana',
modal: 'banana',
Expand All @@ -23,6 +24,7 @@ describe('getHostComponentNames', () => {
expect(getHostComponentNames()).toEqual({
text: 'banana',
textInput: 'banana',
image: 'banana',
switch: 'banana',
scrollView: 'banana',
modal: 'banana',
Expand All @@ -37,6 +39,7 @@ describe('getHostComponentNames', () => {
expect(hostComponentNames).toEqual({
text: 'Text',
textInput: 'TextInput',
image: 'Image',
switch: 'RCTSwitch',
scrollView: 'RCTScrollView',
modal: 'Modal',
Expand Down Expand Up @@ -67,6 +70,7 @@ describe('configureHostComponentNamesIfNeeded', () => {
expect(getConfig().hostComponentNames).toEqual({
text: 'Text',
textInput: 'TextInput',
image: 'Image',
switch: 'RCTSwitch',
scrollView: 'RCTScrollView',
modal: 'Modal',
Expand All @@ -78,6 +82,7 @@ describe('configureHostComponentNamesIfNeeded', () => {
hostComponentNames: {
text: 'banana',
textInput: 'banana',
image: 'banana',
switch: 'banana',
scrollView: 'banana',
modal: 'banana',
Expand All @@ -89,13 +94,14 @@ describe('configureHostComponentNamesIfNeeded', () => {
expect(getConfig().hostComponentNames).toEqual({
text: 'banana',
textInput: 'banana',
image: 'banana',
switch: 'banana',
scrollView: 'banana',
modal: 'banana',
});
});

test('throw an error when autodetection fails', () => {
test('throw an error when auto-detection fails', () => {
const mockCreate = jest.spyOn(TestRenderer, 'create') as jest.Mock;
const renderer = TestRenderer.create(<View />);

Expand Down
202 changes: 119 additions & 83 deletions src/__tests__/react-native-api.test.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as React from 'react';
import { FlatList, ScrollView, Switch, Text, TextInput, View } from 'react-native';
import { FlatList, Image, Modal, ScrollView, Switch, Text, TextInput, View } from 'react-native';
import { render, screen } from '..';

/**
* Tests in this file are intended to give us an proactive warning that React Native behavior has
* changed in a way that may impact our code like queries or event handling.
*/

test('React Native API assumption: <View> renders single host element', () => {
test('React Native API assumption: <View> renders a single host element', () => {
render(<View testID="test" />);

expect(screen.toJSON()).toMatchInlineSnapshot(`
Expand All @@ -17,7 +17,7 @@ test('React Native API assumption: <View> renders single host element', () => {
`);
});

test('React Native API assumption: <Text> renders single host element', () => {
test('React Native API assumption: <Text> renders a single host element', () => {
render(<Text testID="test">Hello</Text>);

expect(screen.toJSON()).toMatchInlineSnapshot(`
Expand All @@ -29,7 +29,7 @@ test('React Native API assumption: <Text> renders single host element', () => {
`);
});

test('React Native API assumption: nested <Text> renders single host element', () => {
test('React Native API assumption: nested <Text> renders a single host element', () => {
render(
<Text testID="test">
<Text testID="before">Before</Text>
Expand Down Expand Up @@ -63,7 +63,7 @@ test('React Native API assumption: nested <Text> renders single host element', (
`);
});

test('React Native API assumption: <TextInput> renders single host element', () => {
test('React Native API assumption: <TextInput> renders a single host element', () => {
render(
<TextInput
testID="test"
Expand Down Expand Up @@ -102,7 +102,7 @@ test('React Native API assumption: <TextInput> with nested Text renders single h
`);
});

test('React Native API assumption: <Switch> renders single host element', () => {
test('React Native API assumption: <Switch> renders a single host element', () => {
render(<Switch testID="test" value={true} onChange={jest.fn()} />);

expect(screen.toJSON()).toMatchInlineSnapshot(`
Expand All @@ -123,7 +123,117 @@ test('React Native API assumption: <Switch> renders single host element', () =>
`);
});

test('React Native API assumption: aria-* props render on host View', () => {
test('React Native API assumption: <Image> renders a single host element', () => {
render(<Image testID="test" source={{ uri: 'https://fake.url/image.jpg' }} alt="Alt text" />);

expect(screen.toJSON()).toMatchInlineSnapshot(`
<Image
alt="Alt text"
source={
{
"uri": "https://fake.url/image.jpg",
}
}
testID="test"
/>
`);
});

test('React Native API assumption: <ScrollView> renders a single host element', () => {
render(
<ScrollView testID="scrollView">
<View testID="view" />
</ScrollView>,
);

expect(screen.toJSON()).toMatchInlineSnapshot(`
<RCTScrollView
testID="scrollView"
>
<View>
<View
testID="view"
/>
</View>
</RCTScrollView>
`);
});

test('React Native API assumption: <FlatList> renders a single host <ScrollView> element', () => {
render(
<FlatList testID="flatList" data={[1, 2]} renderItem={({ item }) => <Text>{item}</Text>} />,
);

expect(screen.toJSON()).toMatchInlineSnapshot(`
<RCTScrollView
data={
[
1,
2,
]
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
onMomentumScrollEnd={[Function]}
onScroll={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
removeClippedSubviews={false}
renderItem={[Function]}
scrollEventThrottle={0.0001}
stickyHeaderIndices={[]}
testID="flatList"
viewabilityConfigCallbackPairs={[]}
>
<View>
<View
onFocusCapture={[Function]}
onLayout={[Function]}
style={null}
>
<Text>
1
</Text>
</View>
<View
onFocusCapture={[Function]}
onLayout={[Function]}
style={null}
>
<Text>
2
</Text>
</View>
</View>
</RCTScrollView>
`);
});

test('React Native API assumption: <Modal> renders a single host element', () => {
render(
<Modal testID="test">
<Text>Modal Content</Text>
</Modal>,
);

expect(screen.toJSON()).toMatchInlineSnapshot(`
<Modal
hardwareAccelerated={false}
testID="test"
visible={true}
>
<Text>
Modal Content
</Text>
</Modal>
`);
});

test('React Native API assumption: aria-* props render directly on host View', () => {
render(
<View
testID="test"
Expand Down Expand Up @@ -171,7 +281,7 @@ test('React Native API assumption: aria-* props render on host View', () => {
`);
});

test('React Native API assumption: aria-* props render on host Text', () => {
test('React Native API assumption: aria-* props render directly on host Text', () => {
render(
<Text
testID="test"
Expand Down Expand Up @@ -219,7 +329,7 @@ test('React Native API assumption: aria-* props render on host Text', () => {
`);
});

test('React Native API assumption: aria-* props render on host TextInput', () => {
test('React Native API assumption: aria-* props render directly on host TextInput', () => {
render(
<TextInput
testID="test"
Expand Down Expand Up @@ -266,77 +376,3 @@ test('React Native API assumption: aria-* props render on host TextInput', () =>
/>
`);
});

test('ScrollView renders correctly', () => {
render(
<ScrollView testID="scrollView">
<View testID="view" />
</ScrollView>,
);

expect(screen.toJSON()).toMatchInlineSnapshot(`
<RCTScrollView
testID="scrollView"
>
<View>
<View
testID="view"
/>
</View>
</RCTScrollView>
`);
});

test('FlatList renders correctly', () => {
render(
<FlatList testID="flatList" data={[1, 2]} renderItem={({ item }) => <Text>{item}</Text>} />,
);

expect(screen.toJSON()).toMatchInlineSnapshot(`
<RCTScrollView
data={
[
1,
2,
]
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
onMomentumScrollEnd={[Function]}
onScroll={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
removeClippedSubviews={false}
renderItem={[Function]}
scrollEventThrottle={0.0001}
stickyHeaderIndices={[]}
testID="flatList"
viewabilityConfigCallbackPairs={[]}
>
<View>
<View
onFocusCapture={[Function]}
onLayout={[Function]}
style={null}
>
<Text>
1
</Text>
</View>
<View
onFocusCapture={[Function]}
onLayout={[Function]}
style={null}
>
<Text>
2
</Text>
</View>
</View>
</RCTScrollView>
`);
});
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type ConfigAliasOptions = {
export type HostComponentNames = {
text: string;
textInput: string;
image: string;
switch: string;
scrollView: string;
modal: string;
Expand Down
Loading

0 comments on commit 04c7ee4

Please sign in to comment.