@red-hat-developer-hub/e2e-test-utils 1.1.12 → 1.1.14
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.
- package/dist/deployment/rhdh/config/auth/github/app-config.yaml +17 -0
- package/dist/deployment/rhdh/config/auth/github/secrets.yaml +12 -0
- package/dist/deployment/rhdh/constants.d.ts +2 -0
- package/dist/deployment/rhdh/constants.d.ts.map +1 -1
- package/dist/deployment/rhdh/constants.js +6 -0
- package/dist/deployment/rhdh/deployment.d.ts.map +1 -1
- package/dist/deployment/rhdh/deployment.js +21 -18
- package/dist/deployment/rhdh/types.d.ts +1 -1
- package/dist/deployment/rhdh/types.d.ts.map +1 -1
- package/dist/playwright/base-config.d.ts.map +1 -1
- package/dist/playwright/base-config.js +1 -0
- package/dist/playwright/fixtures/test.d.ts +4 -1
- package/dist/playwright/fixtures/test.d.ts.map +1 -1
- package/dist/playwright/fixtures/test.js +8 -13
- package/dist/playwright/helpers/api-helper.js +1 -1
- package/dist/playwright/helpers/common.d.ts.map +1 -1
- package/dist/playwright/helpers/common.js +7 -7
- package/dist/playwright/run-once.d.ts +11 -0
- package/dist/playwright/run-once.d.ts.map +1 -0
- package/dist/playwright/run-once.js +40 -0
- package/dist/playwright/teardown-namespaces.d.ts +11 -0
- package/dist/playwright/teardown-namespaces.d.ts.map +1 -0
- package/dist/playwright/teardown-namespaces.js +34 -0
- package/dist/playwright/teardown-reporter.d.ts +15 -0
- package/dist/playwright/teardown-reporter.d.ts.map +1 -0
- package/dist/playwright/teardown-reporter.js +40 -0
- package/package.json +8 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
auth:
|
|
2
|
+
environment: production
|
|
3
|
+
session:
|
|
4
|
+
secret: superSecretSecret
|
|
5
|
+
providers:
|
|
6
|
+
github:
|
|
7
|
+
production:
|
|
8
|
+
clientSecret: ${GITHUB_OAUTH_APP_SECRET}
|
|
9
|
+
clientId: ${GITHUB_OAUTH_APP_ID}
|
|
10
|
+
callbackUrl: ${RHDH_BASE_URL}/api/auth/github/handler/frame
|
|
11
|
+
signInPage: github
|
|
12
|
+
catalog:
|
|
13
|
+
locations:
|
|
14
|
+
- type: url
|
|
15
|
+
target: https://github.com/janus-qe/test-user-entity/blob/main/user.yaml
|
|
16
|
+
rules:
|
|
17
|
+
- allow: [User]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
apiVersion: v1
|
|
2
|
+
kind: Secret
|
|
3
|
+
metadata:
|
|
4
|
+
name: rhdh-secrets
|
|
5
|
+
type: Opaque
|
|
6
|
+
stringData:
|
|
7
|
+
GITHUB_OAUTH_APP_ID: $VAULT_GITHUB_OAUTH_OVERLAYS_APP_ID
|
|
8
|
+
GITHUB_OAUTH_APP_SECRET: $VAULT_GITHUB_OAUTH_OVERLAYS_APP_SECRET
|
|
9
|
+
GH_USER_ID: $VAULT_GH_USER_ID
|
|
10
|
+
GH_USER_PASS: $VAULT_GH_USER_PASS
|
|
11
|
+
GH_2FA_SECRET: $VAULT_GH_2FA_SECRET
|
|
12
|
+
GH_RHDH_QE_USER_TOKEN: $VAULT_GITHUB_USER_TOKEN
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AuthProvider } from "./types.js";
|
|
2
|
+
import { MergeOptions } from "../../utils/merge-yamls.js";
|
|
2
3
|
export declare const DEFAULT_CONFIG_PATHS: {
|
|
3
4
|
appConfig: string;
|
|
4
5
|
secrets: string;
|
|
@@ -14,6 +15,7 @@ export declare const AUTH_CONFIG_PATHS: Record<AuthProvider, {
|
|
|
14
15
|
appConfig: string;
|
|
15
16
|
secrets: string;
|
|
16
17
|
dynamicPlugins: string;
|
|
18
|
+
mergeStrategy?: MergeOptions;
|
|
17
19
|
}>;
|
|
18
20
|
export declare const CHART_URL = "oci://quay.io/rhdh/chart";
|
|
19
21
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/constants.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/constants.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAK1D,eAAO,MAAM,oBAAoB;;;;;;;;;;CAyBhC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CACpC,YAAY,EACZ;IACE,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B,CAoCF,CAAC;AAEF,eAAO,MAAM,SAAS,6BAA6B,CAAC"}
|
|
@@ -23,5 +23,11 @@ export const AUTH_CONFIG_PATHS = {
|
|
|
23
23
|
secrets: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/auth/keycloak/secrets.yaml"),
|
|
24
24
|
dynamicPlugins: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/auth/keycloak/dynamic-plugins.yaml"),
|
|
25
25
|
},
|
|
26
|
+
github: {
|
|
27
|
+
appConfig: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/auth/github/app-config.yaml"),
|
|
28
|
+
secrets: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/auth/github/secrets.yaml"),
|
|
29
|
+
dynamicPlugins: "",
|
|
30
|
+
mergeStrategy: { arrayMergeStrategy: { byKey: "target" } },
|
|
31
|
+
},
|
|
26
32
|
};
|
|
27
33
|
export const CHART_URL = "oci://quay.io/rhdh/chart";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAkB1E,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IAClB,SAAS,yBAAgC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,gBAAgB,CAAC;gBAE9B,SAAS,EAAE,MAAM;IAKvB,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAuCpD,eAAe;YAmBf,aAAa;IAqB3B;;;OAGG;YACW,0BAA0B;YAuC1B,oBAAoB;YAWpB,eAAe;YAmCf,mBAAmB;IAiD3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAWrC;;;;OAIG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC,cAAc,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAIjB,oBAAoB;IAsClC;;;OAGG;YACW,0BAA0B;IAwCxC,OAAO,CAAC,sBAAsB;IAoCxB,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;CAMlB"}
|
|
@@ -5,6 +5,7 @@ import { test } from "@playwright/test";
|
|
|
5
5
|
import { mergeYamlFilesIfExists, deepMerge } from "../../utils/merge-yamls.js";
|
|
6
6
|
import { loadAndInjectPluginMetadata, generateDynamicPluginsConfigFromMetadata, } from "../../utils/plugin-metadata.js";
|
|
7
7
|
import { envsubst } from "../../utils/common.js";
|
|
8
|
+
import { runOnce } from "../../playwright/run-once.js";
|
|
8
9
|
import cloneDeepWith from "lodash.clonedeepwith";
|
|
9
10
|
import fs from "fs-extra";
|
|
10
11
|
import { DEFAULT_CONFIG_PATHS, AUTH_CONFIG_PATHS, CHART_URL, } from "./constants.js";
|
|
@@ -15,29 +16,33 @@ export class RHDHDeployment {
|
|
|
15
16
|
constructor(namespace) {
|
|
16
17
|
this.deploymentConfig = this._buildDeploymentConfig({ namespace });
|
|
17
18
|
this.rhdhUrl = this._buildBaseUrl();
|
|
18
|
-
this._log(`RHDH deployment initialized (namespace: ${this.deploymentConfig.namespace})`);
|
|
19
|
-
this._log("RHDH Base URL: " + this.rhdhUrl);
|
|
20
|
-
console.table(this.deploymentConfig);
|
|
21
19
|
}
|
|
22
20
|
async deploy(options) {
|
|
23
|
-
this._log("Starting RHDH deployment...");
|
|
24
21
|
// Default 600s, custom number to override, null to skip and let consumer control the timeout
|
|
25
22
|
const timeout = options?.timeout === undefined ? 600_000 : options.timeout;
|
|
26
23
|
if (timeout !== null) {
|
|
27
24
|
test.setTimeout(timeout);
|
|
28
25
|
}
|
|
29
|
-
await
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
await this.
|
|
34
|
-
await this.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
const executed = await runOnce(`deploy-${this.deploymentConfig.namespace}`, async () => {
|
|
27
|
+
this._log("Starting RHDH deployment...");
|
|
28
|
+
this._log("RHDH Base URL: " + this.rhdhUrl);
|
|
29
|
+
console.table(this.deploymentConfig);
|
|
30
|
+
await this.k8sClient.createNamespaceIfNotExists(this.deploymentConfig.namespace);
|
|
31
|
+
await this._applyAppConfig();
|
|
32
|
+
await this._applySecrets();
|
|
33
|
+
if (this.deploymentConfig.method === "helm") {
|
|
34
|
+
await this._deployWithHelm(this.deploymentConfig.valueFile);
|
|
35
|
+
await this.scaleDownAndRestart(); // Restart as helm does not monitor config changes
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
await this._applyDynamicPlugins();
|
|
39
|
+
await this._deployWithOperator(this.deploymentConfig.subscription);
|
|
40
|
+
}
|
|
41
|
+
await this.waitUntilReady();
|
|
42
|
+
});
|
|
43
|
+
if (!executed) {
|
|
44
|
+
this._log(`Deployment already completed for namespace "${this.deploymentConfig.namespace}", skipping`);
|
|
39
45
|
}
|
|
40
|
-
await this.waitUntilReady();
|
|
41
46
|
}
|
|
42
47
|
async _applyAppConfig() {
|
|
43
48
|
const authConfig = AUTH_CONFIG_PATHS[this.deploymentConfig.auth];
|
|
@@ -45,7 +50,7 @@ export class RHDHDeployment {
|
|
|
45
50
|
DEFAULT_CONFIG_PATHS.appConfig,
|
|
46
51
|
authConfig.appConfig,
|
|
47
52
|
this.deploymentConfig.appConfig,
|
|
48
|
-
]);
|
|
53
|
+
], authConfig.mergeStrategy);
|
|
49
54
|
this._logBoxen("App Config", appConfigYaml);
|
|
50
55
|
await this.k8sClient.applyConfigMapFromObject("app-config-rhdh", appConfigYaml, this.deploymentConfig.namespace);
|
|
51
56
|
}
|
|
@@ -282,8 +287,6 @@ export class RHDHDeployment {
|
|
|
282
287
|
if (deploymentOptions) {
|
|
283
288
|
this.deploymentConfig = this._buildDeploymentConfig(deploymentOptions);
|
|
284
289
|
this.rhdhUrl = this._buildBaseUrl();
|
|
285
|
-
this._log(`RHDH deployment initialized (namespace: ${this.deploymentConfig.namespace})`);
|
|
286
|
-
console.table(this.deploymentConfig);
|
|
287
290
|
}
|
|
288
291
|
await this.k8sClient.createNamespaceIfNotExists(this.deploymentConfig.namespace);
|
|
289
292
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,CAAC;AACnD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,CAAC;AACnD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GACjD,CAAC,oBAAoB,GAAG,wBAAwB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-config.d.ts","sourceRoot":"","sources":["../../src/playwright/base-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAG1B;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"base-config.d.ts","sourceRoot":"","sources":["../../src/playwright/base-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAG1B;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,oBA6BxB,CAAC;AAEF;;;;;GAKG;AAEH,wBAAgB,YAAY,CAC1B,SAAS,GAAE,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAM,GACrD,oBAAoB,CAKtB"}
|
|
@@ -15,6 +15,7 @@ export const baseConfig = {
|
|
|
15
15
|
["list"],
|
|
16
16
|
["html", { outputFolder: "playwright-report", open: "on-failure" }],
|
|
17
17
|
["json", { outputFile: "playwright-report/results.json" }],
|
|
18
|
+
[resolve(import.meta.dirname, "../playwright/teardown-reporter.js")],
|
|
18
19
|
],
|
|
19
20
|
use: {
|
|
20
21
|
ignoreHTTPSErrors: true,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RHDHDeployment } from "../../deployment/rhdh/index.js";
|
|
2
2
|
import { LoginHelper, UIhelper } from "../helpers/index.js";
|
|
3
|
+
import { runOnce } from "../run-once.js";
|
|
3
4
|
type RHDHDeploymentTestFixtures = {
|
|
4
5
|
rhdh: RHDHDeployment;
|
|
5
6
|
uiHelper: UIhelper;
|
|
@@ -8,6 +9,8 @@ type RHDHDeploymentTestFixtures = {
|
|
|
8
9
|
type RHDHDeploymentWorkerFixtures = {
|
|
9
10
|
rhdhDeploymentWorker: RHDHDeployment;
|
|
10
11
|
};
|
|
12
|
+
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & RHDHDeploymentTestFixtures, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions & RHDHDeploymentWorkerFixtures> & {
|
|
13
|
+
runOnce: typeof runOnce;
|
|
14
|
+
};
|
|
11
15
|
export * from "@playwright/test";
|
|
12
|
-
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & RHDHDeploymentTestFixtures, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions & RHDHDeploymentWorkerFixtures>;
|
|
13
16
|
//# sourceMappingURL=test.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../src/playwright/fixtures/test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../src/playwright/fixtures/test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,KAAK,0BAA0B,GAAG;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC1B,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAClC,oBAAoB,EAAE,cAAc,CAAC;CACtC,CAAC;AA2CF,eAAO,MAAM,IAAI;;CAEf,CAAC;AAEH,cAAc,kBAAkB,CAAC"}
|
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { RHDHDeployment } from "../../deployment/rhdh/index.js";
|
|
2
2
|
import { test as base } from "@playwright/test";
|
|
3
3
|
import { LoginHelper, UIhelper } from "../helpers/index.js";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { runOnce } from "../run-once.js";
|
|
5
|
+
const baseTest = base.extend({
|
|
6
6
|
rhdhDeploymentWorker: [
|
|
7
7
|
// eslint-disable-next-line no-empty-pattern
|
|
8
8
|
async ({}, use, workerInfo) => {
|
|
9
|
-
console.log(`Deploying rhdh for plugin ${workerInfo.project.name} in namespace ${workerInfo.project.name}`);
|
|
10
9
|
const rhdhDeployment = new RHDHDeployment(workerInfo.project.name);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
await use(rhdhDeployment);
|
|
14
|
-
}
|
|
15
|
-
finally {
|
|
16
|
-
if (process.env.CI) {
|
|
17
|
-
console.log(`Deleting namespace ${workerInfo.project.name}`);
|
|
18
|
-
await rhdhDeployment.teardown();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
10
|
+
await rhdhDeployment.configure();
|
|
11
|
+
await use(rhdhDeployment);
|
|
21
12
|
},
|
|
22
13
|
{ scope: "worker", auto: true },
|
|
23
14
|
],
|
|
@@ -46,3 +37,7 @@ export const test = base.extend({
|
|
|
46
37
|
{ scope: "test" },
|
|
47
38
|
],
|
|
48
39
|
});
|
|
40
|
+
export const test = Object.assign(baseTest, {
|
|
41
|
+
runOnce,
|
|
42
|
+
});
|
|
43
|
+
export * from "@playwright/test";
|
|
@@ -11,7 +11,7 @@ export class APIHelper {
|
|
|
11
11
|
method: method,
|
|
12
12
|
headers: {
|
|
13
13
|
Accept: "application/vnd.github+json",
|
|
14
|
-
Authorization: `Bearer ${process.env.
|
|
14
|
+
Authorization: `Bearer ${process.env.VAULT_GITHUB_USER_TOKEN}`,
|
|
15
15
|
"X-GitHub-Api-Version": this.githubAPIVersion,
|
|
16
16
|
},
|
|
17
17
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAOhE,qBAAa,WAAW;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;gBAEP,IAAI,EAAE,IAAI;IAKhB,YAAY;IAcZ,OAAO;YAMC,aAAa;
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAOhE,qBAAa,WAAW;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;gBAEP,IAAI,EAAE,IAAI;IAKhB,YAAY;IAcZ,OAAO;YAMC,aAAa;IAyCrB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAYhD,mBAAmB,CACvB,MAAM,GAAE,MAAkC,EAC1C,QAAQ,GAAE,MAAkC;IASxC,iBAAiB,CACrB,MAAM,GAAE,MAA+C;IA6BnD,4BAA4B;IAqB5B,YAAY,CAAC,KAAK,EAAE,MAAM;IA2B1B,2BAA2B,CAAC,KAAK,UAAQ;IAU/C,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIxC,mBAAmB,IAAI,MAAM;IAIvB,mBAAmB;IAgBzB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAevC,eAAe,IAAI,MAAM;IAKnB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;YAqCxC,sBAAsB;IAoD9B,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAYjE,2BAA2B,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;IAYb,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CA2C7D;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ;;;GAWtE"}
|
|
@@ -35,11 +35,11 @@ export class LoginHelper {
|
|
|
35
35
|
await this.page.waitForSelector("#login_field");
|
|
36
36
|
await this.page.fill("#login_field", userid);
|
|
37
37
|
switch (userid) {
|
|
38
|
-
case process.env.
|
|
39
|
-
await this.page.fill("#password", process.env.
|
|
38
|
+
case process.env.VAULT_GH_USER_ID:
|
|
39
|
+
await this.page.fill("#password", process.env.VAULT_GH_USER_PASS);
|
|
40
40
|
break;
|
|
41
|
-
case process.env.
|
|
42
|
-
await this.page.fill("#password", process.env.
|
|
41
|
+
case process.env.VAULT_GH_USER2_ID:
|
|
42
|
+
await this.page.fill("#password", process.env.VAULT_GH_USER2_PASS);
|
|
43
43
|
break;
|
|
44
44
|
default:
|
|
45
45
|
throw new Error("Invalid User ID");
|
|
@@ -72,7 +72,7 @@ export class LoginHelper {
|
|
|
72
72
|
await this.logintoKeycloak(userid, password);
|
|
73
73
|
await this.page.waitForSelector("nav a", { timeout: 10_000 });
|
|
74
74
|
}
|
|
75
|
-
async loginAsGithubUser(userid = process.env.
|
|
75
|
+
async loginAsGithubUser(userid = process.env.VAULT_GH_USER_ID) {
|
|
76
76
|
const sessionFileName = `authState_${userid}.json`;
|
|
77
77
|
// Check if a session file for this specific user already exists
|
|
78
78
|
if (fs.existsSync(sessionFileName)) {
|
|
@@ -173,8 +173,8 @@ export class LoginHelper {
|
|
|
173
173
|
}
|
|
174
174
|
getGitHub2FAOTP(userid) {
|
|
175
175
|
const secrets = {
|
|
176
|
-
[process.env.
|
|
177
|
-
[process.env.
|
|
176
|
+
[process.env.VAULT_GH_USER_ID]: process.env.VAULT_GH_2FA_SECRET,
|
|
177
|
+
[process.env.VAULT_GH_USER2_ID]: process.env.VAULT_GH_USER2_2FA_SECRET,
|
|
178
178
|
};
|
|
179
179
|
const secret = secrets[userid];
|
|
180
180
|
if (!secret) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executes a function only once per test run, even across multiple workers.
|
|
3
|
+
* Automatically resets between test runs (each run uses a unique flag directory).
|
|
4
|
+
* Safe for fullyParallel: true (uses proper-lockfile for cross-process coordination).
|
|
5
|
+
*
|
|
6
|
+
* @param key - Unique identifier for this setup operation
|
|
7
|
+
* @param fn - Function to execute once
|
|
8
|
+
* @returns true if executed, false if skipped (already ran)
|
|
9
|
+
*/
|
|
10
|
+
export declare function runOnce(key: string, fn: () => Promise<void> | void): Promise<boolean>;
|
|
11
|
+
//# sourceMappingURL=run-once.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-once.d.ts","sourceRoot":"","sources":["../../src/playwright/run-once.ts"],"names":[],"mappings":"AAQA;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAC7B,OAAO,CAAC,OAAO,CAAC,CAyBlB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import lockfile from "proper-lockfile";
|
|
5
|
+
// Each test run gets its own flag directory (ppid = Playwright runner PID)
|
|
6
|
+
const flagDir = path.join(os.tmpdir(), `playwright-once-${process.ppid}`);
|
|
7
|
+
/**
|
|
8
|
+
* Executes a function only once per test run, even across multiple workers.
|
|
9
|
+
* Automatically resets between test runs (each run uses a unique flag directory).
|
|
10
|
+
* Safe for fullyParallel: true (uses proper-lockfile for cross-process coordination).
|
|
11
|
+
*
|
|
12
|
+
* @param key - Unique identifier for this setup operation
|
|
13
|
+
* @param fn - Function to execute once
|
|
14
|
+
* @returns true if executed, false if skipped (already ran)
|
|
15
|
+
*/
|
|
16
|
+
export async function runOnce(key, fn) {
|
|
17
|
+
const flagFile = path.join(flagDir, `${key}.done`);
|
|
18
|
+
const lockTarget = path.join(flagDir, key);
|
|
19
|
+
fs.mkdirSync(flagDir, { recursive: true });
|
|
20
|
+
// already executed, skip without locking
|
|
21
|
+
if (fs.existsSync(flagFile))
|
|
22
|
+
return false;
|
|
23
|
+
// Ensure lock target file exists
|
|
24
|
+
fs.writeFileSync(lockTarget, "", { flag: "a" });
|
|
25
|
+
const release = await lockfile.lock(lockTarget, {
|
|
26
|
+
retries: { retries: 30, minTimeout: 200 },
|
|
27
|
+
stale: 300_000,
|
|
28
|
+
});
|
|
29
|
+
try {
|
|
30
|
+
// Double-check after acquiring lock
|
|
31
|
+
if (fs.existsSync(flagFile))
|
|
32
|
+
return false;
|
|
33
|
+
await fn();
|
|
34
|
+
fs.writeFileSync(flagFile, "");
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
await release();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers a namespace for teardown after all tests in a project complete.
|
|
3
|
+
* Used by consumers who deploy to custom namespaces (not matching the project name).
|
|
4
|
+
*/
|
|
5
|
+
export declare function registerTeardownNamespace(projectName: string, namespace: string): void;
|
|
6
|
+
/**
|
|
7
|
+
* Returns all custom namespaces registered for teardown for a project.
|
|
8
|
+
* Used by the teardown reporter.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getTeardownNamespaces(projectName: string): string[];
|
|
11
|
+
//# sourceMappingURL=teardown-namespaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teardown-namespaces.d.ts","sourceRoot":"","sources":["../../src/playwright/teardown-namespaces.ts"],"names":[],"mappings":"AAsBA;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,IAAI,CASN;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
// Workers use process.ppid (Playwright runner PID)
|
|
5
|
+
// Reporter (main process) uses process.pid
|
|
6
|
+
// Both resolve to the same directory.
|
|
7
|
+
const TEARDOWN_DIR = path.join(os.tmpdir(), `playwright-teardown-${process.ppid || process.pid}`);
|
|
8
|
+
const TEARDOWN_FILE = path.join(TEARDOWN_DIR, "namespaces.json");
|
|
9
|
+
function read() {
|
|
10
|
+
if (!fs.existsSync(TEARDOWN_FILE))
|
|
11
|
+
return {};
|
|
12
|
+
return JSON.parse(fs.readFileSync(TEARDOWN_FILE, "utf-8"));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Registers a namespace for teardown after all tests in a project complete.
|
|
16
|
+
* Used by consumers who deploy to custom namespaces (not matching the project name).
|
|
17
|
+
*/
|
|
18
|
+
export function registerTeardownNamespace(projectName, namespace) {
|
|
19
|
+
fs.mkdirSync(TEARDOWN_DIR, { recursive: true });
|
|
20
|
+
const registry = read();
|
|
21
|
+
const namespaces = registry[projectName] ?? [];
|
|
22
|
+
if (!namespaces.includes(namespace)) {
|
|
23
|
+
namespaces.push(namespace);
|
|
24
|
+
registry[projectName] = namespaces;
|
|
25
|
+
fs.writeFileSync(TEARDOWN_FILE, JSON.stringify(registry));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns all custom namespaces registered for teardown for a project.
|
|
30
|
+
* Used by the teardown reporter.
|
|
31
|
+
*/
|
|
32
|
+
export function getTeardownNamespaces(projectName) {
|
|
33
|
+
return read()[projectName] ?? [];
|
|
34
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Reporter, Suite } from "@playwright/test/reporter";
|
|
2
|
+
/**
|
|
3
|
+
* Playwright reporter that deletes namespaces after all tests complete.
|
|
4
|
+
* Runs in the main process, so it survives worker restarts.
|
|
5
|
+
* Only active when process.env.CI is set.
|
|
6
|
+
*
|
|
7
|
+
* By default, deletes the namespace matching the project name.
|
|
8
|
+
* For custom namespaces, consumers can register them via registerTeardownNamespace().
|
|
9
|
+
*/
|
|
10
|
+
export default class TeardownReporter implements Reporter {
|
|
11
|
+
private _projects;
|
|
12
|
+
onBegin(_config: unknown, suite: Suite): void;
|
|
13
|
+
onEnd(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=teardown-reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teardown-reporter.d.ts","sourceRoot":"","sources":["../../src/playwright/teardown-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAIjE;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,YAAW,QAAQ;IACvD,OAAO,CAAC,SAAS,CAAqB;IAEtC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAOvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA2B7B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { KubernetesClientHelper } from "../utils/kubernetes-client.js";
|
|
2
|
+
import { getTeardownNamespaces } from "./teardown-namespaces.js";
|
|
3
|
+
/**
|
|
4
|
+
* Playwright reporter that deletes namespaces after all tests complete.
|
|
5
|
+
* Runs in the main process, so it survives worker restarts.
|
|
6
|
+
* Only active when process.env.CI is set.
|
|
7
|
+
*
|
|
8
|
+
* By default, deletes the namespace matching the project name.
|
|
9
|
+
* For custom namespaces, consumers can register them via registerTeardownNamespace().
|
|
10
|
+
*/
|
|
11
|
+
export default class TeardownReporter {
|
|
12
|
+
_projects = new Set();
|
|
13
|
+
onBegin(_config, suite) {
|
|
14
|
+
for (const test of suite.allTests()) {
|
|
15
|
+
const name = test.parent.project()?.name;
|
|
16
|
+
if (name)
|
|
17
|
+
this._projects.add(name);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async onEnd() {
|
|
21
|
+
if (!process.env.CI)
|
|
22
|
+
return;
|
|
23
|
+
let k8sClient;
|
|
24
|
+
try {
|
|
25
|
+
k8sClient = new KubernetesClientHelper();
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error(`[TeardownReporter] Cannot connect to cluster, skipping teardown:`, error);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
for (const projectName of this._projects) {
|
|
32
|
+
const customNamespaces = getTeardownNamespaces(projectName);
|
|
33
|
+
const namespaces = customNamespaces.length > 0 ? customNamespaces : [projectName];
|
|
34
|
+
for (const ns of namespaces) {
|
|
35
|
+
console.log(`[TeardownReporter] Deleting namespace "${ns}" (project: ${projectName})`);
|
|
36
|
+
await k8sClient.deleteNamespace(ns);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@red-hat-developer-hub/e2e-test-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14",
|
|
4
4
|
"description": "Test utilities for RHDH E2E tests",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"./keycloak": {
|
|
45
45
|
"types": "./dist/deployment/keycloak/index.d.ts",
|
|
46
46
|
"default": "./dist/deployment/keycloak/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./teardown": {
|
|
49
|
+
"types": "./dist/playwright/teardown-namespaces.d.ts",
|
|
50
|
+
"default": "./dist/playwright/teardown-namespaces.js"
|
|
47
51
|
}
|
|
48
52
|
},
|
|
49
53
|
"publishConfig": {
|
|
@@ -84,7 +88,8 @@
|
|
|
84
88
|
"@types/js-yaml": "^4.0.9",
|
|
85
89
|
"@types/lodash.clonedeepwith": "^4.5.9",
|
|
86
90
|
"@types/lodash.mergewith": "^4.6.9",
|
|
87
|
-
"@types/node": "^24.10.1"
|
|
91
|
+
"@types/node": "^24.10.1",
|
|
92
|
+
"@types/proper-lockfile": "^4.1.4"
|
|
88
93
|
},
|
|
89
94
|
"dependencies": {
|
|
90
95
|
"@axe-core/playwright": "^4.11.0",
|
|
@@ -100,6 +105,7 @@
|
|
|
100
105
|
"lodash.mergewith": "^4.6.2",
|
|
101
106
|
"otplib": "12.0.1",
|
|
102
107
|
"prettier": "^3.7.4",
|
|
108
|
+
"proper-lockfile": "^4.1.2",
|
|
103
109
|
"typescript": "^5.9.3",
|
|
104
110
|
"typescript-eslint": "^8.48.1",
|
|
105
111
|
"zx": "^8.8.5"
|