import Vue from 'vue' import injectStyles from './inject-styles' import { isIE9 } from 'core/util/env' import { nextFrame } from 'web/runtime/transition-util' if (!isIE9) { describe('Transition mode', () => { const { duration, buffer } = injectStyles() const components = { one: { template: '
one
' }, two: { template: '
two
' } } let el beforeEach(() => { el = document.createElement('div') document.body.appendChild(el) }) it('dynamic components, simultaneous', done => { const vm = new Vue({ template: `
`, data: { view: 'one' }, components }).$mount(el) expect(vm.$el.textContent).toBe('one') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).then(done) }) it('dynamic components, out-in', done => { let next const vm = new Vue({ template: `
`, data: { view: 'one' }, components, methods: { afterLeave () { next() } } }).$mount(el) expect(vm.$el.textContent).toBe('one') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
one
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' ) }).thenWaitFor(_next => { next = _next }).then(() => { expect(vm.$el.innerHTML).toBe('') }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).then(done) }) // #3440 it('dynamic components, out-in (with extra re-render)', done => { let next const vm = new Vue({ template: `
`, data: { view: 'one' }, components, methods: { afterLeave () { next() } } }).$mount(el) expect(vm.$el.textContent).toBe('one') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
one
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' ) // Force re-render before the element finishes leaving // this should not cause the incoming element to enter early vm.$forceUpdate() }).thenWaitFor(_next => { next = _next }).then(() => { expect(vm.$el.innerHTML).toBe('') }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).then(done) }) it('dynamic components, in-out', done => { let next const vm = new Vue({ template: `
`, data: { view: 'one' }, components, methods: { afterEnter () { next() } } }).$mount(el) expect(vm.$el.textContent).toBe('one') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(_next => { next = _next }).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).then(done) }) it('dynamic components, in-out with early cancel', done => { let next const vm = new Vue({ template: `
`, data: { view: 'one' }, components, methods: { afterEnter () { next() } } }).$mount(el) expect(vm.$el.textContent).toBe('one') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) // switch again before enter finishes, // this cancels both enter and leave. vm.view = 'one' }).then(() => { // 1. the pending leaving "one" should be removed instantly. // 2. the entering "two" should be placed into its final state instantly. // 3. a new "one" is created and entering expect(vm.$el.innerHTML).toBe( '
two
' + '
one
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' + '
one
' ) }).thenWaitFor(_next => { next = _next }).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' + '
one
' ) }).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' + '
one
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' + '
one
' ) }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' ) }).then(done).then(done) }) it('normal elements with different keys, simultaneous', done => { const vm = new Vue({ template: `
{{view}}
`, data: { view: 'one' }, components }).$mount(el) expect(vm.$el.textContent).toBe('one') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).then(done) }) it('normal elements with different keys, out-in', done => { let next const vm = new Vue({ template: `
{{view}}
`, data: { view: 'one' }, components, methods: { afterLeave () { next() } } }).$mount(el) expect(vm.$el.textContent).toBe('one') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
one
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' ) }).thenWaitFor(_next => { next = _next }).then(() => { expect(vm.$el.innerHTML).toBe('') }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).then(done) }) it('normal elements with different keys, in-out', done => { let next const vm = new Vue({ template: `
{{view}}
`, data: { view: 'one' }, components, methods: { afterEnter () { next() } } }).$mount(el) expect(vm.$el.textContent).toBe('one') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(_next => { next = _next }).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.innerHTML).toBe( '
one
' + '
two
' ) }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }).then(done) }) it('transition out-in on async component (resolve before leave complete)', done => { const vm = new Vue({ template: `
`, components: { componentA: resolve => { setTimeout(() => { resolve({ template: '

component A

' }) next1() }, duration / 2) }, componentB: resolve => { setTimeout(() => { resolve({ template: '

component B

' }) }, duration / 2) } }, data: { ok: true } }).$mount(el) expect(vm.$el.innerHTML).toBe('') function next1 () { Vue.nextTick(() => { expect(vm.$el.children.length).toBe(1) expect(vm.$el.textContent).toBe('component A') expect(vm.$el.children[0].className).toBe('test-anim-enter test-anim-enter-active') nextFrame(() => { expect(vm.$el.children[0].className).toBe('test-anim-enter-active test-anim-enter-to') setTimeout(() => { expect(vm.$el.children[0].className).toBe('') vm.ok = false next2() }, duration + buffer) }) }) } function next2 () { waitForUpdate(() => { expect(vm.$el.children.length).toBe(1) expect(vm.$el.textContent).toBe('component A') expect(vm.$el.children[0].className).toBe('test-anim-leave test-anim-leave-active') }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.children[0].className).toBe('test-anim-leave-active test-anim-leave-to') }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.children.length).toBe(1) expect(vm.$el.textContent).toBe('component B') expect(vm.$el.children[0].className).toMatch('test-anim-enter-active') }).thenWaitFor(duration * 2).then(() => { expect(vm.$el.children[0].className).toBe('') }).then(done) } }) it('transition out-in on async component (resolve after leave complete)', done => { const vm = new Vue({ template: `
`, components: { componentA: { template: '

component A

' }, componentB: resolve => { setTimeout(() => { resolve({ template: '

component B

' }) Vue.nextTick(next) }, (duration + buffer) * 1.7) } }, data: { ok: true } }).$mount(el) expect(vm.$el.innerHTML).toBe('

component A

') let next vm.ok = false waitForUpdate(() => { expect(vm.$el.children.length).toBe(1) expect(vm.$el.textContent).toBe('component A') expect(vm.$el.children[0].className).toBe('test-anim-leave test-anim-leave-active') }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.children[0].className).toBe('test-anim-leave-active test-anim-leave-to') }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.children.length).toBe(0) expect(vm.$el.innerHTML).toBe('') }).thenWaitFor(_next => { next = _next }).then(() => { expect(vm.$el.children.length).toBe(1) expect(vm.$el.textContent).toBe('component B') expect(vm.$el.children[0].className).toBe('test-anim-enter test-anim-enter-active') }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.children[0].className).toBe('test-anim-enter-active test-anim-enter-to') }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.children.length).toBe(1) expect(vm.$el.textContent).toBe('component B') expect(vm.$el.children[0].className).toBe('') }).then(done) }) it('transition in-out on async component', done => { const vm = new Vue({ template: `
`, components: { componentA: resolve => { setTimeout(() => { resolve({ template: '

component A

' }) next1() }, duration / 2) }, componentB: resolve => { setTimeout(() => { resolve({ template: '

component B

' }) next2() }, duration / 2) } }, data: { ok: true } }).$mount(el) expect(vm.$el.innerHTML).toBe('') function next1 () { Vue.nextTick(() => { expect(vm.$el.children.length).toBe(1) expect(vm.$el.textContent).toBe('component A') expect(vm.$el.children[0].className).toBe('test-anim-enter test-anim-enter-active') nextFrame(() => { expect(vm.$el.children[0].className).toBe('test-anim-enter-active test-anim-enter-to') setTimeout(() => { expect(vm.$el.children[0].className).toBe('') vm.ok = false }, duration + buffer) }) }) } function next2 () { waitForUpdate(() => { expect(vm.$el.children.length).toBe(2) expect(vm.$el.textContent).toBe('component Acomponent B') expect(vm.$el.children[0].className).toBe('') expect(vm.$el.children[1].className).toBe('test-anim-enter test-anim-enter-active') }).thenWaitFor(nextFrame).then(() => { expect(vm.$el.children[1].className).toBe('test-anim-enter-active test-anim-enter-to') }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.children.length).toBe(2) expect(vm.$el.textContent).toBe('component Acomponent B') expect(vm.$el.children[0].className).toMatch('test-anim-leave-active') expect(vm.$el.children[1].className).toBe('') }).thenWaitFor(duration + buffer).then(() => { expect(vm.$el.children.length).toBe(1) expect(vm.$el.textContent).toBe('component B') expect(vm.$el.children[0].className).toBe('') }).then(done) } }) it('warn invalid mode', () => { new Vue({ template: '
123
' }).$mount() expect('invalid mode: foo').toHaveBeenWarned() }) }) }