Răsfoiți Sursa

tests for transition-mode

Evan You 10 ani în urmă
părinte
comite
15d650054c

+ 14 - 17
src/platforms/web/runtime/components/transition-control.js

@@ -27,16 +27,22 @@ export default {
       if (this.mode === 'out-in') {
         // return empty node
         // and queue an update when the leave finishes
-        return addHook(oldChild, 'afterLeave', () => {
-          this.$forceUpdate()
+        return addHook(oldChild, {
+          afterLeave: () => {
+            this.$forceUpdate()
+          }
         })
       } else if (this.mode === 'in-out') {
         let delayedLeave
         const performLeave = () => { delayedLeave() }
-        addHook(newChild, 'afterEnter', performLeave)
-        addHook(newChild, 'enterCancelled', performLeave)
-        addHook(oldChild, 'delayLeave', leave => {
-          delayedLeave = leave
+        addHook(newChild, {
+          afterEnter: performLeave,
+          enterCancelled: performLeave
+        })
+        addHook(oldChild, {
+          delayLeave: leave => {
+            delayedLeave = leave
+          }
         })
       }
     }
@@ -44,7 +50,7 @@ export default {
   }
 }
 
-function addHook (vnode: VNode, name: string, hook: Function) {
+function addHook (vnode: VNode, hooks: Object) {
   if (!vnode.data || !vnode.data.transition) {
     return
   }
@@ -54,14 +60,5 @@ function addHook (vnode: VNode, name: string, hook: Function) {
   } else if (typeof trans !== 'object') {
     trans = vnode.data.transition = { name: 'v' }
   }
-  if (trans[name]) {
-    const oldHook = trans[name]
-    trans[name] = function (el) {
-      const res = oldHook.apply(this, arguments)
-      hook()
-      return res
-    }
-  } else {
-    trans[name] = hook
-  }
+  trans.hooks = hooks
 }

+ 21 - 1
src/platforms/web/runtime/modules/transition.js

@@ -189,9 +189,17 @@ function resolveTransition (id: string | Object, context: Component): Object {
     if (id.name) {
       def = resolveAsset(context.$options, 'transitions', id.name)
     }
-    return def
+    def = def
       ? extend(ensureTransitionClasses(id.name, def), id)
       : ensureTransitionClasses(id.name, id)
+    // extra hooks to be merged
+    // added by <transition-control>
+    if (id.hooks) {
+      for (const key in id.hooks) {
+        mergeHook(def, key, id.hooks[key])
+      }
+    }
+    return def
   } else {
     return autoCssTransition('v')
   }
@@ -219,6 +227,18 @@ const autoCssTransition: (name: string) => Object = cached(name => {
   }
 })
 
+function mergeHook (def, key, hook) {
+  const oldHook = def[key]
+  if (oldHook) {
+    def[key] = function () {
+      oldHook.apply(this, arguments)
+      hook()
+    }
+  } else {
+    def[key] = hook
+  }
+}
+
 function addTransitionClass (el: any, cls: string) {
   (el._transitionClasses || (el._transitionClasses = [])).push(cls)
   addClass(el, cls)

+ 262 - 9
test/unit/features/transition/transition-mode.spec.js

@@ -5,28 +5,281 @@ import { nextFrame } from 'web/runtime/modules/transition'
 
 if (!isIE9) {
   describe('Transition mode', () => {
-    it('dynamic components, simultaneous', done => {
+    const duration = injectStyles()
+    const components = {
+      one: { template: '<div>one</div>' },
+      two: { template: '<div>two</div>' }
+    }
 
+    let el
+    beforeEach(() => {
+      el = document.createElement('div')
+      document.body.appendChild(el)
     })
 
-    it('dynamic components, out-in', done => {
+    it('dynamic components, simultaneous', done => {
+      const vm = new Vue({
+        template: `<div>
+          <component
+            :is="view"
+            class="test"
+            transition>
+          </component>
+        </div>`,
+        data: { view: 'one' },
+        components
+      }).$mount(el)
+      expect(vm.$el.textContent).toBe('one')
+      vm.view = 'two'
+      waitForUpdate(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test v-enter">two</div>' +
+          '<div class="test v-leave">one</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test v-enter-active">two</div>' +
+          '<div class="test v-leave-active">one</div>'
+        )
+      }).thenWaitFor(duration + 10).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">two</div>'
+        )
+      }).then(done)
+    })
 
+    it('dynamic components, out-in', done => {
+      let next
+      const vm = new Vue({
+        template: `<div>
+          <component
+            :is="view"
+            class="test"
+            transition="test"
+            transition-mode="out-in">
+          </component>
+        </div>`,
+        data: { view: 'one' },
+        components,
+        transitions: {
+          test: {
+            afterLeave () {
+              next()
+            }
+          }
+        }
+      }).$mount(el)
+      expect(vm.$el.textContent).toBe('one')
+      vm.view = 'two'
+      waitForUpdate(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-leave">one</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-leave-active">one</div>'
+        )
+      }).thenWaitFor(_next => { next = _next }).then(() => {
+        expect(vm.$el.innerHTML).toBe('')
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-enter">two</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-enter-active">two</div>'
+        )
+      }).thenWaitFor(duration + 10).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">two</div>'
+        )
+      }).then(done)
     })
 
     it('dynamic components, in-out', done => {
-
+      let next
+      const vm = new Vue({
+        template: `<div>
+          <component
+            :is="view"
+            class="test"
+            transition="test"
+            transition-mode="in-out">
+          </component>
+        </div>`,
+        data: { view: 'one' },
+        components,
+        transitions: {
+          test: {
+            afterEnter () {
+              next()
+            }
+          }
+        }
+      }).$mount(el)
+      expect(vm.$el.textContent).toBe('one')
+      vm.view = 'two'
+      waitForUpdate(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">one</div>' +
+          '<div class="test test-enter">two</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">one</div>' +
+          '<div class="test test-enter-active">two</div>'
+        )
+      }).thenWaitFor(_next => { next = _next }).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">one</div>' +
+          '<div class="test">two</div>'
+        )
+      }).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-leave">one</div>' +
+          '<div class="test">two</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-leave-active">one</div>' +
+          '<div class="test">two</div>'
+        )
+      }).thenWaitFor(duration + 10).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">two</div>'
+        )
+      }).then(done)
     })
 
-    it('normal elements with different key, simultaneous', done => {
-
+    it('normal elements with different keys, simultaneous', done => {
+      const vm = new Vue({
+        template: `<div>
+          <div
+            :key="view"
+            class="test"
+            transition>{{view}}</div>
+        </div>`,
+        data: { view: 'one' },
+        components
+      }).$mount(el)
+      expect(vm.$el.textContent).toBe('one')
+      vm.view = 'two'
+      waitForUpdate(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test v-enter">two</div>' +
+          '<div class="test v-leave">one</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test v-enter-active">two</div>' +
+          '<div class="test v-leave-active">one</div>'
+        )
+      }).thenWaitFor(duration + 10).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">two</div>'
+        )
+      }).then(done)
     })
 
-    it('normal elements with different key, out-in', done => {
-
+    it('normal elements with different keys, out-in', done => {
+      let next
+      const vm = new Vue({
+        template: `<div>
+          <div
+            :key="view"
+            class="test"
+            transition="test"
+            transition-mode="out-in">{{view}}</div>
+        </div>`,
+        data: { view: 'one' },
+        components,
+        transitions: {
+          test: {
+            afterLeave () {
+              next()
+            }
+          }
+        }
+      }).$mount(el)
+      expect(vm.$el.textContent).toBe('one')
+      vm.view = 'two'
+      waitForUpdate(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-leave">one</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-leave-active">one</div>'
+        )
+      }).thenWaitFor(_next => { next = _next }).then(() => {
+        expect(vm.$el.innerHTML).toBe('')
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-enter">two</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-enter-active">two</div>'
+        )
+      }).thenWaitFor(duration + 10).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">two</div>'
+        )
+      }).then(done)
     })
 
-    it('normal elements with different key, in-out', done => {
-
+    it('normal elements with different keys, in-out', done => {
+      let next
+      const vm = new Vue({
+        template: `<div>
+          <div
+            :key="view"
+            class="test"
+            transition="test"
+            transition-mode="in-out">{{view}}</component>
+        </div>`,
+        data: { view: 'one' },
+        components,
+        transitions: {
+          test: {
+            afterEnter () {
+              next()
+            }
+          }
+        }
+      }).$mount(el)
+      expect(vm.$el.textContent).toBe('one')
+      vm.view = 'two'
+      waitForUpdate(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">one</div>' +
+          '<div class="test test-enter">two</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">one</div>' +
+          '<div class="test test-enter-active">two</div>'
+        )
+      }).thenWaitFor(_next => { next = _next }).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">one</div>' +
+          '<div class="test">two</div>'
+        )
+      }).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-leave">one</div>' +
+          '<div class="test">two</div>'
+        )
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test test-leave-active">one</div>' +
+          '<div class="test">two</div>'
+        )
+      }).thenWaitFor(duration + 10).then(() => {
+        expect(vm.$el.innerHTML).toBe(
+          '<div class="test">two</div>'
+        )
+      }).then(done)
     })
   })
 }