rhdh-e2e-test-utils 1.0.1 → 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 +577 -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/{dynamic-plugins.yaml → common/dynamic-plugins.yaml} +1 -0
  20. package/dist/deployment/rhdh/constants.d.ts +6 -0
  21. package/dist/deployment/rhdh/constants.d.ts.map +1 -1
  22. package/dist/deployment/rhdh/constants.js +17 -5
  23. package/dist/deployment/rhdh/deployment.d.ts +8 -1
  24. package/dist/deployment/rhdh/deployment.d.ts.map +1 -1
  25. package/dist/deployment/rhdh/deployment.js +47 -39
  26. package/dist/deployment/rhdh/index.d.ts +0 -1
  27. package/dist/deployment/rhdh/index.d.ts.map +1 -1
  28. package/dist/deployment/rhdh/types.d.ts +4 -1
  29. package/dist/deployment/rhdh/types.d.ts.map +1 -1
  30. package/dist/eslint/base.config.d.ts.map +1 -1
  31. package/dist/eslint/base.config.js +9 -2
  32. package/dist/playwright/base-config.d.ts +3 -3
  33. package/dist/playwright/base-config.d.ts.map +1 -1
  34. package/dist/playwright/base-config.js +5 -4
  35. package/dist/playwright/fixtures/test.d.ts +4 -1
  36. package/dist/playwright/fixtures/test.d.ts.map +1 -1
  37. package/dist/playwright/fixtures/test.js +16 -4
  38. package/dist/playwright/global-setup.d.ts.map +1 -1
  39. package/dist/playwright/global-setup.js +36 -1
  40. package/dist/playwright/helpers/accessibility.d.ts +13 -0
  41. package/dist/playwright/helpers/accessibility.d.ts.map +1 -0
  42. package/dist/playwright/helpers/accessibility.js +24 -0
  43. package/dist/playwright/helpers/api-endpoints.d.ts +11 -0
  44. package/dist/playwright/helpers/api-endpoints.d.ts.map +1 -0
  45. package/dist/playwright/helpers/api-endpoints.js +15 -0
  46. package/dist/playwright/helpers/api-helper.d.ts +77 -0
  47. package/dist/playwright/helpers/api-helper.d.ts.map +1 -0
  48. package/dist/playwright/helpers/api-helper.js +285 -0
  49. package/dist/playwright/helpers/common.d.ts +31 -0
  50. package/dist/playwright/helpers/common.d.ts.map +1 -0
  51. package/dist/playwright/helpers/common.js +342 -0
  52. package/dist/playwright/helpers/index.d.ts +5 -0
  53. package/dist/playwright/helpers/index.d.ts.map +1 -0
  54. package/dist/playwright/helpers/index.js +4 -0
  55. package/dist/playwright/helpers/navbar.d.ts +2 -0
  56. package/dist/playwright/helpers/navbar.d.ts.map +1 -0
  57. package/dist/playwright/helpers/navbar.js +1 -0
  58. package/dist/playwright/helpers/ui-helper.d.ts +106 -0
  59. package/dist/playwright/helpers/ui-helper.d.ts.map +1 -0
  60. package/dist/playwright/helpers/ui-helper.js +439 -0
  61. package/dist/playwright/page-objects/global-obj.d.ts +25 -0
  62. package/dist/playwright/page-objects/global-obj.d.ts.map +1 -0
  63. package/dist/playwright/page-objects/global-obj.js +24 -0
  64. package/dist/playwright/page-objects/page-obj.d.ts +41 -0
  65. package/dist/playwright/page-objects/page-obj.d.ts.map +1 -0
  66. package/dist/playwright/page-objects/page-obj.js +40 -0
  67. package/dist/playwright/pages/catalog-import.d.ts +31 -0
  68. package/dist/playwright/pages/catalog-import.d.ts.map +1 -0
  69. package/dist/playwright/pages/catalog-import.js +65 -0
  70. package/dist/playwright/pages/catalog.d.ts +14 -0
  71. package/dist/playwright/pages/catalog.d.ts.map +1 -0
  72. package/dist/playwright/pages/catalog.js +37 -0
  73. package/dist/playwright/pages/extensions.d.ts +38 -0
  74. package/dist/playwright/pages/extensions.d.ts.map +1 -0
  75. package/dist/playwright/pages/extensions.js +110 -0
  76. package/dist/playwright/pages/home-page.d.ts +10 -0
  77. package/dist/playwright/pages/home-page.d.ts.map +1 -0
  78. package/dist/playwright/pages/home-page.js +46 -0
  79. package/dist/playwright/pages/index.d.ts +6 -0
  80. package/dist/playwright/pages/index.d.ts.map +1 -0
  81. package/dist/playwright/pages/index.js +5 -0
  82. package/dist/playwright/pages/notifications.d.ts +24 -0
  83. package/dist/playwright/pages/notifications.d.ts.map +1 -0
  84. package/dist/playwright/pages/notifications.js +112 -0
  85. package/dist/utils/kubernetes-client.d.ts +9 -0
  86. package/dist/utils/kubernetes-client.d.ts.map +1 -1
  87. package/dist/utils/kubernetes-client.js +57 -2
  88. package/dist/utils/merge-yamls.d.ts +25 -4
  89. package/dist/utils/merge-yamls.d.ts.map +1 -1
  90. package/dist/utils/merge-yamls.js +52 -12
  91. package/package.json +18 -2
  92. /package/dist/deployment/rhdh/config/{app-config-rhdh.yaml → common/app-config-rhdh.yaml} +0 -0
  93. /package/dist/deployment/rhdh/config/{rhdh-secrets.yaml → common/rhdh-secrets.yaml} +0 -0
  94. /package/dist/deployment/rhdh/{helm → config/helm}/value_file.yaml +0 -0
  95. /package/dist/deployment/rhdh/{operator → config/operator}/subscription.yaml +0 -0
package/README.md CHANGED
@@ -13,8 +13,12 @@ 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)
@@ -28,6 +32,8 @@ A comprehensive test utility package for Red Hat Developer Hub (RHDH) end-to-end
28
32
  `rhdh-e2e-test-utils` simplifies end-to-end testing for RHDH plugins by providing:
29
33
 
30
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
31
37
  - **Playwright Integration**: Custom test fixtures that manage deployment lifecycle
32
38
  - **Kubernetes Utilities**: Helper functions for managing namespaces, ConfigMaps, Secrets, and Routes
33
39
  - **Configuration Merging**: YAML merging with environment variable substitution
@@ -36,6 +42,8 @@ A comprehensive test utility package for Red Hat Developer Hub (RHDH) end-to-end
36
42
  ## Features
37
43
 
38
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)
39
47
  - Automatic namespace creation and cleanup
40
48
  - Dynamic plugin configuration
41
49
  - Helpers for UI, API and common Utils
@@ -80,7 +88,10 @@ The package provides multiple entry points for different use cases:
80
88
  | `rhdh-e2e-test-utils/test` | Playwright test fixtures with RHDH deployment |
81
89
  | `rhdh-e2e-test-utils/playwright-config` | Base Playwright configuration |
82
90
  | `rhdh-e2e-test-utils/rhdh` | RHDH deployment class and types |
91
+ | `rhdh-e2e-test-utils/keycloak` | Keycloak deployment helper for authentication testing |
83
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 |
84
95
  | `rhdh-e2e-test-utils/eslint` | ESLint configuration factory |
85
96
  | `rhdh-e2e-test-utils/tsconfig` | Base TypeScript configuration |
86
97
 
@@ -98,18 +109,15 @@ yarn add @playwright/test rhdh-e2e-test-utils
98
109
 
99
110
  ```typescript
100
111
  // playwright.config.ts
101
- import { defineConfig } from "@playwright/test";
102
- import { createPlaywrightConfig } from "rhdh-e2e-test-utils/playwright-config";
103
-
104
- export default defineConfig(
105
- createPlaywrightConfig({
106
- projects: [
107
- {
108
- name: "my-plugin",
109
- },
110
- ],
111
- })
112
- );
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
+ });
113
121
  ```
114
122
 
115
123
  ### 3. Create Your Test
@@ -130,10 +138,10 @@ test("my plugin test", async ({ page }) => {
130
138
 
131
139
  ### 4. Create Configuration Files
132
140
 
133
- Create a `config/` directory with your RHDH configuration:
141
+ Create a `tests/config/` directory with your RHDH configuration:
134
142
 
135
143
  ```
136
- config/
144
+ tests/config/
137
145
  ├── app-config-rhdh.yaml # App configuration
138
146
  ├── dynamic-plugins.yaml # Dynamic plugins configuration
139
147
  └── rhdh-secrets.yaml # Secrets (with env var placeholders)
@@ -167,6 +175,8 @@ import { test, expect } from "rhdh-e2e-test-utils/test";
167
175
  | Fixture | Scope | Description |
168
176
  |---------|-------|-------------|
169
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 |
170
180
  | `baseURL` | test | Automatically set to the RHDH instance URL |
171
181
 
172
182
  #### Fixture Behavior
@@ -177,6 +187,7 @@ import { test, expect } from "rhdh-e2e-test-utils/test";
177
187
 
178
188
  ```typescript
179
189
  import { test, expect } from "rhdh-e2e-test-utils/test";
190
+
180
191
  test.beforeAll(async ({ rhdh }) => {
181
192
  // Configure RHDH (creates namespace, and optional DeploymentOptions)
182
193
  await rhdh.configure();
@@ -188,10 +199,17 @@ test.beforeAll(async ({ rhdh }) => {
188
199
  await rhdh.deploy();
189
200
  });
190
201
 
191
- test("example test", async ({ page, rhdh }) => {
202
+ test("example test", async ({ page, rhdh, uiHelper, loginHelper }) => {
192
203
  // page.goto("/") will use rhdh.rhdhUrl as base
193
204
  await page.goto("/");
194
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
+
195
213
  // Access deployment info
196
214
  console.log(`Namespace: ${rhdh.deploymentConfig.namespace}`);
197
215
  console.log(`URL: ${rhdh.rhdhUrl}`);
@@ -205,24 +223,22 @@ test("example test", async ({ page, rhdh }) => {
205
223
 
206
224
  ### Playwright Configuration
207
225
 
208
- Use `createPlaywrightConfig` for sensible defaults:
226
+ Use `defineConfig` for sensible defaults:
209
227
 
210
228
  ```typescript
211
- import { defineConfig } from "@playwright/test";
212
- import { createPlaywrightConfig } from "rhdh-e2e-test-utils/playwright-config";
213
-
214
- export default defineConfig(
215
- createPlaywrightConfig({
216
- projects: [
217
- {
218
- name: "tech-radar", // Also used as namespace
219
- },
220
- {
221
- name: "catalog",
222
- },
223
- ],
224
- })
225
- );
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
+ });
226
242
  ```
227
243
 
228
244
  #### Base Configuration Defaults
@@ -238,6 +254,41 @@ export default defineConfig(
238
254
  | `trace` | Retain on failure |
239
255
  | `screenshot` | Only on failure |
240
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
+
241
292
  ### RHDH Deployment
242
293
 
243
294
  #### RHDHDeployment Class
@@ -247,25 +298,33 @@ The core class for managing RHDH deployments:
247
298
  ```typescript
248
299
  import { RHDHDeployment } from "rhdh-e2e-test-utils/rhdh";
249
300
 
250
- const deployment = new RHDHDeployment({
251
- 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({
252
306
  version: "1.5", // Optional, uses RHDH_VERSION env
253
307
  method: "helm", // Optional, uses INSTALLATION_METHOD env
308
+ auth: "keycloak", // Optional, defaults to "keycloak"
254
309
  appConfig: "config/app-config-rhdh.yaml", // Optional
255
310
  secrets: "config/rhdh-secrets.yaml", // Optional
256
311
  dynamicPlugins: "config/dynamic-plugins.yaml", // Optional
257
312
  valueFile: "config/value_file.yaml", // Optional & Helm only
258
313
  subscription: "config/subscription.yaml", // Optional & Operator only
259
314
  });
315
+
316
+ // Deploy RHDH
317
+ await deployment.deploy();
260
318
  ```
261
319
 
262
320
  #### Deployment Options
263
321
 
264
322
  ```typescript
265
323
  type DeploymentOptions = {
266
- namespace: string; // Required: Kubernetes namespace
324
+ namespace?: string; // Kubernetes namespace (set via constructor)
267
325
  version?: string; // RHDH version (e.g., "1.5", "1.5.1-CI")
268
326
  method?: "helm" | "operator"; // Installation method
327
+ auth?: "guest" | "keycloak"; // Authentication provider (default: "keycloak")
269
328
  appConfig?: string; // Path to app-config YAML
270
329
  secrets?: string; // Path to secrets YAML
271
330
  dynamicPlugins?: string; // Path to dynamic-plugins YAML
@@ -274,6 +333,17 @@ type DeploymentOptions = {
274
333
  };
275
334
  ```
276
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
+
277
347
  #### Deployment Methods
278
348
 
279
349
  ##### Helm Deployment
@@ -318,6 +388,162 @@ await rhdh.deploy();
318
388
  | `deploymentConfig` | `DeploymentConfig` | Current deployment configuration |
319
389
  | `k8sClient` | `KubernetesClientHelper` | Kubernetes client instance |
320
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
+
321
547
  ### Utilities
322
548
 
323
549
  #### Bash Command Execution
@@ -388,6 +614,222 @@ const result = envsubst("Port: ${PORT:-8080}");
388
614
  const result = envsubst("API: ${API_URL}");
389
615
  ```
390
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
+
391
833
  ### ESLint Configuration
392
834
 
393
835
  Pre-configured ESLint rules for Playwright tests:
@@ -415,23 +857,40 @@ Extend the base tsconfig:
415
857
 
416
858
  ### Default Configuration Structure
417
859
 
418
- The package includes default configurations that are merged with your project configs:
860
+ The package includes default configurations organized in a modular structure:
419
861
 
420
862
  ```
421
- src/deployment/rhdh/
422
- ├── config/
423
- │ ├── app-config-rhdh.yaml # Base app configuration
424
- │ ├── dynamic-plugins.yaml # Default dynamic plugins
425
- │ └── 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
426
875
  ├── helm/
427
- │ └── value_file.yaml # Default Helm values
876
+ │ └── value_file.yaml # Default Helm values
428
877
  └── operator/
429
- └── subscription.yaml # Default Backstage CR
878
+ └── subscription.yaml # Default Backstage CR
430
879
  ```
431
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
+
432
891
  ### Project Configuration
433
892
 
434
- Create these files in your project's `config/` directory:
893
+ Create these files in your project's `tests/config/` directory:
435
894
 
436
895
  #### app-config-rhdh.yaml
437
896
 
@@ -444,15 +903,12 @@ backend:
444
903
  allow:
445
904
  - host: ${MY_BACKEND_HOST}
446
905
 
447
- auth:
448
- environment: development
449
- providers:
450
- guest:
451
- dangerouslyAllowOutsideDevelopment: true
452
-
453
906
  # Plugin-specific config
454
907
  techRadar:
455
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
456
912
  ```
457
913
 
458
914
  #### dynamic-plugins.yaml
@@ -509,6 +965,22 @@ stringData:
509
965
  |----------|-------------|
510
966
  | `CI` | If set, namespaces are auto-deleted after tests |
511
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.
512
984
 
513
985
 
514
986
  ## Examples
@@ -516,21 +988,70 @@ stringData:
516
988
  ### Custom Deployment Configuration
517
989
 
518
990
  ```typescript
519
- import { RHDHDeployment } from "rhdh-e2e-test-utils/rhdh";
991
+ import { test } from "rhdh-e2e-test-utils/test";
520
992
 
521
993
  test.beforeAll(async ({ rhdh }) => {
522
- rhdh.configure({
523
- namespace: "custom-test-ns",
994
+ await rhdh.configure({
524
995
  version: "1.5",
525
996
  method: "helm",
526
- appConfig: "custom/app-config.yaml",
527
- secrets: "custom/secrets.yaml",
528
- dynamicPlugins: "custom/plugins.yaml",
529
- 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",
530
1002
  });
531
1003
 
532
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
+ });
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");
533
1052
 
1053
+ // Clean up
1054
+ await APIHelper.deleteGitHubRepo("my-org", "test-repo");
534
1055
  });
535
1056
  ```
536
1057