如何在VUE 3测试效果中测试过渡功能?

发布于 2025-01-23 17:49:26 字数 1318 浏览 0 评论 0 原文

我在带Jest和Vue测试UTLIS的Transiton功能方面遇到了问题。这是我的组件代码:

<div class="disclosure">
    <button
        class="label"
        type="button"
        @click="toggle"
    >
        <span class="label-text">
            <slot name="disclosure-title" />
        </span>
    </button>
    <Transition
        name="disclosure"
        @before-enter="transitionBaseState"
        @enter="transitionEndState"
        @before-leave="transitionEndState"
        @leave="transitionBaseState"
    >
        <div
            v-show="open"
            class="panel"
        >
            <div class="panel-content">
                <slot />
            </div>
        </div>
    </Transition>
</div>

我在过渡功能上有问题:过渡性和过渡设备。当我为组件运行EJST规格时,覆盖范围标签显示这些功能未涵盖。您知道测试这些功能的最佳方法是什么?我显示元素的测试是:

it('can be changed to opened by clicking the panel', async () => {
    await wrapper.find(buttonSelector).trigger('click');
    expect(wrapper.find(panelSelector).isVisible()).toBe(true);
});

函数:

function transitionBaseState(el: HTMLElement): void {
    el.style.height = '0';
}
function transitionEndState(el: HTMLElement): void {
    el.style.height = `${el.scrollHeight}px`;
}

I have a problem testing transiton functions with jest and vue test utlis. This is my component code:

<div class="disclosure">
    <button
        class="label"
        type="button"
        @click="toggle"
    >
        <span class="label-text">
            <slot name="disclosure-title" />
        </span>
    </button>
    <Transition
        name="disclosure"
        @before-enter="transitionBaseState"
        @enter="transitionEndState"
        @before-leave="transitionEndState"
        @leave="transitionBaseState"
    >
        <div
            v-show="open"
            class="panel"
        >
            <div class="panel-content">
                <slot />
            </div>
        </div>
    </Transition>
</div>

I have a problem with transition functions: transitionBaseState and transitionEndState. When I run ejst spec for the component, coverage tab shows that those functions are not covered. Do you know what is the best way to test those functions? My test for showing element is this:

it('can be changed to opened by clicking the panel', async () => {
    await wrapper.find(buttonSelector).trigger('click');
    expect(wrapper.find(panelSelector).isVisible()).toBe(true);
});

Functions:

function transitionBaseState(el: HTMLElement): void {
    el.style.height = '0';
}
function transitionEndState(el: HTMLElement): void {
    el.style.height = `${el.scrollHeight}px`;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

南风几经秋 2025-01-30 17:49:26

好的。这是艰难的。 vue测试用 false 值在全球范围内模拟它。

import { config, mount } from '@vue/test-utils'
...

// before Mount
config.global.stubs = {
  transition: false
}
jest.useFakeTimers() // to deal with durations
// mount
// trigger click

jest.advanceTimersByTime(duration + 1)
// expect whatever you need from before-enter, enter, after-enter callbacks
// trigger click

jest.advanceTimersByTime(duration + 1)
// expect whatever you need from before-leave, leave, after-leave callbacks

如您所见,我添加了使用假计时器的解决方法来处理过渡持续时间。

实际上,在大多数情况下,您只是不需要测试过渡的作用。 Vue Core团队已经对此进行了测试,但是也许在某些情况下应该对其进行测试。如您所见,测试中的行数增加只是为了避免默认行为。它还使用无证件模型,您应该在每次更新VUE/VUE检验效果之后保持工作,并进行一些破坏性的更改。

无论如何,我已经在上面测试了此代码。这是我的尝试

it('should call all the callbacks', async () => {
    expect.hasAssertions()
    const callbacks = {
      onBeforeEnter: jest.fn(),
      onEnter: jest.fn(),
      onAfterEnter: jest.fn(),
      onBeforeLeave: jest.fn(),
      onLeave: jest.fn(),
      onAfterLeave: jest.fn(),
    }

    const component = defineComponent({
      name: 'Component',
      data() {
        return {
          flag: false
        }
      },
      template: `<div>
        <transition
          @before-enter="onBeforeEnter"
          @enter="onEnter"
          @after-enter="onAfterEnter"
          @before-leave="onBeforeLeave"
          @leave="onLeave"
          @after-leave="onAfterLeave">
          <span v-if="flag"></span>
        </transition>
      </div>`,
      methods: {
        onBeforeEnter: callbacks.onBeforeEnter,
        onEnter(e: Event, done: Function) {
          callbacks.onEnter()
          done()
        },
        onAfterEnter() {
          callbacks.onAfterEnter()
        },
        onBeforeLeave() {
          callbacks.onBeforeLeave()
        },
        onLeave(e: Event, done: Function) {
          callbacks.onLeave();
          done()
        },
        onAfterLeave() {
          callbacks.onAfterLeave()
        }
      }
    })

    config.global.stubs = {
      transition: false
    }

    jest.useFakeTimers()
    const wrapper = mount(component)

    await wrapper.setData({ flag: true })

    expect(callbacks.onBeforeEnter).toHaveBeenCalled()
    expect(callbacks.onEnter).toHaveBeenCalled()
    expect(callbacks.onAfterEnter).toHaveBeenCalled()

    await wrapper.setData({ flag: false })

    jest.advanceTimersByTime(1)

    expect(callbacks.onBeforeLeave).toHaveBeenCalled()
    expect(callbacks.onLeave).toHaveBeenCalled()
    expect(callbacks.onAfterLeave).toHaveBeenCalled()
  })

Ok. This is the tough one. Vue test utils stubs transitions by default. And this behavior is not covered by docs. You need to mock it globally by hardcoded false value.

import { config, mount } from '@vue/test-utils'
...

// before Mount
config.global.stubs = {
  transition: false
}
jest.useFakeTimers() // to deal with durations
// mount
// trigger click

jest.advanceTimersByTime(duration + 1)
// expect whatever you need from before-enter, enter, after-enter callbacks
// trigger click

jest.advanceTimersByTime(duration + 1)
// expect whatever you need from before-leave, leave, after-leave callbacks

As you can see, I added workaround with fake timers to deal with transition duration.

In fact, in most cases you just don't need to test what transition does. This is already tested by vue core team, but maybe there are some cases where it should be tested. As you can see, the number of line in tests grows just to avoid default behavior. It also uses undocumented mocks and you should to keep that working after every update of Vue/vue-test-utils with some breaking changes.

Anyway I've tested this code above. Here is my attempt

it('should call all the callbacks', async () => {
    expect.hasAssertions()
    const callbacks = {
      onBeforeEnter: jest.fn(),
      onEnter: jest.fn(),
      onAfterEnter: jest.fn(),
      onBeforeLeave: jest.fn(),
      onLeave: jest.fn(),
      onAfterLeave: jest.fn(),
    }

    const component = defineComponent({
      name: 'Component',
      data() {
        return {
          flag: false
        }
      },
      template: `<div>
        <transition
          @before-enter="onBeforeEnter"
          @enter="onEnter"
          @after-enter="onAfterEnter"
          @before-leave="onBeforeLeave"
          @leave="onLeave"
          @after-leave="onAfterLeave">
          <span v-if="flag"></span>
        </transition>
      </div>`,
      methods: {
        onBeforeEnter: callbacks.onBeforeEnter,
        onEnter(e: Event, done: Function) {
          callbacks.onEnter()
          done()
        },
        onAfterEnter() {
          callbacks.onAfterEnter()
        },
        onBeforeLeave() {
          callbacks.onBeforeLeave()
        },
        onLeave(e: Event, done: Function) {
          callbacks.onLeave();
          done()
        },
        onAfterLeave() {
          callbacks.onAfterLeave()
        }
      }
    })

    config.global.stubs = {
      transition: false
    }

    jest.useFakeTimers()
    const wrapper = mount(component)

    await wrapper.setData({ flag: true })

    expect(callbacks.onBeforeEnter).toHaveBeenCalled()
    expect(callbacks.onEnter).toHaveBeenCalled()
    expect(callbacks.onAfterEnter).toHaveBeenCalled()

    await wrapper.setData({ flag: false })

    jest.advanceTimersByTime(1)

    expect(callbacks.onBeforeLeave).toHaveBeenCalled()
    expect(callbacks.onLeave).toHaveBeenCalled()
    expect(callbacks.onAfterLeave).toHaveBeenCalled()
  })
你怎么这么可爱啊 2025-01-30 17:49:26

这是我的解决方案:

const FakeTranstion = defineComponent({
  emits: ['afterEnter'],
  template: '<div><slot /></div>',
  setup(_, { emit }) {
    return {
      emitAfterEnter() {
        emit('afterEnter')
      },
    }
  },
})

const subject = mount(YourComponent, {
  propsData: {
    modelValue,
  },
  global: {
    stubs: {
      transition: FakeTranstion,
    },
  },
 })

 subject.findComponent(FakeTranstion).vm.emitAfterEnter()

This is my solution:

const FakeTranstion = defineComponent({
  emits: ['afterEnter'],
  template: '<div><slot /></div>',
  setup(_, { emit }) {
    return {
      emitAfterEnter() {
        emit('afterEnter')
      },
    }
  },
})

const subject = mount(YourComponent, {
  propsData: {
    modelValue,
  },
  global: {
    stubs: {
      transition: FakeTranstion,
    },
  },
 })

 subject.findComponent(FakeTranstion).vm.emitAfterEnter()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文