Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation Errors Not Displayed in Snapshot Tests or When trying to get Error Messages with vee-validate in Nuxt 3 #4968

Open
1 of 5 tasks
roneldornas opened this issue Jan 6, 2025 · 2 comments

Comments

@roneldornas
Copy link

roneldornas commented Jan 6, 2025

What happened?

When running snapshot tests for a login form using vee-validate with shadcn-vue in a Nuxt 3 application, the validation error messages are not displayed in the snapshot. The test simulates a button click to trigger form submission, but the expected error messages do not appear in the snapshot, indicating that the validations are not being triggered or resolved (I think this the real problem) correctly during the test.

Expected Behavior:
The snapshot should contain the validation error messages after the button click triggers the form submission.

Actual Behavior:
The snapshot does not contain the validation error messages, indicating that the validations are not being triggered or resolved correctly during the test.

Additional Context:
The form validation works correctly when tested manually in the browser, but the issue only occurs during the automated tests.

Reproduction steps

  1. Set up a Nuxt 3 project with vee-validate for form validation.
  2. Create a login form component with vee-validate validation rules.
  3. Write a test using vitest and @vue/test-utils to simulate a button click and capture the snapshot.
  4. Run the test and observe that the snapshot does not contain the expected validation error messages.

Version

Vue.js 3.x and vee-validate 4.x

What browsers are you seeing the problem on?

  • Firefox
  • Chrome
  • Safari
  • Microsoft Edge

Relevant log output

// Test code
import { it, expect, describe, beforeEach } from 'vitest';
import { mountSuspended } from '@nuxt/test-utils/runtime';
import type { VueWrapper } from '@vue/test-utils';
import { flushPromises } from '@vue/test-utils';
import waitForExpect from 'wait-for-expect';

import App from '@/app.vue';

describe('Login page tests', () => {
  let wrapper: VueWrapper;

  beforeEach(async () => {
    wrapper = await mountSuspended(App, {
      route: '/login',
    });
  });

  it('should match snapshot', () => {
    expect(wrapper.html()).toMatchSnapshot();
  });

  it('should match snapshot after click button and inputs show errors', async () => {
    const button = wrapper.findComponent('button[type="submit"]');

    await button.trigger('click');
    await flushPromises();

    await waitForExpect(() => {
      expect(wrapper.html()).toMatchSnapshot();
    });
  });
});


// Component code
<template>
  <div class="w-full">
    <div class="w-full text-center mb-2">
      <h2 class="text-xl font-semibold">{{ $t('auth.login.title') }}</h2>
    </div>

    <form ref="form" novalidate @submit="onSubmit">
      <FormField
        v-slot="{ componentField }"
        :validate-on-model-update="false"
        name="email"
      >
        <FormItem class="mb-2">
          <FormLabel>
            {{ $t('auth.login.form.inputs.email.label') }}
          </FormLabel>

          <FormControl>
            <Input
              type="text"
              :placeholder="$t('auth.login.form.inputs.email.placeholder')"
              v-bind="componentField"
            />
          </FormControl>

          <FormMessage />
        </FormItem>
      </FormField>

      <FormField
        v-slot="{ componentField }"
        :validate-on-model-update="false"
        name="password"
      >
        <FormItem class="mb-4">
          <FormLabel>
            {{ $t('auth.login.form.inputs.password.label') }}
          </FormLabel>

          <FormControl>
            <Input
              type="password"
              :placeholder="$t('auth.login.form.inputs.password.placeholder')"
              v-bind="componentField"
            />
          </FormControl>

          <FormMessage />
        </FormItem>
      </FormField>

      <Button type="submit" class="w-full">
        {{ $t('auth.login.form.button') }}
      </Button>
    </form>

    <div class="my-4 flex flex-col w-full gap-4 justify-center items-center">
      <NuxtLink :to="AuthRoutes.Recover" class="text-sm opacity-60">
        {{ $t('auth.login.actions.recover') }}
      </NuxtLink>

      <div
        class="w-full flex flex-col justify-center items-center border-t pt-2"
      >
        <p class="text-sm opacity-60">
          {{ $t('auth.login.actions.register.description') }}
        </p>

        <NuxtLink :to="AuthRoutes.Register" class="text-sm font-bold">
          {{ $t('auth.login.actions.register.action') }}
        </NuxtLink>
      </div>
    </div>
  </div>
</template>

<script setup>
import { useForm } from 'vee-validate';
import { object, string } from 'yup';

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form';
import { AuthRoutes } from '~/enums/routes';

definePageMeta({
  layout: 'auth',
});

const { t } = useI18n();

const formSchema = object({
  email: string()
    .required(
      t('validations.required', {
        field: t('auth.login.form.inputs.email.label').toLowerCase(),
      })
    )
    .email(
      t('validations.valid', {
        field: t('auth.login.form.inputs.email.label').toLowerCase(),
      })
    ),

  password: string().required(
    t('validations.required', {
      field: t('auth.login.form.inputs.password.label').toLowerCase(),
    })
  ),
});

const form = useForm({
  validationSchema: formSchema,
  initialValues: {
    email: '',
    password: '',
  },
});

const onSubmit = form.handleSubmit((values) => {
  console.log('Form submitted!', values);
});
</script>


//; Test configuration
import { defineVitestConfig } from '@nuxt/test-utils/config';

export default defineVitestConfig({
  test: {
    setupFiles: ['tests/setup.ts'],
    environmentOptions: {
      nuxt: {
        mock: {
          intersectionObserver: true,
        },
      },
    },
  },
});

// setup.ts
import { config } from '@vue/test-utils';
import { createI18n } from 'vue-i18n';

import en from '@/lang/en';

const i18n = createI18n({
  locale: 'en',
  messages: { en },
  missing: (_, key) => console.error(key),
});

config.global.plugins.push(i18n);

Demo link

testing

Code of Conduct

@YuriAsthar
Copy link

Same here

@matheusfnl
Copy link

I'm encountering the same issue!

I followed the documentation's instructions and included wait-for-expect to handle the rendering of error messages. However, even with this, the error messages didn't appear in the HTML.

Here's my test code:

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants