I have a vue.js component that uses the <transition>
element to animate hide/show.
To speed up tests I would like to disable the animation. How can I do that?
* { transition: none !important }
is suggested here: https://github.com/vuejs/vue/issues/463 but it doesnt's seem to make a difference.
I've created a fiddle here: https://jsfiddle.net/z11fe07p/2268/
Running the "test" the last output is "3. Display should be "none", it is: block". If I increase the timeout to 100, or remove the <transition>
element, I get the expected output of "3. Display should be "none", it is: none"
So how can I disable the animation so that I can get rid of the setTimeout
calls?
EDIT:
I tried removing all css styling, but still have the same problem. So the problem is triggered by simply just having the <transition>
element.
EDIT 2:
Updated the fiddle to have no styling, just the <transition>
element. Also included calls to $nextTick()
to make sure that wasn't the reason it was behaving weirdly.
Change the call to wait100
to wait10
instead and you'll see the test start failing
https://jsfiddle.net/z11fe07p/2270/
EDIT 3:
Putting the example code here so it's easier for everyone to play around with :)
new Vue({
el: '#app',
template: `
<span>
<button @click="test()">Run test</button>
<transition>
<p v-show="show">Hello, world!</p>
</transition>
</span>
`,
data() {
return {
show: false,
};
},
methods: {
test() {
const wait10 = _ => new Promise(resolve => setTimeout(resolve, 10));
const wait100 = _ => new Promise(resolve => setTimeout(resolve, 100));
const showParagraph = _ => this.show = true;
const hideParagraph = _ => this.show = false;
const p = document.querySelector('p');
showParagraph();
this.$nextTick()
.then(wait10)
.then(() => {
const display = getComputedStyle(p).display;
assertEqual(display, 'block');
})
.then(hideParagraph)
.then(this.$nextTick)
.then(wait100)
.then(() => {
const display = getComputedStyle(p).display;
assertEqual(display, 'none');
});
}
}
});
function assertEqual(a, b) {
if (a !== b) {
console.error('Expected "' + a + '" to equal "' + b + '"');
}
};
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app"></div>
I essentially change all my transition
and transition-group
s into a div with render functions when env is testing
.
if (process.env.NODE_ENV === 'testing') {
const div = {
functional: true,
render: (h, { data, children }) => h('div', data, children),
}
Vue.component('transition', div)
Vue.component('transition-group', div)
}
I ran into this problem with <transition-group>
. My solution was to replace it during tests using the following code.
Vue.component('transition-group', {
props: ['tag'],
render(createElement) {
return createElement(this.tag || this.$vnode.data.tag || 'span', this.$slots.default);
},
});
This is the bare minimum for turning <transition-group>
into a mirror of <slot>
with an optional dynamically defined tag.
I'll probably need to do the same thing with <transition>
. If so, it may be even simpler, since <transition>
has no tag prop.
My use case was slightly different, but the requirement the same: I wanted to disable certain transition effects for mobile screens.
My solution was to just wrap into a component. This would also work for testing (if 'disable' was set with e.g. process.env.NODE_ENV === 'testing').
<template>
<transition v-if="!disable" :name="name" :mode="mode">
<slot></slot>
</transition>
<div v-else>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
disable: Boolean,
name: String,
mode: String,
},
};
</script>
Purely for testing, I think Bill Criswell's answer is probably the cleanest.
You can set a variable on Vue to indicate testing, and set transition hooks to abort if you are testing.
For my example, you can control the value of the testing variable with a checkbox. The first test result indicates the state before anything happens, so it will just be the same as the third test result of the previous run. Other than that, you can flip the testing switch and get expected results every time.
I've revised my code to isolate the fadeTransition as a separate component with a slot, but I have not found a way to eliminate the added markup in the template for it.
new Vue({
el: '#app',
template: `
<span>
<input type="checkbox" v-model="Vue.testing"> Testing<br>
<button @click="test()">Run test</button>
<fade-transition>
<p id="transition" v-show="show">Hello, world!</p>
</fade-transition>
</span>
`,
components: {
fadeTransition: {
template: `
<transition name="fade"
@enter="killTransition"
@leave="killTransition"
><slot></slot>
</transition>
`,
methods: {
killTransition(el, done) {
if (Vue.testing) done();
}
}
}
},
data() {
return {
show: false,
testing: true
};
},
methods: {
test() {
const p = document.querySelector('#transition');
let display = getComputedStyle(p).display;
console.log('1. Display should be "none", it is:', display);
this.show = true;
this.$nextTick(() => {
display = getComputedStyle(p).display;
console.log('2. Display should be "block", it is:', display);
this.show = false;
this.$nextTick(() => {
display = getComputedStyle(p).display;
console.log('3. Display should be "none", it is:', display);
});
});
}
}
});
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app"></div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With