tests: refactor SwitchRouter tests (#34)

* add test:dev for dev test workflow, which should rather not lint/build and trigger pretest

* refactor tests to use closure based helper instead of class

* refactor SwitchRouter tests with router stateful helpers to improve tests readability

* simplify matcher

* better initialRoute backBehavior test by using a non-default routeName

* minor fix

* Update src/routers/__tests__/routerTestHelper.js

Co-Authored-By: slorber <slorber@users.noreply.github.com>

* Update src/routers/__tests__/routerTestHelper.js

Co-Authored-By: slorber <slorber@users.noreply.github.com>

* Update src/routers/__tests__/routerTestHelper.js

Co-Authored-By: slorber <slorber@users.noreply.github.com>

* revert bad merge stuff

* revert bad merge stuff

* fix tests
This commit is contained in:
Sébastien Lorber
2019-09-16 13:18:22 +02:00
parent 2528421656
commit 513d5d4d8a
2 changed files with 163 additions and 192 deletions

View File

@@ -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 = () => <div />;
const StackA = () => <div />;

View File

@@ -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);
}
};