diff --git a/alfa-client/.gitignore b/alfa-client/.gitignore
index a69cc6f2555e13e9a76cb9fe95054f68cee336bf..0fd5e011fd8d6b4bf1dde4eced5edf2744d7f6de 100644
--- a/alfa-client/.gitignore
+++ b/alfa-client/.gitignore
@@ -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/*
diff --git a/alfa-client/apps/admin-e2e/cypress.config.json b/alfa-client/apps/admin-e2e/cypress.config.json
new file mode 100644
index 0000000000000000000000000000000000000000..04fd739596f02bde492782acbb249cc7eef61cfd
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/cypress.config.json
@@ -0,0 +1,28 @@
+{
+  "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
diff --git a/alfa-client/apps/admin-e2e/cypress.config.ts b/alfa-client/apps/admin-e2e/cypress.config.ts
index 293ed2fa6d2ada5588d9d952e8385eecbd7d92dc..1e57f65df3ed4318fbab8833bc800aed1be41d2e 100644
--- a/alfa-client/apps/admin-e2e/cypress.config.ts
+++ b/alfa-client/apps/admin-e2e/cypress.config.ts
@@ -1,6 +1,24 @@
 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,
+  },
 });
diff --git a/alfa-client/apps/admin-e2e/docker-compose.yml b/alfa-client/apps/admin-e2e/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c58ba68bab694dc4658a032c45d97f96e03fdd9a
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/docker-compose.yml
@@ -0,0 +1,29 @@
+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
diff --git a/alfa-client/apps/admin-e2e/src/components/buildinfo/buildinfo.e2e.component.ts b/alfa-client/apps/admin-e2e/src/components/buildinfo/buildinfo.e2e.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5bede900428396575e45c322fe1357c7ac9d3a3a
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/components/buildinfo/buildinfo.e2e.component.ts
@@ -0,0 +1,12 @@
+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);
+  }
+}
diff --git a/alfa-client/apps/admin-e2e/src/components/user-profile/current-user-profile.component.e2e.ts b/alfa-client/apps/admin-e2e/src/components/user-profile/current-user-profile.component.e2e.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e58121348f8c0dde77d55ac974bbe3947fdac8b0
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/components/user-profile/current-user-profile.component.e2e.ts
@@ -0,0 +1,29 @@
+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);
+  }
+}
diff --git a/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile-icon.component.e2e.ts b/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile-icon.component.e2e.ts
new file mode 100644
index 0000000000000000000000000000000000000000..39726bb181554e60310450fe1416680d6f423a8b
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile-icon.component.e2e.ts
@@ -0,0 +1,35 @@
+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);
+  }
+}
diff --git a/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile-search.component.e2e.ts b/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile-search.component.e2e.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0e41d9b1e7a68291ab2891169dcf3ec7eb204ff3
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile-search.component.e2e.ts
@@ -0,0 +1,23 @@
+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);
+  }
+}
diff --git a/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile.component.e2e.ts b/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile.component.e2e.ts
new file mode 100644
index 0000000000000000000000000000000000000000..74d883dd96be0f2267d6eecde3979f05098af427
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/components/user-profile/user-profile.component.e2e.ts
@@ -0,0 +1,24 @@
+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);
+  }
+}
diff --git a/alfa-client/apps/admin-e2e/src/components/user-settings/user-settings.component.e2e.ts b/alfa-client/apps/admin-e2e/src/components/user-settings/user-settings.component.e2e.ts
new file mode 100644
index 0000000000000000000000000000000000000000..984a1fc4b8ce48a90193eb466e09b9872c857c22
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/components/user-settings/user-settings.component.e2e.ts
@@ -0,0 +1,40 @@
+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);
+  }
+}
diff --git a/alfa-client/apps/admin-e2e/src/e2e/app.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/app.cy.ts
deleted file mode 100644
index 4a9fe8c305fdfba9d5d7afddb9d8ca5c8e7713a8..0000000000000000000000000000000000000000
--- a/alfa-client/apps/admin-e2e/src/e2e/app.cy.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-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/);
-  });
-});
diff --git a/alfa-client/apps/admin-e2e/src/e2e/app/buildinfo.cy.ts b/alfa-client/apps/admin-e2e/src/e2e/app/buildinfo.cy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..51874a457e9cad9f1dba85d94937066c86299568
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/e2e/app/buildinfo.cy.ts
@@ -0,0 +1,30 @@
+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());
+    });
+  });
+});
diff --git a/alfa-client/apps/admin-e2e/src/fixtures/example.json b/alfa-client/apps/admin-e2e/src/fixtures/example.json
deleted file mode 100644
index 02e4254378e9785f013be7cc8d94a8229dcbcbb7..0000000000000000000000000000000000000000
--- a/alfa-client/apps/admin-e2e/src/fixtures/example.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "name": "Using fixtures to represent data",
-  "email": "hello@cypress.io",
-  "body": "Fixtures are a great way to mock data for responses to routes"
-}
diff --git a/alfa-client/apps/admin-e2e/src/fixtures/user/user_ariane.json b/alfa-client/apps/admin-e2e/src/fixtures/user/user_ariane.json
new file mode 100644
index 0000000000000000000000000000000000000000..17db507b6d5ae19d0e39fbb1423bfda7bcb3ce07
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/fixtures/user/user_ariane.json
@@ -0,0 +1,14 @@
+{
+  "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
diff --git a/alfa-client/apps/admin-e2e/src/model/user.ts b/alfa-client/apps/admin-e2e/src/model/user.ts
new file mode 100644
index 0000000000000000000000000000000000000000..466cedd5d06fc1e8d20e37608ef855ed9e1d0f19
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/model/user.ts
@@ -0,0 +1,11 @@
+export interface UserE2E {
+  uuid: string;
+  id: string;
+  name: string;
+  password: string;
+  firstName: string;
+  lastName: string;
+  fullName: string;
+  initials: string;
+  dataTestId: string;
+}
diff --git a/alfa-client/apps/admin-e2e/src/page-objects/header.po.ts b/alfa-client/apps/admin-e2e/src/page-objects/header.po.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d46260e8238f5b182e8d8c79002301ec633c2bd4
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/page-objects/header.po.ts
@@ -0,0 +1,27 @@
+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;
+  }
+}
diff --git a/alfa-client/apps/admin-e2e/src/page-objects/main.po.ts b/alfa-client/apps/admin-e2e/src/page-objects/main.po.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8a32afeb1969570dd1dd3fce5a954aa61797f9ad
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/page-objects/main.po.ts
@@ -0,0 +1,23 @@
+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'));
+}
diff --git a/alfa-client/apps/admin-e2e/src/support/angular.util.ts b/alfa-client/apps/admin-e2e/src/support/angular.util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a6efc19a4f2a233521c5d4fb4ae664f38567508
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/support/angular.util.ts
@@ -0,0 +1,57 @@
+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);
+}
diff --git a/alfa-client/apps/admin-e2e/src/support/app.po.ts b/alfa-client/apps/admin-e2e/src/support/app.po.ts
deleted file mode 100644
index 32934246969c2ecb827ac05677785933a707a54d..0000000000000000000000000000000000000000
--- a/alfa-client/apps/admin-e2e/src/support/app.po.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const getGreeting = () => cy.get('h1');
diff --git a/alfa-client/apps/admin-e2e/src/support/commands.ts b/alfa-client/apps/admin-e2e/src/support/commands.ts
index c421a3c47c1aa0f82f17f545268ec5965e6b5a79..b04b0c52a85d7c634bca9dfc0f7cb962fc3503b8 100644
--- a/alfa-client/apps/admin-e2e/src/support/commands.ts
+++ b/alfa-client/apps/admin-e2e/src/support/commands.ts
@@ -1,35 +1,140 @@
-/// <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));
+  });
+});
+
+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('');
+  });
 });
-//
-// -- 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 getKeycloakBaseRealmUrl(): string {
+  return `${Cypress.env(CypressEnv.KEYCLOAK_URL)}realms/${Cypress.env(CypressEnv.KEYCLOAK_REALM)}/protocol/openid-connect`;
+}
diff --git a/alfa-client/apps/admin-e2e/src/support/cypress-helper.ts b/alfa-client/apps/admin-e2e/src/support/cypress-helper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2af30a94b0cf72ec893754d07956d21f4908fbcd
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/support/cypress-helper.ts
@@ -0,0 +1,195 @@
+import { Interception, RouteHandler, RouteMatcher } from 'cypress/types/net-stubbing';
+import { GridFsChunkE2E, GridFsFileDataE2E, GridFsFileE2E } from '../model/binary-file';
+import { CommandE2E } from '../model/command';
+import { SmockerMocks } from '../model/smocker';
+import { UsermanagerUserE2E } from '../model/usermanager';
+import { VorgangE2E } from '../model/vorgang';
+import { VorgangAttachedItemE2E } from '../model/vorgang-attached-item';
+
+enum CypressTasks {
+  DROP_COLLECTIONS = 'dropCollections',
+  DROP_USER_MANAGER_COLLECTIONS = 'dropUserManagerCollections',
+  INIT_COMMAND_DATA = 'initCommandData',
+  INIT_GRID_FS_FILE_DATA = 'initGridFsFileData',
+  INIT_GRID_FS_CHUNK_DATA = 'initGridFsChunkData',
+  INIT_VORGANG_DATA = 'initVorgangData',
+  INIT_VORGANG_ATTACHED_ITEM_DATA = 'initVorgangAttachedItemData',
+  INIT_USERMANAGER_DATA = 'initUsermanagerData',
+  COUNT_FILES = 'countFiles',
+  DELETE_FOLDER = 'deleteFolder',
+  UNZIP_FILE = 'unzipDownloadFile',
+  GET_DOWNLOAD_FILES = 'getDownloadFiles',
+}
+
+enum MongoCollections {
+  COMMAND = 'command',
+  FS_CHUNKS = 'fs.chunks',
+  FS_FILES = 'fs.files',
+  VORGANG = 'vorgang',
+  VORGANG_ATTACHED_ITEM = 'vorgangAttachedItem',
+  USER = 'User',
+}
+
+const DOWNLOAD_FOLDER: string = 'cypress/downloads';
+
+export function login(userJsonPath: string): void {
+  cy.fixture(userJsonPath).then((user) => {
+    cy.login(user.name, user.password);
+  });
+}
+
+export function visitUrl(url: string): void {
+  cy.visit(url);
+}
+
+export function initCommandData(data: CommandE2E[]): void {
+  cy.task(CypressTasks.DROP_COLLECTIONS, [MongoCollections.COMMAND]);
+  cy.task(CypressTasks.INIT_COMMAND_DATA, { collection: MongoCollections.COMMAND, data });
+}
+
+export function initGridFsData(data: GridFsFileDataE2E[]): void {
+  const files: GridFsFileE2E[] = [];
+  let chunks: GridFsChunkE2E[] = [];
+
+  data.forEach((singleData) => {
+    files.push(singleData.file);
+    chunks = chunks.concat(singleData.chunks);
+  });
+
+  initGridFsFileData(files);
+  initGridFsChunkData(chunks);
+}
+
+function initGridFsFileData(data: GridFsFileE2E[]): void {
+  cy.task(CypressTasks.DROP_COLLECTIONS, [MongoCollections.FS_FILES]);
+  cy.task(CypressTasks.INIT_GRID_FS_FILE_DATA, { collection: MongoCollections.FS_FILES, data });
+}
+
+function initGridFsChunkData(data: GridFsChunkE2E[]): void {
+  cy.task(CypressTasks.DROP_COLLECTIONS, [MongoCollections.FS_CHUNKS]);
+  cy.task(CypressTasks.INIT_GRID_FS_CHUNK_DATA, { collection: MongoCollections.FS_CHUNKS, data });
+}
+
+export function initVorgangAttachedItemData(data: VorgangAttachedItemE2E[]): void {
+  cy.task(CypressTasks.DROP_COLLECTIONS, [MongoCollections.VORGANG_ATTACHED_ITEM]);
+  cy.task(CypressTasks.INIT_VORGANG_ATTACHED_ITEM_DATA, {
+    collection: MongoCollections.VORGANG_ATTACHED_ITEM,
+    data,
+  });
+}
+
+export function initVorgangData(data: VorgangE2E[]): void {
+  cy.task(CypressTasks.DROP_COLLECTIONS, [MongoCollections.VORGANG]);
+  cy.task(CypressTasks.INIT_VORGANG_DATA, { collection: MongoCollections.VORGANG, data });
+}
+
+export function initSearchIndexData(vorgaenge: VorgangE2E[]): void {
+  vorgaenge.forEach((vorgang) => cy.addVorgangToSearchIndex(vorgang));
+}
+
+export function dropSearchIndex() {
+  cy.removeAllDocumentsFromSearchIndex();
+}
+
+export function initUsermanagerData(data: UsermanagerUserE2E[]): void {
+  //  cy.task(CypressTasks.DROP_USER_MANAGER_COLLECTIONS, [MongoCollections.USER]);
+  cy.task(CypressTasks.INIT_USERMANAGER_DATA, { collection: MongoCollections.USER, data });
+}
+
+export function dropCollections() {
+  cy.task(CypressTasks.DROP_COLLECTIONS, [
+    MongoCollections.COMMAND,
+    MongoCollections.VORGANG,
+    MongoCollections.VORGANG_ATTACHED_ITEM,
+    MongoCollections.FS_FILES,
+    MongoCollections.FS_CHUNKS,
+  ]);
+  //  cy.task(CypressTasks.DROP_USER_MANAGER_COLLECTIONS, [MongoCollections.USER]);
+}
+
+export function countDownloadFiles(): Cypress.Chainable<number> {
+  return cy.task(CypressTasks.COUNT_FILES, DOWNLOAD_FOLDER);
+}
+
+export function deleteDownloadFolder() {
+  return cy.task(CypressTasks.DELETE_FOLDER, DOWNLOAD_FOLDER);
+}
+
+export function unzipDownloadFile(file: string) {
+  return cy.task(CypressTasks.UNZIP_FILE, { folderName: DOWNLOAD_FOLDER, fileName: file });
+}
+
+export function getDownloadFiles(): Cypress.Chainable<Array<string>> {
+  return cy.task(CypressTasks.GET_DOWNLOAD_FILES, DOWNLOAD_FOLDER);
+}
+
+export function scrollToWindowBottom(): void {
+  cy.window().scrollTo('bottom');
+}
+
+export function intercept(method: string, url: string): Cypress.Chainable<null> {
+  return cy.intercept(method, url);
+}
+
+export function interceptWithResponse(
+  method,
+  url: RouteMatcher,
+  response: RouteHandler,
+): Cypress.Chainable<null> {
+  return cy.intercept(method, url, response);
+}
+
+export function waitOfInterceptor(interceptor: string): Cypress.Chainable<Interception> {
+  return cy.wait('@' + interceptor);
+}
+
+export function getTestElement(value: string) {
+  return cy.getTestElement(value);
+}
+
+export function getElement(value: string) {
+  return cy.get(value);
+}
+
+export function urlShouldInclude(text: string) {
+  return cy.url().should('include', text);
+}
+
+//TODO: anders loesen -> bad practice
+export function wait(ms: number, reason = ''): void {
+  cy.wait(ms);
+  if (reason) {
+    console.log(`Had to wait ${ms}ms because of: ${reason}`);
+  }
+}
+//
+
+export function reload(): void {
+  cy.reload();
+}
+
+export function readFileFromDownloads(fileName: string): Cypress.Chainable<any> {
+  return cy.readFile(`${DOWNLOAD_FOLDER}/${fileName}`, { timeout: 5000 });
+}
+
+export function pressTab(): void {
+  cy.realPress('Tab');
+}
+
+//Config
+export function getBaseUrl(): string {
+  return Cypress.config().baseUrl;
+}
+
+//Env
+export function getCypressEnv(value: string) {
+  return Cypress.env(value);
+}
+
+export function addSmockerMock(mock: SmockerMocks): void {
+  cy.addMockToSmocker(mock);
+}
+
+export function resetSmocker(): void {
+  cy.resetSmocker();
+}
diff --git a/alfa-client/apps/admin-e2e/src/support/cypress-tasks.ts b/alfa-client/apps/admin-e2e/src/support/cypress-tasks.ts
new file mode 100644
index 0000000000000000000000000000000000000000..45c36f60e72be3f3d674522988b28af680b8aa8b
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/support/cypress-tasks.ts
@@ -0,0 +1,50 @@
+const fs = require('fs');
+
+module.exports = (on: any, config: any) => {
+  on('after:spec', (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => {
+    if (results && results.video && results.stats.failures === 0) {
+      console.log(`Delete recorded video because spec passed: ${results.video}`);
+      fs.unlinkSync(results.video);
+    }
+  });
+
+  // Workaround für Angular 13 und Cypress mit Webpack 4,
+  // Siehe https://github.com/cypress-io/cypress/issues/19066#issuecomment-1012055705
+  // Entfernen, sobald Cypress Webpack 5 nutzt - https://github.com/cypress-io/cypress/issues/19555
+  // Ursache: Angular linker needed to link partial-ivy code,
+  //   see https://angular.io/guide/creating-libraries#consuming-partial-ivy-code-outside-the-angular-cli
+  // Fehlerbild:
+  //   - Anwendung läuft im Browser, aber nicht in Cypress.
+  //   - Fehlermeldung in Cypress: The injectable 'SystemDateTimeProvider' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available.
+  // Lösung:
+  //   - NPM-Paket identifizieren, dass "SystemDateTimeProvider" enthält.
+  //   - NPM-Paket im "test" Attribut unten hinzufügen.
+  const webpackPreprocessor = require('@cypress/webpack-batteries-included-preprocessor');
+  const webpackOptions = webpackPreprocessor.defaultOptions.webpackOptions;
+
+  webpackOptions.module.rules.unshift({
+    test: /[/\\](@angular|@ngxp|angular-oauth2-oidc)[/\\].+\.m?js$/,
+    resolve: {
+      fullySpecified: false,
+    },
+    use: {
+      loader: 'babel-loader',
+      options: {
+        plugins: ['@angular/compiler-cli/linker/babel'],
+        compact: false,
+        cacheDirectory: true,
+      },
+    },
+  });
+
+  on(
+    'file:preprocessor',
+    webpackPreprocessor({
+      webpackOptions: webpackOptions,
+      typescript: require.resolve('typescript'),
+    }),
+  );
+
+  return config;
+  // Ende - Workaround für Angular 13 und Cypress mit Webpack 4
+};
diff --git a/alfa-client/apps/admin-e2e/src/support/cypress.util.ts b/alfa-client/apps/admin-e2e/src/support/cypress.util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..081f34e8cb5b61c51a12c6861b133d671439177e
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/support/cypress.util.ts
@@ -0,0 +1,115 @@
+import { wait } from './cypress-helper';
+
+//TODO Naming der Methoden geradeziehen
+
+export function containClass(element: any, cssClass: string): void {
+  element.should('have.class', cssClass);
+}
+
+export function notContainClass(element: any, cssClass: string): void {
+  element.should('not.have.class', cssClass);
+}
+
+export function exist(element: any): void {
+  element.should('exist');
+}
+
+export function notExist(element: any): void {
+  element.should('not.exist');
+}
+
+export function haveText(element: any, text: string): void {
+  element
+    .invoke('text')
+    .then((elementText) => elementText.trim())
+    .should('equal', text);
+}
+
+export function haveValue(element: any, value: string): void {
+  element.should('have.value', value);
+}
+
+export function haveFocus(element: any): void {
+  element.should('have.focus');
+}
+
+export function mouseEnter(element: any): void {
+  element.trigger('mouseenter');
+}
+
+export function mouseOver(element: any): void {
+  element.trigger('mouseover');
+}
+
+export function contains(element: any, containing: string): void {
+  element.should('exist').contains(containing);
+}
+
+export function notContains(element: any, containing: string): void {
+  element.contains(containing).should('not.exist');
+}
+
+export function haveLength(element: any, length: number): void {
+  element.should('have.length', length);
+}
+
+export function beChecked(element: any): void {
+  element.should('be.checked');
+}
+
+export function notBeChecked(element: any): void {
+  element.should('not.be.checked');
+}
+
+//TODO: "first()" rausnehmen -> im html eine entprechende data-test-id ansprechen?! | trennen in "get" und "verify"
+export function shouldFirstContains(element: any, containing: string) {
+  element.first().should('exist').contains(containing);
+}
+
+export function shouldHaveAttributeBeGreaterThan(
+  element: any,
+  attributeName: string,
+  value: number,
+) {
+  element.first().should('exist').invoke(attributeName).should('be.gt', value);
+}
+
+export function shouldHaveAttributeBeLowerThan(element: any, attributeName: string, value: number) {
+  element.first().should('exist').invoke(attributeName).should('be.gt', value);
+}
+//
+
+export function shouldHaveAttribute(element: any, name: string, value: string) {
+  element.should('have.attr', name, value);
+}
+
+export function visible(element: any) {
+  element.should('be.visible');
+}
+
+export function notBeVisible(element: any) {
+  element.should('not.be.visible');
+}
+
+export function enter(element: any): void {
+  element.clear().type(CypressKeyboardActions.ENTER);
+}
+
+export function enterWith(
+  element: Cypress.Chainable<JQuery<HTMLElement>>,
+  value: string,
+  delayBeforeEnter: number = 200,
+): void {
+  element.clear().type(value);
+  wait(delayBeforeEnter);
+  element.type(CypressKeyboardActions.ENTER);
+}
+
+export function backspaceOn(element: any): void {
+  element.type(CypressKeyboardActions.BACKSPACE);
+}
+
+enum CypressKeyboardActions {
+  ENTER = '{enter}',
+  BACKSPACE = '{backspace}',
+}
diff --git a/alfa-client/apps/admin-e2e/src/support/e2e.ts b/alfa-client/apps/admin-e2e/src/support/e2e.ts
index 1c1a9e772baea367e08b1c7b15e65b3fede3d17f..8fcf97c3b1114c050e12cc79e14102ad3cad924a 100644
--- a/alfa-client/apps/admin-e2e/src/support/e2e.ts
+++ b/alfa-client/apps/admin-e2e/src/support/e2e.ts
@@ -1,5 +1,5 @@
 // ***********************************************************
-// This example support/e2e.ts is processed and
+// This example support/index.js is processed and
 // loaded automatically before your test files.
 //
 // This is a great place to put global configuration and
@@ -13,5 +13,32 @@
 // https://on.cypress.io/configuration
 // ***********************************************************
 
-// Import commands.ts using ES2015 syntax:
+// Import commands.js using ES2015 syntax:
+import 'cypress-mochawesome-reporter/register';
+import 'cypress-real-events';
+import 'cypress-timestamps/support';
 import './commands';
+
+Cypress.on('command:start', ({ attributes }) => {
+  if (attributes.type === 'parent') {
+    Cypress.log({
+      name: `${new Date().toISOString()} - ${attributes.name}`,
+    });
+  }
+});
+
+Cypress.on('after:screenshot', ({ testFailure, takenAt }) => {
+  if (testFailure) {
+    console.log(`Error at: ${takenAt}`);
+  }
+});
+
+Cypress.on('fail', (err) => {
+  console.error(err);
+  err.message = new Date().toISOString() + '\n' + err.message;
+  throw err;
+});
+
+Cypress.Keyboard.defaults({
+  keystrokeDelay: 30,
+});
diff --git a/alfa-client/apps/admin-e2e/src/support/user-util.ts b/alfa-client/apps/admin-e2e/src/support/user-util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6f47fa0da41a00b4563278cdcd753349957a8d4d
--- /dev/null
+++ b/alfa-client/apps/admin-e2e/src/support/user-util.ts
@@ -0,0 +1,23 @@
+import { UserE2E } from '../model/user';
+import { login } from './cypress-helper';
+
+enum User {
+  ARIANE = 'user/user_ariane.json',
+}
+
+export function loginAsAriane(): void {
+  login(User.ARIANE);
+}
+
+//TODO Cleanup
+export function loginByUi(user: UserE2E): void {
+  cy.visit('')
+    .get('#kc-login')
+    .should('exist')
+    .get('#username')
+    .type(user.name)
+    .get('#password')
+    .type(user.password)
+    .get('#kc-login')
+    .click();
+}
diff --git a/alfa-client/package.json b/alfa-client/package.json
index e1523dd25e7b7786a77adf6d0fe52760d69516fe..4e5f97a057455a87edf3bcb6b95e2251c9fcd2f7 100644
--- a/alfa-client/package.json
+++ b/alfa-client/package.json
@@ -4,7 +4,7 @@
   "license": "MIT",
   "scripts": {
     "start": "nx run alfa:serve --port 4300 --disable-host-check",
-    "start:admin": "nx run admin:serve --port 4300 --disable-host-check",
+    "start:admin": "nx run admin:serve --port 4301 --disable-host-check",
     "start:demo": "nx run demo:serve --port 4500 --disable-host-check",
     "start:debug": "nx run alfa:serve --port 4300 --disable-host-check --verbose",
     "start-for-screenreader": "nx run alfa:serve --host 192.168.178.20 --port 4300 --disable-host-check --verbose",