nuxt / test-utils

πŸ§ͺ Test utilities for Nuxt
http://nuxt.com/docs/getting-started/testing
MIT License
322 stars 83 forks source link

Emitted events doesn't work with defineModel #572

Open ExEr7um opened 1 year ago

ExEr7um commented 1 year ago

In v0.8.1 emitted events were fixed in mountSuspended, but they still doesn't work with defineModel.

Added some tests for it in nuxt/nuxt-vitest#193.

ExEr7um commented 1 year ago

@danielroe also added some tests for it in nuxt/nuxt-vitest#193. I included test with mount that passes and test with mountSuspended that fails.

danielroe commented 11 months ago

I've merged the tests in (as a TODO) in https://github.com/nuxt/test-utils/pull/629.

wattanx commented 9 months ago

I will write down what I have found in my research in case it helps anyone.

Even with defineModel, it works as expected for update:modelValue.

<button
  id="changeModelValue"
  @click="$emit('update:modelValue', true)"
 />

If modelValue = true, the event exists in the emitted of MountSuspendedComponent. You can confirm this by modifying the test as follows

// FIXME: fix this failing test
it('can receive emitted events from components mounted within nuxt suspense using defineModel', async () => {
  const component = await mountSuspended(WrapperTests)
  component.find('button#changeModelValue').trigger('click')
  // outouts: { 'update:modelValue': [ [ true ] ] }
  console.log(component.findComponent({ name: 'MountSuspendedComponent' }).emitted())
  expect(component.emitted()).toHaveProperty('update:modelValue')
})
bborlet commented 4 months ago

HI,

This does not work for me : The test is done on the dumbest component with v-model :

<script setup lang="ts">
const innerValue = defineModel({ type: String });
</script>
<template>
    <input v-model="innerValue" />
</template>
    it("emits input event", async () => {
        const wrapper = await mountSuspended(VMT, {
            props: {
                modelValue: "initialText",
                "onUpdate:modelValue": (e) =>
                    wrapper.setProps({ modelValue: e }),
            },
        });
        const input = wrapper.find("input");
        await input.setValue("newvalue");
        expect(wrapper.emitted("update:modelValue")).toBeTruthy(); 
    });

Everything comes straight from the doc, but

The same test but with vue/testutils used directly succeed, even though wrapper is still typed as any:

it("emits input event", async () => {
        const wrapper = mount(VMT, {
            props: {
                modelValue: "initialText",
                "onUpdate:modelValue": (e) =>
                    wrapper.setProps({ modelValue: e }),
            },
        });
        const input = wrapper.find("input");
        await input.setValue("newvalue");
        expect(wrapper.emitted("update:modelValue")).toBeTruthy();
    });

What is the recommended way to test this ? Maybe I should just not test that, it's practically like testing vue in this case right ?

sumomo015 commented 2 months ago

I found that when testing with the following code, a warning like this appears in the terminal. Hope it helps.

it('ε…₯εŠ›ε€€γŒtrimγ•γ‚Œγ‚‹γ“γ¨γ‚’η’Ίθͺγ™γ‚‹', async () => {
  const wrapper = await mountSuspended(PartsInputText, {
    props: {
      'modelValue': '',
      'name': 'test-input',
      'onUpdate:modelValue': (e: string) => wrapper.setProps({ modelValue: e }),
    },
  })
  await wrapper.find('input').setValue('  γƒ†γ‚Ήγƒˆ  ')
  expect(wrapper.emitted('update:modelValue')).toBeTruthy()
  expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['γƒ†γ‚Ήγƒˆ'])
  expect(wrapper.props('modelValue')).toBe('γƒ†γ‚Ήγƒˆ')
})
[Vue warn]: Property "onUpdate:modelValue" was accessed during render but is not defined on instance. 
  at <MountSuspendedComponent modelValue="" name="test-input" onUpdate:modelValue=fn<onUpdate:modelValue> > 
  at <MountSuspendedHelper> 
  at <Anonymous ref="VTU_COMPONENT" > 
  at <VTUROOT>
[Vue warn]: Property "onUpdate:modelValue" was accessed during render but is not defined on instance. 
  at <MountSuspendedComponent modelValue="γƒ†γ‚Ήγƒˆ" name="test-input" onUpdate:modelValue=fn<onUpdate:modelValue> > 
  at <MountSuspendedHelper> 
  at <Anonymous ref="VTU_COMPONENT" > 
  at <VTUROOT>
stafyniaksacha commented 1 week ago

This error also occurs without defineModel neither with option api:

<script setup lang="ts">
// const [modelValue] = defineModel<string>();

const props = defineProps<{
  modelValue: string;
}>();
const emits = defineEmits<{
  'update:modelValue': [value: string];
}>();

const modelValue = computed({
  get() {
    return props.modelValue;
  },
  set(value) {
    emits('update:modelValue', value);
  },
});
</script>
import { mountSuspended } from '@nuxt/test-utils/runtime';
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import MyComp from './MyComp.vue';

describe('MyComp', () => {
  it('should have v-model events working with mountSuspended', async () => {
    const comp = await mountSuspended(MyComp, {
      props: {
        modelValue: 'hello',
        'onUpdate:modelValue': (e: any) => comp.setProps({ modelValue: e }),
      },
    });

    await comp.get('input').setValue('test');
    // this one fails
    expect(comp.props('modelValue')).toBe('test');
  });

  it('should have v-model events working with mount', async () => {
    const comp = await mount(MyComp, {
      props: {
        modelValue: 'hello',
        'onUpdate:modelValue': (e: any) => comp.setProps({ modelValue: e }),
      },
    });

    await comp.get('input').setValue('test');
    expect(comp.props('modelValue')).toBe('test');
  });
});

repro: https://stackblitz.com/edit/nuxt-starter-ategmh?file=components%2FMyComp.vue,components%2FMyComp.spec.ts