Introducing Dainte
Dainte is a testing framework for Svelte. It was inspired by the React library Enzyme. Notably, and similarly to Enzyme, it allows both the runtime and SSR to be tested simultanously in the same test suite.
Runtime
Dainte’s approach is batteries-included runtime testing. For example, given a component:
<!-- App.svelte -->
<script>
let answer = 42
function add() {
answer += 3
}
</script>
<div id="answer">{answer}</div>
<button on:click="{add}">Add</button>
To test this component with Dainte, we instantiate the component with dainte.mount()
. This function returns the mounted component instance and a JSDOM instance where the component is mounted.
import { mount } from 'dainte'
import { tick } from 'svelte'
const { app, window, document } = await mount('./App.svelte')
expect(document.querySelector('#answer').textContent).toBe('42')
const event = new window.MouseEvent('click')
document.querySelector('button').dispatchEvent(event)
await tick()
expect(document.querySelector('#answer').textContent).toBe('45')
Tests must abide by Svelte’s reactive cycle, so it’s necessary to await a svelte.tick()
for changes to be reflected in the DOM.
Component introspection
Often, it’s more desirable to test the logic within the component, rather than the rendering to DOM. To this end, Dainte supports component introspection. Here’s the same test without relying on the DOM:
import { mount } from 'dainte'
import { tick } from 'svelte'
const { app } = await mount('./App.svelte', { inspect: true })
const { add } = app.inspect()
add()
await tick()
const { answer } = app.inspect()
expect(answer).toBe(45)
This bridges the divide between the compiled JavaScript runtime and the test environment runtime, allowing — not only exported let
variables to be read or updated — but full component scope introspection, similar to setting a breakpoint within the component.
Any variable or import within the component can be accessed and tested with the inspect()
method, without having to trigger DOM events or run asserts against the DOM.
One caveat: reactive variables update within the component, variables retrieved by inspect()
will not. In that sense, you can think of inspect()
as creating a snapshot of the component’s state.
Lower-level compiling
Of course, dainte.mount()
is a high-level function, returning an entire DOM instance alongside the component instance. Dainte also includes a lower-level function dainte.compile()
to produce a compiled Svelte component class. This is very similar to Svelte’s built-in svelte.compile()
and shares many of the same options, althrough rather than returning strings of JS and CSS, it returns a component class, ready to be instantiated with the DOM of your choice.
import { compile } from 'dainte'
const { App } = await compile('./App.svelte')
const app = new App({
target: document.body,
})
A big benefit I’ve experienced with the low-level options is closely matching the test environment with the production compile environment. See the full list of options in the Dainte repo.
SSR
Static HTML output and SSR is an important part of Svelte components. Dainte makes it simple to test both side by side with dainte.render()
:
import { render } from 'dainte'
const { html } = await render('./App.svelte')
expect(html).toBe('<div id="answer">42</div>')
Benefits over jest-transform-svelte
I built Dainte and have been using it on client projects as an alternative to jest-transform-svelte
. It ticks some pain points by using a completely different approach to compiling Svelte code:
- Plays nicely with other Jest plugins by not patching
require()
- Every Dainte method has low-level configuration options so tests match production.
- Introspection and state assertions without touching the DOM.
Read the full docs on the repo here: github.com/nathancahill/dainte, and please open issues for any improvements or bug reports. And give it a star!
Stay in touch
The next post will be about mapping Bosnia & Herzegovina's minefields and minimizing risk while hiking and mountaineering. Subscribe to the email list to get notified when it's published. No spam, ever.