Skip to content

Commit

Permalink
feat(guards): support errors in navigation guards
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Apr 8, 2020
1 parent 58d9c23 commit 23ed08d
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 5 deletions.
111 changes: 111 additions & 0 deletions __tests__/guards/navigationGuards.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { guardToPromiseFn } from '../../src/navigationGuards'
import { START_LOCATION_NORMALIZED } from '../../src/types'
import { ErrorTypes } from '../../src/errors'

// stub those two
const to = START_LOCATION_NORMALIZED
const from = {
...START_LOCATION_NORMALIZED,
path: '/other',
fullPath: '/other',
}

describe('guardToPromiseFn', () => {
it('calls the guard with to, from and, next', async () => {
const spy = jest.fn((to, from, next) => next())
await expect(guardToPromiseFn(spy, to, from)()).resolves
expect(spy).toHaveBeenCalledWith(to, from, expect.any(Function))
})

it('resolves if next is called with no arguments', async () => {
await expect(guardToPromiseFn((to, from, next) => next(), to, from)())
.resolves
})

it('resolves if next is called with true', async () => {
await expect(guardToPromiseFn((to, from, next) => next(true), to, from)())
.resolves
})

it('rejects if next is called with false', async () => {
expect.assertions(1)
try {
await guardToPromiseFn((to, from, next) => next(false), to, from)()
} catch (err) {
expect(err).toMatchObject({
from,
to,
type: ErrorTypes.NAVIGATION_ABORTED,
})
}
})

it('rejects if next is called with a string location', async () => {
expect.assertions(1)
try {
await guardToPromiseFn((to, from, next) => next('/new'), to, from)()
} catch (err) {
expect(err).toMatchObject({
from: to,
to: '/new',
type: ErrorTypes.NAVIGATION_GUARD_REDIRECT,
})
}
})

it('rejects if next is called with an object location', async () => {
expect.assertions(1)
let redirectTo = { path: '/new' }
try {
await guardToPromiseFn((to, from, next) => next(redirectTo), to, from)()
} catch (err) {
expect(err).toMatchObject({
from: to,
to: redirectTo,
type: ErrorTypes.NAVIGATION_GUARD_REDIRECT,
})
}
})

it('rejects if next is called with an error', async () => {
expect.assertions(1)
let error = new Error('nope')
try {
await guardToPromiseFn((to, from, next) => next(error), to, from)()
} catch (err) {
expect(err).toBe(error)
}
})

it('rejects if guard rejects a Promise', async () => {
expect.assertions(1)
let error = new Error('nope')
try {
await guardToPromiseFn(
async (to, from, next) => {
throw error
},
to,
from
)()
} catch (err) {
expect(err).toBe(error)
}
})

it('rejects if guard throws an error', async () => {
expect.assertions(1)
let error = new Error('nope')
try {
await guardToPromiseFn(
(to, from, next) => {
throw error
},
to,
from
)()
} catch (err) {
expect(err).toBe(error)
}
})
})
10 changes: 5 additions & 5 deletions src/navigationGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function guardToPromiseFn<
return () =>
new Promise((resolve, reject) => {
const next: NavigationGuardCallback = (
valid?: boolean | RouteLocationRaw | NavigationGuardNextCallback
valid?: boolean | RouteLocationRaw | NavigationGuardNextCallback | Error
) => {
if (valid === false)
reject(
Expand All @@ -68,7 +68,9 @@ export function guardToPromiseFn<
to,
})
)
else if (isRouteLocation(valid)) {
else if (valid instanceof Error) {
reject(valid)
} else if (isRouteLocation(valid)) {
reject(
createRouterError<NavigationRedirectError>(
ErrorTypes.NAVIGATION_GUARD_REDIRECT,
Expand All @@ -78,16 +80,14 @@ export function guardToPromiseFn<
}
)
)
} else if (valid instanceof Error) {
// TODO
} else {
// TODO: call the in component enter callbacks. Maybe somewhere else
// record && record.enterCallbacks.push(valid)
resolve()
}
}

// TODO: write tests
// wrapping with Promise.resolve allows it to work with both async and sync guards
Promise.resolve(guard.call(instance, to, from, next)).catch(err =>
reject(err)
)
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ export interface MatcherLocation
// where the navigation guard callback is defined
export interface NavigationGuardCallback {
(): void
(error: Error): void
(location: RouteLocationRaw): void
(valid: boolean): void
(cb: (vm: any) => void): void
Expand Down

0 comments on commit 23ed08d

Please sign in to comment.