rhdh-e2e-test-utils 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +652 -56
  2. package/dist/deployment/keycloak/config/keycloak-values.yaml +94 -0
  3. package/dist/deployment/keycloak/constants.d.ts +29 -0
  4. package/dist/deployment/keycloak/constants.d.ts.map +1 -0
  5. package/dist/deployment/keycloak/constants.js +75 -0
  6. package/dist/deployment/keycloak/deployment.d.ts +89 -0
  7. package/dist/deployment/keycloak/deployment.d.ts.map +1 -0
  8. package/dist/deployment/keycloak/deployment.js +437 -0
  9. package/dist/deployment/keycloak/index.d.ts +2 -0
  10. package/dist/deployment/keycloak/index.d.ts.map +1 -0
  11. package/dist/deployment/keycloak/index.js +1 -0
  12. package/dist/deployment/keycloak/types.d.ts +59 -0
  13. package/dist/deployment/keycloak/types.d.ts.map +1 -0
  14. package/dist/deployment/keycloak/types.js +1 -0
  15. package/dist/deployment/rhdh/config/auth/guest/app-config.yaml +5 -0
  16. package/dist/deployment/rhdh/config/auth/keycloak/app-config.yaml +19 -0
  17. package/dist/deployment/rhdh/config/auth/keycloak/dynamic-plugins.yaml +3 -0
  18. package/dist/deployment/rhdh/config/auth/keycloak/secrets.yaml +12 -0
  19. package/dist/deployment/rhdh/config/common/app-config-rhdh.yaml +6 -0
  20. package/dist/deployment/rhdh/config/common/dynamic-plugins.yaml +3 -0
  21. package/dist/deployment/rhdh/config/common/rhdh-secrets.yaml +7 -0
  22. package/dist/deployment/rhdh/config/helm/value_file.yaml +7 -0
  23. package/dist/deployment/rhdh/config/operator/subscription.yaml +21 -0
  24. package/dist/deployment/rhdh/constants.d.ts +6 -0
  25. package/dist/deployment/rhdh/constants.d.ts.map +1 -1
  26. package/dist/deployment/rhdh/constants.js +17 -5
  27. package/dist/deployment/rhdh/deployment.d.ts +8 -1
  28. package/dist/deployment/rhdh/deployment.d.ts.map +1 -1
  29. package/dist/deployment/rhdh/deployment.js +47 -39
  30. package/dist/deployment/rhdh/index.d.ts +0 -1
  31. package/dist/deployment/rhdh/index.d.ts.map +1 -1
  32. package/dist/deployment/rhdh/types.d.ts +4 -1
  33. package/dist/deployment/rhdh/types.d.ts.map +1 -1
  34. package/dist/eslint/base.config.d.ts.map +1 -1
  35. package/dist/eslint/base.config.js +9 -2
  36. package/dist/playwright/base-config.d.ts +3 -3
  37. package/dist/playwright/base-config.d.ts.map +1 -1
  38. package/dist/playwright/base-config.js +5 -4
  39. package/dist/playwright/fixtures/test.d.ts +4 -1
  40. package/dist/playwright/fixtures/test.d.ts.map +1 -1
  41. package/dist/playwright/fixtures/test.js +16 -4
  42. package/dist/playwright/global-setup.d.ts.map +1 -1
  43. package/dist/playwright/global-setup.js +36 -1
  44. package/dist/playwright/helpers/accessibility.d.ts +13 -0
  45. package/dist/playwright/helpers/accessibility.d.ts.map +1 -0
  46. package/dist/playwright/helpers/accessibility.js +24 -0
  47. package/dist/playwright/helpers/api-endpoints.d.ts +11 -0
  48. package/dist/playwright/helpers/api-endpoints.d.ts.map +1 -0
  49. package/dist/playwright/helpers/api-endpoints.js +15 -0
  50. package/dist/playwright/helpers/api-helper.d.ts +77 -0
  51. package/dist/playwright/helpers/api-helper.d.ts.map +1 -0
  52. package/dist/playwright/helpers/api-helper.js +285 -0
  53. package/dist/playwright/helpers/common.d.ts +31 -0
  54. package/dist/playwright/helpers/common.d.ts.map +1 -0
  55. package/dist/playwright/helpers/common.js +342 -0
  56. package/dist/playwright/helpers/index.d.ts +5 -0
  57. package/dist/playwright/helpers/index.d.ts.map +1 -0
  58. package/dist/playwright/helpers/index.js +4 -0
  59. package/dist/playwright/helpers/navbar.d.ts +2 -0
  60. package/dist/playwright/helpers/navbar.d.ts.map +1 -0
  61. package/dist/playwright/helpers/navbar.js +1 -0
  62. package/dist/playwright/helpers/ui-helper.d.ts +106 -0
  63. package/dist/playwright/helpers/ui-helper.d.ts.map +1 -0
  64. package/dist/playwright/helpers/ui-helper.js +439 -0
  65. package/dist/playwright/page-objects/global-obj.d.ts +25 -0
  66. package/dist/playwright/page-objects/global-obj.d.ts.map +1 -0
  67. package/dist/playwright/page-objects/global-obj.js +24 -0
  68. package/dist/playwright/page-objects/page-obj.d.ts +41 -0
  69. package/dist/playwright/page-objects/page-obj.d.ts.map +1 -0
  70. package/dist/playwright/page-objects/page-obj.js +40 -0
  71. package/dist/playwright/pages/catalog-import.d.ts +31 -0
  72. package/dist/playwright/pages/catalog-import.d.ts.map +1 -0
  73. package/dist/playwright/pages/catalog-import.js +65 -0
  74. package/dist/playwright/pages/catalog.d.ts +14 -0
  75. package/dist/playwright/pages/catalog.d.ts.map +1 -0
  76. package/dist/playwright/pages/catalog.js +37 -0
  77. package/dist/playwright/pages/extensions.d.ts +38 -0
  78. package/dist/playwright/pages/extensions.d.ts.map +1 -0
  79. package/dist/playwright/pages/extensions.js +110 -0
  80. package/dist/playwright/pages/home-page.d.ts +10 -0
  81. package/dist/playwright/pages/home-page.d.ts.map +1 -0
  82. package/dist/playwright/pages/home-page.js +46 -0
  83. package/dist/playwright/pages/index.d.ts +6 -0
  84. package/dist/playwright/pages/index.d.ts.map +1 -0
  85. package/dist/playwright/pages/index.js +5 -0
  86. package/dist/playwright/pages/notifications.d.ts +24 -0
  87. package/dist/playwright/pages/notifications.d.ts.map +1 -0
  88. package/dist/playwright/pages/notifications.js +112 -0
  89. package/dist/utils/kubernetes-client.d.ts +9 -0
  90. package/dist/utils/kubernetes-client.d.ts.map +1 -1
  91. package/dist/utils/kubernetes-client.js +57 -2
  92. package/dist/utils/merge-yamls.d.ts +25 -4
  93. package/dist/utils/merge-yamls.d.ts.map +1 -1
  94. package/dist/utils/merge-yamls.js +52 -12
  95. package/package.json +19 -6
package/README.md CHANGED
@@ -13,20 +13,27 @@ A comprehensive test utility package for Red Hat Developer Hub (RHDH) end-to-end
13
13
  - [Detailed Usage](#detailed-usage)
14
14
  - [Playwright Test Fixtures](#playwright-test-fixtures)
15
15
  - [Playwright Configuration](#playwright-configuration)
16
+ - [Global Setup](#global-setup)
16
17
  - [RHDH Deployment](#rhdh-deployment)
18
+ - [Keycloak Deployment](#keycloak-deployment)
17
19
  - [Utilities](#utilities)
20
+ - [Helpers](#helpers)
21
+ - [Page Objects](#page-objects)
18
22
  - [ESLint Configuration](#eslint-configuration)
19
23
  - [TypeScript Configuration](#typescript-configuration)
20
24
  - [Configuration Files](#configuration-files)
21
25
  - [Environment Variables](#environment-variables)
22
26
  - [Examples](#examples)
23
27
  - [Development](#development)
28
+ - [Testing Local Changes in Consumer Projects](#testing-local-changes-in-consumer-projects)
24
29
 
25
30
  ## Overview
26
31
 
27
32
  `rhdh-e2e-test-utils` simplifies end-to-end testing for RHDH plugins by providing:
28
33
 
29
34
  - **Automated RHDH Deployment**: Deploy RHDH instances via Helm or the RHDH Operator
35
+ - **Keycloak Integration**: Deploy and configure Keycloak for OIDC authentication testing
36
+ - **Modular Auth Configuration**: Switch between guest and Keycloak authentication with a single option
30
37
  - **Playwright Integration**: Custom test fixtures that manage deployment lifecycle
31
38
  - **Kubernetes Utilities**: Helper functions for managing namespaces, ConfigMaps, Secrets, and Routes
32
39
  - **Configuration Merging**: YAML merging with environment variable substitution
@@ -35,6 +42,8 @@ A comprehensive test utility package for Red Hat Developer Hub (RHDH) end-to-end
35
42
  ## Features
36
43
 
37
44
  - Deploy RHDH using Helm charts or the RHDH Operator
45
+ - Deploy Keycloak for authentication testing with automatic realm, client, and user configuration
46
+ - Modular authentication configuration (guest, Keycloak)
38
47
  - Automatic namespace creation and cleanup
39
48
  - Dynamic plugin configuration
40
49
  - Helpers for UI, API and common Utils
@@ -79,7 +88,10 @@ The package provides multiple entry points for different use cases:
79
88
  | `rhdh-e2e-test-utils/test` | Playwright test fixtures with RHDH deployment |
80
89
  | `rhdh-e2e-test-utils/playwright-config` | Base Playwright configuration |
81
90
  | `rhdh-e2e-test-utils/rhdh` | RHDH deployment class and types |
91
+ | `rhdh-e2e-test-utils/keycloak` | Keycloak deployment helper for authentication testing |
82
92
  | `rhdh-e2e-test-utils/utils` | Utility functions (bash, YAML, Kubernetes) |
93
+ | `rhdh-e2e-test-utils/helpers` | UI, API, and login helper classes |
94
+ | `rhdh-e2e-test-utils/pages` | Page object classes for common RHDH pages |
83
95
  | `rhdh-e2e-test-utils/eslint` | ESLint configuration factory |
84
96
  | `rhdh-e2e-test-utils/tsconfig` | Base TypeScript configuration |
85
97
 
@@ -97,18 +109,15 @@ yarn add @playwright/test rhdh-e2e-test-utils
97
109
 
98
110
  ```typescript
99
111
  // playwright.config.ts
100
- import { defineConfig } from "@playwright/test";
101
- import { createPlaywrightConfig } from "rhdh-e2e-test-utils/playwright-config";
102
-
103
- export default defineConfig(
104
- createPlaywrightConfig({
105
- projects: [
106
- {
107
- name: "my-plugin",
108
- },
109
- ],
110
- })
111
- );
112
+ import { defineConfig } from "rhdh-e2e-test-utils/playwright-config";
113
+
114
+ export default defineConfig({
115
+ projects: [
116
+ {
117
+ name: "my-plugin",
118
+ },
119
+ ],
120
+ });
112
121
  ```
113
122
 
114
123
  ### 3. Create Your Test
@@ -129,10 +138,10 @@ test("my plugin test", async ({ page }) => {
129
138
 
130
139
  ### 4. Create Configuration Files
131
140
 
132
- Create a `config/` directory with your RHDH configuration:
141
+ Create a `tests/config/` directory with your RHDH configuration:
133
142
 
134
143
  ```
135
- config/
144
+ tests/config/
136
145
  ├── app-config-rhdh.yaml # App configuration
137
146
  ├── dynamic-plugins.yaml # Dynamic plugins configuration
138
147
  └── rhdh-secrets.yaml # Secrets (with env var placeholders)
@@ -166,6 +175,8 @@ import { test, expect } from "rhdh-e2e-test-utils/test";
166
175
  | Fixture | Scope | Description |
167
176
  |---------|-------|-------------|
168
177
  | `rhdh` | worker | Shared RHDHDeployment across all tests in a worker |
178
+ | `uiHelper` | test | UIhelper instance for common UI interactions |
179
+ | `loginHelper` | test | LoginHelper instance for authentication flows |
169
180
  | `baseURL` | test | Automatically set to the RHDH instance URL |
170
181
 
171
182
  #### Fixture Behavior
@@ -176,6 +187,7 @@ import { test, expect } from "rhdh-e2e-test-utils/test";
176
187
 
177
188
  ```typescript
178
189
  import { test, expect } from "rhdh-e2e-test-utils/test";
190
+
179
191
  test.beforeAll(async ({ rhdh }) => {
180
192
  // Configure RHDH (creates namespace, and optional DeploymentOptions)
181
193
  await rhdh.configure();
@@ -187,10 +199,17 @@ test.beforeAll(async ({ rhdh }) => {
187
199
  await rhdh.deploy();
188
200
  });
189
201
 
190
- test("example test", async ({ page, rhdh }) => {
202
+ test("example test", async ({ page, rhdh, uiHelper, loginHelper }) => {
191
203
  // page.goto("/") will use rhdh.rhdhUrl as base
192
204
  await page.goto("/");
193
205
 
206
+ // Login as guest user
207
+ await loginHelper.loginAsGuest();
208
+
209
+ // Use UI helper for common interactions
210
+ await uiHelper.verifyHeading("Welcome");
211
+ await uiHelper.clickButton("Get Started");
212
+
194
213
  // Access deployment info
195
214
  console.log(`Namespace: ${rhdh.deploymentConfig.namespace}`);
196
215
  console.log(`URL: ${rhdh.rhdhUrl}`);
@@ -204,24 +223,22 @@ test("example test", async ({ page, rhdh }) => {
204
223
 
205
224
  ### Playwright Configuration
206
225
 
207
- Use `createPlaywrightConfig` for sensible defaults:
226
+ Use `defineConfig` for sensible defaults:
208
227
 
209
228
  ```typescript
210
- import { defineConfig } from "@playwright/test";
211
- import { createPlaywrightConfig } from "rhdh-e2e-test-utils/playwright-config";
212
-
213
- export default defineConfig(
214
- createPlaywrightConfig({
215
- projects: [
216
- {
217
- name: "tech-radar", // Also used as namespace
218
- },
219
- {
220
- name: "catalog",
221
- },
222
- ],
223
- })
224
- );
229
+ // playwright.config.ts
230
+ import { defineConfig } from "rhdh-e2e-test-utils/playwright-config";
231
+
232
+ export default defineConfig({
233
+ projects: [
234
+ {
235
+ name: "tech-radar", // Also used as namespace
236
+ },
237
+ {
238
+ name: "catalog",
239
+ },
240
+ ],
241
+ });
225
242
  ```
226
243
 
227
244
  #### Base Configuration Defaults
@@ -237,6 +254,41 @@ export default defineConfig(
237
254
  | `trace` | Retain on failure |
238
255
  | `screenshot` | Only on failure |
239
256
 
257
+ #### Global Setup
258
+
259
+ The package includes a global setup function that runs once before all tests. It performs the following:
260
+
261
+ 1. **Binary Check**: Verifies that required binaries (`oc`, `kubectl`, `helm`) are installed
262
+ 2. **Cluster Router Base**: Fetches the OpenShift ingress domain and sets `K8S_CLUSTER_ROUTER_BASE`
263
+ 3. **Keycloak Deployment**: Automatically deploys and configures Keycloak for OIDC authentication
264
+
265
+ ```typescript
266
+ // playwright.config.ts
267
+ import { defineConfig } from "rhdh-e2e-test-utils/playwright-config";
268
+
269
+ export default defineConfig({
270
+ // Global setup is automatically included
271
+ projects: [{ name: "my-plugin" }],
272
+ });
273
+ ```
274
+
275
+ **Keycloak Auto-Deployment Behavior:**
276
+
277
+ - Keycloak is deployed to the `rhdh-keycloak` namespace
278
+ - If Keycloak is already running, deployment is skipped
279
+ - All Keycloak environment variables are automatically set after deployment
280
+ - The following test credentials are created:
281
+ - Username: `test1`, Password: `test1@123`
282
+ - Username: `test2`, Password: `test2@123`
283
+
284
+ **Skip Keycloak Deployment:**
285
+
286
+ If your tests don't require Keycloak/OIDC authentication, set the environment variable:
287
+
288
+ ```bash
289
+ SKIP_KEYCLOAK_DEPLOYMENT=true yarn playwright test
290
+ ```
291
+
240
292
  ### RHDH Deployment
241
293
 
242
294
  #### RHDHDeployment Class
@@ -246,25 +298,33 @@ The core class for managing RHDH deployments:
246
298
  ```typescript
247
299
  import { RHDHDeployment } from "rhdh-e2e-test-utils/rhdh";
248
300
 
249
- const deployment = new RHDHDeployment({
250
- namespace: "my-test-namespace",
301
+ // Create deployment with namespace (required)
302
+ const deployment = new RHDHDeployment("my-test-namespace");
303
+
304
+ // Configure with options (call before deploy)
305
+ await deployment.configure({
251
306
  version: "1.5", // Optional, uses RHDH_VERSION env
252
307
  method: "helm", // Optional, uses INSTALLATION_METHOD env
308
+ auth: "keycloak", // Optional, defaults to "keycloak"
253
309
  appConfig: "config/app-config-rhdh.yaml", // Optional
254
310
  secrets: "config/rhdh-secrets.yaml", // Optional
255
311
  dynamicPlugins: "config/dynamic-plugins.yaml", // Optional
256
312
  valueFile: "config/value_file.yaml", // Optional & Helm only
257
313
  subscription: "config/subscription.yaml", // Optional & Operator only
258
314
  });
315
+
316
+ // Deploy RHDH
317
+ await deployment.deploy();
259
318
  ```
260
319
 
261
320
  #### Deployment Options
262
321
 
263
322
  ```typescript
264
323
  type DeploymentOptions = {
265
- namespace: string; // Required: Kubernetes namespace
324
+ namespace?: string; // Kubernetes namespace (set via constructor)
266
325
  version?: string; // RHDH version (e.g., "1.5", "1.5.1-CI")
267
326
  method?: "helm" | "operator"; // Installation method
327
+ auth?: "guest" | "keycloak"; // Authentication provider (default: "keycloak")
268
328
  appConfig?: string; // Path to app-config YAML
269
329
  secrets?: string; // Path to secrets YAML
270
330
  dynamicPlugins?: string; // Path to dynamic-plugins YAML
@@ -273,6 +333,17 @@ type DeploymentOptions = {
273
333
  };
274
334
  ```
275
335
 
336
+ #### Authentication Providers
337
+
338
+ The package supports modular authentication configuration. Set the `auth` option to automatically include the appropriate auth-specific configurations:
339
+
340
+ | Provider | Description |
341
+ |----------|-------------|
342
+ | `guest` | Guest authentication for development/testing |
343
+ | `keycloak` | OIDC authentication via Keycloak (default) |
344
+
345
+ Auth-specific configurations are automatically merged with your project configurations. For Keycloak authentication, see [Keycloak Deployment](#keycloak-deployment).
346
+
276
347
  #### Deployment Methods
277
348
 
278
349
  ##### Helm Deployment
@@ -317,6 +388,162 @@ await rhdh.deploy();
317
388
  | `deploymentConfig` | `DeploymentConfig` | Current deployment configuration |
318
389
  | `k8sClient` | `KubernetesClientHelper` | Kubernetes client instance |
319
390
 
391
+ ### Keycloak Deployment
392
+
393
+ The package provides a `KeycloakHelper` class for deploying and configuring Keycloak in OpenShift, enabling OIDC authentication testing with RHDH.
394
+
395
+ #### KeycloakHelper Class
396
+
397
+ ```typescript
398
+ import { KeycloakHelper } from "rhdh-e2e-test-utils/keycloak";
399
+
400
+ const keycloak = new KeycloakHelper({
401
+ namespace: "rhdh-keycloak", // Optional, defaults to "rhdh-keycloak"
402
+ releaseName: "keycloak", // Optional, defaults to "keycloak"
403
+ adminUser: "admin", // Optional, defaults to "admin"
404
+ adminPassword: "admin123", // Optional, defaults to "admin123"
405
+ });
406
+
407
+ // Deploy Keycloak using Bitnami Helm chart
408
+ await keycloak.deploy();
409
+
410
+ // Configure realm, client, groups, and users for RHDH
411
+ await keycloak.configureForRHDH();
412
+ ```
413
+
414
+ #### Deployment Options
415
+
416
+ ```typescript
417
+ type KeycloakDeploymentOptions = {
418
+ namespace?: string; // Kubernetes namespace (default: "rhdh-keycloak")
419
+ releaseName?: string; // Helm release name (default: "keycloak")
420
+ valuesFile?: string; // Custom Helm values file
421
+ adminUser?: string; // Admin username (default: "admin")
422
+ adminPassword?: string; // Admin password (default: "admin123")
423
+ };
424
+ ```
425
+
426
+ #### KeycloakHelper API
427
+
428
+ | Method | Description |
429
+ |--------|-------------|
430
+ | `deploy()` | Deploy Keycloak using Helm and wait for it to be ready |
431
+ | `configureForRHDH(options?)` | Configure realm, client, groups, and users for RHDH |
432
+ | `isRunning()` | Check if Keycloak is accessible |
433
+ | `connect(config)` | Connect to an existing Keycloak instance |
434
+ | `createRealm(config)` | Create a new realm |
435
+ | `createClient(realm, config)` | Create a client in a realm |
436
+ | `createGroup(realm, config)` | Create a group in a realm |
437
+ | `createUser(realm, config)` | Create a user with optional group membership |
438
+ | `getUsers(realm)` | Get all users in a realm |
439
+ | `getGroups(realm)` | Get all groups in a realm |
440
+ | `deleteUser(realm, username)` | Delete a user |
441
+ | `deleteGroup(realm, groupName)` | Delete a group |
442
+ | `deleteRealm(realm)` | Delete a realm |
443
+ | `teardown()` | Delete the Keycloak namespace |
444
+ | `waitUntilReady(timeout?)` | Wait for Keycloak StatefulSet to be ready |
445
+
446
+ #### Properties
447
+
448
+ | Property | Type | Description |
449
+ |----------|------|-------------|
450
+ | `keycloakUrl` | `string` | The Keycloak instance URL |
451
+ | `realm` | `string` | Configured realm name |
452
+ | `clientId` | `string` | Configured client ID |
453
+ | `clientSecret` | `string` | Configured client secret |
454
+ | `deploymentConfig` | `KeycloakDeploymentConfig` | Current deployment configuration |
455
+ | `k8sClient` | `KubernetesClientHelper` | Kubernetes client instance |
456
+
457
+ #### Default Configuration
458
+
459
+ When using `configureForRHDH()`, the following defaults are applied:
460
+
461
+ **Default Realm**: `rhdh`
462
+
463
+ **Default Client** (`rhdh-client`):
464
+ - Client secret: `rhdh-client-secret`
465
+ - Standard flow, implicit flow, direct access grants enabled
466
+ - Service accounts enabled with realm-management roles
467
+
468
+ **Default Groups**:
469
+ - `developers`
470
+ - `admins`
471
+ - `viewers`
472
+
473
+ **Default Users**:
474
+ | Username | Password | Groups |
475
+ |----------|----------|--------|
476
+ | `test1` | `test1@123` | developers |
477
+ | `test2` | `test2@123` | developers |
478
+
479
+ #### Example: Full RHDH + Keycloak Setup
480
+
481
+ ```typescript
482
+ import { test } from "rhdh-e2e-test-utils/test";
483
+ import { KeycloakHelper } from "rhdh-e2e-test-utils/keycloak";
484
+
485
+ let keycloak: KeycloakHelper;
486
+
487
+ test.beforeAll(async ({ rhdh }) => {
488
+ // Deploy Keycloak
489
+ keycloak = new KeycloakHelper({ namespace: "rhdh-keycloak" });
490
+ await keycloak.deploy();
491
+ await keycloak.configureForRHDH();
492
+
493
+ // Set environment variables for RHDH
494
+ process.env.KEYCLOAK_BASE_URL = keycloak.keycloakUrl;
495
+ process.env.KEYCLOAK_REALM = keycloak.realm;
496
+ process.env.KEYCLOAK_CLIENT_ID = keycloak.clientId;
497
+ process.env.KEYCLOAK_CLIENT_SECRET = keycloak.clientSecret;
498
+ process.env.KEYCLOAK_METADATA_URL = `${keycloak.keycloakUrl}/realms/${keycloak.realm}/.well-known/openid-configuration`;
499
+ process.env.KEYCLOAK_LOGIN_REALM = keycloak.realm;
500
+
501
+ // Deploy RHDH with Keycloak authentication
502
+ await rhdh.configure({ auth: "keycloak" });
503
+ await rhdh.deploy();
504
+ });
505
+
506
+ test("login with Keycloak user", async ({ page, loginHelper }) => {
507
+ await page.goto("/");
508
+ await loginHelper.loginAsKeycloakUser("test1", "test1@123");
509
+ // ... test assertions
510
+ });
511
+
512
+ test.afterAll(async () => {
513
+ await keycloak.teardown();
514
+ });
515
+ ```
516
+
517
+ #### Connect to Existing Keycloak
518
+
519
+ ```typescript
520
+ import { KeycloakHelper } from "rhdh-e2e-test-utils/keycloak";
521
+
522
+ const keycloak = new KeycloakHelper();
523
+
524
+ // Connect with admin credentials
525
+ await keycloak.connect({
526
+ baseUrl: "https://keycloak.example.com",
527
+ username: "admin",
528
+ password: "admin-password",
529
+ });
530
+
531
+ // Or connect with client credentials
532
+ await keycloak.connect({
533
+ baseUrl: "https://keycloak.example.com",
534
+ realm: "my-realm",
535
+ clientId: "admin-client",
536
+ clientSecret: "client-secret",
537
+ });
538
+
539
+ // Now you can manage users, groups, etc.
540
+ await keycloak.createUser("my-realm", {
541
+ username: "newuser",
542
+ password: "password123",
543
+ groups: ["developers"],
544
+ });
545
+ ```
546
+
320
547
  ### Utilities
321
548
 
322
549
  #### Bash Command Execution
@@ -387,6 +614,222 @@ const result = envsubst("Port: ${PORT:-8080}");
387
614
  const result = envsubst("API: ${API_URL}");
388
615
  ```
389
616
 
617
+ ### Helpers
618
+
619
+ The package provides helper classes for common testing operations.
620
+
621
+ #### UIhelper
622
+
623
+ A utility class for common UI interactions with Material-UI components:
624
+
625
+ ```typescript
626
+ import { UIhelper } from "rhdh-e2e-test-utils/helpers";
627
+
628
+ const uiHelper = new UIhelper(page);
629
+
630
+ // Wait for page to fully load
631
+ await uiHelper.waitForLoad();
632
+
633
+ // Verify headings and text
634
+ await uiHelper.verifyHeading("Welcome to RHDH");
635
+ await uiHelper.verifyText("Some content");
636
+
637
+ // Button interactions
638
+ await uiHelper.clickButton("Submit");
639
+ await uiHelper.clickButtonByLabel("Close");
640
+
641
+ // Navigation
642
+ await uiHelper.openSidebar("Catalog");
643
+ await uiHelper.clickTab("Overview");
644
+
645
+ // Table operations
646
+ await uiHelper.verifyRowsInTable(["row1", "row2"]);
647
+ await uiHelper.verifyCellsInTable(["cell1", "cell2"]);
648
+
649
+ // MUI component interactions
650
+ await uiHelper.selectMuiBox("Kind", "Component");
651
+ await uiHelper.fillTextInputByLabel("Name", "my-component");
652
+ ```
653
+
654
+ #### LoginHelper
655
+
656
+ Handles authentication flows for different providers:
657
+
658
+ ```typescript
659
+ import { LoginHelper } from "rhdh-e2e-test-utils/helpers";
660
+
661
+ const loginHelper = new LoginHelper(page);
662
+
663
+ // Guest authentication
664
+ await loginHelper.loginAsGuest();
665
+ await loginHelper.signOut();
666
+
667
+ // Keycloak authentication
668
+ await loginHelper.loginAsKeycloakUser("username", "password");
669
+
670
+ // GitHub authentication (requires environment variables)
671
+ await loginHelper.loginAsGithubUser();
672
+ ```
673
+
674
+ #### APIHelper
675
+
676
+ Provides utilities for API interactions with both GitHub and Backstage catalog:
677
+
678
+ ```typescript
679
+ import { APIHelper } from "rhdh-e2e-test-utils/helpers";
680
+
681
+ // GitHub API operations
682
+ await APIHelper.createGitHubRepo("owner", "repo-name");
683
+ await APIHelper.deleteGitHubRepo("owner", "repo-name");
684
+ const prs = await APIHelper.getGitHubPRs("owner", "repo", "open");
685
+
686
+ // Backstage catalog API operations
687
+ const apiHelper = new APIHelper();
688
+ await apiHelper.setBaseUrl(rhdhUrl);
689
+ await apiHelper.setStaticToken(token);
690
+
691
+ const users = await apiHelper.getAllCatalogUsersFromAPI();
692
+ const groups = await apiHelper.getAllCatalogGroupsFromAPI();
693
+ const locations = await apiHelper.getAllCatalogLocationsFromAPI();
694
+
695
+ // Schedule entity refresh
696
+ await apiHelper.scheduleEntityRefreshFromAPI("my-component", "component", token);
697
+ ```
698
+
699
+ #### setupBrowser
700
+
701
+ Utility function for setting up a shared browser context with video recording. Use this in `test.beforeAll` for serial test suites or when you want to persist the browser context across multiple tests (e.g., to avoid repeated logins):
702
+
703
+ ```typescript
704
+ import { test } from "@playwright/test";
705
+ import { setupBrowser, LoginHelper } from "rhdh-e2e-test-utils/helpers";
706
+ import type { Page, BrowserContext } from "@playwright/test";
707
+
708
+ test.describe.configure({ mode: "serial" });
709
+
710
+ let page: Page;
711
+ let context: BrowserContext;
712
+
713
+ test.beforeAll(async ({ browser }, testInfo) => {
714
+ // Setup shared browser context with video recording
715
+ ({ page, context } = await setupBrowser(browser, testInfo));
716
+
717
+ // Login once, session persists across all tests in this suite
718
+ const loginHelper = new LoginHelper(page);
719
+ await page.goto("/");
720
+ await loginHelper.loginAsKeycloakUser();
721
+ });
722
+
723
+ test.afterAll(async () => {
724
+ await context.close();
725
+ });
726
+
727
+ test("first test - already logged in", async () => {
728
+ await page.goto("/catalog");
729
+ // No need to login again
730
+ });
731
+
732
+ test("second test - session persists", async () => {
733
+ await page.goto("/settings");
734
+ // Still logged in from beforeAll
735
+ });
736
+ ```
737
+
738
+ ### Page Objects
739
+
740
+ Pre-built page object classes for common RHDH pages:
741
+
742
+ ```typescript
743
+ import {
744
+ CatalogPage,
745
+ HomePage,
746
+ CatalogImportPage,
747
+ ExtensionsPage,
748
+ NotificationPage,
749
+ } from "rhdh-e2e-test-utils/pages";
750
+ ```
751
+
752
+ #### CatalogPage
753
+
754
+ ```typescript
755
+ const catalogPage = new CatalogPage(page);
756
+
757
+ // Navigate to catalog
758
+ await catalogPage.go();
759
+
760
+ // Search for entities
761
+ await catalogPage.search("my-component");
762
+
763
+ // Navigate to specific component
764
+ await catalogPage.goToByName("my-component");
765
+ ```
766
+
767
+ #### HomePage
768
+
769
+ ```typescript
770
+ const homePage = new HomePage(page);
771
+
772
+ // Verify quick search functionality
773
+ await homePage.verifyQuickSearchBar("search-term");
774
+
775
+ // Verify quick access sections
776
+ await homePage.verifyQuickAccess("Favorites", "My Component");
777
+ ```
778
+
779
+ #### CatalogImportPage
780
+
781
+ ```typescript
782
+ const catalogImportPage = new CatalogImportPage(page);
783
+
784
+ // Register or refresh an existing component
785
+ const wasAlreadyRegistered = await catalogImportPage.registerExistingComponent(
786
+ "https://github.com/org/repo/blob/main/catalog-info.yaml"
787
+ );
788
+
789
+ // Analyze a component URL
790
+ await catalogImportPage.analyzeComponent("https://github.com/org/repo/blob/main/catalog-info.yaml");
791
+
792
+ // Inspect entity and verify YAML content
793
+ await catalogImportPage.inspectEntityAndVerifyYaml("kind: Component");
794
+ ```
795
+
796
+ #### ExtensionsPage
797
+
798
+ ```typescript
799
+ const extensionsPage = new ExtensionsPage(page);
800
+
801
+ // Filter by support type
802
+ await extensionsPage.selectSupportTypeFilter("Red Hat");
803
+
804
+ // Verify plugin details
805
+ await extensionsPage.verifyPluginDetails({
806
+ pluginName: "Topology",
807
+ badgeLabel: "Red Hat support",
808
+ badgeText: "Red Hat",
809
+ });
810
+
811
+ // Search and verify results
812
+ await extensionsPage.waitForSearchResults("catalog");
813
+ ```
814
+
815
+ #### NotificationPage
816
+
817
+ ```typescript
818
+ const notificationPage = new NotificationPage(page);
819
+
820
+ // Navigate to notifications
821
+ await notificationPage.clickNotificationsNavBarItem();
822
+
823
+ // Check notification content
824
+ await notificationPage.notificationContains("Build completed");
825
+
826
+ // Manage notifications
827
+ await notificationPage.markAllNotificationsAsRead();
828
+ await notificationPage.selectSeverity("critical");
829
+ await notificationPage.viewSaved();
830
+ await notificationPage.sortByNewestOnTop();
831
+ ```
832
+
390
833
  ### ESLint Configuration
391
834
 
392
835
  Pre-configured ESLint rules for Playwright tests:
@@ -414,23 +857,40 @@ Extend the base tsconfig:
414
857
 
415
858
  ### Default Configuration Structure
416
859
 
417
- The package includes default configurations that are merged with your project configs:
860
+ The package includes default configurations organized in a modular structure:
418
861
 
419
862
  ```
420
- src/deployment/rhdh/
421
- ├── config/
422
- │ ├── app-config-rhdh.yaml # Base app configuration
423
- │ ├── dynamic-plugins.yaml # Default dynamic plugins
424
- │ └── rhdh-secrets.yaml # Base secrets template
863
+ src/deployment/rhdh/config/
864
+ ├── common/ # Base configurations (always applied)
865
+ │ ├── app-config-rhdh.yaml # Base app configuration
866
+ │ ├── dynamic-plugins.yaml # Default dynamic plugins
867
+ │ └── rhdh-secrets.yaml # Base secrets template
868
+ ├── auth/ # Auth-specific configurations
869
+ │ ├── guest/
870
+ │ │ └── app-config.yaml # Guest auth configuration
871
+ │ └── keycloak/
872
+ │ ├── app-config.yaml # Keycloak OIDC configuration
873
+ │ ├── dynamic-plugins.yaml # Keycloak-specific plugins
874
+ │ └── secrets.yaml # Keycloak secrets template
425
875
  ├── helm/
426
- │ └── value_file.yaml # Default Helm values
876
+ │ └── value_file.yaml # Default Helm values
427
877
  └── operator/
428
- └── subscription.yaml # Default Backstage CR
878
+ └── subscription.yaml # Default Backstage CR
429
879
  ```
430
880
 
881
+ ### Configuration Merging
882
+
883
+ Configurations are merged in the following order (later overrides earlier):
884
+
885
+ 1. **Common configs** (`config/common/`) - Base configurations
886
+ 2. **Auth configs** (`config/auth/{provider}/`) - Auth-provider-specific configurations
887
+ 3. **Project configs** (`tests/config/`) - Your project's custom configurations
888
+
889
+ This allows you to use built-in defaults while only overriding what you need.
890
+
431
891
  ### Project Configuration
432
892
 
433
- Create these files in your project's `config/` directory:
893
+ Create these files in your project's `tests/config/` directory:
434
894
 
435
895
  #### app-config-rhdh.yaml
436
896
 
@@ -443,15 +903,12 @@ backend:
443
903
  allow:
444
904
  - host: ${MY_BACKEND_HOST}
445
905
 
446
- auth:
447
- environment: development
448
- providers:
449
- guest:
450
- dangerouslyAllowOutsideDevelopment: true
451
-
452
906
  # Plugin-specific config
453
907
  techRadar:
454
908
  url: "http://${DATA_SOURCE_URL}/tech-radar"
909
+
910
+ # Note: Auth configuration is automatically included based on the 'auth' option
911
+ # You only need to add auth config here if you want to override the defaults
455
912
  ```
456
913
 
457
914
  #### dynamic-plugins.yaml
@@ -508,6 +965,22 @@ stringData:
508
965
  |----------|-------------|
509
966
  | `CI` | If set, namespaces are auto-deleted after tests |
510
967
  | `CHART_URL` | Custom Helm chart URL (default: `oci://quay.io/rhdh/chart`) |
968
+ | `SKIP_KEYCLOAK_DEPLOYMENT` | Set to `true` to skip automatic Keycloak deployment in global setup |
969
+
970
+ ### Keycloak Environment Variables (for `auth: "keycloak"`)
971
+
972
+ When using Keycloak authentication, these environment variables are required:
973
+
974
+ | Variable | Description |
975
+ |----------|-------------|
976
+ | `KEYCLOAK_BASE_URL` | Keycloak instance URL |
977
+ | `KEYCLOAK_METADATA_URL` | OIDC metadata URL (e.g., `{KEYCLOAK_BASE_URL}/realms/{realm}/.well-known/openid-configuration`) |
978
+ | `KEYCLOAK_CLIENT_ID` | OIDC client ID |
979
+ | `KEYCLOAK_CLIENT_SECRET` | OIDC client secret |
980
+ | `KEYCLOAK_REALM` | Keycloak realm name |
981
+ | `KEYCLOAK_LOGIN_REALM` | Login realm (usually same as `KEYCLOAK_REALM`) |
982
+
983
+ These are automatically set when using `KeycloakHelper.configureForRHDH()`. See [Keycloak Deployment](#keycloak-deployment) for details.
511
984
 
512
985
 
513
986
  ## Examples
@@ -515,21 +988,70 @@ stringData:
515
988
  ### Custom Deployment Configuration
516
989
 
517
990
  ```typescript
518
- import { RHDHDeployment } from "rhdh-e2e-test-utils/rhdh";
991
+ import { test } from "rhdh-e2e-test-utils/test";
519
992
 
520
993
  test.beforeAll(async ({ rhdh }) => {
521
- rhdh.configure({
522
- namespace: "custom-test-ns",
994
+ await rhdh.configure({
523
995
  version: "1.5",
524
996
  method: "helm",
525
- appConfig: "custom/app-config.yaml",
526
- secrets: "custom/secrets.yaml",
527
- dynamicPlugins: "custom/plugins.yaml",
528
- valueFile: "custom/values.yaml",
997
+ auth: "keycloak", // or "guest" for development
998
+ appConfig: "tests/config/app-config.yaml",
999
+ secrets: "tests/config/secrets.yaml",
1000
+ dynamicPlugins: "tests/config/plugins.yaml",
1001
+ valueFile: "tests/config/values.yaml",
529
1002
  });
530
1003
 
531
1004
  await rhdh.deploy();
1005
+ });
1006
+ ```
1007
+
1008
+ ### Guest Authentication (Development)
1009
+
1010
+ ```typescript
1011
+ import { test } from "rhdh-e2e-test-utils/test";
1012
+
1013
+ test.beforeAll(async ({ rhdh }) => {
1014
+ await rhdh.configure({ auth: "guest" });
1015
+ await rhdh.deploy();
1016
+ });
532
1017
 
1018
+ test("test with guest login", async ({ page, loginHelper }) => {
1019
+ await page.goto("/");
1020
+ await loginHelper.loginAsGuest();
1021
+ // ... test assertions
1022
+ });
1023
+ ```
1024
+
1025
+ ### Using Helpers and Page Objects
1026
+
1027
+ ```typescript
1028
+ import { test, expect } from "rhdh-e2e-test-utils/test";
1029
+ import { CatalogPage } from "rhdh-e2e-test-utils/pages";
1030
+ import { APIHelper } from "rhdh-e2e-test-utils/helpers";
1031
+
1032
+ test.beforeAll(async ({ rhdh }) => {
1033
+ await rhdh.deploy();
1034
+ });
1035
+
1036
+ test("catalog interaction", async ({ page, uiHelper, loginHelper }) => {
1037
+ // Login
1038
+ await loginHelper.loginAsKeycloakUser();
1039
+
1040
+ // Use page object for catalog operations
1041
+ const catalogPage = new CatalogPage(page);
1042
+ await catalogPage.go();
1043
+ await catalogPage.search("my-component");
1044
+
1045
+ // Use UI helper for assertions
1046
+ await uiHelper.verifyRowsInTable(["my-component"]);
1047
+ });
1048
+
1049
+ test("API operations", async ({ rhdh }) => {
1050
+ // Create GitHub repo via API
1051
+ await APIHelper.createGitHubRepo("my-org", "test-repo");
1052
+
1053
+ // Clean up
1054
+ await APIHelper.deleteGitHubRepo("my-org", "test-repo");
533
1055
  });
534
1056
  ```
535
1057
 
@@ -562,6 +1084,80 @@ yarn build
562
1084
  | `yarn prettier:check` | Check code formatting |
563
1085
  | `yarn prettier:fix` | Auto-fix code formatting |
564
1086
 
1087
+ ### Testing Local Changes in Consumer Projects
1088
+
1089
+ When developing features or fixes in `rhdh-e2e-test-utils`, you can test your local changes in a consumer project (e.g., a plugin's e2e-tests) before publishing.
1090
+
1091
+ #### 1. Build your local changes
1092
+
1093
+ ```bash
1094
+ cd /path/to/rhdh-e2e-test-utils
1095
+ yarn build
1096
+ ```
1097
+
1098
+ #### 2. Update the consumer project's package.json
1099
+
1100
+ In your e2e-tests project, update the dependency to point to your local package using the `file:` protocol:
1101
+
1102
+ ```json
1103
+ "rhdh-e2e-test-utils": "file:/path/to/rhdh-e2e-test-utils"
1104
+ ```
1105
+
1106
+ Example:
1107
+ ```json
1108
+ "rhdh-e2e-test-utils": "file:/Users/yourname/Documents/rhdh/rhdh-e2e-test-utils"
1109
+ ```
1110
+
1111
+ #### 3. Install dependencies in the consumer project
1112
+
1113
+ ```bash
1114
+ yarn install
1115
+ ```
1116
+
1117
+ #### 4. Run tests with NODE_PRESERVE_SYMLINKS
1118
+
1119
+ When running tests with a local symlinked package, you **must** set the `NODE_PRESERVE_SYMLINKS` environment variable:
1120
+
1121
+ ```bash
1122
+ NODE_PRESERVE_SYMLINKS=1 yarn test
1123
+ NODE_PRESERVE_SYMLINKS=1 yarn test:headed
1124
+ NODE_PRESERVE_SYMLINKS=1 yarn test:ui
1125
+ ```
1126
+
1127
+ > **Why is NODE_PRESERVE_SYMLINKS needed?**
1128
+ >
1129
+ > When using local packages via `file:` protocol, the package manager creates a symlink. Node.js follows symlinks by default and tries to resolve peer dependencies (like `@playwright/test`) from the original package location. This causes duplicate Playwright instances which fails with:
1130
+ > ```
1131
+ > Error: Requiring @playwright/test second time
1132
+ > ```
1133
+ > Setting `NODE_PRESERVE_SYMLINKS=1` tells Node.js to resolve dependencies from the symlink location (your project's `node_modules`) instead of the original package location.
1134
+
1135
+ #### 5. Rebuild after making changes
1136
+
1137
+ When you make further changes to `rhdh-e2e-test-utils`, rebuild before running tests:
1138
+
1139
+ ```bash
1140
+ cd /path/to/rhdh-e2e-test-utils
1141
+ yarn build
1142
+ ```
1143
+
1144
+ Then run your tests again in the consumer project (no need to reinstall).
1145
+
1146
+ #### 6. Restore the published version
1147
+
1148
+ After testing, restore the published version in the consumer project's `package.json`:
1149
+
1150
+ ```json
1151
+ "rhdh-e2e-test-utils": "^1.0.0"
1152
+ ```
1153
+
1154
+ Then run:
1155
+ ```bash
1156
+ yarn install
1157
+ ```
1158
+
1159
+ You can now run tests normally without `NODE_PRESERVE_SYMLINKS`.
1160
+
565
1161
  ### CI/CD
566
1162
 
567
1163
  The project includes GitHub Actions workflows: