Skip to content
Snippets Groups Projects
Commit 0ec9410a authored by OZGCloud's avatar OZGCloud
Browse files

OZG-6368 OZG-6589 create basic structure for Admin Client E2E testing

parent 42a551b3
No related branches found
No related tags found
No related merge requests found
Showing
with 539 additions and 53 deletions
......@@ -5,10 +5,10 @@
/tmp
/out-tsc
junit.xml
/apps/alfa-e2e/reports
/apps/alfa-e2e/reports_einheitlicher-ansprechpartner
/apps/alfa-e2e/recordings
/apps/alfa-e2e/cypress
/apps/*/reports
/apps/*/reports_einheitlicher-ansprechpartner
/apps/*/recordings
/apps/*/cypress
.scannerwork
test-report.xml
/.angular/cache/*
......
{
"baseUrl": "http://localhost:4301",
"env": {
"dbUrl": "mongodb://localhost:27018",
"database": "local",
"keycloakRealm": "by-e2e-tests-local-dev",
"keycloakUrl": "https://sso.dev.by.ozg-cloud.de/",
"keycloakClient": "admin"
},
"fileServerFolder": ".",
"fixturesFolder": "./src/fixtures",
"video": false,
"videosFolder": "./reports/videos",
"screenshotsFolder": "./reports/screenshots",
"chromeWebSecurity": false,
"reporter": "../../node_modules/cypress-mochawesome-reporter",
"defaultCommandTimeout": 10000,
"specPattern": "src/e2e/**/*.cy.{js,jsx,ts,tsx}",
"supportFile": "src/support/e2e.ts",
"testIsolation": false,
"reporterOptions": {
"html": false,
"json": true,
"reportDir": "./reports/mochawesome-report",
"reportFilename": "report",
"overwrite": true
}
}
\ No newline at end of file
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
import { defineConfig } from 'cypress';
const cypressConfig = require('./cypress.config.json');
const cypressEvents = require('./src/support/cypress-tasks.ts');
export default defineConfig({
e2e: nxE2EPreset(__filename, { cypressDir: 'src' }),
e2e: {
...nxE2EPreset(__dirname),
...cypressConfig,
setupNodeEvents(on, config) {
return cypressEvents(on, config);
},
},
retries: {
experimentalStrategy: 'detect-flake-and-pass-on-threshold',
experimentalOptions: {
maxRetries: 2,
passesRequired: 1,
},
openMode: true,
runMode: true,
},
});
version: '3'
volumes:
mongodb:
services:
mongodb:
image: mongo:7
ports:
- 27017:27017
volumes:
- mongodb:/data/db
healthcheck:
test: ['CMD', 'mongosh', '--eval', 'db.settings.find()']
interval: 10s
timeout: 5s
retries: 5
administration:
image: docker.ozg-sh.de/administration:${ADMINISTRATION_DOCKER_IMAGE:-snapshot-latest}
platform: linux/amd64
environment:
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILE:-local,remotekc}
- SPRING_DATA_MONGODB_URI=mongodb://mongodb:27017/config-db
ports:
- 8080:8080
depends_on:
mongodb:
condition: service_healthy
export class BuildInfoE2EComponent {
private readonly locatorVersion: string = 'build-version';
private readonly locatorBuildTime: string = 'build-time';
public getVersion() {
return cy.getTestElement(this.locatorVersion);
}
public getBuildTime() {
return cy.getTestElement(this.locatorBuildTime);
}
}
import { UserProfileE2EComponent } from './user-profile.component.e2e';
export class CurrentUserProfileE2EComponent {
private readonly locatorUserIconButton: string = 'user-icon-button';
private readonly locatorLogoutButton: string = 'logout-button';
private readonly locatorRoot: string = 'current-user';
public getRoot() {
return cy.getTestElement(this.locatorRoot);
}
public getUserProfile(): UserProfileE2EComponent {
return new UserProfileE2EComponent(this.locatorRoot);
}
public logout(): void {
this.getUserIconButton().click();
this.getLogoutButton().click();
}
public getUserIconButton() {
return cy.getTestElement(this.locatorUserIconButton);
}
private getLogoutButton() {
return cy.getTestElement(this.locatorLogoutButton);
}
}
export class UserProfileIconE2EComponent {
private readonly locatorAssignedIcon: string = 'user-profile-assigned';
private readonly locatorUnassignedIcon: string = 'user-profile-unassigned';
private readonly locatorErrorNotFoundIcon: string = 'user-profile-user-not-found';
private readonly locatorErrorServiceUnavailableIcon: string = 'user-profile-service-unavailable';
private readonly locatorUserProfileButton: string = 'user-profile-button-container';
constructor(private root: string) {}
public getRoot() {
return cy.getTestElement(this.root);
}
public getUnassignedIcon() {
return this.getRoot().findTestElementWithClass(this.locatorUnassignedIcon);
}
public getAssignedIcon() {
return this.getRoot().findTestElementWithClass(this.locatorAssignedIcon);
}
public getErrorResourceNotFoundIcon() {
return this.getRoot().findTestElementWithClass(this.locatorErrorNotFoundIcon);
}
public getErrorServiceUnavailableIcon() {
return this.getRoot().findTestElementWithClass(this.locatorErrorServiceUnavailableIcon);
}
public getButton() {
return this.getRoot().getTestElement(this.locatorUserProfileButton);
}
}
export class UserProfileSearchE2EComponent {
private readonly locatorInput: string = 'Bearbeiter-autocomplete-input';
private readonly locatorError: string = 'Bearbeiter-autocomplete-error';
private readonly locatorOptions: string = 'autocomplete-option';
constructor(private root: string) {}
public getRoot() {
return cy.getTestElement(this.root);
}
public getInput() {
return cy.getTestElement(this.locatorInput);
}
public getSearchOption(prefix: string) {
return cy.getTestElement(prefix + '-' + this.locatorOptions);
}
public getError() {
return cy.getTestElement(this.locatorError);
}
}
import { UserProfileIconE2EComponent } from './user-profile-icon.component.e2e';
import { UserProfileSearchE2EComponent } from './user-profile-search.component.e2e';
export class UserProfileE2EComponent {
private readonly locatorUserProfileName: string = 'user-profile-name';
constructor(private root: string) {}
public getRoot() {
return cy.getTestElement(this.root);
}
public getIconContainer(): UserProfileIconE2EComponent {
return new UserProfileIconE2EComponent(this.root);
}
public getSearchContainer(): UserProfileSearchE2EComponent {
return new UserProfileSearchE2EComponent(this.root);
}
public getName() {
return this.getRoot().findTestElementWithClass(this.locatorUserProfileName);
}
}
import { TOGGLE_ELEMENT } from '../../support/angular.util';
export class UserSettingsE2EComponent {
private readonly rootLocator: string = 'user-settings';
private readonly emailBenachrichtigungLocator: string = 'email-benachrichtigung';
private readonly darkModeLocator: string = 'dark-mode';
private readonly buttonLocator: string = 'icon-button';
public getRoot() {
return cy.getTestElementWithOid(this.rootLocator);
}
public getEmailBenachrichtigung(): ToggleE2EComponent {
return new ToggleE2EComponent(this.emailBenachrichtigungLocator);
}
public getDarkMode(): ToggleE2EComponent {
return new ToggleE2EComponent(this.darkModeLocator);
}
public getButton() {
return this.getRoot().findTestElementWithClass(this.buttonLocator);
}
}
export class ToggleE2EComponent {
private readonly rootLocator: string;
constructor(root: string) {
this.rootLocator = root;
}
public getRoot() {
return cy.getTestElementWithOid(this.rootLocator);
}
public getToggle() {
return this.getRoot().findElement(TOGGLE_ELEMENT);
}
}
import { getGreeting } from '../support/app.po';
describe('admin-e2e', () => {
beforeEach(() => cy.visit('/'));
it('should display welcome message', () => {
// Custom command example, see `../support/commands.ts` file
cy.login('my-email@something.com', 'myPassword');
// Function helper example, see `../support/app.po.ts` file
getGreeting().contains(/Welcome/);
});
});
import { BuildInfoE2EComponent } from 'apps/admin-e2e/src/components/buildinfo/buildinfo.e2e.component';
import { MainPage } from 'apps/admin-e2e/src/page-objects/main.po';
import { exist } from 'apps/admin-e2e/src/support/cypress.util';
import { loginAsAriane } from 'apps/admin-e2e/src/support/user-util';
describe('Buildinfo', () => {
const mainPage: MainPage = new MainPage();
const buildInfo: BuildInfoE2EComponent = mainPage.getBuildInfo();
before(() => {
loginAsAriane();
});
after(() => {
// dropCollections();
});
describe('after login', () => {
it('should show ...', { defaultCommandTimeout: 30000 }, () => {
// waitForSpinnerToDisappear();
// exist(vorgangList.getRoot());
});
});
describe('buildinfo', () => {
it('should show version', () => {
exist(buildInfo.getVersion());
});
});
});
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
{
"name": "ariane",
"password": "Y9nk43yrQ_zzIPpfFU-I",
"firstName": "Ariane",
"lastName": "Administrator",
"fullName": "Ariane Administrator",
"email": "ariane.adminsitrator@ozg-sh.de",
"initials": "AA",
"dataTestId": "Ariane_Administrator",
"clientRoles": [],
"groups": [
"E2E Tests"
]
}
\ No newline at end of file
export interface UserE2E {
uuid: string;
id: string;
name: string;
password: string;
firstName: string;
lastName: string;
fullName: string;
initials: string;
dataTestId: string;
}
import { CurrentUserProfileE2EComponent } from '../components/user-profile/current-user-profile.component.e2e';
import { UserSettingsE2EComponent } from '../components/user-settings/user-settings.component.e2e';
export class HeaderE2EComponent {
private readonly locatorLogo: string = 'admin-logo';
private readonly locatorRoot: string = 'header';
private readonly userSettings: UserSettingsE2EComponent = new UserSettingsE2EComponent();
private readonly currentUserProfile: CurrentUserProfileE2EComponent =
new CurrentUserProfileE2EComponent();
public getRoot() {
return cy.getTestElement(this.locatorRoot);
}
public getLogo() {
return cy.getTestElement(this.locatorLogo);
}
public getUserSettings(): UserSettingsE2EComponent {
return this.userSettings;
}
public getCurrentUserProfile(): CurrentUserProfileE2EComponent {
return this.currentUserProfile;
}
}
import { BuildInfoE2EComponent } from '../components/buildinfo/buildinfo.e2e.component';
import { HeaderE2EComponent } from './header.po';
export class MainPage {
private readonly buildInfo: BuildInfoE2EComponent = new BuildInfoE2EComponent();
private readonly header: HeaderE2EComponent = new HeaderE2EComponent();
public getBuildInfo(): BuildInfoE2EComponent {
return this.buildInfo;
}
public getHeader(): HeaderE2EComponent {
return this.header;
}
}
export function waitForSpinnerToDisappear(): boolean {
return cy.getTestElementWithClass('spinner').should('not.exist');
}
export function waitforSpinnerToAppear(): void {
// exist(cy.getTestElementWithClass('spinner'));
}
import { containClass, mouseEnter, notContainClass } from './cypress.util';
export const TOGGLE_ELEMENT: string = 'mat-slide-toggle';
enum AngularClassesE2E {
MAT_CHECKED = 'mat-mdc-slide-toggle-checked',
MAT_BUTTONG_TOGGLE_CHECKED = 'mat-button-toggle-checked',
MAT_FOCUSED = 'mat-focused',
CDK_KEYBOARD_FOCUSED = 'cdk-keyboard-focused',
MAT_BADGE_HIDDEN = 'mat-badge-hidden',
}
enum AngularElementE2E {
MAT_FORM_FIELD = 'mat-form-field',
}
export function hasTooltip(element: any, value: string) {
mouseEnter(element);
element.get('mat-tooltip-component').contains(value);
// element.get(`div[title="${value}"]`);
}
export function isChecked(element: any) {
containClass(element, AngularClassesE2E.MAT_CHECKED);
}
export function isNotChecked(element: any) {
notContainClass(element, AngularClassesE2E.MAT_CHECKED);
}
export function isButtonToggleChecked(element: any) {
containClass(element, AngularClassesE2E.MAT_BUTTONG_TOGGLE_CHECKED);
}
export function isButtonToggleNotChecked(element: any) {
notContainClass(element, AngularClassesE2E.MAT_BUTTONG_TOGGLE_CHECKED);
}
export function getFormField(element: any) {
return element.find(AngularElementE2E.MAT_FORM_FIELD);
}
export function isMatFocused(element: any) {
containClass(element, AngularClassesE2E.MAT_FOCUSED);
}
export function isKeyboardFocused(element: any) {
containClass(element, AngularClassesE2E.CDK_KEYBOARD_FOCUSED);
}
export function expectIconWithoutBadge(element: any): void {
containClass(element, AngularClassesE2E.MAT_BADGE_HIDDEN);
}
export function expectIconWithBadge(element: any): void {
notContainClass(element, AngularClassesE2E.MAT_BADGE_HIDDEN);
}
export const getGreeting = () => cy.get('h1');
/// <reference types="cypress" />
// ***********************************************
// This example commands.ts 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
// ***********************************************
// eslint-disable-next-line @typescript-eslint/no-namespace
enum HttpMethod {
POST = 'POST',
GET = 'GET',
}
const DATA_TEST_ID: string = 'data-test-id';
const DATA_TEST_CLASS: string = 'data-test-class';
const ACCES_TOKEN: string = 'access_token';
const ID_TOKEN: string = 'id_token';
enum Header {
CONTENT_TYPE = 'Content-Type',
AUTHORIZATION = 'Authorization',
}
const CYPRESS_CONFIG_BASE_URL: unknown = 'baseUrl';
enum CypressEnv {
KEYCLOAK_CLIENT = 'keycloakClient',
KEYCLOAK_REALM = 'keycloakRealm',
KEYCLOAK_URL = 'keycloakUrl',
SEARCH = 'search',
SMOCKER = 'smocker',
}
const CONTENT_TYPE_HEADER_VALUE: string = 'application/x-www-form-urlencoded';
declare namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
login(email: string, password: string): void;
getTestElementWithOid(oid: string, ...args);
getTestElement(selector: string, ...args);
getTestElementWithClass(selector: string, ...args);
findTestElementWithClass(selector: string, ...args);
findElement(selector: string);
login(user, password);
logout();
getUserInfo();
}
}
// -- This is a parent command --
Cypress.Commands.add('login', (email, password) => {
console.log('Custom command example: Login', email, password);
Cypress.Commands.add('getTestElement', (selector, ...args) => {
return cy.get(`[${DATA_TEST_ID}~="${selector}"]`, ...args);
});
Cypress.Commands.add('getTestElementWithClass', (selector, ...args) => {
console.log(
'Achtung: Potentiell nicht eindeutiges Ergebnis, weil eine data-test-class mit cy.get() von der DOM-Root aus gesucht wird.',
);
return cy.get(`[${DATA_TEST_CLASS}="${selector}"]`, ...args);
});
Cypress.Commands.add('getTestElementWithOid', (oid, ...args) => {
return cy.getTestElement(oid, ...args);
});
Cypress.Commands.add(
'findTestElementWithClass',
{ prevSubject: true },
(subject: any, selector) => {
return subject.find(`[${DATA_TEST_CLASS}="${selector}"]`);
},
);
Cypress.Commands.add('findElement', { prevSubject: true }, (subject: any, selector: string) => {
return subject.find(selector);
});
Cypress.Commands.add('login', (user: string, password: string) => {
cy.session(user, () => {
cy.request(buildLoginRequest(user, password)).then((response) => handleLoginResponse(response));
});
//
// -- 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) => { ... })
});
function buildLoginRequest(user: string, password: string): any {
return {
method: HttpMethod.POST,
followRedirect: false,
url: `${getKeycloakBaseRealmUrl()}/token`,
headers: {
[Header.CONTENT_TYPE]: CONTENT_TYPE_HEADER_VALUE,
},
body: buildLoginRequestBody(user, password),
};
}
function buildLoginRequestBody(user: string, password: string): any {
return {
client_id: Cypress.env(CypressEnv.KEYCLOAK_CLIENT),
username: user,
password: password,
grant_type: 'password',
redirect_uri: Cypress.config(CYPRESS_CONFIG_BASE_URL),
response_mode: 'fragment',
response_type: 'code',
scope: 'openid',
};
}
function handleLoginResponse(response): void {
const authorization: any = `bearer ${response.body.access_token}`;
cy.visit('', authorization);
window.sessionStorage.setItem(ACCES_TOKEN, response.body.access_token);
window.sessionStorage.setItem(ID_TOKEN, response.body.id_token);
cy.setCookie('XSRF-TOKEN', response.body.session_state);
}
Cypress.Commands.add('getUserInfo', () => {
return cy.request({
method: HttpMethod.GET,
url: `${getKeycloakBaseRealmUrl()}/userinfo`,
headers: {
[Header.AUTHORIZATION]: `bearer ${window.sessionStorage.getItem(ACCES_TOKEN)}`,
},
});
});
Cypress.Commands.add('logout', () => {
cy.request({
method: HttpMethod.GET,
url: `${getKeycloakBaseRealmUrl()}/logout`,
headers: {
[Header.CONTENT_TYPE]: CONTENT_TYPE_HEADER_VALUE,
},
body: {
refresh_token: window.sessionStorage.getItem(ID_TOKEN),
},
failOnStatusCode: false,
}).then(() => {
window.sessionStorage.clear();
cy.visit('');
});
});
function getKeycloakBaseRealmUrl(): string {
return `${Cypress.env(CypressEnv.KEYCLOAK_URL)}realms/${Cypress.env(CypressEnv.KEYCLOAK_REALM)}/protocol/openid-connect`;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment