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
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')
})
})
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
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)
})
})
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
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')
})
})
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
},
]
+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')
})
})
+// 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
--- /dev/null
+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
+}
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
}
}
- getRouteRecord(location: RouteLocation) {}
-
/**
* Add a global beforeGuard that can confirm, abort or modify a navigation
* @param guard
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