const { HTML5History } = require('../../src/history/html5')
const { Router } = require('../../src/router')
const fakePromise = require('faked-promise')
-const { NAVIGATION_TYPES, createDom, noGuard } = require('../utils')
+const { NAVIGATION_TYPES, createDom, noGuard, tick } = require('../utils')
+
+/** @typedef {import('../../src/types').RouteRecord} RouteRecord */
+/** @typedef {import('../../src/router').RouterOptions} RouterOptions */
/**
- * @param {Partial<import('../../src/router').RouterOptions> & { routes: import('../../src/types').RouteRecord[]}} options
+ * @param {Partial<RouterOptions> & { routes: RouteRecord[]}} options
*/
function createRouter(options) {
return new Router({
const Foo = { template: `<div>Foo</div>` }
const beforeEnter = jest.fn()
-/** @type {import('../../src/types').RouteRecord[]} */
+const beforeEnters = [jest.fn(), jest.fn()]
+/** @type {RouteRecord[]} */
const routes = [
{ path: '/', component: Home },
{ path: '/home', component: Home, beforeEnter },
component: Foo,
beforeEnter,
},
+ {
+ path: '/multiple',
+ beforeEnter: beforeEnters,
+ component: Foo,
+ },
]
-beforeEach(() => {
+function resetMocks() {
beforeEnter.mockReset()
+ beforeEnters.forEach(spy => {
+ spy.mockReset()
+ spy.mockImplementationOnce(noGuard)
+ })
+}
+
+beforeEach(() => {
+ resetMocks()
})
describe('beforeEnter', () => {
expect(beforeEnter).toHaveBeenCalledTimes(1)
})
+ it('supports an array of beforeEnter', async () => {
+ const router = createRouter({ routes })
+ await router[navigationMethod]('/multiple')
+ expect(beforeEnters[0]).toHaveBeenCalledTimes(1)
+ expect(beforeEnters[1]).toHaveBeenCalledTimes(1)
+ expect(beforeEnters[0]).toHaveBeenCalledWith(
+ expect.objectContaining({ path: '/multiple' }),
+ expect.objectContaining({ path: '/' }),
+ expect.any(Function)
+ )
+ })
+
it('calls beforeEnter different records, same component', async () => {
const router = createRouter({ routes })
beforeEnter.mockImplementationOnce(noGuard)
await p
expect(router.currentRoute.fullPath).toBe('/foo')
})
+
+ it('waits before navigating in an array of beforeEnter', async () => {
+ const [p1, r1] = fakePromise()
+ const [p2, r2] = fakePromise()
+ const router = createRouter({ routes })
+ beforeEnters[0].mockImplementationOnce(async (to, from, next) => {
+ await p1
+ next()
+ })
+ beforeEnters[1].mockImplementationOnce(async (to, from, next) => {
+ await p2
+ next()
+ })
+ const p = router[navigationMethod]('/multiple')
+ expect(router.currentRoute.fullPath).toBe('/')
+ expect(beforeEnters[1]).not.toHaveBeenCalled()
+ r1()
+ await p1
+ await tick()
+ r2()
+ await p
+ expect(router.currentRoute.fullPath).toBe('/multiple')
+ })
})
})
})
await this.runGuardQueue(guards)
// check global guards beforeEach
- // avoid if we are not changing route
- // TODO: trigger on child navigation
guards = []
for (const guard of this.beforeGuards) {
guards.push(guardToPromiseFn(guard, to, from))
await this.runGuardQueue(guards)
// check the route beforeEnter
- // TODO: check children. Should we also check reused routes guards
guards = []
for (const record of to.matched) {
// do not trigger beforeEnter on reused views
- if (record.beforeEnter && from.matched.indexOf(record) < 0)
- guards.push(guardToPromiseFn(record.beforeEnter, to, from))
+ if (record.beforeEnter && from.matched.indexOf(record) < 0) {
+ if (Array.isArray(record.beforeEnter)) {
+ for (const beforeEnter of record.beforeEnter)
+ guards.push(guardToPromiseFn(beforeEnter, to, from))
+ } else {
+ guards.push(guardToPromiseFn(record.beforeEnter, to, from))
+ }
+ }
}
// run the queue of per route beforeEnter guards