import { RouterOptions, createRouter as newRouter } from '../../src/router'
import fakePromise from 'faked-promise'
-import { NAVIGATION_TYPES, createDom, noGuard } from '../utils'
+import { createDom, noGuard } from '../utils'
import { RouteRecord, NavigationGuard } from '../../src/types'
import { createWebHistory } from '../../src'
createDom()
})
- NAVIGATION_TYPES.forEach(navigationMethod => {
- describe(navigationMethod, () => {
- it('calls beforeRouteEnter guards on navigation', async () => {
- const router = createRouter({ routes })
- beforeRouteEnter.mockImplementationOnce((to, from, next) => {
- if (to.params.n !== 'valid') return next(false)
- next()
- })
- await router[navigationMethod]('/guard/valid')
- expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
- })
+ it('calls beforeRouteEnter guards on navigation', async () => {
+ const router = createRouter({ routes })
+ beforeRouteEnter.mockImplementationOnce((to, from, next) => {
+ if (to.params.n !== 'valid') return next(false)
+ next()
+ })
+ await router.push('/guard/valid')
+ expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
+ })
- it('calls beforeRouteEnter guards on navigation for nested views', async () => {
- const router = createRouter({ routes })
- await router[navigationMethod]('/nested/nested/foo')
- expect(nested.parent).toHaveBeenCalledTimes(1)
- expect(nested.nestedNested).toHaveBeenCalledTimes(1)
- expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
- expect(nested.nestedAbs).not.toHaveBeenCalled()
- expect(nested.nestedA).not.toHaveBeenCalled()
- })
+ it('calls beforeRouteEnter guards on navigation for nested views', async () => {
+ const router = createRouter({ routes })
+ await router.push('/nested/nested/foo')
+ expect(nested.parent).toHaveBeenCalledTimes(1)
+ expect(nested.nestedNested).toHaveBeenCalledTimes(1)
+ expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
+ expect(nested.nestedAbs).not.toHaveBeenCalled()
+ expect(nested.nestedA).not.toHaveBeenCalled()
+ })
- it('calls beforeRouteEnter guards on navigation for nested views', async () => {
- const router = createRouter({ routes })
- await router[navigationMethod]('/nested/nested/foo')
- expect(nested.parent).toHaveBeenCalledTimes(1)
- expect(nested.nestedNested).toHaveBeenCalledTimes(1)
- expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
- })
+ it('calls beforeRouteEnter guards on navigation for nested views', async () => {
+ const router = createRouter({ routes })
+ await router.push('/nested/nested/foo')
+ expect(nested.parent).toHaveBeenCalledTimes(1)
+ expect(nested.nestedNested).toHaveBeenCalledTimes(1)
+ expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
+ })
- it('calls beforeRouteEnter guards on non-entered nested routes', async () => {
- const router = createRouter({ routes })
- await router.push('/nested/nested')
- resetMocks()
- await router[navigationMethod]('/nested/nested/foo')
- expect(nested.parent).not.toHaveBeenCalled()
- expect(nested.nestedNested).not.toHaveBeenCalled()
- expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
- })
+ it('calls beforeRouteEnter guards on non-entered nested routes', async () => {
+ const router = createRouter({ routes })
+ await router.push('/nested/nested')
+ resetMocks()
+ await router.push('/nested/nested/foo')
+ expect(nested.parent).not.toHaveBeenCalled()
+ expect(nested.nestedNested).not.toHaveBeenCalled()
+ expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
+ })
- it('does not call beforeRouteEnter guards on param change', async () => {
- const router = createRouter({ routes })
- await router.push('/nested/nested/param/1')
- resetMocks()
- await router[navigationMethod]('/nested/nested/param/2')
- expect(nested.parent).not.toHaveBeenCalled()
- expect(nested.nestedNested).not.toHaveBeenCalled()
- expect(nested.nestedNestedParam).not.toHaveBeenCalled()
- })
+ it('does not call beforeRouteEnter guards on param change', async () => {
+ const router = createRouter({ routes })
+ await router.push('/nested/nested/param/1')
+ resetMocks()
+ await router.push('/nested/nested/param/2')
+ expect(nested.parent).not.toHaveBeenCalled()
+ expect(nested.nestedNested).not.toHaveBeenCalled()
+ expect(nested.nestedNestedParam).not.toHaveBeenCalled()
+ })
- it('calls beforeRouteEnter guards on navigation for named views', async () => {
- const router = createRouter({ routes })
- named.default.mockImplementationOnce(noGuard)
- named.other.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/named')
- expect(named.default).toHaveBeenCalledTimes(1)
- expect(named.other).toHaveBeenCalledTimes(1)
- expect(router.currentRoute.value.fullPath).toBe('/named')
- })
+ it('calls beforeRouteEnter guards on navigation for named views', async () => {
+ const router = createRouter({ routes })
+ named.default.mockImplementationOnce(noGuard)
+ named.other.mockImplementationOnce(noGuard)
+ await router.push('/named')
+ expect(named.default).toHaveBeenCalledTimes(1)
+ expect(named.other).toHaveBeenCalledTimes(1)
+ expect(router.currentRoute.value.fullPath).toBe('/named')
+ })
- it('aborts navigation if one of the named views aborts', async () => {
- const router = createRouter({ routes })
- named.default.mockImplementationOnce((to, from, next) => {
- next(false)
- })
- named.other.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/named').catch(err => {}) // catch abort
- expect(named.default).toHaveBeenCalledTimes(1)
- expect(router.currentRoute.value.fullPath).not.toBe('/named')
- })
+ it('aborts navigation if one of the named views aborts', async () => {
+ const router = createRouter({ routes })
+ named.default.mockImplementationOnce((to, from, next) => {
+ next(false)
+ })
+ named.other.mockImplementationOnce(noGuard)
+ await router.push('/named').catch(err => {}) // catch abort
+ expect(named.default).toHaveBeenCalledTimes(1)
+ expect(router.currentRoute.value.fullPath).not.toBe('/named')
+ })
- it('does not call beforeRouteEnter if we were already on the page', async () => {
- const router = createRouter({ routes })
- beforeRouteEnter.mockImplementation(noGuard)
- await router.push('/guard/one')
- expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
- await router[navigationMethod]('/guard/one')
- expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
- })
+ it('does not call beforeRouteEnter if we were already on the page', async () => {
+ const router = createRouter({ routes })
+ beforeRouteEnter.mockImplementation(noGuard)
+ await router.push('/guard/one')
+ expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
+ await router.push('/guard/one')
+ expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
+ })
+
+ it('waits before navigating', async () => {
+ const [promise, resolve] = fakePromise()
+ const router = createRouter({ routes })
+ beforeRouteEnter.mockImplementationOnce(async (to, from, next) => {
+ await promise
+ next()
+ })
+ const p = router.push('/foo')
+ expect(router.currentRoute.value.fullPath).toBe('/')
+ resolve()
+ await p
+ expect(router.currentRoute.value.fullPath).toBe('/foo')
+ })
- it('waits before navigating', async () => {
- const [promise, resolve] = fakePromise()
- const router = createRouter({ routes })
- beforeRouteEnter.mockImplementationOnce(async (to, from, next) => {
- await promise
- next()
- })
- const p = router[navigationMethod]('/foo')
- expect(router.currentRoute.value.fullPath).toBe('/')
- resolve()
- await p
+ // TODO:
+ it.skip('calls next callback', async done => {
+ const router = createRouter({ routes })
+ beforeRouteEnter.mockImplementationOnce((to, from, next) => {
+ next(vm => {
expect(router.currentRoute.value.fullPath).toBe('/foo')
+ expect(vm).toBeTruthy()
+ done()
})
+ })
- // TODO:
- it.skip('calls next callback', async done => {
- const router = createRouter({ routes })
- beforeRouteEnter.mockImplementationOnce((to, from, next) => {
- next(vm => {
- expect(router.currentRoute.value.fullPath).toBe('/foo')
- expect(vm).toBeTruthy()
- done()
- })
- })
-
- await router.push('/')
- await router.push('/guard/2')
- })
+ await router.push('/')
+ await router.push('/guard/2')
+ })
- it.skip('calls next callback after waiting', async done => {
- const [promise, resolve] = fakePromise()
- const router = createRouter({ routes })
- beforeRouteEnter.mockImplementationOnce(async (to, from, next) => {
- await promise
- next(vm => {
- expect(router.currentRoute.value.fullPath).toBe('/foo')
- expect(vm).toBeTruthy()
- done()
- })
- })
- router[navigationMethod]('/foo')
- resolve()
+ it.skip('calls next callback after waiting', async done => {
+ const [promise, resolve] = fakePromise()
+ const router = createRouter({ routes })
+ beforeRouteEnter.mockImplementationOnce(async (to, from, next) => {
+ await promise
+ next(vm => {
+ expect(router.currentRoute.value.fullPath).toBe('/foo')
+ expect(vm).toBeTruthy()
+ done()
})
})
+ router.push('/foo')
+ resolve()
})
})
import { RouterOptions, createRouter as newRouter } from '../../src/router'
-import { NAVIGATION_TYPES, createDom, noGuard } from '../utils'
+import { createDom, noGuard } from '../utils'
import { RouteRecord } from '../../src/types'
import { createWebHistory } from '../../src'
createDom()
})
- NAVIGATION_TYPES.forEach(navigationMethod => {
- describe(navigationMethod, () => {
- it('calls beforeRouteLeave guard on navigation', async () => {
- const router = createRouter({ routes })
- beforeRouteLeave.mockImplementationOnce((to, from, next) => {
- if (to.path === 'foo') next(false)
- else next()
- })
- await router.push('/guard')
- expect(beforeRouteLeave).not.toHaveBeenCalled()
-
- await router[navigationMethod]('/foo')
- expect(beforeRouteLeave).toHaveBeenCalledTimes(1)
- })
+ it('calls beforeRouteLeave guard on navigation', async () => {
+ const router = createRouter({ routes })
+ beforeRouteLeave.mockImplementationOnce((to, from, next) => {
+ if (to.path === 'foo') next(false)
+ else next()
+ })
+ await router.push('/guard')
+ expect(beforeRouteLeave).not.toHaveBeenCalled()
- it('calls beforeRouteLeave guard on navigation between children', async () => {
- const router = createRouter({ routes })
- await router.push({ name: 'nested-path' })
- resetMocks()
- await router[navigationMethod]({ name: 'nested-path-b' })
- expect(nested.nestedEmpty).not.toHaveBeenCalled()
- expect(nested.nestedAbs).not.toHaveBeenCalled()
- expect(nested.nestedB).not.toHaveBeenCalled()
- expect(nested.nestedNestedFoo).not.toHaveBeenCalled()
- expect(nested.parent).not.toHaveBeenCalled()
- expect(nested.nestedNested).not.toHaveBeenCalled()
- expect(nested.nestedA).toHaveBeenCalledTimes(1)
- expect(nested.nestedA).toHaveBeenCalledWith(
- expect.objectContaining({
- name: 'nested-path-b',
- fullPath: '/nested/b',
- }),
- expect.objectContaining({
- name: 'nested-path',
- fullPath: '/nested/a',
- }),
- expect.any(Function)
- )
- })
+ await router.push('/foo')
+ expect(beforeRouteLeave).toHaveBeenCalledTimes(1)
+ })
- it('calls beforeRouteLeave guard on navigation between children in order', async () => {
- const router = createRouter({ routes })
- await router.push({ name: 'nested-nested-foo' })
- resetMocks()
- let count = 0
- nested.nestedNestedFoo.mockImplementation((to, from, next) => {
- expect(count++).toBe(0)
- next()
- })
- nested.nestedNested.mockImplementation((to, from, next) => {
- expect(count++).toBe(1)
- next()
- })
- nested.parent.mockImplementation((to, from, next) => {
- expect(count++).toBe(2)
- next()
- })
+ it('calls beforeRouteLeave guard on navigation between children', async () => {
+ const router = createRouter({ routes })
+ await router.push({ name: 'nested-path' })
+ resetMocks()
+ await router.push({ name: 'nested-path-b' })
+ expect(nested.nestedEmpty).not.toHaveBeenCalled()
+ expect(nested.nestedAbs).not.toHaveBeenCalled()
+ expect(nested.nestedB).not.toHaveBeenCalled()
+ expect(nested.nestedNestedFoo).not.toHaveBeenCalled()
+ expect(nested.parent).not.toHaveBeenCalled()
+ expect(nested.nestedNested).not.toHaveBeenCalled()
+ expect(nested.nestedA).toHaveBeenCalledTimes(1)
+ expect(nested.nestedA).toHaveBeenCalledWith(
+ expect.objectContaining({
+ name: 'nested-path-b',
+ fullPath: '/nested/b',
+ }),
+ expect.objectContaining({
+ name: 'nested-path',
+ fullPath: '/nested/a',
+ }),
+ expect.any(Function)
+ )
+ })
- await router[navigationMethod]('/')
- expect(nested.parent).toHaveBeenCalledTimes(1)
- expect(nested.nestedNested).toHaveBeenCalledTimes(1)
- expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
- })
+ it('calls beforeRouteLeave guard on navigation between children in order', async () => {
+ const router = createRouter({ routes })
+ await router.push({ name: 'nested-nested-foo' })
+ resetMocks()
+ let count = 0
+ nested.nestedNestedFoo.mockImplementation((to, from, next) => {
+ expect(count++).toBe(0)
+ next()
+ })
+ nested.nestedNested.mockImplementation((to, from, next) => {
+ expect(count++).toBe(1)
+ next()
+ })
+ nested.parent.mockImplementation((to, from, next) => {
+ expect(count++).toBe(2)
+ next()
+ })
- it('can cancel navigation', async () => {
- const router = createRouter({ routes })
- beforeRouteLeave.mockImplementationOnce(async (to, from, next) => {
- next(false)
- })
- await router.push('/guard')
- const p = router[navigationMethod]('/')
- const currentRoute = router.currentRoute.value
- expect(currentRoute.fullPath).toBe('/guard')
- await p.catch(err => {}) // catch the navigation abortion
- expect(currentRoute.fullPath).toBe('/guard')
- })
+ await router.push('/')
+ expect(nested.parent).toHaveBeenCalledTimes(1)
+ expect(nested.nestedNested).toHaveBeenCalledTimes(1)
+ expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
+ })
- it.todo('invokes with the component context')
- it.todo('invokes with the component context with named views')
- it.todo('invokes with the component context with nested views')
- it.todo('invokes with the component context with nested named views')
+ it('can cancel navigation', async () => {
+ const router = createRouter({ routes })
+ beforeRouteLeave.mockImplementationOnce(async (to, from, next) => {
+ next(false)
})
+ await router.push('/guard')
+ const p = router.push('/')
+ const currentRoute = router.currentRoute.value
+ expect(currentRoute.fullPath).toBe('/guard')
+ await p.catch(err => {}) // catch the navigation abortion
+ expect(currentRoute.fullPath).toBe('/guard')
})
+
+ it.todo('invokes with the component context')
+ it.todo('invokes with the component context with named views')
+ it.todo('invokes with the component context with nested views')
+ it.todo('invokes with the component context with nested named views')
})
import fakePromise from 'faked-promise'
-import { NAVIGATION_TYPES, createDom, noGuard } from '../utils'
+import { createDom, noGuard } from '../utils'
import { createRouter as newRouter, createWebHistory } from '../../src'
import { RouteRecord } from '../../src/types'
createDom()
})
- NAVIGATION_TYPES.forEach(navigationMethod => {
- describe(navigationMethod, () => {
- it('calls beforeRouteUpdate guards when changing params', async () => {
- const router = createRouter({ routes })
- beforeRouteUpdate.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/guard/valid')
- // not called on initial navigation
- expect(beforeRouteUpdate).not.toHaveBeenCalled()
- await router[navigationMethod]('/guard/other')
- expect(beforeRouteUpdate).toHaveBeenCalledTimes(1)
- })
-
- it('waits before navigating', async () => {
- const [promise, resolve] = fakePromise()
- const router = createRouter({ routes })
- beforeRouteUpdate.mockImplementationOnce(async (to, from, next) => {
- await promise
- next()
- })
- await router[navigationMethod]('/guard/one')
- const p = router[navigationMethod]('/guard/foo')
- expect(router.currentRoute.value.fullPath).toBe('/guard/one')
- resolve()
- await p
- expect(router.currentRoute.value.fullPath).toBe('/guard/foo')
- })
+ it('calls beforeRouteUpdate guards when changing params', async () => {
+ const router = createRouter({ routes })
+ beforeRouteUpdate.mockImplementationOnce(noGuard)
+ await router.push('/guard/valid')
+ // not called on initial navigation
+ expect(beforeRouteUpdate).not.toHaveBeenCalled()
+ await router.push('/guard/other')
+ expect(beforeRouteUpdate).toHaveBeenCalledTimes(1)
+ })
- it.todo('invokes with the component context')
- it.todo('invokes with the component context with named views')
- it.todo('invokes with the component context with nested views')
- it.todo('invokes with the component context with nested named views')
+ it('waits before navigating', async () => {
+ const [promise, resolve] = fakePromise()
+ const router = createRouter({ routes })
+ beforeRouteUpdate.mockImplementationOnce(async (to, from, next) => {
+ await promise
+ next()
})
+ await router.push('/guard/one')
+ const p = router.push('/guard/foo')
+ expect(router.currentRoute.value.fullPath).toBe('/guard/one')
+ resolve()
+ await p
+ expect(router.currentRoute.value.fullPath).toBe('/guard/foo')
})
+
+ it.todo('invokes with the component context')
+ it.todo('invokes with the component context with named views')
+ it.todo('invokes with the component context with nested views')
+ it.todo('invokes with the component context with nested named views')
})
-import { NAVIGATION_TYPES, createDom } from '../utils'
+import { createDom } from '../utils'
import { createWebHistory, createRouter as newRouter } from '../../src'
function createRouter(
createDom()
})
- NAVIGATION_TYPES.forEach(navigationMethod => {
- describe(navigationMethod, () => {
- it('calls afterEach guards on push', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- router.afterEach(spy)
- await router[navigationMethod]('/foo')
- expect(spy).toHaveBeenCalledTimes(1)
- expect(spy).toHaveBeenCalledWith(
- expect.objectContaining({ fullPath: '/foo' }),
- expect.objectContaining({ fullPath: '/' })
- )
- })
+ it('calls afterEach guards on push', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ router.afterEach(spy)
+ await router.push('/foo')
+ expect(spy).toHaveBeenCalledTimes(1)
+ expect(spy).toHaveBeenCalledWith(
+ expect.objectContaining({ fullPath: '/foo' }),
+ expect.objectContaining({ fullPath: '/' })
+ )
+ })
- it('can be removed', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- const remove = router.afterEach(spy)
- remove()
- await router[navigationMethod]('/foo')
- expect(spy).not.toHaveBeenCalled()
- })
+ it('can be removed', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ const remove = router.afterEach(spy)
+ remove()
+ await router.push('/foo')
+ expect(spy).not.toHaveBeenCalled()
+ })
- it('calls afterEach guards on push', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- await router.push('/nested')
- router.afterEach(spy)
- await router[navigationMethod]('/nested/home')
- expect(spy).toHaveBeenCalledTimes(1)
- expect(spy).toHaveBeenLastCalledWith(
- expect.objectContaining({ name: 'nested-home' }),
- expect.objectContaining({ name: 'nested-default' })
- )
- await router[navigationMethod]('/nested')
- expect(spy).toHaveBeenLastCalledWith(
- expect.objectContaining({ name: 'nested-default' }),
- expect.objectContaining({ name: 'nested-home' })
- )
- expect(spy).toHaveBeenCalledTimes(2)
- })
+ it('calls afterEach guards on push', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ await router.push('/nested')
+ router.afterEach(spy)
+ await router.push('/nested/home')
+ expect(spy).toHaveBeenCalledTimes(1)
+ expect(spy).toHaveBeenLastCalledWith(
+ expect.objectContaining({ name: 'nested-home' }),
+ expect.objectContaining({ name: 'nested-default' })
+ )
+ await router.push('/nested')
+ expect(spy).toHaveBeenLastCalledWith(
+ expect.objectContaining({ name: 'nested-default' }),
+ expect.objectContaining({ name: 'nested-home' })
+ )
+ expect(spy).toHaveBeenCalledTimes(2)
+ })
- it('does not call afterEach if navigation is cancelled', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- router.afterEach(spy)
- router.beforeEach((to, from, next) => {
- next(false) // cancel the navigation
- })
- await router[navigationMethod]('/foo').catch(err => {}) // ignore the error
- expect(spy).not.toHaveBeenCalled()
- })
+ it('does not call afterEach if navigation is cancelled', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ router.afterEach(spy)
+ router.beforeEach((to, from, next) => {
+ next(false) // cancel the navigation
})
+ await router.push('/foo').catch(err => {}) // ignore the error
+ expect(spy).not.toHaveBeenCalled()
})
})
import { RouterOptions } from '../../src/router'
import fakePromise from 'faked-promise'
-import { NAVIGATION_TYPES, createDom, tick, noGuard } from '../utils'
+import { createDom, tick, noGuard } from '../utils'
import { RouteRecord, RouteLocation } from '../../src/types'
import { createWebHistory, createRouter as newRouter } from '../../src'
createDom()
})
- NAVIGATION_TYPES.forEach(navigationMethod => {
- describe(navigationMethod, () => {
- it('calls beforeEach guards on navigation', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- router.beforeEach(spy)
- spy.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/foo')
- expect(spy).toHaveBeenCalledTimes(1)
- })
-
- it('can be removed', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- const remove = router.beforeEach(spy)
- remove()
- spy.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/foo')
- expect(spy).not.toHaveBeenCalled()
- })
-
- it('does not call beforeEach guard if we were already on the page', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- await router.push('/foo')
- router.beforeEach(spy)
- spy.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/foo')
- expect(spy).not.toHaveBeenCalled()
- })
-
- it('calls beforeEach guards on navigation between children routes', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- await router.push('/nested')
- router.beforeEach(spy)
- spy.mockImplementation(noGuard)
- await router[navigationMethod]('/nested/home')
- expect(spy).toHaveBeenCalledTimes(1)
- expect(spy).toHaveBeenLastCalledWith(
- expect.objectContaining({ name: 'nested-home' }),
- expect.objectContaining({ name: 'nested-default' }),
- expect.any(Function)
- )
- await router[navigationMethod]('/nested')
- expect(spy).toHaveBeenLastCalledWith(
- expect.objectContaining({ name: 'nested-default' }),
- expect.objectContaining({ name: 'nested-home' }),
- expect.any(Function)
- )
- expect(spy).toHaveBeenCalledTimes(2)
- })
-
- it('can redirect to a different location', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- await router.push('/foo')
- spy.mockImplementation((to, from, next) => {
- // only allow going to /other
- if (to.fullPath !== '/other') next('/other')
- else next()
- })
- router.beforeEach(spy)
- expect(spy).not.toHaveBeenCalled()
- await router[navigationMethod]('/')
- expect(spy).toHaveBeenCalledTimes(2)
- // called before redirect
- expect(spy).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({ path: '/' }),
- expect.objectContaining({ path: '/foo' }),
- expect.any(Function)
- )
- expect(spy).toHaveBeenNthCalledWith(
- 2,
- expect.objectContaining({ path: '/other' }),
- expect.objectContaining({ path: '/foo' }),
- expect.any(Function)
- )
- expect(router.currentRoute.value.fullPath).toBe('/other')
- })
-
- async function assertRedirect(redirectFn: (i: string) => RouteLocation) {
- const spy = jest.fn()
- const router = createRouter({ routes })
- await router.push('/')
- spy.mockImplementation((to, from, next) => {
- // only allow going to /other
- const i = Number(to.params.i)
- if (i >= 3) next()
- else next(redirectFn(String(i + 1)))
- })
- router.beforeEach(spy)
- expect(spy).not.toHaveBeenCalled()
- await router[navigationMethod]('/n/0')
- expect(spy).toHaveBeenCalledTimes(4)
- expect(router.currentRoute.value.fullPath).toBe('/n/3')
- }
-
- it('can redirect multiple times with string redirect', async () => {
- await assertRedirect(i => '/n/' + i)
- })
-
- it('can redirect multiple times with path object', async () => {
- await assertRedirect(i => ({ path: '/n/' + i }))
- })
-
- it('can redirect multiple times with named route', async () => {
- await assertRedirect(i => ({ name: 'n', params: { i } }))
- })
-
- it('is called when changing params', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes: [...routes] })
- await router.push('/n/2')
- spy.mockImplementation(noGuard)
- router.beforeEach(spy)
- spy.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/n/1')
- expect(spy).toHaveBeenCalledTimes(1)
- })
-
- it('is not called with same params', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes: [...routes] })
- await router.push('/n/2')
- spy.mockImplementation(noGuard)
- router.beforeEach(spy)
- spy.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/n/2')
- expect(spy).not.toHaveBeenCalled()
- })
-
- it('waits before navigating', async () => {
- const [promise, resolve] = fakePromise()
- const router = createRouter({ routes })
- router.beforeEach(async (to, from, next) => {
- await promise
- next()
- })
- const p = router[navigationMethod]('/foo')
- expect(router.currentRoute.value.fullPath).toBe('/')
- resolve()
- await p
- expect(router.currentRoute.value.fullPath).toBe('/foo')
- })
-
- it('waits in the right order', async () => {
- const [p1, r1] = fakePromise()
- const [p2, r2] = fakePromise()
- const router = createRouter({ routes })
- const guard1 = jest.fn()
- let order = 0
- guard1.mockImplementationOnce(async (to, from, next) => {
- expect(order++).toBe(0)
- await p1
- next()
- })
- router.beforeEach(guard1)
- const guard2 = jest.fn()
- guard2.mockImplementationOnce(async (to, from, next) => {
- expect(order++).toBe(1)
- await p2
- next()
- })
- router.beforeEach(guard2)
- let navigation = router[navigationMethod]('/foo')
- expect(router.currentRoute.value.fullPath).toBe('/')
- expect(guard1).not.toHaveBeenCalled()
- expect(guard2).not.toHaveBeenCalled()
- r1() // resolve the first guard
- await tick() // wait a tick
- await tick() // mocha requires an extra tick here
- expect(guard1).toHaveBeenCalled()
- // we haven't resolved the second gurad yet
- expect(router.currentRoute.value.fullPath).toBe('/')
- r2()
- await navigation
- expect(guard2).toHaveBeenCalled()
- expect(router.currentRoute.value.fullPath).toBe('/foo')
- })
-
- it('adds meta information', async () => {
- const spy = jest.fn()
- const router = createRouter({ routes })
- router.beforeEach(spy)
- spy.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/n/2')
- expect(spy).toHaveBeenCalledTimes(1)
- expect(spy).toHaveBeenCalledWith(
- expect.objectContaining({ meta: { requiresLogin: true } }),
- expect.objectContaining({ meta: {} }),
- expect.any(Function)
- )
- })
+ it('calls beforeEach guards on navigation', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ router.beforeEach(spy)
+ spy.mockImplementationOnce(noGuard)
+ await router.push('/foo')
+ expect(spy).toHaveBeenCalledTimes(1)
+ })
+
+ it('can be removed', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ const remove = router.beforeEach(spy)
+ remove()
+ spy.mockImplementationOnce(noGuard)
+ await router.push('/foo')
+ expect(spy).not.toHaveBeenCalled()
+ })
+
+ it('does not call beforeEach guard if we were already on the page', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ await router.push('/foo')
+ router.beforeEach(spy)
+ spy.mockImplementationOnce(noGuard)
+ await router.push('/foo')
+ expect(spy).not.toHaveBeenCalled()
+ })
+
+ it('calls beforeEach guards on navigation between children routes', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ await router.push('/nested')
+ router.beforeEach(spy)
+ spy.mockImplementation(noGuard)
+ await router.push('/nested/home')
+ expect(spy).toHaveBeenCalledTimes(1)
+ expect(spy).toHaveBeenLastCalledWith(
+ expect.objectContaining({ name: 'nested-home' }),
+ expect.objectContaining({ name: 'nested-default' }),
+ expect.any(Function)
+ )
+ await router.push('/nested')
+ expect(spy).toHaveBeenLastCalledWith(
+ expect.objectContaining({ name: 'nested-default' }),
+ expect.objectContaining({ name: 'nested-home' }),
+ expect.any(Function)
+ )
+ expect(spy).toHaveBeenCalledTimes(2)
+ })
+
+ it('can redirect to a different location', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ await router.push('/foo')
+ spy.mockImplementation((to, from, next) => {
+ // only allow going to /other
+ if (to.fullPath !== '/other') next('/other')
+ else next()
+ })
+ router.beforeEach(spy)
+ expect(spy).not.toHaveBeenCalled()
+ await router.push('/')
+ expect(spy).toHaveBeenCalledTimes(2)
+ // called before redirect
+ expect(spy).toHaveBeenNthCalledWith(
+ 1,
+ expect.objectContaining({ path: '/' }),
+ expect.objectContaining({ path: '/foo' }),
+ expect.any(Function)
+ )
+ expect(spy).toHaveBeenNthCalledWith(
+ 2,
+ expect.objectContaining({ path: '/other' }),
+ expect.objectContaining({ path: '/foo' }),
+ expect.any(Function)
+ )
+ expect(router.currentRoute.value.fullPath).toBe('/other')
+ })
+
+ async function assertRedirect(redirectFn: (i: string) => RouteLocation) {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ await router.push('/')
+ spy.mockImplementation((to, from, next) => {
+ // only allow going to /other
+ const i = Number(to.params.i)
+ if (i >= 3) next()
+ else next(redirectFn(String(i + 1)))
+ })
+ router.beforeEach(spy)
+ expect(spy).not.toHaveBeenCalled()
+ await router.push('/n/0')
+ expect(spy).toHaveBeenCalledTimes(4)
+ expect(router.currentRoute.value.fullPath).toBe('/n/3')
+ }
+
+ it('can redirect multiple times with string redirect', async () => {
+ await assertRedirect(i => '/n/' + i)
+ })
+
+ it('can redirect multiple times with path object', async () => {
+ await assertRedirect(i => ({ path: '/n/' + i }))
+ })
+
+ it('can redirect multiple times with named route', async () => {
+ await assertRedirect(i => ({ name: 'n', params: { i } }))
+ })
+
+ it('is called when changing params', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes: [...routes] })
+ await router.push('/n/2')
+ spy.mockImplementation(noGuard)
+ router.beforeEach(spy)
+ spy.mockImplementationOnce(noGuard)
+ await router.push('/n/1')
+ expect(spy).toHaveBeenCalledTimes(1)
+ })
+
+ it('is not called with same params', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes: [...routes] })
+ await router.push('/n/2')
+ spy.mockImplementation(noGuard)
+ router.beforeEach(spy)
+ spy.mockImplementationOnce(noGuard)
+ await router.push('/n/2')
+ expect(spy).not.toHaveBeenCalled()
+ })
+
+ it('waits before navigating', async () => {
+ const [promise, resolve] = fakePromise()
+ const router = createRouter({ routes })
+ router.beforeEach(async (to, from, next) => {
+ await promise
+ next()
+ })
+ const p = router.push('/foo')
+ expect(router.currentRoute.value.fullPath).toBe('/')
+ resolve()
+ await p
+ expect(router.currentRoute.value.fullPath).toBe('/foo')
+ })
+
+ it('waits in the right order', async () => {
+ const [p1, r1] = fakePromise()
+ const [p2, r2] = fakePromise()
+ const router = createRouter({ routes })
+ const guard1 = jest.fn()
+ let order = 0
+ guard1.mockImplementationOnce(async (to, from, next) => {
+ expect(order++).toBe(0)
+ await p1
+ next()
})
+ router.beforeEach(guard1)
+ const guard2 = jest.fn()
+ guard2.mockImplementationOnce(async (to, from, next) => {
+ expect(order++).toBe(1)
+ await p2
+ next()
+ })
+ router.beforeEach(guard2)
+ let navigation = router.push('/foo')
+ expect(router.currentRoute.value.fullPath).toBe('/')
+ expect(guard1).not.toHaveBeenCalled()
+ expect(guard2).not.toHaveBeenCalled()
+ r1() // resolve the first guard
+ await tick() // wait a tick
+ await tick() // mocha requires an extra tick here
+ expect(guard1).toHaveBeenCalled()
+ // we haven't resolved the second gurad yet
+ expect(router.currentRoute.value.fullPath).toBe('/')
+ r2()
+ await navigation
+ expect(guard2).toHaveBeenCalled()
+ expect(router.currentRoute.value.fullPath).toBe('/foo')
+ })
+
+ it('adds meta information', async () => {
+ const spy = jest.fn()
+ const router = createRouter({ routes })
+ router.beforeEach(spy)
+ spy.mockImplementationOnce(noGuard)
+ await router.push('/n/2')
+ expect(spy).toHaveBeenCalledTimes(1)
+ expect(spy).toHaveBeenCalledWith(
+ expect.objectContaining({ meta: { requiresLogin: true } }),
+ expect.objectContaining({ meta: {} }),
+ expect.any(Function)
+ )
})
})
import { RouterOptions, createRouter as newRouter } from '../../src/router'
import fakePromise from 'faked-promise'
-import { NAVIGATION_TYPES, createDom, noGuard, tick } from '../utils'
+import { createDom, noGuard, tick } from '../utils'
import { RouteRecord } from '../../src/types'
import { createWebHistory } from '../../src'
createDom()
})
- NAVIGATION_TYPES.forEach(navigationMethod => {
- describe(navigationMethod, () => {
- it('calls beforeEnter guards on navigation', async () => {
- const router = createRouter({ routes })
- beforeEnter.mockImplementationOnce(noGuard)
- await router[navigationMethod]('/guard/valid')
- expect(beforeEnter).toHaveBeenCalledTimes(1)
- })
+ it('calls beforeEnter guards on navigation', async () => {
+ const router = createRouter({ routes })
+ beforeEnter.mockImplementationOnce(noGuard)
+ await router.push('/guard/valid')
+ expect(beforeEnter).toHaveBeenCalledTimes(1)
+ })
- it('supports an array of beforeEnter', async () => {
- const router = createRouter({ routes })
- await router[navigationMethod]('/multiple')
- expect(beforeEnters[0]).toHaveBeenCalledTimes(1)
- expect(beforeEnters[1]).toHaveBeenCalledTimes(1)
- expect(beforeEnters[0]).toHaveBeenCalledWith(
- expect.objectContaining({ path: '/multiple' }),
- expect.objectContaining({ path: '/' }),
- expect.any(Function)
- )
- })
+ it('supports an array of beforeEnter', async () => {
+ const router = createRouter({ routes })
+ await router.push('/multiple')
+ expect(beforeEnters[0]).toHaveBeenCalledTimes(1)
+ expect(beforeEnters[1]).toHaveBeenCalledTimes(1)
+ expect(beforeEnters[0]).toHaveBeenCalledWith(
+ expect.objectContaining({ path: '/multiple' }),
+ expect.objectContaining({ path: '/' }),
+ expect.any(Function)
+ )
+ })
- it('call beforeEnter in nested views', async () => {
- const router = createRouter({ routes })
- await router.push('/nested/a')
- resetMocks()
- await router[navigationMethod]('/nested/nested/foo')
- expect(nested.parent).not.toHaveBeenCalled()
- expect(nested.nestedA).not.toHaveBeenCalled()
- expect(nested.nestedNested).toHaveBeenCalledTimes(1)
- expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
- expect(nested.nestedNested).toHaveBeenCalledWith(
- expect.objectContaining({ path: '/nested/nested/foo' }),
- expect.objectContaining({ path: '/nested/a' }),
- expect.any(Function)
- )
- expect(nested.nestedNestedFoo).toHaveBeenCalledWith(
- expect.objectContaining({ path: '/nested/nested/foo' }),
- expect.objectContaining({ path: '/nested/a' }),
- expect.any(Function)
- )
- })
+ it('call beforeEnter in nested views', async () => {
+ const router = createRouter({ routes })
+ await router.push('/nested/a')
+ resetMocks()
+ await router.push('/nested/nested/foo')
+ expect(nested.parent).not.toHaveBeenCalled()
+ expect(nested.nestedA).not.toHaveBeenCalled()
+ expect(nested.nestedNested).toHaveBeenCalledTimes(1)
+ expect(nested.nestedNestedFoo).toHaveBeenCalledTimes(1)
+ expect(nested.nestedNested).toHaveBeenCalledWith(
+ expect.objectContaining({ path: '/nested/nested/foo' }),
+ expect.objectContaining({ path: '/nested/a' }),
+ expect.any(Function)
+ )
+ expect(nested.nestedNestedFoo).toHaveBeenCalledWith(
+ expect.objectContaining({ path: '/nested/nested/foo' }),
+ expect.objectContaining({ path: '/nested/a' }),
+ expect.any(Function)
+ )
+ })
- it('calls beforeEnter different records, same component', async () => {
- const router = createRouter({ routes })
- beforeEnter.mockImplementationOnce(noGuard)
- await router.push('/')
- expect(beforeEnter).not.toHaveBeenCalled()
- await router[navigationMethod]('/home')
- expect(beforeEnter).toHaveBeenCalledTimes(1)
- })
+ it('calls beforeEnter different records, same component', async () => {
+ const router = createRouter({ routes })
+ beforeEnter.mockImplementationOnce(noGuard)
+ await router.push('/')
+ expect(beforeEnter).not.toHaveBeenCalled()
+ await router.push('/home')
+ expect(beforeEnter).toHaveBeenCalledTimes(1)
+ })
- it('does not call beforeEnter guard if we were already on the page', async () => {
- const router = createRouter({ routes })
- beforeEnter.mockImplementation(noGuard)
- await router.push('/guard/one')
- expect(beforeEnter).toHaveBeenCalledTimes(1)
- await router[navigationMethod]('/guard/one')
- expect(beforeEnter).toHaveBeenCalledTimes(1)
- })
+ it('does not call beforeEnter guard if we were already on the page', async () => {
+ const router = createRouter({ routes })
+ beforeEnter.mockImplementation(noGuard)
+ await router.push('/guard/one')
+ expect(beforeEnter).toHaveBeenCalledTimes(1)
+ await router.push('/guard/one')
+ expect(beforeEnter).toHaveBeenCalledTimes(1)
+ })
- it('waits before navigating', async () => {
- const [promise, resolve] = fakePromise()
- const router = createRouter({ routes })
- beforeEnter.mockImplementationOnce(async (to, from, next) => {
- await promise
- next()
- })
- const p = router[navigationMethod]('/foo')
- expect(router.currentRoute.value.fullPath).toBe('/')
- resolve()
- await p
- expect(router.currentRoute.value.fullPath).toBe('/foo')
- })
+ it('waits before navigating', async () => {
+ const [promise, resolve] = fakePromise()
+ const router = createRouter({ routes })
+ beforeEnter.mockImplementationOnce(async (to, from, next) => {
+ await promise
+ next()
+ })
+ const p = router.push('/foo')
+ expect(router.currentRoute.value.fullPath).toBe('/')
+ resolve()
+ await p
+ expect(router.currentRoute.value.fullPath).toBe('/foo')
+ })
- it('waits before navigating in an array of beforeEnter', async () => {
- const [p1, r1] = fakePromise()
- const [p2, r2] = fakePromise()
- const router = createRouter({ routes })
- beforeEnters[0].mockImplementationOnce(async (to, from, next) => {
- await p1
- next()
- })
- beforeEnters[1].mockImplementationOnce(async (to, from, next) => {
- await p2
- next()
- })
- const p = router[navigationMethod]('/multiple')
- expect(router.currentRoute.value.fullPath).toBe('/')
- expect(beforeEnters[1]).not.toHaveBeenCalled()
- r1()
- await p1
- await tick()
- r2()
- await p
- expect(router.currentRoute.value.fullPath).toBe('/multiple')
- })
+ it('waits before navigating in an array of beforeEnter', async () => {
+ const [p1, r1] = fakePromise()
+ const [p2, r2] = fakePromise()
+ const router = createRouter({ routes })
+ beforeEnters[0].mockImplementationOnce(async (to, from, next) => {
+ await p1
+ next()
+ })
+ beforeEnters[1].mockImplementationOnce(async (to, from, next) => {
+ await p2
+ next()
})
+ const p = router.push('/multiple')
+ expect(router.currentRoute.value.fullPath).toBe('/')
+ expect(beforeEnters[1]).not.toHaveBeenCalled()
+ r1()
+ await p1
+ await tick()
+ r2()
+ await p
+ expect(router.currentRoute.value.fullPath).toBe('/multiple')
})
})
)
})
+ it('can replaces current location with a string location', async () => {
+ const { router, history } = await newRouter()
+ jest.spyOn(history, 'replace')
+ await router.replace('/foo')
+ expect(history.replace).toHaveBeenCalledTimes(1)
+ expect(history.replace).toHaveBeenCalledWith(
+ expect.objectContaining({
+ fullPath: '/foo',
+ path: '/foo',
+ query: {},
+ hash: '',
+ }),
+ undefined
+ )
+ })
+
+ it('can replaces current location with an object location', async () => {
+ const { router, history } = await newRouter()
+ jest.spyOn(history, 'replace')
+ await router.replace({ path: '/foo' })
+ expect(history.replace).toHaveBeenCalledTimes(1)
+ expect(history.replace).toHaveBeenCalledWith(
+ expect.objectContaining({
+ fullPath: '/foo',
+ path: '/foo',
+ query: {},
+ hash: '',
+ }),
+ undefined
+ )
+ })
+
it('navigates if the location does not exist', async () => {
const { router } = await newRouter()
const spy = jest.fn((to, from, next) => next())
}
}
-export type NAVIGATION_METHOD = 'push' | 'replace'
-export const NAVIGATION_TYPES: NAVIGATION_METHOD[] = ['push', 'replace']
-
export interface RouteRecordViewLoose
extends Pick<
RouteRecordMultipleViews,
return pushWithRedirect(to, undefined)
}
+ function replace(to: RouteLocation | RouteLocationNormalized) {
+ const location = typeof to === 'string' ? { path: to } : to
+ return push({ ...location, replace: true })
+ }
+
async function pushWithRedirect(
to: RouteLocation | RouteLocationNormalized,
redirectedFrom: RouteLocationNormalized | undefined
return currentRoute.value
}
- function replace(to: RouteLocation | RouteLocationNormalized) {
- const location = typeof to === 'string' ? { path: to } : to
- return push({ ...location, replace: true })
- }
-
async function navigate(
to: RouteLocationNormalized,
from: RouteLocationNormalizedResolved