]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: prevent calling before enter guards on reused views
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 2 May 2019 10:55:22 +0000 (12:55 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Thu, 2 May 2019 10:55:22 +0000 (12:55 +0200)
__tests__/guards/component-beforeRouteEnter.spec.js
__tests__/guards/global-beforeEach.spec.js
__tests__/guards/route-beforeEnter.spec.js
__tests__/utils.ts
src/router.ts

index ec9dcaf6d179b74b3a95919bf873ffab66f9673f..01b749bc8976cc8011bbe94b17a561c335a4eabb 100644 (file)
@@ -72,7 +72,16 @@ describe('beforeRouteEnter', () => {
         expect(spy).toHaveBeenCalledTimes(1)
       })
 
-      it.skip('does not call beforeRouteEnter if we were already on the page', () => {})
+      it('does not call beforeRouteEnter if we were already on the page', async () => {
+        const router = createRouter({ routes })
+        beforeRouteEnter.mockImplementation((to, from, next) => {
+          next()
+        })
+        await router.push('/guard/one')
+        expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
+        await router[navigationMethod]('/guard/one')
+        expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
+      })
 
       it('waits before navigating', async () => {
         const [promise, resolve] = fakePromise()
index a22c5893f3b7888be04b648ed0d96e5c50ea742c..acbd66746258af54406b63dad530dc3274f78462 100644 (file)
@@ -43,7 +43,17 @@ describe('router.beforeEach', () => {
         expect(spy).toHaveBeenCalledTimes(1)
       })
 
-      it.skip('does not call beforeEach guard if we were already on the page', () => {})
+      it('does not call beforeEach guard if we were already on the page', async () => {
+        const spy = jest.fn()
+        const router = createRouter({ routes })
+        await router.push('/foo')
+        router.beforeEach(spy)
+        spy.mockImplementationOnce((to, from, next) => {
+          next()
+        })
+        await router[navigationMethod]('/foo')
+        expect(spy).not.toHaveBeenCalled()
+      })
 
       it('waits before navigating', async () => {
         const [promise, resolve] = fakePromise()
index 55ee3a0b189efde6bcb1b27059ecc64407e7f09b..5086e6dd45cbf04949059648b978164d786e11bb 100644 (file)
@@ -52,8 +52,16 @@ describe('beforeEnter', () => {
         expect(beforeEnter).toHaveBeenCalledTimes(1)
       })
 
-      it.skip('calls beforeEnter guards on replace', () => {})
-      it.skip('does not call beforeEnter guard if we were already on the page', () => {})
+      it('does not call beforeEnter guard if we were already on the page', async () => {
+        const router = createRouter({ routes })
+        beforeEnter.mockImplementation((to, from, next) => {
+          next()
+        })
+        await router.push('/guard/one')
+        expect(beforeEnter).toHaveBeenCalledTimes(1)
+        await router[navigationMethod]('/guard/one')
+        expect(beforeEnter).toHaveBeenCalledTimes(1)
+      })
 
       it('waits before navigating', async () => {
         const [promise, resolve] = fakePromise()
index 589c133ff13664179bec609e367a954cfb2bbfc1..d5c234a902840f26d53b05ff1c5e166efe628cf5 100644 (file)
@@ -17,6 +17,10 @@ export function createDom(options?: ConstructorOptions) {
 
   // @ts-ignore
   global.window = dom.window
+  // @ts-ignore
+  global.location = dom.window.location
+  // @ts-ignore
+  global.document = dom.window.document
 
   return dom
 }
index 1090913914587234636cbf9c3dd09f2c3ffa27ca..7419763c3da9a1995f05b9c18ce67d1d405e6dd0 100644 (file)
@@ -122,22 +122,27 @@ export class Router {
       await guard()
     }
 
-    // check global guards first
-    guards = []
-    for (const guard of this.beforeGuards) {
-      guards.push(guardToPromiseFn(guard, to, from))
-    }
+    // check global guards beforeEach
+    // avoid if we are not changing route
+    // TODO: trigger on child navigation
+    if (last(to.matched) !== last(from.matched)) {
+      guards = []
+      for (const guard of this.beforeGuards) {
+        guards.push(guardToPromiseFn(guard, to, from))
+      }
 
-    // console.log('Guarding against', guards.length, 'guards')
-    for (const guard of guards) {
-      await guard()
+      // console.log('Guarding against', guards.length, 'guards')
+      for (const guard of guards) {
+        await guard()
+      }
     }
 
     // check the route beforeEnter
     // TODO: check children. Should we also check reused routes guards
     guards = []
     for (const record of to.matched) {
-      if (record.beforeEnter)
+      // do not trigger beforeEnter on reused views
+      if (record.beforeEnter && from.matched.indexOf(record) < 0)
         guards.push(guardToPromiseFn(record.beforeEnter, to, from))
     }
 
@@ -150,12 +155,16 @@ export class Router {
     guards = []
     // TODO: is it okay to resolve all matched component or should we do it in order
     await Promise.all(
-      to.matched.map(async ({ component }) => {
+      to.matched.map(async record => {
         // TODO: cache async routes per record
+        const { component } = record
         const resolvedComponent = await (typeof component === 'function'
           ? component()
           : component)
-        if (resolvedComponent.beforeRouteEnter) {
+        if (
+          resolvedComponent.beforeRouteEnter &&
+          from.matched.indexOf(record)
+        ) {
           // TODO: handle the next callback
           guards.push(
             guardToPromiseFn(resolvedComponent.beforeRouteEnter, to, from)
@@ -209,3 +218,7 @@ function guardToPromiseFn(
       guard(to, from, next)
     })
 }
+
+function last<T>(array: T[]): T {
+  return array[array.length - 1]
+}