Cody Bontecou

Cypress Component Testing with Nuxt 3

March 20, 2023 · 3 minute read · Testing,Nuxt,Cypress

This is a guide on setting up Cypress Component Testing with Nuxt 3.

Initialize Nuxt project

npx nuxi init nuxt-project
cd nuxt-project
npm install

Install + initialize Cypress

npm install cypress typescript --save-dev
npx cypress open

Configure Cypress

Create a cypress.config.ts file in your root directory and add the following:

// cypress.config.ts
import { defineConfig } from 'cypress'

module.exports = defineConfig({
  component: {
    devServer: {
      framework: 'vue',
      bundler: 'vite',
    },
  },
})

This is the baseline configuration that works for Vue projects. Nuxt projects require more configuration to get going.

In your root directory, create an additional configuration file. I use vite.config.cypress.component.ts. In it, paste the following:

// `vite.config.cypress.component.ts`
import vue from '@vitejs/plugin-vue'

export default {
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: tag => tag.includes('-'),
        },
      },
    }),
  ],
}

Now, add this config to your cypres.config.ts file so that it looks like so:

// cypress.config.ts
import { defineConfig } from 'cypress'
import viteConfig from '~/vite.config.cypress.component.js'

module.exports = defineConfig({
  component: {
    devServer: {
      framework: 'vue',
      bundler: 'vite',
      viteConfig,
    },
  },
})

Unfortunately, I don't know exactly what this configuration is doing behind the scenes. I just know it works.

I found this code snippet in this Github thread by @elwinvaneede.

Additional Cypress Component Config

We need a few more files to make Cypress happy.

Create a cypress directory containing a component and a support directory in our root directory.

├── nuxt-project/
└── component/
│ ├── Hello.cy.ts
└── support/
│ ├── component-index.html
│ ├── component.ts
  • component-index.html is the base HTML file that Cypress mounts our components to.
    • This file looks like so:
// support/component-index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <title>Components App</title>
  </head>
  <body>
    <div data-cy-root></div>
  </body>
</html>
  • component.ts is where we write specific configurations for our component tests.

This file will provide the mount function:

// support/component.ts
import { mount } from 'cypress/vue'

declare global {
  namespace Cypress {
    interface Chainable {
      mount: typeof mount
    }
  }
}

Cypress.Commands.add('mount', mount)

Running the Component Test Runner

When you run npx cypress open, you should see the component test dashboard with a green configured button. Click it, then Chrome, then Start Component Testing in Chrome, and now we should see our spec runner.

In our Nuxt project, let's create a basic component to test against.

For funsies, I'm just creating a Hello.vue component with the following:

// components/Hello.vue
<template>
  <button>Hello World</button>
</template>

I'm rendering this within my root app.vue file:

// app.vue
<template>
  <div>
    <Hello />
  </div>
</template>

Now we can mount this component in a cypress test. In our cypress/component/Hello.cy.ts file, paste the following:

// cypress/component/Hello.cy.ts
import Hello from '../../components/Hello.vue'

describe('Hello.cy', () => {
  it('renders', () => {
    cy.mount(Hello)
  })
})

Styling Our Components with TailwindCSS Within the Tests

You're probably using a library such as TailwindCSS to style your component. If that's the case, you'll notice your component isn't styled when running within the component test runner.

Let's fix that.

Every tutorial I found said I should be able to import my css file within my cypress/support/component.ts file or link to it using the component-index.html file. I'm not sure why, but neither of these solutions worked for me.

Instead, I needed to create a Cypress plugin for tailwind at cypress/plugins/tailwind.ts with the following:

// cypress/support/component.ts
before(() => {
  cy.exec('npx tailwindcss -i ./assets/css/main.css -m').then(({ stdout }) => {
    if (!document.head.querySelector('#tailwind-style')) {
      const link = document.createElement('style')
      link.id = 'tailwind-style'
      link.innerHTML = stdout

      document.head.appendChild(link)
    }
  })
})

Then import this plugin into your cypress/support/component.ts file:

// cypress/support/component.ts
import { mount } from 'cypress/vue'
import '../plugins/tailwind'

declare global {
  namespace Cypress {
    interface Chainable {
      mount: typeof mount
    }
  }
}

Cypress.Commands.add('mount', mount)

From what I understand, this is compiling Tailwind and appending the css output to a style tag that is then loaded into the component test runner's HTML.

This does create a bit of lag, but it's the only way I've been able to solve this problem.

Source Code

You can view the code for this project at this repository.

Newsletter

Subscribe to get my latest content. No spam.