Welcome to the NOVA LAB Launch Platform! Year 2087 - before sending systems to Mars, every piece of code must pass rigorous testing. Just as rocket systems are checked before launch, we test our Vue applications before deployment.
Vitest is a modern testing framework for Vue:
1npm install -D vitest @vue/test-utils happy-dom1// vitest.config.js
2import { defineConfig } from 'vitest/config'
3import vue from '@vitejs/plugin-vue'
4
5export default defineConfig({
6 plugins: [vue()],
7 test: {
8 globals: true,
9 environment: 'happy-dom'
10 }
11})1// package.json
2{
3 "scripts": {
4 "test": "vitest",
5 "test:ui": "vitest --ui",
6 "test:coverage": "vitest --coverage"
7 }
8}1// components/Counter.spec.js
2import { describe, it, expect } from 'vitest'
3import { mount } from '@vue/test-utils'
4import Counter from './Counter.vue'
5
6describe('Counter', () => {
7 it('renders count', () => {
8 const wrapper = mount(Counter)
9 expect(wrapper.text()).toContain('Count: 0')
10 })
11
12 it('increments count on button click', async () => {
13 const wrapper = mount(Counter)
14
15 await wrapper.find('button.increment').trigger('click')
16
17 expect(wrapper.text()).toContain('Count: 1')
18 })
19
20 it('accepts initial count prop', () => {
21 const wrapper = mount(Counter, {
22 props: {
23 initialCount: 10
24 }
25 })
26
27 expect(wrapper.text()).toContain('Count: 10')
28 })
29})1<!-- Counter.vue -->
2<template>
3 <div class="counter">
4 <p>Count: {{ count }}</p>
5 <button class="increment" @click="count++">+</button>
6 <button class="decrement" @click="count--">-</button>
7 </div>
8</template>
9
10<script setup>
11import { ref } from 'vue'
12
13const props = defineProps({
14 initialCount: {
15 type: Number,
16 default: 0
17 }
18})
19
20const count = ref(props.initialCount)
21</script>1import { mount } from '@vue/test-utils'
2import ArtworkCard from './ArtworkCard.vue'
3
4describe('ArtworkCard', () => {
5 it('emits select event on click', async () => {
6 const artwork = {
7 id: 1,
8 title: 'Mona Lisa',
9 artist: 'Leonardo'
10 }
11
12 const wrapper = mount(ArtworkCard, {
13 props: { artwork }
14 })
15
16 await wrapper.trigger('click')
17
18 expect(wrapper.emitted()).toHaveProperty('select')
19 expect(wrapper.emitted('select')[0]).toEqual([artwork])
20 })
21
22 it('emits favorite event on heart click', async () => {
23 const wrapper = mount(ArtworkCard, {
24 props: {
25 artwork: { id: 1, title: 'Test' }
26 }
27 })
28
29 await wrapper.find('.favorite-btn').trigger('click')
30
31 expect(wrapper.emitted('favorite')).toBeTruthy()
32 })
33})1import { mount } from '@vue/test-utils'
2import BaseCard from './BaseCard.vue'
3
4describe('BaseCard', () => {
5 it('renders default slot', () => {
6 const wrapper = mount(BaseCard, {
7 slots: {
8 default: '<p>Test content</p>'
9 }
10 })
11
12 expect(wrapper.html()).toContain('Test content')
13 })
14
15 it('renders named slots', () => {
16 const wrapper = mount(BaseCard, {
17 slots: {
18 header: '<h2>Header</h2>',
19 default: '<p>Body</p>',
20 footer: '<footer>Footer</footer>'
21 }
22 })
23
24 expect(wrapper.find('h2').text()).toBe('Header')
25 expect(wrapper.find('p').text()).toBe('Body')
26 expect(wrapper.find('footer').text()).toBe('Footer')
27 })
28})