import React, { ReactNode } from 'react';
import { inspectElements, PropsExtractor } from '../index';
type WrappedButtonProps = {
title: string;
onPress?: () => void;
};
function WrappedButton({ title, onPress }: WrappedButtonProps) {
return ;
}
function DoubleWrappedButton(props: WrappedButtonProps) {
return ;
}
function View({ children }: any) {
return
{children}
;
}
const defaultExtractor: PropsExtractor = ({
props,
type,
}) => {
if (type === WrappedButton) {
return props;
}
return true;
};
const extractProps = (node: ReactNode | ReactNode[]) => {
return inspectElements(node, defaultExtractor);
};
const propsOne = { title: 'one', onPress: jest.fn() } as const;
const propsTwo = { title: 'two', onPress: jest.fn() } as const;
const propsThree = { title: 'three', onPress: jest.fn() } as const;
describe('extractProps', () => {
it('extracts titles, onPresses when given an array of WrappedButton', () => {
const propsArray = [
{ title: 'one', onPress: jest.fn() },
{ title: 'two', onPress: jest.fn(), disabled: true },
{ title: 'three', onPress: jest.fn(), destructive: true },
] as const;
const titlesAndOnPresses = extractProps([
...propsArray.map((props) => ),
]);
expect(titlesAndOnPresses).toStrictEqual([
{
title: propsArray[0].title,
onPress: propsArray[0].onPress,
},
{
title: propsArray[1].title,
onPress: propsArray[1].onPress,
disabled: true,
},
{
title: propsArray[2].title,
onPress: propsArray[2].onPress,
destructive: true,
},
]);
});
it('works for single child element', () => {
const props = { title: 'one', onPress: jest.fn() };
const propsTwo = { title: 'two', onPress: jest.fn() };
expect(extractProps()).toStrictEqual([props]);
expect(extractProps()).toStrictEqual([
propsTwo,
]);
});
it('works when child elements are wrapped in a Fragment', () => {
const props = { title: 'one', onPress: jest.fn() };
const propsTwo = { title: 'two', onPress: jest.fn() };
expect(
inspectElements(
<>
<>
>
>,
{ maxDepth: 4, propsExtractor: defaultExtractor }
)
).toStrictEqual([props, propsTwo]);
});
it('limits the depth to the value of maxDepth param', () => {
const props = { title: 'one', onPress: jest.fn() };
const TripleWrappedButton = (props: WrappedButtonProps) => (
);
const FourWrappedButton = (props: WrappedButtonProps) => (
);
expect(
inspectElements(
<>
{/* last is ignored */}
>,
{
maxDepth: 4,
propsExtractor: defaultExtractor,
}
)
).toStrictEqual([
props,
{ ...props, title: '2' },
{ ...props, title: '3' },
]);
});
it('works for react host components', () => {
expect(
inspectElements(, ({ props, type }) => {
if (type === 'button') {
return props;
}
return false;
})
).toStrictEqual([propsOne]);
});
it('ignores when hook / class component is used in the provided components (they might be present for styling purposes and actually rendered elsewhere)', () => {
function MyComponent({
title,
onPress,
}: {
title: string;
onPress: () => void;
}) {
const [titleFromState] = React.useState('from state hook');
return ;
}
const propsArray = [
{ title: 'one', onPress: jest.fn() },
{ title: 'two', onPress: jest.fn() },
];
const items = propsArray.map((props) => );
// does not throw even if we use hooks and violate the rules
// https://reactjs.org/docs/hooks-rules.html
expect(extractProps(items)).toStrictEqual([]);
class Component extends React.Component {
render() {
return ;
}
}
// does not throw when class component is used
expect(extractProps()).toStrictEqual([]);
});
describe('corner cases', () => {
const NullNestedComponent = () => null;
it.each([null, undefined, ])(
'inspectElements works for case %#',
(input) => {
expect(extractProps(input)).toStrictEqual([]);
expect(extractProps([input])).toStrictEqual([]);
}
);
});
it('works with multi-level nesting', () => {
expect(
inspectElements(
<>
<>
>
{/* span is ignored*/}
>,
{
maxDepth: 4,
propsExtractor: ({ props, type }) => {
if (type === WrappedButton) {
return props;
} else if (type === 'span') {
return false;
}
return true;
},
}
)
).toStrictEqual([propsOne, propsTwo, propsThree]);
});
it('ignores non-detected values', () => {
const titlesAndOnPresses = extractProps([
<>>,
<>child>,
,
null,
false,
'hello',
123,
]);
expect(titlesAndOnPresses).toStrictEqual([]);
});
});