]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test: more tests for suspense
authorEvan You <yyx990803@gmail.com>
Wed, 11 Sep 2019 15:09:16 +0000 (11:09 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 11 Sep 2019 15:10:14 +0000 (11:10 -0400)
packages/runtime-core/__tests__/rendererSuspense.spec.ts

index 16953bee44d9eb0498402535373c35a571397e9a..c5d2ad4154fd21b50ad277bf19e0d733d2b8bd31 100644 (file)
@@ -6,47 +6,50 @@ import {
   render,
   nodeOps,
   serializeInner,
-  nextTick
+  nextTick,
+  onMounted,
+  watch,
+  onUnmounted
 } from '@vue/runtime-test'
 
 describe('renderer: suspense', () => {
-  it('basic usage (nested + multiple deps)', async () => {
-    const msg = ref('hello')
-    const deps: Promise<any>[] = []
+  const deps: Promise<any>[] = []
+
+  beforeEach(() => {
+    deps.length = 0
+  })
 
-    const createAsyncComponent = (loader: () => Promise<ComponentOptions>) => ({
+  // a simple async factory for testing purposes only.
+  function createAsyncComponent<T extends ComponentOptions>(
+    comp: T,
+    delay: number = 0
+  ) {
+    return {
       async setup(props: any, { slots }: any) {
-        const p = loader()
+        const p: Promise<T> = new Promise(r => setTimeout(() => r(comp), delay))
         deps.push(p)
         const Inner = await p
         return () => h(Inner, props, slots)
       }
-    })
+    }
+  }
 
-    const AsyncChild = createAsyncComponent(
-      () =>
-        new Promise(resolve => {
-          setTimeout(() => {
-            resolve({
-              setup(props: { msg: string }) {
-                return () => h('div', props.msg)
-              }
-            })
-          }, 0)
-        })
-    )
+  it('basic usage (nested + multiple deps)', async () => {
+    const msg = ref('hello')
+
+    const AsyncChild = createAsyncComponent({
+      setup(props: { msg: string }) {
+        return () => h('div', props.msg)
+      }
+    })
 
     const AsyncChild2 = createAsyncComponent(
-      () =>
-        new Promise(resolve => {
-          setTimeout(() => {
-            resolve({
-              setup(props: { msg: string }) {
-                return () => h('div', props.msg)
-              }
-            })
-          }, 10)
-        })
+      {
+        setup(props: { msg: string }) {
+          return () => h('div', props.msg)
+        }
+      },
+      10
     )
 
     const Mid = {
@@ -77,12 +80,89 @@ describe('renderer: suspense', () => {
   })
 
   test('fallback content', async () => {
+    const Async = createAsyncComponent({
+      render() {
+        return h('div', 'async')
+      }
+    })
+
+    const Comp = {
+      setup() {
+        return () =>
+          h(Suspense, null, {
+            default: h(Async),
+            fallback: h('div', 'fallback')
+          })
+      }
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>async</div>`)
+  })
+
+  test('onResolve', async () => {
+    const Async = createAsyncComponent({
+      render() {
+        return h('div', 'async')
+      }
+    })
+
+    const onResolve = jest.fn()
+
+    const Comp = {
+      setup() {
+        return () =>
+          h(
+            Suspense,
+            {
+              onResolve
+            },
+            {
+              default: h(Async),
+              fallback: h('div', 'fallback')
+            }
+          )
+      }
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+    expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(onResolve).not.toHaveBeenCalled()
+
+    await Promise.all(deps)
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>async</div>`)
+    expect(onResolve).toHaveBeenCalled()
+  })
+
+  test('buffer mounted/updated hooks & watch callbacks', async () => {
     const deps: Promise<any>[] = []
+    const calls: string[] = []
+    const toggle = ref(true)
 
     const Async = {
       async setup() {
         const p = new Promise(r => setTimeout(r, 1))
         deps.push(p)
+
+        watch(() => {
+          calls.push('watch callback')
+        })
+
+        onMounted(() => {
+          calls.push('mounted')
+        })
+
+        onUnmounted(() => {
+          calls.push('unmounted')
+        })
+
         await p
         // test resume for returning bindings
         return {
@@ -98,7 +178,7 @@ describe('renderer: suspense', () => {
       setup() {
         return () =>
           h(Suspense, null, {
-            default: h(Async),
+            default: toggle.value ? h(Async) : null,
             fallback: h('div', 'fallback')
           })
       }
@@ -107,22 +187,30 @@ describe('renderer: suspense', () => {
     const root = nodeOps.createElement('div')
     render(h(Comp), root)
     expect(serializeInner(root)).toBe(`<div>fallback</div>`)
+    expect(calls).toEqual([])
 
     await Promise.all(deps)
     await nextTick()
     expect(serializeInner(root)).toBe(`<div>async</div>`)
-  })
-
-  test.todo('buffer mounted/updated hooks & watch callbacks')
+    expect(calls).toEqual([`watch callback`, `mounted`])
 
-  test.todo('onResolve')
+    // effects inside an already resolved suspense should happen at normal timing
+    toggle.value = false
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<!---->`)
+    expect(calls).toEqual([`watch callback`, `mounted`, 'unmounted'])
+  })
 
+  // should receive updated props/slots when resolved
   test.todo('content update before suspense resolve')
 
+  // mount/unmount hooks should not even fire
   test.todo('unmount before suspense resolve')
 
   test.todo('nested suspense')
 
+  test.todo('new async dep after resolve should cause suspense to restart')
+
   test.todo('error handling')
 
   test.todo('portal inside suspense')