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
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
docker/postgresql-data
docker/postgresql-16-data
docker/roller-data
it-selenium
it-playwright
155 changes: 135 additions & 20 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ on:
branches: [master]
pull_request:
branches: [master, 'feature/**']

jobs:
build-test:
name: Build+Test on Linux/JDK ${{ matrix.java }}
name: Build+Test on Linux/JDK ${{ matrix.java }}
runs-on: ubuntu-latest
timeout-minutes: 30

Expand All @@ -35,17 +35,12 @@ jobs:
java: [ '17', '21', '25' ]

steps:
- name: Set up JDK ${{ matrix.java }}
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
java-version: ${{ matrix.java }}
distribution: 'zulu'

- name: Setup Xvfb
run: |
echo "DISPLAY=:99.0" >> $GITHUB_ENV
Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &

- name: Checkout Project
uses: actions/checkout@v4
with:
Expand All @@ -56,23 +51,12 @@ jobs:
- name: Build Roller and run JUnit Tests
run: mvn -V -ntp install

- name: Run Integration Tests
run: |
cd it-selenium
mvn -V -ntp install

- name: Publish JUnit Report
uses: test-summary/action@v2
if: always()
with:
paths: "app/target/surefire-reports/TEST-*.xml"

- name: Publish IT Report
uses: test-summary/action@v2
if: always()
with:
paths: "it-selenium/target/failsafe-reports/TEST-*.xml"

# only on integration and only once in this matrix
- name: Upload Dev Build on Integration
if: ${{ (matrix.java == '17') && (github.event_name == 'push') }}
Expand All @@ -82,3 +66,134 @@ jobs:
path: ./app/target/roller.war
retention-days: 90
if-no-files-found: error

# Browser tests against Roller's own user database: the new-user journey
# (register, create a weblog, publish and read an entry). The OIDC tests
# skip themselves on this instance, and roller.expectedAuth turns any other
# unexpected skip into a failure.
ui-tests-db:
name: UI tests (db)
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'zulu'

- name: Checkout Project
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: false
show-progress: false

- name: Build Roller
run: mvn -V -ntp -DskipTests install

- name: Start Roller with Jetty and Derby
run: |
mvn -ntp jetty:run > jetty.log 2>&1 &
timeout 300 bash -c 'until curl -sf http://localhost:8080/roller/ > /dev/null; do sleep 5; done'

- name: Install Playwright browser
working-directory: it-playwright
run: mvn -ntp test-compile exec:java -Dexec.classpathScope=test -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install --with-deps chromium"

- name: Run UI tests
working-directory: it-playwright
run: mvn -ntp verify -Droller.expectedAuth=db

- name: Publish UI Test Report
uses: test-summary/action@v2
if: always()
with:
paths: "it-playwright/target/failsafe-reports/TEST-*.xml"

- name: Upload traces and server log
if: failure()
uses: actions/upload-artifact@v4
with:
name: ui-tests-db-diagnostics
path: |
it-playwright/target/playwright-traces
jetty.log
if-no-files-found: ignore

# Browser tests against the Docker Compose stack (Tomcat + PostgreSQL +
# Keycloak), once per authentication method the stack supports: pure OIDC,
# and db-oidc where form login and OIDC are offered side by side. In db-oidc
# mode every suite runs: the journey registers the first user through the
# form before OIDC sign-in provisions any accounts.
ui-tests-oidc:
name: UI tests (${{ matrix.auth }})
runs-on: ubuntu-latest
timeout-minutes: 45

strategy:
fail-fast: false
matrix:
auth: [ 'oidc', 'db-oidc' ]

steps:
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'zulu'

- name: Checkout Project
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: false
show-progress: false

# Roller discovers Keycloak's endpoints at http://keycloak:9080 and
# redirects the browser to them, so the runner must resolve the compose
# hostname too
- name: Resolve Keycloak's hostname on the runner
run: echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts

- name: Build and start Roller, PostgreSQL and Keycloak
run: docker compose up -d --build
env:
AUTHENTICATION_METHOD: ${{ matrix.auth }}

# a fresh database lands on Roller's auto-installer, which waits for a
# click; drive it so the login page is reachable
- name: Wait for Roller and install its database
run: |
timeout 300 bash -c 'until curl -sf http://localhost:8080/ > /dev/null; do sleep 5; done'
curl -sf -X POST http://localhost:8080/roller-ui/install/install!create.rol > /dev/null
curl -sf -X POST http://localhost:8080/roller-ui/install/install!bootstrap.rol > /dev/null

- name: Install Playwright browser
working-directory: it-playwright
run: mvn -ntp test-compile exec:java -Dexec.classpathScope=test -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install --with-deps chromium"

- name: Run UI tests
working-directory: it-playwright
run: mvn -ntp verify -Droller.baseUrl=http://localhost:8080/ -Droller.expectedAuth=${{ matrix.auth }}

- name: Publish UI Test Report
uses: test-summary/action@v2
if: always()
with:
paths: "it-playwright/target/failsafe-reports/TEST-*.xml"

- name: Collect container logs
if: failure()
run: docker compose logs > compose.log

- name: Upload traces and container logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: ui-tests-${{ matrix.auth }}-diagnostics
path: |
it-playwright/target/playwright-traces
compose.log
if-no-files-found: ignore
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ docker/postgresql-data
docker/roller-data
assembly-release/release.sh
it-selenium/overlays/

# Playwright MCP session output
.playwright-mcp/

# runtime logs from mvn jetty:run
logs/
30 changes: 23 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Roller is made up of the following Maven projects:
* _app_: Roller Weblogger webapp, JSP pages, Velocity templates
* _assembly-release_: Used to create official distributions of Roller
* _docs_: Roller documentation in ASCII Doc format
* _it-selenium_: Integrated browser tests for Roller using Selenium
* _it-playwright_: Browser tests for Roller using Playwright (run separately, see below)

## Documentation

Expand Down Expand Up @@ -46,7 +46,7 @@ Compile and build Roller:
$ cd roller
$ mvn -DskipTests=true install

Run Roller in Jetty with an embedded Derby database (for testing only):
Run Roller in Jetty with an in-memory Derby database (for testing only):

$ mvn jetty:run

Expand All @@ -63,10 +63,26 @@ Get the code:

$ git clone https://github.com/apache/roller.git

Run Docker Compose to build and launch Roller along with a PostgreSQL database:
The compose stack runs Roller against a PostgreSQL database with Keycloak as an OpenID Connect identity provider.
Roller and your browser must both reach Keycloak at the same hostname, so add this line to `/etc/hosts` once:

127.0.0.1 keycloak

Run Docker Compose to build and launch everything:

$ cd roller
$ docker-compose up

It will take a while to build and start the Docker image.
Once it's done browse to <http://localhost:8080/roller> to try Roller.
$ docker compose up

It will take a while to build and start the Docker image.
Once it's done browse to <http://localhost:8080/> and log in as `admin`/`admin` (administrator) or `user`/`user` (regular user).


## Running the tests

Unit tests run as part of the normal build:

$ mvn install

Browser-based UI tests live in `it-playwright` and run against a started Roller, whichever way you started it.
See [it-playwright/README.md](it-playwright/README.md) for instructions.
CI runs both on every push and pull request.
2 changes: 1 addition & 1 deletion assembly-release/src/main/assembly/source.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<include>app/**</include>
<include>assembly-release/**</include>
<include>docs/**</include>
<include>it-selenium/**</include>
<include>it-playwright/**</include>
<include>LICENSE.txt</include>
<include>NOTICE.txt</include>
<include>pom.xml</include>
Expand Down
82 changes: 82 additions & 0 deletions it-playwright/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. The ASF licenses this file to You
under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. For additional information regarding
copyright in this work, please see the NOTICE file in the top level
directory of this distribution.
-->

# Roller UI tests

Browser tests for Roller, written in Java with [Playwright](https://playwright.dev/java/).
They are deliberately not part of the main Maven build: nothing here ships to end users, and the build should not depend on a browser being available.
CI runs them on every push and pull request (see `.github/workflows/main.yml`).

The suite points at a running Roller and adapts to how it is configured:

* `NewUserJourneyIT` covers what the old Selenium suite did: register the first user, sign in, create a weblog, publish an entry and read it back on the blog. Roller only accepts registrations while it has no users, so this test skips itself on an instance that already has one, and on an instance that delegates login to an identity provider.
* `OidcLoginIT` signs in through an external OIDC provider as an administrator and as a regular user, checks that only the administrator can reach server administration, and that a signed-in OIDC user can create a weblog and see it rendered. It skips itself when no provider is configured.
* `LoginPageIT` checks the login page offers exactly the sign-in mechanisms of the configured authentication method. It only runs when you declare that method (see below).

Skipping keeps casual local runs friendly, but it also means a misconfigured instance could pass with everything skipped.
Declare what the instance is supposed to be and mismatches become failures instead:

mvn verify -Droller.expectedAuth=db only the username/password form
mvn verify -Droller.expectedAuth=oidc only identity-provider buttons
mvn verify -Droller.expectedAuth=db-oidc both

CI covers all three: `db` on Jetty with Derby, `oidc` and `db-oidc` on the Docker Compose stack (the compose file's authentication method can be overridden with the `AUTHENTICATION_METHOD` environment variable).
Roller's remaining authentication methods have no coverage here: `ldap` needs a directory server and `cma` needs container-managed security, and neither is part of this project's stacks.

## Running against Jetty and Derby (database auth)

From the project root, build once and start Roller with an in-memory database:

mvn -DskipTests install
mvn jetty:run

Then, from this directory:

mvn verify

Every `jetty:run` start gives a fresh database, which is what the new-user journey needs.
Run it again without restarting and the journey reports itself skipped rather than failing, because the first user now exists.

## Running against Docker Compose (OIDC auth)

The compose stack runs Roller against PostgreSQL with Keycloak as the identity provider.
Roller and your browser must both reach Keycloak at the same hostname, so add this line to `/etc/hosts` once:

127.0.0.1 keycloak

Then, from the project root:

docker compose up -d

and from this directory:

mvn verify -Droller.baseUrl=http://localhost:8080/

Keycloak is seeded with an administrator (`admin`/`admin`) and a regular user (`user`/`user`).
To offer form login next to the provider buttons, start the stack with `AUTHENTICATION_METHOD=db-oidc docker compose up -d` instead; on a fresh database the new-user journey then runs against it too.

## Options

mvn verify -Dplaywright.headed=true watch the browser
mvn verify -Droller.baseUrl=<url> point at any Roller instance
mvn verify -Dit.test=NewUserJourneyIT run a single test class

Playwright downloads the browser it needs on first run.
When a test fails, a trace is written to `target/playwright-traces/<test>.zip`; open it with:

npx playwright show-trace target/playwright-traces/<test>.zip
Loading
Loading