Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ error-screenshots
**/frontend/index.html

**/vite.generated.ts
**/bundles/*
9 changes: 7 additions & 2 deletions vaadin-testbench-loadtest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ This is a PoC of a tool that utilizes Vaadin TestBench E2E tests as "user storie
```
k6-testbench-recorder/
├── testbench-converter-plugin/ # Maven plugin for k6 recording and conversion
├── demo-web-app/ # Sample Vaadin application with TestBench tests
└── demo-web-app-loadtest/ # Integration test module demonstrating the workflow
├── testbench-loadtest-support/ # A JUnit 5 extension
├── loadtest-helper/ # Drop-in helper for Vaadin load testing
└───── load-tests/ # Test module
├── demo-web-app/ # Sample Vaadin application with TestBench tests
└── demo-web-app-loadtest/ # Integration test module demonstrating the workflow
├── demo-web-app-playwright/ # Sample Vaadin application with Playwright tests
└── demo-web-app-playwright-loadtest/ # Integration test module demonstrating the workflow for playwright
```

## Prerequisites
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Example setup for load testing Spring Boot app with Playwright tests


In the actual web app project directory (../demo-web-app-playwright), execute `mvn install`.

Then here the most trivial test is to run the web app locally and execute against it:

mvn verify

Check pom.xml for example show to configure running against another server (more realistic approach that you really should do to get meaningful results).

Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.vaadin</groupId>
<artifactId>load-tests</artifactId>
<version>10.2-SNAPSHOT</version>
</parent>

<artifactId>demo-web-app-playwright-loadtest</artifactId>
<name>Demo Web Application Playwright Load Tests</name>
<packaging>pom</packaging>

<description>
K6 load testing module for the demo Vaadin application using Playwright.

Usage modes:
1. Development (default): Starts server locally, records Playwright scenarios, runs quick test
mvn verify

2. Remote load testing: Runs pre-recorded tests against a remote server
mvn verify -Premote -Dk6.appHost=staging.example.com -Dk6.appPort=8080

For production load testing, always run the load generator on a separate machine
from the application server to get accurate performance metrics.
</description>

<properties>
<!-- Local server configuration (used in default profile) -->
<app.port>8081</app.port>
<management.port>8082</management.port>

<!-- Remote server configuration (used in remote profile) -->
<k6.appHost>localhost</k6.appHost>
<k6.appPort>8080</k6.appPort>

<!-- K6 test configuration -->
<k6.vus>100</k6.vus>
<k6.duration>30s</k6.duration>
<k6.testDir>${project.build.directory}/k6/tests</k6.testDir>

<!-- Combined scenario weights (percentage of VUs for each scenario) -->
<k6.combineScenarios>true</k6.combineScenarios>
<k6.scenarioWeights>helloWorld:70,crudExample:30</k6.scenarioWeights>

<!-- Enable Vaadin-specific metrics collection (requires VaadinActuator in demo app) -->
<k6.collectVaadinMetrics>true</k6.collectVaadinMetrics>

<!-- Skip flags for flexible execution -->
<k6.skipRecord>false</k6.skipRecord>
<k6.skipRun>false</k6.skipRun>
</properties>

<dependencies>
<!-- Dependency on demo-web-app-playwright to ensure it's built first -->
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>demo-web-app-playwright</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Copy the demo-web-app-playwright JAR to target -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>copy-demo-app</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.vaadin</groupId>
<artifactId>demo-web-app-playwright</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<destFileName>demo-web-app-playwright.jar</destFileName>
</artifactItem>
</artifactItems>
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

<profiles>
<!--
DEFAULT PROFILE: Local Development Workflow
- Starts the application server locally
- Records Playwright scenarios (no proxy needed)
- Runs a quick load test to verify the recording

Usage: mvn verify
-->
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>testbench-converter-plugin</artifactId>
<version>${project.version}</version>
<executions>
<!-- Start and wait for the demo app -->
<execution>
<id>start-demo-app</id>
<goals>
<goal>start-server</goal>
</goals>
<configuration>
<serverJar>${project.build.directory}/demo-web-app-playwright.jar</serverJar>
<serverPort>${app.port}</serverPort>
<managementPort>${management.port}</managementPort>
</configuration>
</execution>

<!-- Record Playwright scenarios (no proxy needed) -->
<execution>
<id>record-scenarios</id>
<phase>integration-test</phase>
<goals>
<goal>record-playwright</goal>
</goals>
<configuration>
<skip>${k6.skipRecord}</skip>
<testClasses>
<testClass>HelloWorldPlaywrightIT</testClass>
<testClass>CrudExamplePlaywrightIT</testClass>
</testClasses>
<appPort>${app.port}</appPort>
<testWorkDir>${project.basedir}/../demo-web-app-playwright</testWorkDir>
<harDir>${project.build.directory}</harDir>
<outputDir>${project.build.directory}/k6/tests</outputDir>
</configuration>
</execution>

<!-- Run k6 load tests -->
<execution>
<id>run-load-tests</id>
<phase>integration-test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<skip>${k6.skipRun}</skip>
<testDir>${k6.testDir}</testDir>
<virtualUsers>${k6.vus}</virtualUsers>
<duration>${k6.duration}</duration>
<appPort>${app.port}</appPort>
<managementPort>${management.port}</managementPort>
<combineScenarios>${k6.combineScenarios}</combineScenarios>
<scenarioWeights>${k6.scenarioWeights}</scenarioWeights>
<collectVaadinMetrics>${k6.collectVaadinMetrics}</collectVaadinMetrics>
</configuration>
</execution>

<!-- Stop the demo app -->
<execution>
<id>stop-demo-app</id>
<goals>
<goal>stop-server</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

<!--
REMOTE PROFILE: Production Load Testing
- Does NOT start any server (assumes server is running elsewhere)
- Runs pre-recorded k6 tests against a configurable remote target
- Designed to run from a dedicated load generation machine

Usage:
mvn verify -Premote -Dk6.appHost=staging.example.com
mvn verify -Premote -Dk6.appHost=10.0.1.50 -Dk6.appPort=8080 -Dk6.vus=100 -Dk6.duration=5m
-->
<profile>
<id>remote</id>
<build>
<plugins>
<!-- Validate remote server is accessible -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>check-remote-server</id>
<phase>pre-integration-test</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>bash</executable>
<arguments>
<argument>-c</argument>
<argument>
echo "=============================================="
echo "Remote Load Test Configuration"
echo "=============================================="
echo " Target: http://${k6.appHost}:${k6.appPort}"
echo " Tests: ${k6.testDir}"
echo " VUs: ${k6.vus}"
echo " Duration: ${k6.duration}"
echo "=============================================="
echo ""
echo "Checking if remote server is accessible..."
if curl -s -o /dev/null --connect-timeout 10 "http://${k6.appHost}:${k6.appPort}"; then
echo "Remote server is accessible!"
else
echo "ERROR: Cannot reach http://${k6.appHost}:${k6.appPort}"
echo "Make sure the application is running on the target server."
exit 1
fi
</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>

<!-- K6 load tests against remote server -->
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>testbench-converter-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<id>run-remote-load-tests</id>
<phase>integration-test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<testDir>${k6.testDir}</testDir>
<virtualUsers>${k6.vus}</virtualUsers>
<duration>${k6.duration}</duration>
<appIp>${k6.appHost}</appIp>
<appPort>${k6.appPort}</appPort>
<combineScenarios>${k6.combineScenarios}</combineScenarios>
<scenarioWeights>${k6.scenarioWeights}</scenarioWeights>
<collectVaadinMetrics>${k6.collectVaadinMetrics}</collectVaadinMetrics>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

<!--
RECORD-ONLY PROFILE: Just record scenarios without running load test
Useful when preparing tests for later execution on remote infrastructure

Usage: mvn verify -Precord-only
-->
<profile>
<id>record-only</id>
<properties>
<k6.skipRun>true</k6.skipRun>
</properties>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Demo Web Application (Playwright)

A sample Vaadin application with Spring Boot, demonstrating common patterns like CRUD operations and form handling. Uses Playwright for integration tests instead of TestBench.

## Running the Application

```bash
mvn spring-boot:run
```

The application starts at http://localhost:8080

## Project Structure

```
src/main/java/com/vaadin/laboratory/
├── Application.java # Spring Boot entry point
├── data/ # JPA entities and repositories
│ ├── SamplePerson.java
│ └── SamplePersonRepository.java
├── services/ # Business logic
│ └── SamplePersonService.java
└── views/ # Vaadin UI views
├── MainLayout.java # Application layout with navigation
├── helloworld/
│ └── HelloWorldView.java # Simple hello world demo
└── crudexample/
└── CrudExampleView.java # CRUD grid with form editing

src/test/java/com/vaadin/laboratory/views/scenario/
├── HelloWorldPlaywrightIT.java # E2E test for HelloWorld view
└── CrudExamplePlaywrightIT.java # E2E test for CRUD view
```

## Views

### Hello World
A simple view with a text field and button that displays a greeting notification.

### CRUD Example
A master-detail view with a grid of sample persons and an editing form. Demonstrates:
- Lazy-loading grid with Spring Data
- Form binding with validation
- Create, update, and delete operations

## Integration Tests

The `scenario` package contains Playwright-based end-to-end tests that simulate real user interactions. These tests use `PlaywrightHelper` from the `loadtest-helper` module, which provides:

- Browser context creation with automatic HAR recording when run via the `loadtest:record-playwright` Maven goal
- Base URL resolution from environment variables and system properties

### Running Tests

```bash
# Run all integration tests (requires the app to be running)
mvn failsafe:integration-test -Dit.test=HelloWorldPlaywrightIT

# Run with the it profile (starts/stops the app automatically)
mvn verify -Pit
```

## Building

```bash
# Development build
mvn package

# Production build with optimized frontend
mvn package -Pproduction
```
Loading
Loading