| name | judo-e2e-testing-docs |
| description | E2E testing guide for JUDO React frontends using Playwright. Covers test patterns, helpers, and browser automation. |
| disable-model-invocation | false |
| user-invocable | false |
| agent | general-purpose |
E2E Testing Guide
Overview
This guide covers end-to-end (E2E) testing for JUDO-generated React frontends using Playwright. E2E tests verify the complete user workflow by automating browser interactions against the running application.
Note: Throughout this document:
{{ lowerCase model.name \}} refers to the application name from judo.properties (app_name property)
[actor_fqn] represents the modeled actor's fully qualified name (e.g., service__actor)
Key Concepts
- Generated Project Skeleton: Like the frontend, the E2E module is generated from the model. It provides utilities, helpers, and configuration but no extension points (unlike frontend hooks).
- Manual Test Implementation: Tests must be written manually. The generated project provides the structure and helper utilities.
- Backend-Served Frontend: Tests run against the frontend served by the JUDO backend (not a dev server).
- Playwright Framework: Uses Microsoft Playwright for browser automation with TypeScript.
Prerequisites
Required Tools
- Node.js: Version 18.15.0 LTS or as specified in
.nvmrc
- PNPM: Package manager for dependencies
- Running Backend: JUDO backend serving the frontend application
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm use
npm i -g pnpm
Enable E2E Generation
To enable the E2E generator in JUDO, edit the generator-parameter.properties file in your project root and change:
generateE2eModule=false
to:
generateE2eModule=true
Then rebuild the project with:
./judo.sh build
This will generate an E2E test module in your application structure. The E2E tests are typically generated using Playwright for React frontends.
Project Structure
application/e2e/{{ lowerCase model.name \}}__[actor_fqn]/
├── playwright/
│ ├── tests/ # ✏️ TEST FILES HERE
│ │ └── *.spec.ts # Playwright test files
│ ├── helpers/ # 📦 GENERATED UTILITIES
│ │ ├── ApiHelper.ts # API request handling
│ │ ├── TableHelper.ts # Table interaction helpers
│ │ ├── utils.ts # Common test utilities
│ │ └── visualElementIds/
│ │ └── VisualElementIds.ts # Generated element IDs
│ ├── .env # Environment configuration
│ ├── playwright.config.ts # Playwright configuration
│ ├── package.json # Dependencies
│ └── README.md # Quick start guide
└── pom.xml # Maven build config
Environment Configuration
Configure the test environment in .env:
APP_URL='http://localhost:8181'
The test base URL is constructed as:
${APP_URL}/${ModelName}/${ActorName}
Example: http://localhost:8181/{{ model.name \}}/Actor
Authentication (Actors with Principal)
When an Actor has a principal (authenticated user), the application requires login through Keycloak. Configure Playwright to handle authentication:
- Create
tests/auth.setup.ts to perform Keycloak login
- Configure
playwright.config.ts with setup project and storageState
- Tests automatically reuse the saved session
See Language-Independent Testing for complete authentication setup examples.
Note: Anonymous actors (no principal) don't require authentication setup.
Running Tests
Install Dependencies
cd application/e2e/{{ lowerCase model.name \}}__[actor_fqn]/playwright
pnpm install
Run Tests
pnpm test
npx playwright test
pnpm test:dev
npx playwright test --ui
npx playwright test tests/MyTest.spec.ts
npx playwright test --headed
npx playwright test --debug
View Test Reports
npx playwright show-report
Video Recording and Screenshots
Playwright can record videos and capture screenshots for debugging failed tests.
Configuration in playwright.config.ts:
use: {
video: "retain-on-failure",
screenshot: "only-on-failure",
},
Video Recording Options:
| Option | Behavior |
|---|
"off" | No video recording |
"on" | Record all tests (uses more disk space) |
"retain-on-failure" | Record all, keep only failed tests (recommended) |
"on-first-retry" | Record only on retry attempts |
Screenshot Options:
| Option | Behavior |
|---|
"off" | No screenshots |
"on" | Screenshot after each test |
"only-on-failure" | Screenshot only when test fails (recommended) |
Viewing Results:
Videos and screenshots are saved in test-results/ folder and included in the HTML report:
npx playwright show-report
How to Enable Video Recording:
- Edit
playwright.config.ts - set video option in the use section:
use: {
video: "on",
},
- Run tests with HTML reporter:
npx playwright test --reporter=html
- View the report:
npx playwright show-report
- Click on any test name in the report to see the video under "Attachments" section.
Video files location: test-results/<test-name>/video.webm
To switch back to retain-on-failure (recommended for CI):
use: {
video: "retain-on-failure",
},
Related Documentation
Quick Start Example
import { test, expect } from '@playwright/test';
import { ApiHelper } from '../helpers/ApiHelper';
import { TableHelper } from '../helpers/TableHelper';
import {
navigateToAccess,
createOnAccesList,
fillPrimitiveField,
submitOnAccesListCreate,
TEST_DATA_PREFIX
} from '../helpers/utils';
import { faker } from '@faker-js/faker';
test.describe('Entity CRUD Operations', () => {
const apiHelper = new ApiHelper();
const tableHelper = new TableHelper();
test('Create and verify entity', async ({ page }) => {
const entityName = TEST_DATA_PREFIX + faker.string.alpha(10);
await apiHelper.gotoWithWait(page);
await navigateToAccess(page, ' Entities');
await createOnAccesList(page);
await fillPrimitiveField(page, "Name *", entityName);
await submitOnAccesListCreate(page);
await expect(tableHelper.getCell(
tableHelper.getRow(page, 1),
"name"
)).toContainText(entityName);
});
});
Best Practices
- Use TEST_DATA_PREFIX: Prefix test data for easy cleanup and identification
- Wait for API responses: Use
ApiHelper.triggerAndWaitWithRegex() for reliable tests
- Use generated VisualElementIds: Reference UI elements by their model IDs when available
- Clean up test data: Delete created entities after tests when possible
- Use faker for unique data: Generate random data to avoid conflicts
- Structure tests by feature: Group related tests in describe blocks
- Avoid i18n labels: Use
data-testid, href patterns, or VisualElementIds instead of translated text (see Language-Independent Testing)
- Handle duplicate testids: Use
.first() when selecting elements in nested UI structures
- Use href for menu navigation:
page.locator('a[href*="/Service/Actor/PageName/"]') is language-independent