]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test: wip tests for BaseTransition
authorEvan You <yyx990803@gmail.com>
Tue, 26 Nov 2019 23:07:05 +0000 (18:07 -0500)
committerEvan You <yyx990803@gmail.com>
Tue, 26 Nov 2019 23:07:05 +0000 (18:07 -0500)
packages/runtime-core/__tests__/components/BaseTransition.spec.ts [new file with mode: 0644]
packages/runtime-core/__tests__/components/KeepAlive.spec.ts
packages/runtime-core/__tests__/components/Suspense.spec.ts
packages/runtime-core/src/components/BaseTransition.ts

diff --git a/packages/runtime-core/__tests__/components/BaseTransition.spec.ts b/packages/runtime-core/__tests__/components/BaseTransition.spec.ts
new file mode 100644 (file)
index 0000000..a0a0e8a
--- /dev/null
@@ -0,0 +1,176 @@
+import {
+  nodeOps,
+  render,
+  h,
+  BaseTransition,
+  BaseTransitionProps,
+  ref,
+  nextTick,
+  serializeInner
+} from '@vue/runtime-test'
+
+function mount(props: BaseTransitionProps, slot: () => any) {
+  const root = nodeOps.createElement('div')
+  render(h(BaseTransition, props, slot), root)
+  return root
+}
+
+function mockProps() {
+  const cbs = {
+    doneEnter: () => {},
+    doneLeave: () => {}
+  }
+  const props: BaseTransitionProps = {
+    onBeforeEnter: jest.fn(el => {
+      expect(el.parentNode).toBeNull()
+    }),
+    onEnter: jest.fn((el, done) => {
+      cbs.doneEnter = done
+    }),
+    onAfterEnter: jest.fn(),
+    onEnterCancelled: jest.fn(),
+    onBeforeLeave: jest.fn(),
+    onLeave: jest.fn((el, done) => {
+      cbs.doneLeave = done
+    }),
+    onAfterLeave: jest.fn(),
+    onLeaveCancelled: jest.fn()
+  }
+  return {
+    props,
+    cbs
+  }
+}
+
+function assertCalls(
+  props: BaseTransitionProps,
+  calls: Record<string, number>
+) {
+  Object.keys(calls).forEach((key: keyof BaseTransitionProps) => {
+    expect(props[key]).toHaveBeenCalledTimes(calls[key])
+  })
+}
+
+describe('BaseTransition', () => {
+  describe('with elements', () => {
+    test('toggle on-off', async () => {
+      const toggle = ref(true)
+      const { props, cbs } = mockProps()
+      const root = mount(props, () => (toggle.value ? h('div') : null))
+
+      // without appear: true, enter hooks should not be called on mount
+      expect(props.onBeforeEnter).not.toHaveBeenCalled()
+      expect(props.onEnter).not.toHaveBeenCalled()
+      expect(props.onAfterEnter).not.toHaveBeenCalled()
+
+      toggle.value = false
+      await nextTick()
+      // comment placeholder enters immediately
+      expect(serializeInner(root)).toBe('<div></div><!---->')
+      expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
+      expect(props.onLeave).toHaveBeenCalledTimes(1)
+      expect(props.onAfterLeave).not.toHaveBeenCalled()
+      cbs.doneLeave()
+      expect(serializeInner(root)).toBe('<!---->')
+      expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
+
+      toggle.value = true
+      await nextTick()
+      expect(serializeInner(root)).toBe('<div></div>')
+      // before enter spy asserts node has no parent when it's called
+      expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
+      expect(props.onEnter).toHaveBeenCalledTimes(1)
+      expect(props.onAfterEnter).not.toHaveBeenCalled()
+      cbs.doneEnter()
+      expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
+
+      assertCalls(props, {
+        onBeforeEnter: 1,
+        onEnter: 1,
+        onAfterEnter: 1,
+        onEnterCancelled: 0,
+        onBeforeLeave: 1,
+        onLeave: 1,
+        onAfterLeave: 1,
+        onLeaveCancelled: 0
+      })
+    })
+
+    test('toggle before finish', async () => {
+      const toggle = ref(false)
+      const { props, cbs } = mockProps()
+      const root = mount(props, () => (toggle.value ? h('div') : null))
+
+      // start enter
+      toggle.value = true
+      await nextTick()
+      expect(serializeInner(root)).toBe(`<div></div>`)
+      expect(props.onBeforeEnter).toHaveBeenCalledTimes(1)
+      expect(props.onEnter).toHaveBeenCalledTimes(1)
+
+      // leave before enter finishes
+      toggle.value = false
+      await nextTick()
+      expect(serializeInner(root)).toBe(`<div></div><!---->`)
+      expect(props.onEnterCancelled).toHaveBeenCalled()
+      expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
+      expect(props.onLeave).toHaveBeenCalledTimes(1)
+      expect(props.onAfterLeave).not.toHaveBeenCalled()
+      // calling doneEnter now should have no effect
+      cbs.doneEnter()
+      expect(props.onAfterEnter).not.toHaveBeenCalled()
+
+      // enter again before leave finishes
+      toggle.value = true
+      await nextTick()
+      expect(props.onBeforeEnter).toHaveBeenCalledTimes(2)
+      expect(props.onEnter).toHaveBeenCalledTimes(2)
+      // 1. should remove the previous leaving <div> so there is only one <div>
+      // 2. should remove the comment placeholder for the off branch
+      expect(serializeInner(root)).toBe(`<div></div>`)
+      // note onLeaveCancelled is NOT called because it was a forced early
+      // removal instead of a cancel. Instead, onAfterLeave should be called.
+      expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
+      // calling doneLeave again should have no effect now
+      cbs.doneLeave()
+      expect(props.onAfterLeave).toHaveBeenCalledTimes(1)
+      cbs.doneEnter()
+      expect(props.onAfterEnter).toHaveBeenCalledTimes(1)
+
+      assertCalls(props, {
+        onBeforeEnter: 2,
+        onEnter: 2,
+        onAfterEnter: 1,
+        onEnterCancelled: 1,
+        onBeforeLeave: 1,
+        onLeave: 1,
+        onAfterLeave: 1,
+        onLeaveCancelled: 0
+      })
+    })
+
+    test('toggle between branches', () => {})
+
+    test('toggle between branches before finish', () => {})
+
+    test('persisted: true', () => {
+      // test onLeaveCancelled
+    })
+
+    test('appear: true', () => {})
+
+    test('mode: "out-in"', () => {})
+
+    test('mode: "out-in" toggle before finish', () => {})
+
+    test('mode: "in-out" toggle before finish', () => {})
+  })
+
+  describe('with components', () => {
+    // TODO
+  })
+
+  describe('with KeepAlive', () => {
+    // TODO
+  })
+})
index 9b9e3cc0d411041166122864aea3abb8739d106b..9bcec22e58667ed92ccd05b9f8ae7d06b124bdb2 100644 (file)
@@ -11,7 +11,7 @@ import {
 } from '@vue/runtime-test'
 import { KeepAliveProps } from '../../src/components/KeepAlive'
 
-describe('keep-alive', () => {
+describe('KeepAlive', () => {
   let one: ComponentOptions
   let two: ComponentOptions
   let views: Record<string, ComponentOptions>
index 62e5001743768de074e5beeeb9555794b3d3c6a3..264bd7ca4b12877f1e942c83d926801126139172 100644 (file)
@@ -13,7 +13,7 @@ import {
   onErrorCaptured
 } from '@vue/runtime-test'
 
-describe('renderer: suspense', () => {
+describe('Suspense', () => {
   const deps: Promise<any>[] = []
 
   beforeEach(() => {
index 4f4961b023f3ed60088dfed295de2a7e08854f1e..a5060a8542af9d4245cb7dbfe5b1799084a6daad 100644 (file)
@@ -39,7 +39,7 @@ export interface BaseTransitionProps {
   onBeforeLeave?: (el: any) => void
   onLeave?: (el: any, done: () => void) => void
   onAfterLeave?: (el: any) => void
-  onLeaveCancelled?: (el: any) => void
+  onLeaveCancelled?: (el: any) => void // only fired in persisted mode
 }
 
 export interface TransitionHooks {