]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: add navigation duplicated failure
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 24 Apr 2020 14:46:27 +0000 (16:46 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 24 Apr 2020 14:46:27 +0000 (16:46 +0200)
__tests__/errors.spec.ts
src/errors.ts
src/router.ts

index 305d77637b110199fdc1d7cb29820be992530a70..52c5cfd89331341e4b5a3338ff1e907dab1abf35 100644 (file)
@@ -39,6 +39,30 @@ describe('Errors & Navigation failures', () => {
     )
   })
 
+  it('Duplicated navigation triggers afterEach', async () => {
+    let expectedFailure = expect.objectContaining({
+      type: NavigationFailureType.duplicated,
+      to: expect.objectContaining({ path: '/' }),
+      from: expect.objectContaining({ path: '/' }),
+    })
+
+    const { router } = createRouter()
+
+    await expect(router.push('/')).resolves.toEqual(undefined)
+    expect(afterEach).toHaveBeenCalledTimes(1)
+    expect(onError).toHaveBeenCalledTimes(0)
+
+    await expect(router.push('/')).resolves.toEqual(expectedFailure)
+    expect(afterEach).toHaveBeenCalledTimes(2)
+    expect(onError).toHaveBeenCalledTimes(0)
+
+    expect(afterEach).toHaveBeenCalledWith(
+      expect.any(Object),
+      expect.any(Object),
+      expectedFailure
+    )
+  })
+
   it('next("/location") triggers afterEach', async () => {
     await testNavigation(
       ((to, from, next) => {
index 1685921a3ac945dc9ed7d57ee5d6a522cdf0c7c0..7772a7d02884abf607d6d016d742bb189edd59c7 100644 (file)
@@ -10,12 +10,7 @@ export const enum ErrorTypes {
   NAVIGATION_GUARD_REDIRECT,
   NAVIGATION_ABORTED,
   NAVIGATION_CANCELLED,
-  // Using string enums because error codes are exposed to developers
-  // and number enums could collide with other error codes in runtime
-  // MATCHER_NOT_FOUND = 'MATCHER_NOT_FOUND',
-  // NAVIGATION_GUARD_REDIRECT = 'NAVIGATION_GUARD_REDIRECT',
-  // NAVIGATION_ABORTED = 'NAVIGATION_ABORTED',
-  // NAVIGATION_CANCELLED = 'NAVIGATION_CANCELLED',
+  NAVIGATION_DUPLICATED,
 }
 
 interface RouterErrorBase extends Error {
@@ -31,9 +26,13 @@ export interface MatcherError extends RouterErrorBase {
 export enum NavigationFailureType {
   cancelled = ErrorTypes.NAVIGATION_CANCELLED,
   aborted = ErrorTypes.NAVIGATION_ABORTED,
+  duplicated = ErrorTypes.NAVIGATION_DUPLICATED,
 }
 export interface NavigationFailure extends RouterErrorBase {
-  type: ErrorTypes.NAVIGATION_ABORTED | ErrorTypes.NAVIGATION_CANCELLED
+  type:
+    | ErrorTypes.NAVIGATION_CANCELLED
+    | ErrorTypes.NAVIGATION_ABORTED
+    | ErrorTypes.NAVIGATION_DUPLICATED
   from: RouteLocationNormalized
   to: RouteLocationNormalized
 }
@@ -67,6 +66,9 @@ const ErrorTypeMessages = {
   [ErrorTypes.NAVIGATION_CANCELLED]({ from, to }: NavigationFailure) {
     return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new \`push\` or \`replace\``
   },
+  [ErrorTypes.NAVIGATION_DUPLICATED]({ from, to }: NavigationFailure) {
+    return `Avoided redundant navigation to current location: "${from.fullPath}"`
+  },
 }
 
 // Possible internal errors
index 72eb14bc8be39852e3ca1e16071a6afb1e85783e..97ce880bbde4555865e4042a02d7cf6b7a475c1c 100644 (file)
@@ -304,9 +304,6 @@ export function createRouter({
     // to could be a string where `replace` is a function
     const replace = (to as RouteLocationOptions).replace === true
 
-    // TODO: create navigation failure
-    if (!force && isSameRouteLocation(from, targetLocation)) return
-
     const lastMatched =
       targetLocation.matched[targetLocation.matched.length - 1]
     if (lastMatched && 'redirect' in lastMatched) {
@@ -334,36 +331,44 @@ export function createRouter({
     toLocation.redirectedFrom = redirectedFrom
     let failure: NavigationFailure | void
 
-    // trigger all guards, throw if navigation is rejected
-    try {
-      await navigate(toLocation, from)
-    } catch (error) {
-      // a more recent navigation took place
-      if (pendingLocation !== toLocation) {
-        failure = createRouterError<NavigationFailure>(
-          ErrorTypes.NAVIGATION_CANCELLED,
-          {
-            from,
-            to: toLocation,
-          }
-        )
-      } else if (error.type === ErrorTypes.NAVIGATION_ABORTED) {
-        failure = error as NavigationFailure
-      } else if (error.type === ErrorTypes.NAVIGATION_GUARD_REDIRECT) {
-        // preserve the original redirectedFrom if any
-        return pushWithRedirect(
-          // keep options
-          {
-            ...locationAsObject((error as NavigationRedirectError).to),
-            state: data,
-            force,
-            replace,
-          },
-          redirectedFrom || toLocation
-        )
-      } else {
-        // unknown error, throws
-        triggerError(error, true)
+    if (!force && isSameRouteLocation(from, targetLocation))
+      failure = createRouterError<NavigationFailure>(
+        ErrorTypes.NAVIGATION_DUPLICATED,
+        { to: toLocation, from }
+      )
+
+    if (!failure) {
+      // trigger all guards, throw if navigation is rejected
+      try {
+        await navigate(toLocation, from)
+      } catch (error) {
+        // a more recent navigation took place
+        if (pendingLocation !== toLocation) {
+          failure = createRouterError<NavigationFailure>(
+            ErrorTypes.NAVIGATION_CANCELLED,
+            {
+              from,
+              to: toLocation,
+            }
+          )
+        } else if (error.type === ErrorTypes.NAVIGATION_ABORTED) {
+          failure = error as NavigationFailure
+        } else if (error.type === ErrorTypes.NAVIGATION_GUARD_REDIRECT) {
+          // preserve the original redirectedFrom if any
+          return pushWithRedirect(
+            // keep options
+            {
+              ...locationAsObject((error as NavigationRedirectError).to),
+              state: data,
+              force,
+              replace,
+            },
+            redirectedFrom || toLocation
+          )
+        } else {
+          // unknown error, throws
+          triggerError(error, true)
+        }
       }
     }