From: Eduardo San Martin Morote Date: Fri, 27 Mar 2020 14:52:22 +0000 (+0100) Subject: test: remove duplicated cases for replace X-Git-Tag: v4.0.0-alpha.4~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0bea0b7501d79942a3622a26f139ea6edccbc32a;p=thirdparty%2Fvuejs%2Frouter.git test: remove duplicated cases for replace --- diff --git a/__tests__/guards/component-beforeRouteEnter.spec.ts b/__tests__/guards/component-beforeRouteEnter.spec.ts index 5f4f48bb..9dba2910 100644 --- a/__tests__/guards/component-beforeRouteEnter.spec.ts +++ b/__tests__/guards/component-beforeRouteEnter.spec.ts @@ -1,6 +1,6 @@ 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' @@ -121,129 +121,125 @@ describe('beforeRouteEnter', () => { 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() }) }) diff --git a/__tests__/guards/component-beforeRouteLeave.spec.ts b/__tests__/guards/component-beforeRouteLeave.spec.ts index f8867679..9a5ce649 100644 --- a/__tests__/guards/component-beforeRouteLeave.spec.ts +++ b/__tests__/guards/component-beforeRouteLeave.spec.ts @@ -1,5 +1,5 @@ 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' @@ -103,87 +103,83 @@ describe('beforeRouteLeave', () => { 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') }) diff --git a/__tests__/guards/component-beforeRouteUpdate.spec.ts b/__tests__/guards/component-beforeRouteUpdate.spec.ts index f832179c..6250b8fa 100644 --- a/__tests__/guards/component-beforeRouteUpdate.spec.ts +++ b/__tests__/guards/component-beforeRouteUpdate.spec.ts @@ -1,5 +1,5 @@ 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' @@ -39,37 +39,33 @@ describe('beforeRouteUpdate', () => { 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') }) diff --git a/__tests__/guards/global-after.spec.ts b/__tests__/guards/global-after.spec.ts index 3b6a2039..a7e55c90 100644 --- a/__tests__/guards/global-after.spec.ts +++ b/__tests__/guards/global-after.spec.ts @@ -1,4 +1,4 @@ -import { NAVIGATION_TYPES, createDom } from '../utils' +import { createDom } from '../utils' import { createWebHistory, createRouter as newRouter } from '../../src' function createRouter( @@ -35,58 +35,54 @@ describe('router.afterEach', () => { 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() }) }) diff --git a/__tests__/guards/global-beforeEach.spec.ts b/__tests__/guards/global-beforeEach.spec.ts index 58df9ed3..8ab1421a 100644 --- a/__tests__/guards/global-beforeEach.spec.ts +++ b/__tests__/guards/global-beforeEach.spec.ts @@ -1,6 +1,6 @@ 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' @@ -37,201 +37,197 @@ describe('router.beforeEach', () => { 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) + ) }) }) diff --git a/__tests__/guards/route-beforeEnter.spec.ts b/__tests__/guards/route-beforeEnter.spec.ts index 58ed8918..a69c8396 100644 --- a/__tests__/guards/route-beforeEnter.spec.ts +++ b/__tests__/guards/route-beforeEnter.spec.ts @@ -1,6 +1,6 @@ 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' @@ -106,102 +106,98 @@ describe('beforeEnter', () => { 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') }) }) diff --git a/__tests__/router.spec.ts b/__tests__/router.spec.ts index 73db6546..6617fa67 100644 --- a/__tests__/router.spec.ts +++ b/__tests__/router.spec.ts @@ -149,6 +149,38 @@ describe('Router', () => { ) }) + 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()) diff --git a/__tests__/utils.ts b/__tests__/utils.ts index f9023308..06120165 100644 --- a/__tests__/utils.ts +++ b/__tests__/utils.ts @@ -21,9 +21,6 @@ export async function ticks(n: number) { } } -export type NAVIGATION_METHOD = 'push' | 'replace' -export const NAVIGATION_TYPES: NAVIGATION_METHOD[] = ['push', 'replace'] - export interface RouteRecordViewLoose extends Pick< RouteRecordMultipleViews, diff --git a/src/router.ts b/src/router.ts index 89907ee1..22182c09 100644 --- a/src/router.ts +++ b/src/router.ts @@ -213,6 +213,11 @@ export function createRouter({ 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 @@ -266,11 +271,6 @@ export function createRouter({ 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