refactor: migrate tests to typescript

This commit is contained in:
satyajit.happy
2019-05-05 13:57:08 +02:00
parent 3b907d39ee
commit 413cd75055
14 changed files with 81 additions and 28 deletions

View File

@@ -52,6 +52,7 @@
"@types/jest": "^24.0.12",
"@types/react": "^16.8.14",
"@types/react-native": "^0.57.51",
"@types/react-test-renderer": "^16.8.1",
"commitlint": "^7.5.2",
"eslint": "^5.16.0",
"eslint-config-satya164": "^2.4.1",

View File

@@ -7,7 +7,10 @@ import createStackNavigator from '../createStackNavigator';
import createAppContainer, {
_TESTING_ONLY_reset_container_count,
// @ts-ignore
} from '@react-navigation/native/src/createAppContainer';
import { NavigationProp } from '../../types';
const NavigationTestUtils = {
resetInternalState: _TESTING_ONLY_reset_container_count,
};
@@ -19,7 +22,11 @@ const styles = StyleSheet.create({
});
class HomeScreen extends Component {
static navigationOptions = ({ navigation }) => ({
static navigationOptions = ({
navigation,
}: {
navigation: NavigationProp;
}) => ({
title: `Welcome ${
navigation.state.params ? navigation.state.params.user : 'anonymous'
}`,
@@ -69,7 +76,10 @@ describe('StackNavigator', () => {
it('passes navigation to headerRight when wrapped in withNavigation', () => {
const spy = jest.fn();
class TestComponent extends React.Component {
class TestComponent extends React.Component<{
onPress: (navigation: NavigationProp) => undefined;
navigation: NavigationProp;
}> {
render() {
return <View>{this.props.onPress(this.props.navigation)}</View>;
}

View File

@@ -26,6 +26,7 @@ export type NavigationState = {
index: number;
routes: Route[];
isTransitioning?: boolean;
params?: { [key: string]: unknown };
};
export type NavigationProp<RouteName = string, Params = object> = {
@@ -82,7 +83,7 @@ export type HeaderTransitionConfig = {
export type NavigationStackOptions = {
title?: string;
header?: (props: HeaderProps) => React.ReactNode | null;
header?: (props: HeaderProps) => React.ReactNode;
headerTitle?: string;
headerTitleStyle?: StyleProp<TextStyle>;
headerTitleContainerStyle?: StyleProp<ViewStyle>;
@@ -92,11 +93,11 @@ export type NavigationStackOptions = {
headerBackTitle?: string;
headerBackTitleStyle?: StyleProp<TextStyle>;
headerTruncatedBackTitle?: string;
headerLeft?: React.ComponentType<HeaderBackbuttonProps>;
headerLeft?: React.FunctionComponent<HeaderBackbuttonProps>;
headerLeftContainerStyle?: StyleProp<ViewStyle>;
headerRight?: React.ComponentType<{}>;
headerRight?: (() => React.ReactNode) | React.ReactNode;
headerRightContainerStyle?: StyleProp<ViewStyle>;
headerBackImage?: React.ComponentType<{
headerBackImage?: React.FunctionComponent<{
tintColor: string;
title?: string | null;
}>;

View File

@@ -13,7 +13,6 @@ import {
} from '../../types';
type Props = {
screenProps: unknown;
navigation: NavigationProp;
descriptors: { [key: string]: SceneDescriptor };
navigationConfig: NavigationConfig;
@@ -21,6 +20,7 @@ type Props = {
onGestureBegin?: () => void;
onGestureCanceled?: () => void;
onGestureEnd?: () => void;
screenProps?: unknown;
};
const USE_NATIVE_DRIVER = true;

View File

@@ -65,7 +65,7 @@ type Props = {
onGestureBegin?: () => void;
onGestureEnd?: () => void;
onGestureCanceled?: () => void;
screenProps: unknown;
screenProps?: unknown;
};
type State = {

View File

@@ -37,7 +37,7 @@ type Props = {
) => void | Promise<any>;
navigation: NavigationProp;
descriptors: { [key: string]: SceneDescriptor };
screenProps: unknown;
screenProps?: unknown;
};
type State = {

View File

@@ -1,25 +1,31 @@
import ScenesReducer from '../ScenesReducer';
const MOCK_DESCRIPTOR = {};
import { Scene, NavigationState, SceneDescriptor } from '../../types';
const MOCK_DESCRIPTOR: SceneDescriptor = {} as any;
/**
* Simulate scenes transtion with changes of navigation states.
*/
function testTransition(states) {
function testTransition(states: string[][]) {
let descriptors = states
.reduce((acc, state) => acc.concat(state), [])
.reduce((acc, key) => {
acc[key] = MOCK_DESCRIPTOR;
return acc;
}, {});
const routes = states.map(keys => ({
.reduce((acc, state) => acc.concat(state), [] as string[])
.reduce(
(acc, key) => {
acc[key] = MOCK_DESCRIPTOR;
return acc;
},
{} as { [key: string]: SceneDescriptor }
);
const routes = states.map((keys, i) => ({
key: String(i),
index: keys.length - 1,
routes: keys.map(key => ({ key, routeName: '' })),
isTransitioning: false,
}));
let scenes = [];
let prevState = null;
routes.forEach(nextState => {
let scenes: Scene[] = [];
let prevState: NavigationState | null = null;
routes.forEach((nextState: NavigationState) => {
scenes = ScenesReducer(scenes, nextState, prevState, descriptors);
prevState = nextState;
});
@@ -100,55 +106,61 @@ describe('ScenesReducer', () => {
it('gets active scene when index changes', () => {
const state1 = {
key: '0',
index: 0,
routes: [{ key: '1', routeName: '' }],
isTransitioning: false,
};
const state2 = {
key: '0',
index: 1,
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
isTransitioning: false,
};
const scenes1 = ScenesReducer([], state1, null);
const scenes2 = ScenesReducer(scenes1, state2, state1);
const route = scenes2.find(scene => scene.isActive).route;
const scenes1 = ScenesReducer([], state1, null, {});
const scenes2 = ScenesReducer(scenes1, state2, state1, {});
const route = scenes2.find(scene => scene.isActive)!.route;
expect(route).toEqual({ key: '2', routeName: '' });
});
it('gets same scenes', () => {
const state1 = {
key: '0',
index: 1,
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
isTransitioning: false,
};
const state2 = {
key: '0',
index: 1,
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
isTransitioning: false,
};
const scenes1 = ScenesReducer([], state1, null);
const scenes2 = ScenesReducer(scenes1, state2, state1);
const scenes1 = ScenesReducer([], state1, null, {});
const scenes2 = ScenesReducer(scenes1, state2, state1, {});
expect(scenes1).toBe(scenes2);
});
it('gets different scenes when keys are different', () => {
const state1 = {
key: '0',
index: 1,
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
isTransitioning: false,
};
const state2 = {
key: '0',
index: 1,
routes: [{ key: '2', routeName: '' }, { key: '1', routeName: '' }],
isTransitioning: false,
};
const descriptors = { 1: jest.mock(), 2: jest.mock() };
const descriptors = { 1: {}, 2: {} } as any;
const scenes1 = ScenesReducer([], state1, null, descriptors);
const scenes2 = ScenesReducer(scenes1, state2, state1, descriptors);
@@ -157,6 +169,7 @@ describe('ScenesReducer', () => {
it('gets different scenes when routes are different', () => {
const state1 = {
key: '0',
index: 1,
routes: [
{ key: '1', x: 1, routeName: '' },
@@ -166,6 +179,7 @@ describe('ScenesReducer', () => {
};
const state2 = {
key: '0',
index: 1,
routes: [
{ key: '1', x: 3, routeName: '' },
@@ -186,6 +200,7 @@ describe('ScenesReducer', () => {
// anything except the last route in the array of routes.
it('gets different scenes when state index changes', () => {
const state1 = {
key: '0',
index: 1,
routes: [
{ key: '1', x: 1, routeName: '' },
@@ -195,6 +210,7 @@ describe('ScenesReducer', () => {
};
const state2 = {
key: '0',
index: 0,
routes: [
{ key: '1', x: 1, routeName: '' },

View File

@@ -13,6 +13,7 @@ describe('Transitioner', () => {
configureTransition: () => ({}),
navigation: {
state: {
key: '0',
index: 0,
routes: [
{ key: '1', routeName: 'Foo' },
@@ -23,6 +24,10 @@ describe('Transitioner', () => {
dispatch: () => false,
setParams: () => false,
navigate: () => false,
isFocused: () => false,
dangerouslyGetParent: () => undefined,
getParam: jest.fn(),
addListener: jest.fn(),
},
render: () => <div />,
onTransitionStart: onTransitionStartCallback,
@@ -34,6 +39,7 @@ describe('Transitioner', () => {
navigation: {
...transitionerProps.navigation,
state: {
key: '0',
index: 0,
routes: [
{ key: '1', routeName: 'Foo', params: { name: 'Zoom' } },
@@ -42,8 +48,12 @@ describe('Transitioner', () => {
},
},
};
const component = renderer.create(<Transitioner {...transitionerProps} />);
component.update(<Transitioner {...nextTransitionerProps} />);
const component = renderer.create(
<Transitioner descriptors={{}} {...transitionerProps} />
);
component.update(
<Transitioner descriptors={{}} {...nextTransitionerProps} />
);
expect(onTransitionStartCallback).not.toBeCalled();
expect(onTransitionEndCallback).not.toBeCalled();
});

View File

@@ -27,5 +27,9 @@ declare module '@react-navigation/core' {
stackConfig: object
): React.ComponentType;
export function withNavigation<Props extends { navigation: object }>(
Comp: React.ComponentType<Props>
): React.ComponentType<Pick<Props, Exclude<keyof Props, 'navigation'>>>;
export function StackRouter(routeConfigMap: object, stackConfig: object);
}

View File

@@ -2,6 +2,10 @@ declare module '@react-navigation/native' {
import { ComponentType } from 'react';
import { StyleProp, ViewStyle, ViewProps } from 'react-native';
export function createAppContainer<Props>(
Comp: React.ComponentType<Props>
): React.ComponentType<Props>;
export function withOrientation<Props extends { isLandscape: boolean }>(
Comp: React.ComponentType<Props>
): React.ComponentType<Pick<Props, Exclude<keyof Props, 'isLandscape'>>>;

View File

@@ -1149,6 +1149,13 @@
"@types/prop-types" "*"
"@types/react" "*"
"@types/react-test-renderer@^16.8.1":
version "16.8.1"
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.8.1.tgz#96f3ce45a3a41c94eca532a99103dd3042c9d055"
integrity sha512-8gU69ELfJGxzVWVYj4MTtuHxz9nO+d175XeQ1XrXXxesUBsB4KK6OCfzVhEX6leZWWBDVtMJXp/rUjhClzL7gw==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^16.8.14":
version "16.8.15"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.15.tgz#a76515fed5aa3e996603056f54427fec5f2a5122"