diff --git a/packages/core/src/routers/__tests__/SwitchRouter-test.js b/packages/core/src/routers/__tests__/SwitchRouter-test.js index c72aca22..6b616d5d 100644 --- a/packages/core/src/routers/__tests__/SwitchRouter-test.js +++ b/packages/core/src/routers/__tests__/SwitchRouter-test.js @@ -3,186 +3,148 @@ import React from 'react'; import SwitchRouter from '../SwitchRouter'; import StackRouter from '../StackRouter'; -import * as SwitchActions from '../SwitchActions'; import * as NavigationActions from '../../NavigationActions'; +import { getRouterTestHelper } from './routerTestHelper'; describe('SwitchRouter', () => { it('resets the route when unfocusing a tab by default', () => { - const router = getExampleRouter(); - const state = router.getStateForAction({ type: NavigationActions.INIT }); - const state2 = router.getStateForAction( - { type: NavigationActions.NAVIGATE, routeName: 'A2' }, - state - ); - expect(state2.routes[0].index).toEqual(1); - expect(state2.routes[0].routes.length).toEqual(2); + const { navigateTo, getState } = getRouterTestHelper(getExampleRouter()); - const state3 = router.getStateForAction( - { type: NavigationActions.NAVIGATE, routeName: 'B' }, - state2 - ); + navigateTo('A2'); + expect(getState().routes[0].index).toEqual(1); + expect(getState().routes[0].routes.length).toEqual(2); - expect(state3.routes[0].index).toEqual(0); - expect(state3.routes[0].routes.length).toEqual(1); + navigateTo('B'); + expect(getState().routes[0].index).toEqual(0); + expect(getState().routes[0].routes.length).toEqual(1); }); it('does not reset the route on unfocus if resetOnBlur is false', () => { - const router = getExampleRouter({ resetOnBlur: false }); - const state = router.getStateForAction({ type: NavigationActions.INIT }); - const state2 = router.getStateForAction( - { type: NavigationActions.NAVIGATE, routeName: 'A2' }, - state - ); - expect(state2.routes[0].index).toEqual(1); - expect(state2.routes[0].routes.length).toEqual(2); - - const state3 = router.getStateForAction( - { type: NavigationActions.NAVIGATE, routeName: 'B' }, - state2 + const { navigateTo, getState } = getRouterTestHelper( + getExampleRouter({ resetOnBlur: false }) ); - expect(state3.routes[0].index).toEqual(1); - expect(state3.routes[0].routes.length).toEqual(2); + navigateTo('A2'); + expect(getState().routes[0].index).toEqual(1); + expect(getState().routes[0].routes.length).toEqual(2); + + navigateTo('B'); + expect(getState().routes[0].index).toEqual(1); + expect(getState().routes[0].routes.length).toEqual(2); }); it('ignores back by default', () => { - const router = getExampleRouter(); - const state = router.getStateForAction({ type: NavigationActions.INIT }); - const state2 = router.getStateForAction( - { type: SwitchActions.JUMP_TO, routeName: 'B' }, - state - ); - expect(state2.index).toEqual(1); + const { jumpTo, back, getState } = getRouterTestHelper(getExampleRouter()); - const state3 = router.getStateForAction( - { type: NavigationActions.BACK }, - state2 - ); + jumpTo('B'); + expect(getState().index).toEqual(1); - expect(state3.index).toEqual(1); + back(); + expect(getState().index).toEqual(1); }); it('handles initialRoute backBehavior', () => { - const router = getExampleRouter({ backBehavior: 'initialRoute' }); - - const state = router.getStateForAction({ type: NavigationActions.INIT }); - expect(state.routeKeyHistory).toBeUndefined(); - - const state2 = router.getStateForAction( - { type: SwitchActions.JUMP_TO, routeName: 'B' }, - state + const { jumpTo, back, getState } = getRouterTestHelper( + getExampleRouter({ backBehavior: 'initialRoute', initialRouteName: 'B' }) ); - expect(state2.index).toEqual(1); + expect(getState().routeKeyHistory).toBeUndefined(); + expect(getState().index).toEqual(1); - const state3 = router.getStateForAction( - { type: NavigationActions.BACK }, - state2 - ); + jumpTo('C'); + expect(getState().index).toEqual(2); - expect(state3.index).toEqual(0); + jumpTo('A'); + expect(getState().index).toEqual(0); + + back(); + expect(getState().index).toEqual(1); + + back(); + expect(getState().index).toEqual(1); }); it('handles order backBehavior', () => { - const routerHelper = new ExampleRouterHelper({ backBehavior: 'order' }); - expect(routerHelper.getCurrentState().routeKeyHistory).toBeUndefined(); + const { navigateTo, back, getState } = getRouterTestHelper( + getExampleRouter({ backBehavior: 'order' }) + ); + expect(getState().routeKeyHistory).toBeUndefined(); - expect( - routerHelper.applyAction({ - type: SwitchActions.JUMP_TO, - routeName: 'C', - }) - ).toMatchObject({ index: 2 }); + navigateTo('C'); + expect(getState().index).toEqual(2); - expect( - routerHelper.applyAction({ type: NavigationActions.BACK }) - ).toMatchObject({ index: 1 }); + back(); + expect(getState().index).toEqual(1); - expect( - routerHelper.applyAction({ type: NavigationActions.BACK }) - ).toMatchObject({ index: 0 }); + back(); + expect(getState().index).toEqual(0); - expect( - routerHelper.applyAction({ type: NavigationActions.BACK }) - ).toMatchObject({ index: 0 }); + back(); + expect(getState().index).toEqual(0); }); it('handles history backBehavior', () => { - const routerHelper = new ExampleRouterHelper({ backBehavior: 'history' }); - expect(routerHelper.getCurrentState().routeKeyHistory).toMatchObject(['A']); + const { navigateTo, back, getState } = getRouterTestHelper( + getExampleRouter({ backBehavior: 'history' }) + ); + expect(getState().routeKeyHistory).toEqual(['A']); - expect( - routerHelper.applyAction({ - type: NavigationActions.NAVIGATE, - routeName: 'B', - }) - ).toMatchObject({ index: 1, routeKeyHistory: ['A', 'B'] }); + navigateTo('B'); + expect(getState().index).toEqual(1); + expect(getState().routeKeyHistory).toEqual(['A', 'B']); - expect( - routerHelper.applyAction({ - type: NavigationActions.NAVIGATE, - routeName: 'A', - }) - ).toMatchObject({ index: 0, routeKeyHistory: ['B', 'A'] }); + navigateTo('A'); + expect(getState().index).toEqual(0); + expect(getState().routeKeyHistory).toEqual(['B', 'A']); - expect( - routerHelper.applyAction({ - type: NavigationActions.NAVIGATE, - routeName: 'C', - }) - ).toMatchObject({ index: 2, routeKeyHistory: ['B', 'A', 'C'] }); + navigateTo('C'); + expect(getState().index).toEqual(2); + expect(getState().routeKeyHistory).toEqual(['B', 'A', 'C']); - expect( - routerHelper.applyAction({ - type: NavigationActions.NAVIGATE, - routeName: 'A', - }) - ).toMatchObject({ index: 0, routeKeyHistory: ['B', 'C', 'A'] }); + navigateTo('A'); + expect(getState().index).toEqual(0); + expect(getState().routeKeyHistory).toEqual(['B', 'C', 'A']); - expect( - routerHelper.applyAction({ type: NavigationActions.BACK }) - ).toMatchObject({ index: 2, routeKeyHistory: ['B', 'C'] }); + back(); + expect(getState().index).toEqual(2); + expect(getState().routeKeyHistory).toEqual(['B', 'C']); - expect( - routerHelper.applyAction({ type: NavigationActions.BACK }) - ).toMatchObject({ index: 1, routeKeyHistory: ['B'] }); + back(); + expect(getState().index).toEqual(1); + expect(getState().routeKeyHistory).toEqual(['B']); - expect( - routerHelper.applyAction({ type: NavigationActions.BACK }) - ).toMatchObject({ index: 1, routeKeyHistory: ['B'] }); + back(); + expect(getState().index).toEqual(1); + expect(getState().routeKeyHistory).toEqual(['B']); }); it('handles nested actions', () => { - const router = getExampleRouter(); - const state = router.getStateForAction({ type: NavigationActions.INIT }); - const state2 = router.getStateForAction( - { - type: NavigationActions.NAVIGATE, - routeName: 'B', - action: { type: NavigationActions.NAVIGATE, routeName: 'B2' }, - }, - state - ); - const subState = state2.routes[state2.index]; - const activeGrandChildRoute = subState.routes[subState.index]; - expect(activeGrandChildRoute.routeName).toEqual('B2'); + const { navigateTo, getSubState } = getRouterTestHelper(getExampleRouter()); + + navigateTo('B', { + action: { type: NavigationActions.NAVIGATE, routeName: 'B2' }, + }); + expect(getSubState(1).routeName).toEqual('B'); + expect(getSubState(2).routeName).toEqual('B2'); }); it('handles nested actions and params simultaneously', () => { - const router = getExampleRouter(); - const state = router.getStateForAction({ type: NavigationActions.INIT }); - const state2 = router.getStateForAction( - { + const { navigateTo, getSubState } = getRouterTestHelper(getExampleRouter()); + + const params1 = { foo: 'bar' }; + const params2 = { bar: 'baz' }; + + navigateTo('B', { + params: params1, + action: { type: NavigationActions.NAVIGATE, - routeName: 'B', - params: { foo: 'bar' }, - action: { type: NavigationActions.NAVIGATE, routeName: 'B2' }, + routeName: 'B2', + params: params2, }, - state - ); - const subState = state2.routes[state2.index]; - const activeGrandChildRoute = subState.routes[subState.index]; - expect(subState.params.foo).toEqual('bar'); - expect(activeGrandChildRoute.routeName).toEqual('B2'); + }); + expect(getSubState(1).routeName).toEqual('B'); + expect(getSubState(1).params).toEqual(params1); + expect(getSubState(2).routeName).toEqual('B2'); + expect(getSubState(2).params).toEqual(params2); }); it('order of handling navigate action is correct for nested switchrouters', () => { @@ -209,35 +171,17 @@ describe('SwitchRouter', () => { } ); - const state = router.getStateForAction({ type: NavigationActions.INIT }); - expect(state.routes[state.index].routeName).toEqual('OtherNestedSwitch'); + const { navigateTo, getSubState } = getRouterTestHelper(router); + expect(getSubState(1).routeName).toEqual('OtherNestedSwitch'); - const state2 = router.getStateForAction( - { - type: NavigationActions.NAVIGATE, - routeName: 'Bar', - }, - state - ); - expect(state2.routes[state2.index].routeName).toEqual('Bar'); + navigateTo('Bar'); + expect(getSubState(1).routeName).toEqual('Bar'); - const state3 = router.getStateForAction( - { - type: NavigationActions.NAVIGATE, - routeName: 'NestedSwitch', - }, - state2 - ); - const state4 = router.getStateForAction( - { - type: NavigationActions.NAVIGATE, - routeName: 'Bar', - }, - state3 - ); - let activeState4 = state4.routes[state4.index]; - expect(activeState4.routeName).toEqual('NestedSwitch'); - expect(activeState4.routes[activeState4.index].routeName).toEqual('Bar'); + navigateTo('NestedSwitch'); + navigateTo('Bar'); + + expect(getSubState(1).routeName).toEqual('NestedSwitch'); + expect(getSubState(2).routeName).toEqual('Bar'); }); // https://github.com/react-navigation/react-navigation.github.io/issues/117#issuecomment-385597628 @@ -259,42 +203,14 @@ describe('SwitchRouter', () => { } ); - const state = router.getStateForAction({ type: NavigationActions.INIT }); - expect(state.routes[state.index].routeName).toEqual('Login'); + const { navigateTo, getSubState } = getRouterTestHelper(router); + expect(getSubState(1).routeName).toEqual('Login'); - const state2 = router.getStateForAction( - { - type: SwitchActions.JUMP_TO, - routeName: 'Home', - }, - state - ); - expect(state2.routes[state2.index].routeName).toEqual('Home'); + navigateTo('Home'); + expect(getSubState(1).routeName).toEqual('Home'); }); }); -// A simple helper that makes it easier to write basic routing tests -// As we generally want to apply one action after the other, -// it's often convenient to manipulate a structure that keeps the router state -class ExampleRouterHelper { - constructor(config) { - this._router = getExampleRouter(config); - this._currentState = this._router.getStateForAction({ - type: NavigationActions.INIT, - }); - } - - applyAction = action => { - this._currentState = this._router.getStateForAction( - action, - this._currentState - ); - return this._currentState; - }; - - getCurrentState = () => this._currentState; -} - const getExampleRouter = (config = {}) => { const PlainScreen = () =>
; const StackA = () =>
; diff --git a/packages/core/src/routers/__tests__/routerTestHelper.js b/packages/core/src/routers/__tests__/routerTestHelper.js new file mode 100644 index 00000000..3ac1eb57 --- /dev/null +++ b/packages/core/src/routers/__tests__/routerTestHelper.js @@ -0,0 +1,55 @@ +import * as NavigationActions from '../../NavigationActions'; +import * as SwitchActions from '../../routers/SwitchActions'; + +// A simple helper that makes it easier to write basic routing tests +// We generally want to apply one action after the other and check router returns correct state +// it's often convenient to manipulate a structure that keeps the router state to avoid +// creating many state1, state2, state3 local variables which are prone to typos... + +const defaultInitAction = { + type: NavigationActions.INIT, +}; + +export const getRouterTestHelper = (router, initAction = defaultInitAction) => { + let state = router.getStateForAction(initAction); + + const applyAction = action => { + state = router.getStateForAction(action, state); + }; + + const navigateTo = (routeName, otherActionAttributes) => + applyAction({ + type: NavigationActions.NAVIGATE, + routeName, + ...otherActionAttributes, + }); + + const jumpTo = (routeName, otherActionAttributes) => + applyAction({ + type: SwitchActions.JUMP_TO, + routeName, + ...otherActionAttributes, + }); + + const back = () => + applyAction({ + type: NavigationActions.BACK, + }); + + const getState = () => state; + + const getSubState = (level = 1) => { + return getSubStateRecursive(state, level); + }; + + return { applyAction, navigateTo, jumpTo, back, getState, getSubState }; +}; + +const getSubStateRecursive = (state, level = 1) => { + if (level === 0) { + return state; + } else { + const directSubState = state.routes[state.index]; + return getSubStateRecursive(directSubState, level - 1); + } +};