@red-hat-developer-hub/e2e-test-utils 1.1.10
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/LICENSE +201 -0
- package/README.md +6 -0
- package/dist/deployment/keycloak/config/keycloak-values.yaml +96 -0
- package/dist/deployment/keycloak/constants.d.ts +29 -0
- package/dist/deployment/keycloak/constants.d.ts.map +1 -0
- package/dist/deployment/keycloak/constants.js +75 -0
- package/dist/deployment/keycloak/deployment.d.ts +89 -0
- package/dist/deployment/keycloak/deployment.d.ts.map +1 -0
- package/dist/deployment/keycloak/deployment.js +435 -0
- package/dist/deployment/keycloak/index.d.ts +2 -0
- package/dist/deployment/keycloak/index.d.ts.map +1 -0
- package/dist/deployment/keycloak/index.js +1 -0
- package/dist/deployment/keycloak/types.d.ts +59 -0
- package/dist/deployment/keycloak/types.d.ts.map +1 -0
- package/dist/deployment/keycloak/types.js +1 -0
- package/dist/deployment/rhdh/config/auth/guest/app-config.yaml +5 -0
- package/dist/deployment/rhdh/config/auth/keycloak/app-config.yaml +19 -0
- package/dist/deployment/rhdh/config/auth/keycloak/dynamic-plugins.yaml +3 -0
- package/dist/deployment/rhdh/config/auth/keycloak/secrets.yaml +12 -0
- package/dist/deployment/rhdh/config/common/app-config-rhdh.yaml +6 -0
- package/dist/deployment/rhdh/config/common/dynamic-plugins.yaml +3 -0
- package/dist/deployment/rhdh/config/common/rhdh-secrets.yaml +7 -0
- package/dist/deployment/rhdh/config/helm/value_file.yaml +7 -0
- package/dist/deployment/rhdh/config/operator/subscription.yaml +21 -0
- package/dist/deployment/rhdh/constants.d.ts +19 -0
- package/dist/deployment/rhdh/constants.d.ts.map +1 -0
- package/dist/deployment/rhdh/constants.js +27 -0
- package/dist/deployment/rhdh/deployment.d.ts +40 -0
- package/dist/deployment/rhdh/deployment.d.ts.map +1 -0
- package/dist/deployment/rhdh/deployment.js +303 -0
- package/dist/deployment/rhdh/index.d.ts +2 -0
- package/dist/deployment/rhdh/index.d.ts.map +1 -0
- package/dist/deployment/rhdh/index.js +1 -0
- package/dist/deployment/rhdh/types.d.ts +31 -0
- package/dist/deployment/rhdh/types.d.ts.map +1 -0
- package/dist/deployment/rhdh/types.js +1 -0
- package/dist/eslint/base.config.d.ts +10 -0
- package/dist/eslint/base.config.d.ts.map +1 -0
- package/dist/eslint/base.config.js +213 -0
- package/dist/playwright/base-config.d.ts +14 -0
- package/dist/playwright/base-config.d.ts.map +1 -0
- package/dist/playwright/base-config.js +47 -0
- package/dist/playwright/fixtures/test.d.ts +13 -0
- package/dist/playwright/fixtures/test.d.ts.map +1 -0
- package/dist/playwright/fixtures/test.js +48 -0
- package/dist/playwright/global-setup.d.ts +6 -0
- package/dist/playwright/global-setup.d.ts.map +1 -0
- package/dist/playwright/global-setup.js +67 -0
- package/dist/playwright/helpers/accessibility.d.ts +13 -0
- package/dist/playwright/helpers/accessibility.d.ts.map +1 -0
- package/dist/playwright/helpers/accessibility.js +24 -0
- package/dist/playwright/helpers/api-endpoints.d.ts +11 -0
- package/dist/playwright/helpers/api-endpoints.d.ts.map +1 -0
- package/dist/playwright/helpers/api-endpoints.js +15 -0
- package/dist/playwright/helpers/api-helper.d.ts +77 -0
- package/dist/playwright/helpers/api-helper.d.ts.map +1 -0
- package/dist/playwright/helpers/api-helper.js +285 -0
- package/dist/playwright/helpers/common.d.ts +31 -0
- package/dist/playwright/helpers/common.d.ts.map +1 -0
- package/dist/playwright/helpers/common.js +342 -0
- package/dist/playwright/helpers/index.d.ts +5 -0
- package/dist/playwright/helpers/index.d.ts.map +1 -0
- package/dist/playwright/helpers/index.js +4 -0
- package/dist/playwright/helpers/navbar.d.ts +2 -0
- package/dist/playwright/helpers/navbar.d.ts.map +1 -0
- package/dist/playwright/helpers/navbar.js +1 -0
- package/dist/playwright/helpers/ui-helper.d.ts +106 -0
- package/dist/playwright/helpers/ui-helper.d.ts.map +1 -0
- package/dist/playwright/helpers/ui-helper.js +439 -0
- package/dist/playwright/page-objects/global-obj.d.ts +25 -0
- package/dist/playwright/page-objects/global-obj.d.ts.map +1 -0
- package/dist/playwright/page-objects/global-obj.js +24 -0
- package/dist/playwright/page-objects/page-obj.d.ts +41 -0
- package/dist/playwright/page-objects/page-obj.d.ts.map +1 -0
- package/dist/playwright/page-objects/page-obj.js +40 -0
- package/dist/playwright/pages/catalog-import.d.ts +31 -0
- package/dist/playwright/pages/catalog-import.d.ts.map +1 -0
- package/dist/playwright/pages/catalog-import.js +65 -0
- package/dist/playwright/pages/catalog.d.ts +14 -0
- package/dist/playwright/pages/catalog.d.ts.map +1 -0
- package/dist/playwright/pages/catalog.js +37 -0
- package/dist/playwright/pages/extensions.d.ts +38 -0
- package/dist/playwright/pages/extensions.d.ts.map +1 -0
- package/dist/playwright/pages/extensions.js +110 -0
- package/dist/playwright/pages/home-page.d.ts +10 -0
- package/dist/playwright/pages/home-page.d.ts.map +1 -0
- package/dist/playwright/pages/home-page.js +46 -0
- package/dist/playwright/pages/index.d.ts +6 -0
- package/dist/playwright/pages/index.d.ts.map +1 -0
- package/dist/playwright/pages/index.js +5 -0
- package/dist/playwright/pages/notifications.d.ts +24 -0
- package/dist/playwright/pages/notifications.d.ts.map +1 -0
- package/dist/playwright/pages/notifications.js +112 -0
- package/dist/utils/bash.d.ts +3 -0
- package/dist/utils/bash.d.ts.map +1 -0
- package/dist/utils/bash.js +4 -0
- package/dist/utils/common.d.ts +3 -0
- package/dist/utils/common.d.ts.map +1 -0
- package/dist/utils/common.js +8 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/kubernetes-client.d.ts +97 -0
- package/dist/utils/kubernetes-client.d.ts.map +1 -0
- package/dist/utils/kubernetes-client.js +558 -0
- package/dist/utils/merge-yamls.d.ts +50 -0
- package/dist/utils/merge-yamls.d.ts.map +1 -0
- package/dist/utils/merge-yamls.js +96 -0
- package/dist/utils/plugin-metadata.d.ts +116 -0
- package/dist/utils/plugin-metadata.d.ts.map +1 -0
- package/dist/utils/plugin-metadata.js +330 -0
- package/package.json +108 -0
- package/tsconfig.base.json +16 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { KubernetesClientHelper } from "../../utils/kubernetes-client.js";
|
|
2
|
+
import type { DeploymentOptions, DeploymentConfig } from "./types.js";
|
|
3
|
+
export declare class RHDHDeployment {
|
|
4
|
+
k8sClient: KubernetesClientHelper;
|
|
5
|
+
rhdhUrl: string;
|
|
6
|
+
deploymentConfig: DeploymentConfig;
|
|
7
|
+
constructor(namespace: string);
|
|
8
|
+
deploy(): Promise<void>;
|
|
9
|
+
private _applyAppConfig;
|
|
10
|
+
private _applySecrets;
|
|
11
|
+
/**
|
|
12
|
+
* Builds the merged dynamic plugins configuration.
|
|
13
|
+
* Merges: package defaults + auth config + user config + metadata (for PR builds).
|
|
14
|
+
*/
|
|
15
|
+
private _buildDynamicPluginsConfig;
|
|
16
|
+
private _applyDynamicPlugins;
|
|
17
|
+
private _deployWithHelm;
|
|
18
|
+
private _deployWithOperator;
|
|
19
|
+
rolloutRestart(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Performs a clean restart by scaling down to 0 first, waiting for pods to terminate,
|
|
22
|
+
* then scaling back up. This prevents MigrationLocked errors by ensuring no pods
|
|
23
|
+
* hold database locks when new pods start.
|
|
24
|
+
*/
|
|
25
|
+
scaleDownAndRestart(): Promise<void>;
|
|
26
|
+
waitUntilReady(timeout?: number): Promise<void>;
|
|
27
|
+
teardown(): Promise<void>;
|
|
28
|
+
private _resolveChartVersion;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the semantic version from the "next" tag by looking up the
|
|
31
|
+
* downstream image (rhdh-hub-rhel9) and finding tags with the same digest.
|
|
32
|
+
*/
|
|
33
|
+
private _resolveVersionFromNextTag;
|
|
34
|
+
private _buildDeploymentConfig;
|
|
35
|
+
configure(deploymentOptions?: DeploymentOptions): Promise<void>;
|
|
36
|
+
private _buildBaseUrl;
|
|
37
|
+
private _log;
|
|
38
|
+
private _logBoxen;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=deployment.d.ts.map
|
|
@@ -0,0 +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;AAiB1E,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;IAUvB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;YAqBf,eAAe;YAgBf,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;IAcrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;CAMlB"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { KubernetesClientHelper } from "../../utils/kubernetes-client.js";
|
|
2
|
+
import { $ } from "../../utils/bash.js";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { test } from "@playwright/test";
|
|
5
|
+
import { mergeYamlFilesIfExists, deepMerge } from "../../utils/merge-yamls.js";
|
|
6
|
+
import { loadAndInjectPluginMetadata, generateDynamicPluginsConfigFromMetadata, } from "../../utils/plugin-metadata.js";
|
|
7
|
+
import { envsubst } from "../../utils/common.js";
|
|
8
|
+
import cloneDeepWith from "lodash.clonedeepwith";
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import { DEFAULT_CONFIG_PATHS, AUTH_CONFIG_PATHS, CHART_URL, } from "./constants.js";
|
|
11
|
+
export class RHDHDeployment {
|
|
12
|
+
k8sClient = new KubernetesClientHelper();
|
|
13
|
+
rhdhUrl;
|
|
14
|
+
deploymentConfig;
|
|
15
|
+
constructor(namespace) {
|
|
16
|
+
this.deploymentConfig = this._buildDeploymentConfig({ namespace });
|
|
17
|
+
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
|
+
}
|
|
22
|
+
async deploy() {
|
|
23
|
+
this._log("Starting RHDH deployment...");
|
|
24
|
+
test.setTimeout(600_000);
|
|
25
|
+
await this.k8sClient.createNamespaceIfNotExists(this.deploymentConfig.namespace);
|
|
26
|
+
await this._applyAppConfig();
|
|
27
|
+
await this._applySecrets();
|
|
28
|
+
if (this.deploymentConfig.method === "helm") {
|
|
29
|
+
await this._deployWithHelm(this.deploymentConfig.valueFile);
|
|
30
|
+
await this.scaleDownAndRestart(); // Restart as helm does not monitor config changes
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
await this._applyDynamicPlugins();
|
|
34
|
+
await this._deployWithOperator(this.deploymentConfig.subscription);
|
|
35
|
+
}
|
|
36
|
+
await this.waitUntilReady();
|
|
37
|
+
}
|
|
38
|
+
async _applyAppConfig() {
|
|
39
|
+
const authConfig = AUTH_CONFIG_PATHS[this.deploymentConfig.auth];
|
|
40
|
+
const appConfigYaml = await mergeYamlFilesIfExists([
|
|
41
|
+
DEFAULT_CONFIG_PATHS.appConfig,
|
|
42
|
+
authConfig.appConfig,
|
|
43
|
+
this.deploymentConfig.appConfig,
|
|
44
|
+
]);
|
|
45
|
+
this._logBoxen("App Config", appConfigYaml);
|
|
46
|
+
await this.k8sClient.applyConfigMapFromObject("app-config-rhdh", appConfigYaml, this.deploymentConfig.namespace);
|
|
47
|
+
}
|
|
48
|
+
async _applySecrets() {
|
|
49
|
+
const authConfig = AUTH_CONFIG_PATHS[this.deploymentConfig.auth];
|
|
50
|
+
const secretsYaml = await mergeYamlFilesIfExists([
|
|
51
|
+
DEFAULT_CONFIG_PATHS.secrets,
|
|
52
|
+
authConfig.secrets,
|
|
53
|
+
this.deploymentConfig.secrets,
|
|
54
|
+
]);
|
|
55
|
+
// Use cloneDeepWith to substitute env vars in-place, avoiding JSON.parse issues
|
|
56
|
+
// with control characters in secrets (e.g., private keys with newlines)
|
|
57
|
+
const substituted = cloneDeepWith(secretsYaml, (value) => {
|
|
58
|
+
if (typeof value === "string")
|
|
59
|
+
return envsubst(value);
|
|
60
|
+
});
|
|
61
|
+
await this.k8sClient.applySecretFromObject("rhdh-secrets", substituted, this.deploymentConfig.namespace);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Builds the merged dynamic plugins configuration.
|
|
65
|
+
* Merges: package defaults + auth config + user config + metadata (for PR builds).
|
|
66
|
+
*/
|
|
67
|
+
async _buildDynamicPluginsConfig() {
|
|
68
|
+
const userConfigPath = this.deploymentConfig.dynamicPlugins;
|
|
69
|
+
const userConfigExists = userConfigPath && fs.existsSync(userConfigPath);
|
|
70
|
+
const authConfig = AUTH_CONFIG_PATHS[this.deploymentConfig.auth];
|
|
71
|
+
// If user's dynamic-plugins config doesn't exist, auto-generate from metadata
|
|
72
|
+
if (!userConfigExists) {
|
|
73
|
+
this._log(`Dynamic plugins config not found at '${userConfigPath}', auto-generating from metadata...`);
|
|
74
|
+
const metadataConfig = await generateDynamicPluginsConfigFromMetadata();
|
|
75
|
+
// Merge with package defaults and auth config
|
|
76
|
+
const authPlugins = await mergeYamlFilesIfExists([DEFAULT_CONFIG_PATHS.dynamicPlugins, authConfig.dynamicPlugins], { arrayMergeStrategy: { byKey: "package" } });
|
|
77
|
+
return deepMerge(metadataConfig, authPlugins, {
|
|
78
|
+
arrayMergeStrategy: { byKey: "package" },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// User config exists - merge provided configs and inject metadata for listed plugins only
|
|
82
|
+
let dynamicPluginsConfig = await mergeYamlFilesIfExists([
|
|
83
|
+
DEFAULT_CONFIG_PATHS.dynamicPlugins,
|
|
84
|
+
authConfig.dynamicPlugins,
|
|
85
|
+
userConfigPath,
|
|
86
|
+
], { arrayMergeStrategy: { byKey: "package" } });
|
|
87
|
+
// Inject plugin metadata configuration for plugins in the config
|
|
88
|
+
dynamicPluginsConfig =
|
|
89
|
+
await loadAndInjectPluginMetadata(dynamicPluginsConfig);
|
|
90
|
+
return dynamicPluginsConfig;
|
|
91
|
+
}
|
|
92
|
+
async _applyDynamicPlugins() {
|
|
93
|
+
const dynamicPluginsYaml = await this._buildDynamicPluginsConfig();
|
|
94
|
+
this._logBoxen("Dynamic Plugins", dynamicPluginsYaml);
|
|
95
|
+
await this.k8sClient.applyConfigMapFromObject("dynamic-plugins", dynamicPluginsYaml, this.deploymentConfig.namespace);
|
|
96
|
+
}
|
|
97
|
+
async _deployWithHelm(valueFile) {
|
|
98
|
+
const chartVersion = await this._resolveChartVersion(this.deploymentConfig.version);
|
|
99
|
+
this._log(`Helm chart version resolved to: ${chartVersion}`);
|
|
100
|
+
const valueFileObject = (await mergeYamlFilesIfExists([
|
|
101
|
+
DEFAULT_CONFIG_PATHS.helm.valueFile,
|
|
102
|
+
valueFile,
|
|
103
|
+
]));
|
|
104
|
+
this._logBoxen("Value File", valueFileObject);
|
|
105
|
+
// Merge dynamic plugins into the values file (including auth-specific plugins)
|
|
106
|
+
if (!valueFileObject.global) {
|
|
107
|
+
valueFileObject.global = {};
|
|
108
|
+
}
|
|
109
|
+
valueFileObject.global.dynamic = await this._buildDynamicPluginsConfig();
|
|
110
|
+
this._logBoxen("Dynamic Plugins", valueFileObject.global.dynamic);
|
|
111
|
+
fs.writeFileSync(`/tmp/${this.deploymentConfig.namespace}-value-file.yaml`, yaml.dump(valueFileObject));
|
|
112
|
+
await $ `
|
|
113
|
+
helm upgrade redhat-developer-hub -i "${process.env.CHART_URL || CHART_URL}" --version "${chartVersion}" \
|
|
114
|
+
-f "/tmp/${this.deploymentConfig.namespace}-value-file.yaml" \
|
|
115
|
+
--set global.clusterRouterBase="${process.env.K8S_CLUSTER_ROUTER_BASE}" \
|
|
116
|
+
--namespace="${this.deploymentConfig.namespace}"
|
|
117
|
+
`;
|
|
118
|
+
this._log(`Helm deployment completed successfully`);
|
|
119
|
+
}
|
|
120
|
+
async _deployWithOperator(subscription) {
|
|
121
|
+
const subscriptionObject = await mergeYamlFilesIfExists([
|
|
122
|
+
DEFAULT_CONFIG_PATHS.operator.subscription,
|
|
123
|
+
subscription,
|
|
124
|
+
]);
|
|
125
|
+
this._logBoxen("Subscription", subscriptionObject);
|
|
126
|
+
fs.writeFileSync(`/tmp/${this.deploymentConfig.namespace}-subscription.yaml`, yaml.dump(subscriptionObject));
|
|
127
|
+
const version = this.deploymentConfig.version;
|
|
128
|
+
const isSemanticVersion = /^\d+(\.\d+)?$/.test(version);
|
|
129
|
+
// Use main branch for non-semantic versions (e.g., "next", "latest")
|
|
130
|
+
const branch = isSemanticVersion ? `release-${version}` : "main";
|
|
131
|
+
// Build version argument based on version type
|
|
132
|
+
let versionArg;
|
|
133
|
+
if (isSemanticVersion) {
|
|
134
|
+
versionArg = `-v ${version}`;
|
|
135
|
+
}
|
|
136
|
+
else if (version === "next") {
|
|
137
|
+
versionArg = "--next";
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
throw new Error(`Invalid RHDH version "${version}". Use semantic version (e.g., "1.5") or "next".`);
|
|
141
|
+
}
|
|
142
|
+
this._log(`Using operator branch: ${branch}, version arg: ${versionArg}`);
|
|
143
|
+
await $ `
|
|
144
|
+
set -e;
|
|
145
|
+
curl -sf https://raw.githubusercontent.com/redhat-developer/rhdh-operator/refs/heads/${branch}/.rhdh/scripts/install-rhdh-catalog-source.sh | bash -s -- ${versionArg} --install-operator rhdh
|
|
146
|
+
|
|
147
|
+
timeout 300 bash -c '
|
|
148
|
+
while ! oc get crd/backstages.rhdh.redhat.com -n "${this.deploymentConfig.namespace}" >/dev/null 2>&1; do
|
|
149
|
+
echo "Waiting for Backstage CRD to be created..."
|
|
150
|
+
sleep 20
|
|
151
|
+
done
|
|
152
|
+
echo "Backstage CRD is created."
|
|
153
|
+
' || echo "Error: Timed out waiting for Backstage CRD creation."
|
|
154
|
+
|
|
155
|
+
oc apply -f "/tmp/${this.deploymentConfig.namespace}-subscription.yaml" -n "${this.deploymentConfig.namespace}"
|
|
156
|
+
`;
|
|
157
|
+
this._log("Operator deployment executed successfully.");
|
|
158
|
+
}
|
|
159
|
+
async rolloutRestart() {
|
|
160
|
+
this._log(`Restarting RHDH deployment in namespace ${this.deploymentConfig.namespace}...`);
|
|
161
|
+
await $ `oc rollout restart deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' -n ${this.deploymentConfig.namespace}`;
|
|
162
|
+
this._log(`RHDH deployment restarted successfully in namespace ${this.deploymentConfig.namespace}`);
|
|
163
|
+
await this.waitUntilReady();
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Performs a clean restart by scaling down to 0 first, waiting for pods to terminate,
|
|
167
|
+
* then scaling back up. This prevents MigrationLocked errors by ensuring no pods
|
|
168
|
+
* hold database locks when new pods start.
|
|
169
|
+
*/
|
|
170
|
+
async scaleDownAndRestart() {
|
|
171
|
+
const namespace = this.deploymentConfig.namespace;
|
|
172
|
+
await $ `oc scale deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' --replicas=0 -n ${namespace}`;
|
|
173
|
+
await $ `oc wait --for=delete pod -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub),app.kubernetes.io/name!=postgresql' -n ${namespace} --timeout=120s || true`;
|
|
174
|
+
await $ `oc scale deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' --replicas=1 -n ${namespace}`;
|
|
175
|
+
}
|
|
176
|
+
async waitUntilReady(timeout = 500) {
|
|
177
|
+
this._log(`Waiting for RHDH deployment to be ready in namespace ${this.deploymentConfig.namespace}...`);
|
|
178
|
+
const labelSelector = "app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)";
|
|
179
|
+
try {
|
|
180
|
+
await this.k8sClient.waitForPodsWithFailureDetection(this.deploymentConfig.namespace, labelSelector, timeout);
|
|
181
|
+
this._log(`RHDH deployment is ready in namespace ${this.deploymentConfig.namespace}`);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
throw new Error(`RHDH deployment failed in namespace ${this.deploymentConfig.namespace}: ${error instanceof Error ? error.message : error}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async teardown() {
|
|
188
|
+
await this.k8sClient.deleteNamespace(this.deploymentConfig.namespace);
|
|
189
|
+
}
|
|
190
|
+
async _resolveChartVersion(version) {
|
|
191
|
+
let resolvedVersion = version;
|
|
192
|
+
// Handle "next" tag by looking up the corresponding version from downstream image
|
|
193
|
+
if (version === "next") {
|
|
194
|
+
resolvedVersion = await this._resolveVersionFromNextTag();
|
|
195
|
+
this._log(`Resolved "next" tag to version: ${resolvedVersion}`);
|
|
196
|
+
}
|
|
197
|
+
// Semantic versions (e.g., 1.2, 1.10)
|
|
198
|
+
if (/^(\d+(\.\d+)?)$/.test(resolvedVersion)) {
|
|
199
|
+
const response = await fetch("https://quay.io/api/v1/repository/rhdh/chart/tag/?onlyActiveTags=true&limit=600");
|
|
200
|
+
if (!response.ok)
|
|
201
|
+
throw new Error(`Failed to fetch chart versions: ${response.statusText}`);
|
|
202
|
+
const data = (await response.json());
|
|
203
|
+
const matching = data.tags
|
|
204
|
+
.map((t) => t.name)
|
|
205
|
+
.filter((name) => name.startsWith(`${resolvedVersion}-`))
|
|
206
|
+
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
|
207
|
+
const latest = matching.at(-1);
|
|
208
|
+
if (!latest)
|
|
209
|
+
throw new Error(`No chart version found for ${resolvedVersion}`);
|
|
210
|
+
return latest;
|
|
211
|
+
}
|
|
212
|
+
// CI build versions (e.g., 1.2.3-CI)
|
|
213
|
+
if (resolvedVersion.endsWith("CI"))
|
|
214
|
+
return resolvedVersion;
|
|
215
|
+
throw new Error(`Invalid Helm chart version format: "${version}"`);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Resolve the semantic version from the "next" tag by looking up the
|
|
219
|
+
* downstream image (rhdh-hub-rhel9) and finding tags with the same digest.
|
|
220
|
+
*/
|
|
221
|
+
async _resolveVersionFromNextTag() {
|
|
222
|
+
// Fetch all active tags in a single API call
|
|
223
|
+
const response = await fetch("https://quay.io/api/v1/repository/rhdh/rhdh-hub-rhel9/tag/?onlyActiveTags=true&limit=75");
|
|
224
|
+
if (!response.ok) {
|
|
225
|
+
throw new Error(`Failed to fetch image tags: ${response.statusText}`);
|
|
226
|
+
}
|
|
227
|
+
// Use Record to avoid snake_case linting issues with Quay API response
|
|
228
|
+
const data = (await response.json());
|
|
229
|
+
// Find the "next" tag and get its digest
|
|
230
|
+
const nextTag = data.tags.find((t) => t["name"] === "next");
|
|
231
|
+
if (!nextTag) {
|
|
232
|
+
throw new Error('No "next" tag found in rhdh-hub-rhel9 repository');
|
|
233
|
+
}
|
|
234
|
+
const digest = nextTag["manifest_digest"];
|
|
235
|
+
this._log(`"next" tag digest: ${digest}`);
|
|
236
|
+
// Find semantic version tag (e.g., "1.10") with the same digest
|
|
237
|
+
const semanticVersionTag = data.tags.find((t) => t["manifest_digest"] === digest &&
|
|
238
|
+
/^\d+\.\d+$/.test(t["name"]));
|
|
239
|
+
if (!semanticVersionTag) {
|
|
240
|
+
throw new Error(`Could not find semantic version tag for "next" (digest: ${digest})`);
|
|
241
|
+
}
|
|
242
|
+
return semanticVersionTag["name"];
|
|
243
|
+
}
|
|
244
|
+
_buildDeploymentConfig(input) {
|
|
245
|
+
// Default to "next" if RHDH_VERSION not set
|
|
246
|
+
const version = input.version ?? process.env.RHDH_VERSION ?? "next";
|
|
247
|
+
// Default to "helm" if INSTALLATION_METHOD not set
|
|
248
|
+
const method = input.method ??
|
|
249
|
+
process.env.INSTALLATION_METHOD ??
|
|
250
|
+
"helm";
|
|
251
|
+
const base = {
|
|
252
|
+
version,
|
|
253
|
+
namespace: input.namespace ?? this.deploymentConfig.namespace,
|
|
254
|
+
auth: input.auth ?? "keycloak",
|
|
255
|
+
appConfig: input.appConfig ?? `tests/config/app-config-rhdh.yaml`,
|
|
256
|
+
secrets: input.secrets ?? `tests/config/rhdh-secrets.yaml`,
|
|
257
|
+
dynamicPlugins: input.dynamicPlugins ?? `tests/config/dynamic-plugins.yaml`,
|
|
258
|
+
};
|
|
259
|
+
if (method === "helm") {
|
|
260
|
+
return {
|
|
261
|
+
...base,
|
|
262
|
+
method,
|
|
263
|
+
valueFile: input.valueFile ?? `tests/config/value_file.yaml`,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
else if (method === "operator") {
|
|
267
|
+
return {
|
|
268
|
+
...base,
|
|
269
|
+
method,
|
|
270
|
+
subscription: input.subscription ?? `tests/config/subscription.yaml`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
throw new Error(`Invalid RHDH installation method: ${method}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async configure(deploymentOptions) {
|
|
278
|
+
if (deploymentOptions) {
|
|
279
|
+
this.deploymentConfig = this._buildDeploymentConfig(deploymentOptions);
|
|
280
|
+
this.rhdhUrl = this._buildBaseUrl();
|
|
281
|
+
this._log(`RHDH deployment initialized (namespace: ${this.deploymentConfig.namespace})`);
|
|
282
|
+
console.table(this.deploymentConfig);
|
|
283
|
+
}
|
|
284
|
+
await this.k8sClient.createNamespaceIfNotExists(this.deploymentConfig.namespace);
|
|
285
|
+
}
|
|
286
|
+
_buildBaseUrl() {
|
|
287
|
+
const prefix = this.deploymentConfig.method === "helm"
|
|
288
|
+
? "redhat-developer-hub"
|
|
289
|
+
: "backstage-developer-hub";
|
|
290
|
+
const baseUrl = `https://${prefix}-${this.deploymentConfig.namespace}.${process.env.K8S_CLUSTER_ROUTER_BASE}`;
|
|
291
|
+
process.env.RHDH_BASE_URL = baseUrl;
|
|
292
|
+
return baseUrl;
|
|
293
|
+
}
|
|
294
|
+
_log(...args) {
|
|
295
|
+
console.log("[RHDHDeployment]", ...args);
|
|
296
|
+
}
|
|
297
|
+
_logBoxen(title, data) {
|
|
298
|
+
const content = yaml.dump(data, { lineWidth: -1 });
|
|
299
|
+
console.log(`\n┌─ ${title} ${"─".repeat(60)}`);
|
|
300
|
+
console.log(content);
|
|
301
|
+
console.log(`└${"─".repeat(60 + title.length + 3)}\n`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RHDHDeployment } from "./deployment.js";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type DeploymentMethod = "helm" | "operator";
|
|
2
|
+
export type AuthProvider = "guest" | "keycloak";
|
|
3
|
+
export type DeploymentOptions = {
|
|
4
|
+
version?: string;
|
|
5
|
+
namespace?: string;
|
|
6
|
+
auth?: AuthProvider;
|
|
7
|
+
appConfig?: string;
|
|
8
|
+
secrets?: string;
|
|
9
|
+
dynamicPlugins?: string;
|
|
10
|
+
method?: DeploymentMethod;
|
|
11
|
+
valueFile?: string;
|
|
12
|
+
subscription?: string;
|
|
13
|
+
};
|
|
14
|
+
export type HelmDeploymentConfig = {
|
|
15
|
+
method: "helm";
|
|
16
|
+
valueFile: string;
|
|
17
|
+
};
|
|
18
|
+
export type OperatorDeploymentConfig = {
|
|
19
|
+
method: "operator";
|
|
20
|
+
subscription: string;
|
|
21
|
+
};
|
|
22
|
+
export type DeploymentConfigBase = {
|
|
23
|
+
version: string;
|
|
24
|
+
namespace: string;
|
|
25
|
+
auth: AuthProvider;
|
|
26
|
+
appConfig: string;
|
|
27
|
+
secrets: string;
|
|
28
|
+
dynamicPlugins: string;
|
|
29
|
+
};
|
|
30
|
+
export type DeploymentConfig = DeploymentConfigBase & (HelmDeploymentConfig | OperatorDeploymentConfig);
|
|
31
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +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;AAEhD,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Linter } from "eslint";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a base ESLint configuration for RHDH E2E tests.
|
|
4
|
+
* This configuration includes TypeScript, Playwright, and file naming conventions.
|
|
5
|
+
*
|
|
6
|
+
* @param tsconfigRootDir - The root directory for tsconfig.json resolution
|
|
7
|
+
* @returns ESLint flat config array
|
|
8
|
+
*/
|
|
9
|
+
export declare function createEslintConfig(tsconfigRootDir: string): Linter.Config[];
|
|
10
|
+
//# sourceMappingURL=base.config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.config.d.ts","sourceRoot":"","sources":["../../src/eslint/base.config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CA4M3E"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
import checkFile from "eslint-plugin-check-file";
|
|
4
|
+
import playwright from "eslint-plugin-playwright";
|
|
5
|
+
/**
|
|
6
|
+
* Creates a base ESLint configuration for RHDH E2E tests.
|
|
7
|
+
* This configuration includes TypeScript, Playwright, and file naming conventions.
|
|
8
|
+
*
|
|
9
|
+
* @param tsconfigRootDir - The root directory for tsconfig.json resolution
|
|
10
|
+
* @returns ESLint flat config array
|
|
11
|
+
*/
|
|
12
|
+
export function createEslintConfig(tsconfigRootDir) {
|
|
13
|
+
return [
|
|
14
|
+
// Global ignores - must be first for ESLint flat config
|
|
15
|
+
{
|
|
16
|
+
ignores: [
|
|
17
|
+
"node_modules/**",
|
|
18
|
+
"playwright-report/**",
|
|
19
|
+
"test-results/**",
|
|
20
|
+
"blob-report/**",
|
|
21
|
+
"*.config.js",
|
|
22
|
+
"dist/**",
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
js.configs.recommended,
|
|
26
|
+
...tseslint.configs.recommended,
|
|
27
|
+
{
|
|
28
|
+
files: ["**/*.ts"],
|
|
29
|
+
languageOptions: {
|
|
30
|
+
parserOptions: {
|
|
31
|
+
project: "./tsconfig.json",
|
|
32
|
+
tsconfigRootDir,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
rules: {
|
|
36
|
+
// TypeScript naming conventions
|
|
37
|
+
"@typescript-eslint/naming-convention": [
|
|
38
|
+
"error",
|
|
39
|
+
{
|
|
40
|
+
selector: "variable",
|
|
41
|
+
format: ["camelCase", "PascalCase"],
|
|
42
|
+
leadingUnderscore: "allow",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
selector: "variable",
|
|
46
|
+
modifiers: ["const"],
|
|
47
|
+
format: ["camelCase", "PascalCase", "UPPER_CASE"],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
selector: "function",
|
|
51
|
+
format: ["camelCase", "PascalCase"],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
selector: "parameter",
|
|
55
|
+
format: ["camelCase", "PascalCase"],
|
|
56
|
+
leadingUnderscore: "allow",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
selector: "typeLike",
|
|
60
|
+
format: ["PascalCase"],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
selector: "enumMember",
|
|
64
|
+
format: ["PascalCase"],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
selector: "memberLike",
|
|
68
|
+
modifiers: ["private"],
|
|
69
|
+
format: ["camelCase"],
|
|
70
|
+
leadingUnderscore: "allow",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
selector: "memberLike",
|
|
74
|
+
modifiers: ["public"],
|
|
75
|
+
format: ["camelCase"],
|
|
76
|
+
},
|
|
77
|
+
// Allow HTTP headers in object literals which require specific formats
|
|
78
|
+
{
|
|
79
|
+
selector: "objectLiteralProperty",
|
|
80
|
+
format: null,
|
|
81
|
+
filter: {
|
|
82
|
+
regex: "^(Accept|Authorization|Content-Type|X-GitHub-Api-Version|X-[A-Za-z-]+)$",
|
|
83
|
+
match: true,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
// Promise handling
|
|
88
|
+
"@typescript-eslint/no-floating-promises": "error",
|
|
89
|
+
"@typescript-eslint/await-thenable": "error",
|
|
90
|
+
"@typescript-eslint/no-misused-promises": "error",
|
|
91
|
+
// Allow any type in tests (for mocking, test data)
|
|
92
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
93
|
+
// Prefer modern syntax
|
|
94
|
+
"@typescript-eslint/prefer-optional-chain": "error",
|
|
95
|
+
// Allow unused vars starting with underscore
|
|
96
|
+
"@typescript-eslint/no-unused-vars": [
|
|
97
|
+
"error",
|
|
98
|
+
{
|
|
99
|
+
argsIgnorePattern: "^_",
|
|
100
|
+
varsIgnorePattern: "^_",
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
// Allow empty functions (for test stubs)
|
|
104
|
+
"@typescript-eslint/no-empty-function": "off",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
files: ["**/*.{js,ts}"],
|
|
109
|
+
plugins: {
|
|
110
|
+
"check-file": checkFile,
|
|
111
|
+
},
|
|
112
|
+
rules: {
|
|
113
|
+
"check-file/filename-naming-convention": [
|
|
114
|
+
"error",
|
|
115
|
+
{
|
|
116
|
+
"**/*.{js,ts}": "KEBAB_CASE",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
ignoreMiddleExtensions: true,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
"check-file/folder-naming-convention": [
|
|
123
|
+
"error",
|
|
124
|
+
{
|
|
125
|
+
"**": "KEBAB_CASE",
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
// Playwright test files
|
|
131
|
+
{
|
|
132
|
+
...playwright.configs["flat/recommended"],
|
|
133
|
+
files: [
|
|
134
|
+
"**/*.spec.ts",
|
|
135
|
+
"**/*.test.ts",
|
|
136
|
+
"**/tests/**/*.ts",
|
|
137
|
+
"**/e2e/**/*.ts",
|
|
138
|
+
],
|
|
139
|
+
rules: {
|
|
140
|
+
...playwright.configs["flat/recommended"].rules,
|
|
141
|
+
// Playwright best practices
|
|
142
|
+
"playwright/expect-expect": "warn",
|
|
143
|
+
"playwright/max-nested-describe": ["warn", { max: 2 }],
|
|
144
|
+
"playwright/missing-playwright-await": "error",
|
|
145
|
+
"playwright/no-conditional-in-test": "warn",
|
|
146
|
+
"playwright/no-element-handle": "warn",
|
|
147
|
+
"playwright/no-eval": "error",
|
|
148
|
+
"playwright/no-focused-test": "error",
|
|
149
|
+
"playwright/no-force-option": "warn",
|
|
150
|
+
"playwright/no-page-pause": "warn",
|
|
151
|
+
"playwright/no-skipped-test": [
|
|
152
|
+
"warn",
|
|
153
|
+
{
|
|
154
|
+
allowConditional: true,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
"playwright/no-useless-await": "warn",
|
|
158
|
+
"playwright/no-useless-not": "warn",
|
|
159
|
+
"playwright/no-wait-for-selector": "warn",
|
|
160
|
+
"playwright/no-wait-for-timeout": "warn",
|
|
161
|
+
"playwright/prefer-web-first-assertions": "error",
|
|
162
|
+
"playwright/require-top-level-describe": "off",
|
|
163
|
+
"playwright/valid-describe-callback": "off",
|
|
164
|
+
"playwright/valid-expect": "error",
|
|
165
|
+
"playwright/valid-title": "warn",
|
|
166
|
+
// Custom restrictions
|
|
167
|
+
"no-restricted-syntax": [
|
|
168
|
+
"error",
|
|
169
|
+
{
|
|
170
|
+
selector: "CallExpression[callee.property.name='fixme'][callee.object.property.name='describe'][callee.object.object.name='test']",
|
|
171
|
+
message: "test.describe.fixme() is not valid. Use test.fixme() on individual tests instead.",
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
// Disallow console.log in tests (use test.info() instead)
|
|
175
|
+
"no-console": [
|
|
176
|
+
"warn",
|
|
177
|
+
{
|
|
178
|
+
allow: ["warn", "error"],
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
// Page Object Models - require class suffix
|
|
184
|
+
{
|
|
185
|
+
files: ["**/page-objects/**/*.ts", "**/page/**/*.ts", "**/pages/**/*.ts"],
|
|
186
|
+
rules: {
|
|
187
|
+
"@typescript-eslint/naming-convention": [
|
|
188
|
+
"error",
|
|
189
|
+
{
|
|
190
|
+
selector: "class",
|
|
191
|
+
format: ["PascalCase"],
|
|
192
|
+
suffix: ["Page", "Component", "PO"],
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
// Fixtures
|
|
198
|
+
{
|
|
199
|
+
files: ["**/fixtures/**/*.ts"],
|
|
200
|
+
rules: {
|
|
201
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
// Config files
|
|
205
|
+
{
|
|
206
|
+
files: ["playwright.config.ts", "**/*.config.ts"],
|
|
207
|
+
rules: {
|
|
208
|
+
"@typescript-eslint/naming-convention": "off",
|
|
209
|
+
"check-file/filename-naming-convention": "off",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PlaywrightTestConfig } from "@playwright/test";
|
|
2
|
+
/**
|
|
3
|
+
* Base Playwright configuration that can be extended by workspace-specific configs.
|
|
4
|
+
* Provides sensible defaults for RHDH plugin e2e testing.
|
|
5
|
+
*/
|
|
6
|
+
export declare const baseConfig: PlaywrightTestConfig;
|
|
7
|
+
/**
|
|
8
|
+
* Defines a workspace-specific config by merging with base config.
|
|
9
|
+
* Only allows overriding the projects configuration.
|
|
10
|
+
* @param overrides - Object containing projects to override
|
|
11
|
+
* @returns Merged Playwright configuration
|
|
12
|
+
*/
|
|
13
|
+
export declare function defineConfig(overrides?: Pick<PlaywrightTestConfig, "projects">): PlaywrightTestConfig;
|
|
14
|
+
//# sourceMappingURL=base-config.d.ts.map
|
|
@@ -0,0 +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,oBA4BxB,CAAC;AAEF;;;;;GAKG;AAEH,wBAAgB,YAAY,CAC1B,SAAS,GAAE,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAM,GACrD,oBAAoB,CAKtB"}
|