Playwright Fixtures
The package extends Playwright's test framework with custom fixtures designed for RHDH testing.
Importing Fixtures
import { test, expect } from "rhdh-e2e-test-utils/test";This import replaces the standard Playwright import and provides additional fixtures.
Available Fixtures
| Fixture | Scope | Type | Description |
|---|---|---|---|
rhdh | worker | RHDHDeployment | Shared RHDH deployment across all tests in a worker |
uiHelper | test | UIhelper | UI interaction helper for Material-UI components |
loginHelper | test | LoginHelper | Authentication helper for various providers |
baseURL | test | string | Automatically set to the RHDH instance URL |
Fixture Scopes
Worker-Scoped Fixtures
The rhdh fixture is worker-scoped, meaning:
- One deployment object is shared across all tests in a worker
- You control when deployment happens (usually in
test.beforeAll) - All tests in the same worker can share the same RHDH instance
test.beforeAll(async ({ rhdh }) => {
// This runs once per worker
await rhdh.configure({ auth: "keycloak" });
await rhdh.deploy();
});Test-Scoped Fixtures
The uiHelper, loginHelper, and baseURL fixtures are test-scoped:
- Created fresh for each test
- Tied to the test's page instance
test("example", async ({ uiHelper, loginHelper }) => {
// Fresh instances for this test
await loginHelper.loginAsKeycloakUser();
await uiHelper.verifyHeading("Welcome");
});Fixture Details
rhdh Fixture
The rhdh fixture provides access to the RHDHDeployment instance:
test.beforeAll(async ({ rhdh }) => {
// Configure deployment options
await rhdh.configure({
auth: "keycloak",
appConfig: "tests/config/app-config.yaml",
});
// Deploy RHDH
await rhdh.deploy();
});
test("access deployment info", async ({ rhdh }) => {
console.log(`URL: ${rhdh.rhdhUrl}`);
console.log(`Namespace: ${rhdh.deploymentConfig.namespace}`);
// Access Kubernetes client
const route = await rhdh.k8sClient.getRouteLocation(
rhdh.deploymentConfig.namespace,
"my-route"
);
});uiHelper Fixture
The uiHelper fixture provides UI interaction methods:
test("UI interactions", async ({ uiHelper }) => {
// Wait for page to load
await uiHelper.waitForLoad();
// Verify content
await uiHelper.verifyHeading("Welcome");
await uiHelper.verifyText("Some content");
// Navigate
await uiHelper.openSidebar("Catalog");
await uiHelper.clickTab("Overview");
// Interact with forms
await uiHelper.fillTextInputByLabel("Name", "my-component");
await uiHelper.clickButton("Submit");
});loginHelper Fixture
The loginHelper fixture handles authentication:
test.beforeEach(async ({ loginHelper }) => {
// Guest authentication
await loginHelper.loginAsGuest();
// Or Keycloak authentication
await loginHelper.loginAsKeycloakUser();
// Or with specific credentials
await loginHelper.loginAsKeycloakUser("test1", "test1@123");
});
test.afterEach(async ({ loginHelper }) => {
await loginHelper.signOut();
});baseURL Fixture
The baseURL fixture is automatically set to the RHDH URL:
test("using baseURL", async ({ page, baseURL }) => {
// page.goto("/") automatically uses baseURL
await page.goto("/");
// Equivalent to:
await page.goto(baseURL);
});Namespace Derivation
The namespace for each worker is derived from the Playwright project name:
// playwright.config.ts
export default defineConfig({
projects: [
{ name: "tech-radar" }, // Namespace: tech-radar
{ name: "catalog" }, // Namespace: catalog
],
});Auto-Cleanup
In CI environments (when CI environment variable is set):
- Namespaces are automatically deleted after tests complete
- Prevents resource accumulation on shared clusters
For local development:
- Namespaces are preserved for debugging
- Manual cleanup may be required
Best Practices for Projects and Spec Files
Each Playwright project name creates a separate namespace. The fixture creates one RHDHDeployment per worker, and you typically call rhdh.deploy() once in beforeAll.
Recommended for overlay workspaces:
- Use one Playwright project named after the workspace.
- Keep one spec file per workspace unless you have a strong reason to split.
This keeps deployment cost low and avoids multiple namespaces unless required.
When You Need Multiple Projects or Spec Files
If requirements differ (different auth, configs, or namespaces), you can:
- Use multiple projects with different names and config overrides.
- Manually manage deployments using
RHDHDeploymentfor advanced flows.
Example using multiple projects:
export default defineConfig({
projects: [
{ name: "workspace-default" },
{ name: "workspace-guest" },
],
});Example with manual deployment:
import { RHDHDeployment } from "rhdh-e2e-test-utils/rhdh";
test.beforeAll(async () => {
const rhdh = new RHDHDeployment("custom-namespace");
await rhdh.configure({ auth: "guest" });
await rhdh.deploy();
});Example: Complete Test Setup
import { test, expect } from "rhdh-e2e-test-utils/test";
test.describe("My Plugin Tests", () => {
test.beforeAll(async ({ rhdh }) => {
await rhdh.configure({
auth: "keycloak",
appConfig: "tests/config/app-config.yaml",
dynamicPlugins: "tests/config/plugins.yaml",
});
await rhdh.deploy();
});
test.beforeEach(async ({ page, loginHelper }) => {
await page.goto("/");
await loginHelper.loginAsKeycloakUser();
});
test("should display heading", async ({ uiHelper }) => {
await uiHelper.openSidebar("My Plugin");
await uiHelper.verifyHeading("My Plugin");
});
test("should show data", async ({ uiHelper }) => {
await uiHelper.openSidebar("My Plugin");
await uiHelper.verifyRowsInTable(["Item 1", "Item 2"]);
});
});