react-navigation
Version:
Routing and navigation for your React Native apps
259 lines (221 loc) • 6.84 kB
JavaScript
import React from 'react';
import { StyleSheet, View } from 'react-native';
import renderer from 'react-test-renderer';
import NavigationActions from '../NavigationActions';
import createStackNavigator from '../navigators/createStackNavigator';
import { _TESTING_ONLY_reset_container_count } from '../createNavigationContainer';
describe('NavigationContainer', () => {
jest.useFakeTimers();
beforeEach(() => {
_TESTING_ONLY_reset_container_count();
});
const FooScreen = () => <div />;
const BarScreen = () => <div />;
const BazScreen = () => <div />;
const CarScreen = () => <div />;
const DogScreen = () => <div />;
const ElkScreen = () => <div />;
const NavigationContainer = createStackNavigator(
{
foo: {
screen: FooScreen,
},
bar: {
screen: BarScreen,
},
baz: {
screen: BazScreen,
},
car: {
screen: CarScreen,
},
dog: {
screen: DogScreen,
},
elk: {
screen: ElkScreen,
},
},
{
initialRouteName: 'foo',
}
);
describe('state.nav', () => {
it("should be preloaded with the router's initial state", () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
expect(navigationContainer.state.nav).toMatchObject({ index: 0 });
expect(navigationContainer.state.nav.routes).toBeInstanceOf(Array);
expect(navigationContainer.state.nav.routes.length).toBe(1);
expect(navigationContainer.state.nav.routes[0]).toMatchObject({
routeName: 'foo',
});
});
});
describe('dispatch', () => {
it('returns true when given a valid action', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
jest.runOnlyPendingTimers();
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'bar' })
)
).toEqual(true);
});
it('returns false when given an invalid action', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
jest.runOnlyPendingTimers();
expect(navigationContainer.dispatch(NavigationActions.back())).toEqual(
false
);
});
it('updates state.nav with an action by the next tick', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'bar' })
)
).toEqual(true);
// Fake the passing of a tick
jest.runOnlyPendingTimers();
expect(navigationContainer.state.nav).toMatchObject({
index: 1,
routes: [{ routeName: 'foo' }, { routeName: 'bar' }],
});
});
it('does not discard actions when called twice in one tick', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
const initialState = JSON.parse(
JSON.stringify(navigationContainer.state.nav)
);
// First dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'bar' })
)
).toEqual(true);
// Make sure that the test runner has NOT synchronously applied setState before the tick
expect(navigationContainer.state.nav).toMatchObject(initialState);
// Second dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'baz' })
)
).toEqual(true);
// Fake the passing of a tick
jest.runOnlyPendingTimers();
expect(navigationContainer.state.nav).toMatchObject({
index: 2,
routes: [
{ routeName: 'foo' },
{ routeName: 'bar' },
{ routeName: 'baz' },
],
});
});
it('does not discard actions when called more than 2 times in one tick', () => {
const navigationContainer = renderer
.create(<NavigationContainer />)
.getInstance();
const initialState = JSON.parse(
JSON.stringify(navigationContainer.state.nav)
);
// First dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'bar' })
)
).toEqual(true);
// Make sure that the test runner has NOT synchronously applied setState before the tick
expect(navigationContainer.state.nav).toMatchObject(initialState);
// Second dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'baz' })
)
).toEqual(true);
// Third dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'car' })
)
).toEqual(true);
// Fourth dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'dog' })
)
).toEqual(true);
// Fifth dispatch
expect(
navigationContainer.dispatch(
NavigationActions.navigate({ routeName: 'elk' })
)
).toEqual(true);
// Fake the passing of a tick
jest.runOnlyPendingTimers();
expect(navigationContainer.state.nav).toMatchObject({
index: 5,
routes: [
{ routeName: 'foo' },
{ routeName: 'bar' },
{ routeName: 'baz' },
{ routeName: 'car' },
{ routeName: 'dog' },
{ routeName: 'elk' },
],
});
});
});
describe('warnings', () => {
function spyConsole() {
let spy = {};
beforeEach(() => {
spy.console = jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
spy.console.mockRestore();
});
return spy;
}
describe('detached navigators', () => {
beforeEach(() => {
_TESTING_ONLY_reset_container_count();
});
let spy = spyConsole();
it('warns when you render more than one navigator explicitly', () => {
class BlankScreen extends React.Component {
render() {
return <View />;
}
}
class RootScreen extends React.Component {
render() {
return (
<View>
<ChildNavigator />
</View>
);
}
}
const ChildNavigator = createStackNavigator({
Child: BlankScreen,
});
const RootStack = createStackNavigator({
Root: RootScreen,
});
renderer.create(<RootStack />).toJSON();
expect(spy).toMatchSnapshot();
});
});
});
});