]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
test: edge cases isReady
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 28 Feb 2022 14:02:58 +0000 (15:02 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 28 Feb 2022 14:02:58 +0000 (15:02 +0100)
__tests__/isReady.spec.ts [new file with mode: 0644]
src/router.ts

diff --git a/__tests__/isReady.spec.ts b/__tests__/isReady.spec.ts
new file mode 100644 (file)
index 0000000..cad258a
--- /dev/null
@@ -0,0 +1,111 @@
+import { createMemoryHistory, createRouter } from '../src'
+import { components } from './utils'
+import { RouteRecordRaw } from '../src/types'
+
+// generic component because we are not displaying anything so it doesn't matter
+const component = components.Home
+
+const routes: RouteRecordRaw[] = [
+  { path: '/', component },
+  { path: '/bar', component },
+  { path: '/foo', component, name: 'Foo' },
+  {
+    path: '/fail-lazy',
+    component: async () => {
+      throw new Error('async')
+    },
+  },
+]
+
+describe('isReady', () => {
+  function newRouter(
+    options: Partial<Parameters<typeof createRouter>[0]> = {}
+  ) {
+    const history = options.history || createMemoryHistory()
+    const router = createRouter({ history, routes, ...options })
+
+    return router
+  }
+
+  it('resolves a normal navigation', async () => {
+    const router = newRouter()
+    router.push('/foo')
+    await expect(router.isReady()).resolves.toBe(undefined)
+    // can be called again
+    await expect(router.isReady()).resolves.toBe(undefined)
+  })
+
+  it('resolves a redirected navigation', async () => {
+    const router = newRouter()
+    router.beforeEach(to => (to.path === '/bar' ? true : '/bar'))
+    router.push('/foo')
+    await expect(router.isReady()).resolves.toBe(undefined)
+    expect(router.currentRoute.value).toMatchObject({
+      redirectedFrom: expect.objectContaining({ path: '/foo' }),
+    })
+    // can be called again
+    await expect(router.isReady()).resolves.toBe(undefined)
+  })
+
+  it('rejects when an error is thrown in a navigation guard', async () => {
+    const router = newRouter()
+    const errorSpy = jest.fn()
+    const error = new Error('failed')
+    router.onError(errorSpy)
+    const remove = router.beforeEach(async () => {
+      throw error
+    })
+    router.push('/foo').catch(() => {})
+    await expect(router.isReady()).rejects.toBe(error)
+    expect(errorSpy).toHaveBeenCalledTimes(1)
+    expect(errorSpy).toHaveBeenCalledWith(
+      error,
+      // to
+      expect.objectContaining({ path: '/foo' }),
+      // from
+      expect.objectContaining({ path: '/' })
+    )
+
+    // result can change
+    remove()
+    router.push('/foo').catch(() => {})
+    await expect(router.isReady()).resolves.toBe(undefined)
+    expect(errorSpy).toHaveBeenCalledTimes(1)
+  })
+
+  it('rejects a cancelled navigation', async () => {
+    const router = newRouter()
+    const errorSpy = jest.fn()
+    router.onError(errorSpy)
+    const remove = router.beforeEach(() => false)
+    router.push('/foo').catch(() => {})
+    await expect(router.isReady()).rejects.toMatchObject({
+      to: expect.objectContaining({ path: '/foo' }),
+      from: expect.objectContaining({ path: '/' }),
+    })
+    expect(errorSpy).toHaveBeenCalledTimes(0)
+
+    // can be checked again
+    router.push('/foo').catch(() => {})
+    await expect(router.isReady()).rejects.toMatchObject({
+      to: expect.objectContaining({ path: '/foo' }),
+      from: expect.objectContaining({ path: '/' }),
+    })
+    expect(errorSpy).toHaveBeenCalledTimes(0)
+
+    // result can change
+    remove()
+    router.push('/foo').catch(() => {})
+    await expect(router.isReady()).resolves.toBe(undefined)
+    expect(errorSpy).toHaveBeenCalledTimes(0)
+  })
+
+  it('rejects failed lazy loading', async () => {
+    const router = newRouter()
+    const errorSpy = jest.fn()
+    router.onError(errorSpy)
+    router.push('/fail-lazy').catch(() => {})
+    await expect(router.isReady()).rejects.toEqual(expect.any(Error))
+    expect(errorSpy).toHaveBeenCalledTimes(1)
+  })
+})
index 90ddeef30ae00ced009d0acdd3522d46fd36d80c..9a87439b00e500c7d8d29c5c22d5fdfbd3c567c6 100644 (file)
@@ -1103,14 +1103,19 @@ export function createRouter(options: RouterOptions): Router {
    * only be called once, otherwise does nothing.
    * @param err - optional error
    */
-  function markAsReady(err?: any): void {
-    if (ready) return
-    ready = true
-    setupListeners()
-    readyHandlers
-      .list()
-      .forEach(([resolve, reject]) => (err ? reject(err) : resolve()))
-    readyHandlers.reset()
+  function markAsReady<E = any>(err: E): E
+  function markAsReady<E = any>(): void
+  function markAsReady<E = any>(err?: E): E | void {
+    if (!ready) {
+      // still not ready if an error happened
+      ready = !err
+      setupListeners()
+      readyHandlers
+        .list()
+        .forEach(([resolve, reject]) => (err ? reject(err) : resolve()))
+      readyHandlers.reset()
+    }
+    return err
   }
 
   // Scroll behavior