Skip to content

Svelte Component Testing with Cypress + Vite

Cypress is a well-known e2e and integration testing framework. But since v7, a Cypress Component Test Runner was introduced, and it can be used to render and test components in isolation. It's still in alpha, so things may change! In this blog post, we will set up our environment to test Svelte components while using Vite.

🔗Starting a new project

First, we will start by creating a new project with Vite.

npm init vite@latest
// Project name: › cypress-svelte-testing
// Select a framework: › svelte
// Select a variant: › svelte-ts

cd cypress-svelte-testing
npm install

*Note: check Vite's documentation to find out how to start a project with other package managers.

The project is now configured to use Svelte and TypeScript.

To start our server, we need to run the command npm run dev.

Svelte default site

We will be testing this application which consists of two components: App and Counter. App is the shell, or the main component wrapping the counter component: a button that updates its count when clicked.

🔗Installing dependencies

To run our tests, we need to install a few dependencies: cypress: The testing framework @cypress/vite-dev-server: Responsible for lanching and restarting the server when files change. cypress-svelte-unit-test: A package to mount our components in the testing environment. Unfortunately, there's is no support at the moment of writing for our current environment, so we will be using a fork, and then we'll install it from the repo. @testing-library/cypress: Optional. Adds selectors for our queries.

Let's go ahead and install all of these as dev-dependencies.

npm i --save-dev cypress @cypress/vite-dev-server @testing-library/cypress

npm i --save-dev https://github.com/flakolefluk/cypress-svelte-unit-test

🔗Configuring the test environment

Now, we need to follow we will have to update or create some files to configure our environment. We'll go through each of them.

  • <root>/cypress.json to let cypress know where to find the component test files.
{
  "componentFolder": "src",
  "testFiles": "**/*spec.{js,jsx,ts,tsx}"
}
  • <root>/cypress/plugins/index.ts to configure the dev server:
import { startDevServer } from '@cypress/vite-dev-server';
import path from 'path';

module.exports = (on, config) => {
  on('dev-server:start', async (options) => {
    return startDevServer({
      options,
      viteConfig: {
        configFile: path.resolve(__dirname, '..', '..', 'vite.config.js'),
      },
    });
  });

  return config;
};
  • <root>/cypress/support/commands.js to add the testing library commands
import '@testing-library/cypress/add-commands';
  • <root>/tsconfig.json to add typescript support for the testing library commands
{
  // ...
  "compilerOptions": {
	// ...
	// add the following and preserve the rest of the config file
    "types": ["cypress", "@testing-library/cypress"]
  },
  // ...
}
  • <root>/package.json to add a few scripts that will run our tests
{
  // ...
  "scripts": {
// ...
    "cy:open-ct": "cypress open-ct",
    "cy:run-ct": "cypress run-ct"
  },
  // ...
}

Our tests are ready to be written!

In our cypress configuration file, we declared that the tests will live inside the src. This makes it possible to locate our tests next to our components.

🔗Writing tests

We will test both components that were created along with the project.

Create two files, one next to each component:

// <root>/src/App.spec.ts
import App from './App.svelte';
import { mount } from 'cypress-svelte-unit-test';
describe('App', function () {
  it('renders App with correct heading', () => {
    mount(App);
    cy.findByRole('heading', { level: 1, name: /hello typescript/i }).should(
      'exist'
    ).should('be.visible');
  });
});
// <root>/src/Counter.spec.ts
import Counter from './Counter.svelte';
import { mount } from 'cypress-svelte-unit-test';
describe('Counter', function () {
  it('Renders button and updates count when clicked', () => {
    mount(Counter);
    cy.findByRole('button', { name: 'Clicks: 0' }).should('exist');
    cy.findByRole('button', { name: /Click/i }).click();
    cy.findByRole('button', { name: 'Clicks: 1' }).should('exist');
  });
});

I wrote a couple of tests as a start. First, we check the App component and that it contains a heading with content, we also check that the element is visible.

For our Counter component, we first check that it exists. Then we click it and verify that the text inside it is updated with the expected current count.

In both tests, I'm using the queries provided by the *testing-library * package.

🔗Running our tests

We can run our tests in two ways, with the run-ct and open-ct commands. The first one will tun the tests headlessly by default, while the second one will open the test runner.

Let's try both.

  • npm run cy:run-ct

Your tests will run in your terminal and you'll get a nice overview of them.

Screen Shot 2021-10-15 at 17.09.07

  • npm run cy:open-ct

Your tests will open in a browser and you'll be able to go through every step of your tests, which will make debugging a lot easier as you can see what's happening.

cypress-svelte-01 cypress-svelte-02

Time to write more tests!

🔗Final thoughts

I hope this tutorial will help you set up your environment for working with Vite and Cypress to test your Svelte components. I find that testing with the open-ct command is really useful when writing tests. Most of the configuration shown is applicable to other frameworks too (There are other packages that will mount the components). Check the official documentation for more. You can find the code shown here in this repo.


This Dot Labs is a development consultancy focused on providing staff augmentation, architectural guidance, and consulting to companies.

We help implement and teach modern web best practices with technologies such as React, Angular, Vue, Web Components, GraphQL, Node, and more.

You might also like

Javascript

Getting Started with RxJS

Javascript

Testing Web Components with Cypress and TypeScript

Javascript

Web Components Integration using LitElement and TypeScript

Javascript

Navigation Lifecycle using Vaadin Router, LitElement and TypeScript