]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
test(ssr): add basic test
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 17 Sep 2019 17:09:26 +0000 (19:09 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Tue, 17 Sep 2019 17:09:26 +0000 (19:09 +0200)
__tests__/ssr/basic.spec.js
__tests__/ssr/shared.ts [new file with mode: 0644]
src/index.ts
src/router.ts

index d768414d67d7e7143415d48ab25d88d773b2521b..a553485c87115a6ddd0213e1b5ecb047c72140b4 100644 (file)
@@ -1,69 +1,31 @@
-/** @type {import('vue').VueConstructor} */
-// @ts-ignore
-const Vue = require('vue')
-const Router = require('../../src').default
-const { components, isMocha } = require('../utils')
-const { createRenderer } = require('vue-server-renderer')
+const { renderApp, renderer } = require('./shared')
 
-describe.skip('SSR: basicRenderer', () => {
-  Vue.use(Router)
-
-  function createRouter() {
-    // TODO: a more complex routing that can be used for most tests
-    return new Router({
-      mode: 'history',
-      routes: [
-        {
-          path: '/',
-          component: components.Home,
-        },
-        {
-          path: '/foo',
-          component: components.Foo,
-        },
-      ],
-    })
-  }
-
-  function createApp() {
-    // create router instance
-    const router = createRouter()
-
-    const app = new Vue({
-      // @ts-ignore
-      router,
-      render: h => h('div', {}, [h('RouterView')]),
-    })
-
-    // return both the app and the router
-    return { app, router }
-  }
-
-  function renderApp(context) {
-    return new Promise((resolve, reject) => {
-      const { app, router } = createApp()
-
-      // set server-side router's location
-      router.push(context.url)
-
-      // wait until router has resolved possible async components and hooks
-      // TODO: rename the promise one to isReady
-      router.onReady().then(() => {
-        // const matchedComponents = router.getMatchedComponents()
-        // no matched routes, reject with 404
-        if (!matchedComponents.length) {
-          return reject({ code: 404 })
-        }
+describe('SSR: basicRenderer', () => {
+  it('renders the view', async () => {
+    const app = await renderApp({ url: '/' })
+    const result = await renderer.renderToString(app)
+    expect(result).toMatchInlineSnapshot(
+      `"<div data-server-rendered=\\"true\\"><div>Home</div></div>"`
+    )
+  })
 
-        // the Promise should resolve to the app instance so it can be rendered
-        resolve(app)
-      }, reject)
-    })
-  }
+  /**
+   * TODO:
+   * - KeepAlive
+   * - Suspense
+   * - Navigation Guards
+   *  - Cancelled
+   *  - Redirection
+   * - Async components
+   *  - Views
+   *  - Inner components
+   */
 
-  it('should work', done => {
-    renderToString(
-      new Vue({
+  it('should work', async () => {
+    const app = await renderApp(
+      { url: '/' },
+      {},
+      {
         template: `
         <div>
           <p class="hi">yoyo</p>
@@ -98,42 +60,20 @@ describe.skip('SSR: basicRenderer', () => {
             })
           },
         },
-      }),
-      (err, result) => {
-        expect(err).toBeNull()
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<p class="hi">yoyo</p> ' +
-            '<div id="ho" class="red"></div> ' +
-            '<span>hi</span> ' +
-            '<input value="hi"> ' +
-            '<img src="https://vuejs.org/images/logo.png"> ' +
-            '<div class="a">test</div> ' +
-            '<span class="b">testAsync</span>' +
-            '</div>'
-        )
-        done()
       }
     )
-  })
+    const result = await renderer.renderToString(app)
 
-  // #5941
-  it('should work peoperly when accessing $ssrContext in root component', done => {
-    let ssrContext
-    renderToString(
-      new Vue({
-        template: `
-        <div></div>
-      `,
-        created() {
-          ssrContext = this.$ssrContext
-        },
-      }),
-      err => {
-        expect(err).toBeNull()
-        expect(ssrContext).toBeUndefined()
-        done()
-      }
+    expect(result).toContain(
+      '<div data-server-rendered="true">' +
+        '<p class="hi">yoyo</p> ' +
+        '<div id="ho" class="red"></div> ' +
+        '<span>hi</span> ' +
+        '<input value="hi"> ' +
+        '<img src="https://vuejs.org/images/logo.png"> ' +
+        '<div class="a">test</div> ' +
+        '<span class="b">testAsync</span>' +
+        '</div>'
     )
   })
 })
diff --git a/__tests__/ssr/shared.ts b/__tests__/ssr/shared.ts
new file mode 100644 (file)
index 0000000..143fac8
--- /dev/null
@@ -0,0 +1,76 @@
+import Vue from 'vue'
+import Router from '../../src'
+import { components } from '../utils'
+
+import { createRenderer } from 'vue-server-renderer'
+import { RouterOptions } from '../../src/router'
+
+Vue.use(Router)
+
+export const renderer = createRenderer()
+
+export function createRouter(options?: Partial<RouterOptions>) {
+  // TODO: a more complex routing that can be used for most tests
+  return new Router({
+    mode: 'history',
+    routes: [
+      {
+        path: '/',
+        component: components.Home,
+      },
+      {
+        path: '/foo',
+        component: components.Foo,
+      },
+    ],
+    ...options,
+  })
+}
+
+export function createApp(
+  routerOptions?: Partial<RouterOptions>,
+  options?: any
+) {
+  // create router instance
+  const router = createRouter(routerOptions)
+
+  const app = new Vue({
+    // @ts-ignore
+    router,
+    template: `<div>
+      <router-view/>
+      </div>`,
+    ...options,
+    // render: h => h('div', {}, [h('RouterView')]),
+  })
+
+  // return both the app and the router
+  return { app, router }
+}
+
+export function renderApp(
+  context: { url: string },
+  routerOptions?: Partial<RouterOptions>,
+  vueOptions?: any
+) {
+  return new Promise<ReturnType<typeof createApp>['app']>((resolve, reject) => {
+    const { app, router } = createApp(routerOptions, vueOptions)
+
+    // set server-side router's location
+    router.push(context.url).catch(err => {})
+
+    // wait until router has resolved possible async components and hooks
+    // TODO: rename the promise one to isReady
+    router.onReady().then(() => {
+      // const matchedComponents = router.getMatchedComponents()
+      const matchedComponents = router.currentRoute.matched
+      // no matched routes, reject with 404
+      if (!matchedComponents.length) {
+        return reject({ code: 404 })
+      }
+
+      // the Promise should resolve to the app instance so it can be rendered
+      resolve(app)
+    }, reject)
+  })
+}
index 7c596c08bcec6632c02db46e58a209984da0ec3e..66033ec73038b6c1e6c37338f373b017800ce9da 100644 (file)
@@ -31,7 +31,7 @@ const plugin: PluginFunction<void> = Vue => {
           // true
         )
 
-        router.doInitialNavigation()
+        router.doInitialNavigation().catch(() => {})
       } else {
         // @ts-ignore we are adding this
         this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
index 5fa9139218d54375c98415cf23484279cb073c5e..66f36d1305de39ed87c94f02cf65da63e1b0bd43 100644 (file)
@@ -497,9 +497,13 @@ export class Router {
    */
   protected markAsReady(err?: any): void {
     if (this.ready) return
-    for (const [resolve, reject] of this.onReadyCbs) {
-      if (err) reject(err)
-      else resolve()
+    for (const [resolve] of this.onReadyCbs) {
+      // TODO: is this okay?
+      // always resolve, as the router is ready even if there was an error
+      // @ts-ignore
+      resolve(err)
+      // if (err) reject(err)
+      // else resolve()
     }
     this.onReadyCbs = []
     this.ready = true
@@ -517,6 +521,7 @@ export class Router {
     try {
       await this.navigate(toLocation, this.currentRoute)
     } catch (error) {
+      this.markAsReady(error)
       if (NavigationGuardRedirect.is(error)) {
         // push was called while waiting in guards
         if (this.pendingLocation !== toLocation) {
@@ -533,13 +538,16 @@ export class Router {
           throw new NavigationCancelled(toLocation, this.currentRoute)
         }
 
+        // this throws, so nothing ahead happens
         this.triggerError(error)
       }
     }
 
     // push was called while waiting in guards
     if (this.pendingLocation !== toLocation) {
-      throw new NavigationCancelled(toLocation, this.currentRoute)
+      const error = new NavigationCancelled(toLocation, this.currentRoute)
+      this.markAsReady(error)
+      throw error
     }
 
     // NOTE: here we removed the pushing to history part as the history
@@ -552,6 +560,7 @@ export class Router {
     // navigation is confirmed, call afterGuards
     for (const guard of this.afterGuards) guard(toLocation, from)
 
+    this.markAsReady()
     return this.currentRoute
   }