Unit tests with Nuxt 3 and Vitest

We found these to be the best packages for testing with Nuxt 3. It takes car of a lot of config you would have to do manually with just Vitest. Especially auto imports.

Just for example – do not use! This is the full setup via defineConfig and Vite

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import path from 'path';

export default defineConfig({
  plugins: [
    vue(),
    Components({
      dirs: ['./components'], // this will auto import everything from within nuxt
    }),
    AutoImport({
      imports: ['vue', 'vitest', 'pinia'],
      dirs: ['./components', './composables/**/*', './stores/**/*', './plugins', './config'],
      dts: true,
    }),
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './'),
      '~': path.resolve(__dirname, './'),
    },
  },
  test: {
    globals: true,
    environment: 'jsdom',
  },
});

Setup

pnpm add -D nuxt-vitest vitest@0.33 happy-dom vitest-environment-nuxt

Newer Vitest releases have a bug so use vitest@0.33 for now.

// Nuxt.config.ts
export default defineNuxtConfig({
  // ...
  modules: [
    'nuxt-vitest'
  ]
})
// vitest.config.js
import { defineVitestConfig } from 'nuxt-vitest/config';
export default defineVitestConfig({
  test: {
    environment: 'nuxt',
  },
});

Testing a component

Component

// ApiTypeTabsDescription.vue
<template>
  <div class="mb-5" data-test-id="text-with-more">
    <p class="default-registration-info-text">
      {{ applicationInstitutionTabDescription }}
      <a class="register-show-more-btn-text" @click="showSeeMoreText = !showSeeMoreText">
        {{ moreOrHideText }}
      </a>
    </p>
    <p v-if="showSeeMoreText" class="register-show-more-description">
      {{ moreInfoText }}
    </p>
  </div>
</template>
<script setup lang="ts">
const applicationInstitutionTabDescription =
  'Please select tab based on the PSD2 permissions authorised for this software';
const moreInfoText =
  'This depends on whether software is authorised to retrieve account data provided by banks and financial institutions (AIS) and/or initiate payments from a user’s account (PIS)';
const showSeeMoreText = ref(false);
const moreOrHideText = computed(() => {
  if (showSeeMoreText.value === false) {
    return 'More';
  } else {
    return 'Hide';
  }
});
</script>

Test file

// components/__tests__/ApiTypeTabsDescription.spec.ts

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount, VueWrapper } from '@vue/test-utils';

import ApiTypeTabsDescription from '~/components/ApiTypeTabsDescription.vue';
let wrapper: VueWrapper<any, any>;
wrapper = mount(ApiTypeTabsDescription, { props: {} });

describe('ApiTypeTabsDescription', () => {
  it('Default text exists', async () => {
    expect(wrapper.find('[data-test-id="text-with-more"]').exists()).toBe(true);
  });

  it('More text does not exist', async () => {
    expect(wrapper.find('.register-show-more-description').exists()).toBe(false);
  });

  it('More text is visible', async () => {
    const toggle = wrapper.find('.register-show-more-btn-text');
    await toggle.trigger('click');
    await wrapper.vm.$nextTick();
    expect(wrapper.find('.register-show-more-description').exists()).toBe(true);
  });

  it('More text is hidden again', async () => {
    const toggle = wrapper.find('.register-show-more-btn-text');
    // NB. this is the 2nd click in the mount so it will be false
    await toggle.trigger('click');
    await wrapper.vm.$nextTick();
    expect(wrapper.find('.register-show-more-description').exists()).toBe(false);
  });
});

Running the test with watchers

Add

// package.json
   "scripts": {
//   ...
     "test": "vitest"
//   ...

Run

pnpm test

Posted

in

, ,

by

Tags:

Comments

Leave a Reply