Skip to content

Commit 9561ad6

Browse files
docs update (#49)
1 parent 090747c commit 9561ad6

File tree

3 files changed

+99
-79
lines changed

3 files changed

+99
-79
lines changed

docs/guide/core-concepts/playwright-fixtures.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,15 @@ test.describe("Feature B", () => {
251251

252252
### Key: Unique Identifier
253253

254-
The `key` parameter must be unique across all `runOnce` calls in your test run. Use a descriptive name that reflects the operation:
254+
The `key` must be globally unique across **all spec files and projects** in the same Playwright run. If two `runOnce` calls in different files use the same key, only the first one will execute. Use a prefix that includes the workspace or project name:
255255

256256
```typescript
257+
// In tech-radar.spec.ts
257258
await test.runOnce("tech-radar-deploy", async () => { ... });
258259
await test.runOnce("tech-radar-data-provider", async () => { ... });
260+
261+
// In catalog.spec.ts
262+
await test.runOnce("catalog-deploy", async () => { ... });
259263
await test.runOnce("catalog-seed-data", async () => { ... });
260264
```
261265

docs/overlay/examples/tech-radar.md

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -222,28 +222,30 @@ const setupScript = path.join(
222222
);
223223

224224
test.describe("Test tech-radar plugin", () => {
225-
// beforeAll runs once per worker before any tests
225+
// Wrap in runOnce — the external service deployment is expensive
226+
// and should not re-run when Playwright restarts the worker after a test failure
226227
test.beforeAll(async ({ rhdh }) => {
227-
// Get the namespace from deployment config
228-
const project = rhdh.deploymentConfig.namespace;
229-
230-
// Configure RHDH with Keycloak authentication
231-
await rhdh.configure({ auth: "keycloak" });
232-
233-
// Deploy the external data provider service
234-
await $`bash ${setupScript} ${project}`;
235-
236-
// Get the route URL and set as environment variable
237-
// Remove http:// prefix as the config expects just the host
238-
process.env.TECH_RADAR_DATA_URL = (
239-
await rhdh.k8sClient.getRouteLocation(
240-
project,
241-
"test-backstage-customization-provider",
242-
)
243-
).replace("http://", "");
244-
245-
// Now deploy RHDH (will use the TECH_RADAR_DATA_URL env var)
246-
await rhdh.deploy();
228+
await test.runOnce("tech-radar-setup", async () => {
229+
const project = rhdh.deploymentConfig.namespace;
230+
231+
// Configure RHDH with Keycloak authentication
232+
await rhdh.configure({ auth: "keycloak" });
233+
234+
// Deploy the external data provider service
235+
await $`bash ${setupScript} ${project}`;
236+
237+
// Get the route URL and set as environment variable
238+
// Remove http:// prefix as the config expects just the host
239+
process.env.TECH_RADAR_DATA_URL = (
240+
await rhdh.k8sClient.getRouteLocation(
241+
project,
242+
"test-backstage-customization-provider",
243+
)
244+
).replace("http://", "");
245+
246+
// Deploy RHDH (will use the TECH_RADAR_DATA_URL env var)
247+
await rhdh.deploy();
248+
});
247249
});
248250

249251
// beforeEach runs before each test

docs/overlay/tutorials/custom-deployment.md

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,32 @@ Your plugin tests may require pre-requisites when:
1818

1919
## The Pattern
2020

21-
Deploy pre-requisites **after** `rhdh.configure()` but **before** `rhdh.deploy()`:
21+
Deploy pre-requisites **after** `rhdh.configure()` but **before** `rhdh.deploy()`. Since these operations are expensive and create persistent cluster resources, wrap the entire block in `test.runOnce` to prevent re-execution on worker restarts:
2222

2323
```typescript
2424
test.beforeAll(async ({ rhdh }) => {
25-
const project = rhdh.deploymentConfig.namespace;
25+
await test.runOnce("my-plugin-setup", async () => {
26+
const project = rhdh.deploymentConfig.namespace;
2627

27-
// 1. Configure RHDH first
28-
await rhdh.configure({ auth: "keycloak" });
28+
// 1. Configure RHDH first
29+
await rhdh.configure({ auth: "keycloak" });
2930

30-
// 2. Deploy pre-requisite service (see examples below)
31-
// ...
31+
// 2. Deploy pre-requisite service (see examples below)
32+
// ...
3233

33-
// 3. Set environment variable if needed for RHDH config
34-
process.env.MY_SERVICE_URL = "...";
34+
// 3. Set environment variable if needed for RHDH config
35+
process.env.MY_SERVICE_URL = "...";
3536

36-
// 4. Deploy RHDH (uses the environment variable)
37-
await rhdh.deploy();
37+
// 4. Deploy RHDH (uses the environment variable)
38+
await rhdh.deploy();
39+
});
3840
});
3941
```
4042

43+
::: tip When is `test.runOnce` needed?
44+
`rhdh.deploy()` already skips automatically on worker restarts. But the pre-deploy steps (deploying external services, running scripts) don't have this protection. `test.runOnce` ensures the **entire setup** runs only once. The `key` must be **globally unique** across all spec files and projects in the same Playwright run — prefix it with your workspace name (e.g., `"tech-radar-setup"`). See [`test.runOnce`](/guide/core-concepts/playwright-fixtures#test-runonce-—-run-any-expensive-operation-once) for details.
45+
:::
46+
4147
## Examples
4248

4349
### Using TypeScript / k8sClient
@@ -48,26 +54,28 @@ You can deploy pre-requisites directly in TypeScript using the Kubernetes client
4854
import { test } from "@red-hat-developer-hub/e2e-test-utils/test";
4955

5056
test.beforeAll(async ({ rhdh }) => {
51-
const project = rhdh.deploymentConfig.namespace;
52-
const k8s = rhdh.k8sClient;
53-
54-
await rhdh.configure({ auth: "keycloak" });
55-
56-
// Create a ConfigMap
57-
await k8s.applyConfigMapFromObject(
58-
"my-config",
59-
{ "config.json": JSON.stringify({ key: "value" }) },
60-
project,
61-
);
62-
63-
// Create a Secret
64-
await k8s.applySecretFromObject(
65-
"my-secret",
66-
{ stringData: { API_KEY: process.env.VAULT_API_KEY } },
67-
project,
68-
);
69-
70-
await rhdh.deploy();
57+
await test.runOnce("my-plugin-k8s-setup", async () => {
58+
const project = rhdh.deploymentConfig.namespace;
59+
const k8s = rhdh.k8sClient;
60+
61+
await rhdh.configure({ auth: "keycloak" });
62+
63+
// Create a ConfigMap
64+
await k8s.applyConfigMapFromObject(
65+
"my-config",
66+
{ "config.json": JSON.stringify({ key: "value" }) },
67+
project,
68+
);
69+
70+
// Create a Secret
71+
await k8s.applySecretFromObject(
72+
"my-secret",
73+
{ stringData: { API_KEY: process.env.VAULT_API_KEY } },
74+
project,
75+
);
76+
77+
await rhdh.deploy();
78+
});
7179
});
7280
```
7381

@@ -80,21 +88,23 @@ import { test } from "@red-hat-developer-hub/e2e-test-utils/test";
8088
import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils";
8189

8290
test.beforeAll(async ({ rhdh }) => {
83-
const project = rhdh.deploymentConfig.namespace;
91+
await test.runOnce("my-plugin-oc-setup", async () => {
92+
const project = rhdh.deploymentConfig.namespace;
8493

85-
await rhdh.configure({ auth: "keycloak" });
94+
await rhdh.configure({ auth: "keycloak" });
8695

87-
// Deploy an app from image
88-
await $`oc new-app my-image:tag --name=my-service --namespace=${project}`;
89-
await $`oc expose svc/my-service --namespace=${project}`;
96+
// Deploy an app from image
97+
await $`oc new-app my-image:tag --name=my-service --namespace=${project}`;
98+
await $`oc expose svc/my-service --namespace=${project}`;
9099

91-
// Get service URL
92-
process.env.MY_SERVICE_URL = await rhdh.k8sClient.getRouteLocation(
93-
project,
94-
"my-service",
95-
);
100+
// Get service URL
101+
process.env.MY_SERVICE_URL = await rhdh.k8sClient.getRouteLocation(
102+
project,
103+
"my-service",
104+
);
96105

97-
await rhdh.deploy();
106+
await rhdh.deploy();
107+
});
98108
});
99109
```
100110

@@ -109,11 +119,13 @@ import path from "path";
109119
const setupScript = path.join(import.meta.dirname, "deploy-service.sh");
110120

111121
test.beforeAll(async ({ rhdh }) => {
112-
const project = rhdh.deploymentConfig.namespace;
122+
await test.runOnce("my-plugin-script-setup", async () => {
123+
const project = rhdh.deploymentConfig.namespace;
113124

114-
await rhdh.configure({ auth: "keycloak" });
115-
await $`bash ${setupScript} ${project}`;
116-
await rhdh.deploy();
125+
await rhdh.configure({ auth: "keycloak" });
126+
await $`bash ${setupScript} ${project}`;
127+
await rhdh.deploy();
128+
});
117129
});
118130
```
119131

@@ -123,22 +135,24 @@ The tech-radar plugin requires an external data provider:
123135

124136
```typescript
125137
test.beforeAll(async ({ rhdh }) => {
126-
const project = rhdh.deploymentConfig.namespace;
138+
await test.runOnce("tech-radar-setup", async () => {
139+
const project = rhdh.deploymentConfig.namespace;
127140

128-
await rhdh.configure({ auth: "keycloak" });
141+
await rhdh.configure({ auth: "keycloak" });
129142

130-
// Deploy the data provider service
131-
await $`bash ${setupScript} ${project}`;
143+
// Deploy the data provider service
144+
await $`bash ${setupScript} ${project}`;
132145

133-
// Get URL and set as env var for rhdh-secrets.yaml substitution
134-
process.env.TECH_RADAR_DATA_URL = (
135-
await rhdh.k8sClient.getRouteLocation(
136-
project,
137-
"test-backstage-customization-provider",
138-
)
139-
).replace("http://", "");
146+
// Get URL and set as env var for rhdh-secrets.yaml substitution
147+
process.env.TECH_RADAR_DATA_URL = (
148+
await rhdh.k8sClient.getRouteLocation(
149+
project,
150+
"test-backstage-customization-provider",
151+
)
152+
).replace("http://", "");
140153

141-
await rhdh.deploy();
154+
await rhdh.deploy();
155+
});
142156
});
143157
```
144158

0 commit comments

Comments
 (0)