diff --git a/.github/workflows/test_e2e.yml b/.github/workflows/test_e2e.yml
index 50de7090b..9a36f063f 100644
--- a/.github/workflows/test_e2e.yml
+++ b/.github/workflows/test_e2e.yml
@@ -24,7 +24,7 @@ jobs:
- name: Prepare test system
run: |
sudo chown runner:docker -R *
- bun install
+ bun install --frozen-lockfile
sudo cp ./nginx/e2e-test.conf /etc/nginx/sites-available/default
- name: Boot up test system | seed backend
@@ -40,9 +40,8 @@ jobs:
cd backend
cp .env.test_e2e .env
cd ..
- bun turbo backend#build
- bun turbo frontend#build
- bun turbo backend#start frontend#start --env-mode=loose &
+ bun turbo backend#build frontend#build --env-mode=loose
+ bun turbo backend#start frontend#start --env-mode=loose &
- name: End-to-end tests | prepare
run: |
@@ -106,3 +105,154 @@ jobs:
with:
name: backend-logs-pr-#${{ steps.pr.outputs.number }}
path: /home/runner/work/gradido/gradido/logs/backend
+
+ end-to-end-tests-playwright:
+ name: End-to-End Tests Playwright
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set Node.js version
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18.20.7'
+
+ - name: install bun
+ uses: oven-sh/setup-bun@v2
+
+ - name: Boot up test system | docker-compose mariadb mailserver
+ run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb mailserver
+
+ - name: Prepare test system
+ run: |
+ sudo chown runner:docker -R *
+ bun install --frozen-lockfile
+ sudo cp ./nginx/e2e-test.conf /etc/nginx/sites-available/default
+
+ - name: Boot up test system | seed backend
+ run: bun turbo seed
+
+ - name: copy test config
+ run: |
+ cd frontend
+ cp .env.test_e2e .env
+ cd ../backend
+ cp .env.test_e2e .env
+
+ - name: Moving logs after seeding
+ run: |
+ mkdir -p /home/runner/work/gradido/gradido/logs/backend/seed
+ mv /home/runner/work/gradido/gradido/logs/backend/*.log /home/runner/work/gradido/gradido/logs/backend/seed/
+
+ - name: Boot up test system | docker-compose backend, frontend
+ run: |
+ bun turbo backend#build frontend#build --env-mode=loose
+ bun turbo backend#start frontend#start --env-mode=loose &
+
+ - name: End-to-end tests | prepare
+ run: |
+ cd e2e-tests/playwright/typescript
+ bun install
+
+ - name: wait for frontend and backend to be ready
+ run: |
+ until nc -z 127.0.0.1 3000; do echo waiting for frontend; sleep 1; done;
+ until nc -z 127.0.0.1 4000; do echo waiting for backend; sleep 1; done;
+
+ - name: Start local nginx webserver
+ run: |
+ sudo nginx -t
+ sudo systemctl start nginx
+
+ - name: wait for nginx and mailserver to be ready
+ run: |
+ until nc -z 127.0.0.1 80; do echo waiting for nginx; sleep 1; done;
+ until nc -z 127.0.0.1 1025; do echo waiting for mailserver; sleep 1; done;
+
+ - name: End-to-end tests | run tests
+ id: e2e-tests
+ run: |
+ cd e2e-tests/playwright/typescript
+ bun run test --project="Google Chrome"
+
+ - name: End-to-end tests | if tests failed, upload video
+ id: e2e-video
+ if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: cypress-videos-pr-#${{ steps.pr.outputs.number }}
+ path: /home/runner/work/gradido/gradido/e2e-tests/playwright/typescript/test-results
+
+ end-to-end-tests-playwright-java:
+ name: End-to-End Tests Playwright java
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set Node.js version
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18.20.7'
+
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: install bun
+ uses: oven-sh/setup-bun@v2
+
+ - name: Boot up test system | docker-compose mariadb mailserver
+ run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb mailserver
+
+ - name: Prepare test system
+ run: |
+ sudo chown runner:docker -R *
+ bun install
+ sudo cp ./nginx/e2e-test.conf /etc/nginx/sites-available/default
+
+ - name: Boot up test system | seed backend
+ run: bun turbo seed
+
+ - name: Moving logs after seeding
+ run: |
+ mkdir -p /home/runner/work/gradido/gradido/logs/backend/seed
+ mv /home/runner/work/gradido/gradido/logs/backend/*.log /home/runner/work/gradido/gradido/logs/backend/seed/
+
+ - name: Boot up test system | docker-compose backend, frontend
+ run: |
+ cd backend
+ cp .env.test_e2e .env
+ cd ..
+ bun turbo backend#build frontend#build --env-mode=loose
+ bun turbo backend#start frontend#start --env-mode=loose &
+
+ - name: End-to-end tests | prepare
+ run: |
+ cd e2e-tests/playwright/typescript
+ bun install
+
+ - name: wait for frontend and backend to be ready
+ run: |
+ until nc -z 127.0.0.1 3000; do echo waiting for frontend; sleep 1; done;
+ until nc -z 127.0.0.1 4000; do echo waiting for backend; sleep 1; done;
+
+ - name: Start local nginx webserver
+ run: |
+ sudo nginx -t
+ sudo systemctl start nginx
+
+ - name: wait for nginx and mailserver to be ready
+ run: |
+ until nc -z 127.0.0.1 80; do echo waiting for nginx; sleep 1; done;
+ until nc -z 127.0.0.1 1025; do echo waiting for mailserver; sleep 1; done;
+
+ - name: End-to-end tests | run tests
+ id: e2e-tests
+ run: |
+ cd e2e-tests/playwright/java
+ mvn -B install -D skipTests --no-transfer-progress
+ mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps"
+ mvn test
\ No newline at end of file
diff --git a/backend/.env.test_e2e b/backend/.env.test_e2e
index 62abb975d..e7f3a29c8 100644
--- a/backend/.env.test_e2e
+++ b/backend/.env.test_e2e
@@ -4,6 +4,7 @@ JWT_EXPIRES_IN=2m
GDT_ACTIVE=false
HUMHUB_ACTIVE=false
GMS_ACTIVE=false
+USE_CRYPTO_WORKER=true
# Email
EMAIL=true
diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts
index 4189f87e3..451f671ac 100644
--- a/backend/src/seeds/index.ts
+++ b/backend/src/seeds/index.ts
@@ -58,6 +58,7 @@ const run = async () => {
const { con } = server
await cleanDB()
logger.info('##seed## clean database successful...')
+ logger.info(`crypto worker enabled: ${CONFIG.USE_CRYPTO_WORKER}`)
// seed home community
await writeHomeCommunityEntry()
diff --git a/e2e-tests/.gitignore b/e2e-tests/.gitignore
index 0d29725c0..405b844c5 100644
--- a/e2e-tests/.gitignore
+++ b/e2e-tests/.gitignore
@@ -3,4 +3,4 @@ cypress/screenshots/
cypress/videos/
cypress/reports/
cucumber-messages.ndjson
-
+**/target
diff --git a/e2e-tests/playwright/Dockerfile b/e2e-tests/playwright/Dockerfile
deleted file mode 100644
index c0dfbde47..000000000
--- a/e2e-tests/playwright/Dockerfile
+++ /dev/null
@@ -1,42 +0,0 @@
-###############################################################################
-# Dockerfile to create a ready-to-use Playwright Docker image for end-to-end
-# testing.
-#
-# To avoid hardcoded versoning of Playwright, this Dockerfile is a custom
-# version of the ready-to-use Dockerfile privided by Playwright developement
-# (https://github.com/microsoft/playwright/blob/main/utils/docker/Dockerfile.focal)
-#
-# Here the latest stable versions of the browsers Chromium, Firefox, and Webkit
-# (Safari) are installed, icluding all dependencies based on Ubuntu specified by
-# Playwright developement.
-###############################################################################
-
-FROM ubuntu:focal
-
-# set a timezone for the Playwright browser dependency installation
-ARG TZ=Europe/Berlin
-
-ARG DOCKER_WORKDIR=/tests/
-WORKDIR $DOCKER_WORKDIR
-
-# package manager preparation
-RUN apt-get -qq update && apt-get install -qq -y curl gpg > /dev/null
-# for Node.js
-RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
-# for Yarn
-RUN curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
- echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
-
-# install node v16 and Yarn
-RUN apt-get -qq update && apt-get install -qq -y nodejs yarn
-
-COPY tests/package.json tests/yarn.lock $DOCKER_WORKDIR
-
-# install Playwright with all dependencies
-# for the browsers chromium, firefox, and webkit
-RUN yarn install && yarn playwright install --with-deps
-
-# clean up
-RUN rm -rf /var/lib/apt/lists/* && apt-get -qq clean
-
-COPY tests/ $DOCKER_WORKDIR
diff --git a/e2e-tests/playwright/README.md b/e2e-tests/playwright/README.md
deleted file mode 100644
index a3a2b34bb..000000000
--- a/e2e-tests/playwright/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Gradido End-to-End Testing with [Playwright](https://playwright.dev/) (CI-ready via Docker)
-
-
-A sample setup to show-case Playwright (using Typescript) as an end-to-end testing tool for Gradido runniing in a Docker container.
-Here we have a simple UI-based happy path login test running against the DEV system.
-
-## Precondition
-Since dependencies and configurations for Github Actions integration is not set up yet, please run in root directory
-
-```bash
-docker-compose up
-```
-
-to boot up the DEV system, before running the test.
-
-## Execute the test
-
-```bash
-# build a Docker image from the Dockerfile
-docker build -t gradido_e2e-tests-playwright .
-
-# run the Docker container and execute the given tests
-docker run -it --network=host gradido_e2e-tests-playwright yarn playwright-e2e-tests
-```
diff --git a/e2e-tests/playwright/java/Readme.md b/e2e-tests/playwright/java/Readme.md
new file mode 100644
index 000000000..68aebae3d
--- /dev/null
+++ b/e2e-tests/playwright/java/Readme.md
@@ -0,0 +1,12 @@
+# gradido-e2e-tests-playwright with java
+
+Experimental End-to-end tests with Playwright and Java
+
+## Prerequisites
+
+- Java 17
+- Maven
+- Playwright
+- Gradle
+- Git
+
diff --git a/e2e-tests/playwright/java/pom.xml b/e2e-tests/playwright/java/pom.xml
new file mode 100644
index 000000000..4e8b82d8c
--- /dev/null
+++ b/e2e-tests/playwright/java/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+ net.gradido
+ gradido-e2e-tests-playwright
+ 0.0.1
+ Gradido Playwright End-to-End Tests in Java
+
+ UTF-8
+ 17
+ 17
+
+
+
+ com.microsoft.playwright
+ playwright
+ 1.52.0
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.10.0
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.10.1
+
+
+ 17
+ 17
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M9
+
+
+ **/*Test*.java
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/base/BaseTest.java b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/base/BaseTest.java
new file mode 100644
index 000000000..fbf751f6c
--- /dev/null
+++ b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/base/BaseTest.java
@@ -0,0 +1,69 @@
+package net.gradido.e2e.base;
+
+import com.microsoft.playwright.Browser;
+import com.microsoft.playwright.BrowserContext;
+import com.microsoft.playwright.Playwright;
+import com.microsoft.playwright.Page;
+import com.microsoft.playwright.Tracing;
+import org.junit.jupiter.api.*;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+// Subclasses will inherit PER_CLASS behavior.
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class BaseTest {
+
+ protected Playwright playwright;
+ protected Browser browser;
+
+ @BeforeAll
+ void setUpAll() {
+ playwright = Playwright.create();
+ browser = playwright.chromium().launch();
+ }
+
+ @AfterAll
+ void tearDownAll() {
+ playwright.close();
+ }
+
+ // New instance for each test method.
+ protected BrowserContext context;
+ protected Page page;
+ protected Path currentTracePath;
+
+ @BeforeEach
+ void setUp(TestInfo testInfo) {
+ context = browser.newContext(
+ new Browser.NewContextOptions().setBaseURL("http://localhost:3000")
+ );
+ context.route("**/*", route -> {
+ String url = route.request().url();
+
+ // we skip fontawesome and googleapis requests, we don't need them for functions test, but they cost time
+ if (url.contains("use.fontawesome.com") || url.contains("fonts.googleapis.com")) {
+ route.abort();
+ return;
+ }
+ route.resume();
+ });
+
+ // Start tracing before creating
+ String testName = testInfo.getDisplayName().replaceAll("[^a-zA-Z0-9]", "");
+ currentTracePath = Paths.get("target/traces/" + testName + ".zip");
+ context.tracing().start(new Tracing.StartOptions()
+ .setScreenshots(true)
+ .setSnapshots(true)
+ .setSources(true));
+ page = context.newPage();
+ }
+
+ @AfterEach
+ void tearDown() {
+ // Stop tracing and export it into a zip archive.
+ context.tracing().stop(new Tracing.StopOptions().setPath(currentTracePath));
+
+ page.close();
+ context.close();
+ }
+}
\ No newline at end of file
diff --git a/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/components/Toasts.java b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/components/Toasts.java
new file mode 100644
index 000000000..2e59684b3
--- /dev/null
+++ b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/components/Toasts.java
@@ -0,0 +1,26 @@
+package net.gradido.e2e.components;
+
+import com.microsoft.playwright.Locator;
+import com.microsoft.playwright.Page;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class Toasts {
+ public final Locator toastSlot;
+ public final Locator toastTypeError;
+ public final Locator toastTitle;
+ public final Locator toastMessage;
+
+ public Toasts(Page page) {
+ toastSlot = page.locator("#__BVID__toaster-container");
+ toastTypeError = toastSlot.locator(".toast.text-bg-danger");
+ toastTitle = toastTypeError.locator(".gdd-toaster-title");
+ toastMessage = toastTypeError.locator(".gdd-toaster-body");
+ }
+
+ public void assertErrorToastVisible() {
+ toastTypeError.waitFor();
+ assertTrue(toastTitle.isVisible());
+ assertTrue(toastMessage.isVisible());
+ }
+}
diff --git a/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/pages/LoginPage.java b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/pages/LoginPage.java
new file mode 100644
index 000000000..1de1ba9f5
--- /dev/null
+++ b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/pages/LoginPage.java
@@ -0,0 +1,32 @@
+package net.gradido.e2e.pages;
+
+import com.microsoft.playwright.Locator;
+import com.microsoft.playwright.Page;
+import com.microsoft.playwright.Response;
+
+public class LoginPage {
+ private final Page page;
+
+ private final Locator emailInput;
+ private final Locator passwordInput;
+ private final Locator submitButton;
+
+ public LoginPage(Page page) {
+ this.page = page;
+ emailInput = page.locator("input[name='email']");
+ passwordInput = page.locator("input[name='password']");
+ submitButton = page.locator("button[type='submit']");
+ }
+
+ public void login(String email, String password) {
+ emailInput.fill(email);
+ passwordInput.fill(password);
+ Response response = page.waitForResponse("**/graphql", () -> {
+ submitButton.click();
+ });
+ }
+
+ public void navigate() {
+ page.navigate("http://localhost:3000/login");
+ }
+}
\ No newline at end of file
diff --git a/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/tests/InvalidLoginTest.java b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/tests/InvalidLoginTest.java
new file mode 100644
index 000000000..5d9a70d5b
--- /dev/null
+++ b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/tests/InvalidLoginTest.java
@@ -0,0 +1,26 @@
+package net.gradido.e2e.tests;
+
+import net.gradido.e2e.base.BaseTest;
+import net.gradido.e2e.pages.LoginPage;
+import net.gradido.e2e.components.Toasts;
+import org.junit.jupiter.api.*;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class InvalidLoginTest extends BaseTest {
+ private LoginPage loginPage;
+
+ @BeforeEach
+ void initPageObjects() {
+ loginPage = new LoginPage(page);
+ }
+
+ @Test
+ void invalidUserSeesError() {
+ loginPage.navigate();
+ loginPage.login("peter@lustig.de", "wrongpass");
+ Toasts toast = new Toasts(page);
+ toast.assertErrorToastVisible();
+ }
+}
\ No newline at end of file
diff --git a/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/tests/ValidLoginTest.java b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/tests/ValidLoginTest.java
new file mode 100644
index 000000000..e9b3d4a50
--- /dev/null
+++ b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/tests/ValidLoginTest.java
@@ -0,0 +1,28 @@
+package net.gradido.e2e.tests;
+
+import net.gradido.e2e.base.BaseTest;
+import net.gradido.e2e.pages.LoginPage;
+import net.gradido.e2e.components.Toasts;
+
+import org.junit.jupiter.api.*;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ValidLoginTest extends BaseTest {
+ private LoginPage loginPage;
+
+ @BeforeEach
+ void initPageObjects() {
+ loginPage = new LoginPage(page);
+ }
+
+ @Test
+ void validUserCanLogin() {
+ loginPage.navigate();
+ loginPage.login("peter@lustig.de", "Aa12345_");
+ page.waitForURL("http://localhost:3000/overview");
+ assertTrue(page.url().contains("/overview"));
+ }
+}
+
diff --git a/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/utils/TestUtil.java b/e2e-tests/playwright/java/src/test/java/net/gradido/e2e/utils/TestUtil.java
new file mode 100644
index 000000000..e69de29bb
diff --git a/e2e-tests/playwright/java/src/test/resources/junit-platform.properties b/e2e-tests/playwright/java/src/test/resources/junit-platform.properties
new file mode 100644
index 000000000..4570caf06
--- /dev/null
+++ b/e2e-tests/playwright/java/src/test/resources/junit-platform.properties
@@ -0,0 +1,5 @@
+junit.jupiter.execution.parallel.enabled = true
+junit.jupiter.execution.parallel.mode.default = same_thread
+junit.jupiter.execution.parallel.mode.classes.default = concurrent
+junit.jupiter.execution.parallel.config.strategy=dynamic
+junit.jupiter.execution.parallel.config.dynamic.factor=0.5
\ No newline at end of file
diff --git a/e2e-tests/playwright/tests/global-setup.ts b/e2e-tests/playwright/tests/global-setup.ts
deleted file mode 100644
index 1581a9aea..000000000
--- a/e2e-tests/playwright/tests/global-setup.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { FullConfig } from '@playwright/test';
-
-async function globalSetup(config: FullConfig) {
- process.env.EMAIL = 'bibi@bloxberg.de';
- process.env.PASSWORD = 'Aa12345_';
- process.env.GMS_ACTIVE = false;
- process.env.HUMHUB_ACTIVE = false;
-}
-
-export default globalSetup;
diff --git a/e2e-tests/playwright/tests/gradido_login.spec.ts b/e2e-tests/playwright/tests/gradido_login.spec.ts
deleted file mode 100644
index 0853780d1..000000000
--- a/e2e-tests/playwright/tests/gradido_login.spec.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { test, expect } from '@playwright/test';
-import { LoginPage } from './models/login_page';
-import { WelcomePage } from './models/welcome_page';
-
-
-test('Gradido login test (happy path)', async ({ page }) => {
- const { EMAIL, PASSWORD } = process.env;
- const loginPage = new LoginPage(page);
- await loginPage.goto();
- await loginPage.enterEmail(EMAIL);
- await loginPage.enterPassword(PASSWORD);
- await loginPage.submitLogin();
- // assertions
- await expect(page).toHaveURL('./overview');
-});
diff --git a/e2e-tests/playwright/tests/models/login_page.ts b/e2e-tests/playwright/tests/models/login_page.ts
deleted file mode 100644
index 50abb4933..000000000
--- a/e2e-tests/playwright/tests/models/login_page.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { expect, test, Locator, Page } from '@playwright/test';
-
-export class LoginPage {
- readonly page: Page;
- readonly url: string;
- readonly emailInput: Locator;
- readonly passwordInput: Locator;
- readonly submitBtn: Locator;
-
- constructor(page: Page) {
- this.page = page;
- this.url = './login';
- this.emailInput = page.locator('id=Email-input-field');
- this.passwordInput = page.locator('id=Password-input-field');
- this.submitBtn = page.locator('text=Login');
- }
-
- async goto() {
- await this.page.goto(this.url);
- }
-
- async enterEmail(email: string) {
- await this.emailInput.fill(email);
- }
-
- async enterPassword(password: string) {
- await this.passwordInput.fill(password);
- }
-
- async submitLogin() {
- await this.submitBtn.click();
- }
-}
diff --git a/e2e-tests/playwright/tests/models/welcome_page.ts b/e2e-tests/playwright/tests/models/welcome_page.ts
deleted file mode 100644
index 81d73a771..000000000
--- a/e2e-tests/playwright/tests/models/welcome_page.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { expect, Locator, Page } from '@playwright/test';
-
-export class WelcomePage {
- readonly page: Page;
- readonly url: string;
- readonly profileLink: Locator;
-
- constructor(page: Page){
- this.page = page;
- this.url = './overview';
- this.profileLink = page.locator('href=/profile');
- }
-}
diff --git a/e2e-tests/playwright/tests/playwright.config.ts b/e2e-tests/playwright/tests/playwright.config.ts
deleted file mode 100644
index 13ea41d89..000000000
--- a/e2e-tests/playwright/tests/playwright.config.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import type { PlaywrightTestConfig } from '@playwright/test';
-
-const config: PlaywrightTestConfig = {
- globalSetup: require.resolve('./global-setup'),
- ignoreHTTPSErrors: true,
- locale: 'de-DE',
- reporter: process.env.CI ? 'github' : 'list',
- retries: 1,
- screenshot: 'only-on-failure',
- testDir: '.',
- timeout: 30000,
- trace: 'on-first-retry',
- video: 'never',
- viewport: { width: 1280, height: 720 },
- use: {
- baseURL: process.env.URL || 'http://127.0.0.1:3000',
- browserName: 'webkit',
-
- },
-};
-export default config;
diff --git a/e2e-tests/playwright/typescript/.gitignore b/e2e-tests/playwright/typescript/.gitignore
new file mode 100644
index 000000000..58786aac7
--- /dev/null
+++ b/e2e-tests/playwright/typescript/.gitignore
@@ -0,0 +1,7 @@
+
+# Playwright
+node_modules/
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/e2e-tests/playwright/typescript/components/Toasts.ts b/e2e-tests/playwright/typescript/components/Toasts.ts
new file mode 100644
index 000000000..2cfc64f16
--- /dev/null
+++ b/e2e-tests/playwright/typescript/components/Toasts.ts
@@ -0,0 +1,22 @@
+import type { Locator, Page } from '@playwright/test'
+import { expect } from '@playwright/test'
+
+export class Toasts {
+ public readonly toastSlot: Locator
+ public readonly toastTypeError: Locator
+ public readonly toastTitle: Locator
+ public readonly toastMessage: Locator
+
+ constructor(page: Page) {
+ this.toastSlot = page.locator('#__BVID__toaster-container')
+ this.toastTypeError = this.toastSlot.locator('.toast.text-bg-danger')
+ this.toastTitle = this.toastTypeError.locator('.gdd-toaster-title')
+ this.toastMessage = this.toastTypeError.locator('.gdd-toaster-body')
+ }
+
+ async assertErrorToastVisible(): Promise {
+ await this.toastTypeError.waitFor({ state: 'visible' })
+ expect(this.toastTitle).toBeVisible()
+ expect(this.toastMessage).toBeVisible()
+ }
+}
diff --git a/e2e-tests/playwright/typescript/config/index.ts b/e2e-tests/playwright/typescript/config/index.ts
new file mode 100644
index 000000000..0f62c6739
--- /dev/null
+++ b/e2e-tests/playwright/typescript/config/index.ts
@@ -0,0 +1 @@
+export const FRONTEND_URL = "http://localhost:3000"
\ No newline at end of file
diff --git a/e2e-tests/playwright/typescript/package.json b/e2e-tests/playwright/typescript/package.json
new file mode 100644
index 000000000..66b8c8806
--- /dev/null
+++ b/e2e-tests/playwright/typescript/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "typescript",
+ "version": "1.0.0",
+ "main": "index.js",
+ "license": "MIT",
+ "devDependencies": {
+ "@playwright/test": "^1.53.1",
+ "@types/node": "^24.0.7"
+ },
+ "scripts": {
+ "test": "playwright test --reporter=list"
+ }
+}
diff --git a/e2e-tests/playwright/typescript/pages/LoginPage.ts b/e2e-tests/playwright/typescript/pages/LoginPage.ts
new file mode 100644
index 000000000..8a10c05c3
--- /dev/null
+++ b/e2e-tests/playwright/typescript/pages/LoginPage.ts
@@ -0,0 +1,28 @@
+import type { Locator, Page } from '@playwright/test'
+import { FRONTEND_URL } from '../config'
+
+export class LoginPage {
+ readonly page: Page
+ readonly emailInput: Locator
+ readonly passwordInput: Locator
+ readonly submitButton: Locator
+
+ constructor(page: Page) {
+ this.page = page
+ this.emailInput = page.locator('#email-input-field')
+ this.passwordInput = page.locator('#password-input-field')
+ this.submitButton = page.locator("button[type='submit']")
+ }
+
+ async goto() {
+ await this.page.goto(`${FRONTEND_URL}/login`)
+ }
+
+ async login(email: string, password: string) {
+ await this.emailInput.fill(email)
+ await this.passwordInput.fill(password)
+ const responsePromise = this.page.waitForResponse('**/graphql')
+ await this.submitButton.click()
+ await responsePromise
+ }
+}
\ No newline at end of file
diff --git a/e2e-tests/playwright/typescript/playwright.config.ts b/e2e-tests/playwright/typescript/playwright.config.ts
new file mode 100644
index 000000000..1418b5af9
--- /dev/null
+++ b/e2e-tests/playwright/typescript/playwright.config.ts
@@ -0,0 +1,82 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// import dotenv from 'dotenv';
+// import path from 'path';
+// dotenv.config({ path: path.resolve(__dirname, '.env') });
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ // Glob patterns or regular expressions that match test files.
+ testMatch: '*',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: 2,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? '90%' : '75%',
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://localhost:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ video: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ {
+ name: 'Google Chrome',
+ use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+ },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://localhost:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/e2e-tests/playwright/typescript/tests/InvalidLoginTest.ts b/e2e-tests/playwright/typescript/tests/InvalidLoginTest.ts
new file mode 100644
index 000000000..23747030c
--- /dev/null
+++ b/e2e-tests/playwright/typescript/tests/InvalidLoginTest.ts
@@ -0,0 +1,13 @@
+import { test, expect } from '@playwright/test'
+import { LoginPage } from '../pages/LoginPage'
+import { Toasts } from '../components/Toasts'
+
+test('invalid login', async ({ page }) => {
+ const loginPage = new LoginPage(page)
+ await loginPage.goto()
+ await loginPage.login('peter@lustig.de', 'wrongpass')
+
+ const toast = new Toasts(page)
+ await toast.assertErrorToastVisible()
+ await page.waitForTimeout(50)
+})
\ No newline at end of file
diff --git a/e2e-tests/playwright/typescript/tests/ValidLoginTest.ts b/e2e-tests/playwright/typescript/tests/ValidLoginTest.ts
new file mode 100644
index 000000000..3380ba3c8
--- /dev/null
+++ b/e2e-tests/playwright/typescript/tests/ValidLoginTest.ts
@@ -0,0 +1,11 @@
+import { test, expect } from '@playwright/test'
+import { LoginPage } from '../pages/LoginPage'
+import { FRONTEND_URL } from '../config'
+
+test('valid login', async ({ page }) => {
+ const loginPage = new LoginPage(page)
+ await loginPage.goto()
+ await loginPage.login('peter@lustig.de', 'Aa12345_')
+ await page.waitForURL(`${FRONTEND_URL}/overview`)
+ expect(page.url()).toContain('/overview')
+})
\ No newline at end of file
diff --git a/e2e-tests/playwright/typescript/yarn.lock b/e2e-tests/playwright/typescript/yarn.lock
new file mode 100644
index 000000000..0cd9b4a0a
--- /dev/null
+++ b/e2e-tests/playwright/typescript/yarn.lock
@@ -0,0 +1,41 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@playwright/test@^1.53.1":
+ version "1.53.1"
+ resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.53.1.tgz#3ad5a2ce334b4a78390fd91e0a9d8423c09bc808"
+ integrity sha512-Z4c23LHV0muZ8hfv4jw6HngPJkbbtZxTkxPNIg7cJcTc9C28N/p2q7g3JZS2SiKBBHJ3uM1dgDye66bB7LEk5w==
+ dependencies:
+ playwright "1.53.1"
+
+"@types/node@^24.0.7":
+ version "24.0.7"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.7.tgz#ee580f7850c7eabaeef61ef96b8d8c04fdf94f53"
+ integrity sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw==
+ dependencies:
+ undici-types "~7.8.0"
+
+fsevents@2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+playwright-core@1.53.1:
+ version "1.53.1"
+ resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.53.1.tgz#0b6f7a2006ccb6126ffcc3e3b2fa9efda23b6638"
+ integrity sha512-Z46Oq7tLAyT0lGoFx4DOuB1IA9D1TPj0QkYxpPVUnGDqHHvDpCftu1J2hM2PiWsNMoZh8+LQaarAWcDfPBc6zg==
+
+playwright@1.53.1:
+ version "1.53.1"
+ resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.53.1.tgz#86fb041b237a6868d163c87c4b9737fd1cac145e"
+ integrity sha512-LJ13YLr/ocweuwxyGf1XNFWIU4M2zUSo149Qbp+A4cpwDjsxRPj7k6H25LBrEHiEwxvRbD8HdwvQmRMSvquhYw==
+ dependencies:
+ playwright-core "1.53.1"
+ optionalDependencies:
+ fsevents "2.3.2"
+
+undici-types@~7.8.0:
+ version "7.8.0"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.8.0.tgz#de00b85b710c54122e44fbfd911f8d70174cd294"
+ integrity sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==
diff --git a/frontend/.env.test_e2e b/frontend/.env.test_e2e
new file mode 100644
index 000000000..fb85151cb
--- /dev/null
+++ b/frontend/.env.test_e2e
@@ -0,0 +1 @@
+GRAPHQL_URI=http://127.0.0.1:4000/graphql
\ No newline at end of file