test(webapp): provide tests in CI
- add url and page as env variables to choice where to test and update the others - To pass a variable to a job's service, they have to be declared in the "variables" global -> doesn't work (see: [#37]) - use of the deployed image to do E2E test instead of services of gitlab
This commit is contained in:
@@ -7,4 +7,6 @@ MQTT_URL=
|
|||||||
MQTT_USERNAME=
|
MQTT_USERNAME=
|
||||||
MQTT_PASSWORD=
|
MQTT_PASSWORD=
|
||||||
REST_USERNAME=
|
REST_USERNAME=
|
||||||
REST_PASSWORD=
|
REST_PASSWORD=
|
||||||
|
REST_URL=
|
||||||
|
REST_PAGE=
|
||||||
|
|||||||
@@ -109,8 +109,10 @@ services:
|
|||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- VUE_APP_INFLUXDB_USER=$REST_USERNAME
|
- VUE_APP_REST_USER=$REST_USERNAME
|
||||||
- VUE_APP_INFLUXDB_PASSWORD=$REST_PASSWORD
|
- VUE_APP_REST_PASSWORD=$REST_PASSWORD
|
||||||
|
- VUE_APP_REST_URL=$REST_URL
|
||||||
|
- VUE_APP_REST_PAGE=$REST_PAGE
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ variables:
|
|||||||
DOCKER_IMAGE: registry.forge.hefr.ch/team-raclette/project-softweng/web-app:latest
|
DOCKER_IMAGE: registry.forge.hefr.ch/team-raclette/project-softweng/web-app:latest
|
||||||
NODE_IMAGE: cypress/included:cypress-14.4.1-node-22.16.0-chrome-137.0.7151.68-1-ff-139.0.1-edge-137.0.3296.62-1
|
NODE_IMAGE: cypress/included:cypress-14.4.1-node-22.16.0-chrome-137.0.7151.68-1-ff-139.0.1-edge-137.0.3296.62-1
|
||||||
|
|
||||||
|
VUE_APP_REST_USER: $REST_USER
|
||||||
|
VUE_APP_REST_PASSWORD: $REST_PASSWORD
|
||||||
|
VUE_APP_REST_URL: $REST_URL
|
||||||
|
VUE_APP_REST_PAGE: $TEST_PAGE
|
||||||
|
|
||||||
default:
|
default:
|
||||||
image: $DOCKER_IMAGE
|
image: $DOCKER_IMAGE
|
||||||
|
|
||||||
@@ -35,9 +40,10 @@ web-app-unit-tests:
|
|||||||
web-app-e2e-tests:
|
web-app-e2e-tests:
|
||||||
image: $NODE_IMAGE
|
image: $NODE_IMAGE
|
||||||
stage: web-app-tests
|
stage: web-app-tests
|
||||||
services:
|
|
||||||
- name: $DOCKER_IMAGE
|
# services:
|
||||||
alias: app
|
# - name: $DOCKER_IMAGE
|
||||||
|
# alias: app
|
||||||
script:
|
script:
|
||||||
- echo "Running e2e tests"
|
- echo "Running e2e tests"
|
||||||
- apt-get update
|
- apt-get update
|
||||||
@@ -45,6 +51,6 @@ web-app-e2e-tests:
|
|||||||
- cd ./web-app
|
- cd ./web-app
|
||||||
- npm install --include=dev
|
- npm install --include=dev
|
||||||
- echo "Wait web-app app is running"
|
- echo "Wait web-app app is running"
|
||||||
- for i in {1..6}; do curl -f http://app:8080 && break || sleep 5; done
|
- for i in {1..6}; do curl -f https://app.mse.kb28.ch/ && break || sleep 5; done
|
||||||
- echo "Flask app is running"
|
- echo "App is running"
|
||||||
- npm run test:e2e
|
- npm run test:e2e
|
||||||
|
|||||||
@@ -5,5 +5,9 @@ export default defineConfig({
|
|||||||
setupNodeEvents(on, config) {
|
setupNodeEvents(on, config) {
|
||||||
// implement node event listeners here
|
// implement node event listeners here
|
||||||
},
|
},
|
||||||
|
specPattern: [
|
||||||
|
"cypress/e2e/*.cy.{js,jsx,ts,tsx}",
|
||||||
|
"cypress/unit/*.cy.{js,jsx,ts,tsx}",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
describe("Test fetch measurments button in the main page", () => {
|
describe("Test fetch measurments button in the main page", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit("http://localhost:8080/");
|
cy.visit("https://app.mse.kb28.ch");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Fetch timeseries with valid user-room-device", () => {
|
it("Fetch timeseries with valid user-room-device", () => {
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
"build": "vue-cli-service build",
|
"build": "vue-cli-service build",
|
||||||
"cypress:open": "cypress open",
|
"cypress:open": "cypress open",
|
||||||
"cypress:run": "cypress run",
|
"cypress:run": "cypress run",
|
||||||
"test": "cypress run --spec 'cypress/e2e/*.cy.ts'"
|
"test:e2e": "cypress run --spec 'cypress/e2e/*.cy.ts'",
|
||||||
|
"test:unit": "cypress run --spec 'cypress/unit/*.cy.ts'",
|
||||||
|
"test": "test:unit | test:e2e"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import axios, { AxiosResponse } from "axios";
|
import axios, { AxiosResponse } from "axios";
|
||||||
import { BASE, PING, RACLETTE, PONG } from "../const";
|
import { BASE, PING, PAGE, PONG } from "../const";
|
||||||
|
|
||||||
export class HttpClient {
|
export class HttpClient {
|
||||||
private _url: string;
|
private _url: string;
|
||||||
@@ -45,7 +45,7 @@ export class HttpClient {
|
|||||||
room: string,
|
room: string,
|
||||||
device: string
|
device: string
|
||||||
): Promise<AxiosResponse<any, any>> {
|
): Promise<AxiosResponse<any, any>> {
|
||||||
const response = await axios.get(`${BASE}${this._url}/${RACLETTE}`, {
|
const response = await axios.get(`${BASE}${this._url}/${PAGE}`, {
|
||||||
headers: this.getAuthHeader(),
|
headers: this.getAuthHeader(),
|
||||||
params: {
|
params: {
|
||||||
user: user,
|
user: user,
|
||||||
@@ -62,7 +62,7 @@ export class HttpClient {
|
|||||||
device: string
|
device: string
|
||||||
): Promise<AxiosResponse<any, any>> {
|
): Promise<AxiosResponse<any, any>> {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${BASE}${this._url}/${RACLETTE}`,
|
`${BASE}${this._url}/${PAGE}`,
|
||||||
{
|
{
|
||||||
command: "MEASURE_NEW",
|
command: "MEASURE_NEW",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
export const APP_NAME = "Home Monitor";
|
export const APP_NAME = "Home Monitor";
|
||||||
|
|
||||||
export const URL = "rest.mse.kb28.ch";
|
export const URL = process.env.VUE_APP_REST_URL;
|
||||||
|
|
||||||
// load environment varaibles - need to have the prefix VUE_APP
|
// load environment varaibles - need to have the prefix VUE_APP
|
||||||
export const USERNAME = process.env.VUE_APP_INFLUXDB_USER;
|
export const USERNAME = process.env.VUE_APP_REST_USER;
|
||||||
export const PASSWORD = process.env.VUE_APP_INFLUXDB_PASSWORD;
|
export const PASSWORD = process.env.VUE_APP_REST_PASSWORD;
|
||||||
|
|
||||||
export const HUMIDITY = "humidity";
|
export const HUMIDITY = "humidity";
|
||||||
export const TEMPERATURE = "temperature";
|
export const TEMPERATURE = "temperature";
|
||||||
@@ -17,5 +17,5 @@ export const DEVICE = "Shed";
|
|||||||
|
|
||||||
export const BASE = "https://";
|
export const BASE = "https://";
|
||||||
export const PING = "ping";
|
export const PING = "ping";
|
||||||
export const RACLETTE = "raclette";
|
export const PAGE = process.env.VUE_APP_REST_PAGE;
|
||||||
export const PONG = "pong";
|
export const PONG = "pong";
|
||||||
|
|||||||
275
web-app/test.md
275
web-app/test.md
@@ -1,274 +1,43 @@
|
|||||||
# End-to-End Testing Documentation for Home Monitor
|
# End-to-End and Unit Testing Documentation for Home Monitor
|
||||||
|
|
||||||
## Table of Contents
|
## Test Description
|
||||||
- [Introduction](#introduction)
|
|
||||||
- [Testing Strategy](#testing-strategy)
|
|
||||||
- [Test Environment Setup](#test-environment-setup)
|
|
||||||
- [Running Tests](#running-tests)
|
|
||||||
- [Test Structure](#test-structure)
|
|
||||||
- [Test Cases Overview](#test-cases-overview)
|
|
||||||
- [Writing New Tests](#writing-new-tests)
|
|
||||||
- [Best Practices](#best-practices)
|
|
||||||
- [Troubleshooting](#troubleshooting)
|
|
||||||
- [References](#references)
|
|
||||||
|
|
||||||
## Introduction
|
The Home Monitor project uses Cypress to automate both End-to-End (E2E) and unit tests. These tests ensure that the main features of the application work correctly, both from the user’s and the developer’s perspectives.
|
||||||
|
|
||||||
End-to-End (E2E) testing verifies that the application functions correctly from a user's perspective by testing the complete application workflow. This document outlines the E2E testing approach for the Home Monitor application.
|
### Types of Tests
|
||||||
|
|
||||||
### Goals of E2E Testing
|
- **End-to-End (E2E) Tests**: Simulate real user behavior on the web interface. They check critical user flows, data display, error handling, and the responsiveness of the application.
|
||||||
|
- **Unit Tests**: Verify the correct functioning of JavaScript/TypeScript components and functions in isolation.
|
||||||
|
|
||||||
- Validate critical user flows function correctly
|
### Test Structure
|
||||||
- Ensure components work together as expected
|
|
||||||
- Detect regression issues before production deployment
|
|
||||||
- Verify application behavior in real-world scenarios
|
|
||||||
|
|
||||||
## Testing Strategy
|
- E2E tests are located in `web-app/cypress/e2e/`
|
||||||
|
- Unit tests are located in `web-app/cypress/unit/`
|
||||||
|
- Custom commands and global setup are in `web-app/cypress/support/`
|
||||||
|
- Cypress configuration is in `web-app/cypress.config.ts`
|
||||||
|
|
||||||
Home Monitor uses Cypress for E2E testing with two primary approaches:
|
### Example Scenarios Covered
|
||||||
|
|
||||||
1. **Mock Testing (Without Server)**: Fast, reliable tests using mocked DOM elements and data
|
- Application loading and display of main sections
|
||||||
2. **Real Environment Testing (With Server)**: Full application testing with a running development server
|
- Control panel functionality (buttons, filters)
|
||||||
|
- User, room, and device selection
|
||||||
|
- Display and update of measurement charts
|
||||||
|
- Handling of error or empty data states
|
||||||
|
- Responsive layout for mobile and desktop
|
||||||
|
|
||||||
### Mock vs. Real Testing Comparison
|
## How to Run the Tests
|
||||||
|
|
||||||
| Aspect | Mock Testing | Real Environment Testing |
|
|
||||||
|--------|-------------|--------------------------|
|
|
||||||
| Speed | Very fast (< 1s per test) | Slower (depends on app loading time) |
|
|
||||||
| Reliability | Highly stable | May be affected by server/network issues |
|
|
||||||
| Coverage | Limited to structure and basic interactions | Tests actual rendering and behaviors |
|
|
||||||
| Dependencies | None (no server needed) | Requires development server |
|
|
||||||
| Best for | CI/CD, quick verification | Final validation, regression testing |
|
|
||||||
|
|
||||||
## Test Environment Setup
|
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Node.js (v14+)
|
- Node.js (v14+)
|
||||||
- npm or yarn
|
- npm or yarn
|
||||||
- Chrome browser (for visual testing)
|
- Chrome browser
|
||||||
|
|
||||||
### Installation
|
### Install Dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install dependencies
|
|
||||||
cd project-softweng/web-app
|
cd project-softweng/web-app
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running Tests
|
### Run unit Tests
|
||||||
|
|
||||||
### Option 1: Tests with Mock Environment (No Server)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run all tests in headless mode
|
|
||||||
export CYPRESS_SKIP_SERVER_CHECK=true
|
|
||||||
npm run test:e2e
|
|
||||||
|
|
||||||
# Run specific test file
|
|
||||||
export CYPRESS_SKIP_SERVER_CHECK=true
|
|
||||||
npx cypress run --spec "tests/e2e/app.spec.ts"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Tests with Real Application (Server Required)
|
|
||||||
|
|
||||||
#### Automated Script Method
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Use the automated script (starts server, runs tests, stops server)
|
|
||||||
./tests/e2e/run-e2e-tests.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Manual Method
|
|
||||||
|
|
||||||
1. Start the development server:
|
|
||||||
```bash
|
|
||||||
# Terminal 1
|
|
||||||
npm run serve
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Run the tests:
|
|
||||||
```bash
|
|
||||||
# Terminal 2
|
|
||||||
# Headless mode
|
|
||||||
npx cypress run --spec "tests/e2e/app.spec.ts"
|
|
||||||
|
|
||||||
# Interactive mode
|
|
||||||
npx cypress open
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interactive Mode (Development)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run cypress:open
|
|
||||||
```
|
|
||||||
|
|
||||||
This opens the Cypress Test Runner where you can:
|
|
||||||
- Select individual tests to run
|
|
||||||
- See test execution in real-time
|
|
||||||
- Debug failing tests with time-travel debugging
|
|
||||||
|
|
||||||
## Test Structure
|
|
||||||
|
|
||||||
### Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
web-app/
|
|
||||||
├── tests/
|
|
||||||
│ ├── e2e/ # E2E test files
|
|
||||||
│ │ ├── app.spec.ts # Main application tests
|
|
||||||
│ │ └── run-e2e-tests.sh # Script for running tests with server
|
|
||||||
│ └── support/ # Support files
|
|
||||||
│ ├── commands.ts # Custom Cypress commands
|
|
||||||
│ └── e2e.ts # Global setup for E2E tests
|
|
||||||
└── cypress.config.ts # Cypress configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Files Organization
|
|
||||||
|
|
||||||
Each test file follows this structure:
|
|
||||||
1. **Setup**: Import dependencies and set up the test environment
|
|
||||||
2. **Beforehooks**: Prepare the application state before each test
|
|
||||||
3. **Test Cases**: Individual test scenarios grouped by feature
|
|
||||||
4. **Helper Functions**: Support functions for test cases
|
|
||||||
|
|
||||||
## Test Cases Overview
|
|
||||||
|
|
||||||
The E2E test suite covers the following scenarios:
|
|
||||||
|
|
||||||
1. **Application Loading**
|
|
||||||
- Verify application loads with correct title
|
|
||||||
- Confirm main layout sections are visible
|
|
||||||
|
|
||||||
2. **Control Panel Functionality**
|
|
||||||
- Verify control panel buttons are present
|
|
||||||
- Test "Fetch Measurements" button works
|
|
||||||
- Test "New Measurements" button works
|
|
||||||
|
|
||||||
3. **Filtering Controls**
|
|
||||||
- Test user selection dropdown
|
|
||||||
- Test room selection dropdown
|
|
||||||
- Test device selection dropdown
|
|
||||||
|
|
||||||
4. **Data Visualization**
|
|
||||||
- Verify chart displays correctly
|
|
||||||
- Test chart updates when data changes
|
|
||||||
|
|
||||||
5. **Error Handling**
|
|
||||||
- Test error state display
|
|
||||||
- Test empty data state display
|
|
||||||
|
|
||||||
6. **Responsive Design**
|
|
||||||
- Verify layout adapts to desktop viewport
|
|
||||||
- Verify layout adapts to mobile viewport
|
|
||||||
|
|
||||||
## Writing New Tests
|
|
||||||
|
|
||||||
### Adding a New Test Case
|
|
||||||
|
|
||||||
1. Identify the feature or flow to test
|
|
||||||
2. Determine the expected behavior
|
|
||||||
3. Add a new test case to the appropriate spec file:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
it('should [describe expected behavior]', () => {
|
|
||||||
// Setup any preconditions
|
|
||||||
|
|
||||||
// Perform actions
|
|
||||||
|
|
||||||
// Assert expected outcomes
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Commands
|
|
||||||
|
|
||||||
Custom commands are available to simplify test writing:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Select an option from a multiselect dropdown
|
|
||||||
cy.selectMultiselectOption('user-select', 'user1');
|
|
||||||
|
|
||||||
// Wait for chart to load and be visible
|
|
||||||
cy.waitForChart();
|
|
||||||
|
|
||||||
// Check application loading state
|
|
||||||
cy.checkLoadingState(false);
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Mocking
|
|
||||||
|
|
||||||
Use Cypress's intercept feature to mock API responses:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Mock GET request
|
|
||||||
cy.intercept('GET', '**/api/measurements*', {
|
|
||||||
statusCode: 200,
|
|
||||||
body: {
|
|
||||||
data: [
|
|
||||||
{ time: new Date(2023, 0, 1, 10, 0).getTime(), value: 22.5, type: 'temperature' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}).as('getMeasurements');
|
|
||||||
|
|
||||||
// Wait for the intercepted request
|
|
||||||
cy.wait('@getMeasurements');
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Test Independence**
|
|
||||||
- Each test should be able to run independently
|
|
||||||
- Avoid dependencies between tests
|
|
||||||
- Reset state between tests
|
|
||||||
|
|
||||||
2. **Selector Strategy**
|
|
||||||
- Prefer data attributes for test selectors (e.g., `data-cy`, `data-testid`)
|
|
||||||
- Avoid using CSS classes that might change with styling updates
|
|
||||||
- Establish a consistent selector naming convention
|
|
||||||
|
|
||||||
3. **Handling Asynchronous Operations**
|
|
||||||
- Use explicit waits rather than arbitrary timeouts
|
|
||||||
- Wait for specific elements or network requests rather than fixed delays
|
|
||||||
- Handle loading states appropriately
|
|
||||||
|
|
||||||
4. **Test Data Management**
|
|
||||||
- Use consistent test data
|
|
||||||
- Mock external dependencies
|
|
||||||
- Consider using fixtures for complex data structures
|
|
||||||
|
|
||||||
5. **Error Handling**
|
|
||||||
- Add proper error handling in tests
|
|
||||||
- Use `Cypress.on('uncaught:exception')` for expected application errors
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues and Solutions
|
|
||||||
|
|
||||||
| Issue | Solution |
|
|
||||||
|-------|----------|
|
|
||||||
| Tests fail with 401 errors | Add `Cypress.on('uncaught:exception', () => false)` to handle authentication errors |
|
|
||||||
| Elements not found | Increase timeouts or check selectors; ensure elements are in the DOM |
|
|
||||||
| Click actions failing | Use `{ force: true }` option if elements might be covered by overlays |
|
|
||||||
| Tests passing locally but failing in CI | Check environment differences; ensure CI has all required dependencies |
|
|
||||||
| Timeouts on waiting for elements | Increase `defaultCommandTimeout` in cypress.config.ts |
|
|
||||||
|
|
||||||
### Debugging Strategies
|
|
||||||
|
|
||||||
1. **Use Cypress's Debug Tools**
|
|
||||||
- Add `.debug()` to pause execution at a specific point
|
|
||||||
- Use the time-travel debugger in interactive mode
|
|
||||||
|
|
||||||
2. **Add Logging**
|
|
||||||
- Use `cy.log()` to add informative messages in the test
|
|
||||||
- Check browser console for application errors
|
|
||||||
|
|
||||||
3. **Visualize Test State**
|
|
||||||
- Enable screenshots and videos for failed tests
|
|
||||||
- Use `cy.screenshot()` at critical points
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [Cypress Documentation](https://docs.cypress.io/)
|
|
||||||
- [Vue Test Utils](https://vue-test-utils.vuejs.org/)
|
|
||||||
- [Testing Library](https://testing-library.com/)
|
|
||||||
- [Cypress Best Practices](https://docs.cypress.io/guides/references/best-practices)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user