TutorialsLast Updated Jan 5, 20245 min read

Continuous integration for Svelte applications

Fikayo Adepoju

Fullstack Developer and Tech Author

Developer A sits at a desk working on a beginner-level project.

Svelte is a popular Javascript framework, used by 21% of JavaScript developers (State of JS 2022). Unlike other frameworks, Svelte does not do its DOM-updating work in the browser using the Virtual DOM. Instead, Svelte compiles JavaScript code in its build step to efficiently update your DOM when a state change occurs. This strategy has led to its steadily rising popularity and adoption by JavaScript developers.

In this tutorial, you will create an automated continuous integration (CI) pipeline that automates the process of running tests written for Svelte apps.

Prerequisites

To follow this tutorial, a few things are required:

  1. Basic knowledge of Javascript
  2. Node.js installed on your system
  3. A CircleCI account
  4. A GitHub account

Our tutorials are platform-agnostic, but use CircleCI as an example. If you don’t have a CircleCI account, sign up for a free one here.

With all these installed and set up, you can begin the tutorial.

Scaffolding a new Svelte project

To begin, you need to create our Svelte project. Choose a location on your system, then run:

npm create svelte@latest svelte-sample-app

This command uses SvelteKit to scaffold the new Svelte project. This is the approach recommended by the Svelte team.

You will prompted to provide details of the app. Respond like this:


Need to install the following packages:
create-svelte@5.1.1
Ok to proceed? (y) y

create-svelte version 5.1.1

┌  Welcome to SvelteKit!
│
◇  Which Svelte app template?
│  SvelteKit demo app
│
◇  Add type checking with TypeScript?
│  Yes, using TypeScript syntax
│
◇  Select additional options (use arrow keys/space bar)
│  Add ESLint for code linting, Add Prettier for code formatting, Add Vitest for unit testing
│
└  Your project is ready!

✔ Typescript
  Inside Svelte components, use <script lang="ts">

✔ ESLint
  https://github.com/sveltejs/eslint-plugin-svelte

✔ Prettier
  https://prettier.io/docs/en/options.html
  https://github.com/sveltejs/prettier-plugin-svelte#options

✔ Vitest
  https://vitest.dev

This command will begin scaffolding a new Svelte project. The svelte-sample-app part defines the project folder’s name. You can use any name you like.

Once the scaffolding process is done, go into the root of your project and install the required packages:

cd svelte-sample-app
npm install

Once the installation is complete, boot up a server to serve the application in development mode. Run:

npm run dev

This will serve the application at http://localhost:5173/

Note: 5173 is the default port. When it is in use, a different port will be assigned.

App first view

You now have a functional Svelte app up and running.

Setting up testing with Vitest and the Svelte testing library

As part of the initial project scaffolding, you instructed Svelte to add Vitest for unit testing. This makes it easier to focus on writing your test suites. You also need to install these:

  • @testing-library/svelte: The Svelte testing library.
  • jsdom: A library that parses and interacts with assembled HTML just like a browser.

To install them, run this:

npm i -D @testing-library/svelte jsdom

Now, you have everything needed to run tests for your Svelte components with Vitest. Find out more about Vitest here.

Adding tests to our Svelte project

To begin writing tests, create a simple component that will be tested later on. Inside the src/components folder, create a new file named ButtonComp.svelte and add thisy code:

<script>
	export let text = '';

	let buttonText = 'Button';

	function handleClick() {
		buttonText = 'Button Clicked';
	}
</script>

<h1>{text}</h1>

<button on:click={handleClick}>{buttonText}</button>

This code creates a component that displays a header containing a dynamic text variable. This variable can be passed into the component by a parent component. It also displays a button that changes its label when clicked, using the click event handled by a handleClick method.

To test the new component, place it in your application’s homepage. Open the src/routes/+page.svelte file and replace the entire contents with this code snippet:

<script>
	import welcome from '$lib/images/svelte-welcome.webp';
	import welcome_fallback from '$lib/images/svelte-welcome.png';
	import ButtonComp from '../components/ButtonComp.svelte';
</script>

<svelte:head>
	<title>Home</title>
	<meta name="description" content="Svelte demo app" />
</svelte:head>

<section>
	<h1>
		<span class="welcome">
			<picture>
				<source srcset={welcome} type="image/webp" />
				<img src={welcome_fallback} alt="Welcome" />
			</picture>
		</span>

		to your new<br />Svelte app
	</h1>

	<main>
		<p>
			Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte
			apps.
		</p>
		<ButtonComp text="Click on button below" />
	</main>
</section>

<style>
	section {
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;
		flex: 0.6;
	}

	.welcome {
		display: block;
		position: relative;
		width: 100%;
		height: 0;
		padding: 0 0 calc(100% * 495 / 2048) 0;
	}

	.welcome img {
		position: absolute;
		width: 100%;
		height: 100%;
		top: 0;
		display: block;
	}

	main {
		text-align: center;
		padding: 1em;
		max-width: 240px;
		margin: 0 auto;
	}

	h1 {
		color: #ff3e00;
		text-transform: uppercase;
		font-size: 3em;
		font-weight: 100;
	}

	@media (min-width: 640px) {
		main {
			max-width: none;
		}
	}
</style>

This code replaces the existing content of the home page, imports the ButtonComp component, and creates an instance of it. The code passes in the Click on button below value to the text prop.

App with component

The header displays Click on button below because that was passed as a string to the text prop. Click the button and observe the label change from Button to Button Clicked. You will be writing tests to assert that these behaviors are exhibited by the component.

Inside the src folder, create a new tests folder for the test suite for the ButtonComp component. Within the new folder, create a file named button.test.ts and add this:

import { cleanup, render, fireEvent, screen } from "@testing-library/svelte";
import { describe, it, expect, afterEach, beforeEach } from "vitest";
import ButtonComp from "../components/ButtonComp.svelte";

/**
 * @vitest-environment jsdom
 */
describe("Hello Svelte", () => {
  beforeEach(() => {
    render(ButtonComp, { text: "Click on button below" });
  });

  afterEach(() => cleanup());

  it("'Click on button below' is rendered on the header", () => {
    const button = screen.getByRole("heading");

    expect(button.innerHTML).toBe("Click on button below");
  });

  it("Button text changes when button is clicked", async () => {
    const button = screen.getByText("Button");

    await fireEvent.click(button);

    expect(button.innerHTML).toBe("Button Clicked");
  });
});

This file imports the ButtonComp component and tests it. The first test confirms that the label Click on button below is found in your component, rendering it by passing the string Click on button below as the value of the text prop.

The second test checks the behaviour of the button within the component. It is rendered once again and gets a reference to the button within it. Then the click event on the button is fired, and its current state is checked to confirm that the button’s label indeed changes to the expected Button Clicked string.

Save the file and use the test command on the terminal to run your tests:

npm run test

Local test run

Your tests running successfully and they both passed.

Automating our tests

Now that you have set up your project and run its test locally, you can write your CI pipeline script to automate the testing process.

Inside the root of the project, create a new folder named .circle. Within this folder, create a config.yml file and add this code to it:

version: 2.1

orbs:
  node: circleci/node@5.1.0

jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: "cimg/base:stable"
    steps:
      - checkout
      - node/install
      - run:
          name: Install Dependencies
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules
      - run:
          name: Run tests
          command: npm run test

This config first pulls the circleci/node orb, which allows you to install packages with caching enabled by default. It enables caching for the project’s dependencies and then runs your tests.

Review pushing your project to GitHub to learn how to deploy your project to Github.

Next, log into your CircleCI account. If you signed up with your GitHub account, all your repositories will be displayed on your project’s dashboard.

Next to your svelte-sample-app project, click Set Up Project.

Add project - CircleCI

Enter the name of the branch where your configuration file is located. Click Set Up Project.

Add Config - CircleCI

This will trigger the pipeline and build successfully.

Build Success - CircleCI

Click build to review the details of the test results.

Build Success - CircleCI

Your tests are running perfectly!

Conclusion

In this tutorial, you built a continuous integration (CI) pipeline to automate the testing of a Svelte app. As you continue to add more features and more tests to the project, a simple push to the repository is all you need to make sure your tests are run. Getting pass or fail results helps guide your development and keeps you from committing bad code to your repo.

The full project can be found here on GitHub.

As a follow-up, you can extend your CI practice with continuous deployment to automatically deploy your updates to your preferred hosting platform. Check out Automatically deploy Svelte applications to Heroku for an example.

Happy coding!


Fikayo Adepoju is a LinkedIn Learning (Lynda.com) Author, Full-stack developer, technical writer, and tech content creator proficient in Web and Mobile technologies and DevOps with over 10 years experience developing scalable distributed applications. With over 40 articles written for CircleCI, Twilio, Auth0, and The New Stack blogs, and also on his personal Medium page, he loves to share his knowledge to as many developers as would benefit from it. You can also check out his video courses on Udemy.

Copy to clipboard