]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat(warn): warn if guard returns without calling next
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 31 Jul 2020 14:26:18 +0000 (16:26 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 31 Jul 2020 14:26:18 +0000 (16:26 +0200)
__tests__/guards/guardToPromiseFn.spec.ts
src/navigationGuards.ts
src/types/index.ts

index c593c4d61642c95ffcd5de5e8808efa273ff45cb..471502d80dac2739b42783423b955409b8377dbf 100644 (file)
@@ -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
@@ -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)
@@ -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()
+  })
 })
index d618f34ce2509387de172c9b68564ce242ac5624..e2e35c6da718b5bb8abf9bdec4feb222b3475925 100644 (file)
@@ -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))
     })
 }
@@ -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)
   }
 }
index 629219e814c913e7702b8b1c7a0c889611c5ab14..fd9e14a2f94b406638a78b7c825ec05847558b92 100644 (file)
@@ -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