]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: allow inject within global navigation guards
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 11 May 2023 10:38:51 +0000 (12:38 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Thu, 11 May 2023 10:38:51 +0000 (12:38 +0200)
packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts [new file with mode: 0644]
packages/router/src/router.ts

diff --git a/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts b/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts
new file mode 100644 (file)
index 0000000..a660b1c
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * @jest-environment jsdom
+ */
+import { createDom, newRouter as createRouter } from '../utils'
+import { mount } from '@vue/test-utils'
+import { inject } from 'vue'
+import { mockWarn } from 'jest-mock-warn'
+import type { Router } from '../../src'
+
+describe('inject() within navigation guards', () => {
+  mockWarn()
+  beforeAll(() => {
+    createDom()
+  })
+
+  const PageComponent = {
+    template: `<div>Page</div>`,
+  }
+
+  function factory(router: Router) {
+    return mount(
+      {
+        template: `<RouterView />`,
+      },
+      {
+        global: {
+          plugins: [router],
+          provide: {
+            test: 'hello',
+          },
+        },
+      }
+    )
+  }
+
+  const globalGuards = ['beforeEach', 'beforeResolve', 'afterEach'] as const
+
+  for (const guardName of globalGuards) {
+    it(`router.${guardName}()`, async () => {
+      expect.assertions(1)
+      const router = createRouter({
+        routes: [{ path: '/', component: PageComponent }],
+      })
+      router[guardName](() => {
+        expect(inject('test')).toBe('hello')
+      })
+      factory(router)
+      await router.isReady()
+    })
+  }
+})
index 8493484be83e1066b8358c0dbe2a1299fd718bfa..d2605a70bfac90b6028d0790be2e5d1e633cde78 100644 (file)
@@ -13,6 +13,7 @@ import {
   RouteLocationOptions,
   MatcherLocationRaw,
   RouteParams,
+  NavigationGuardReturn,
 } from './types'
 import { RouterHistory, HistoryState, NavigationType } from './history/common'
 import {
@@ -778,6 +779,14 @@ export function createRouter(options: RouterOptions): Router {
     return error ? Promise.reject(error) : Promise.resolve()
   }
 
+  function runWithContext<T>(fn: () => T): T {
+    const app: App | undefined = installedApps.values().next().value
+    // support Vue < 3.3
+    return app && typeof app.runWithContext === 'function'
+      ? app.runWithContext(fn)
+      : fn()
+  }
+
   // TODO: refactor the whole before guards by internally using router.beforeEach
 
   function navigate(
@@ -907,7 +916,9 @@ export function createRouter(options: RouterOptions): Router {
   ): void {
     // navigation is confirmed, call afterGuards
     // TODO: wrap with error handlers
-    for (const guard of afterGuards.list()) guard(to, from, failure)
+    for (const guard of afterGuards.list()) {
+      runWithContext(() => guard(to, from, failure))
+    }
   }
 
   /**
@@ -1263,14 +1274,15 @@ export function createRouter(options: RouterOptions): Router {
     },
   }
 
-  return router
-}
+  // TODO: type this as NavigationGuardReturn or similar instead of any
+  function runGuardQueue(guards: Lazy<any>[]): Promise<any> {
+    return guards.reduce(
+      (promise, guard) => promise.then(() => runWithContext(guard)),
+      Promise.resolve()
+    )
+  }
 
-function runGuardQueue(guards: Lazy<any>[]): Promise<void> {
-  return guards.reduce(
-    (promise, guard) => promise.then(() => guard()),
-    Promise.resolve()
-  )
+  return router
 }
 
 function extractChangingRecords(