]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
after guards
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 30 Apr 2019 21:27:11 +0000 (23:27 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Tue, 30 Apr 2019 21:27:11 +0000 (23:27 +0200)
__tests__/after-guards.spec.js [new file with mode: 0644]
src/router.ts
src/types/index.ts

diff --git a/__tests__/after-guards.spec.js b/__tests__/after-guards.spec.js
new file mode 100644 (file)
index 0000000..5d46f16
--- /dev/null
@@ -0,0 +1,65 @@
+// @ts-check
+require('./helper')
+const expect = require('expect')
+const { HTML5History } = require('../src/history/html5')
+const { Router } = require('../src/router')
+const { JSDOM } = require('jsdom')
+const fakePromise = require('faked-promise')
+
+const tick = () => new Promise(resolve => process.nextTick(resolve))
+
+/**
+ * @param {Partial<import('../src/router').RouterOptions> & { routes: import('../src/types').RouteRecord[]}} options
+ */
+function createRouter(options) {
+  return new Router({
+    history: new HTML5History(),
+    ...options,
+  })
+}
+
+const Home = { template: `<div>Home</div>` }
+const Foo = { template: `<div>Foo</div>` }
+
+/** @type {import('../src/types').RouteRecord[]} */
+const routes = [
+  { path: '/', component: Home },
+  { path: '/foo', component: Foo },
+]
+
+describe('router.afterEach', () => {
+  beforeAll(() => {
+    // TODO: move to utils for tests that need DOM
+    const dom = new JSDOM(
+      `<!DOCTYPE html><html><head></head><body></body></html>`,
+      {
+        url: 'https://example.org/',
+        referrer: 'https://example.com/',
+        contentType: 'text/html',
+      }
+    )
+
+    // @ts-ignore
+    global.window = dom.window
+  })
+
+  it('calls afterEach guards on push', async () => {
+    const spy = jest.fn()
+    const router = createRouter({ routes })
+    router.afterEach(spy)
+    await router.push('/foo')
+    expect(spy).toHaveBeenCalledTimes(1)
+    expect(spy).toHaveBeenCalledWith(
+      expect.objectContaining({ fullPath: '/foo' }),
+      expect.objectContaining({ fullPath: '/' })
+    )
+  })
+
+  it.skip('calls afterEach guards on replace', async () => {
+    const spy = jest.fn()
+    const router = createRouter({ routes })
+    router.afterEach(spy)
+    // await router.replace('/foo')
+    expect(spy).toHaveBeenCalledTimes(1)
+  })
+})
index 7167a2d2ab64144f31ca9215d6cb00a87167f9f1..0219b3e3c667fa7b20591e4384aea361d660e434 100644 (file)
@@ -10,6 +10,7 @@ import {
   NavigationGuard,
   TODO,
   NavigationGuardCallback,
+  PostNavigationGuard,
 } from './types/index'
 
 export interface RouterOptions {
@@ -21,6 +22,7 @@ export class Router {
   protected history: BaseHistory
   private matcher: RouterMatcher
   private beforeGuards: NavigationGuard[] = []
+  private afterGuards: PostNavigationGuard[] = []
   currentRoute: Readonly<RouteLocationNormalized> = START_LOCATION_NORMALIZED
 
   constructor(options: RouterOptions) {
@@ -32,7 +34,7 @@ export class Router {
     this.history.listen((to, from, info) => {
       // TODO: check navigation guards
       const matchedRoute = this.matcher.resolve(to, this.currentRoute)
-      console.log({ to, matchedRoute })
+      // console.log({ to, matchedRoute })
       // TODO: navigate
 
       this.currentRoute = {
@@ -68,7 +70,11 @@ export class Router {
     const toLocation: RouteLocationNormalized = { ...url, ...location }
     await this.navigate(toLocation, this.currentRoute)
     this.history.push(url)
+    const from = this.currentRoute
     this.currentRoute = toLocation
+
+    // navigation is confirmed, call afterGuards
+    for (const guard of this.afterGuards) guard(toLocation, from)
   }
 
   private async navigate(
@@ -94,7 +100,7 @@ export class Router {
       )
     }
 
-    console.log('Guarding against', guards.length, 'guards')
+    // console.log('Guarding against', guards.length, 'guards')
     for (const guard of guards) {
       await guard()
     }
@@ -102,10 +108,25 @@ export class Router {
 
   getRouteRecord(location: RouteLocation) {}
 
+  /**
+   * Add a global beforeGuard that can confirm, abort or modify a navigation
+   * @param guard
+   */
   beforeEach(guard: NavigationGuard): ListenerRemover {
     this.beforeGuards.push(guard)
     return () => {
       this.beforeGuards.splice(this.beforeGuards.indexOf(guard), 1)
     }
   }
+
+  /**
+   * Add a global after guard that is called once the navigation is confirmed
+   * @param guard
+   */
+  afterEach(guard: PostNavigationGuard): ListenerRemover {
+    this.afterGuards.push(guard)
+    return () => {
+      this.afterGuards.splice(this.afterGuards.indexOf(guard), 1)
+    }
+  }
 }
index e43c8efa6e6e118b2a9a3ad9186875b7f0fb4069..7ef0a8398ecd362534bdca9d2f4824036f715feb 100644 (file)
@@ -103,3 +103,7 @@ export interface NavigationGuard {
     next: NavigationGuardCallback
   ): any
 }
+
+export interface PostNavigationGuard {
+  (to: RouteLocationNormalized, from: RouteLocationNormalized): any
+}