Skip to content

Commit

Permalink
feat(warn): warn if guard returns without calling next
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Jul 31, 2020
1 parent 267c9fa commit 6e16bdd
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 0 deletions.
17 changes: 17 additions & 0 deletions __tests__/guards/guardToPromiseFn.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { guardToPromiseFn } from '../../src/navigationGuards'
import { START_LOCATION_NORMALIZED } from '../../src/types'
import { ErrorTypes } from '../../src/errors'
import { mockWarn } from 'jest-mock-warn'

// stub those two
const to = START_LOCATION_NORMALIZED
Expand All @@ -11,6 +12,7 @@ const from = {
}

describe('guardToPromiseFn', () => {
mockWarn()
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.toEqual(undefined)
Expand Down Expand Up @@ -232,4 +234,19 @@ describe('guardToPromiseFn', () => {
}
})
})

it('warns if guard resolves without calling next', async () => {
expect.assertions(2)
await expect(
guardToPromiseFn((to, from, next) => false, to, from)()
).rejects.toEqual(expect.any(Error))

// try {
// await guardToPromiseFn((to, from, next) => false, to, from)()
// } catch (error) {
// expect(error).toEqual(expect.any(Error))
// }

expect('callback was never called').toHaveBeenWarned()
})
})
13 changes: 13 additions & 0 deletions src/navigationGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ export function guardToPromiseFn(
)

if (guard.length < 3) guardCall.then(next)
if (__DEV__ && guard.length > 2)
guardCall.then(() => {
// @ts-ignore: _called is added at canOnlyBeCalledOnce
if (!next._called)
warn(
`The "next" callback was never called inside of ${
guard.name ? '"' + guard.name + '"' : ''
}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`
)
reject(new Error('Invalid navigation guard'))
})
guardCall.catch(err => reject(err))
})
}
Expand All @@ -180,6 +191,8 @@ function canOnlyBeCalledOnce(
warn(
`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`
)
// @ts-ignore: we put it in the original one because it's easier to check
next._called = true
if (called === 1) next.apply(null, arguments as any)
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,13 @@ export interface NavigationGuardNext {
(location: RouteLocationRaw): void
(valid: boolean): void
(cb: NavigationGuardNextCallback): void
/**
* Allows to detect if `next` isn't called in a resolved guard. Used
* internally in DEV mode to emit a warning. Commented out to simplify
* typings.
* @internal
*/
// _called: boolean
}

export type NavigationGuardNextCallback = (vm: ComponentPublicInstance) => any
Expand Down

0 comments on commit 6e16bdd

Please sign in to comment.