Integration tests using cypress
Test client integration (client loads) Test auth integration (client logs in) Test backend integration (accept request) - broken Test autoupdate integration (sends au to client) Add manual cypress tests Add dockered cypress tests Add Readme Add test execution to makefile Add test execution to github-actions TODO: - Create user for tests - flush db after every test
This commit is contained in:
parent
97ea71c8db
commit
85c0e50c21
22
.github/workflows/test-integration.yml
vendored
Normal file
22
.github/workflows/test-integration.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Run integration tests (cypress)
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
run-cypress:
|
||||
name: "Runs integration tests in cypress"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build and run OS4 Dev
|
||||
run: make run-dev ARGS="-d"
|
||||
|
||||
- name: Run integration tests (cypress docker)
|
||||
run: make cypress-docker
|
||||
|
||||
- name: Stop OS4
|
||||
run: make stop-dev
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -20,6 +20,13 @@ dev-commands/export.json
|
||||
/docker/secrets/auth_*_key
|
||||
docker/secrets/*.env
|
||||
|
||||
# Integration testing
|
||||
/integration/results
|
||||
/integration/cypress/downloads
|
||||
/integration/cypress/screenshots
|
||||
/integration/cypress/videos
|
||||
/integration/node_modules
|
||||
|
||||
# Old OS3 files and folders
|
||||
.coverage
|
||||
.mypy_cache
|
||||
@ -36,7 +43,7 @@ tests
|
||||
.venv/
|
||||
.virtualenv/
|
||||
.vscode/
|
||||
package-lock.json
|
||||
client/package-lock.json
|
||||
server/
|
||||
# OS3+-Submodules
|
||||
/autoupdate/
|
||||
|
22
Makefile
22
Makefile
@ -1,5 +1,11 @@
|
||||
run-system-tests:
|
||||
echo "TODO: write complete system tests"
|
||||
run-integration-tests:
|
||||
@echo "Start OpenSlides Dev"
|
||||
make run-dev ARGS="-d"
|
||||
@echo "Start integration tests"
|
||||
make cypress-docker
|
||||
docker-compose -f integration/docker-compose.yml up
|
||||
@echo "Stop OpenSlides Dev"
|
||||
make stop-dev
|
||||
|
||||
run-service-tests:
|
||||
git submodule foreach 'make run-tests'
|
||||
@ -9,7 +15,7 @@ build-dev:
|
||||
make -C proxy build-dev
|
||||
|
||||
run-dev: | build-dev
|
||||
docker-compose -f docker/docker-compose.dev.yml up
|
||||
docker-compose -f docker/docker-compose.dev.yml up $(ARGS)
|
||||
|
||||
stop-dev:
|
||||
docker-compose -f docker/docker-compose.dev.yml down --volumes --remove-orphans
|
||||
@ -27,3 +33,13 @@ services-to-master:
|
||||
#
|
||||
# [1] ...or main, or whatever branch the OS4 one is. See .gitmodules.
|
||||
git submodule foreach -q --recursive 'git checkout $(git config -f $$toplevel/.gitmodules submodule.$$name.branch || echo master); git pull upstream $$(git config -f $$toplevel/.gitmodules submodule.$$name.branch || echo master)'
|
||||
|
||||
cypress-open:
|
||||
cd integration; npm run cypress:open
|
||||
|
||||
cypress-run:
|
||||
cd integration; npm run cypress:run
|
||||
|
||||
cypress-docker:
|
||||
docker-compose -f integration/docker-compose.yml build
|
||||
docker-compose -f integration/docker-compose.yml up
|
9
integration/Dockerfile
Normal file
9
integration/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM cypress/included:8.4.0
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./cypress ./cypress/
|
||||
COPY ./cypress-docker.json ./cypress.json
|
||||
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint
|
||||
ENTRYPOINT entrypoint
|
44
integration/README.md
Normal file
44
integration/README.md
Normal file
@ -0,0 +1,44 @@
|
||||
# Integration tests
|
||||
|
||||
To test the integration of our OpenSlides services we are using cypress.
|
||||
There are not too many of them, but they are sufficient tell that our services are properly integrated.
|
||||
|
||||
- The client can be accessed
|
||||
- Authentication works (admin admin can be logged in)
|
||||
- The backend accepts requests (of some sort)
|
||||
- The auto update transmits data to the client
|
||||
|
||||
(this list is not exhaustive)
|
||||
|
||||
## Writing tests and using cypress
|
||||
|
||||
To write and execute tests meaningfully, you will want to install cypress locally.
|
||||
Inside the `/integration` directory, execute
|
||||
|
||||
$ npm install
|
||||
$ npm run cypress:open
|
||||
(or `make cypress-open` from the main directory)
|
||||
|
||||
The cypress runner will open using electron and executes the tests for you.
|
||||
|
||||
## Run in docker and CI
|
||||
|
||||
Start OpenSlides (Usually in dev setup). From the main directory run
|
||||
|
||||
$ make run-dev
|
||||
|
||||
in the `/integration` just fire
|
||||
|
||||
$ docker-compose build
|
||||
$ docker-compose up
|
||||
(or `make cypress-docker` from the main directory)
|
||||
|
||||
Cypress will run dockered and report errors inside the CLI.
|
||||
Screenshots and videos of the tests can be found in the `/integration/results` folder.
|
||||
|
||||
You can streamline the whole process by using
|
||||
|
||||
$ make run-system-tests
|
||||
|
||||
From the main directory.
|
||||
This can take while, since the docker of OS4 has to be build first.
|
3
integration/cypress-docker.json
Normal file
3
integration/cypress-docker.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"baseUrl": "https://host.docker.internal:8000"
|
||||
}
|
6
integration/cypress.json
Normal file
6
integration/cypress.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"baseUrl": "https://localhost:8000",
|
||||
"screenshotsFolder": "results/screenshots",
|
||||
"videosFolder": "results/videos",
|
||||
"downloadsFolder": "results/downloads"
|
||||
}
|
5
integration/cypress/fixtures/example.json
Normal file
5
integration/cypress/fixtures/example.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
7
integration/cypress/integration/client-load.spec.js
Normal file
7
integration/cypress/integration/client-load.spec.js
Normal file
@ -0,0 +1,7 @@
|
||||
describe("Load client", () => {
|
||||
it("successfully loads", () => {
|
||||
cy.visit("/");
|
||||
cy.url().should("include", "/login");
|
||||
cy.contains("OpenSlides");
|
||||
});
|
||||
});
|
13
integration/cypress/integration/client-login.spec.js
Normal file
13
integration/cypress/integration/client-login.spec.js
Normal file
@ -0,0 +1,13 @@
|
||||
describe("Login using UI", () => {
|
||||
it("Login over login-form, set cookie", () => {
|
||||
|
||||
const username = "admin"
|
||||
const password = "admin"
|
||||
|
||||
cy.visit("/login");
|
||||
cy.get("#mat-input-0").type(username);
|
||||
cy.get("#mat-input-1").type(`${password}{enter}`);
|
||||
cy.url().should("not.include", "login");
|
||||
cy.getCookie("refreshId").should("exist");
|
||||
});
|
||||
});
|
27
integration/cypress/integration/committee-create.spec.js
Normal file
27
integration/cypress/integration/committee-create.spec.js
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Some bugs
|
||||
*/
|
||||
// describe("Create a new committee", () => {
|
||||
// beforeEach(() => {
|
||||
// cy.login();
|
||||
// cy.visit("/committees");
|
||||
// });
|
||||
|
||||
// it("Creates a committee", () => {
|
||||
// cy.visit("/committees/create");
|
||||
// const committeeName = `Cypress Committee ${Date.now().toString()}`;
|
||||
// cy.get("#mat-input-0").type(committeeName);
|
||||
|
||||
// cy.intercept({
|
||||
// method: "POST",
|
||||
// url: "/system/action/handle_request",
|
||||
// }).as("handle_request");
|
||||
|
||||
// cy.get("form").submit();
|
||||
// cy.wait("@handle_request");
|
||||
|
||||
// cy.url().should("not.include", "create");
|
||||
// // only on clean db
|
||||
// //cy.contains(committeeName);
|
||||
// });
|
||||
// });
|
@ -0,0 +1,30 @@
|
||||
describe("Get autoupdates for committees detail view", () => {
|
||||
let committeeName;
|
||||
let committeeId;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
committeeName = `Cypress ${Date.now().toString()}`;
|
||||
const committeeData = {
|
||||
organization_id: 1,
|
||||
name: committeeName,
|
||||
manager_ids: [1],
|
||||
};
|
||||
cy.os4request("committee.create", committeeData).then((res) => {
|
||||
committeeId = res.id;
|
||||
});
|
||||
});
|
||||
|
||||
it("Receives a name change", () => {
|
||||
cy.visit(`/committees/${committeeId}`);
|
||||
cy.contains(committeeName);
|
||||
const updatedName = committeeName + "update";
|
||||
const committeeData = {
|
||||
id: committeeId,
|
||||
name: updatedName,
|
||||
};
|
||||
cy.os4request("committee.update", committeeData).then(() => {
|
||||
cy.contains(updatedName);
|
||||
});
|
||||
});
|
||||
});
|
43
integration/cypress/integration/committee-update.spec.js
Normal file
43
integration/cypress/integration/committee-update.spec.js
Normal file
@ -0,0 +1,43 @@
|
||||
describe("Update a committee", () => {
|
||||
let committeeName;
|
||||
let committeeId;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
committeeName = `Cypress ${Date.now().toString()}`;
|
||||
const committeeData = {
|
||||
organization_id: 1,
|
||||
name: committeeName,
|
||||
manager_ids: [1],
|
||||
};
|
||||
cy.os4request("committee.create", committeeData).then((res) => {
|
||||
committeeId = res.id;
|
||||
});
|
||||
cy.visit("/committees/");
|
||||
});
|
||||
|
||||
it("Has new Committee", () => {
|
||||
cy.visit(`/committees/${committeeId}`);
|
||||
cy.url().should("include", committeeId);
|
||||
cy.get("h1").contains(committeeName);
|
||||
});
|
||||
|
||||
/**
|
||||
* Some bugs
|
||||
*/
|
||||
// it("Can just update a new Committee", () => {
|
||||
// cy.visit(`/committees/${committeeId}/edit-committee`);
|
||||
// cy.url().should("include", `${committeeId}/edit-committee`);
|
||||
// cy.get(".title-slot").contains("Edit committee");
|
||||
// cy.get("#mat-input-0").type("edit");
|
||||
|
||||
// // cy.intercept({
|
||||
// // method: "POST",
|
||||
// // url: "/system/action/handle_request",
|
||||
// // }).as("au");
|
||||
// // cy.wait("@au");
|
||||
// cy.get("form").submit();
|
||||
|
||||
// cy.url().should("not.include", `edit-committee`);
|
||||
// });
|
||||
});
|
22
integration/cypress/plugins/index.js
Normal file
22
integration/cypress/plugins/index.js
Normal file
@ -0,0 +1,22 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
92
integration/cypress/support/commands.js
Normal file
92
integration/cypress/support/commands.js
Normal file
@ -0,0 +1,92 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
|
||||
/**
|
||||
* Login
|
||||
*/
|
||||
Cypress.Commands.add("login", (username = "admin", password = "admin") => {
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: "/system/auth/login/",
|
||||
body: {
|
||||
username,
|
||||
password,
|
||||
},
|
||||
})
|
||||
.as("loginResponse")
|
||||
.then((response) => {
|
||||
Cypress.env("authToken", response.headers.authentication);
|
||||
return response;
|
||||
})
|
||||
.its("status")
|
||||
.should("eq", 200)
|
||||
});
|
||||
|
||||
/**
|
||||
* Create models
|
||||
*/
|
||||
Cypress.Commands.add("os4request", (osAction, body) => {
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: "/system/action/handle_request",
|
||||
body: [
|
||||
{
|
||||
action: osAction,
|
||||
data: [
|
||||
{
|
||||
...body,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
.should((response) => {
|
||||
expect(response.status).to.eq(200);
|
||||
})
|
||||
.its("body")
|
||||
.should("contain", {
|
||||
success: true,
|
||||
})
|
||||
.then((body) => {
|
||||
return body.results[0][0]
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Extend "request" with auth header
|
||||
*/
|
||||
Cypress.Commands.overwrite("request", (originalFn, ...options) => {
|
||||
const optionsObject = options[0];
|
||||
const token = Cypress.env("authToken");
|
||||
if (!!token && optionsObject === Object(optionsObject)) {
|
||||
optionsObject.headers = {
|
||||
authentication: token,
|
||||
...optionsObject.headers,
|
||||
};
|
||||
return originalFn(optionsObject);
|
||||
}
|
||||
return originalFn(...options);
|
||||
});
|
20
integration/cypress/support/index.js
Normal file
20
integration/cypress/support/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
10
integration/docker-compose.yml
Normal file
10
integration/docker-compose.yml
Normal file
@ -0,0 +1,10 @@
|
||||
version: "3"
|
||||
services:
|
||||
cypress:
|
||||
build: .
|
||||
image: cypress
|
||||
volumes:
|
||||
- ./results/videos :/app/cypress/videos
|
||||
- ./results/screenshots :/app/cypress/screenshots
|
||||
extra_hosts:
|
||||
host.docker.internal: host-gateway
|
22
integration/entrypoint.sh
Executable file
22
integration/entrypoint.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (C) 2020 by Intevation GmbH
|
||||
# Author(s):
|
||||
# Sean Engelhardt <sean.engelhardt@intevation.de>
|
||||
#
|
||||
# This program is distributed under the MIT license, as described
|
||||
# in the LICENSE file included with the distribution.
|
||||
# SPDX-License-Identifier: MIT
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
HOST="https://host.docker.internal:8000"
|
||||
|
||||
echo "wait until OpenSlides is up"
|
||||
until [[ $(curl -k -s -o /dev/null -w %{http_code} $HOST) -eq 200 ]];
|
||||
do
|
||||
sleep 5
|
||||
done
|
||||
echo ready
|
||||
|
||||
exec npx cypress run
|
3200
integration/package-lock.json
generated
Normal file
3200
integration/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
9
integration/package.json
Normal file
9
integration/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"scripts": {
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:run": "cypress run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "8.4.0"
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit 55e35cedbe8ea77047635772d3486e7d3c38fa03
|
||||
Subproject commit 9aa339decc73053c25714622ea71401eae77d2d5
|
Loading…
Reference in New Issue
Block a user