Skip to main content

How to unit-test the front end

You can use the foundation-testing package to ensure that your Genesis Web Application provides a seamless user experience by testing components, functions and user interactions across various scenarios.

You can create test suites in the tests/unit folder to cover all the critical components and functions of your app.

  • Use UVU for fast and lightweight unit testing.
  • Use Sinon.JS if you need spies, stubs or mocks.

You can execute tests locally or on your CI pipeline.

Setting up unit tests

Installing dependencies

  1. Install Node.js, which is required for running JavaScript- and TypeScript-based tests.

  2. Install Foundation Testing. This provides utilities for testing within your project, and integrates Playwright, which has Behavior-Driven Development (BDD) tools, and enables you to use Gherkin syntax in tests.

npm install --save-dev @genesislcap/foundation-testing

 `npm install --save-dev @genesislcap/foundation-testing```
@genesislcap/foundation-testing:

### A typical project structure
Unit testing needs the simplest of project structures. The **unit** folder contains unit test suites.

Usually, there is one file per route or component you are testing, plus and additional ones for utility functions, etc.

```plaintext
test/
├── unit/
│ ├── home-page.ts
│ └── utils.ts

Writing Tests

Suites

Test suites are a good way to keep your unit tests organised.

Ideally, you want to have a test suite for every page or significant component in your application. Test suites can also be used to test arbitrary utility or helper functions.

Unit tests

Each test suite can contain as many unit tests as you want. Test are implemented in TypeScript using UVU and the foundation-testing libraries. Functions are structured as arrow functions.

Example (unit/example.test.ts):

import {Example} from '../../src/routes/example/example'
import {assert, createComponentSuite} from "@genesislcap/foundation-testing";

Example; // reference to avoid tree shaking.

const Suite = createComponentSuite<Example>('Example', 'example-route');

Suite('Can be created in the DOM', async ({element}) => {
assert.ok(element);
});

Suite.run();

Detailed examples

Testing components

You can use the createComponentSuite utility provided by @genesislcap/foundation-testing to create a test suite for your component.

Apart from setting up and tearing down your element fixture with a wrapping design system and DI container, this util also enables you to provide DI container mocks, which are required for certain testing flows.

Example (list-item.test.ts):

The example file below list-item.test.ts tests our ListItems (list-items-container) component to check that:

  • it can be created in the DOM
  • it displays a list item for every row in our mock data
  • we can navigate to a details view by clicking on a list item

We have included a partial mock of the Connect service so that snapshots return our mock data during the tests.

import {assert, createComponentSuite, Registration} from "@genesislcap/foundation-testing";
import {Connect, Message} from "@genesislcap/foundation-comms";
import {DOM} from "@genesislcap/web-core";
import {ListItems} from "../../src/routes/list-items/list-items";

ListItems; // reference to avoid tree shaking.

const rowData = [...];

const mockData = {
"ROWS_COUNT": 100,
"MESSAGE_TYPE": "QUERY_UPDATE",
"ROW": rowData,
"MORE_ROWS": false,
"SOURCE_REF": "f5ebbfcd-7753-41d0-a1ec-eda8ffc307a3",
"SEQUENCE_ID": 1
}

// partial mock of the Connect service so that snapshots return our mock data during these tests
const connectMock = {
snapshot: (): Promise<Message> => Promise.resolve(mockData)
};

const mocks = [
Registration.instance(Connect, connectMock),
];

const Suite = createComponentSuite<ListItems>('ListItems', 'list-items-container', undefined, mocks);

Suite('Can be created in the DOM.', async ({element}) => {
assert.ok(element);
});

Suite('Displays a list item for every row.', async ({element}) => {
await DOM.nextUpdate();
const content = element.shadowRoot?.querySelector('div.content');
const listItems = content.querySelectorAll('div.list-item');
assert.equal(listItems.length, element.entities.length);
});

Suite('Can navigate to details from a list item', async ({element}) => {
await DOM.nextUpdate();
const content = element.shadowRoot?.querySelector('div.content');
const firstListItem = content.querySelectorAll('div.list-item')[0];
const anchor = firstListItem.querySelector('rapid-anchor');
assert.ok(anchor);
await (anchor as HTMLElement)?.click();
assert.equal(window?.location?.pathname, `/list-item-detail/${element.entities[0].TRADE_ID}`);
});

Suite.run();

Testing logic

You can use the createLogicSuite utility provided by @genesislcap/foundation-testing to test function output based on specific input arguments.

Below is an example file utils.test.ts, which tests that our timeOfDay utility function returns the correct time of day for any input hour. It returns null for invalid input arguments.

import {createLogicSuite} from "@genesislcap/foundation-testing";
import {timeOfDay} from "../../src/utils/util";

const timeOfDaySuite = createLogicSuite('timeOfDay');

timeOfDaySuite('timeOfDay should return the correct time of day', ({runCases}) => {
runCases(timeOfDay, [
[[0], 'night'],
[[5], 'night'],
[[6], 'morning'],
[[11], 'morning'],
[[12], 'afternoon'],
[[16], 'afternoon'],
[[17], 'evening'],
[[20], 'evening'],
[[21], 'night'],
[[24], 'night'],
[[25], null],
[[-1], null],
[[false], null],
[['banana'], null],
[[{foo: 'bar'}], null],
[[[1, 2, 3]], null],
]);
});
timeOfDaySuite.run();

Unit testing scripts

Below is a guide on how to use these scripts to run your unit tests effectively, along with explanations of each script.

Example scripts

Here are the key scripts that you can add to your package.json for unit testing:

"scripts": {
"test": "genx test",
"test:debug": "genx test --debug --browser",
}

Running unit tests

To run all unit tests in your project, use the following command:

npm run test
  • What it does. This script runs all the unit tests. The tests are executed using the genx tool.

Debugging unit tests

If you need to debug your unit tests, use the following command:

npm run test:debug
  • What it does. This script runs the unit tests in debug mode, in a browser. During the tests, the genx test --debug --browser command enables you to pause the execution and inspect the state of your application.

Watching unit tests

If you want to watch and run your unit tests whenever there are changes, use:

npm run test:unit:watch
  • What it does. This script combines multiple watch commands to monitor changes in your unit-test files, and reruns the tests automatically.