refactor: simplify navigation

This commit is contained in:
Maciej Jastrzebski
2024-10-16 10:38:56 +02:00
parent abe8d71eaf
commit ecc2b7f08e
4 changed files with 15 additions and 143 deletions
+2 -99
View File
@@ -1,12 +1,7 @@
import React from 'react';
import { Text, TextInput, View } from 'react-native';
import { View } from 'react-native';
import { render, screen } from '../..';
import {
getHostChildren,
getHostSelves,
getHostSiblings,
getUnsafeRootElement,
} from '../component-tree';
import { getHostSiblings, getUnsafeRootElement } from '../component-tree';
function MultipleHostChildren() {
return (
@@ -18,98 +13,6 @@ function MultipleHostChildren() {
);
}
describe('getHostChildren()', () => {
it('returns host children for host component', () => {
render(
<View testID="grandparent">
<View testID="parent">
<View testID="subject" />
<Text testID="sibling">Hello</Text>
</View>
</View>,
);
const hostSubject = screen.getByTestId('subject');
expect(getHostChildren(hostSubject)).toEqual([]);
const hostSibling = screen.getByTestId('sibling');
expect(getHostChildren(hostSibling)).toEqual([]);
const hostParent = screen.getByTestId('parent');
expect(getHostChildren(hostParent)).toEqual([hostSubject, hostSibling]);
const hostGrandparent = screen.getByTestId('grandparent');
expect(getHostChildren(hostGrandparent)).toEqual([hostParent]);
});
it('returns host children for composite component', () => {
render(
<View testID="parent">
<MultipleHostChildren />
<View testID="subject" />
<View testID="sibling" />
</View>,
);
expect(getHostChildren(screen.getByTestId('parent'))).toEqual([
screen.getByTestId('child1'),
screen.getByTestId('child2'),
screen.getByTestId('child3'),
screen.getByTestId('subject'),
screen.getByTestId('sibling'),
]);
});
});
describe('getHostSelves()', () => {
it('returns passed element for host components', () => {
render(
<View testID="grandparent">
<View testID="parent">
<View testID="subject" />
<View testID="sibling" />
</View>
</View>,
);
const hostSubject = screen.getByTestId('subject');
expect(getHostSelves(hostSubject)).toEqual([hostSubject]);
const hostSibling = screen.getByTestId('sibling');
expect(getHostSelves(hostSibling)).toEqual([hostSibling]);
const hostParent = screen.getByTestId('parent');
expect(getHostSelves(hostParent)).toEqual([hostParent]);
const hostGrandparent = screen.getByTestId('grandparent');
expect(getHostSelves(hostGrandparent)).toEqual([hostGrandparent]);
});
test('returns single host element for React Native composite components', () => {
render(
<View testID="parent">
<Text testID="text">Text</Text>
<TextInput
testID="textInput"
defaultValue="TextInputValue"
placeholder="TextInputPlaceholder"
/>
</View>,
);
const compositeText = screen.getByText('Text');
const hostText = screen.getByTestId('text');
expect(getHostSelves(compositeText)).toEqual([hostText]);
const compositeTextInputByValue = screen.getByDisplayValue('TextInputValue');
const compositeTextInputByPlaceholder = screen.getByPlaceholderText('TextInputPlaceholder');
const hostTextInput = screen.getByTestId('textInput');
expect(getHostSelves(compositeTextInputByValue)).toEqual([hostTextInput]);
expect(getHostSelves(compositeTextInputByPlaceholder)).toEqual([hostTextInput]);
});
});
describe('getHostSiblings()', () => {
it('returns host siblings for host component', () => {
render(
+5 -39
View File
@@ -8,51 +8,17 @@ export function isHostElement(element?: HostElement | null): element is HostElem
return typeof element?.type === 'string' && element.type !== 'CONTAINER';
}
/**
* Returns host children for given element.
* @param element The element start traversing from.
*/
export function getHostChildren(element: HostElement | null): HostElement[] {
if (element == null) {
return [];
}
const hostChildren: HostElement[] = [];
element.children.forEach((child) => {
if (typeof child !== 'object') {
return;
}
if (isHostElement(child)) {
hostChildren.push(child);
} else {
hostChildren.push(...getHostChildren(child));
}
});
return hostChildren;
}
/**
* Return the array of host elements that represent the passed element.
*
* @param element The element start traversing from.
* @returns If the passed element is a host element, it will return an array containing only that element,
* if the passed element is a composite element, it will return an array containing its host children (zero, one or many).
*/
export function getHostSelves(element: HostElement | null): HostElement[] {
return isHostElement(element) ? [element] : getHostChildren(element);
}
/**
* Returns host siblings for given element.
* @param element The element start traversing from.
*/
export function getHostSiblings(element: HostElement | null): HostElement[] {
const hostParent = element?.parent ?? null;
const hostSelves = getHostSelves(element);
return getHostChildren(hostParent).filter((sibling) => !hostSelves.includes(sibling));
return (
hostParent?.children.filter(
(sibling): sibling is HostElement => typeof sibling === 'object' && sibling !== element,
) ?? []
);
}
/**
+1 -2
View File
@@ -1,12 +1,11 @@
import { matcherHint, RECEIVED_COLOR } from 'jest-matcher-utils';
import { getHostChildren } from '../helpers/component-tree';
import { HostElement } from '../renderer/host-element';
import { checkHostElement, formatElementArray } from './utils';
export function toBeEmptyElement(this: jest.MatcherContext, element: HostElement) {
checkHostElement(element, toBeEmptyElement, this);
const hostChildren = getHostChildren(element);
const hostChildren = element.children;
return {
pass: hostChildren.length === 0,
+7 -3
View File
@@ -10,7 +10,7 @@ import prettyFormat, { plugins } from 'pretty-format';
import redent from 'redent';
import { isHostElement } from '../helpers/component-tree';
import { defaultMapProps } from '../helpers/format-default';
import { HostElement } from '../renderer/host-element';
import { HostElement, HostNode } from '../renderer/host-element';
class HostElementTypeError extends Error {
constructor(received: unknown, matcherFn: jest.CustomMatcher, context: jest.MatcherContext) {
@@ -60,11 +60,15 @@ export function checkHostElement(
*
* @param element Element to format.
*/
export function formatElement(element: HostElement | null) {
export function formatElement(element: HostNode | null) {
if (element == null) {
return ' null';
}
if (typeof element === 'string') {
return element;
}
const { children, ...props } = element.props;
const childrenToDisplay = typeof children === 'string' ? [children] : undefined;
@@ -89,7 +93,7 @@ export function formatElement(element: HostElement | null) {
);
}
export function formatElementArray(elements: HostElement[]) {
export function formatElementArray(elements: HostNode[]) {
if (elements.length === 0) {
return ' (no elements)';
}