Continuous integration for Svelte applications
Fullstack Developer and Tech Author
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:
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.
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.
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
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.
Enter the name of the branch where your configuration file is located. Click Set Up Project.
This will trigger the pipeline and build successfully.
Click build to review the details of the test results.
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!