Skip to content

Playwright Fixtures

The package extends Playwright's test framework with custom fixtures designed for RHDH testing.

Importing Fixtures

typescript
import { test, expect } from "rhdh-e2e-test-utils/test";

This import replaces the standard Playwright import and provides additional fixtures.

Available Fixtures

FixtureScopeTypeDescription
rhdhworkerRHDHDeploymentShared RHDH deployment across all tests in a worker
uiHelpertestUIhelperUI interaction helper for Material-UI components
loginHelpertestLoginHelperAuthentication helper for various providers
baseURLteststringAutomatically 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
typescript
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
typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
// 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:

  1. Use multiple projects with different names and config overrides.
  2. Manually manage deployments using RHDHDeployment for advanced flows.

Example using multiple projects:

typescript
export default defineConfig({
  projects: [
    { name: "workspace-default" },
    { name: "workspace-guest" },
  ],
});

Example with manual deployment:

typescript
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

typescript
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"]);
  });
});

Released under the Apache-2.0 License.