Skip to content

Commit

Permalink
feat: add pending navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Nov 10, 2020
1 parent 600bc95 commit 5fc6beb
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 25 deletions.
27 changes: 23 additions & 4 deletions __tests__/navigations.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { mount } from '@vue/test-utils'
import { NavigationFailureType } from 'vue-router'
import { addGlobalInjections, createMockedRouter } from '../src'
import Test from './fixtures/Test'

Expand Down Expand Up @@ -32,14 +33,32 @@ describe('Navigations', () => {
it('rejects next navigation with an error', async () => {
const wrapper = mount(Test)
const error = new Error('fail')
router.failNextNavigation(error)
router.setNextGuardReturn(error)
await expect(wrapper.vm.$router.push('/foo')).rejects.toBe(error)
})

it('can abort the next navigation', async () => {
const wrapper = mount(Test)
const error = new Error('fail')
router.failNextNavigation(error)
expect(wrapper.vm.$router.push).toHaveBeenCalledTimes(0)
router.setNextGuardReturn(false)
await expect(wrapper.vm.$router.push('/foo')).resolves.toMatchObject({
type: NavigationFailureType.aborted,
})
})

it('can redirect the next navigation', async () => {
const wrapper = mount(Test)
router.setNextGuardReturn('/bar')
await expect(wrapper.vm.$router.push('/foo')).resolves.toBe(undefined)
expect(wrapper.text()).toBe('/bar')
})

it.skip('can wait for an ongoing navigation', async () => {
const wrapper = mount(Test)

// to force async navigation
router.setNextGuardReturn('/bar')
wrapper.vm.$router.push('/foo')
await expect(router.getPendingNavigation()).resolves.toBe(undefined)
expect(wrapper.text()).toBe('/bar')
})
})
53 changes: 32 additions & 21 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
createRouter,
createWebHashHistory,
isNavigationFailure,
routeLocationKey,
RouteLocationNormalizedLoaded,
RouteLocationRaw,
Expand Down Expand Up @@ -46,14 +45,14 @@ export function createMockedRouter() {
],
})

const { push } = router

const pushMock = jest.fn((to) => {
router.currentRoute.value = router.resolve(to)
return consumeNavigationFailure()
return consumeNextReturn(to)
})

const replaceMock = jest.fn((to) => {
router.currentRoute.value = router.resolve(to)
return consumeNavigationFailure()
return consumeNextReturn({ ...to, replace: true })
})

router.push = pushMock
Expand All @@ -64,33 +63,45 @@ export function createMockedRouter() {
replaceMock.mockClear()
})

let nextFailure: Error | boolean | RouteLocationRaw | undefined = undefined
let nextReturn: Error | boolean | RouteLocationRaw | undefined = undefined

function failNextNavigation(
failure: Error | boolean | RouteLocationRaw | undefined
function setNextGuardReturn(
returnValue: Error | boolean | RouteLocationRaw | undefined
) {
nextFailure = failure
nextReturn = returnValue
}

function consumeNavigationFailure() {
let p: Promise<any> = Promise.resolve()

if (nextFailure) {
if (isNavigationFailure(nextFailure)) {
p = Promise.resolve(nextFailure)
} else {
p = Promise.reject(nextFailure)
}
function consumeNextReturn(to: RouteLocationRaw) {
if (nextReturn != null) {
const removeGuard = router.beforeEach(() => {
const value = nextReturn
removeGuard()
nextReturn = undefined
return value
})
pendingNavigation = push(to)
pendingNavigation
.catch(() => {})
.finally(() => {
pendingNavigation = undefined
})
return pendingNavigation
}

nextFailure = undefined
// NOTE: should we trigger a push to reset the internal pending navigation of the router?
router.currentRoute.value = router.resolve(to)
return Promise.resolve()
}

return p
let pendingNavigation: ReturnType<typeof push> | undefined
function getPendingNavigation() {
return pendingNavigation || Promise.resolve()
}

return {
...router,
failNextNavigation,
setNextGuardReturn,
getPendingNavigation,
}
}

Expand Down

0 comments on commit 5fc6beb

Please sign in to comment.