test: add test to check if action is handled correctly

This commit is contained in:
satyajit.happy
2019-07-13 08:31:21 +02:00
parent 06a3fbc13d
commit 59aacc3080
3 changed files with 253 additions and 32 deletions

View File

@@ -1,5 +1,6 @@
import { ScreenProps } from './types';
export default function Screen(_: ScreenProps) {
/* istanbul ignore next */
return null;
}

View File

@@ -1,11 +1,11 @@
import * as React from 'react';
import { render } from 'react-native-testing-library';
import Screen from '../Screen';
import NavigationContainer from '../NavigationContainer';
import useNavigationBuilder from '../useNavigationBuilder';
import { Router } from '../types';
import Screen from '../Screen';
const MockRouter: Router<{ type: 'UPDATE' | 'NOOP' }> = {
const MockRouter: Router<{ type: string }> = {
getInitialState({ screens, initialRouteName }) {
const routeNames = Object.keys(screens);
@@ -26,10 +26,10 @@ const MockRouter: Router<{ type: 'UPDATE' | 'NOOP' }> = {
return { ...state };
case 'NOOP':
return null;
return state;
default:
return state;
return null;
}
},
@@ -37,7 +37,7 @@ const MockRouter: Router<{ type: 'UPDATE' | 'NOOP' }> = {
};
it('initializes state for a navigator on navigation', () => {
expect.assertions(1);
expect.assertions(2);
const TestNavigator = (props: any) => {
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
@@ -56,34 +56,254 @@ it('initializes state for a navigator on navigation', () => {
return null;
};
const onStateChange = jest.fn();
const element = (
<NavigationContainer
onStateChange={state =>
expect(state).toEqual({
index: 0,
key: 'root',
names: ['foo', 'bar', 'baz'],
routes: [
{ key: 'foo', name: 'foo' },
{ key: 'bar', name: 'bar' },
{ key: 'baz', name: 'baz' },
],
})
}
>
<NavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={jest.fn()} />
<Screen name="baz" component={jest.fn()} />
<Screen name="baz">
{() => (
<TestNavigator>
<Screen name="qux" component={jest.fn()} />
</TestNavigator>
)}
</Screen>
</TestNavigator>
</NavigationContainer>
);
render(element).update(element);
expect(onStateChange).toBeCalledTimes(1);
expect(onStateChange).toBeCalledWith({
index: 0,
key: 'root',
names: ['foo', 'bar', 'baz'],
routes: [
{ key: 'foo', name: 'foo' },
{ key: 'bar', name: 'bar' },
{ key: 'baz', name: 'baz' },
],
});
});
it('initializes state for nested navigator on navigation', () => {
expect.assertions(2);
const TestNavigator = (props: any) => {
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[
navigation.state.routes[navigation.state.index].key
].render();
};
const TestScreen = (props: any) => {
React.useEffect(() => {
props.navigation.dispatch({ type: 'UPDATE' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
};
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="baz">
<Screen name="foo" component={jest.fn()} />
<Screen name="bar" component={jest.fn()} />
<Screen name="baz">
{() => (
<TestNavigator>
<Screen name="qux" component={TestScreen} />
</TestNavigator>
)}
</Screen>
</TestNavigator>
</NavigationContainer>
);
render(element).update(element);
expect(onStateChange).toBeCalledTimes(1);
expect(onStateChange).toBeCalledWith({
index: 2,
key: 'root',
names: ['foo', 'bar', 'baz'],
routes: [
{ key: 'foo', name: 'foo' },
{ key: 'bar', name: 'bar' },
{
key: 'baz',
name: 'baz',
state: {
index: 0,
key: 'root',
names: ['qux'],
routes: [{ key: 'qux', name: 'qux' }],
},
},
],
});
});
it("doesn't update state if nothing changed", () => {
expect.assertions(1);
const TestNavigator = (props: any) => {
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[
navigation.state.routes[navigation.state.index].key
].render();
};
const FooScreen = (props: any) => {
React.useEffect(() => {
props.navigation.dispatch({ type: 'NOOP' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
};
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
render(element).update(element);
expect(onStateChange).toBeCalledTimes(0);
});
it("doesn't update state if action wasn't handled", () => {
expect.assertions(1);
const TestNavigator = (props: any) => {
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[
navigation.state.routes[navigation.state.index].key
].render();
};
const FooScreen = (props: any) => {
React.useEffect(() => {
props.navigation.dispatch({ type: 'INVALID' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
};
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={jest.fn()} />
</TestNavigator>
</NavigationContainer>
);
render(element).update(element);
expect(onStateChange).toBeCalledTimes(0);
});
it("lets parent handle the action if child didn't", () => {
expect.assertions(2);
const ParentRouter: Router<{ type: string }> = {
...MockRouter,
getStateForAction(state, action) {
if (action.type === 'REVERSE') {
return {
...state,
routes: state.routes.slice().reverse(),
};
}
return MockRouter.getStateForAction(state, action);
},
};
const ParentNavigator = (props: any) => {
const { navigation, descriptors } = useNavigationBuilder(
ParentRouter,
props
);
return descriptors[
navigation.state.routes[navigation.state.index].key
].render();
};
const ChildNavigator = (props: any) => {
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[
navigation.state.routes[navigation.state.index].key
].render();
};
const TestScreen = (props: any) => {
React.useEffect(() => {
props.navigation.dispatch({ type: 'REVERSE' });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
};
const onStateChange = jest.fn();
const element = (
<NavigationContainer onStateChange={onStateChange}>
<ParentNavigator initialRouteName="baz">
<Screen name="foo">{() => null}</Screen>
<Screen name="bar">{() => null}</Screen>
<Screen name="baz">
{() => (
<ChildNavigator>
<Screen name="qux" component={TestScreen} />
</ChildNavigator>
)}
</Screen>
</ParentNavigator>
</NavigationContainer>
);
render(element).update(element);
expect(onStateChange).toBeCalledTimes(1);
expect(onStateChange).toBeCalledWith({
index: 2,
key: 'root',
names: ['foo', 'bar', 'baz'],
routes: [
{ key: 'baz', name: 'baz' },
{ key: 'bar', name: 'bar' },
{ key: 'foo', name: 'foo' },
],
});
});
it('allows arbitrary state updates by dispatching a function', () => {
expect.assertions(1);
expect.assertions(2);
const TestNavigator = (props: any) => {
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
@@ -108,17 +328,10 @@ it('allows arbitrary state updates by dispatching a function', () => {
const BarScreen = () => null;
const onStateChange = jest.fn();
const element = (
<NavigationContainer
onStateChange={state =>
expect(state).toEqual({
index: 1,
key: 'root',
names: ['foo', 'bar'],
routes: [{ key: 'bar', name: 'bar' }, { key: 'foo', name: 'foo' }],
})
}
>
<NavigationContainer onStateChange={onStateChange}>
<TestNavigator initialRouteName="foo">
<Screen name="foo" component={FooScreen} />
<Screen name="bar" component={BarScreen} />
@@ -127,6 +340,14 @@ it('allows arbitrary state updates by dispatching a function', () => {
);
render(element).update(element);
expect(onStateChange).toBeCalledTimes(1);
expect(onStateChange).toBeCalledWith({
index: 1,
key: 'root',
names: ['foo', 'bar'],
routes: [{ key: 'bar', name: 'bar' }, { key: 'foo', name: 'foo' }],
});
});
it('throws if navigator is not inside a container', () => {

View File

@@ -23,7 +23,6 @@ export default function useOnAction({
return React.useCallback(
(action: NavigationAction) => {
const state = getState();
const result = getStateForAction(state, action);
if (result !== null) {