import Vue from 'vue' import { injectStyles, waitForUpdate, nextFrame } from './helpers' describe('Transition basic', () => { const { duration, buffer } = injectStyles() as { duration: number buffer: number } const explicitDuration = duration * 2 let el beforeEach(() => { el = document.createElement('div') document.body.appendChild(el) }) it('basic transition', done => { const vm = new Vue({ template: '
foo
', data: { ok: true } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('named transition', done => { const vm = new Vue({ template: '
foo
', data: { ok: true } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('custom transition classes', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test bye byebye active more') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test byebye active more bye-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe('test hello hello-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe('test hello-active hello-to') }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('dynamic transition', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true, trans: 'test' } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true vm.trans = 'changed' }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test changed-enter changed-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test changed-enter-active changed-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('inline transition object', done => { const enter = jasmine.createSpy() const leave = jasmine.createSpy() const vm = new Vue({ render(h) { return h('div', null, [ h( 'transition', { props: { name: 'inline', enterClass: 'hello', enterToClass: 'hello-to', enterActiveClass: 'hello-active', leaveClass: 'bye', leaveToClass: 'bye-to', leaveActiveClass: 'byebye active' }, on: { enter, leave } }, this.ok ? [h('div', { class: 'test' }, 'foo')] : undefined ) ]) }, data: { ok: true } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test bye byebye active') expect(leave).toHaveBeenCalled() }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe('test byebye active bye-to') }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe('test hello hello-active') expect(enter).toHaveBeenCalled() }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe('test hello-active hello-to') }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition events', done => { const onLeaveSpy = jasmine.createSpy() const onEnterSpy = jasmine.createSpy() const beforeLeaveSpy = jasmine.createSpy() const beforeEnterSpy = jasmine.createSpy() const afterLeaveSpy = jasmine.createSpy() const afterEnterSpy = jasmine.createSpy() const vm = new Vue({ template: `
foo
`, data: { ok: true }, methods: { beforeLeave: el => { expect(el).toBe(vm.$el.children[0]) expect(el.className).toBe('test') beforeLeaveSpy(el) }, leave: el => onLeaveSpy(el), afterLeave: el => afterLeaveSpy(el), beforeEnter: el => { expect(vm.$el.contains(el)).toBe(false) expect(el.className).toBe('test') beforeEnterSpy(el) }, enter: el => { expect(vm.$el.contains(el)).toBe(true) onEnterSpy(el) }, afterEnter: el => afterEnterSpy(el) } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') let _el = vm.$el.children[0] vm.ok = false waitForUpdate(() => { expect(beforeLeaveSpy).toHaveBeenCalledWith(_el) expect(onLeaveSpy).toHaveBeenCalledWith(_el) expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(afterLeaveSpy).not.toHaveBeenCalled() expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(afterLeaveSpy).toHaveBeenCalledWith(_el) expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { _el = vm.$el.children[0] expect(beforeEnterSpy).toHaveBeenCalledWith(_el) expect(onEnterSpy).toHaveBeenCalledWith(_el) expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(afterEnterSpy).not.toHaveBeenCalled() expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(afterEnterSpy).toHaveBeenCalledWith(_el) expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition events (v-show)', done => { const onLeaveSpy = jasmine.createSpy() const onEnterSpy = jasmine.createSpy() const beforeLeaveSpy = jasmine.createSpy() const beforeEnterSpy = jasmine.createSpy() const afterLeaveSpy = jasmine.createSpy() const afterEnterSpy = jasmine.createSpy() const vm = new Vue({ template: `
foo
`, data: { ok: true }, methods: { beforeLeave: el => { expect(el.style.display).toBe('') expect(el).toBe(vm.$el.children[0]) expect(el.className).toBe('test') beforeLeaveSpy(el) }, leave: el => { expect(el.style.display).toBe('') onLeaveSpy(el) }, afterLeave: el => { expect(el.style.display).toBe('none') afterLeaveSpy(el) }, beforeEnter: el => { expect(el.className).toBe('test') expect(el.style.display).toBe('none') beforeEnterSpy(el) }, enter: el => { expect(el.style.display).toBe('') onEnterSpy(el) }, afterEnter: el => { expect(el.style.display).toBe('') afterEnterSpy(el) } } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') let _el = vm.$el.children[0] vm.ok = false waitForUpdate(() => { expect(beforeLeaveSpy).toHaveBeenCalledWith(_el) expect(onLeaveSpy).toHaveBeenCalledWith(_el) expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(afterLeaveSpy).not.toHaveBeenCalled() expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(afterLeaveSpy).toHaveBeenCalledWith(_el) expect(vm.$el.children[0].style.display).toBe('none') vm.ok = true }) .then(() => { _el = vm.$el.children[0] expect(beforeEnterSpy).toHaveBeenCalledWith(_el) expect(onEnterSpy).toHaveBeenCalledWith(_el) expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(afterEnterSpy).not.toHaveBeenCalled() expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(afterEnterSpy).toHaveBeenCalledWith(_el) expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('explicit user callback in JavaScript hooks', done => { let next const vm = new Vue({ template: `
foo
`, data: { ok: true }, methods: { enter: (el, cb) => { next = cb }, leave: (el, cb) => { next = cb } } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) expect(next).toBeTruthy() next() expect(vm.$el.children.length).toBe(0) }) .then(() => { vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) expect(next).toBeTruthy() next() expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('css: false', done => { const enterSpy = jasmine.createSpy() const leaveSpy = jasmine.createSpy() const vm = new Vue({ template: `
foo
`, data: { ok: true }, methods: { enter: enterSpy, leave: leaveSpy } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(leaveSpy).toHaveBeenCalled() expect(vm.$el.innerHTML).toBe('') vm.ok = true }) .then(() => { expect(enterSpy).toHaveBeenCalled() expect(vm.$el.innerHTML).toBe('
foo
') }) .then(done) }) it('no transition detected', done => { const enterSpy = jasmine.createSpy() const leaveSpy = jasmine.createSpy() const vm = new Vue({ template: '
foo
', data: { ok: true }, methods: { enter: enterSpy, leave: leaveSpy } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(leaveSpy).toHaveBeenCalled() expect(vm.$el.innerHTML).toBe( '
foo
' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.innerHTML).toBe('') vm.ok = true }) .then(() => { expect(enterSpy).toHaveBeenCalled() expect(vm.$el.innerHTML).toBe( '
foo
' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.innerHTML).toBe('
foo
') }) .then(done) }) it('enterCancelled', done => { const spy = jasmine.createSpy() const vm = new Vue({ template: `
foo
`, data: { ok: false }, methods: { enterCancelled: spy } }).$mount(el) expect(vm.$el.innerHTML).toBe('') vm.ok = true waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration / 2) .then(() => { vm.ok = false }) .then(() => { expect(spy).toHaveBeenCalled() expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) }) .then(done) }) it('should remove stale leaving elements', done => { const spy = jasmine.createSpy() const vm = new Vue({ template: `
foo
`, data: { ok: true }, methods: { afterLeave: spy } }).$mount(el) expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(duration / 2) .then(() => { vm.ok = true }) .then(() => { expect(spy).toHaveBeenCalled() expect(vm.$el.children.length).toBe(1) // should have removed leaving element expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.innerHTML).toBe('
foo
') }) .then(done) }) it('transition with v-show', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.textContent).toBe('foo') expect(vm.$el.children[0].style.display).toBe('') expect(vm.$el.children[0].className).toBe('test') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].style.display).toBe('none') vm.ok = true }) .then(() => { expect(vm.$el.children[0].style.display).toBe('') expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition with v-show, inside child component', done => { const vm = new Vue({ template: `
`, data: { ok: true }, components: { test: { template: `
foo
` } } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.textContent).toBe('foo') expect(vm.$el.children[0].style.display).toBe('') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].style.display).toBe('none') vm.ok = true }) .then(() => { expect(vm.$el.children[0].style.display).toBe('') expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('leaveCancelled (v-show only)', done => { const spy = jasmine.createSpy() const vm = new Vue({ template: `
foo
`, data: { ok: true }, methods: { leaveCancelled: spy } }).$mount(el) expect(vm.$el.children[0].style.display).toBe('') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(10) .then(() => { vm.ok = true }) .then(() => { expect(spy).toHaveBeenCalled() expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].style.display).toBe('') }) .then(done) }) it('leave transition with v-show: cancelled on next frame', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) vm.ok = false waitForUpdate(() => { vm.ok = true }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('enter transition with v-show: cancelled on next frame', done => { const vm = new Vue({ template: `
foo
`, data: { ok: false } }).$mount(el) vm.ok = true waitForUpdate(() => { vm.ok = false }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('animations', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { 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) vm.ok = true }) .then(() => { 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[0].className).toBe('') }) .then(done) }) it('explicit transition type', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-anim-long-leave test-anim-long-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-anim-long-leave-active test-anim-long-leave-to' ) }) .thenWaitFor(duration + 5) .then(() => { // should not end early due to transition presence expect(vm.$el.children[0].className).toBe( 'test test-anim-long-leave-active test-anim-long-leave-to' ) }) .thenWaitFor(duration + 5) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-anim-long-enter test-anim-long-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-anim-long-enter-active test-anim-long-enter-to' ) }) .thenWaitFor(duration + 5) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-anim-long-enter-active test-anim-long-enter-to' ) }) .thenWaitFor(duration + 5) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition on appear', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-appear test-appear-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-appear-active test-appear-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition on appear with v-show', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition on SVG elements', done => { const vm = new Vue({ template: ` `, data: { ok: true } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test') vm.ok = false waitForUpdate(() => { expect(vm.$el.childNodes[0].getAttribute('class')).toBe( 'test v-leave v-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.childNodes[0].getAttribute('class')).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.childNodes.length).toBe(1) expect(vm.$el.childNodes[0].nodeType).toBe(8) // should be an empty comment node expect(vm.$el.childNodes[0].textContent).toBe('') vm.ok = true }) .then(() => { expect(vm.$el.childNodes[0].getAttribute('class')).toBe( 'test v-enter v-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.childNodes[0].getAttribute('class')).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test') }) .then(done) }) it('transition on child components', done => { const vm = new Vue({ template: `
`, data: { ok: true }, components: { test: { template: `
foo
` // test transition override from parent } } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition inside child component with v-if', done => { const vm = new Vue({ template: `
`, data: { ok: true }, components: { test: { template: `
foo
` } } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition with appear inside child component with v-if', done => { const vm = new Vue({ template: `
`, data: { ok: true }, components: { test: { template: `
foo
` } } }).$mount(el) waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-appear test-appear-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-appear-active test-appear-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') vm.ok = false }) .then(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) }) .then(done) }) it('transition inside nested child component with v-if', done => { const vm = new Vue({ template: `
`, data: { ok: true }, components: { foo: { template: '', components: { bar: { template: '
foo
' } } } } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('transition with appear inside nested child component with v-if', done => { const vm = new Vue({ template: `
`, data: { ok: true }, components: { foo: { template: '', components: { bar: { template: `
foo
` } } } } }).$mount(el) waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-appear test-appear-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-appear-active test-appear-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') vm.ok = false }) .then(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) }) .then(done) }) it('custom transition higher-order component', done => { const vm = new Vue({ template: '
foo
', data: { ok: true }, components: { 'my-transition': { functional: true, render(h, { data, children }) { ;(data.props || (data.props = {})).name = 'test' return h('transition', data, children) } } } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('
foo
') vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave test-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-leave-active test-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter test-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test test-enter-active test-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('warn when used on multiple elements', () => { new Vue({ template: `

1

2

` }).$mount() expect( ` can only be used on a single element` ).toHaveBeenWarned() }) describe('explicit durations -', () => { it('single value', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(explicitDuration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter v-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(explicitDuration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('enter and auto leave', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter v-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(explicitDuration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('leave and auto enter', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(explicitDuration + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter v-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('separate enter and leave', done => { const enter = explicitDuration const leave = explicitDuration * 2 const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(leave + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter v-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(enter + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }) it('enter and leave + duration change', done => { const enter1 = explicitDuration * 2 const enter2 = explicitDuration const leave1 = explicitDuration * 0.5 const leave2 = explicitDuration * 3 const vm = new Vue({ template: `
foo
`, data: { ok: true, enter: enter1, leave: leave1 } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active') }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(leave1 + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter v-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(enter1 + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') vm.enter = enter2 vm.leave = leave2 }) .then(() => { vm.ok = false }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave v-leave-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(leave2 + buffer) .then(() => { expect(vm.$el.children.length).toBe(0) vm.ok = true }) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter v-enter-active' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(enter2 + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') }) .then(done) }, 10000) it('warn invalid durations', done => { const vm = new Vue({ template: `
foo
`, data: { ok: true } }).$mount(el) vm.ok = false waitForUpdate(() => { expect( ` explicit leave duration is not a valid number - got "foo"` ).toHaveBeenWarned() }) .thenWaitFor(duration + buffer) .then(() => { vm.ok = true }) .then(() => { expect( ` explicit enter duration is NaN` ).toHaveBeenWarned() }) .then(done) }) }) // #6687 it('transition on child components with empty root node', done => { const vm = new Vue({ template: `
`, data: { view: 'one' }, components: { one: { template: '
one
' }, two: { template: '
two
' } } }).$mount(el) // should not apply transition on initial render by default expect(vm.$el.innerHTML).toBe('') vm.view = 'two' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( '
two
' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-enter-active v-enter-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.children[0].className).toBe('test') vm.view = 'one' }) .then(() => { // incoming comment node is appended instantly because it doesn't have // data and therefore doesn't go through the transition module. expect(vm.$el.innerHTML).toBe( '
two
' ) }) .thenWaitFor(nextFrame) .then(() => { expect(vm.$el.children[0].className).toBe( 'test v-leave-active v-leave-to' ) }) .thenWaitFor(duration + buffer) .then(() => { expect(vm.$el.innerHTML).toBe('') }) .then(done) }) // #8199 it('should not throw error when replaced by v-html contents', done => { const vm = new Vue({ template: `
a
`, data: { ok: true } }).$mount(el) vm.ok = false waitForUpdate(() => { expect(vm.$el.children[0].innerHTML).toBe('false') }).then(done) }) })