]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
chore: refactor tests for replace
authorEduardo San Martin Morote <posva13@gmail.com>
Wed, 1 May 2019 15:49:01 +0000 (17:49 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Wed, 1 May 2019 15:49:01 +0000 (17:49 +0200)
__tests__/guards/component-beforeRouteEnter.spec.js
__tests__/guards/global-after.spec.js
__tests__/guards/global-beforeEach.spec.js
__tests__/guards/route-beforeEnter.spec.js
__tests__/helper.js
__tests__/utils.ts [new file with mode: 0644]
src/router.ts
src/types/index.ts

index 4c865c98dee211c40decfacdfbda61910ae80415..ec9dcaf6d179b74b3a95919bf873ffab66f9673f 100644 (file)
@@ -3,10 +3,8 @@ 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))
+const { NAVIGATION_TYPES, createDom } = require('../utils')
 
 /**
  * @param {Partial<import('../../src/router').RouterOptions> & { routes: import('../../src/types').RouteRecord[]}} options
@@ -41,62 +39,54 @@ beforeEach(() => {
 
 describe('beforeRouteEnter', () => {
   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
+    createDom()
   })
 
-  it('calls beforeRouteEnter guards on push', async () => {
-    const router = createRouter({ routes })
-    beforeRouteEnter.mockImplementationOnce((to, from, next) => {
-      if (to.params.n !== 'valid') return next(false)
-      next()
-    })
-    await router.push('/guard/valid')
-    expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
-  })
+  NAVIGATION_TYPES.forEach(navigationMethod => {
+    describe(navigationMethod, () => {
+      it('calls beforeRouteEnter guards on navigation', async () => {
+        const router = createRouter({ routes })
+        beforeRouteEnter.mockImplementationOnce((to, from, next) => {
+          if (to.params.n !== 'valid') return next(false)
+          next()
+        })
+        await router[navigationMethod]('/guard/valid')
+        expect(beforeRouteEnter).toHaveBeenCalledTimes(1)
+      })
 
-  it('resolves async components before guarding', async () => {
-    const spy = jest.fn((to, from, next) => next())
-    const component = {
-      template: `<div></div>`,
-      beforeRouteEnter: spy,
-    }
-    const [promise, resolve] = fakePromise()
-    const router = createRouter({
-      routes: [...routes, { path: '/async', component: () => promise }],
-    })
-    const pushPromise = router.push('/async')
-    expect(spy).not.toHaveBeenCalled()
-    resolve(component)
-    await pushPromise
+      it('resolves async components before guarding', async () => {
+        const spy = jest.fn((to, from, next) => next())
+        const component = {
+          template: `<div></div>`,
+          beforeRouteEnter: spy,
+        }
+        const [promise, resolve] = fakePromise()
+        const router = createRouter({
+          routes: [...routes, { path: '/async', component: () => promise }],
+        })
+        const pushPromise = router[navigationMethod]('/async')
+        expect(spy).not.toHaveBeenCalled()
+        resolve(component)
+        await pushPromise
 
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
+        expect(spy).toHaveBeenCalledTimes(1)
+      })
 
-  it.skip('calls beforeRouteEnter guards on replace', () => {})
-  it.skip('does not call beforeRouteEnter if we were already on the page', () => {})
+      it.skip('does not call beforeRouteEnter if we were already on the page', () => {})
 
-  it('waits before navigating', async () => {
-    const [promise, resolve] = fakePromise()
-    const router = createRouter({ routes })
-    beforeRouteEnter.mockImplementationOnce(async (to, from, next) => {
-      await promise
-      next()
+      it('waits before navigating', async () => {
+        const [promise, resolve] = fakePromise()
+        const router = createRouter({ routes })
+        beforeRouteEnter.mockImplementationOnce(async (to, from, next) => {
+          await promise
+          next()
+        })
+        const p = router[navigationMethod]('/foo')
+        expect(router.currentRoute.fullPath).toBe('/')
+        resolve()
+        await p
+        expect(router.currentRoute.fullPath).toBe('/foo')
+      })
     })
-    const p = router.push('/foo')
-    expect(router.currentRoute.fullPath).toBe('/')
-    resolve()
-    await p
-    expect(router.currentRoute.fullPath).toBe('/foo')
   })
 })
index d57cb6c0c82d3fc6bfa5cf1d758b3096c62308e9..719d98bd3c049103b63c1aa3b4a765b9daec93f8 100644 (file)
@@ -3,7 +3,7 @@ require('../helper')
 const expect = require('expect')
 const { HTML5History } = require('../../src/history/html5')
 const { Router } = require('../../src/router')
-const { JSDOM } = require('jsdom')
+const { NAVIGATION_TYPES, createDom } = require('../utils')
 
 /**
  * @param {Partial<import('../../src/router').RouterOptions> & { routes: import('../../src/types').RouteRecord[]}} options
@@ -26,48 +26,33 @@ const routes = [
 
 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: '/' })
-    )
+    createDom()
   })
 
-  it('does not call afterEach if navigation is cancelled', async () => {
-    const spy = jest.fn()
-    const router = createRouter({ routes })
-    router.afterEach(spy)
-    router.beforeEach((to, from, next) => {
-      next(false) // cancel the navigation
+  NAVIGATION_TYPES.forEach(navigationMethod => {
+    describe(navigationMethod, () => {
+      it('calls afterEach guards on push', async () => {
+        const spy = jest.fn()
+        const router = createRouter({ routes })
+        router.afterEach(spy)
+        await router[navigationMethod]('/foo')
+        expect(spy).toHaveBeenCalledTimes(1)
+        expect(spy).toHaveBeenCalledWith(
+          expect.objectContaining({ fullPath: '/foo' }),
+          expect.objectContaining({ fullPath: '/' })
+        )
+      })
+
+      it('does not call afterEach if navigation is cancelled', async () => {
+        const spy = jest.fn()
+        const router = createRouter({ routes })
+        router.afterEach(spy)
+        router.beforeEach((to, from, next) => {
+          next(false) // cancel the navigation
+        })
+        await router[navigationMethod]('/foo').catch(err => {}) // ignore the error
+        expect(spy).not.toHaveBeenCalled()
+      })
     })
-    await router.push('/foo').catch(err => {}) // ignore the error
-    expect(spy).not.toHaveBeenCalled()
-  })
-
-  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 006558d8df4543ee55b703a49c8db0671d773f7d..83da878db858bea99dfd46debee7ee886827db30 100644 (file)
@@ -3,10 +3,8 @@ 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))
+const { NAVIGATION_TYPES, createDom, tick } = require('../utils')
 
 /**
  * @param {Partial<import('../../src/router').RouterOptions> & { routes: import('../../src/types').RouteRecord[]}} options
@@ -29,72 +27,64 @@ const routes = [
 
 describe('router.beforeEach', () => {
   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
+    createDom()
   })
 
-  it('calls beforeEach guards on push', () => {
-    const spy = jest.fn()
-    const router = createRouter({ routes })
-    router.beforeEach(spy)
-    router.push('/foo')
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
+  NAVIGATION_TYPES.forEach(navigationMethod => {
+    describe(navigationMethod, () => {
+      it('calls beforeEach guards on navigation', () => {
+        const spy = jest.fn()
+        const router = createRouter({ routes })
+        router.beforeEach(spy)
+        router[navigationMethod]('/foo')
+        expect(spy).toHaveBeenCalledTimes(1)
+      })
 
-  it.skip('calls beforeEach guards on replace', () => {})
-  it.skip('does not call beforeEach guard if we were already on the page', () => {})
+      it.skip('does not call beforeEach guard if we were already on the page', () => {})
 
-  it('waits before navigating', async () => {
-    const [promise, resolve] = fakePromise()
-    const router = createRouter({ routes })
-    router.beforeEach(async (to, from, next) => {
-      await promise
-      next()
-    })
-    const p = router.push('/foo')
-    expect(router.currentRoute.fullPath).toBe('/')
-    resolve()
-    await p
-    expect(router.currentRoute.fullPath).toBe('/foo')
-  })
+      it('waits before navigating', async () => {
+        const [promise, resolve] = fakePromise()
+        const router = createRouter({ routes })
+        router.beforeEach(async (to, from, next) => {
+          await promise
+          next()
+        })
+        const p = router[navigationMethod]('/foo')
+        expect(router.currentRoute.fullPath).toBe('/')
+        resolve()
+        await p
+        expect(router.currentRoute.fullPath).toBe('/foo')
+      })
 
-  it('waits in the right order', async () => {
-    const [p1, r1] = fakePromise()
-    const [p2, r2] = fakePromise()
-    const router = createRouter({ routes })
-    const guard1 = jest.fn(async (to, from, next) => {
-      await p1
-      next()
-    })
-    router.beforeEach(guard1)
-    const guard2 = jest.fn(async (to, from, next) => {
-      await p2
-      next()
+      it('waits in the right order', async () => {
+        const [p1, r1] = fakePromise()
+        const [p2, r2] = fakePromise()
+        const router = createRouter({ routes })
+        const guard1 = jest.fn(async (to, from, next) => {
+          await p1
+          next()
+        })
+        router.beforeEach(guard1)
+        const guard2 = jest.fn(async (to, from, next) => {
+          await p2
+          next()
+        })
+        router.beforeEach(guard2)
+        let navigation = router[navigationMethod]('/foo')
+        expect(router.currentRoute.fullPath).toBe('/')
+        expect(guard1).toHaveBeenCalled()
+        expect(guard2).not.toHaveBeenCalled()
+        r1()
+        // wait until the guard is called
+        await tick()
+        await tick()
+        expect(guard2).toHaveBeenCalled()
+        r2()
+        expect(router.currentRoute.fullPath).toBe('/')
+        await navigation
+        expect(guard2).toHaveBeenCalled()
+        expect(router.currentRoute.fullPath).toBe('/foo')
+      })
     })
-    router.beforeEach(guard2)
-    let navigation = router.push('/foo')
-    expect(router.currentRoute.fullPath).toBe('/')
-    expect(guard1).toHaveBeenCalled()
-    expect(guard2).not.toHaveBeenCalled()
-    r1()
-    // wait until the guard is called
-    await tick()
-    await tick()
-    expect(guard2).toHaveBeenCalled()
-    r2()
-    expect(router.currentRoute.fullPath).toBe('/')
-    await navigation
-    expect(guard2).toHaveBeenCalled()
-    expect(router.currentRoute.fullPath).toBe('/foo')
   })
 })
index 219585c85c295346fa6bda160b944f746159dc6e..55ee3a0b189efde6bcb1b27059ecc64407e7f09b 100644 (file)
@@ -3,10 +3,8 @@ 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))
+const { NAVIGATION_TYPES, createDom } = require('../utils')
 
 /**
  * @param {Partial<import('../../src/router').RouterOptions> & { routes: import('../../src/types').RouteRecord[]}} options
@@ -33,46 +31,43 @@ const routes = [
   },
 ]
 
+beforeEach(() => {
+  beforeEnter.mockReset()
+})
+
 describe('beforeEnter', () => {
   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
+    createDom()
   })
 
-  it('calls beforeEnter guards on push', async () => {
-    const router = createRouter({ routes })
-    beforeEnter.mockImplementationOnce((to, from, next) => {
-      if (to.params.n !== 'valid') return next(false)
-      next()
-    })
-    await router.push('/guard/valid')
-    expect(beforeEnter).toHaveBeenCalledTimes(1)
-  })
+  NAVIGATION_TYPES.forEach(navigationMethod => {
+    describe(navigationMethod, () => {
+      it('calls beforeEnter guards on navigation', async () => {
+        const router = createRouter({ routes })
+        beforeEnter.mockImplementationOnce((to, from, next) => {
+          if (to.params.n !== 'valid') return next(false)
+          next()
+        })
+        await router[navigationMethod]('/guard/valid')
+        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.skip('calls beforeEnter guards on replace', () => {})
+      it.skip('does not call beforeEnter guard if we were already on the page', () => {})
 
-  it('waits before navigating', async () => {
-    const [promise, resolve] = fakePromise()
-    const router = createRouter({ routes })
-    beforeEnter.mockImplementationOnce(async (to, from, next) => {
-      await promise
-      next()
+      it('waits before navigating', async () => {
+        const [promise, resolve] = fakePromise()
+        const router = createRouter({ routes })
+        beforeEnter.mockImplementationOnce(async (to, from, next) => {
+          await promise
+          next()
+        })
+        const p = router[navigationMethod]('/foo')
+        expect(router.currentRoute.fullPath).toBe('/')
+        resolve()
+        await p
+        expect(router.currentRoute.fullPath).toBe('/foo')
+      })
     })
-    const p = router.push('/foo')
-    expect(router.currentRoute.fullPath).toBe('/')
-    resolve()
-    await p
-    expect(router.currentRoute.fullPath).toBe('/foo')
   })
 })
index 855127f8606d92ccb54a7ade91322d49bb25b4bf..9f46366f0e5af5e22e6580ac18baafa6e56f95f0 100644 (file)
@@ -1,3 +1,4 @@
+// make tests compatible with mocha so we can write using jest syntax
 if (typeof global !== 'undefined' && typeof global.beforeAll === 'undefined') {
   global.beforeAll = global.before
   global.afterAll = global.after
diff --git a/__tests__/utils.ts b/__tests__/utils.ts
new file mode 100644 (file)
index 0000000..4a3af18
--- /dev/null
@@ -0,0 +1,22 @@
+import { JSDOM, ConstructorOptions } from 'jsdom'
+
+export const tick = () => new Promise(resolve => process.nextTick(resolve))
+
+export const NAVIGATION_TYPES = ['push', 'replace']
+
+export function createDom(options?: ConstructorOptions) {
+  const dom = new JSDOM(
+    `<!DOCTYPE html><html><head></head><body></body></html>`,
+    {
+      url: 'https://example.org/',
+      referrer: 'https://example.com/',
+      contentType: 'text/html',
+      ...options,
+    }
+  )
+
+  // @ts-ignore
+  global.window = dom.window
+
+  return dom
+}
index 307657686c09ad0e0ad1d16e65f1829a32e591a1..b16fd80950700cc9ce13c20a6120e41b0d023bb5 100644 (file)
@@ -77,6 +77,11 @@ export class Router {
     for (const guard of this.afterGuards) guard(toLocation, from)
   }
 
+  replace(to: RouteLocation) {
+    const location = typeof to === 'string' ? { path: to } : to
+    return this.push({ ...location, replace: true })
+  }
+
   private async navigate(
     to: RouteLocationNormalized,
     from: RouteLocationNormalized
@@ -132,8 +137,6 @@ export class Router {
     }
   }
 
-  getRouteRecord(location: RouteLocation) {}
-
   /**
    * Add a global beforeGuard that can confirm, abort or modify a navigation
    * @param guard
index 33a950b9c17d37043dfc45a019a20f0cbd260e19..536743d92d1c58a09f121a60e1f87737f3df3d34 100644 (file)
@@ -27,12 +27,16 @@ export interface LocationAsRelative {
   params?: RouteParams
 }
 
+interface RouteLocationOptions {
+  replace?: boolean
+}
+
 // User level location
 export type RouteLocation =
   | string
-  | RouteQueryAndHash & LocationAsPath
-  | RouteQueryAndHash & LocationAsName
-  | RouteQueryAndHash & LocationAsRelative
+  | RouteQueryAndHash & LocationAsPath & RouteLocationOptions
+  | RouteQueryAndHash & LocationAsName & RouteLocationOptions
+  | RouteQueryAndHash & LocationAsRelative & RouteLocationOptions
 
 // exposed to the user in a very consistant way
 export interface RouteLocationNormalized