Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I disable vue.js transitions for tests?

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>
like image 771
Tobbe Avatar asked Jun 29 '17 13:06

Tobbe


4 Answers

I essentially change all my transition and transition-groups 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)
}
like image 129
Bill Criswell Avatar answered Oct 19 '22 02:10

Bill Criswell


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.

like image 44
dankuck Avatar answered Oct 19 '22 03:10

dankuck


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.

like image 23
Adam McCann Avatar answered Oct 19 '22 04:10

Adam McCann


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>
like image 29
Roy J Avatar answered Oct 19 '22 02:10

Roy J