loading ...
: ( 51 |Hello world
26 | ] 27 | ] 28 | 29 | const url = '/hello/world' 30 | 31 | const expectedAction = { 32 | actions: [ updateUrl(url), actionFoo(), actionBar() ], 33 | after: [ actionBaz ], 34 | type: CHANGE_PAGE_TO, 35 | url 36 | } 37 | 38 | getAction(to, routes).then(action => { 39 | expect(action).toEqual(expectedAction) 40 | done() 41 | }) 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /test/helpers/getLocation.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import expect from 'expect' 4 | 5 | import getLocation, { getPathName, getQuery, getQueryString } from '../../src/helpers/getLocation' 6 | 7 | describe('helper', () => { 8 | describe('getPathName()', () => { 9 | it('should work with a root path string', () => { 10 | const to = '/' 11 | const expectedPathName = '/' 12 | expect(getPathName(to)).toBe(expectedPathName) 13 | }) 14 | 15 | it('should work with a path string', () => { 16 | const to = '/hello/world' 17 | const expectedPathName = '/hello/world' 18 | expect(getPathName(to)).toBe(expectedPathName) 19 | }) 20 | 21 | it('should work with a path string including query string', () => { 22 | const to = '/hello/world?with=a&query=string' 23 | const expectedPathName = '/hello/world' 24 | expect(getPathName(to)).toBe(expectedPathName) 25 | }) 26 | 27 | it('should work with an array of a single root path', () => { 28 | const to = [ '/' ] 29 | const expectedPathName = '/' 30 | expect(getPathName(to)).toBe(expectedPathName) 31 | }) 32 | 33 | it('should work with an array of multiple path parts', () => { 34 | const to = [ 'hello', 'world' ] 35 | const expectedPathName = '/hello/world' 36 | expect(getPathName(to)).toBe(expectedPathName) 37 | }) 38 | 39 | it('should work with an array of multiple path parts including a numeric value', () => { 40 | const to = [ 'hello', 1, 'world' ] 41 | const expectedPathName = '/hello/1/world' 42 | expect(getPathName(to)).toBe(expectedPathName) 43 | }) 44 | 45 | it('should work with an array of multiple path parts including a boolean value', () => { 46 | const to = [ 'hello', true, 'world' ] 47 | const expectedPathName = '/hello/true/world' 48 | expect(getPathName(to)).toBe(expectedPathName) 49 | }) 50 | 51 | it('should work with an array of multiple path parts including a query object', () => { 52 | const to = [ 'hello', 'world', { with: 'a', query: 'string' } ] 53 | const expectedPathName = '/hello/world' 54 | expect(getPathName(to)).toBe(expectedPathName) 55 | }) 56 | }) 57 | 58 | describe('getQuery()', () => { 59 | it('should work with a root path string', () => { 60 | const to = '/' 61 | const expectedQuery = {} 62 | expect(getQuery(to)).toEqual(expectedQuery) 63 | }) 64 | 65 | it('should work with a path string', () => { 66 | const to = '/hello/world' 67 | const expectedQuery = {} 68 | expect(getQuery(to)).toEqual(expectedQuery) 69 | }) 70 | 71 | it('should work with a path string including a query string', () => { 72 | const to = '/hello/world?with=a&query=string' 73 | const expectedQuery = { with: 'a', query: 'string' } 74 | expect(getQuery(to)).toEqual(expectedQuery) 75 | }) 76 | 77 | it('should work with an array of a single root path', () => { 78 | const to = [ '/' ] 79 | const expectedQuery = {} 80 | expect(getQuery(to)).toEqual(expectedQuery) 81 | }) 82 | 83 | it('should work with an array of multiple path parts', () => { 84 | const to = [ 'hello', 'world' ] 85 | const expectedQuery = {} 86 | expect(getQuery(to)).toEqual(expectedQuery) 87 | }) 88 | 89 | it('should work with an array of multiple path parts including a query object', () => { 90 | const to = '/?foo%5B%5D=bar,baz' 91 | const expectedQuery = { foo: [ 'bar', 'baz' ] } 92 | expect(getQuery(to)).toEqual(expectedQuery) 93 | }) 94 | 95 | it('should work when the query object includes an array', () => { 96 | const to = '/?foo%5Bbar%5D=baz' 97 | const expectedQuery = { foo: { bar: 'baz' } } 98 | expect(getQuery(to)).toEqual(expectedQuery) 99 | }) 100 | 101 | it('should work when the query object includes an object', () => { 102 | const to = [ 'hello', 'world', { with: 'a', query: 'string' } ] 103 | const expectedQuery = { with: 'a', query: 'string' } 104 | expect(getQuery(to)).toEqual(expectedQuery) 105 | }) 106 | 107 | it('should decode special characters', () => { 108 | const to = '/hello/world?test=Rock+%26+Roll' 109 | const expectedQuery = { test: 'Rock & Roll' } 110 | expect(getQuery(to)).toEqual(expectedQuery) 111 | }) 112 | }) 113 | 114 | describe('getQueryString()', () => { 115 | it('should work with an empty query object', () => { 116 | const query = {} 117 | const expectedQueryString = '' 118 | expect(getQueryString(query)).toBe(expectedQueryString) 119 | }) 120 | 121 | it('should work with multiple query items', () => { 122 | const query = { with: 'a', query: 'string' } 123 | const expectedQueryString = '?with=a&query=string' 124 | expect(getQueryString(query)).toBe(expectedQueryString) 125 | }) 126 | 127 | it('should work with a query item that is an array', () => { 128 | const query = { foo: [ 'bar', 'baz' ] } 129 | const expectedQueryString = '?foo%5B%5D=bar,baz' 130 | expect(getQueryString(query)).toBe(expectedQueryString) 131 | }) 132 | 133 | it('should work with a query item that is an object', () => { 134 | const query = { foo: { bar: 'baz' } } 135 | const expectedQueryString = '?foo%5Bbar%5D=baz' 136 | expect(getQueryString(query)).toBe(expectedQueryString) 137 | }) 138 | 139 | it('should work with an query item that has an empty value', () => { 140 | const query = { foo: '' } 141 | const expectedQueryString = '' 142 | expect(getQueryString(query)).toBe(expectedQueryString) 143 | }) 144 | 145 | it('should work with an query item that is an object and has an empty value', () => { 146 | const query = { foo: { bar: '' } } 147 | const expectedQueryString = '' 148 | expect(getQueryString(query)).toBe(expectedQueryString) 149 | }) 150 | 151 | it('should encode special characters', () => { 152 | const query = { test: 'Rock & Roll' } 153 | const expectedQueryString = '?test=Rock+%26+Roll' 154 | expect(getQueryString(query)).toBe(expectedQueryString) 155 | }) 156 | }) 157 | 158 | describe('getLocation()', () => { 159 | it('should work with a path string', () => { 160 | const to = '/hello/world' 161 | const expectedLocation = { 162 | pathName: '/hello/world', 163 | query: {}, 164 | queryString: '', 165 | url: '/hello/world' 166 | } 167 | 168 | expect(getLocation(to)).toEqual(expectedLocation) 169 | }) 170 | 171 | it('should work with a path string including a query string', () => { 172 | const to = '/hello/world?a=b&c=d' 173 | const expectedLocation = { 174 | pathName: '/hello/world', 175 | query: { a: 'b', c: 'd' }, 176 | queryString: '?a=b&c=d', 177 | url: '/hello/world?a=b&c=d' 178 | } 179 | 180 | expect(getLocation(to)).toEqual(expectedLocation) 181 | }) 182 | 183 | it('should work with an array of a single path', () => { 184 | const to = [ '/hello/world' ] 185 | const expectedLocation = { 186 | pathName: '/hello/world', 187 | query: {}, 188 | queryString: '', 189 | url: '/hello/world' 190 | } 191 | 192 | expect(getLocation(to)).toEqual(expectedLocation) 193 | }) 194 | 195 | it('should work with an array of a single path without a slash prefix', () => { 196 | const to = [ 'hello/world' ] 197 | const expectedLocation = { 198 | pathName: '/hello/world', 199 | query: {}, 200 | queryString: '', 201 | url: '/hello/world' 202 | } 203 | 204 | expect(getLocation(to)).toEqual(expectedLocation) 205 | }) 206 | 207 | it('should work with an array of a single path with a query string', () => { 208 | const to = [ 'hello/world?a=b&c=d' ] 209 | const expectedLocation = { 210 | pathName: '/hello/world', 211 | query: { a: 'b', c: 'd' }, 212 | queryString: '?a=b&c=d', 213 | url: '/hello/world?a=b&c=d' 214 | } 215 | 216 | expect(getLocation(to)).toEqual(expectedLocation) 217 | }) 218 | 219 | it('should work with an array of multiple path parts', () => { 220 | const to = [ 'hello', 'world' ] 221 | const expectedLocation = { 222 | pathName: '/hello/world', 223 | query: {}, 224 | queryString: '', 225 | url: '/hello/world' 226 | } 227 | 228 | expect(getLocation(to)).toEqual(expectedLocation) 229 | }) 230 | 231 | it('should work with an array of multiple path parts including a query object', () => { 232 | const to = [ 'hello', 'world', { a: 'b', c: 'd' } ] 233 | const expectedLocation = { 234 | pathName: '/hello/world', 235 | query: { a: 'b', c: 'd' }, 236 | queryString: '?a=b&c=d', 237 | url: '/hello/world?a=b&c=d' 238 | } 239 | 240 | expect(getLocation(to)).toEqual(expectedLocation) 241 | }) 242 | }) 243 | }) 244 | -------------------------------------------------------------------------------- /test/helpers/getRoute.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import expect from 'expect' 4 | import React from 'react' 5 | 6 | import getRoute, { 7 | match, 8 | getActionCreators, 9 | getComponent, 10 | getParams 11 | } from '../../src/helpers/getRoute' 12 | 13 | describe('helper', () => { 14 | describe('match()', () => { 15 | it('should match route paths', () => { 16 | const pathName = '/' 17 | const routePath = '/' 18 | expect(match(pathName, routePath)).toBe(true) 19 | }) 20 | 21 | it('should match multiple part paths', () => { 22 | const pathName = '/hello/world' 23 | const routePath = '/hello/world' 24 | expect(match(pathName, routePath)).toBe(true) 25 | }) 26 | 27 | it('should match multiple part paths when param in route', () => { 28 | const pathName = '/users/1/followers' 29 | const routePath = '/users/:id/followers' 30 | expect(match(pathName, routePath)).toBe(true) 31 | }) 32 | 33 | it('should match multiple part paths when only a single splat in root', () => { 34 | const pathName = '/hello/world' 35 | const routePath = '*' 36 | expect(match(pathName, routePath)).toBe(true) 37 | }) 38 | 39 | it('should match multiple part paths when splat in route', () => { 40 | const pathName = '/hello/world' 41 | const routePath = '/hello/*' 42 | expect(match(pathName, routePath)).toBe(true) 43 | }) 44 | 45 | it('should not match incorrect paths', () => { 46 | const pathName = '/hello/world' 47 | const routePath = '/hello/there' 48 | expect(match(pathName, routePath)).toBe(false) 49 | }) 50 | 51 | it('should not match if route longer than path', () => { 52 | const pathName = '/hello/crazy' 53 | const routePath = '/hello/crazy/world' 54 | expect(match(pathName, routePath)).toBe(false) 55 | }) 56 | 57 | it('should not match if path longer than route', () => { 58 | const pathName = '/hello/crazy/world' 59 | const routePath = '/hello/crazy' 60 | expect(match(pathName, routePath)).toBe(false) 61 | }) 62 | }) 63 | 64 | describe('getActionCreators()', () => { 65 | it('should work with no actions', () => { 66 | const actions = {} 67 | const route = [ '/hello/world', 'Hello world
' ] 68 | expect(getActionCreators(route)).toEqual(actions) 69 | }) 70 | 71 | it('should work with a single action', () => { 72 | const updateFoo = () => ({}) 73 | const actions = { foo: updateFoo } 74 | const route = [ '/hello/world', actions, 'Hello world
' ] 75 | expect(getActionCreators(route)).toEqual(actions) 76 | }) 77 | 78 | it('should work with multiple actions', () => { 79 | const updateFoo = () => ({}) 80 | const updateBar = () => ({}) 81 | const actions = { foo: updateFoo, bar: updateBar } 82 | const route = [ '/hello/world', actions, 'Hello world
' ] 83 | expect(getActionCreators(route)).toEqual(actions) 84 | }) 85 | }) 86 | 87 | describe('getComponent()', () => { 88 | it('should return react component', () => { 89 | const component =Hello world
90 | const route = [ '/hello/world', component ] 91 | expect(getComponent(route)).toBe(component) 92 | }) 93 | }) 94 | 95 | describe('getParams()', () => { 96 | it('should work with no params in route', () => { 97 | const routePath = '/hello/world' 98 | const pathName = '/hello/world' 99 | const expectedParams = {} 100 | expect(getParams(routePath, pathName)).toEqual(expectedParams) 101 | }) 102 | 103 | it('should work with a single param in route', () => { 104 | const routePath = '/hello/:foo' 105 | const pathName = '/hello/world' 106 | const expectedParams = { foo: 'world' } 107 | expect(getParams(routePath, pathName)).toEqual(expectedParams) 108 | }) 109 | 110 | it('should work with multiple params in route', () => { 111 | const routePath = '/:foo/:bar' 112 | const pathName = '/hello/world' 113 | const expectedParams = { foo: 'hello', bar: 'world' } 114 | expect(getParams(routePath, pathName)).toEqual(expectedParams) 115 | }) 116 | }) 117 | 118 | describe('getRoute()', () => { 119 | const HelloWorld =Hello world
120 | const Home =Home
121 | const NotFound =Page not found
122 | const UserFollowers =User followers
123 | 124 | const updatePage = () => ({}) 125 | const someUserAction = () => ({}) 126 | const updateName = () => ({}) 127 | 128 | const helloWorldActions = { name: updateName } 129 | const userFollowersActions = { id: someUserAction, page: updatePage } 130 | 131 | const routes = [ 132 | [ 'user/:id/followers', userFollowersActions, UserFollowers ], 133 | [ 'hello', 'world', helloWorldActions, HelloWorld ], 134 | [ '/', Home ], 135 | [ '*', NotFound ] 136 | ] 137 | 138 | it('should get not found route', () => { 139 | expect(getRoute('/favicon.ico', routes)).toEqual({ 140 | actionCreators: {}, 141 | component: NotFound, 142 | params: {} 143 | }) 144 | }) 145 | 146 | it('should get not found route', () => { 147 | expect(getRoute('/favicon.ico', routes)).toEqual({ 148 | actionCreators: {}, 149 | component: NotFound, 150 | params: {} 151 | }) 152 | }) 153 | 154 | it('should get not found route', () => { 155 | expect(getRoute('/something/that/does/not/match', routes)).toEqual({ 156 | actionCreators: {}, 157 | component: NotFound, 158 | params: {} 159 | }) 160 | }) 161 | 162 | it('should get root route', () => { 163 | expect(getRoute('/', routes)).toEqual({ 164 | actionCreators: {}, 165 | component: Home, 166 | params: {} 167 | }) 168 | }) 169 | 170 | it('should get route with actions', () => { 171 | expect(getRoute('/hello/world?name=colin', routes)).toEqual({ 172 | actionCreators: helloWorldActions, 173 | component: HelloWorld, 174 | params: {} 175 | }) 176 | }) 177 | 178 | it('should get route with actions and params', () => { 179 | expect(getRoute('/user/2/followers?page=5', routes)).toEqual({ 180 | actionCreators: userFollowersActions, 181 | component: UserFollowers, 182 | params: { id: '2' } 183 | }) 184 | }) 185 | }) 186 | }) 187 | -------------------------------------------------------------------------------- /test/helpers/getState.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import expect from 'expect' 4 | 5 | import { getState, routerReducer } from '../../src' 6 | 7 | describe('helper', () => { 8 | describe('getState()', () => { 9 | const UPDATE_FOO = 'UPDATE_FOO' 10 | const UPDATE_BAR = 'UPDATE_BAR' 11 | const UPDATE_BAZ = 'UPDATE_BAZ' 12 | 13 | const updateFoo = foo => ({ foo, type: UPDATE_FOO }) 14 | 15 | const updateBar = bar => new Promise(resolve => { 16 | setTimeout(() => { 17 | resolve({ bar, type: UPDATE_BAR }) 18 | }, 5) 19 | }) 20 | 21 | const updateBaz = ({ foo, bar = 'michoacan' }) => new Promise(resolve => { 22 | setTimeout(() => { 23 | resolve({ baz: foo + bar, type: UPDATE_BAZ }) 24 | }, 5) 25 | }) 26 | 27 | const fooReducer = (state = 1, action) => { 28 | switch (action.type) { 29 | case UPDATE_FOO: 30 | return parseInt(action.foo, 10) 31 | default: 32 | return state 33 | } 34 | } 35 | 36 | const barReducer = (state = '', action) => { 37 | switch (action.type) { 38 | case UPDATE_BAR: 39 | return action.bar 40 | default: 41 | return state 42 | } 43 | } 44 | 45 | const bazReducer = (state = '', action) => { 46 | switch (action.type) { 47 | case UPDATE_BAZ: 48 | return action.baz 49 | default: 50 | return state 51 | } 52 | } 53 | 54 | it('should return correct state', done => { 55 | const reducer = routerReducer({ foo: fooReducer }) 56 | 57 | const routes = [ 58 | [ 'hello', 'world', { foo: updateFoo } ] 59 | ] 60 | 61 | const url = '/hello/world?foo=2' 62 | 63 | const expectedState = { 64 | foo: 2, 65 | url 66 | } 67 | 68 | getState(url, routes, reducer).then(state => { 69 | expect(state).toEqual(expectedState) 70 | done() 71 | }) 72 | }) 73 | 74 | it('should return correct state with async action creator', done => { 75 | const reducer = routerReducer({ foo: fooReducer, bar: barReducer }) 76 | 77 | const routes = [ 78 | [ 'hello', 'world', { foo: updateFoo, bar: updateBar } ] 79 | ] 80 | 81 | const url = '/hello/world?foo=3&bar=guanajuato' 82 | 83 | const expectedState = { 84 | bar: 'guanajuato', 85 | foo: 3, 86 | url 87 | } 88 | 89 | getState(url, routes, reducer).then(state => { 90 | expect(state).toEqual(expectedState) 91 | done() 92 | }) 93 | }) 94 | 95 | it('should return correct state with async after action creator', done => { 96 | const reducer = routerReducer({ foo: fooReducer, baz: bazReducer }) 97 | 98 | const routes = [ 99 | [ 'hello', 'world', { foo: updateFoo, after: updateBaz } ] 100 | ] 101 | 102 | const url = '/hello/world?foo=4' 103 | 104 | const expectedState = { 105 | baz: '4michoacan', 106 | foo: 4, 107 | url 108 | } 109 | 110 | getState(url, routes, reducer).then(state => { 111 | expect(state).toEqual(expectedState) 112 | done() 113 | }) 114 | }) 115 | 116 | it( 117 | 'should return correct state with both async action creator and async after action creator', 118 | done => { 119 | const reducer = routerReducer({ foo: fooReducer, bar: barReducer, baz: bazReducer }) 120 | 121 | const routes = [ 122 | [ 'hello', 'world', { foo: updateFoo, bar: updateBar, after: updateBaz } ] 123 | ] 124 | 125 | const url = '/hello/world?foo=5&bar=oaxaca' 126 | 127 | const expectedState = { 128 | bar: 'oaxaca', 129 | baz: '5oaxaca', 130 | foo: 5, 131 | url 132 | } 133 | 134 | getState(url, routes, reducer).then(state => { 135 | expect(state).toEqual(expectedState) 136 | done() 137 | }) 138 | } 139 | ) 140 | }) 141 | }) 142 | -------------------------------------------------------------------------------- /test/helpers/resolveActionCreator.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import expect from 'expect' 4 | 5 | import { 6 | resolveActionCreator, 7 | getResolvedActionCreators 8 | } from '../../src/helpers/resolveActionCreator' 9 | 10 | describe('helper', () => { 11 | describe('resolveActionCreator()', () => { 12 | const action = { type: 'HELLO_WORLD' } 13 | const actionCreator = () => action 14 | 15 | it('should return a promise', () => { 16 | expect(resolveActionCreator(actionCreator, undefined)).toBeA(Promise) 17 | }) 18 | 19 | it('should return an action once resolved', done => { 20 | resolveActionCreator(actionCreator, undefined).then(a => { 21 | expect(a).toEqual(action) 22 | done() 23 | }) 24 | }) 25 | 26 | it('should pass data as arguments to action creator', done => { 27 | const FOO = 'FOO' 28 | 29 | const fooActionCreator = foo => ({ 30 | foo, 31 | type: FOO 32 | }) 33 | 34 | const expectedAction = { 35 | foo: 'bar', 36 | type: FOO 37 | } 38 | 39 | resolveActionCreator(fooActionCreator, 'bar').then(a => { 40 | expect(a).toEqual(expectedAction) 41 | done() 42 | }) 43 | }) 44 | 45 | it('should work with an async action creator', done => { 46 | const asyncActionCreator = () => new Promise(resolve => { 47 | setTimeout(() => { 48 | resolve(action) 49 | }, 5) 50 | }) 51 | 52 | resolveActionCreator(asyncActionCreator, undefined).then(a => { 53 | expect(a).toEqual(action) 54 | done() 55 | }) 56 | }) 57 | }) 58 | 59 | describe('getResolvedActionCreators()', () => { 60 | const FOO = 'FOO' 61 | const BAR = 'BAR' 62 | 63 | const fooAction = { type: FOO } 64 | const barAction = { type: BAR } 65 | 66 | const expectedActions = [ fooAction, barAction ] 67 | 68 | const fooActionCreator = () => fooAction 69 | const barActionCreator = () => barAction 70 | 71 | it('should return an empty array by default', () => { 72 | expect(getResolvedActionCreators(new Map())).toEqual([]) 73 | }) 74 | 75 | it('should return an array of promises', () => { 76 | const actionCreatorsMap = new Map() 77 | 78 | actionCreatorsMap.set(fooActionCreator, undefined) 79 | actionCreatorsMap.set(barActionCreator, undefined) 80 | 81 | getResolvedActionCreators(actionCreatorsMap).forEach(promise => { 82 | expect(promise).toBeA(Promise) 83 | }) 84 | }) 85 | 86 | it('should return an array of actions once resolved', done => { 87 | const actionCreatorsMap = new Map() 88 | 89 | actionCreatorsMap.set(fooActionCreator, undefined) 90 | actionCreatorsMap.set(barActionCreator, undefined) 91 | 92 | Promise.all(getResolvedActionCreators(actionCreatorsMap)).then(actions => { 93 | expect(actions).toEqual(expectedActions) 94 | done() 95 | }) 96 | }) 97 | 98 | it('should pass data as arguments to each action creator', done => { 99 | const fooWithArgsActionCreator = type => ({ type }) 100 | const barWithArgsActionCreator = type => ({ type }) 101 | 102 | const actionCreatorsMap = new Map() 103 | 104 | actionCreatorsMap.set(fooWithArgsActionCreator, FOO) 105 | actionCreatorsMap.set(barWithArgsActionCreator, BAR) 106 | 107 | Promise.all(getResolvedActionCreators(actionCreatorsMap)).then(actions => { 108 | expect(actions).toEqual(expectedActions) 109 | done() 110 | }) 111 | }) 112 | 113 | it('should work with an async action creator', done => { 114 | const asyncActionCreator = () => new Promise(resolve => { 115 | setTimeout(() => { 116 | resolve(barAction) 117 | }, 5) 118 | }) 119 | 120 | const actionCreatorsMap = new Map() 121 | 122 | actionCreatorsMap.set(fooActionCreator, undefined) 123 | actionCreatorsMap.set(asyncActionCreator, undefined) 124 | 125 | Promise.all(getResolvedActionCreators(actionCreatorsMap)).then(actions => { 126 | expect(actions).toEqual(expectedActions) 127 | done() 128 | }) 129 | }) 130 | }) 131 | }) 132 | -------------------------------------------------------------------------------- /test/middleware/router.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import expect from 'expect' 4 | import React from 'react' 5 | 6 | import routerMiddleware from '../../src/middleware/router' 7 | import updateUrl from '../../src/actions/updateUrl' 8 | import { CHANGE_PAGE_TO } from '../../src/constants' 9 | 10 | describe('middleware', () => { 11 | describe('router()', () => { 12 | const dispatch = () => ({}) 13 | 14 | const next = action => action 15 | 16 | const updatePage = page => ({ type: 'UPDATE_PAGE', page }) 17 | 18 | const routes = [ 19 | [ 'hello', 'world', { page: updatePage },Hello world
] 20 | ] 21 | 22 | const router = routerMiddleware(routes, { isServer: true }) 23 | 24 | it('should not alter action that is not change page to', () => { 25 | const action = { 26 | type: 'HELLO_WORLD' 27 | } 28 | 29 | expect(router({ dispatch })(next)(action)).toEqual(action) 30 | }) 31 | 32 | it('should add correct actions and url props to change page to action', done => { 33 | const action = { 34 | options: { shouldAddToHistory: false }, 35 | to: [ 'hello', 'world' ], 36 | type: CHANGE_PAGE_TO 37 | } 38 | 39 | const url = '/hello/world' 40 | 41 | const actions = [ updateUrl(url), updatePage(undefined) ] 42 | 43 | const result = router({ dispatch })(next)(action) 44 | 45 | setTimeout(() => { 46 | result.then(({ a, u }) => { 47 | expect(a).toEqual(actions) 48 | expect(u).toEqual(url) 49 | }) 50 | 51 | done() 52 | }, 0) 53 | }) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /test/reducers/router.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import expect from 'expect' 4 | 5 | import routerReducer from '../../src/reducers/router' 6 | import { CHANGE_PAGE_TO, UPDATE_URL } from '../../src/constants' 7 | 8 | describe('reducer', () => { 9 | describe('router()', () => { 10 | const pageReducer = (state = 1, { page }) => (parseInt(page || state, 10)) 11 | 12 | const reducers = { 13 | page: pageReducer 14 | } 15 | 16 | const reducer = routerReducer(reducers) 17 | 18 | it('should return default state when no action passed to reducer', () => { 19 | const expectedState = { 20 | page: 1, 21 | url: '/' 22 | } 23 | 24 | expect(reducer({}, {})).toEqual(expectedState) 25 | }) 26 | 27 | it('should run root reducer when not change page to action type', () => { 28 | const expectedState = { 29 | page: 5, 30 | url: '/' 31 | } 32 | 33 | expect(reducer({}, { page: 5 })).toEqual(expectedState) 34 | }) 35 | 36 | it('should run batch reducer when change page to action type', () => { 37 | const expectedState = { 38 | page: 1, 39 | url: '/hello/world' 40 | } 41 | 42 | expect(reducer({}, { 43 | actions: [{ type: UPDATE_URL, url: '/hello/world' }, { page: undefined }], 44 | after: [], 45 | to: [ 'hello', 'world' ], 46 | type: CHANGE_PAGE_TO 47 | })).toEqual(expectedState) 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/reducers/url.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import expect from 'expect' 4 | 5 | import urlReducer from '../../src/reducers/url' 6 | import { UPDATE_URL } from '../../src/constants' 7 | 8 | describe('reducer', () => { 9 | describe('url()', () => { 10 | it('should return the default state by default', () => { 11 | expect(urlReducer()).toEqual('/') 12 | }) 13 | 14 | it('should handle update url action type', () => { 15 | const url = '/hello-world' 16 | 17 | expect(urlReducer('/initial-url', { 18 | type: UPDATE_URL, 19 | url 20 | })).toEqual(url) 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | export default { 2 | externals: { 3 | react: 'React', 4 | 'react-redux': 'ReactRedux' 5 | }, 6 | module: { 7 | loaders: [{ 8 | exclude: /node_modules/, 9 | loader: 'babel', 10 | test: /\.js$/ 11 | }] 12 | } 13 | } 14 | --------------------------------------------------------------------------------