Skip to content

Commit 72e3133

Browse files
refactor: component tree dead code (#1403)
1 parent 01af9e7 commit 72e3133

8 files changed

+75
-187
lines changed

src/__tests__/config.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ test('resetToDefaults() resets config to defaults', () => {
3535

3636
test('resetToDefaults() resets internal config to defaults', () => {
3737
configureInternal({
38-
hostComponentNames: { text: 'A', textInput: 'A' },
38+
hostComponentNames: { text: 'A', textInput: 'A', switch: 'A' },
39+
});
40+
expect(getConfig().hostComponentNames).toEqual({
41+
text: 'A',
42+
textInput: 'A',
43+
switch: 'A',
3944
});
40-
expect(getConfig().hostComponentNames).toEqual({ text: 'A', textInput: 'A' });
4145

4246
resetToDefaults();
4347
expect(getConfig().hostComponentNames).toBe(undefined);

src/__tests__/host-component-names.test.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@ beforeEach(() => {
1717
describe('getHostComponentNames', () => {
1818
test('returns host component names from internal config', () => {
1919
configureInternal({
20-
hostComponentNames: { text: 'banana', textInput: 'banana' },
20+
hostComponentNames: {
21+
text: 'banana',
22+
textInput: 'banana',
23+
switch: 'banana',
24+
},
2125
});
2226

2327
expect(getHostComponentNames()).toEqual({
2428
text: 'banana',
2529
textInput: 'banana',
30+
switch: 'banana',
2631
});
2732
});
2833

@@ -34,6 +39,7 @@ describe('getHostComponentNames', () => {
3439
expect(hostComponentNames).toEqual({
3540
text: 'Text',
3641
textInput: 'TextInput',
42+
switch: 'RCTSwitch',
3743
});
3844
expect(getConfig().hostComponentNames).toBe(hostComponentNames);
3945
});
@@ -61,19 +67,25 @@ describe('configureHostComponentNamesIfNeeded', () => {
6167
expect(getConfig().hostComponentNames).toEqual({
6268
text: 'Text',
6369
textInput: 'TextInput',
70+
switch: 'RCTSwitch',
6471
});
6572
});
6673

6774
test('does not update internal config when host component names are already configured', () => {
6875
configureInternal({
69-
hostComponentNames: { text: 'banana', textInput: 'banana' },
76+
hostComponentNames: {
77+
text: 'banana',
78+
textInput: 'banana',
79+
switch: 'banana',
80+
},
7081
});
7182

7283
configureHostComponentNamesIfNeeded();
7384

7485
expect(getConfig().hostComponentNames).toEqual({
7586
text: 'banana',
7687
textInput: 'banana',
88+
switch: 'banana',
7789
});
7890
});
7991

src/__tests__/react-native-api.test.tsx

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { View, Text, TextInput } from 'react-native';
2+
import { View, Text, TextInput, Switch } from 'react-native';
33
import { render } from '..';
44

55
/**
@@ -19,7 +19,6 @@ test('React Native API assumption: <View> renders single host element', () => {
1919

2020
test('React Native API assumption: <Text> renders single host element', () => {
2121
const view = render(<Text testID="test">Hello</Text>);
22-
expect(view.getByText('Hello')).toBe(view.getByTestId('test'));
2322

2423
expect(view.toJSON()).toMatchInlineSnapshot(`
2524
<Text
@@ -40,11 +39,6 @@ test('React Native API assumption: nested <Text> renders single host element', (
4039
</Text>
4140
</Text>
4241
);
43-
expect(view.getByText(/Hello/)).toBe(view.getByTestId('test'));
44-
expect(view.getByText('Before')).toBe(view.getByTestId('before'));
45-
expect(view.getByText('Deeply nested')).toBe(
46-
view.getByTestId('deeplyNested')
47-
);
4842

4943
expect(view.toJSON()).toMatchInlineSnapshot(`
5044
<Text
@@ -78,9 +72,6 @@ test('React Native API assumption: <TextInput> renders single host element', ()
7872
placeholder="Placeholder"
7973
/>
8074
);
81-
expect(view.getByPlaceholderText('Placeholder')).toBe(
82-
view.getByTestId('test')
83-
);
8475

8576
expect(view.toJSON()).toMatchInlineSnapshot(`
8677
<TextInput
@@ -91,3 +82,45 @@ test('React Native API assumption: <TextInput> renders single host element', ()
9182
/>
9283
`);
9384
});
85+
86+
test('React Native API assumption: <TextInput> with nested Text renders single host element', () => {
87+
const view = render(
88+
<TextInput testID="test" placeholder="Placeholder">
89+
<Text>Hello</Text>
90+
</TextInput>
91+
);
92+
93+
expect(view.toJSON()).toMatchInlineSnapshot(`
94+
<TextInput
95+
placeholder="Placeholder"
96+
testID="test"
97+
>
98+
<Text>
99+
Hello
100+
</Text>
101+
</TextInput>
102+
`);
103+
});
104+
105+
test('React Native API assumption: <Switch> renders single host element', () => {
106+
const view = render(
107+
<Switch testID="test" value={true} onChange={jest.fn()} />
108+
);
109+
110+
expect(view.toJSON()).toMatchInlineSnapshot(`
111+
<RCTSwitch
112+
accessibilityRole="switch"
113+
onChange={[Function]}
114+
onResponderTerminationRequest={[Function]}
115+
onStartShouldSetResponder={[Function]}
116+
style={
117+
{
118+
"height": 31,
119+
"width": 51,
120+
}
121+
}
122+
testID="test"
123+
value={true}
124+
/>
125+
`);
126+
});

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type ConfigAliasOptions = {
2323
export type HostComponentNames = {
2424
text: string;
2525
textInput: string;
26+
switch: string;
2627
};
2728

2829
export type InternalConfig = Config & {

src/helpers/__tests__/component-tree.test.tsx

Lines changed: 0 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@ import { render } from '../..';
44
import {
55
getHostChildren,
66
getHostParent,
7-
getHostSelf,
87
getHostSelves,
98
getHostSiblings,
10-
getCompositeParentOfType,
11-
isHostElementForType,
129
} from '../component-tree';
1310

1411
function ZeroHostChildren() {
@@ -106,72 +103,6 @@ describe('getHostChildren()', () => {
106103
});
107104
});
108105

109-
describe('getHostSelf()', () => {
110-
it('returns passed element for host components', () => {
111-
const view = render(
112-
<View testID="grandparent">
113-
<View testID="parent">
114-
<View testID="subject" />
115-
<View testID="sibling" />
116-
</View>
117-
</View>
118-
);
119-
120-
const hostSubject = view.getByTestId('subject');
121-
expect(getHostSelf(hostSubject)).toEqual(hostSubject);
122-
123-
const hostSibling = view.getByTestId('sibling');
124-
expect(getHostSelf(hostSibling)).toEqual(hostSibling);
125-
126-
const hostParent = view.getByTestId('parent');
127-
expect(getHostSelf(hostParent)).toEqual(hostParent);
128-
129-
const hostGrandparent = view.getByTestId('grandparent');
130-
expect(getHostSelf(hostGrandparent)).toEqual(hostGrandparent);
131-
});
132-
133-
it('returns single host child for React Native composite components', () => {
134-
const view = render(
135-
<View testID="parent">
136-
<Text testID="text">Text</Text>
137-
<TextInput
138-
testID="textInput"
139-
defaultValue="TextInputValue"
140-
placeholder="TextInputPlaceholder"
141-
/>
142-
</View>
143-
);
144-
145-
const compositeText = view.UNSAFE_getByType(Text);
146-
const hostText = view.getByTestId('text');
147-
expect(getHostSelf(compositeText)).toEqual(hostText);
148-
149-
const compositeTextInput = view.UNSAFE_getByType(TextInput);
150-
const hostTextInput = view.getByTestId('textInput');
151-
expect(getHostSelf(compositeTextInput)).toEqual(hostTextInput);
152-
});
153-
154-
it('throws on non-single host children elements for custom composite components', () => {
155-
const view = render(
156-
<View testID="parent">
157-
<ZeroHostChildren />
158-
<MultipleHostChildren />
159-
</View>
160-
);
161-
162-
const zeroCompositeComponent = view.UNSAFE_getByType(ZeroHostChildren);
163-
expect(() => getHostSelf(zeroCompositeComponent)).toThrow(
164-
'Expected exactly one host element, but found none.'
165-
);
166-
167-
const multipleCompositeComponent =
168-
view.UNSAFE_getByType(MultipleHostChildren);
169-
expect(() => getHostSelf(multipleCompositeComponent)).toThrow(
170-
'Expected exactly one host element, but found 3.'
171-
);
172-
});
173-
});
174-
175106
describe('getHostSelves()', () => {
176107
it('returns passed element for host components', () => {
177108
const view = render(
@@ -293,38 +224,3 @@ describe('getHostSiblings()', () => {
293224
]);
294225
});
295226
});
296-
297-
test('getCompositeParentOfType', () => {
298-
const root = render(
299-
<View testID="view">
300-
<Text testID="text" />
301-
</View>
302-
);
303-
const hostView = root.getByTestId('view');
304-
const hostText = root.getByTestId('text');
305-
306-
const compositeView = getCompositeParentOfType(hostView, View);
307-
// We get the corresponding composite component (same testID), but not the host
308-
expect(compositeView?.type).toBe(View);
309-
expect(compositeView?.props.testID).toBe('view');
310-
const compositeText = getCompositeParentOfType(hostText, Text);
311-
expect(compositeText?.type).toBe(Text);
312-
expect(compositeText?.props.testID).toBe('text');
313-
314-
// Checks parent type
315-
expect(getCompositeParentOfType(hostText, View)).toBeNull();
316-
expect(getCompositeParentOfType(hostView, Text)).toBeNull();
317-
318-
// Ignores itself, stops if ancestor is host
319-
expect(getCompositeParentOfType(compositeText!, Text)).toBeNull();
320-
expect(getCompositeParentOfType(compositeView!, View)).toBeNull();
321-
});
322-
323-
test('isHostElementForType', () => {
324-
const view = render(<View testID="test" />);
325-
const hostComponent = view.getByTestId('test');
326-
const compositeComponent = getCompositeParentOfType(hostComponent, View);
327-
expect(isHostElementForType(hostComponent, View)).toBe(true);
328-
expect(isHostElementForType(hostComponent, Text)).toBe(false);
329-
expect(isHostElementForType(compositeComponent!, View)).toBe(false);
330-
});

src/helpers/accessiblity.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ import {
22
AccessibilityState,
33
AccessibilityValue,
44
StyleSheet,
5-
Switch,
6-
Text,
7-
TextInput,
85
} from 'react-native';
96
import { ReactTestInstance } from 'react-test-renderer';
10-
import { getHostSiblings, isHostElementForType } from './component-tree';
7+
import { getConfig } from '../config';
8+
import { getHostSiblings } from './component-tree';
119

1210
type IsInaccessibleOptions = {
1311
cache?: WeakMap<ReactTestInstance, boolean>;
@@ -101,9 +99,11 @@ export function isAccessibilityElement(
10199
return element.props.accessible;
102100
}
103101

102+
const hostComponentNames = getConfig().hostComponentNames;
103+
104104
return (
105-
isHostElementForType(element, Text) ||
106-
isHostElementForType(element, TextInput) ||
107-
isHostElementForType(element, Switch)
105+
element?.type === hostComponentNames?.text ||
106+
element?.type === hostComponentNames?.textInput ||
107+
element?.type === hostComponentNames?.switch
108108
);
109109
}

src/helpers/component-tree.ts

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ReactTestInstance } from 'react-test-renderer';
44
* Checks if the given element is a host element.
55
* @param element The element to check.
66
*/
7-
export function isHostElement(element?: ReactTestInstance | null): boolean {
7+
export function isHostElement(element?: ReactTestInstance | null) {
88
return typeof element?.type === 'string';
99
}
1010

@@ -59,32 +59,6 @@ export function getHostChildren(
5959
return hostChildren;
6060
}
6161

62-
/**
63-
* Return a single host element that represent the passed host or composite element.
64-
*
65-
* @param element The element start traversing from.
66-
* @throws Error if the passed element is a composite element and has no host children or has more than one host child.
67-
* @returns If the passed element is a host element, it will return itself, if the passed element is a composite
68-
* element, it will return a single host descendant.
69-
*/
70-
export function getHostSelf(
71-
element: ReactTestInstance | null
72-
): ReactTestInstance {
73-
const hostSelves = getHostSelves(element);
74-
75-
if (hostSelves.length === 0) {
76-
throw new Error(`Expected exactly one host element, but found none.`);
77-
}
78-
79-
if (hostSelves.length > 1) {
80-
throw new Error(
81-
`Expected exactly one host element, but found ${hostSelves.length}.`
82-
);
83-
}
84-
85-
return hostSelves[0];
86-
}
87-
8862
/**
8963
* Return the array of host elements that represent the passed element.
9064
*
@@ -113,37 +87,3 @@ export function getHostSiblings(
11387
(sibling) => !hostSelves.includes(sibling)
11488
);
11589
}
116-
117-
export function getCompositeParentOfType(
118-
element: ReactTestInstance,
119-
type: React.ComponentType
120-
) {
121-
let current = element.parent;
122-
123-
while (!isHostElement(current)) {
124-
// We're at the root of the tree
125-
if (!current) {
126-
return null;
127-
}
128-
129-
if (current.type === type) {
130-
return current;
131-
}
132-
current = current.parent;
133-
}
134-
135-
return null;
136-
}
137-
138-
/**
139-
* Note: this function should be generally used for core React Native types like `View`, `Text`, `TextInput`, etc.
140-
*/
141-
export function isHostElementForType(
142-
element: ReactTestInstance,
143-
type: React.ComponentType
144-
) {
145-
// Not a host element
146-
if (!isHostElement(element)) return false;
147-
148-
return getCompositeParentOfType(element, type) !== null;
149-
}

0 commit comments

Comments
 (0)