import Vue from 'vue' import { injectStyles, waitForUpdate, nextFrame } from './helpers' 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) }) 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 * 2) .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() }) })