Bladeren bron

tests for advanced async component features

Evan You 9 jaren geleden
bovenliggende
commit
d2b7142cf5
2 gewijzigde bestanden met toevoegingen van 156 en 10 verwijderingen
  1. 22 10
      src/core/vdom/helpers/resolve-async-component.js
  2. 134 0
      test/unit/features/component/component-async.spec.js

+ 22 - 10
src/core/vdom/helpers/resolve-async-component.js

@@ -75,30 +75,42 @@ export function resolveAsyncComponent (
           res.then(resolve, reject)
         }
       } else if (isDef(res.component) && typeof res.component.then === 'function') {
+        res.component.then(resolve, reject)
+
         if (isDef(res.error)) {
           factory.errorComp = ensureCtor(res.error, baseCtor)
         }
 
         if (isDef(res.loading)) {
           factory.loadingComp = ensureCtor(res.loading, baseCtor)
-          setTimeout(() => {
-            if (isUndef(factory.resolved) && isUndef(factory.error)) {
-              factory.loading = true
-              forceRender()
-            }
-          }, res.delay || 200)
+          if (res.delay === 0) {
+            factory.loading = true
+          } else {
+            setTimeout(() => {
+              if (isUndef(factory.resolved) && isUndef(factory.error)) {
+                factory.loading = true
+                forceRender()
+              }
+            }, res.delay || 200)
+          }
         }
 
         if (isDef(res.timeout)) {
-          setTimeout(reject, res.timeout)
+          setTimeout(() => {
+            reject(
+              process.env.NODE_ENV !== 'production'
+                ? `timeout (${res.timeout}ms)`
+                : null
+            )
+          }, res.timeout)
         }
-
-        res.component.then(resolve, reject)
       }
     }
 
     sync = false
     // return in case resolved synchronously
-    return factory.resolved
+    return factory.loading
+      ? factory.loadingComp
+      : factory.resolved
   }
 }

+ 134 - 0
test/unit/features/component/component-async.spec.js

@@ -159,4 +159,138 @@ describe('Component async', () => {
       done()
     }
   })
+
+  describe('loading/error/timeout', () => {
+    it('with loading component', done => {
+      const vm = new Vue({
+        template: `<div><test/></div>`,
+        components: {
+          test: () => ({
+            component: new Promise(resolve => {
+              setTimeout(() => {
+                resolve({ template: '<div>hi</div>' })
+                // wait for promise resolve and then parent update
+                Promise.resolve().then(() => {
+                  Vue.nextTick(next)
+                })
+              }, 5)
+            }),
+            loading: { template: `<div>loading</div>` },
+            delay: 1
+          })
+        }
+      }).$mount()
+
+      expect(vm.$el.innerHTML).toBe('<!---->')
+
+      let loadingAsserted = false
+      setTimeout(() => {
+        Vue.nextTick(() => {
+          loadingAsserted = true
+          expect(vm.$el.textContent).toBe('loading')
+        })
+      }, 1)
+
+      function next () {
+        expect(loadingAsserted).toBe(true)
+        expect(vm.$el.textContent).toBe('hi')
+        done()
+      }
+    })
+
+    it('with loading component (0 delay)', done => {
+      const vm = new Vue({
+        template: `<div><test/></div>`,
+        components: {
+          test: () => ({
+            component: new Promise(resolve => {
+              setTimeout(() => {
+                resolve({ template: '<div>hi</div>' })
+                // wait for promise resolve and then parent update
+                Promise.resolve().then(() => {
+                  Vue.nextTick(next)
+                })
+              }, 0)
+            }),
+            loading: { template: `<div>loading</div>` },
+            delay: 0
+          })
+        }
+      }).$mount()
+
+      expect(vm.$el.textContent).toBe('loading')
+
+      function next () {
+        expect(vm.$el.textContent).toBe('hi')
+        done()
+      }
+    })
+
+    it('with error component', done => {
+      const vm = new Vue({
+        template: `<div><test/></div>`,
+        components: {
+          test: () => ({
+            component: new Promise((resolve, reject) => {
+              setTimeout(() => {
+                reject()
+                // wait for promise resolve and then parent update
+                Promise.resolve().then(() => {
+                  Vue.nextTick(next)
+                })
+              }, 0)
+            }),
+            loading: { template: `<div>loading</div>` },
+            error: { template: `<div>error</div>` },
+            delay: 0
+          })
+        }
+      }).$mount()
+
+      expect(vm.$el.textContent).toBe('loading')
+
+      function next () {
+        expect(`Failed to resolve async component`).toHaveBeenWarned()
+        expect(vm.$el.textContent).toBe('error')
+        done()
+      }
+    })
+
+    it('with error component + timeout', done => {
+      const vm = new Vue({
+        template: `<div><test/></div>`,
+        components: {
+          test: () => ({
+            component: new Promise((resolve, reject) => {
+              setTimeout(() => {
+                resolve({ template: '<div>hi</div>' })
+                // wait for promise resolve and then parent update
+                Promise.resolve().then(() => {
+                  Vue.nextTick(next)
+                })
+              }, 5)
+            }),
+            loading: { template: `<div>loading</div>` },
+            error: { template: `<div>error</div>` },
+            delay: 0,
+            timeout: 1
+          })
+        }
+      }).$mount()
+
+      expect(vm.$el.textContent).toBe('loading')
+
+      setTimeout(() => {
+        Vue.nextTick(() => {
+          expect(`Failed to resolve async component`).toHaveBeenWarned()
+          expect(vm.$el.textContent).toBe('error')
+        })
+      }, 1)
+
+      function next () {
+        expect(vm.$el.textContent).toBe('error') // late resolve ignored
+        done()
+      }
+    })
+  })
 })