Testing Lit components created with TypeScript and Vite using Web Test Runner

By Markus Johansson

With only about a month left until the initial release of Umbraco 14 aka Bellissima and the new Backoffice, I'm working hard to migrate/update my email package Newsletter Studio. This package makes it possible to create and design emails to send as campaigns or use as transactional emails.

The new Backoffice

The new Umbraco backoffice is based on Web Components and implemented using Lit and TypeScript. Web Components and Lit were new technologies to me when I started the migration, and it took quite some learning to get up to speed, but it has been a fun journey.

My package is quite complicated so I figured I needed a way to run different kinds of tests (unit tests, end-2-end tests). As always, I looked at the Umbraco source code to find some inspiration. I must say that it has been very interesting and I've learned a lot from looking at how the great minds at Umbraco HQ have done things.

Web Test Runner

When it comes to testing they've chosen to use Web Test Runner which is a modern test runner that will execute tests in a real browser. Many other test runners will execute in a Node environment and "mock" the browser DOM (which is not an easy thing to do), Web Test Runner will execute all the unit tests in a real browser which saves us a lot of hassle.

Now I've set up my project to be ready for the new import map feature in modern browsers. This feature basically allows us to write JavaScript code imports like:"import {Thing} from 'lib';" and then tell the browser which file/URL to load when it sees "lib".

When using TypeScript and Vite it's quite easy to set this up using TypeScript paths, Matt Brailsford wrote a a great article about this that I highly recommend reading.

Now, when I added Web Test Runner to my project I started by trying to use the same dependencies as Bellissima but I had a really hard time getting my imports to work in the browser that executed the tests. The @web/test-runner uses @web/dev-server to serve code to the browser during the test runs. Since we're using TypeScript the recommended approach seems to be to use @web/dev-server-esbuild and since we want import maps we would also need @web/dev-server-import-maps.

Not that easy

Setting this up basically makes us have two different build pipelines, one with Vite and another one for the Web Test Runner. I spent a fair amount of time trying to get this to work but kept getting an error in the test run browser: "Error: Failed to fetch dynamically imported module". I think I looked at all websites on the internet 🤥 but I could not find a solution. Obviously, it works since the Bellissima project uses this approach, but since I was frustrated I started to look for alternatives.

Another approach

Luckily this brought my attention to the plugin @remcovaes/web-test-runner-vite-plugin. This plugin will reuse all the configuration from the Vite setup (including the Paths from tsconfig.json which had to be duplicated in the Web Test Runner configuration) and after installing it, my tests were working in 2 minutes. After spending many hours debugging I could not believe it was working, I was actually thinking: "No, this can't be right, it can't be this easy" 😆. But it was. The code in my web-test-runner.config.mjs file went from 50 lines to 15 lines and the experience was really nice.

Now I have a nice setup to be able to unit test my web components and TypeScript code. I've shared a working example of the setup in this github repository if anyone else is struggling with the same problems.

More blog posts

15 januari 2021

Umbraco and MiniProfiler