Riot Component Unit Test with Vitest (Node env)
Tags: riotjs, javascript, beginners, frontend
Published: 2024-03-21 10:12:55

This article covers testing Riot Input components using Vitest and Riot/SSR in a Node environment. 

> A second method exists: Vitest into a JsDom environment (or HappyDom); [read this article to learn more](https://dev.to/steeve/riot-component-unit-test-with-vitest-jsdom-env-182l)

Before going forward, ensure you have a base Riot+Vite project and have created at least one component. If not, you can read [my previous article on creating an Input Component](https://dev.to/steeve/input-component-with-riotjs-material-design-30c4).

> These articles form a series focusing on RiotJS paired with BeerCSS, designed to guide you through creating components and mastering best practices for building production-ready applications. I assume you have a foundational understanding of Riot; however, feel free to refer to the documentation if needed: https://riot.js.org/documentation/

## Riot + Vitest + Riot-SSR: Next Generation Testing Framework

As I am using Vite as a development server to get a real-time rendering of my Riot application, using Vitest for testing brings many advantages: 
- A test runner that uses the same configuration as Vite (vite.config.js).
- It provides a compatible Jest API (one of the most used test runners)
- Performances: it uses Worker threads to run as much as possible in parallel.
- Easy setup and configuration (almost none).

Add Vitest to your Riot project:
```
npm install -D vitest
```
To execute the test, add the following section to your `package.json`:
```json
{
  "scripts": {
    "test": "vitest"
  }
}
```
And that's it for Vitest! If you need a specific configuration, add **test** property in your Vite config `vite.config.js`. Configuration documentation: https://vitest.dev/config/

## Write a first Test with Riot-SSR

Our tests will use Riot SSR (Riot Server-Side Rendering) because it is a module that renders riot components on the server.

First, add Riot-SSR to your project:

```bash
npm i -D @riotjs/ssr
```

The module takes a component and renders it as HTML; we must assert if we don't get the expected result.

**The HTML rendering is only a String, so we can't test Component events**, such as value changes, button clicks, key types, etc. A Browser Dom environment, such as JsDom, is required to test Riot Component reactivity.

Here is the minimum test we can create with Vitest and Riot-SSR:
```js
import { assert, describe, it } from 'vitest'
import render from '@riotjs/ssr'

import cInput from '../components/c-input.riot'

describe('Component c-input', () => {
    it('should render the input without props', () => {
        const html = render('c-input', cInput, {})
    
        assert.strictEqual(html, '<c-input><div class="field border"><input value="" type="text"></div></c-input>')
    })
});

```
Code Breakdown:
1. Modules and the component are loaded.
2. Vitest provides common utilities for testing, similar to Mocha and Jest:
   * **describe()** is used to define a group of tests
   * **it()** for defining a test
   * **assert()** for validating tests
3. The **render()** function takes 3 arguments: 
   * First: The component name built as String
   * Second: The component module
   * Third: Optional `props` are properties passed to the components.
4. Validate the result of the rendered HTML as String with the **assert.strictEqual(result, expected)**.

Now start the test through the NPM command:
```bash
npm run test
```
We get the following result:
```bash
 DEV  v1.4.0 /riot-beercss

 ✓ tests/c-input.test.js (1)
   ✓ Component c-input (1)
     ✓ should load a basic input without props

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  13:41:34
   Duration  242ms (transform 108ms, setup 0ms, collect 128ms, tests 6ms, environment 0ms, prepare 35ms)
```
✅ The test succeeds; everything is good. Vitest is listening to changes, and it will print the result when a new test is created.

## Advanced Tests

The goal now is to try all input states by passing props to the components, render it, and it should render the expected result.

One test should validate one state of the input.

Let's try testing a default value for the input:
```js
    it('should render the input with a default value', () => {
        const _htmlExpected = '<c-input><div class="field border"><input value="Firstname" type="text"></div></c-input>';
        const _html = render('c-input', cInput, { value: "Firstname"})
    
        assert.strictEqual(_html, _htmlExpected)
    })
```
Code Details:
1. First the expected HTML result is stored into a constant
2. The Component is built with a props `value: "Firstname"`
3. The result is validated with the assert expression. In this case, everything is fine.

Now we can replicate this testing method for all props and multiple props combined: Let's create a rounded small input with a "Password" type, label, and an error.
```js
it('should render multiple props: label, type, error and round', () => {
    const _htmlExpected = '<c-input><div class="field border round invalid label"><input value="" type="password"><label>Password</label><span class="error">The password is too show, minimum 20 characters.</span></div></c-input>';
    const _html = render('c-input', cInput, { label: "Password", type: "password", error: "The password is too show, minimum 20 characters.", round: true})
    
    assert.strictEqual(_html, _htmlExpected)
})
```
Code Breakdown:
* Multiple props are passed to the **render()** function
* A constant is created with the expected HTML result based on the component's logic.

The test pass ✅

Find all input tests the following GitHub repository:
https://github.com/steevepay/riot-beercss/blob/main/tests/c-input.node.test.js

## Conclusion

Combining Riot-SSR with Vitest is a good method for quickly testing the rendering of Riot Components, but it does not provide a Browser for testing HTML events.

I covered another [testing method in a JsDom environment](https://dev.to/steeve/riot-component-unit-test-with-vitest-jsdom-env-182l), to compare the two solutions:
* **For extensive but verbose Riot testing:** use a JsDom environment
* **For fast, no-brain but limited Riot testing:** use a Node Server Environment with Riot-SSR

Feel free to comment if you have questions or need help about RiotJS.

Have a great day! Cheers 🍻