Leon Gaban
Leon Gaban

Reputation: 39034

Vitest error "TypeError: Cannot read properties of undefined (reading 'resolve')" (router.resolve)

I have a very basic test, however it throws the error below when I try to mount a component const wrapper = mount(HomeHeader). I've pasted my vite config, test, component and packages.

This error happened without updating my vite config, but I went ahead and updated the config with test: { globals: true, } so I don't have to import test and expect.

Full error

stderr | src/components/home/__tests__/HomeHeader.test.js > HomeHeader renders properly
[Vue warn]: injection "Symbol([vue-router]: router)" not found.
  at <RouterLink to="/" >
  at <HomeHeader ref="VTU_COMPONENT" >
  at <VTUROOT>
[Vue warn]: injection "Symbol([vue-router]: route location)" not found.
  at <RouterLink to="/" >
  at <HomeHeader ref="VTU_COMPONENT" >
  at <VTUROOT>
[Vue warn]: Unhandled error during execution of setup function
  at <RouterLink to="/" >
  at <HomeHeader ref="VTU_COMPONENT" >
  at <VTUROOT>

FAIL  src/components/home/__tests__/HomeHeader.spec.js > HomeHeader > renders properly
TypeError: Cannot read properties of undefined (reading 'resolve')
 ❯ ReactiveEffect.fn node_modules/vue-router/dist/vue-router.cjs.js:2068:45
    2066|     const router = vue.inject(routerKey);
    2067|     const currentRoute = vue.inject(routeLocationKey);
    2068|     const route = vue.computed(() => router.resolve(vue.unref(props.to)));
       |                                             ^

Vitest UI

enter image description here

Vite Config

import { fileURLToPath, URL } from 'url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'

// https://vitejs.dev/config/
export default defineConfig({
  test: {
    globals: true,
  },
  plugins: [vue(), vueJsx()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
})

My test

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

describe('HomeHeader', () => {
  it('renders properly', () => {
    // This breaks
    const wrapper = mount(HomeHeader)
    expect(wrapper.text()).toContain('MOONHOLDINGS.XYZ')
  })
})

The component being tested

<script setup>
import { RouterLink } from 'vue-router'
import {
  MOON_XYZ,
  LOGIN,
  LOGIN_PATH,
  GET_STARTED,
  SIGN_UP_PATH,
} from '../../constants'
import PrimaryBtn from '@/components/partials/PrimaryBtn.vue'
</script>

<template>
  <main>
    <header>
      <h1>
        <RouterLink to="/">
          {{ MOON_XYZ }}
        </RouterLink>
      </h1>
      <nav>
        <RouterLink :to="LOGIN_PATH">{{ LOGIN }}</RouterLink>
        <PrimaryBtn :copy="GET_STARTED" :url="SIGN_UP_PATH" />
      </nav>
    </header>
  </main>
</template>

<style lang="scss" scoped>
h1 {
  margin: 1em;
  font-size: 1.375rem;
  color: #fff;
}

a {
  color: #fff;
  text-decoration: none;
}

nav {
  position: absolute;
  right: 2em;

  a {
    display: inline-block;
    padding: 0 1rem;
    font-size: 2em;
    text-decoration: none;
    color: #fff;
    transition: 0.4s;

    .router-link-exact-active {
      color: #fff;
    }

    .router-link-exact-active:hover {
      background-color: transparent;
    }

    &:hover {
      text-decoration: underline;
    }

    &:first-of-type {
      border: 0;
    }
  }
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }
}
</style>

Script command "coverage": "vitest run --coverage",

My dependencies

"dependencies": {
  "animate.css": "^4.1.1",
  "axios": "^0.27.2",
  "pinia": "^2.0.14",
  "vue": "^3.2.36",
  "vue-router": "^4.0.15"
},
"devDependencies": {
  "@rushstack/eslint-patch": "^1.1.0",
  "@vitejs/plugin-vue": "^2.3.3",
  "@vitejs/plugin-vue-jsx": "^1.3.10",
  "@vitest/ui": "^0.20.3",
  "@vue/eslint-config-prettier": "^7.0.0",
  "@vue/test-utils": "^2.0.2",
  "c8": "^7.11.3",
  "eslint": "^8.5.0",
  "eslint-plugin-vue": "^9.0.0",
  "happy-dom": "^6.0.4",
  "jsdom": "^19.0.0",
  "npm-run-all": "^4.1.5",
  "prettier": "^2.5.1",
  "ramda": "^0.28.0",
  "sass": "^1.53.0",
  "start-server-and-test": "^1.14.0",
  "vite": "^2.9.9",
  "vitest": "^0.20.3",
  "vue-tsc": "^0.35.2"
}

Anyone else run into this before while using Vitest?

enter image description here

Upvotes: 8

Views: 25276

Answers (1)

Metalmi
Metalmi

Reputation: 581

Vue Router is automatically mocked and because of that, no methods can be called from it. That's the reason why you are getting errors on calling resolve().

Correct test should look like this:

import { vi } from 'vitest';
import { mount } from '@vue/test-utils'
import HomeHeader from '../HomeHeader.vue'

// you need to mock router
vi.mock('vue-router', () => ({
  resolve: vi.fn(),
}));

describe('HomeHeader', () => {
  it('renders properly', () => {
    const wrapper = mount(HomeHeader)
    expect(wrapper.text()).toContain('MOONHOLDINGS.XYZ')
  })
})

Upvotes: 6

Related Questions