vue2/test/unit/specs/transition/transition_spec.js

402 lines
12 KiB
JavaScript

var Vue = require('../../../../src/vue')
var _ = require('../../../../src/util')
var transition = require('../../../../src/transition')
if (_.inBrowser && !_.isIE9) {
describe('Transition', function () {
describe('Wrapper methods', function () {
var spy, el, target, parent, vm
beforeEach(function () {
el = document.createElement('div')
target = document.createElement('div')
parent = document.createElement('div')
parent.appendChild(target)
spy = jasmine.createSpy('transition skip')
vm = new Vue()
spyOn(transition, 'apply')
})
it('append', function () {
transition.append(el, parent, vm, spy)
expect(parent.lastChild).toBe(el)
expect(spy).toHaveBeenCalled()
})
it('before', function () {
transition.before(el, target, vm, spy)
expect(parent.firstChild).toBe(el)
expect(el.nextSibling).toBe(target)
expect(spy).toHaveBeenCalled()
})
it('remove', function () {
transition.remove(target, vm, spy)
expect(parent.childNodes.length).toBe(0)
expect(spy).toHaveBeenCalled()
})
it('removeThenAppend', function () {
transition.removeThenAppend(target, el, vm, spy)
expect(parent.childNodes.length).toBe(0)
expect(el.firstChild).toBe(target)
expect(spy).toHaveBeenCalled()
})
})
describe('Skipping', function () {
var el, vm, op, cb
beforeEach(function () {
el = document.createElement('div')
op = jasmine.createSpy('transition skip op')
cb = jasmine.createSpy('transition skip cb')
vm = new Vue()
})
it('skip el with no transition data', function () {
transition.apply(el, 1, op, vm, cb)
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
})
it('skip vm still being compiled', function () {
el.__v_trans = { id: 'test' }
transition.apply(el, 1, op, vm, cb)
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
})
it('skip vm with parent still being compiled', function () {
el.__v_trans = { id: 'test' }
var child = vm.$addChild({
el: el
})
expect(child._isCompiled).toBe(true)
transition.apply(el, 1, op, child, cb)
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
})
it('skip when no transition available', function () {
var e = _.transitionEndEvent
_.transitionEndEvent = null
el.__v_trans = { id: 'test' }
vm.$mount(el)
transition.apply(el, 1, op, vm, cb)
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
_.transitionEndEvent = e
})
})
describe('CSS transitions', function () {
var duration = '50ms'
// insert a test css
function insertCSS (text) {
var cssEl = document.createElement('style')
cssEl.textContent = text
document.head.appendChild(cssEl)
}
insertCSS(
'.test {\
transition: opacity ' + duration + ' ease;\
-webkit-transition: opacity ' + duration + ' ease;}'
)
insertCSS('.test-enter, .test-leave { opacity: 0; }')
insertCSS(
'.test-anim-enter {\
animation: test-enter ' + duration + ';\
-webkit-animation: test-enter ' + duration + ';}\
.test-anim-leave {\
animation: test-leave ' + duration + ';\
-webkit-animation: test-leave ' + duration + ';}\
@keyframes test-enter {\
from { opacity: 0 }\
to { opacity: 1 }}\
@-webkit-keyframes test-enter {\
from { opacity: 0 }\
to { opacity: 1 }}\
@keyframes test-leave {\
from { opacity: 1 }\
to { opacity: 0 }}\
@-webkit-keyframes test-leave {\
from { opacity: 1 }\
to { opacity: 0 }}'
)
var vm, el, op, cb
beforeEach(function (done) {
el = document.createElement('div')
el.__v_trans = {}
vm = new Vue({ el: el })
op = jasmine.createSpy('css op')
cb = jasmine.createSpy('css cb')
document.body.appendChild(el)
// !IMPORTANT!
// this ensures we force a layout for every test.
_.nextTick(done)
spyOn(window, 'getComputedStyle').and.callThrough()
})
afterEach(function () {
document.body.removeChild(el)
})
it('skip on 0s duration (execute right at next frame)', function (done) {
el.__v_trans.id = 'test'
el.style.transition =
el.style.WebkitTransition = 'opacity 0s ease'
transition.apply(el, 1, op, vm, cb)
_.nextTick(function () {
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
expect(el.classList.contains('test-enter')).toBe(false)
transition.apply(el, -1, op, vm, cb)
_.nextTick(function () {
expect(op.calls.count()).toBe(2)
expect(cb.calls.count()).toBe(2)
expect(el.classList.contains('test-leave')).toBe(false)
done()
})
})
})
it('skip when no transition available', function (done) {
el.__v_trans.id = 'test-no-trans'
transition.apply(el, 1, op, vm, cb)
_.nextTick(function () {
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
expect(el.classList.contains('test-no-trans-enter')).toBe(false)
transition.apply(el, -1, op, vm, cb)
_.nextTick(function () {
expect(op.calls.count()).toBe(2)
expect(cb.calls.count()).toBe(2)
expect(el.classList.contains('test-no-trans-leave')).toBe(false)
done()
})
})
})
it('transition enter', function (done) {
document.body.removeChild(el)
el.__v_trans.id = 'test'
// inline style
el.style.transition =
el.style.WebkitTransition = 'opacity ' + duration + ' ease'
transition.apply(el, 1, function () {
document.body.appendChild(el)
op()
}, vm, cb)
expect(op).toHaveBeenCalled()
expect(cb).not.toHaveBeenCalled()
_.nextTick(function () {
expect(el.classList.contains('test-enter')).toBe(false)
_.on(el, _.transitionEndEvent, function () {
expect(cb).toHaveBeenCalled()
done()
})
})
})
it('transition leave', function (done) {
el.__v_trans.id = 'test'
// cascaded class style
el.classList.add('test')
// wait a tick before applying the transition
// because doing so in the same frame won't trigger
// transition
_.nextTick(function () {
transition.apply(el, -1, op, vm, cb)
_.nextTick(function () {
expect(op).not.toHaveBeenCalled()
expect(cb).not.toHaveBeenCalled()
expect(el.classList.contains('test-leave')).toBe(true)
_.on(el, _.transitionEndEvent, function () {
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
expect(el.classList.contains('test-leave')).toBe(false)
done()
})
})
})
})
it('animation enter', function (done) {
document.body.removeChild(el)
el.__v_trans.id = 'test-anim'
transition.apply(el, 1, function () {
document.body.appendChild(el)
op()
}, vm, cb)
_.nextTick(function () {
expect(op).toHaveBeenCalled()
expect(cb).not.toHaveBeenCalled()
expect(el.classList.contains('test-anim-enter')).toBe(true)
_.on(el, _.animationEndEvent, function () {
expect(el.classList.contains('test-anim-enter')).toBe(false)
expect(cb).toHaveBeenCalled()
done()
})
})
})
it('animation leave', function (done) {
el.__v_trans.id = 'test-anim'
transition.apply(el, -1, op, vm, cb)
_.nextTick(function () {
expect(op).not.toHaveBeenCalled()
expect(cb).not.toHaveBeenCalled()
expect(el.classList.contains('test-anim-leave')).toBe(true)
_.on(el, _.animationEndEvent, function () {
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
expect(el.classList.contains('test-anim-leave')).toBe(false)
done()
})
})
})
it('clean up unfinished callback', function (done) {
el.__v_trans.id = 'test'
el.classList.add('test')
transition.apply(el, -1, function () {
document.body.removeChild(el)
}, vm, cb)
// cancel early
_.nextTick(function () {
expect(el.__v_trans.callback).toBeTruthy()
expect(el.classList.contains('test-leave')).toBe(true)
transition.apply(el, 1, function () {
document.body.appendChild(el)
}, vm)
expect(cb).not.toHaveBeenCalled()
expect(el.classList.contains('test-leave')).toBe(false)
expect(el.__v_trans.callback).toBeNull()
// IMPORTANT
// Let the queue flush finish before enter the next
// test. Don't remove the nextTick.
_.nextTick(done)
})
})
it('cache transition sniff results', function (done) {
el.__v_trans.id = 'test'
el.classList.add('test')
transition.apply(el, 1, op, vm)
_.nextTick(function () {
expect(window.getComputedStyle.calls.count()).toBe(1)
transition.apply(el, 1, op, vm)
_.nextTick(function () {
expect(window.getComputedStyle.calls.count()).toBe(1)
done()
})
})
})
})
describe('JavaScript transitions', function () {
var el, vm, op, cb, def, emitter
beforeEach(function () {
emitter = {}
el = document.createElement('div')
el.__v_trans = { id: 'test' }
document.body.appendChild(el)
op = jasmine.createSpy('js transition op')
cb = jasmine.createSpy('js transition cb')
def = {}
vm = new Vue({
el: el,
transitions: {
test: def
}
})
})
afterEach(function () {
document.body.removeChild(el)
})
it('beforeEnter', function () {
var spy = jasmine.createSpy('js transition beforeEnter')
def.beforeEnter = function (el) {
spy(this, el)
}
transition.apply(el, 1, op, vm, cb)
expect(spy).toHaveBeenCalledWith(vm, el)
})
it('enter', function () {
var spy = jasmine.createSpy('js enter')
def.enter = function (e, done) {
expect(e).toBe(el)
expect(op).toHaveBeenCalled()
done()
expect(cb).toHaveBeenCalled()
spy(this)
}
transition.apply(el, 1, op, vm, cb)
expect(spy).toHaveBeenCalledWith(vm)
})
it('leave', function () {
var spy = jasmine.createSpy('js leave')
def.leave = function (e, done) {
expect(e).toBe(el)
done()
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
spy(this)
}
transition.apply(el, -1, op, vm, cb)
expect(spy).toHaveBeenCalledWith(vm)
})
it('no def', function () {
transition.apply(el, 1, op, vm, cb)
expect(op).toHaveBeenCalled()
expect(cb).toHaveBeenCalled()
transition.apply(el, -1, op, vm, cb)
expect(op.calls.count()).toBe(2)
expect(cb.calls.count()).toBe(2)
})
it('optional cleanup callback', function (done) {
var cleanupSpy = jasmine.createSpy('js cleanup')
var leaveSpy = jasmine.createSpy('js leave')
def.enter = function (el, done) {
var to = setTimeout(done, 30)
return function () {
clearTimeout(to)
cleanupSpy()
}
}
def.leave = function (el, done) {
expect(cleanupSpy).toHaveBeenCalled()
leaveSpy()
done()
}
transition.apply(el, 1, op, vm, cb)
setTimeout(function () {
transition.apply(el, -1, op, vm)
expect(leaveSpy).toHaveBeenCalled()
setTimeout(function () {
expect(cb).not.toHaveBeenCalled()
done()
}, 30)
}, 15)
})
})
})
}