This is an DIY practical.
You must to get your solution to your GitLab repository before 20.4.2025.
./practical-08/
The objective is to add tests to "user-interface" from "practical-05". Start by copying your assignment from "./practical-05/user-interface" to "./practical-08/". Follow the slides to implement test using Vitest, Cypress and basic CI using GitLab. You CAN modify copied assignment sources. Do NOT modify the original.
Vitest is a test framework with Vite-native support. Meaning, you can easily use it with Vite project.
The objective is to implement Vitest for unit testing.
Unit testing is the process where you test the smallest functional unit of code.
Vitest has an excellent Getting Started. THe main points to follow are:
npm install -D vitest
// Vitest provides:
// - test executor : the vitest command
// - test definition (describe, test)
// - test assertions (expect)
import { describe, test, expect } from "vitest";
// We test only a single file.
import { createState } from "./application-state";
// We define a collection of test relevant for given function.
describe("createState", () => {
// This is the single test.
// The test function can be asynchronous (async).
test("Default test.", () => {
// Prepare input and initial state.
const search = "?data-source=1&submit-url=2";
// Compute actual state.
const actual = createState(search);
// Define and assert for expected state.
const expected = {
dataSource: "1",
submitUrl: "2",
initialized: false,
submitted: false,
};
// We use "toStrictEqual" to check for the internals of the object.
expect(actual).toStrictEqual(expected);
});
});
Name of the test must start with "#1". The objective is to test how is the initial state created.
This test should not include any async functionality. You are expected to copy the data in your test.
Name of the test must start with "#2". Objective is to test stat change.
Name of the test must start with "#3".
Objective is to test initial data loading from remote source.
The next step is to add a test coverage. This provides us with an estimate of how much code we test. Beware that test coverage should not be the ultimate metric! Yet, when paired with additional rules it can be useful. E.g. 100% test coverage for controllers.
Computing test coverage with Vitest is easy. You need utilize "istanbul" ("npm i -D @vitest/coverage-istanbul") to compute the test coverage and export it as "html" and "json". See following slide for example of the configuration file.
Add a "coverage": "vitest run --coverage" command to "package.json" to run the tests and compute the coverage.
Example of "vite.config.ts" file with React and istanbul code coverage.
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
test: {
coverage: {
provider: 'istanbul',
reporter: ['json', 'html'],
},
},
})
By default the coverage report is saved to "coverage" directory. As a result, you need to add "coverage" into your ".gitignore" file.
The objective is to implement a simple CI pipeline using GitLab. You should already know the basics from NSWI177.
You can read the .gitlab-ci.yml documentation.
End-to-end (E2E) testing is a Software testing methodology to test a functional and data application flow consisting of several sub-systems working together from start to end.
End-to-end testing, also known as E2E testing, is an approach to testing that that simulates real user experiences to validate the complete system.
For the purpose of this practical we limit ourself only to simulating a user interaction with the dialog. We basically simulate user interaction with our application using a web browser.
In order to implement the tests we need to:
When it comes to the browser and its control, we can use Playwright, Puppeteer, Cypress, ...
For purpose of this practical we utilize Cypress as it has simple setup, intuitive API, built in assertions,automatic waiting, ...
There is a Cypress Install guide. Please install Cypress using npm "npm install cypress --save-dev".
Cypress integrates a custom application which allow you to easily create and execute the test. Before opening the application you may need to modify your "tsconfig.json" and add following fragment.
"compilerOptions": {
"module": "ESNext"
},
Next open the application using "npx cypress open" and select "E2E testing". Confirm quick configuration and select a browser you would like to choose. From the perspective of the tutorial you should continue with E2E Your First Test.
Select "Create new spec" and accept the default.
describe('template spec', () => {
it('passes', () => {
cy.visit('https://example.cypress.io')
})
})
Look around the application. Try to modify the source file, by default "spec.cy.js" using your IDE of choice. Reload and re-execute the test.
In order to run the test for our application we need to start it first. Start your application using "npm run dev" and update the test in "spec.cy.js" to load your application. Do not point Cypress to webik, instead point it to your local dev server.
You may get a "ECONNREFUSED" error. This meas that the browser is not able to access your application. You can test it manually by opening a new tab and navigating to URL of your application. Try to solve this on your own using any tools necessary. There is also a possible solution in this presentation source.
Cypress provide a rich API. While I do recommend you to take a explore the official documentation, here is a list of few of the basic one:
A simple example using this may look like this:
// Intercept POST call to "http://example.com/data-submit", return "{}" as a response, and assign "postSubmitData" as an alias.
cy.intercept("POST", "http://example.com/data-submit", "{}")
.as("postSubmitData");
// Select by text and perform click operation.
cy.contains("Send").click();
// Wait for application to make the POST request and check the content.
cy.wait("@postSubmitData").then(interception => {
const request = JSON.parse(interception.request.body);
expect(request.title).to.be.equal("Instance");
});
The test should be based at test #2.
"fixture" provide a way how to fetch data content from files in "./cypress/fixtures". You can pair it with intercept like this:
cy.intercept("GET", "data-source", { fixture: "data-source" })
.as("getDataSource");
This will load file "./cypress/fixtures/data-source.json".
Update your cypress test to test application running at URL as defined by CYPRESS_SERVER. Value of CYPRESS_SERVER can be specified as environment variable or using CYPRESS_SERVER property in .env file.
You can utilize Cypress.env to access environment variables. You can load .env file to Cypress using following configuration file:
import dotenv from "dotenv";
dotenv.config();
import { defineConfig } from "cypress";
export default defineConfig({
e2e: { },
env: { CYPRESS_SERVER: process.env.CYPRESS_SERVER }
});
Do not forget to install and save "dotenv" as a dependency.
...