From dc5fa4fbed89dbf8f8a22fa380a2b3b17d3a6ecd Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 25 Feb 2026 15:31:09 +0200 Subject: [PATCH 01/26] feat(e2e): introduce e2e tests for bulk-import plugin Signed-off-by: Oleksandr Andriienko Co-authored-by: Gustavo Lira e Silva --- workspaces/bulk-import/e2e-tests/.yarnrc.yml | 1 + .../bulk-import/e2e-tests/eslint.config.js | 3 + workspaces/bulk-import/e2e-tests/package.json | 36 + .../e2e-tests/playwright.config.ts | 16 + .../tests/config/app-config-rhdh.yaml | 18 + .../tests/config/dynamic-plugins.yaml | 79 + .../e2e-tests/tests/config/rhdh-secrets.yaml | 7 + .../bulk-import/e2e-tests/tests/fixtures.ts | 78 + .../tests/scripts/install-orchestrator.sh | 486 +++ .../e2e-tests/tests/specs/bulk-import.spec.ts | 154 + .../bulk-import/e2e-tests/tsconfig.json | 4 + workspaces/bulk-import/e2e-tests/yarn.lock | 2611 +++++++++++++++++ 12 files changed, 3493 insertions(+) create mode 100644 workspaces/bulk-import/e2e-tests/.yarnrc.yml create mode 100644 workspaces/bulk-import/e2e-tests/eslint.config.js create mode 100644 workspaces/bulk-import/e2e-tests/package.json create mode 100644 workspaces/bulk-import/e2e-tests/playwright.config.ts create mode 100644 workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml create mode 100644 workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml create mode 100644 workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml create mode 100644 workspaces/bulk-import/e2e-tests/tests/fixtures.ts create mode 100755 workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh create mode 100644 workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts create mode 100644 workspaces/bulk-import/e2e-tests/tsconfig.json create mode 100644 workspaces/bulk-import/e2e-tests/yarn.lock diff --git a/workspaces/bulk-import/e2e-tests/.yarnrc.yml b/workspaces/bulk-import/e2e-tests/.yarnrc.yml new file mode 100644 index 000000000..3186f3f07 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/workspaces/bulk-import/e2e-tests/eslint.config.js b/workspaces/bulk-import/e2e-tests/eslint.config.js new file mode 100644 index 000000000..e83fbd7c7 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/eslint.config.js @@ -0,0 +1,3 @@ +import { createEslintConfig } from "rhdh-e2e-test-utils/eslint"; + +export default createEslintConfig(import.meta.dirname); diff --git a/workspaces/bulk-import/e2e-tests/package.json b/workspaces/bulk-import/e2e-tests/package.json new file mode 100644 index 000000000..45ab62949 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/package.json @@ -0,0 +1,36 @@ +{ + "name": "bulk-imprort-e2e-tests", + "version": "1.0.0", + "private": true, + "type": "module", + "engines": { + "node": ">=22", + "yarn": ">=3" + }, + "packageManager": "yarn@3.8.7", + "description": "E2E tests for Bulk import plugin", + "scripts": { + "test": "CI=true playwright test", + "report": "playwright show-report", + "test:ui": "playwright test --ui", + "test:headed": "playwright test --headed", + "lint:check": "eslint .", + "lint:fix": "eslint . --fix", + "prettier:check": "prettier --check .", + "prettier:fix": "prettier --write .", + "check": "tsc --noEmit && yarn lint:check && yarn prettier:check" + }, + "devDependencies": { + "@eslint/js": "^9.39.2", + "@playwright/test": "^1.56.1", + "@types/node": "^24.10.1", + "dotenv": "^16.4.7", + "eslint": "^9.39.2", + "eslint-plugin-check-file": "^3.3.1", + "eslint-plugin-playwright": "^2.4.0", + "prettier": "^3.7.4", + "rhdh-e2e-test-utils": "1.1.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.50.0" + } +} diff --git a/workspaces/bulk-import/e2e-tests/playwright.config.ts b/workspaces/bulk-import/e2e-tests/playwright.config.ts new file mode 100644 index 000000000..495ac5ab0 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/playwright.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "rhdh-e2e-test-utils/playwright-config"; +import dotenv from "dotenv"; + +dotenv.config({ path: `${import.meta.dirname}/.env` }); +/** + * Bulk import plugin e2e test configuration. + * Extends the base config from rhdh-e2e-test-utils. + */ +export default defineConfig({ + projects: [ + { + name: "bulk-import", + timeout: 30 * 60 * 1000, // 30 min + }, + ], +}); diff --git a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml new file mode 100644 index 000000000..7a0a91b18 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml @@ -0,0 +1,18 @@ +# rhdh app config file +# this file is used to merge with the default values of the rhdh app config + +integrations: + github: + - host: github.com + token: ${GITHUB_TOKEN} + # apps: + # - appId: ${GITHUB_APP_APP_ID} + # clientId: ${GITHUB_APP_CLIENT_ID} + # clientSecret: ${GITHUB_APP_CLIENT_SECRET} + # webhookUrl: ${GITHUB_APP_WEBHOOK_URL} + # webhookSecret: ${GITHUB_APP_WEBHOOK_SECRET} + # privateKey: | + # ${GITHUB_APP_PRIVATE_KEY} + gitlab: + - host: gitlab.com + token: temp diff --git a/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml b/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml new file mode 100644 index 000000000..7564e2dda --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml @@ -0,0 +1,79 @@ +plugins: + # bulk-import plugins + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic + disabled: false + pluginConfig: + bulkImport: + importAPI: orchestrator + orchestratorWorkflow: universal-pr + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import + disabled: false + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-bulk-import: + appIcons: + - name: bulkImportIcon + importName: BulkImportIcon + dynamicRoutes: + - path: /bulk-import/repositories + importName: BulkImportPage + menuItem: + icon: bulkImportIcon + text: Bulk import + + - package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-github-dynamic + disabled: true + + # Group: Orchestrator + - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator:bs_1.45.3__5.3.1" + disabled: false + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + textKey: menuItem.orchestrator + path: /orchestrator + entityTabs: + - path: /workflows + title: Workflows + titleKey: catalog.entityPage.workflows.title + mountPoint: entity.page.workflows + mountPoints: + - mountPoint: entity.page.workflows/cards + importName: OrchestratorCatalogTab + config: + layout: + gridColumn: 1 / -1 + if: + anyOf: + - IsOrchestratorCatalogTabAvailable + + - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-backend:bs_1.45.3__8.5.1" + disabled: false + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.orchestrator.svc.cluster.local:80 + + - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-scaffolder-backend-module-orchestrator:bs_1.45.3__1.3.1" + disabled: false + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.orchestrator.svc.cluster.local:80 + + - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-form-widgets:bs_1.45.3__1.6.0" + disabled: false + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: { } diff --git a/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml b/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml new file mode 100644 index 000000000..ef347f301 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: rhdh-secrets +type: Opaque +stringData: + GITHUB_TOKEN: $GITHUB_TOKEN diff --git a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts new file mode 100644 index 000000000..57f179f6a --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts @@ -0,0 +1,78 @@ +/** + * Custom Playwright test with RHDH fixtures. + * Uses standard @playwright/test so timeout is not overridden by rhdh-e2e-test-utils (500s). + */ +import { test as base } from "@playwright/test"; +import { RHDHDeployment } from "rhdh-e2e-test-utils/rhdh"; +import { LoginHelper, UIhelper } from "rhdh-e2e-test-utils/helpers"; +import { $ } from "rhdh-e2e-test-utils/utils"; + +const BEFORE_ALL_TIMEOUT_MS = 30 * 60 * 1000; // 30 min for orchestrator + RHDH deploy + +export const test = base.extend<{ + rhdhDeploymentWorker: RHDHDeployment; + rhdh: RHDHDeployment; + uiHelper: UIhelper; + loginHelper: LoginHelper; + baseURL: string; +}>({ + rhdhDeploymentWorker: [ + async ({}, use, workerInfo) => { + const projectName = workerInfo.project.name; + console.log( + `Deploying rhdh for plugin ${projectName} in namespace ${projectName}`, + ); + const rhdhDeployment = new RHDHDeployment(projectName); + + try { + base.setTimeout(BEFORE_ALL_TIMEOUT_MS); + + await rhdhDeployment.configure({ auth: "keycloak" }); + + const orchestratorNamespace = "orchestrator"; + await $`bash tests/scripts/install-orchestrator.sh ${orchestratorNamespace}`; + + await rhdhDeployment.deploy({ timeoutMs: BEFORE_ALL_TIMEOUT_MS }); + + await use(rhdhDeployment); + } finally { + if (process.env.CI) { + console.log(`Deleting namespace ${projectName}`); + await rhdhDeployment.teardown(); + } + } + }, + { scope: "worker", auto: true }, + ], + + rhdh: [ + async ({ rhdhDeploymentWorker }, use) => { + await use(rhdhDeploymentWorker); + }, + { scope: "test", auto: true }, + ], + + uiHelper: [ + async ({ page }, use) => { + await use(new UIhelper(page)); + }, + { scope: "test" }, + ], + + loginHelper: [ + async ({ page }, use) => { + await use(new LoginHelper(page)); + }, + { scope: "test" }, + ], + + baseURL: [ + async ({ rhdhDeploymentWorker }, use) => { + await use(rhdhDeploymentWorker.rhdhUrl); + }, + { scope: "test" }, + ], +}); + +export { expect } from "@playwright/test"; +export type { Page } from "@playwright/test"; diff --git a/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh b/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh new file mode 100755 index 000000000..6c532d49d --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh @@ -0,0 +1,486 @@ +#!/bin/bash +# +# Standalone script to install the orchestrator (Serverless Logic / SonataFlow) +# on OpenShift. +# +# Usage: ./install-orchestrator.sh [namespace] +# Default namespace: orchestrator +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export NAME_SPACE="${1:-${NAME_SPACE:-orchestrator}}" + +# --------------------------------------------------------------------------- +# Logging +# --------------------------------------------------------------------------- +if [[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]]; then + : "${LOG_NO_COLOR:=false}" +else + : "${LOG_NO_COLOR:=true}" +fi +: "${LOG_LEVEL:=INFO}" + +log::timestamp() { date -u '+%Y-%m-%dT%H:%M:%SZ'; } +log::level_value() { + local level; level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" + case "${level}" in DEBUG) echo 0 ;; INFO) echo 1 ;; WARN|WARNING) echo 2 ;; ERROR|ERR) echo 3 ;; *) echo 1 ;; esac +} +log::should_log() { + local requested_level config_level + requested_level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" + config_level="$(echo "${LOG_LEVEL}" | tr '[:lower:]' '[:upper:]')" + [[ "$(log::level_value "${requested_level}")" -ge "$(log::level_value "${config_level}")" ]] +} +log::reset_code() { [[ "${LOG_NO_COLOR}" == "true" ]] && printf '' || printf '\033[0m'; } +log::color_for_level() { + [[ "${LOG_NO_COLOR}" == "true" ]] && { printf ''; return 0; } + local level; level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" + case "${level}" in + DEBUG) printf '\033[36m' ;; INFO) printf '\033[34m' ;; WARN|WARNING) printf '\033[33m' ;; + ERROR|ERR) printf '\033[31m' ;; SUCCESS) printf '\033[32m' ;; SECTION) printf '\033[35m\033[1m' ;; + *) printf '\033[37m' ;; + esac +} +log::icon_for_level() { + local level; level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" + case "${level}" in DEBUG) printf '🐞' ;; INFO) printf 'ℹ' ;; WARN|WARNING) printf '⚠' ;; ERROR|ERR) printf '❌' ;; SUCCESS) printf '✓' ;; *) printf '-' ;; esac +} +log::emit_line() { + local level="$1" icon="$2" line="$3" color reset timestamp + log::should_log "${level}" || return 0 + timestamp="$(log::timestamp)" + color="$(log::color_for_level "${level}")" + reset="$(log::reset_code)" + printf '%s[%s] %s %s%s\n' "${color}" "${timestamp}" "${icon}" "${line}" "${reset}" >&2 +} +log::emit() { + local level="$1"; shift + local icon message; icon="$(log::icon_for_level "${level}")"; message="${*:-}" + [[ -z "${message}" ]] && return 0 + while IFS= read -r line; do log::emit_line "${level}" "${icon}" "${line}"; done <<< "${message}" +} +log::debug() { log::emit "DEBUG" "$@"; } +log::info() { log::emit "INFO" "$@"; } +log::warn() { log::emit "WARN" "$@"; } +log::error() { log::emit "ERROR" "$@"; } +log::success() { log::emit "SUCCESS" "$@"; } + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +# Escape double quotes for yq string values (yq v4 mikefarah) +escape_yq() { printf '%s' "$1" | sed 's/"/\\"/g'; } + +# --------------------------------------------------------------------------- +# Operator subscription and status +# --------------------------------------------------------------------------- +install_subscription() { + local name=$1 namespace=$2 channel=$3 package=$4 source_name=$5 source_namespace=$6 + oc apply -f - << EOD +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: $name + namespace: $namespace +spec: + channel: $channel + installPlanApproval: Automatic + name: $package + source: $source_name + sourceNamespace: $source_namespace +EOD +} + +check_operator_status() { + local timeout=${1:-300} namespace=$2 operator_name=$3 expected_status=${4:-Succeeded} + log::info "Checking operator '${operator_name}' in '${namespace}' (timeout ${timeout}s, expected: ${expected_status})" + timeout "${timeout}" bash -c " + while true; do + CURRENT_PHASE=\$(oc get csv -n '${namespace}' -o jsonpath='{.items[?(@.spec.displayName==\"${operator_name}\")].status.phase}') + echo \"[check_operator_status] Phase: \${CURRENT_PHASE}\" >&2 + [[ \"\${CURRENT_PHASE}\" == \"${expected_status}\" ]] && echo \"[check_operator_status] Operator reached ${expected_status}\" >&2 && break + sleep 10 + done + " || { log::error "Operator did not reach ${expected_status} in time."; return 1; } +} + +install_serverless_logic_ocp_operator() { + install_subscription logic-operator-rhel8 openshift-operators alpha logic-operator-rhel8 redhat-operators openshift-marketplace +} +waitfor_serverless_logic_ocp_operator() { + check_operator_status 500 openshift-operators "OpenShift Serverless Logic Operator (Alpha)" Succeeded +} + +install_serverless_ocp_operator() { + install_subscription serverless-operator openshift-operators stable serverless-operator redhat-operators openshift-marketplace +} +waitfor_serverless_ocp_operator() { + check_operator_status 300 openshift-operators "Red Hat OpenShift Serverless" Succeeded +} + +# --------------------------------------------------------------------------- +# Namespace +# --------------------------------------------------------------------------- +force_delete_namespace() { + local project=$1 timeout_seconds=${2:-120} elapsed=0 sleep_interval=2 + log::warn "Force deleting namespace ${project}" + oc get namespace "$project" -o json | jq '.spec = {"finalizers":[]}' | oc replace --raw "/api/v1/namespaces/$project/finalize" -f - + while oc get namespace "$project" &>/dev/null; do + [[ $elapsed -ge $timeout_seconds ]] && { log::warn "Timeout deleting ${project}"; return 1; } + sleep $sleep_interval + elapsed=$((elapsed + sleep_interval)) + done + log::success "Namespace '${project}' deleted." +} + +delete_namespace() { + local project=$1 + if oc get namespace "$project" &>/dev/null; then + log::warn "Deleting namespace ${project}..." + oc delete namespace "$project" --grace-period=0 --force || true + if oc get namespace "$project" -o jsonpath='{.status.phase}' 2>/dev/null | grep -q Terminating; then + force_delete_namespace "$project" + fi + fi +} + +configure_namespace() { + local project=$1 + log::warn "Recreating namespace: $project" + delete_namespace "$project" + oc create namespace "${project}" || { log::error "Failed to create namespace ${project}"; exit 1; } + oc config set-context --current --namespace="${project}" || { log::error "Failed to set context"; exit 1; } + log::info "Namespace ${project} is ready." +} + +# --------------------------------------------------------------------------- +# Deployment wait +# --------------------------------------------------------------------------- +wait_for_deployment() { + local namespace=$1 resource_name=$2 timeout_minutes=${3:-5} check_interval=${4:-10} + [[ -z "$namespace" || -z "$resource_name" ]] && { log::error "wait_for_deployment: namespace and resource_name required"; return 1; } + local max_attempts=$((timeout_minutes * 60 / check_interval)) + log::info "Waiting for '$resource_name' in '$namespace' (timeout ${timeout_minutes}m)..." + for ((i = 1; i <= max_attempts; i++)); do + local pod_name + pod_name=$(oc get pods -n "$namespace" 2>/dev/null | grep "$resource_name" | awk '{print $1}' | head -n 1) + if [[ -n "$pod_name" ]]; then + local is_ready + is_ready=$(oc get pod "$pod_name" -n "$namespace" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null) + if [[ "$is_ready" == "True" ]] && oc get pod "$pod_name" -n "$namespace" 2>/dev/null | grep -q Running; then + log::success "Pod '$pod_name' is ready" + return 0 + fi + fi + sleep "$check_interval" + done + log::error "Timeout waiting for $resource_name" + return 1 +} + +# --------------------------------------------------------------------------- +# PostgreSQL (simple deployment for orchestrator) +# --------------------------------------------------------------------------- +create_simple_postgres_deployment() { + local namespace=$1 postgres_name="backstage-psql" + if oc get deployment "$postgres_name" -n "$namespace" &>/dev/null; then + log::info "PostgreSQL '$postgres_name' already exists" + return 0 + fi + log::info "Creating PostgreSQL '$postgres_name' in '$namespace'" + oc create secret generic "${postgres_name}-secret" -n "$namespace" \ + --from-literal=POSTGRESQL_USER=postgres \ + --from-literal=POSTGRESQL_PASSWORD=postgres \ + --from-literal=POSTGRESQL_DATABASE=postgres \ + --from-literal=POSTGRES_USER=postgres \ + --from-literal=POSTGRES_PASSWORD=postgres \ + --from-literal=POSTGRES_DB=postgres \ + --dry-run=client -o yaml | oc apply -f - -n "$namespace" || true + + oc apply -f - -n "$namespace" << EOF +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: ${postgres_name}-pvc + namespace: ${namespace} +spec: + accessModes: [ReadWriteOnce] + resources: { requests: { storage: 1Gi } } +EOF + + oc apply -f - -n "$namespace" << EOF +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: ${postgres_name} + namespace: ${namespace} +spec: + serviceName: ${postgres_name} + replicas: 1 + selector: { matchLabels: { app: ${postgres_name} } } + template: + metadata: { labels: { app: ${postgres_name} } } + spec: + containers: + - name: postgres + image: registry.redhat.io/rhel9/postgresql-15:latest + env: + - name: POSTGRESQL_USER + valueFrom: { secretKeyRef: { name: ${postgres_name}-secret, key: POSTGRESQL_USER } } + - name: POSTGRESQL_PASSWORD + valueFrom: { secretKeyRef: { name: ${postgres_name}-secret, key: POSTGRESQL_PASSWORD } } + - name: POSTGRESQL_DATABASE + valueFrom: { secretKeyRef: { name: ${postgres_name}-secret, key: POSTGRESQL_DATABASE } } + ports: [ { containerPort: 5432, name: postgres } ] + volumeMounts: [ { name: postgres-data, mountPath: /var/lib/pgsql/data } ] + livenessProbe: + exec: { command: [ /usr/libexec/check-container, --live ] } + initialDelaySeconds: 120 + periodSeconds: 10 + readinessProbe: + exec: { command: [ /usr/libexec/check-container ] } + initialDelaySeconds: 5 + periodSeconds: 10 + volumes: [ { name: postgres-data, persistentVolumeClaim: { claimName: ${postgres_name}-pvc } } ] +EOF + + oc apply -f - -n "$namespace" << EOF +apiVersion: v1 +kind: Service +metadata: + name: ${postgres_name} + namespace: ${namespace} +spec: + selector: { app: ${postgres_name} } + ports: [ { name: postgres, port: 5432, targetPort: 5432 } ] + type: ClusterIP +EOF + + log::info "Waiting for PostgreSQL StatefulSet..." + oc wait statefulset "$postgres_name" -n "$namespace" --for=jsonpath='{.status.readyReplicas}'=1 --timeout=300s || true + sleep 5 + oc exec -n "$namespace" statefulset/"$postgres_name" -- psql -U postgres -c "CREATE DATABASE backstage_plugin_orchestrator;" 2>/dev/null || log::warn "Orchestrator DB may already exist" + log::success "PostgreSQL deployment created." +} + +# --------------------------------------------------------------------------- +# SonataFlow platform +# --------------------------------------------------------------------------- +create_sonataflow_platform() { + local namespace=$1 postgres_secret_name=$2 postgres_service_name=$3 + if ! oc get crd sonataflowplatforms.sonataflow.org &>/dev/null && ! oc get crd sonataflowplatform.sonataflow.org &>/dev/null; then + log::error "SonataFlowPlatform CRD not found. Install Serverless Logic Operator first." + return 1 + fi + if oc get sonataflowplatform sonataflow-platform -n "$namespace" &>/dev/null || oc get sfp sonataflow-platform -n "$namespace" &>/dev/null; then + log::info "SonataFlowPlatform already exists" + return 0 + fi + log::info "Creating SonataFlowPlatform in '$namespace'" + oc apply -f - -n "$namespace" << EOF +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform + namespace: ${namespace} +spec: + services: + dataIndex: + persistence: + postgresql: + secretRef: { name: ${postgres_secret_name}, userKey: POSTGRES_USER, passwordKey: POSTGRES_PASSWORD } + serviceRef: { name: ${postgres_service_name}, namespace: ${namespace}, port: 5432, databaseName: backstage_plugin_orchestrator } + jobService: + persistence: + postgresql: + secretRef: { name: ${postgres_secret_name}, userKey: POSTGRES_USER, passwordKey: POSTGRES_PASSWORD } + serviceRef: { name: ${postgres_service_name}, namespace: ${namespace}, port: 5432, databaseName: backstage_plugin_orchestrator } +EOF + local attempt=0 max_attempts=60 + while [ $attempt -lt $max_attempts ]; do + if oc get deployment sonataflow-platform-data-index-service -n "$namespace" &>/dev/null && \ + oc get deployment sonataflow-platform-jobs-service -n "$namespace" &>/dev/null; then + log::success "SonataFlowPlatform services created" + wait_for_deployment "$namespace" sonataflow-platform-data-index-service 20 || true + wait_for_deployment "$namespace" sonataflow-platform-jobs-service 20 || true + log::success "SonataFlowPlatform ready." + return 0 + fi + attempt=$((attempt + 1)) + [ $((attempt % 10)) -eq 0 ] && log::info "Waiting for SonataFlowPlatform... ($attempt/$max_attempts)" + sleep 5 + done + log::warn "SonataFlowPlatform services did not appear in time." +} + +# --------------------------------------------------------------------------- +# Orchestrator connection info +# --------------------------------------------------------------------------- +print_orchestrator_connection_info() { + local namespace=$1 + local data_index_service="sonataflow-platform-data-index-service" + local service_url="http://${data_index_service}.${namespace}.svc.cluster.local" + log::info "==========================================" + log::info "Orchestrator Plugin Connection Information" + log::info "==========================================" + log::info "Namespace: ${namespace}" + log::info "Internal URL for Orchestrator Backend Plugin: ${service_url}" + log::info "dynamic-plugins.yaml: pluginConfig.orchestrator.dataIndexService.url: ${service_url}" + if oc get svc "${data_index_service}" -n "${namespace}" &>/dev/null; then + local port; port=$(oc get svc "${data_index_service}" -n "${namespace}" -o jsonpath='{.spec.ports[0].port}' 2>/dev/null || echo "8080") + log::info "Service: ${data_index_service}, port: ${port}" + else + log::warn "Service '${data_index_service}' not found yet." + fi + log::info "==========================================" +} + +# --------------------------------------------------------------------------- +# Wait for SonataFlow CRDs +# --------------------------------------------------------------------------- +wait_for_sonataflow_crds() { + log::info "Waiting for SonataFlow CRDs..." + local attempt=0 max_attempts=60 + while [ $attempt -lt $max_attempts ]; do + if oc get crd sonataflows.sonataflow.org &>/dev/null; then + log::success "SonataFlow CRD is available." + return 0 + fi + attempt=$((attempt + 1)) + [ $((attempt % 6)) -eq 0 ] && log::info "Waiting for sonataflows.sonataflow.org... ($attempt/$max_attempts)" + sleep 5 + done + log::error "Timed out waiting for SonataFlow CRD." + return 1 +} + +# --------------------------------------------------------------------------- +# Deploy orchestrator workflows (operator path: git clone + helm greeting) +# Uses local yaml/ if present, otherwise clones repo. +# --------------------------------------------------------------------------- +deploy_orchestrator_workflows_operator() { + local namespace=$1 + local WORKFLOW_REPO="https://github.com/AndrienkoAleksandr/serverless-workflows.git" + local WORKFLOW_DIR="/tmp/serverless-workflows" + local LOCAL_MANIFESTS="${SCRIPT_DIR}/yaml" + + # PostgreSQL + if ! oc get statefulset backstage-psql -n "$namespace" &>/dev/null && ! oc get deployment backstage-psql -n "$namespace" &>/dev/null; then + log::info "Creating simple PostgreSQL deployment..." + create_simple_postgres_deployment "$namespace" + else + log::info "PostgreSQL found, waiting for ready..." + if oc get statefulset backstage-psql -n "$namespace" &>/dev/null; then + oc wait statefulset backstage-psql -n "$namespace" --for=jsonpath='{.status.readyReplicas}'=1 --timeout=300s || true + else + wait_for_deployment "$namespace" backstage-psql 15 || true + fi + fi + + local pqsl_secret_name pqsl_svc_name pqsl_user_key pqsl_password_key sonataflow_db + pqsl_secret_name=$(oc get secrets -n "$namespace" -o name 2>/dev/null | grep "backstage-psql" | grep "secret" | head -1 | sed 's|secret\/||') + pqsl_svc_name=$(oc get svc -n "$namespace" -o name 2>/dev/null | grep "backstage-psql" | grep -v secret | head -1 | sed 's|service\/||') + pqsl_secret_name="${pqsl_secret_name:-backstage-psql-secret}" + pqsl_svc_name="${pqsl_svc_name:-backstage-psql}" + pqsl_user_key="POSTGRES_USER" + pqsl_password_key="POSTGRES_PASSWORD" + sonataflow_db="backstage_plugin_orchestrator" + log::info "PostgreSQL secret: $pqsl_secret_name, service: $pqsl_svc_name" + + if ! oc get sonataflowplatform sonataflow-platform -n "$namespace" &>/dev/null && ! oc get sfp sonataflow-platform -n "$namespace" &>/dev/null; then + create_sonataflow_platform "$namespace" "$pqsl_secret_name" "$pqsl_svc_name" + else + log::info "SonataFlowPlatform already exists" + wait_for_deployment "$namespace" sonataflow-platform-data-index-service 20 || true + wait_for_deployment "$namespace" sonataflow-platform-jobs-service 20 || true + fi + + if ! oc get crd sonataflows.sonataflow.org &>/dev/null; then + log::error "SonataFlow CRD not found." + return 1 + fi + + # Wait for 2 SonataFlow resources + timeout 30s bash -c " + until [[ \$(oc get sf -n $namespace --no-headers 2>/dev/null | wc -l) -ge 2 ]]; do + echo \"Waiting for sf resources... \$(oc get sf -n $namespace --no-headers 2>/dev/null | wc -l)\" + sleep 5 + done + " || true + + + # Prefer local yaml/ if it exists and has content + if [[ -d "${LOCAL_MANIFESTS}" ]] && [[ -n "$(ls -A "${LOCAL_MANIFESTS}" 2>/dev/null)" ]]; then + log::info "Using local workflow manifests from ${LOCAL_MANIFESTS}" + # Apply all YAMLs in yaml/ with correct namespace + for f in "${LOCAL_MANIFESTS}"/*.yaml "${LOCAL_MANIFESTS}"/*.yml; do + [[ -e "$f" ]] && oc apply -f "$f" -n "$namespace" && log::info "Applied $(basename "$f")" + done + else + log::info "Cloning workflow repo..." + rm -rf "${WORKFLOW_DIR}" + git clone --single-branch --branch bulk-import-workflow-sample "${WORKFLOW_REPO}" "${WORKFLOW_DIR}" + local WORKFLOW_MANIFESTS="${WORKFLOW_DIR}/workflows/experimentals/bulk-import-git-repos/manifests" + if [[ -d "${WORKFLOW_MANIFESTS}" ]]; then + log::info "Applying workflow manifests from repo..." + + snToDbPatch="${WORKFLOW_MANIFESTS}/04-sonataflow_universal-pr.yaml" + yq eval -i '.spec.persistence.postgresql.secretRef.name = "'"$(escape_yq "$pqsl_secret_name")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.secretRef.userKey = "'"$(escape_yq "$pqsl_user_key")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.secretRef.passwordKey = "'"$(escape_yq "$pqsl_password_key")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.serviceRef.name = "'"$(escape_yq "$pqsl_svc_name")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.serviceRef.namespace = "'"$(escape_yq "$namespace")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.serviceRef.databaseName = "'"$(escape_yq "$sonataflow_db")"'"' "$snToDbPatch" + + oc apply -f "${WORKFLOW_MANIFESTS}" -n "$namespace" + else + log::warn "Manifests path not found in repo: ${WORKFLOW_MANIFESTS}" + fi + fi + + wait_for_deployment "$namespace" universal-pr 5 || true + log::info "Orchestrator workflows deployment done." +} + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- +main() { + log::info "Starting orchestrator deployment for namespace: ${NAME_SPACE}" + + if ! oc whoami &>/dev/null && ! kubectl cluster-info &>/dev/null; then + log::error "Not logged into OpenShift/Kubernetes cluster" + return 1 + fi + + log::info "Checking Serverless operators..." + if ! oc get subscription serverless-operator -n openshift-operators &>/dev/null; then + log::info "Installing OpenShift Serverless Operator..." + install_serverless_ocp_operator + else + log::info "OpenShift Serverless Operator already installed" + fi + + if ! oc get subscription logic-operator-rhel8 -n openshift-operators &>/dev/null; then + log::info "Installing OpenShift Serverless Logic Operator..." + install_serverless_logic_ocp_operator + else + log::info "OpenShift Serverless Logic Operator already installed" + fi + + log::info "Waiting for operators to be ready..." + waitfor_serverless_ocp_operator + waitfor_serverless_logic_ocp_operator + wait_for_sonataflow_crds + + configure_namespace "${NAME_SPACE}" + log::info "Deploying orchestrator workflows..." + deploy_orchestrator_workflows_operator "${NAME_SPACE}" + print_orchestrator_connection_info "${NAME_SPACE}" + + log::success "Orchestrator deployment completed successfully!" +} + +main "$@" diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts new file mode 100644 index 000000000..c3b7f3b5f --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -0,0 +1,154 @@ +import { APIHelper } from "rhdh-e2e-test-utils/helpers"; +import { test, expect, Page } from "../fixtures"; + +export const WAIT_OBJECTS = { + MuiLinearProgress: 'div[class*="MuiLinearProgress-root"]', + MuiCircularProgress: '[class*="MuiCircularProgress-root"]', +}; + +test.describe("Bulk import tests", () => { + const catalogRepoName = `janus-test-1-bulk-import-test-${Date.now()}`; + const githubOrg = 'cloud-eda'; + const catalogRepoDetails = { + name: catalogRepoName, + url: `github.com/${githubOrg}/${catalogRepoName}`, + org: `github.com/${githubOrg}`, + owner: githubOrg, + }; + + test.beforeAll(async () => { + test.info().annotations.push({ + type: "component", + description: "plugins", + }); + + // Create the repository with catalog-info.yaml file dynamically + await APIHelper.createGitHubRepoWithFile( + catalogRepoDetails.owner, + catalogRepoDetails.name, + "test", + "ABC", + ); + }); + + test.beforeEach(async ({ loginHelper }) => { + // Login before each test + await loginHelper.loginAsKeycloakUser(); + }); + + test("should display plugin page", async ({ page, uiHelper }) => { + // Navigate to your plugin + await uiHelper.openSidebar("Bulk import"); + + // Verify the page loaded + await uiHelper.verifyHeading("Bulk import"); + + // Add more assertions as needed + await expect(page.locator("text=Selected repositories (0)")).toBeVisible(); + + await expect( + page.getByText("Source control tool", { exact: true }), + ).toBeVisible(); + await page + .getByLabel("Importing requires approval.") + .getByTestId("HelpOutlineIcon") + .hover(); + await expect( + page.getByRole("tooltip", { name: "Importing requires approval." }), // remove this tooltip in the code... + ).toBeVisible(); + await expect(page.getByRole("radio", { name: "GitHub" })).toBeChecked(); + await page.getByRole("radio", { name: "GitLab" }).check(); + await expect(page.getByRole("radio", { name: "GitLab" })).toBeChecked(); + await page.getByRole("radio", { name: "GitHub" }).check(); + + // Verify bulk import form structure (avoid full aria snapshot — table data is dynamic from GitHub) + const article = page.getByRole("article"); + await expect(article).toBeVisible(); + await expect(article.getByRole("table")).toBeVisible(); + await expect( + article.getByRole("columnheader", { name: "Name" }), + ).toBeVisible(); + await expect( + article.getByRole("columnheader", { name: "URL" }), + ).toBeVisible(); + await expect( + article.getByRole("columnheader", { name: "Organization" }), + ).toBeVisible(); + await expect( + article.getByRole("columnheader", { name: "Status" }), + ).toBeVisible(); + await expect( + article.getByRole("checkbox", { name: "select all repositories" }), + ).toBeVisible(); + await expect(article.getByRole("button", { name: "Import" })).toBeVisible(); + await expect(article.getByRole("link", { name: "Cancel" })).toBeVisible(); + }); + + test("should interact with plugin features", async ({ page, uiHelper }) => { + await uiHelper.openSidebar("Bulk import"); + + // Wait to ensure the repo will appear in the Bulk Import UI + await expect(async () => { + await page.reload(); + // whait why progressors will dissapear + for (const item of Object.values(WAIT_OBJECTS)) { + await page.waitForSelector(item, { + state: "hidden", + timeout: 12000, + }); + } + + await uiHelper.searchInputPlaceholder(catalogRepoDetails.name); + await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, []); + }).toPass({ + intervals: [5_000], + timeout: 40_000, + }); + + await page + .locator(`tr:has(:text-is("${catalogRepoDetails.name}"))`) + .getByRole("checkbox") + .check(); + await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, [ + catalogRepoDetails.url, + ]); + + await expect(await uiHelper.clickButton("Import")).toBeDisabled({ + timeout: 10000, + }); + + const workflowPage = await clickLinkWithNewTab(page, "View workflow"); + await expect(workflowPage.getByRole("link", { name: "PR_URL" })).toBeVisible( + { timeout: 10000 }, + ); + }); + + test.afterAll(async () => { + try { + // Delete the dynamically created GitHub repository with catalog-info.yaml + await APIHelper.deleteGitHubRepo( + catalogRepoDetails.owner, + catalogRepoDetails.name, + ); + + console.log( + `[Cleanup] Deleted GitHub repository: ${catalogRepoDetails.name}`, + ); + } catch (error) { + console.error(`[Cleanup] Final cleanup failed: ${(error as any).message}`); + } + }); +}); + +/** Clicks a link that opens in a new tab and returns the new page (so you can assert on it). */ +async function clickLinkWithNewTab( + page: Page, + name: string | RegExp, +): Promise { + const [newPage] = await Promise.all([ + page.context().waitForEvent("page"), + page.getByRole("link", { name }).click(), + ]); + await newPage.waitForLoadState(); + return newPage; +} diff --git a/workspaces/bulk-import/e2e-tests/tsconfig.json b/workspaces/bulk-import/e2e-tests/tsconfig.json new file mode 100644 index 000000000..f875a2050 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "rhdh-e2e-test-utils/tsconfig", + "include": ["**/*.ts"] +} diff --git a/workspaces/bulk-import/e2e-tests/yarn.lock b/workspaces/bulk-import/e2e-tests/yarn.lock new file mode 100644 index 000000000..93eef77a5 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/yarn.lock @@ -0,0 +1,2611 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"@axe-core/playwright@npm:^4.11.0": + version: 4.11.0 + resolution: "@axe-core/playwright@npm:4.11.0" + dependencies: + axe-core: ~4.11.0 + peerDependencies: + playwright-core: ">= 1.0.0" + checksum: 036d13cb73f9c3bdcff039caa8a3f4ae9c2fffeb41855053dd78f72d98d1635b0d7eec38ff5087beaa5e15c99e060a771d1b449f08df58694f02781d54aa155c + languageName: node + linkType: hard + +"@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": + version: 4.9.1 + resolution: "@eslint-community/eslint-utils@npm:4.9.1" + dependencies: + eslint-visitor-keys: ^3.4.3 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 0a27c2d676c4be6b329ebb5dd8f6c5ef5fae9a019ff575655306d72874bb26f3ab20e0b241a5f086464bb1f2511ca26a29ff6f80c1e2b0b02eca4686b4dfe1b5 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2": + version: 4.12.2 + resolution: "@eslint-community/regexpp@npm:4.12.2" + checksum: 1770bc81f676a72f65c7200b5675ff7a349786521f30e66125faaf767fde1ba1c19c3790e16ba8508a62a3933afcfc806a893858b3b5906faf693d862b9e4120 + languageName: node + linkType: hard + +"@eslint/config-array@npm:^0.21.1": + version: 0.21.1 + resolution: "@eslint/config-array@npm:0.21.1" + dependencies: + "@eslint/object-schema": ^2.1.7 + debug: ^4.3.1 + minimatch: ^3.1.2 + checksum: fc5b57803b059f7c1f62950ef83baf045a01887fc00551f9e87ac119246fcc6d71c854a7f678accc79cbf829ed010e8135c755a154b0f54b129c538950cd7e6a + languageName: node + linkType: hard + +"@eslint/config-helpers@npm:^0.4.2": + version: 0.4.2 + resolution: "@eslint/config-helpers@npm:0.4.2" + dependencies: + "@eslint/core": ^0.17.0 + checksum: 63ff6a0730c9fff2edb80c89b39b15b28d6a635a1c3f32cf0d7eb3e2625f2efbc373c5531ae84e420ae36d6e37016dd40c365b6e5dee6938478e9907aaadae0b + languageName: node + linkType: hard + +"@eslint/core@npm:^0.17.0": + version: 0.17.0 + resolution: "@eslint/core@npm:0.17.0" + dependencies: + "@types/json-schema": ^7.0.15 + checksum: ff9b5b4987f0bae4f2a4cfcdc7ae584ad3b0cb58526ca562fb281d6837700a04c7f3c86862e95126462318f33f60bf38e1cb07ed0e2449532d4b91cd5f4ab1f2 + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^3.3.1": + version: 3.3.3 + resolution: "@eslint/eslintrc@npm:3.3.3" + dependencies: + ajv: ^6.12.4 + debug: ^4.3.2 + espree: ^10.0.1 + globals: ^14.0.0 + ignore: ^5.2.0 + import-fresh: ^3.2.1 + js-yaml: ^4.1.1 + minimatch: ^3.1.2 + strip-json-comments: ^3.1.1 + checksum: d1e16e47f1bb29af32defa597eaf84ac0ff8c06760c0a5f4933c604cd9d931d48c89bed96252222f22abac231898a53bc41385a5e6129257f0060b5ec431bdb2 + languageName: node + linkType: hard + +"@eslint/js@npm:9.39.2, @eslint/js@npm:^9.39.1, @eslint/js@npm:^9.39.2": + version: 9.39.2 + resolution: "@eslint/js@npm:9.39.2" + checksum: 362aa447266fa5717e762b2b252f177345cb0d7b2954113db9773b3a28898f7cbbc807e07f8078995e6da3f62791f7c5fa2c03517b7170a8e76613cf7fd83c92 + languageName: node + linkType: hard + +"@eslint/object-schema@npm:^2.1.7": + version: 2.1.7 + resolution: "@eslint/object-schema@npm:2.1.7" + checksum: fc5708f192476956544def13455d60fd1bafbf8f062d1e05ec5c06dd470b02078eaf721e696a8b31c1c45d2056723a514b941ae5eea1398cc7e38eba6711a775 + languageName: node + linkType: hard + +"@eslint/plugin-kit@npm:^0.4.1": + version: 0.4.1 + resolution: "@eslint/plugin-kit@npm:0.4.1" + dependencies: + "@eslint/core": ^0.17.0 + levn: ^0.4.1 + checksum: 3f4492e02a3620e05d46126c5cfeff5f651ecf33466c8f88efb4812ae69db5f005e8c13373afabc070ecca7becd319b656d6670ad5093f05ca63c2a8841d99ba + languageName: node + linkType: hard + +"@humanfs/core@npm:^0.19.1": + version: 0.19.1 + resolution: "@humanfs/core@npm:0.19.1" + checksum: 611e0545146f55ddfdd5c20239cfb7911f9d0e28258787c4fc1a1f6214250830c9367aaaeace0096ed90b6739bee1e9c52ad5ba8adaf74ab8b449119303babfe + languageName: node + linkType: hard + +"@humanfs/node@npm:^0.16.6": + version: 0.16.7 + resolution: "@humanfs/node@npm:0.16.7" + dependencies: + "@humanfs/core": ^0.19.1 + "@humanwhocodes/retry": ^0.4.0 + checksum: 7d2a396a94d80158ce320c0fd7df9aebb82edb8b667e5aaf8f87f4ca50518d0941ca494e0cd68e06b061e777ce5f7d26c45f93ac3fa9f7b11fd1ff26e3cd1440 + languageName: node + linkType: hard + +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 0fd22007db8034a2cdf2c764b140d37d9020bbfce8a49d3ec5c05290e77d4b0263b1b972b752df8c89e5eaa94073408f2b7d977aed131faf6cf396ebb5d7fb61 + languageName: node + linkType: hard + +"@humanwhocodes/retry@npm:^0.4.0, @humanwhocodes/retry@npm:^0.4.2": + version: 0.4.3 + resolution: "@humanwhocodes/retry@npm:0.4.3" + checksum: d423455b9d53cf01f778603404512a4246fb19b83e74fe3e28c70d9a80e9d4ae147d2411628907ca983e91a855a52535859a8bb218050bc3f6dbd7a553b7b442 + languageName: node + linkType: hard + +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 102fbc6d2c0d5edf8f6dbf2b3feb21695a21bc850f11bc47c4f06aa83bd8884fde3fe9d6d797d619901d96865fdcb4569ac2a54c937992c48885c5e3d9967fe8 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.0": + version: 5.0.0 + resolution: "@isaacs/brace-expansion@npm:5.0.0" + dependencies: + "@isaacs/balanced-match": ^4.0.1 + checksum: d7a3b8b0ddbf0ccd8eeb1300e29dd0a0c02147e823d8138f248375a365682360620895c66d113e05ee02389318c654379b0e538b996345b83c914941786705b1 + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: ^7.0.4 + checksum: 5d36d289960e886484362d9eb6a51d1ea28baed5f5d0140bbe62b99bac52eaf06cc01c2bc0d3575977962f84f6b2c4387b043ee632216643d4787b0999465bf2 + languageName: node + linkType: hard + +"@jsep-plugin/assignment@npm:^1.3.0": + version: 1.3.0 + resolution: "@jsep-plugin/assignment@npm:1.3.0" + peerDependencies: + jsep: ^0.4.0||^1.0.0 + checksum: 5549497d403a6c00969d61202a6d3dafc5a349929d190a524363dcfacb3436dbda3d9f88b2ec1330247a594ad3c5f1c17b0997769d0b206802281bad6cf9a410 + languageName: node + linkType: hard + +"@jsep-plugin/regex@npm:^1.0.4": + version: 1.0.4 + resolution: "@jsep-plugin/regex@npm:1.0.4" + peerDependencies: + jsep: ^0.4.0||^1.0.0 + checksum: 78ef01554535ac6c108851a2a6d86377bce10de01a263ad7b31f9b37c8aa9fc6c49f24b753e5da7d771c5921c913e43c1c33e0bc0fa5d02562d906c83a237836 + languageName: node + linkType: hard + +"@keycloak/keycloak-admin-client@npm:^26.0.0": + version: 26.5.2 + resolution: "@keycloak/keycloak-admin-client@npm:26.5.2" + dependencies: + camelize-ts: ^3.0.0 + url-template: ^3.1.1 + checksum: 3ceedb4347880c87d93dd3644e062602139959f649ee79e6eeb570814c8c0b640ebff69c682855deba6e311a6f9c7486a88b9ff2cf29d80e9f68539771bfd120 + languageName: node + linkType: hard + +"@kubernetes/client-node@npm:^1.4.0": + version: 1.4.0 + resolution: "@kubernetes/client-node@npm:1.4.0" + dependencies: + "@types/js-yaml": ^4.0.1 + "@types/node": ^24.0.0 + "@types/node-fetch": ^2.6.13 + "@types/stream-buffers": ^3.0.3 + form-data: ^4.0.0 + hpagent: ^1.2.0 + isomorphic-ws: ^5.0.0 + js-yaml: ^4.1.0 + jsonpath-plus: ^10.3.0 + node-fetch: ^2.7.0 + openid-client: ^6.1.3 + rfc4648: ^1.3.0 + socks-proxy-agent: ^8.0.4 + stream-buffers: ^3.0.2 + tar-fs: ^3.0.9 + ws: ^8.18.2 + checksum: d62b22db84d1832c904ca1489ca2de029b1698ae111cf0fb1dac96ae9e5a68414e1c128adf9ffdd0fd75c937aef831a3e39c48c1b43850c62765d6e61ce25efd + languageName: node + linkType: hard + +"@npmcli/agent@npm:^4.0.0": + version: 4.0.0 + resolution: "@npmcli/agent@npm:4.0.0" + dependencies: + agent-base: ^7.1.0 + http-proxy-agent: ^7.0.0 + https-proxy-agent: ^7.0.1 + lru-cache: ^11.2.1 + socks-proxy-agent: ^8.0.3 + checksum: 89ae20b44859ff8d4de56ade319d8ceaa267a0742d6f7345fe98aa5cd8614ced7db85ea4dc5bfbd6614dbb200a10b134e087143582534c939e8a02219e8665c8 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^5.0.0": + version: 5.0.0 + resolution: "@npmcli/fs@npm:5.0.0" + dependencies: + semver: ^7.3.5 + checksum: 897dac32eb37e011800112d406b9ea2ebd96f1dab01bb8fbeb59191b86f6825dffed6a89f3b6c824753d10f8735b76d630927bd7610e9e123b129ef2e5f02cb5 + languageName: node + linkType: hard + +"@otplib/core@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/core@npm:12.0.1" + checksum: b3c34bc20b31bc3f49cc0dc3c0eb070491c0101e8c1efa83cec48ca94158bd736aaca8187df667fc0c4a239d4ac52076bc44084bee04a50c80c3630caf77affa + languageName: node + linkType: hard + +"@otplib/plugin-crypto@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/plugin-crypto@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + checksum: 6867c74ee8aca6c2db9670362cf51e44f3648602c39318bf537421242e33f0012a172acd43bbed9a21d706e535dc4c66aff965380673391e9fd74cf685b5b13a + languageName: node + linkType: hard + +"@otplib/plugin-thirty-two@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/plugin-thirty-two@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + thirty-two: ^1.0.2 + checksum: 920099e40d3e8c2941291c84c70064c2d86d0d1ed17230d650445d5463340e406bc413ddf2e40c374ddc4ee988ef1e3facacab9b5248b1ff361fd13df52bf88f + languageName: node + linkType: hard + +"@otplib/preset-default@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/preset-default@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + "@otplib/plugin-crypto": ^12.0.1 + "@otplib/plugin-thirty-two": ^12.0.1 + checksum: 8133231384f6277f77eb8e42ef83bc32a8b01059bef147d1c358d9e9bfd292e1c239f581fe008367a48489dd68952b7ac0948e6c41412fc06079da2c91b71d16 + languageName: node + linkType: hard + +"@otplib/preset-v11@npm:^12.0.1": + version: 12.0.1 + resolution: "@otplib/preset-v11@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + "@otplib/plugin-crypto": ^12.0.1 + "@otplib/plugin-thirty-two": ^12.0.1 + checksum: 367cb09397e617c21ec748d54e920ab43f1c5dfba70cbfd88edf73aecca399cf0c09fefe32518f79c7ee8a06e7058d14b200da378cc7d46af3cac4e22a153e2f + languageName: node + linkType: hard + +"@playwright/test@npm:^1.56.1": + version: 1.58.0 + resolution: "@playwright/test@npm:1.58.0" + dependencies: + playwright: 1.58.0 + bin: + playwright: cli.js + checksum: 5f9bb3a32f99c176b525d4535afc6c4aacb7faa7c97c4c04cd75c39e59991789c9bf63cb788163747b2934fc7c987969c3ded278e93984a9596846ac6ac3afd3 + languageName: node + linkType: hard + +"@types/estree@npm:^1.0.6": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: bd93e2e415b6f182ec4da1074e1f36c480f1d26add3e696d54fb30c09bc470897e41361c8fd957bf0985024f8fbf1e6e2aff977d79352ef7eb93a5c6dcff6c11 + languageName: node + linkType: hard + +"@types/js-yaml@npm:^4.0.1": + version: 4.0.9 + resolution: "@types/js-yaml@npm:4.0.9" + checksum: e5e5e49b5789a29fdb1f7d204f82de11cb9e8f6cb24ab064c616da5d6e1b3ccfbf95aa5d1498a9fbd3b9e745564e69b4a20b6c530b5a8bbb2d4eb830cda9bc69 + languageName: node + linkType: hard + +"@types/json-schema@npm:^7.0.15": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 + languageName: node + linkType: hard + +"@types/node-fetch@npm:^2.6.13": + version: 2.6.13 + resolution: "@types/node-fetch@npm:2.6.13" + dependencies: + "@types/node": "*" + form-data: ^4.0.4 + checksum: e4b4db3a8c23309dadf0beb87e88882af1157f0c08b7b76027ac40add6ed363c924e2fa275f42ae45eacf776b25ed439d14400d9d6372eb39634dd4c7e7e1ad8 + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 25.1.0 + resolution: "@types/node@npm:25.1.0" + dependencies: + undici-types: ~7.16.0 + checksum: 7e96871cd81bd12fa9e7da87bbeffcc0b9ffb8972dd4aadc59454430fc9e87a4e3dc63efc66fd1747c462431e46b706f68e8f98261d4e6056bf01c90165e5644 + languageName: node + linkType: hard + +"@types/node@npm:^24.0.0, @types/node@npm:^24.10.1": + version: 24.10.9 + resolution: "@types/node@npm:24.10.9" + dependencies: + undici-types: ~7.16.0 + checksum: ee6e0a13b286c4cec32c29e2a7d862345660f6720f805315733f7802f6ece45d23fa0d4baee56276e48e62c4b7c3d335e5f40955179afc383a26b91bcb88293a + languageName: node + linkType: hard + +"@types/stream-buffers@npm:^3.0.3": + version: 3.0.8 + resolution: "@types/stream-buffers@npm:3.0.8" + dependencies: + "@types/node": "*" + checksum: 2e269491769f3c529236cdd743a505d1dca52d14b3672714730d8940000d948bdf9468b2e54609b9d591d31e51e8e043eb44a830a6189bc727bd9dfc4dd70cdf + languageName: node + linkType: hard + +"@typescript-eslint/eslint-plugin@npm:8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.54.0" + dependencies: + "@eslint-community/regexpp": ^4.12.2 + "@typescript-eslint/scope-manager": 8.54.0 + "@typescript-eslint/type-utils": 8.54.0 + "@typescript-eslint/utils": 8.54.0 + "@typescript-eslint/visitor-keys": 8.54.0 + ignore: ^7.0.5 + natural-compare: ^1.4.0 + ts-api-utils: ^2.4.0 + peerDependencies: + "@typescript-eslint/parser": ^8.54.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 31934585af6b0ce4efe23d1bfb9ff681dcd821d32bb954453e7773e9a3a42c0a2d43b1b5072dc3badac49ee355cfa0e840535cc668afe4f0b58cffa3d8e9f4d1 + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/parser@npm:8.54.0" + dependencies: + "@typescript-eslint/scope-manager": 8.54.0 + "@typescript-eslint/types": 8.54.0 + "@typescript-eslint/typescript-estree": 8.54.0 + "@typescript-eslint/visitor-keys": 8.54.0 + debug: ^4.4.3 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 1a4c8c6edd67b3f301d00f0ad1739d0536b7843ef1a7091d2444c3fe752932786851c49d4d26e87cc914dfae49dddf77f0354d71dbfc382ff8959cd1b7bcbbbe + languageName: node + linkType: hard + +"@typescript-eslint/project-service@npm:8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/project-service@npm:8.54.0" + dependencies: + "@typescript-eslint/tsconfig-utils": ^8.54.0 + "@typescript-eslint/types": ^8.54.0 + debug: ^4.4.3 + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: 3c2a5c758aa92d3673050383f4a9889c8175738372caf40082929082dfff87d5dbf54b9d22d97915f0f47393950df9fc338526dcc10be0512315aff82e65ad99 + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/scope-manager@npm:8.54.0" + dependencies: + "@typescript-eslint/types": 8.54.0 + "@typescript-eslint/visitor-keys": 8.54.0 + checksum: 9a6bbdf019c3bed31aa81f11cd2d4f98e1b71a83d3f68ccdbd2a6539bfe1575ec59f37cd96b74311df1183c78348325d6b8ddcb653f7096f0d3e36299ae3c3e9 + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:8.54.0, @typescript-eslint/tsconfig-utils@npm:^8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.54.0" + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: f8907f6e803563b460e035a688f30dbbb690d40c3fd9bb8e30c4628905bd49cf9de4947042268c0b50ce4e7aac3249712a33e91afde9a08df064ad782cd38dee + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/type-utils@npm:8.54.0" + dependencies: + "@typescript-eslint/types": 8.54.0 + "@typescript-eslint/typescript-estree": 8.54.0 + "@typescript-eslint/utils": 8.54.0 + debug: ^4.4.3 + ts-api-utils: ^2.4.0 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 501a27b5e39305bfd47c6b678a71cbff87268f57f8985081666d50724b1a3c4530561cea9a24b0823d466c6cdca680647013ee5e9ed54aaa5110f2e42fdbc6ac + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:8.54.0, @typescript-eslint/types@npm:^8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/types@npm:8.54.0" + checksum: 53ee5c5ef804e8cd1dd9a4c7f7a82e45a17d97ee78b1e108c56c919d08f86c2c9e4fec8c732e0d23995cf63532923456e7757b41833f40b93f1fca28b2db571a + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.54.0" + dependencies: + "@typescript-eslint/project-service": 8.54.0 + "@typescript-eslint/tsconfig-utils": 8.54.0 + "@typescript-eslint/types": 8.54.0 + "@typescript-eslint/visitor-keys": 8.54.0 + debug: ^4.4.3 + minimatch: ^9.0.5 + semver: ^7.7.3 + tinyglobby: ^0.2.15 + ts-api-utils: ^2.4.0 + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + checksum: 0a4cf84abba5fba389515224e60fa0830c3d5403a2954e43d7390311cab25bb37728de124eb17e9d5bd05ee067e3b7ef815808e3c3abd58d8eeb3eae1988b6f1 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/utils@npm:8.54.0" + dependencies: + "@eslint-community/eslint-utils": ^4.9.1 + "@typescript-eslint/scope-manager": 8.54.0 + "@typescript-eslint/types": 8.54.0 + "@typescript-eslint/typescript-estree": 8.54.0 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 365032335805e331fd92d09898b018e7fef6fb4c46582a8b13c5e3f42806ced7275bd71cc2d4783ecd8428547ac97ed012b7cedfea483bc1533513ae8dd5dba0 + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:8.54.0": + version: 8.54.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.54.0" + dependencies: + "@typescript-eslint/types": 8.54.0 + eslint-visitor-keys: ^4.2.1 + checksum: 36aafcffee5223041e3c898a8622589ae04e89cfad3d785bf506ab2126606af5ddac48bd6dbbf1c1098a0e21206b4f9edc90971f9f11a220423a924345adb184 + languageName: node + linkType: hard + +"abbrev@npm:^4.0.0": + version: 4.0.0 + resolution: "abbrev@npm:4.0.0" + checksum: d0344b63d28e763f259b4898c41bdc92c08e9d06d0da5617d0bbe4d78244e46daea88c510a2f9472af59b031d9060ec1a999653144e793fd029a59dae2f56dc8 + languageName: node + linkType: hard + +"acorn-jsx@npm:^5.3.2": + version: 5.3.2 + resolution: "acorn-jsx@npm:5.3.2" + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: c3d3b2a89c9a056b205b69530a37b972b404ee46ec8e5b341666f9513d3163e2a4f214a71f4dfc7370f5a9c07472d2fd1c11c91c3f03d093e37637d95da98950 + languageName: node + linkType: hard + +"acorn@npm:^8.15.0": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" + bin: + acorn: bin/acorn + checksum: 309c6b49aedf1a2e34aaf266de06de04aab6eb097c02375c66fdeb0f64556a6a823540409914fb364d9a11bc30d79d485a2eba29af47992d3490e9886c4391c3 + languageName: node + linkType: hard + +"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": + version: 7.1.4 + resolution: "agent-base@npm:7.1.4" + checksum: 86a7f542af277cfbd77dd61e7df8422f90bac512953709003a1c530171a9d019d072e2400eab2b59f84b49ab9dd237be44315ca663ac73e82b3922d10ea5eafa + languageName: node + linkType: hard + +"ajv@npm:^6.12.4": + version: 6.12.6 + resolution: "ajv@npm:6.12.6" + dependencies: + fast-deep-equal: ^3.1.1 + fast-json-stable-stringify: ^2.0.0 + json-schema-traverse: ^0.4.1 + uri-js: ^4.2.2 + checksum: 874972efe5c4202ab0a68379481fbd3d1b5d0a7bd6d3cc21d40d3536ebff3352a2a1fabb632d4fd2cc7fe4cbdcd5ed6782084c9bbf7f32a1536d18f9da5007d4 + languageName: node + linkType: hard + +"ansi-align@npm:^3.0.1": + version: 3.0.1 + resolution: "ansi-align@npm:3.0.1" + dependencies: + string-width: ^4.1.0 + checksum: 6abfa08f2141d231c257162b15292467081fa49a208593e055c866aa0455b57f3a86b5a678c190c618faa79b4c59e254493099cb700dd9cf2293c6be2c8f5d8d + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.2.2 + resolution: "ansi-regex@npm:6.2.2" + checksum: 9b17ce2c6daecc75bcd5966b9ad672c23b184dc3ed9bf3c98a0702f0d2f736c15c10d461913568f2cf527a5e64291c7473358885dd493305c84a1cfed66ba94f + languageName: node + linkType: hard + +"ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: ^2.0.1 + checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 + languageName: node + linkType: hard + +"ansi-styles@npm:^6.2.1": + version: 6.2.3 + resolution: "ansi-styles@npm:6.2.3" + checksum: f1b0829cf048cce870a305819f65ce2adcebc097b6d6479e12e955fd6225df9b9eb8b497083b764df796d94383ff20016cc4dbbae5b40f36138fb65a9d33c2e2 + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 83644b56493e89a254bae05702abf3a1101b4fa4d0ca31df1c9985275a5a5bd47b3c27b7fa0b71098d41114d8ca000e6ed90cad764b306f8a503665e4d517ced + languageName: node + linkType: hard + +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 9102e246d1ed9b37ac36f57f0a6ca55226876553251a31fc80677e71471f463a54c872dc78d5d7f80740c8ba624395cccbe8b60f7b690c4418f487d8e9fd1106 + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 74a71a4a2dd7afd06ebb612f6d612c7f4766a351bedffde466023bf6dae629e46b0d2cd38786239e0fbf245de0c7df76035465e16d1213774a0efb22fec0d713 + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 7b78c451df768adba04e2d02e63e2d0bf3b07adcd6e42b4cf665cb7ce899bedd344c69a1dcbce355b5f972d597b25aaa1c1742b52cffd9caccb22f348114f6be + languageName: node + linkType: hard + +"axe-core@npm:~4.11.0": + version: 4.11.1 + resolution: "axe-core@npm:4.11.1" + checksum: 92b3c79af3695bcebac0e7f3f90f4bc11d2b39ccdc670937290e8dacbc943473713cc06b771dea0563c66d57d93d940ed89e082bfdecccf9dd70782d4bb243c0 + languageName: node + linkType: hard + +"b4a@npm:^1.6.4": + version: 1.7.3 + resolution: "b4a@npm:1.7.3" + peerDependencies: + react-native-b4a: "*" + peerDependenciesMeta: + react-native-b4a: + optional: true + checksum: b2e5f572bb2024b612eddedfbfce9626e103fd523da3d7bb8267f345bca6d7d4772ed00310527c7a629fc54184d5b1b47b73f8dd976d713aea7e16d2af38aecf + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"bare-events@npm:^2.5.4, bare-events@npm:^2.7.0": + version: 2.8.2 + resolution: "bare-events@npm:2.8.2" + peerDependencies: + bare-abort-controller: "*" + peerDependenciesMeta: + bare-abort-controller: + optional: true + checksum: 97e6fc825bc984363a1f695c9a962b1b9f0ea467baa95d7059565a0aa31f4b5fc0f5eb71c087456ae3a436f39fce14a6147a12dd63aac0ff1d20cc5ad64cd780 + languageName: node + linkType: hard + +"bare-fs@npm:^4.0.1": + version: 4.5.3 + resolution: "bare-fs@npm:4.5.3" + dependencies: + bare-events: ^2.5.4 + bare-path: ^3.0.0 + bare-stream: ^2.6.4 + bare-url: ^2.2.2 + fast-fifo: ^1.3.2 + peerDependencies: + bare-buffer: "*" + peerDependenciesMeta: + bare-buffer: + optional: true + checksum: add0e1655e95a06045db027cfd0b10cd7d468133c34727d4ec8ae5bcbe11abab59e0c494e979e890e37b13259617b62b24a96baf89a8d014197aa872c00c350f + languageName: node + linkType: hard + +"bare-os@npm:^3.0.1": + version: 3.6.2 + resolution: "bare-os@npm:3.6.2" + checksum: 339187a5f294b0433f2aa0e3180ffe635f82b9d4536fc818ec8b2b44af752421b1d4bb8eebefd5034cc462a26756900d67b95052ed5eeb2278cb2e23e566ae7a + languageName: node + linkType: hard + +"bare-path@npm:^3.0.0": + version: 3.0.0 + resolution: "bare-path@npm:3.0.0" + dependencies: + bare-os: ^3.0.1 + checksum: 51d559515f332f62cf9c37c38f2640c1b84b5e8c9de454b70baf029f806058cf94c51d6a0dfec0025cc7760f2069dc3e16c82f0d24f4a9ddb18c829bf9c0206d + languageName: node + linkType: hard + +"bare-stream@npm:^2.6.4": + version: 2.7.0 + resolution: "bare-stream@npm:2.7.0" + dependencies: + streamx: ^2.21.0 + peerDependencies: + bare-buffer: "*" + bare-events: "*" + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + checksum: aa7a762b65271022ecc154c1f65eff37a9ff5aa1effd3f3065074b2885d4e6101c1f9fe06001cf3f88f73b56cc2a4b87ce70405d266891ac5082aeb1e1f6a310 + languageName: node + linkType: hard + +"bare-url@npm:^2.2.2": + version: 2.3.2 + resolution: "bare-url@npm:2.3.2" + dependencies: + bare-path: ^3.0.0 + checksum: 7b2a6335a55a010ffcc863f62cc5bfaa216b383bc05a8e7fb30caccb5600e09d403ad482fc671582eba531bbca4a891dba8eefa866f2e2d222b0a72f2460c340 + languageName: node + linkType: hard + +"boxen@npm:^8.0.1": + version: 8.0.1 + resolution: "boxen@npm:8.0.1" + dependencies: + ansi-align: ^3.0.1 + camelcase: ^8.0.0 + chalk: ^5.3.0 + cli-boxes: ^3.0.0 + string-width: ^7.2.0 + type-fest: ^4.21.0 + widest-line: ^5.0.0 + wrap-ansi: ^9.0.0 + checksum: f42d9e628e03e5c84ac9cda3173f75cadbdf60ed94fc06aaeef79f7c84a8181c4d79a8f40253192a1613993036c81811ad6957f346e5aa6abb7e9d1d799cbfd5 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.12 + resolution: "brace-expansion@npm:1.1.12" + dependencies: + balanced-match: ^1.0.0 + concat-map: 0.0.1 + checksum: 12cb6d6310629e3048cadb003e1aca4d8c9bb5c67c3c321bafdd7e7a50155de081f78ea3e0ed92ecc75a9015e784f301efc8132383132f4f7904ad1ac529c562 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.2 + resolution: "brace-expansion@npm:2.0.2" + dependencies: + balanced-match: ^1.0.0 + checksum: 01dff195e3646bc4b0d27b63d9bab84d2ebc06121ff5013ad6e5356daa5a9d6b60fa26cf73c74797f2dc3fbec112af13578d51f75228c1112b26c790a87b0488 + languageName: node + linkType: hard + +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: ^7.1.1 + checksum: b95aa0b3bd909f6cd1720ffcf031aeaf46154dd88b4da01f9a1d3f7ea866a79eba76a6d01cbc3c422b2ee5cdc39a4f02491058d5df0d7bf6e6a162a832df1f69 + languageName: node + linkType: hard + +"bulk-imprort-e2e-tests@workspace:.": + version: 0.0.0-use.local + resolution: "bulk-imprort-e2e-tests@workspace:." + dependencies: + "@eslint/js": ^9.39.2 + "@playwright/test": ^1.56.1 + "@types/node": ^24.10.1 + dotenv: ^16.4.7 + eslint: ^9.39.2 + eslint-plugin-check-file: ^3.3.1 + eslint-plugin-playwright: ^2.4.0 + prettier: ^3.7.4 + rhdh-e2e-test-utils: 1.1.0 + typescript: ^5.9.3 + typescript-eslint: ^8.50.0 + languageName: unknown + linkType: soft + +"cacache@npm:^20.0.1": + version: 20.0.3 + resolution: "cacache@npm:20.0.3" + dependencies: + "@npmcli/fs": ^5.0.0 + fs-minipass: ^3.0.0 + glob: ^13.0.0 + lru-cache: ^11.1.0 + minipass: ^7.0.3 + minipass-collect: ^2.0.1 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + p-map: ^7.0.2 + ssri: ^13.0.0 + unique-filename: ^5.0.0 + checksum: 595e6b91d72972d596e1e9ccab8ddbf08b773f27240220b1b5b1b7b3f52173cfbcf095212e5d7acd86c3bd453c28e69b116469889c511615ef3589523d542639 + languageName: node + linkType: hard + +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: ^1.3.0 + function-bind: ^1.1.2 + checksum: b2863d74fcf2a6948221f65d95b91b4b2d90cfe8927650b506141e669f7d5de65cea191bf788838bc40d13846b7886c5bc5c84ab96c3adbcf88ad69a72fcdc6b + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3 + languageName: node + linkType: hard + +"camelcase@npm:^8.0.0": + version: 8.0.0 + resolution: "camelcase@npm:8.0.0" + checksum: 6da7abe997af29e80052f17aa21628c7cce14af364cef9f07a2a44d59614dd6f361d405f121938e673424d673697a8c53ad17be8c4b03b0a727307c4db8b5b5e + languageName: node + linkType: hard + +"camelize-ts@npm:^3.0.0": + version: 3.0.0 + resolution: "camelize-ts@npm:3.0.0" + checksum: 835f7f79ddec6e6e0364c6a8294ce82586bca5d9443001f28077169181801cb126d8bc608c85504aa6c877de6fe5f7c9533f80996dc81117d865ff92c676d680 + languageName: node + linkType: hard + +"chalk@npm:^4.0.0": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc + languageName: node + linkType: hard + +"chalk@npm:^5.3.0": + version: 5.6.2 + resolution: "chalk@npm:5.6.2" + checksum: 4ee2d47a626d79ca27cb5299ecdcce840ef5755e287412536522344db0fc51ca0f6d6433202332c29e2288c6a90a2b31f3bd626bc8c14743b6b6ee28abd3b796 + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: fd73a4bab48b79e66903fe1cafbdc208956f41ea4f856df883d0c7277b7ab29fd33ee65f93b2ec9192fc0169238f2f8307b7735d27c155821d886b84aa97aa8d + languageName: node + linkType: hard + +"cli-boxes@npm:^3.0.0": + version: 3.0.0 + resolution: "cli-boxes@npm:3.0.0" + checksum: 637d84419d293a9eac40a1c8c96a2859e7d98b24a1a317788e13c8f441be052fc899480c6acab3acc82eaf1bccda6b7542d7cdcf5c9c3cc39227175dc098d5b2 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: ~1.1.4 + checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: ~1.0.0 + checksum: 49fa4aeb4916567e33ea81d088f6584749fc90c7abec76fd516bf1c5aa5c79f3584b5ba3de6b86d26ddd64bae5329c4c7479343250cfe71c75bb366eae53bb7c + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: ^3.1.0 + shebang-command: ^2.0.0 + which: ^2.0.1 + checksum: 8d306efacaf6f3f60e0224c287664093fa9185680b2d195852ba9a863f85d02dcc737094c6e512175f8ee0161f9b87c73c6826034c2422e39de7d6569cf4503b + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.3": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: ^2.1.3 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 4805abd570e601acdca85b6aa3757186084a45cff9b2fa6eee1f3b173caa776b45f478b2a71a572d616d2010cea9211d0ac4a02a610e4c18ac4324bde3760834 + languageName: node + linkType: hard + +"deep-is@npm:^0.1.3": + version: 0.1.4 + resolution: "deep-is@npm:0.1.4" + checksum: edb65dd0d7d1b9c40b2f50219aef30e116cedd6fc79290e740972c132c09106d2e80aa0bc8826673dd5a00222d4179c84b36a790eef63a4c4bca75a37ef90804 + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 46fe6e83e2cb1d85ba50bd52803c68be9bd953282fa7096f51fc29edd5d67ff84ff753c51966061e5ba7cb5e47ef6d36a91924eddb7f3f3483b1c560f77a0020 + languageName: node + linkType: hard + +"dotenv@npm:^16.4.7": + version: 16.6.1 + resolution: "dotenv@npm:16.6.1" + checksum: e8bd63c9a37f57934f7938a9cf35de698097fadf980cb6edb61d33b3e424ceccfe4d10f37130b904a973b9038627c2646a3365a904b4406514ea94d7f1816b69 + languageName: node + linkType: hard + +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: ^1.0.1 + es-errors: ^1.3.0 + gopd: ^1.2.0 + checksum: 149207e36f07bd4941921b0ca929e3a28f1da7bd6b6ff8ff7f4e2f2e460675af4576eeba359c635723dc189b64cdd4787e0255897d5b135ccc5d15cb8685fc90 + languageName: node + linkType: hard + +"emoji-regex@npm:^10.3.0": + version: 10.6.0 + resolution: "emoji-regex@npm:10.6.0" + checksum: 8785f6a7ec4559c931bd6640f748fe23791f5af4c743b131d458c5551b4aa7da2a9cd882518723cb3859e8b0b59b0cc08f2ce0f8e65c61a026eed71c2dc407d5 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: ^0.6.2 + checksum: bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f + languageName: node + linkType: hard + +"end-of-stream@npm:^1.1.0": + version: 1.4.5 + resolution: "end-of-stream@npm:1.4.5" + dependencies: + once: ^1.4.0 + checksum: 1e0cfa6e7f49887544e03314f9dfc56a8cb6dde910cbb445983ecc2ff426fc05946df9d75d8a21a3a64f2cecfe1bf88f773952029f46756b2ed64a24e95b1fb8 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 8b7b1be20d2de12d2255c0bc2ca638b7af5171142693299416e6a9339bd7d88fc8d7707d913d78e0993176005405a236b066b45666b27b797252c771156ace54 + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 0512f4e5d564021c9e3a644437b0155af2679d10d80f21adaf868e64d30efdfbd321631956f20f42d655fedb2e3a027da479fad3fa6048f768eb453a80a5f80a + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: ec1414527a0ccacd7f15f4a3bc66e215f04f595ba23ca75cdae0927af099b5ec865f9f4d33e9d7e86f512f252876ac77d4281a7871531a50678132429b1271b5 + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: ^1.3.0 + checksum: 214d3767287b12f36d3d7267ef342bbbe1e89f899cfd67040309fc65032372a8e60201410a99a1645f2f90c1912c8c49c8668066f6bdd954bcd614dda2e3da97 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: ^1.3.0 + get-intrinsic: ^1.2.6 + has-tostringtag: ^1.0.2 + hasown: ^2.0.2 + checksum: 789f35de4be3dc8d11fdcb91bc26af4ae3e6d602caa93299a8c45cf05d36cc5081454ae2a6d3afa09cceca214b76c046e4f8151e092e6fc7feeb5efb9e794fc6 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 + languageName: node + linkType: hard + +"eslint-plugin-check-file@npm:^3.3.1": + version: 3.3.1 + resolution: "eslint-plugin-check-file@npm:3.3.1" + dependencies: + is-glob: ^4.0.3 + micromatch: ^4.0.8 + peerDependencies: + eslint: ">=9.0.0" + checksum: 32b5c3954d4710901b0365308996432596ae1373e678255e016522bbefe19dfe5510dc9b661db937eeb8f3ebb11efa58b2d39c6554a31ae8e5edd2b864c7a79c + languageName: node + linkType: hard + +"eslint-plugin-playwright@npm:^2.4.0": + version: 2.5.1 + resolution: "eslint-plugin-playwright@npm:2.5.1" + dependencies: + globals: ^16.4.0 + peerDependencies: + eslint: ">=8.40.0" + checksum: 86c586b6e8b50a1701ef1770c0958ee45f4eaa0619a27ef3d46d6834c2e480a546a3bc5d65595948dd019190d4b66ba6676e81ca2898c5c27b90f427021dd874 + languageName: node + linkType: hard + +"eslint-scope@npm:^8.4.0": + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" + dependencies: + esrecurse: ^4.3.0 + estraverse: ^5.2.0 + checksum: cf88f42cd5e81490d549dc6d350fe01e6fe420f9d9ea34f134bb359b030e3c4ef888d36667632e448937fe52449f7181501df48c08200e3d3b0fee250d05364e + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 3a77e3f99a49109f6fb2c5b7784bc78f9743b834d238cdba4d66c602c6b52f19ed7bcd0a5c5dbbeae3a8689fd785e76c001799f53d2228b278282cf9f699fff5 + languageName: node + linkType: hard + +"eslint@npm:^9.39.1, eslint@npm:^9.39.2": + version: 9.39.2 + resolution: "eslint@npm:9.39.2" + dependencies: + "@eslint-community/eslint-utils": ^4.8.0 + "@eslint-community/regexpp": ^4.12.1 + "@eslint/config-array": ^0.21.1 + "@eslint/config-helpers": ^0.4.2 + "@eslint/core": ^0.17.0 + "@eslint/eslintrc": ^3.3.1 + "@eslint/js": 9.39.2 + "@eslint/plugin-kit": ^0.4.1 + "@humanfs/node": ^0.16.6 + "@humanwhocodes/module-importer": ^1.0.1 + "@humanwhocodes/retry": ^0.4.2 + "@types/estree": ^1.0.6 + ajv: ^6.12.4 + chalk: ^4.0.0 + cross-spawn: ^7.0.6 + debug: ^4.3.2 + escape-string-regexp: ^4.0.0 + eslint-scope: ^8.4.0 + eslint-visitor-keys: ^4.2.1 + espree: ^10.4.0 + esquery: ^1.5.0 + esutils: ^2.0.2 + fast-deep-equal: ^3.1.3 + file-entry-cache: ^8.0.0 + find-up: ^5.0.0 + glob-parent: ^6.0.2 + ignore: ^5.2.0 + imurmurhash: ^0.1.4 + is-glob: ^4.0.0 + json-stable-stringify-without-jsonify: ^1.0.1 + lodash.merge: ^4.6.2 + minimatch: ^3.1.2 + natural-compare: ^1.4.0 + optionator: ^0.9.3 + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + bin: + eslint: bin/eslint.js + checksum: bfa288fe6b19b6e7f8868e1434d8e469603203d6259e4451b8be4e2172de3172f3b07ed8943ba3904f3545c7c546062c0d656774baa0a10a54483f3907c525e3 + languageName: node + linkType: hard + +"espree@npm:^10.0.1, espree@npm:^10.4.0": + version: 10.4.0 + resolution: "espree@npm:10.4.0" + dependencies: + acorn: ^8.15.0 + acorn-jsx: ^5.3.2 + eslint-visitor-keys: ^4.2.1 + checksum: 5f9d0d7c81c1bca4bfd29a55270067ff9d575adb8c729a5d7f779c2c7b910bfc68ccf8ec19b29844b707440fc159a83868f22c8e87bbf7cbcb225ed067df6c85 + languageName: node + linkType: hard + +"esquery@npm:^1.5.0": + version: 1.7.0 + resolution: "esquery@npm:1.7.0" + dependencies: + estraverse: ^5.1.0 + checksum: 3239792b68cf39fe18966d0ca01549bb15556734f0144308fd213739b0f153671ae916013fce0bca032044a4dbcda98b43c1c667f20c20a54dec3597ac0d7c27 + languageName: node + linkType: hard + +"esrecurse@npm:^4.3.0": + version: 4.3.0 + resolution: "esrecurse@npm:4.3.0" + dependencies: + estraverse: ^5.2.0 + checksum: ebc17b1a33c51cef46fdc28b958994b1dc43cd2e86237515cbc3b4e5d2be6a811b2315d0a1a4d9d340b6d2308b15322f5c8291059521cc5f4802f65e7ec32837 + languageName: node + linkType: hard + +"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 072780882dc8416ad144f8fe199628d2b3e7bbc9989d9ed43795d2c90309a2047e6bc5979d7e2322a341163d22cfad9e21f4110597fe487519697389497e4e2b + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87 + languageName: node + linkType: hard + +"events-universal@npm:^1.0.0": + version: 1.0.1 + resolution: "events-universal@npm:1.0.1" + dependencies: + bare-events: ^2.7.0 + checksum: fb8451c98535bde30585004303a368d55c38e5bc3ed6aa9b5d29fecaabaf8ec276a33ff77dcc1d1c05eecf83b8161f184cabc9a03b76a06c10e9a4ce827a6abc + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.3 + resolution: "exponential-backoff@npm:3.1.3" + checksum: 471fdb70fd3d2c08a74a026973bdd4105b7832911f610ca67bbb74e39279411c1eed2f2a110c9d41c2edd89459ba58fdaba1c174beed73e7a42d773882dcff82 + languageName: node + linkType: hard + +"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: e21a9d8d84f53493b6aa15efc9cfd53dd5b714a1f23f67fb5dc8f574af80df889b3bce25dc081887c6d25457cce704e636395333abad896ccdec03abaf1f3f9d + languageName: node + linkType: hard + +"fast-fifo@npm:^1.2.0, fast-fifo@npm:^1.3.2": + version: 1.3.2 + resolution: "fast-fifo@npm:1.3.2" + checksum: 6bfcba3e4df5af7be3332703b69a7898a8ed7020837ec4395bb341bd96cc3a6d86c3f6071dd98da289618cf2234c70d84b2a6f09a33dd6f988b1ff60d8e54275 + languageName: node + linkType: hard + +"fast-json-stable-stringify@npm:^2.0.0": + version: 2.1.0 + resolution: "fast-json-stable-stringify@npm:2.1.0" + checksum: b191531e36c607977e5b1c47811158733c34ccb3bfde92c44798929e9b4154884378536d26ad90dfecd32e1ffc09c545d23535ad91b3161a27ddbb8ebe0cbecb + languageName: node + linkType: hard + +"fast-levenshtein@npm:^2.0.6": + version: 2.0.6 + resolution: "fast-levenshtein@npm:2.0.6" + checksum: 92cfec0a8dfafd9c7a15fba8f2cc29cd0b62b85f056d99ce448bbcd9f708e18ab2764bda4dd5158364f4145a7c72788538994f0d1787b956ef0d1062b0f7c24c + languageName: node + linkType: hard + +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: bd537daa9d3cd53887eed35efa0eab2dbb1ca408790e10e024120e7a36c6e9ae2b33710cb8381e35def01bc9c1d7eaba746f886338413e68ff6ebaee07b9a6e8 + languageName: node + linkType: hard + +"file-entry-cache@npm:^8.0.0": + version: 8.0.0 + resolution: "file-entry-cache@npm:8.0.0" + dependencies: + flat-cache: ^4.0.0 + checksum: f67802d3334809048c69b3d458f672e1b6d26daefda701761c81f203b80149c35dea04d78ea4238969dd617678e530876722a0634c43031a0957f10cc3ed190f + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: ^5.0.1 + checksum: b4abfbca3839a3d55e4ae5ec62e131e2e356bf4859ce8480c64c4876100f4df292a63e5bb1618e1d7460282ca2b305653064f01654474aa35c68000980f17798 + languageName: node + linkType: hard + +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: ^6.0.0 + path-exists: ^4.0.0 + checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 + languageName: node + linkType: hard + +"flat-cache@npm:^4.0.0": + version: 4.0.1 + resolution: "flat-cache@npm:4.0.1" + dependencies: + flatted: ^3.2.9 + keyv: ^4.5.4 + checksum: 899fc86bf6df093547d76e7bfaeb900824b869d7d457d02e9b8aae24836f0a99fbad79328cfd6415ee8908f180699bf259dc7614f793447cb14f707caf5996f6 + languageName: node + linkType: hard + +"flatted@npm:^3.2.9": + version: 3.3.3 + resolution: "flatted@npm:3.3.3" + checksum: 8c96c02fbeadcf4e8ffd0fa24983241e27698b0781295622591fc13585e2f226609d95e422bcf2ef044146ffacb6b68b1f20871454eddf75ab3caa6ee5f4a1fe + languageName: node + linkType: hard + +"form-data@npm:^4.0.0, form-data@npm:^4.0.4": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.8 + es-set-tostringtag: ^2.1.0 + hasown: ^2.0.2 + mime-types: ^2.1.12 + checksum: af8328413c16d0cded5fccc975a44d227c5120fd46a9e81de8acf619d43ed838414cc6d7792195b30b248f76a65246949a129a4dadd148721948f90cd6d4fb69 + languageName: node + linkType: hard + +"fs-extra@npm:^11.3.2": + version: 11.3.3 + resolution: "fs-extra@npm:11.3.3" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: fb2acabbd1e04bcaca90eadfe98e6ffba1523b8009afbb9f4c0aae5efbca0bd0bf6c9a6831df5af5aaacb98d3e499898be848fb0c03d31ae7b9d1b053e81c151 + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: ^7.0.3 + checksum: 8722a41109130851d979222d3ec88aabaceeaaf8f57b2a8f744ef8bd2d1ce95453b04a61daa0078822bc5cd21e008814f06fe6586f56fef511e71b8d2394d802 + languageName: node + linkType: hard + +"fsevents@npm:2.3.2": + version: 2.3.2 + resolution: "fsevents@npm:2.3.2" + dependencies: + node-gyp: latest + checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@2.3.2#~builtin": + version: 2.3.2 + resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" + dependencies: + node-gyp: latest + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 2b0ff4ce708d99715ad14a6d1f894e2a83242e4a52ccfcefaee5e40050562e5f6dafc1adbb4ce2d4ab47279a45dc736ab91ea5042d843c3c092820dfe032efb1 + languageName: node + linkType: hard + +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 3bf87f7b0230de5d74529677e6c3ceb3b7b5d9618b5a22d92b45ce3876defbaf5a77791b25a61b0fa7d13f95675b5ff67a7769f3b9af33f096e34653519e873d + languageName: node + linkType: hard + +"get-east-asian-width@npm:^1.0.0": + version: 1.4.0 + resolution: "get-east-asian-width@npm:1.4.0" + checksum: 1d9a81a8004f4217ebef5d461875047d269e4b57e039558fd65130877cd4da8e3f61e1c4eada0c8b10e2816c7baf7d5fddb7006f561da13bc6f6dd19c1e964a4 + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.2.6": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: ^1.0.0 + async-generator-function: ^1.0.0 + call-bind-apply-helpers: ^1.0.2 + es-define-property: ^1.0.1 + es-errors: ^1.3.0 + es-object-atoms: ^1.1.1 + function-bind: ^1.1.2 + generator-function: ^2.0.0 + get-proto: ^1.0.1 + gopd: ^1.2.0 + has-symbols: ^1.1.0 + hasown: ^2.0.2 + math-intrinsics: ^1.1.0 + checksum: c02b3b6a445f9cd53e14896303794ac60f9751f58a69099127248abdb0251957174c6524245fc68579dc8e6a35161d3d94c93e665f808274716f4248b269436a + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: ^1.0.1 + es-object-atoms: ^1.0.0 + checksum: 4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b + languageName: node + linkType: hard + +"glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: ^4.0.3 + checksum: c13ee97978bef4f55106b71e66428eb1512e71a7466ba49025fc2aec59a5bfb0954d5abd58fc5ee6c9b076eef4e1f6d3375c2e964b88466ca390da4419a786a8 + languageName: node + linkType: hard + +"glob@npm:^13.0.0": + version: 13.0.0 + resolution: "glob@npm:13.0.0" + dependencies: + minimatch: ^10.1.1 + minipass: ^7.1.2 + path-scurry: ^2.0.0 + checksum: 963730222b0acc85a0d2616c08ba3a5d5b5f33fbf69182791967b8a02245db505577a6fc19836d5d58e1cbbfb414ad4f62f605a0372ab05cd9e6998efe944369 + languageName: node + linkType: hard + +"globals@npm:^14.0.0": + version: 14.0.0 + resolution: "globals@npm:14.0.0" + checksum: 534b8216736a5425737f59f6e6a5c7f386254560c9f41d24a9227d60ee3ad4a9e82c5b85def0e212e9d92162f83a92544be4c7fd4c902cb913736c10e08237ac + languageName: node + linkType: hard + +"globals@npm:^16.4.0": + version: 16.5.0 + resolution: "globals@npm:16.5.0" + checksum: e0363245cfc6e36ac6bf940415160a05d66e7985fa3856d5383ad49292b6d249d80fd03759e09d6491109648a121849b23b77c7391a11862923e6995268a7cd6 + languageName: node + linkType: hard + +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: cc6d8e655e360955bdccaca51a12a474268f95bb793fc3e1f2bdadb075f28bfd1fd988dab872daf77a61d78cbaf13744bc8727a17cfb1d150d76047d805375f3 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: b2316c7302a0e8ba3aaba215f834e96c22c86f192e7310bdf689dd0e6999510c89b00fbc5742571507cebf25764d68c988b3a0da217369a73596191ac0ce694b + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: ^1.0.3 + checksum: 999d60bb753ad714356b2c6c87b7fb74f32463b8426e159397da4bde5bca7e598ab1073f4d8d4deafac297f2eb311484cd177af242776bf05f0d11565680468d + languageName: node + linkType: hard + +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: ^1.1.2 + checksum: e8516f776a15149ca6c6ed2ae3110c417a00b62260e222590e54aa367cbcd6ed99122020b37b7fbdf05748df57b265e70095d7bf35a47660587619b15ffb93db + languageName: node + linkType: hard + +"hpagent@npm:^1.2.0": + version: 1.2.0 + resolution: "hpagent@npm:1.2.0" + checksum: b029da695edae438cee4da2a437386f9db4ac27b3ceb7306d02e1b586c9c194741ed2e943c8a222e0cfefaf27ee3f863aca7ba1721b0950a2a19bf25bc0d85e2 + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.1.1": + version: 4.2.0 + resolution: "http-cache-semantics@npm:4.2.0" + checksum: 7a7246ddfce629f96832791176fd643589d954e6f3b49548dadb4290451961237fab8fcea41cd2008fe819d95b41c1e8b97f47d088afc0a1c81705287b4ddbcc + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: ^7.1.0 + debug: ^4.3.4 + checksum: 670858c8f8f3146db5889e1fa117630910101db601fff7d5a8aa637da0abedf68c899f03d3451cac2f83bcc4c3d2dabf339b3aa00ff8080571cceb02c3ce02f3 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" + dependencies: + agent-base: ^7.1.2 + debug: 4 + checksum: b882377a120aa0544846172e5db021fa8afbf83fea2a897d397bd2ddd8095ab268c24bc462f40a15f2a8c600bf4aa05ce52927f70038d4014e68aefecfa94e8d + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: ">= 2.1.2 < 3.0.0" + checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf + languageName: node + linkType: hard + +"ignore@npm:^5.2.0": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 2acfd32a573260ea522ea0bfeff880af426d68f6831f973129e2ba7363f422923cf53aab62f8369cbf4667c7b25b6f8a3761b34ecdb284ea18e87a5262a865be + languageName: node + linkType: hard + +"ignore@npm:^7.0.5": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: d0862bf64d3d58bf34d5fb0a9f725bec9ca5ce8cd1aecc8f28034269e8f69b8009ffd79ca3eda96962a6a444687781cd5efdb8c7c8ddc0a6996e36d31c217f14 + languageName: node + linkType: hard + +"import-fresh@npm:^3.2.1": + version: 3.3.1 + resolution: "import-fresh@npm:3.3.1" + dependencies: + parent-module: ^1.0.0 + resolve-from: ^4.0.0 + checksum: a06b19461b4879cc654d46f8a6244eb55eb053437afd4cbb6613cad6be203811849ed3e4ea038783092879487299fda24af932b86bdfff67c9055ba3612b8c87 + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 7cae75c8cd9a50f57dadd77482359f659eaebac0319dd9368bcd1714f55e65badd6929ca58569da2b6494ef13fdd5598cd700b1eba23f8b79c5f19d195a3ecf7 + languageName: node + linkType: hard + +"ip-address@npm:^10.0.1": + version: 10.1.0 + resolution: "ip-address@npm:10.1.0" + checksum: 76b1abcdf52a32e2e05ca1f202f3a8ab8547e5651a9233781b330271bd7f1a741067748d71c4cbb9d9906d9f1fa69e7ddc8b4a11130db4534fdab0e908c84e0d + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.3": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: ^2.1.1 + checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4 + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 456ac6f8e0f3111ed34668a624e45315201dff921e5ac181f8ec24923b99e9f32ca1a194912dc79d539c97d33dba17dc635202ff0b2cf98326f608323276d27a + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e + languageName: node + linkType: hard + +"isomorphic-ws@npm:^5.0.0": + version: 5.0.0 + resolution: "isomorphic-ws@npm:5.0.0" + peerDependencies: + ws: "*" + checksum: e20eb2aee09ba96247465fda40c6d22c1153394c0144fa34fe6609f341af4c8c564f60ea3ba762335a7a9c306809349f9b863c8beedf2beea09b299834ad5398 + languageName: node + linkType: hard + +"jose@npm:^6.1.0": + version: 6.1.3 + resolution: "jose@npm:6.1.3" + checksum: 7f51c7e77f82b70ef88ede9fd1760298bc0ffbf143b9d94f78c08462987ae61864535c1856bc6c26d335f857c7d41f4fffcc29134212c19ea929ce34a4c790f0 + languageName: node + linkType: hard + +"js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1": + version: 4.1.1 + resolution: "js-yaml@npm:4.1.1" + dependencies: + argparse: ^2.0.1 + bin: + js-yaml: bin/js-yaml.js + checksum: ea2339c6930fe048ec31b007b3c90be2714ab3e7defcc2c27ebf30c74fd940358f29070b4345af0019ef151875bf3bc3f8644bea1bab0372652b5044813ac02d + languageName: node + linkType: hard + +"jsep@npm:^1.4.0": + version: 1.4.0 + resolution: "jsep@npm:1.4.0" + checksum: 8e7af5ecb91483b227092b87a3e85b5df3e848dbe6f201b19efcb18047567530d21dfeecb0978e09d1f66554fcfaed84176819eeacdfc86f61dc05c40c18f824 + languageName: node + linkType: hard + +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 9026b03edc2847eefa2e37646c579300a1f3a4586cfb62bf857832b60c852042d0d6ae55d1afb8926163fa54c2b01d83ae24705f34990348bdac6273a29d4581 + languageName: node + linkType: hard + +"json-schema-traverse@npm:^0.4.1": + version: 0.4.1 + resolution: "json-schema-traverse@npm:0.4.1" + checksum: 7486074d3ba247769fda17d5181b345c9fb7d12e0da98b22d1d71a5db9698d8b4bd900a3ec1a4ffdd60846fc2556274a5c894d0c48795f14cb03aeae7b55260b + languageName: node + linkType: hard + +"json-stable-stringify-without-jsonify@npm:^1.0.1": + version: 1.0.1 + resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" + checksum: cff44156ddce9c67c44386ad5cddf91925fe06b1d217f2da9c4910d01f358c6e3989c4d5a02683c7a5667f9727ff05831f7aa8ae66c8ff691c556f0884d49215 + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.2.0 + resolution: "jsonfile@npm:6.2.0" + dependencies: + graceful-fs: ^4.1.6 + universalify: ^2.0.0 + dependenciesMeta: + graceful-fs: + optional: true + checksum: c3028ec5c770bb41290c9bb9ca04bdd0a1b698ddbdf6517c9453d3f90fc9e000c9675959fb46891d317690a93c62de03ff1735d8dbe02be83e51168ce85815d3 + languageName: node + linkType: hard + +"jsonpath-plus@npm:^10.3.0": + version: 10.3.0 + resolution: "jsonpath-plus@npm:10.3.0" + dependencies: + "@jsep-plugin/assignment": ^1.3.0 + "@jsep-plugin/regex": ^1.0.4 + jsep: ^1.4.0 + bin: + jsonpath: bin/jsonpath-cli.js + jsonpath-plus: bin/jsonpath-cli.js + checksum: b0f7e9af4d3c586911690c0afc20cbdc3c8af963a739c7f7a62905ed34a25bf5279f2c254e7222aa65f834d2ae3c8cc217986d528c8206896afa008d6619cde7 + languageName: node + linkType: hard + +"keyv@npm:^4.5.4": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: 3.0.1 + checksum: 74a24395b1c34bd44ad5cb2b49140d087553e170625240b86755a6604cd65aa16efdbdeae5cdb17ba1284a0fbb25ad06263755dbc71b8d8b06f74232ce3cdd72 + languageName: node + linkType: hard + +"levn@npm:^0.4.1": + version: 0.4.1 + resolution: "levn@npm:0.4.1" + dependencies: + prelude-ls: ^1.2.1 + type-check: ~0.4.0 + checksum: 12c5021c859bd0f5248561bf139121f0358285ec545ebf48bb3d346820d5c61a4309535c7f387ed7d84361cf821e124ce346c6b7cef8ee09a67c1473b46d0fc4 + languageName: node + linkType: hard + +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: ^5.0.0 + checksum: 72eb661788a0368c099a184c59d2fee760b3831c9c1c33955e8a19ae4a21b4116e53fa736dc086cdeb9fce9f7cc508f2f92d2d3aae516f133e16a2bb59a39f5a + languageName: node + linkType: hard + +"lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005 + languageName: node + linkType: hard + +"lodash.mergewith@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.mergewith@npm:4.6.2" + checksum: a6db2a9339752411f21b956908c404ec1e088e783a65c8b29e30ae5b3b6384f82517662d6f425cc97c2070b546cc2c7daaa8d33f78db7b6e9be06cd834abdeb8 + languageName: node + linkType: hard + +"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1": + version: 11.2.5 + resolution: "lru-cache@npm:11.2.5" + checksum: b3cd18066c81e0540429507036e0a37f860325348f6a85376ed71196e5b893668af410849574c5fb49110bc0afff76b4f87646cb93b2482a315c9e7b8435630f + languageName: node + linkType: hard + +"make-fetch-happen@npm:^15.0.0": + version: 15.0.3 + resolution: "make-fetch-happen@npm:15.0.3" + dependencies: + "@npmcli/agent": ^4.0.0 + cacache: ^20.0.1 + http-cache-semantics: ^4.1.1 + minipass: ^7.0.2 + minipass-fetch: ^5.0.0 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + negotiator: ^1.0.0 + proc-log: ^6.0.0 + promise-retry: ^2.0.1 + ssri: ^13.0.0 + checksum: 4fb9dbb739b33565c85dacdcff7eb9388d8f36f326a59dc13375f01af809c42c48aa5d1f4840ee36623b2461a15476e1e79e4548ca1af30b42e1e324705ac8b3 + languageName: node + linkType: hard + +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 0e513b29d120f478c85a70f49da0b8b19bc638975eca466f2eeae0071f3ad00454c621bf66e16dd435896c208e719fc91ad79bbfba4e400fe0b372e7c1c9c9a2 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: ^3.0.3 + picomatch: ^2.3.1 + checksum: 79920eb634e6f400b464a954fcfa589c4e7c7143209488e44baf627f9affc8b1e306f41f4f0deedde97e69cb725920879462d3e750ab3bd3c1aed675bb3a8966 + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: 1.52.0 + checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 + languageName: node + linkType: hard + +"minimatch@npm:^10.1.1": + version: 10.1.1 + resolution: "minimatch@npm:10.1.1" + dependencies: + "@isaacs/brace-expansion": ^5.0.0 + checksum: 8820c0be92994f57281f0a7a2cc4268dcc4b610f9a1ab666685716b4efe4b5898b43c835a8f22298875b31c7a278a5e3b7e253eee7c886546bb0b61fb94bca6b + languageName: node + linkType: hard + +"minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: ^1.1.7 + checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a + languageName: node + linkType: hard + +"minimatch@npm:^9.0.5": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: ^2.0.1 + checksum: 2c035575eda1e50623c731ec6c14f65a85296268f749b9337005210bb2b34e2705f8ef1a358b188f69892286ab99dc42c8fb98a57bde55c8d81b3023c19cea28 + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: ^7.0.3 + checksum: b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 + languageName: node + linkType: hard + +"minipass-fetch@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass-fetch@npm:5.0.0" + dependencies: + encoding: ^0.1.13 + minipass: ^7.0.3 + minipass-sized: ^1.0.3 + minizlib: ^3.0.1 + dependenciesMeta: + encoding: + optional: true + checksum: 416645d1e54c09fdfe64ec1676541ac2f6f2af3abc7ad25f2f22c4518535997c1ecd2c0c586ea8a5c6499ad7d8f97671f50ff38488ada54bf61fde309f731379 + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: ^3.0.0 + checksum: 56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: ^3.0.0 + checksum: b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: ^3.0.0 + checksum: 79076749fcacf21b5d16dd596d32c3b6bf4d6e62abb43868fac21674078505c8b15eaca4e47ed844985a4514854f917d78f588fcd029693709417d8f98b2bd60 + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: ^4.0.0 + checksum: a30d083c8054cee83cdcdc97f97e4641a3f58ae743970457b1489ce38ee1167b3aaf7d815cd39ec7a99b9c40397fd4f686e83750e73e652b21cb516f6d845e48 + languageName: node + linkType: hard + +"minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 2bfd325b95c555f2b4d2814d49325691c7bee937d753814861b0b49d5edcda55cbbf22b6b6a60bb91eddac8668771f03c5ff647dcd9d0f798e9548b9cdc46ee3 + languageName: node + linkType: hard + +"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: ^7.1.2 + checksum: a15e6f0128f514b7d41a1c68ce531155447f4669e32d279bba1c1c071ef6c2abd7e4d4579bb59ccc2ed1531346749665968fdd7be8d83eb6b6ae2fe1f3d370a7 + languageName: node + linkType: hard + +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d + languageName: node + linkType: hard + +"natural-compare@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare@npm:1.4.0" + checksum: 23ad088b08f898fc9b53011d7bb78ec48e79de7627e01ab5518e806033861bef68d5b0cd0e2205c2f36690ac9571ff6bcb05eb777ced2eeda8d4ac5b44592c3d + languageName: node + linkType: hard + +"negotiator@npm:^1.0.0": + version: 1.0.0 + resolution: "negotiator@npm:1.0.0" + checksum: 20ebfe79b2d2e7cf9cbc8239a72662b584f71164096e6e8896c8325055497c96f6b80cd22c258e8a2f2aa382a787795ec3ee8b37b422a302c7d4381b0d5ecfbb + languageName: node + linkType: hard + +"node-fetch@npm:^2.7.0": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: ^5.0.0 + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: d76d2f5edb451a3f05b15115ec89fc6be39de37c6089f1b6368df03b91e1633fd379a7e01b7ab05089a25034b2023d959b47e59759cb38d88341b2459e89d6e5 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 12.2.0 + resolution: "node-gyp@npm:12.2.0" + dependencies: + env-paths: ^2.2.0 + exponential-backoff: ^3.1.1 + graceful-fs: ^4.2.6 + make-fetch-happen: ^15.0.0 + nopt: ^9.0.0 + proc-log: ^6.0.0 + semver: ^7.3.5 + tar: ^7.5.4 + tinyglobby: ^0.2.12 + which: ^6.0.0 + bin: + node-gyp: bin/node-gyp.js + checksum: d4ce0acd08bd41004f45e10cef468f4bd15eaafb3acc388a0c567416e1746dc005cc080b8a3495e4e2ae2eed170a2123ff622c2d6614062f4a839837dcf1dd9d + languageName: node + linkType: hard + +"nopt@npm:^9.0.0": + version: 9.0.0 + resolution: "nopt@npm:9.0.0" + dependencies: + abbrev: ^4.0.0 + bin: + nopt: bin/nopt.js + checksum: 7a5d9ab0629eaec1944a95438cc4efa6418ed2834aa8eb21a1bea579a7d8ac3e30120131855376a96ef59ab0e23ad8e0bc94d3349770a95e5cb7119339f7c7fb + languageName: node + linkType: hard + +"oauth4webapi@npm:^3.8.2": + version: 3.8.3 + resolution: "oauth4webapi@npm:3.8.3" + checksum: 66d5c8215aff90280339911061534c183c8cf645cad1870d8d41810888f3972b5705a7c45c564c60c5c4b79e3d5a62b401c7be59bbd7bf45509094379fad2a39 + languageName: node + linkType: hard + +"once@npm:^1.3.1, once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: 1 + checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"openid-client@npm:^6.1.3": + version: 6.8.1 + resolution: "openid-client@npm:6.8.1" + dependencies: + jose: ^6.1.0 + oauth4webapi: ^3.8.2 + checksum: 05b2cdfe8d8e66502e42e7152b437ea2800325fa1216648e522bcef3a3ed7f8c894d8f02e362d9a3ac0da77f995a58313275199e5d0d0ea672a54d070b3f48af + languageName: node + linkType: hard + +"optionator@npm:^0.9.3": + version: 0.9.4 + resolution: "optionator@npm:0.9.4" + dependencies: + deep-is: ^0.1.3 + fast-levenshtein: ^2.0.6 + levn: ^0.4.1 + prelude-ls: ^1.2.1 + type-check: ^0.4.0 + word-wrap: ^1.2.5 + checksum: ecbd010e3dc73e05d239976422d9ef54a82a13f37c11ca5911dff41c98a6c7f0f163b27f922c37e7f8340af9d36febd3b6e9cef508f3339d4c393d7276d716bb + languageName: node + linkType: hard + +"otplib@npm:12.0.1": + version: 12.0.1 + resolution: "otplib@npm:12.0.1" + dependencies: + "@otplib/core": ^12.0.1 + "@otplib/preset-default": ^12.0.1 + "@otplib/preset-v11": ^12.0.1 + checksum: 4a1b91cf1b8e920b50ad4bac2ef2a89126630c62daf68e9b32ff15106b2551db905d3b979955cf5f8f114da0a8883cec3d636901d65e793c1745bb4174e2a572 + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: ^0.1.0 + checksum: 7c3690c4dbf62ef625671e20b7bdf1cbc9534e83352a2780f165b0d3ceba21907e77ad63401708145ca4e25bfc51636588d89a8c0aeb715e6c37d1c066430360 + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: ^3.0.2 + checksum: 1623088f36cf1cbca58e9b61c4e62bf0c60a07af5ae1ca99a720837356b5b6c5ba3eb1b2127e47a06865fee59dd0453cad7cc844cda9d5a62ac1a5a51b7c86d3 + languageName: node + linkType: hard + +"p-map@npm:^7.0.2": + version: 7.0.4 + resolution: "p-map@npm:7.0.4" + checksum: 4be2097e942f2fd3a4f4b0c6585c721f23851de8ad6484d20c472b3ea4937d5cd9a59914c832b1bceac7bf9d149001938036b82a52de0bc381f61ff2d35d26a5 + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: ^3.0.0 + checksum: 6ba8b255145cae9470cf5551eb74be2d22281587af787a2626683a6c20fbb464978784661478dd2a3f1dad74d1e802d403e1b03c1a31fab310259eec8ac560ff + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 505807199dfb7c50737b057dd8d351b82c033029ab94cb10a657609e00c1bc53b951cfdbccab8de04c5584d5eff31128ce6afd3db79281874a5ef2adbba55ed1 + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 + languageName: node + linkType: hard + +"path-scurry@npm:^2.0.0": + version: 2.0.1 + resolution: "path-scurry@npm:2.0.1" + dependencies: + lru-cache: ^11.0.0 + minipass: ^7.1.2 + checksum: a022c6c38fed836079d03f96540eafd4cd989acf287b99613c82300107f366e889513ad8b671a2039a9d251122621f9c6fa649f0bd4d50acf95a6943a6692dbf + languageName: node + linkType: hard + +"picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 050c865ce81119c4822c45d3c84f1ced46f93a0126febae20737bd05ca20589c564d6e9226977df859ed5e03dc73f02584a2b0faad36e896936238238b0446cf + languageName: node + linkType: hard + +"picomatch@npm:^4.0.3": + version: 4.0.3 + resolution: "picomatch@npm:4.0.3" + checksum: 6817fb74eb745a71445debe1029768de55fd59a42b75606f478ee1d0dc1aa6e78b711d041a7c9d5550e042642029b7f373dc1a43b224c4b7f12d23436735dba0 + languageName: node + linkType: hard + +"playwright-core@npm:1.58.0": + version: 1.58.0 + resolution: "playwright-core@npm:1.58.0" + bin: + playwright-core: cli.js + checksum: b7382011a9de8be947eefb93e75848bbe0653edd451697a89f4124ea9129197160644a9ff3073af30ce86e79693424dd38a91ca71236b137197854536610f291 + languageName: node + linkType: hard + +"playwright@npm:1.58.0": + version: 1.58.0 + resolution: "playwright@npm:1.58.0" + dependencies: + fsevents: 2.3.2 + playwright-core: 1.58.0 + dependenciesMeta: + fsevents: + optional: true + bin: + playwright: cli.js + checksum: 8f67b7dd946901867785f24c3a6fe4d4f284f4fe54d82193d515d8a45a6402725d06f43b8ed885d96f67671c1334e2f8a2131fe993e0f2c2ab29a56a32157489 + languageName: node + linkType: hard + +"prelude-ls@npm:^1.2.1": + version: 1.2.1 + resolution: "prelude-ls@npm:1.2.1" + checksum: cd192ec0d0a8e4c6da3bb80e4f62afe336df3f76271ac6deb0e6a36187133b6073a19e9727a1ff108cd8b9982e4768850d413baa71214dd80c7979617dca827a + languageName: node + linkType: hard + +"prettier@npm:^3.7.4": + version: 3.8.1 + resolution: "prettier@npm:3.8.1" + bin: + prettier: bin/prettier.cjs + checksum: 36fe4ecd95751aa17fea70b48afd5086e88002988238112fc1be30a5307af6983e1833be790b0cc1c54702b71f73b12bfec12c05166d7619e3151ab221654297 + languageName: node + linkType: hard + +"proc-log@npm:^6.0.0": + version: 6.1.0 + resolution: "proc-log@npm:6.1.0" + checksum: ac450ff8244e95b0c9935b52d629fef92ae69b7e39aea19972a8234259614d644402dd62ce9cb094f4a637d8a4514cba90c1456ad785a40ad5b64d502875a817 + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: ^2.0.2 + retry: ^0.12.0 + checksum: f96a3f6d90b92b568a26f71e966cbbc0f63ab85ea6ff6c81284dc869b41510e6cdef99b6b65f9030f0db422bf7c96652a3fff9f2e8fb4a0f069d8f4430359429 + languageName: node + linkType: hard + +"pump@npm:^3.0.0": + version: 3.0.3 + resolution: "pump@npm:3.0.3" + dependencies: + end-of-stream: ^1.1.0 + once: ^1.3.1 + checksum: 52843fc933b838c0330f588388115a1b28ef2a5ffa7774709b142e35431e8ab0c2edec90de3fa34ebb72d59fef854f151eea7dfc211b6dcf586b384556bd2f39 + languageName: node + linkType: hard + +"punycode@npm:^2.1.0": + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2 + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: f4ba0b8494846a5066328ad33ef8ac173801a51739eb4d63408c847da9a2e1c1de1e6cbbf72699211f3d13f8fc1325648b169bd15eb7da35688e30a5fb0e4a7f + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 623bd7d2e5119467ba66202d733ec3c2e2e26568074923bc0585b6b99db14f357e79bdedb63cab56cec47491c4a0da7e6021a7465ca6dc4f481d3898fdd3158c + languageName: node + linkType: hard + +"rfc4648@npm:^1.3.0": + version: 1.5.4 + resolution: "rfc4648@npm:1.5.4" + checksum: 4bfd555f16b8ed1bceb3cf4d79242cf3253a2600de51f45ac19549f366b4b8edc0d9b8feaae778059c137f1d10ebd87e125fc839636f6267a7b6badaa95a9f00 + languageName: node + linkType: hard + +"rhdh-e2e-test-utils@npm:1.1.0": + version: 1.1.0 + resolution: "rhdh-e2e-test-utils@npm:1.1.0" + dependencies: + "@axe-core/playwright": ^4.11.0 + "@eslint/js": ^9.39.1 + "@keycloak/keycloak-admin-client": ^26.0.0 + "@kubernetes/client-node": ^1.4.0 + boxen: ^8.0.1 + eslint: ^9.39.1 + eslint-plugin-check-file: ^3.3.1 + eslint-plugin-playwright: ^2.4.0 + fs-extra: ^11.3.2 + js-yaml: ^4.1.1 + lodash.mergewith: ^4.6.2 + otplib: 12.0.1 + prettier: ^3.7.4 + typescript: ^5.9.3 + typescript-eslint: ^8.48.1 + zx: ^8.8.5 + peerDependencies: + "@playwright/test": ^1.57.0 + checksum: 82e0f75e8ef56464d816195e0416be559ea382083a7f4af9ec61456b51244201d84dc6d9ce93f6670b715aa391e93a9394151551e743617c2fd6c23a20d07ce1 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.7.3": + version: 7.7.3 + resolution: "semver@npm:7.7.3" + bin: + semver: bin/semver.js + checksum: f013a3ee4607857bcd3503b6ac1d80165f7f8ea94f5d55e2d3e33df82fce487aa3313b987abf9b39e0793c83c9fc67b76c36c067625141a9f6f704ae0ea18db2 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: ^3.0.0 + checksum: 6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: b5167a7142c1da704c0e3af85c402002b597081dd9575031a90b4f229ca5678e9a36e8a374f1814c8156a725d17008ae3bde63b92f9cfd132526379e580bec8b + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3, socks-proxy-agent@npm:^8.0.4": + version: 8.0.5 + resolution: "socks-proxy-agent@npm:8.0.5" + dependencies: + agent-base: ^7.1.2 + debug: ^4.3.4 + socks: ^2.8.3 + checksum: b4fbcdb7ad2d6eec445926e255a1fb95c975db0020543fbac8dfa6c47aecc6b3b619b7fb9c60a3f82c9b2969912a5e7e174a056ae4d98cb5322f3524d6036e1d + languageName: node + linkType: hard + +"socks@npm:^2.8.3": + version: 2.8.7 + resolution: "socks@npm:2.8.7" + dependencies: + ip-address: ^10.0.1 + smart-buffer: ^4.2.0 + checksum: 4bbe2c88cf0eeaf49f94b7f11564a99b2571bde6fd1e714ff95b38f89e1f97858c19e0ab0e6d39eb7f6a984fa67366825895383ed563fe59962a1d57a1d55318 + languageName: node + linkType: hard + +"ssri@npm:^13.0.0": + version: 13.0.0 + resolution: "ssri@npm:13.0.0" + dependencies: + minipass: ^7.0.3 + checksum: 9705dff9e686b11f3035fb4c3d44ce690359a15a54adcd6a18951f2763f670877321178dc72c37a2b804dba3287ecaa48726dbd0cff79b2715b1cc24521b3af3 + languageName: node + linkType: hard + +"stream-buffers@npm:^3.0.2": + version: 3.0.3 + resolution: "stream-buffers@npm:3.0.3" + checksum: 3f0bdc4b1fd3ff370cef5a2103dd930b8981d42d97741eeb087a660771e27f0fc35fa8a351bb36e15bbbbce0eea00fefed60d6cdff4c6c3f527580529f183807 + languageName: node + linkType: hard + +"streamx@npm:^2.15.0, streamx@npm:^2.21.0": + version: 2.23.0 + resolution: "streamx@npm:2.23.0" + dependencies: + events-universal: ^1.0.0 + fast-fifo: ^1.3.2 + text-decoder: ^1.1.0 + checksum: d57de47db76ffd926842afab562fc8b139d7333269c6db11da5a233e8524cd326161b48bdddd28cefc010cec0b143af04dbb6f5e92d5034839ce7428928dfcae + languageName: node + linkType: hard + +"string-width@npm:^4.1.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: ^8.0.0 + is-fullwidth-code-point: ^3.0.0 + strip-ansi: ^6.0.1 + checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + +"string-width@npm:^7.0.0, string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: ^10.3.0 + get-east-asian-width: ^1.0.0 + strip-ansi: ^7.1.0 + checksum: 42f9e82f61314904a81393f6ef75b832c39f39761797250de68c041d8ba4df2ef80db49ab6cd3a292923a6f0f409b8c9980d120f7d32c820b4a8a84a2598a295 + languageName: node + linkType: hard + +"strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + +"strip-ansi@npm:^7.1.0": + version: 7.1.2 + resolution: "strip-ansi@npm:7.1.2" + dependencies: + ansi-regex: ^6.0.1 + checksum: db0e3f9654e519c8a33c50fc9304d07df5649388e7da06d3aabf66d29e5ad65d5e6315d8519d409c15b32fa82c1df7e11ed6f8cd50b0e4404463f0c9d77c8d0b + languageName: node + linkType: hard + +"strip-json-comments@npm:^3.1.1": + version: 3.1.1 + resolution: "strip-json-comments@npm:3.1.1" + checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: ^4.0.0 + checksum: 3dda818de06ebbe5b9653e07842d9479f3555ebc77e9a0280caf5a14fb877ffee9ed57007c3b78f5a6324b8dbeec648d9e97a24e2ed9fdb81ddc69ea07100f4a + languageName: node + linkType: hard + +"tar-fs@npm:^3.0.9": + version: 3.1.1 + resolution: "tar-fs@npm:3.1.1" + dependencies: + bare-fs: ^4.0.1 + bare-path: ^3.0.0 + pump: ^3.0.0 + tar-stream: ^3.1.5 + dependenciesMeta: + bare-fs: + optional: true + bare-path: + optional: true + checksum: eaae4c5410897e00bd03c44bcaf9bad43f793d1dd8fa2fe0ba808c809c8b169d6f40c093eab0ce20e12e9b6b8bf63f11438b2a6a8695c1dfc441fa71a5013896 + languageName: node + linkType: hard + +"tar-stream@npm:^3.1.5": + version: 3.1.7 + resolution: "tar-stream@npm:3.1.7" + dependencies: + b4a: ^1.6.4 + fast-fifo: ^1.2.0 + streamx: ^2.15.0 + checksum: 6393a6c19082b17b8dcc8e7fd349352bb29b4b8bfe1075912b91b01743ba6bb4298f5ff0b499a3bbaf82121830e96a1a59d4f21a43c0df339e54b01789cb8cc6 + languageName: node + linkType: hard + +"tar@npm:^7.5.4": + version: 7.5.7 + resolution: "tar@npm:7.5.7" + dependencies: + "@isaacs/fs-minipass": ^4.0.0 + chownr: ^3.0.0 + minipass: ^7.1.2 + minizlib: ^3.1.0 + yallist: ^5.0.0 + checksum: 82fa04804b6cae4c0b46b84e97a08c39e1c17bb959350baa32d139bcf5e1fc7ebc3ceb72465dd3e2e311992386ecc13599a257d5672158490ceb9464146d5573 + languageName: node + linkType: hard + +"text-decoder@npm:^1.1.0": + version: 1.2.3 + resolution: "text-decoder@npm:1.2.3" + dependencies: + b4a: ^1.6.4 + checksum: d7642a61f9d72330eac52ff6b6e8d34dea03ebbb1e82749a8734e7892e246cf262ed70730d20c4351c5dc5334297b9cc6c0b6a8725a204a63a197d7728bb35e5 + languageName: node + linkType: hard + +"thirty-two@npm:^1.0.2": + version: 1.0.2 + resolution: "thirty-two@npm:1.0.2" + checksum: f6700b31d16ef942fdc0d14daed8a2f69ea8b60b0e85db8b83adf58d84bbeafe95a17d343ab55efaae571bb5148b62fc0ee12b04781323bf7af7d7e9693eec76 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15": + version: 0.2.15 + resolution: "tinyglobby@npm:0.2.15" + dependencies: + fdir: ^6.5.0 + picomatch: ^4.0.3 + checksum: 0e33b8babff966c6ab86e9b825a350a6a98a63700fa0bb7ae6cf36a7770a508892383adc272f7f9d17aaf46a9d622b455e775b9949a3f951eaaf5dfb26331d44 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: ^7.0.0 + checksum: f76fa01b3d5be85db6a2a143e24df9f60dd047d151062d0ba3df62953f2f697b16fe5dad9b0ac6191c7efc7b1d9dcaa4b768174b7b29da89d4428e64bc0a20ed + languageName: node + linkType: hard + +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 726321c5eaf41b5002e17ffbd1fb7245999a073e8979085dacd47c4b4e8068ff5777142fc6726d6ca1fd2ff16921b48788b87225cbc57c72636f6efa8efbffe3 + languageName: node + linkType: hard + +"ts-api-utils@npm:^2.4.0": + version: 2.4.0 + resolution: "ts-api-utils@npm:2.4.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: beae72a4fa22a7cc91a8a0f3dfb487d72e30f06ac50ff72f327d061dea2d4940c6451d36578d949caad3893d4d2c7d42d53b7663597ccda54ad32cdb842c3e34 + languageName: node + linkType: hard + +"type-check@npm:^0.4.0, type-check@npm:~0.4.0": + version: 0.4.0 + resolution: "type-check@npm:0.4.0" + dependencies: + prelude-ls: ^1.2.1 + checksum: ec688ebfc9c45d0c30412e41ca9c0cdbd704580eb3a9ccf07b9b576094d7b86a012baebc95681999dd38f4f444afd28504cb3a89f2ef16b31d4ab61a0739025a + languageName: node + linkType: hard + +"type-fest@npm:^4.21.0": + version: 4.41.0 + resolution: "type-fest@npm:4.41.0" + checksum: 7055c0e3eb188425d07403f1d5dc175ca4c4f093556f26871fe22041bc93d137d54bef5851afa320638ca1379106c594f5aa153caa654ac1a7f22c71588a4e80 + languageName: node + linkType: hard + +"typescript-eslint@npm:^8.48.1, typescript-eslint@npm:^8.50.0": + version: 8.54.0 + resolution: "typescript-eslint@npm:8.54.0" + dependencies: + "@typescript-eslint/eslint-plugin": 8.54.0 + "@typescript-eslint/parser": 8.54.0 + "@typescript-eslint/typescript-estree": 8.54.0 + "@typescript-eslint/utils": 8.54.0 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <6.0.0" + checksum: 9418344c1d6aabae2247d8b51354d26e81e8b486541234d4823a71fd06a310eb715acf2b92fb038790e1e4c80c477e9ec90fb130885dda303fc00b94911b62ac + languageName: node + linkType: hard + +"typescript@npm:^5.9.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 0d0ffb84f2cd072c3e164c79a2e5a1a1f4f168e84cb2882ff8967b92afe1def6c2a91f6838fb58b168428f9458c57a2ba06a6737711fdd87a256bbe83e9a217f + languageName: node + linkType: hard + +"typescript@patch:typescript@^5.9.3#~builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#~builtin::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: a5a6dc399d3761ded54192031f11d3ad5df8001c7febe3fbbc8098efcb552cdf8f2f402b3618c56dafcd04fef63dee005f4900f608e185404caedc46480539ed + languageName: node + linkType: hard + +"undici-types@npm:~7.16.0": + version: 7.16.0 + resolution: "undici-types@npm:7.16.0" + checksum: 1ef68fc6c5bad200c8b6f17de8e5bc5cfdcadc164ba8d7208cd087cfa8583d922d8316a7fd76c9a658c22b4123d3ff847429185094484fbc65377d695c905857 + languageName: node + linkType: hard + +"unique-filename@npm:^5.0.0": + version: 5.0.0 + resolution: "unique-filename@npm:5.0.0" + dependencies: + unique-slug: ^6.0.0 + checksum: a5f67085caef74bdd2a6869a200ed5d68d171f5cc38435a836b5fd12cce4e4eb55e6a190298035c325053a5687ed7a3c96f0a91e82215fd14729769d9ac57d9b + languageName: node + linkType: hard + +"unique-slug@npm:^6.0.0": + version: 6.0.0 + resolution: "unique-slug@npm:6.0.0" + dependencies: + imurmurhash: ^0.1.4 + checksum: ad6cf238b10292d944521714d31bc9f3ca79fa80cb7a154aad183056493f98e85de669412c6bbfe527ffa9bdeff36d3dd4d5bccaf562c794f2580ab11932b691 + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60 + languageName: node + linkType: hard + +"uri-js@npm:^4.2.2": + version: 4.4.1 + resolution: "uri-js@npm:4.4.1" + dependencies: + punycode: ^2.1.0 + checksum: 7167432de6817fe8e9e0c9684f1d2de2bb688c94388f7569f7dbdb1587c9f4ca2a77962f134ec90be0cc4d004c939ff0d05acc9f34a0db39a3c797dada262633 + languageName: node + linkType: hard + +"url-template@npm:^3.1.1": + version: 3.1.1 + resolution: "url-template@npm:3.1.1" + checksum: ac09daaeaec55a6b070b838ed161d66b050a46fc12ac251cb2db1ce356e786cfb117ee4391d943aaaa757971c509a0142b3cd83dfd8cc3d7b6d90a99d001a5f9 + languageName: node + linkType: hard + +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: c92a0a6ab95314bde9c32e1d0a6dfac83b578f8fa5f21e675bc2706ed6981bc26b7eb7e6a1fab158e5ce4adf9caa4a0aee49a52505d4d13c7be545f15021b17c + languageName: node + linkType: hard + +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: ~0.0.3 + webidl-conversions: ^3.0.0 + checksum: b8daed4ad3356cc4899048a15b2c143a9aed0dfae1f611ebd55073310c7b910f522ad75d727346ad64203d7e6c79ef25eafd465f4d12775ca44b90fa82ed9e2c + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: ^2.0.0 + bin: + node-which: ./bin/node-which + checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 + languageName: node + linkType: hard + +"which@npm:^6.0.0": + version: 6.0.0 + resolution: "which@npm:6.0.0" + dependencies: + isexe: ^3.1.1 + bin: + node-which: bin/which.js + checksum: df19b2cd8aac94b333fa29b42e8e371a21e634a742a3b156716f7752a5afe1d73fb5d8bce9b89326f453d96879e8fe626eb421e0117eb1a3ce9fd8c97f6b7db9 + languageName: node + linkType: hard + +"widest-line@npm:^5.0.0": + version: 5.0.0 + resolution: "widest-line@npm:5.0.0" + dependencies: + string-width: ^7.0.0 + checksum: 07f6527b961b88d40ac250596c06fada00cbe049080c6cc8ef4d7bc4f4ab03d7eb1a1c2e5585dd0d8b6ec99ba6f168d5b236edd8ba9221aeb8d914451f0235f9 + languageName: node + linkType: hard + +"word-wrap@npm:^1.2.5": + version: 1.2.5 + resolution: "word-wrap@npm:1.2.5" + checksum: f93ba3586fc181f94afdaff3a6fef27920b4b6d9eaefed0f428f8e07adea2a7f54a5f2830ce59406c8416f033f86902b91eb824072354645eea687dff3691ccb + languageName: node + linkType: hard + +"wrap-ansi@npm:^9.0.0": + version: 9.0.2 + resolution: "wrap-ansi@npm:9.0.2" + dependencies: + ansi-styles: ^6.2.1 + string-width: ^7.0.0 + strip-ansi: ^7.1.0 + checksum: 9827bf8bbb341d2d15f26d8507d98ca2695279359073422fe089d374b30e233d24ab95beca55cf9ab8dcb89face00e919be4158af50d4b6d8eab5ef4ee399e0c + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard + +"ws@npm:^8.18.2": + version: 8.19.0 + resolution: "ws@npm:8.19.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 7a426122c373e053a65a2affbcdcdbf8f643ba0265577afd4e08595397ca244c05de81570300711e2363a9dab5aea3ae644b445bc7468b1ebbb51bfe2efb20e1 + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: eba51182400b9f35b017daa7f419f434424410691bbc5de4f4240cc830fdef906b504424992700dc047f16b4d99100a6f8b8b11175c193f38008e9c96322b6a5 + languageName: node + linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 + languageName: node + linkType: hard + +"zx@npm:^8.8.5": + version: 8.8.5 + resolution: "zx@npm:8.8.5" + bin: + zx: build/cli.js + checksum: bf3bb23ae0d0a1198ea53a7ac7e341900b33251aeb4ed657dcbfcc76ed7b0411bd47cc6bdaf67f7b2b79f83339e25d1e47bbf359a8ededfdc33792fb22a92c4c + languageName: node + linkType: hard From bf284d85761d663b73208c6c060be44e8bb0aa80 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Thu, 26 Feb 2026 23:17:37 +0200 Subject: [PATCH 02/26] feat(e2e): fix code format Signed-off-by: Oleksandr Andriienko --- .../tests/config/app-config-rhdh.yaml | 2 +- .../tests/config/dynamic-plugins.yaml | 42 +++++++++---------- .../e2e-tests/tests/specs/bulk-import.spec.ts | 16 +++---- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml index 7a0a91b18..17f7754be 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml @@ -4,7 +4,7 @@ integrations: github: - host: github.com - token: ${GITHUB_TOKEN} + token: ${GITHUB_TOKEN} # apps: # - appId: ${GITHUB_APP_APP_ID} # clientId: ${GITHUB_APP_CLIENT_ID} diff --git a/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml b/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml index 7564e2dda..17f231759 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml @@ -33,29 +33,29 @@ plugins: frontend: red-hat-developer-hub.backstage-plugin-orchestrator: appIcons: - - importName: OrchestratorIcon - name: orchestratorIcon + - importName: OrchestratorIcon + name: orchestratorIcon dynamicRoutes: - - importName: OrchestratorPage - menuItem: - icon: orchestratorIcon - text: Orchestrator - textKey: menuItem.orchestrator - path: /orchestrator + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + textKey: menuItem.orchestrator + path: /orchestrator entityTabs: - - path: /workflows - title: Workflows - titleKey: catalog.entityPage.workflows.title - mountPoint: entity.page.workflows + - path: /workflows + title: Workflows + titleKey: catalog.entityPage.workflows.title + mountPoint: entity.page.workflows mountPoints: - - mountPoint: entity.page.workflows/cards - importName: OrchestratorCatalogTab - config: - layout: - gridColumn: 1 / -1 - if: - anyOf: - - IsOrchestratorCatalogTabAvailable + - mountPoint: entity.page.workflows/cards + importName: OrchestratorCatalogTab + config: + layout: + gridColumn: 1 / -1 + if: + anyOf: + - IsOrchestratorCatalogTabAvailable - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-backend:bs_1.45.3__8.5.1" disabled: false @@ -76,4 +76,4 @@ plugins: pluginConfig: dynamicPlugins: frontend: - red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: { } + red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: {} diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index c3b7f3b5f..d477e2279 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -2,13 +2,13 @@ import { APIHelper } from "rhdh-e2e-test-utils/helpers"; import { test, expect, Page } from "../fixtures"; export const WAIT_OBJECTS = { - MuiLinearProgress: 'div[class*="MuiLinearProgress-root"]', - MuiCircularProgress: '[class*="MuiCircularProgress-root"]', + muiLinearProgress: 'div[class*="MuiLinearProgress-root"]', + muiCircularProgress: '[class*="MuiCircularProgress-root"]', }; test.describe("Bulk import tests", () => { const catalogRepoName = `janus-test-1-bulk-import-test-${Date.now()}`; - const githubOrg = 'cloud-eda'; + const githubOrg = "cloud-eda"; const catalogRepoDetails = { name: catalogRepoName, url: `github.com/${githubOrg}/${catalogRepoName}`, @@ -118,9 +118,9 @@ test.describe("Bulk import tests", () => { }); const workflowPage = await clickLinkWithNewTab(page, "View workflow"); - await expect(workflowPage.getByRole("link", { name: "PR_URL" })).toBeVisible( - { timeout: 10000 }, - ); + await expect( + workflowPage.getByRole("link", { name: "PR_URL" }), + ).toBeVisible({ timeout: 10000 }); }); test.afterAll(async () => { @@ -135,7 +135,9 @@ test.describe("Bulk import tests", () => { `[Cleanup] Deleted GitHub repository: ${catalogRepoDetails.name}`, ); } catch (error) { - console.error(`[Cleanup] Final cleanup failed: ${(error as any).message}`); + console.error( + `[Cleanup] Final cleanup failed: ${(error as any).message}`, + ); } }); }); From 83be7bdaf58db16c86f8c3b23c7b66966ade3831 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Thu, 26 Feb 2026 23:30:17 +0200 Subject: [PATCH 03/26] feat(e2e): fix tsc check Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/package.json | 3 ++- .../bulk-import/e2e-tests/tests/fixtures.ts | 18 +++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/package.json b/workspaces/bulk-import/e2e-tests/package.json index 45ab62949..836933d57 100644 --- a/workspaces/bulk-import/e2e-tests/package.json +++ b/workspaces/bulk-import/e2e-tests/package.json @@ -18,7 +18,8 @@ "lint:fix": "eslint . --fix", "prettier:check": "prettier --check .", "prettier:fix": "prettier --write .", - "check": "tsc --noEmit && yarn lint:check && yarn prettier:check" + "check": "tsc --noEmit && yarn lint:check && yarn prettier:check", + "tsc:check": "tsc --noEmit" }, "devDependencies": { "@eslint/js": "^9.39.2", diff --git a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts index 57f179f6a..36daf3285 100644 --- a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts +++ b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts @@ -9,13 +9,17 @@ import { $ } from "rhdh-e2e-test-utils/utils"; const BEFORE_ALL_TIMEOUT_MS = 30 * 60 * 1000; // 30 min for orchestrator + RHDH deploy -export const test = base.extend<{ - rhdhDeploymentWorker: RHDHDeployment; - rhdh: RHDHDeployment; - uiHelper: UIhelper; - loginHelper: LoginHelper; - baseURL: string; -}>({ +export const test = base.extend< + { + rhdh: RHDHDeployment; + uiHelper: UIhelper; + loginHelper: LoginHelper; + baseURL: string; + }, + { + rhdhDeploymentWorker: RHDHDeployment; + } +>({ rhdhDeploymentWorker: [ async ({}, use, workerInfo) => { const projectName = workerInfo.project.name; From d17fd63c3b76f1ec0417a39e629e00c32ea05597 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Fri, 27 Feb 2026 00:16:19 +0200 Subject: [PATCH 04/26] feat(e2e): fix up Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/tests/fixtures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts index 36daf3285..26fdd9387 100644 --- a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts +++ b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts @@ -36,7 +36,7 @@ export const test = base.extend< const orchestratorNamespace = "orchestrator"; await $`bash tests/scripts/install-orchestrator.sh ${orchestratorNamespace}`; - await rhdhDeployment.deploy({ timeoutMs: BEFORE_ALL_TIMEOUT_MS }); + await rhdhDeployment.deploy(); await use(rhdhDeployment); } finally { From bb182280d6d6c27b73d6a663067c6f318c6e220a Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 3 Mar 2026 12:00:30 +0200 Subject: [PATCH 05/26] feat(e2e): use newer e2e library Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/package.json | 2 +- .../e2e-tests/playwright.config.ts | 2 +- .../bulk-import/e2e-tests/tests/fixtures.ts | 6 +- .../e2e-tests/tests/specs/bulk-import.spec.ts | 2 +- .../bulk-import/e2e-tests/tsconfig.json | 2 +- workspaces/bulk-import/e2e-tests/yarn.lock | 223 +++--------------- 6 files changed, 41 insertions(+), 196 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/package.json b/workspaces/bulk-import/e2e-tests/package.json index 836933d57..121e05da7 100644 --- a/workspaces/bulk-import/e2e-tests/package.json +++ b/workspaces/bulk-import/e2e-tests/package.json @@ -24,13 +24,13 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@playwright/test": "^1.56.1", + "@red-hat-developer-hub/e2e-test-utils": "1.1.10", "@types/node": "^24.10.1", "dotenv": "^16.4.7", "eslint": "^9.39.2", "eslint-plugin-check-file": "^3.3.1", "eslint-plugin-playwright": "^2.4.0", "prettier": "^3.7.4", - "rhdh-e2e-test-utils": "1.1.0", "typescript": "^5.9.3", "typescript-eslint": "^8.50.0" } diff --git a/workspaces/bulk-import/e2e-tests/playwright.config.ts b/workspaces/bulk-import/e2e-tests/playwright.config.ts index 495ac5ab0..91d0b8566 100644 --- a/workspaces/bulk-import/e2e-tests/playwright.config.ts +++ b/workspaces/bulk-import/e2e-tests/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from "rhdh-e2e-test-utils/playwright-config"; +import { defineConfig } from "@red-hat-developer-hub/e2e-test-utils/playwright-config"; import dotenv from "dotenv"; dotenv.config({ path: `${import.meta.dirname}/.env` }); diff --git a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts index 26fdd9387..d7ab9b23e 100644 --- a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts +++ b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts @@ -3,9 +3,9 @@ * Uses standard @playwright/test so timeout is not overridden by rhdh-e2e-test-utils (500s). */ import { test as base } from "@playwright/test"; -import { RHDHDeployment } from "rhdh-e2e-test-utils/rhdh"; -import { LoginHelper, UIhelper } from "rhdh-e2e-test-utils/helpers"; -import { $ } from "rhdh-e2e-test-utils/utils"; +import { RHDHDeployment } from "@red-hat-developer-hub/e2e-test-utils/rhdh"; +import { LoginHelper, UIhelper } from "@red-hat-developer-hub/e2e-test-utils/helpers"; +import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils"; const BEFORE_ALL_TIMEOUT_MS = 30 * 60 * 1000; // 30 min for orchestrator + RHDH deploy diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index d477e2279..51040911d 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -1,4 +1,4 @@ -import { APIHelper } from "rhdh-e2e-test-utils/helpers"; +import { APIHelper } from "@red-hat-developer-hub/e2e-test-utils/helpers"; import { test, expect, Page } from "../fixtures"; export const WAIT_OBJECTS = { diff --git a/workspaces/bulk-import/e2e-tests/tsconfig.json b/workspaces/bulk-import/e2e-tests/tsconfig.json index f875a2050..ede55abc0 100644 --- a/workspaces/bulk-import/e2e-tests/tsconfig.json +++ b/workspaces/bulk-import/e2e-tests/tsconfig.json @@ -1,4 +1,4 @@ { - "extends": "rhdh-e2e-test-utils/tsconfig", + "extends": "@red-hat-developer-hub/e2e-test-utils/tsconfig", "include": ["**/*.ts"] } diff --git a/workspaces/bulk-import/e2e-tests/yarn.lock b/workspaces/bulk-import/e2e-tests/yarn.lock index 93eef77a5..343609eb6 100644 --- a/workspaces/bulk-import/e2e-tests/yarn.lock +++ b/workspaces/bulk-import/e2e-tests/yarn.lock @@ -293,6 +293,32 @@ __metadata: languageName: node linkType: hard +"@red-hat-developer-hub/e2e-test-utils@npm:1.1.10": + version: 1.1.10 + resolution: "@red-hat-developer-hub/e2e-test-utils@npm:1.1.10" + dependencies: + "@axe-core/playwright": ^4.11.0 + "@eslint/js": ^9.39.1 + "@keycloak/keycloak-admin-client": ^26.0.0 + "@kubernetes/client-node": ^1.4.0 + eslint: ^9.39.1 + eslint-plugin-check-file: ^3.3.1 + eslint-plugin-playwright: ^2.4.0 + fs-extra: ^11.3.2 + js-yaml: ^4.1.1 + lodash.clonedeepwith: ^4.5.0 + lodash.mergewith: ^4.6.2 + otplib: 12.0.1 + prettier: ^3.7.4 + typescript: ^5.9.3 + typescript-eslint: ^8.48.1 + zx: ^8.8.5 + peerDependencies: + "@playwright/test": ^1.57.0 + checksum: b12ed69e5b1c9604336e451f50d9e506ef9ba152924927aab4292db39d52e3baecf9d243774b2a4dcd5a325a22c9afa4aa82b9b77463263b3161ab34e941c13d + languageName: node + linkType: hard + "@types/estree@npm:^1.0.6": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" @@ -530,29 +556,6 @@ __metadata: languageName: node linkType: hard -"ansi-align@npm:^3.0.1": - version: 3.0.1 - resolution: "ansi-align@npm:3.0.1" - dependencies: - string-width: ^4.1.0 - checksum: 6abfa08f2141d231c257162b15292467081fa49a208593e055c866aa0455b57f3a86b5a678c190c618faa79b4c59e254493099cb700dd9cf2293c6be2c8f5d8d - languageName: node - linkType: hard - -"ansi-regex@npm:^5.0.1": - version: 5.0.1 - resolution: "ansi-regex@npm:5.0.1" - checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b - languageName: node - linkType: hard - -"ansi-regex@npm:^6.0.1": - version: 6.2.2 - resolution: "ansi-regex@npm:6.2.2" - checksum: 9b17ce2c6daecc75bcd5966b9ad672c23b184dc3ed9bf3c98a0702f0d2f736c15c10d461913568f2cf527a5e64291c7473358885dd493305c84a1cfed66ba94f - languageName: node - linkType: hard - "ansi-styles@npm:^4.1.0": version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" @@ -562,13 +565,6 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.2.1": - version: 6.2.3 - resolution: "ansi-styles@npm:6.2.3" - checksum: f1b0829cf048cce870a305819f65ce2adcebc097b6d6479e12e955fd6225df9b9eb8b497083b764df796d94383ff20016cc4dbbae5b40f36138fb65a9d33c2e2 - languageName: node - linkType: hard - "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -695,22 +691,6 @@ __metadata: languageName: node linkType: hard -"boxen@npm:^8.0.1": - version: 8.0.1 - resolution: "boxen@npm:8.0.1" - dependencies: - ansi-align: ^3.0.1 - camelcase: ^8.0.0 - chalk: ^5.3.0 - cli-boxes: ^3.0.0 - string-width: ^7.2.0 - type-fest: ^4.21.0 - widest-line: ^5.0.0 - wrap-ansi: ^9.0.0 - checksum: f42d9e628e03e5c84ac9cda3173f75cadbdf60ed94fc06aaeef79f7c84a8181c4d79a8f40253192a1613993036c81811ad6957f346e5aa6abb7e9d1d799cbfd5 - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.12 resolution: "brace-expansion@npm:1.1.12" @@ -745,13 +725,13 @@ __metadata: dependencies: "@eslint/js": ^9.39.2 "@playwright/test": ^1.56.1 + "@red-hat-developer-hub/e2e-test-utils": 1.1.10 "@types/node": ^24.10.1 dotenv: ^16.4.7 eslint: ^9.39.2 eslint-plugin-check-file: ^3.3.1 eslint-plugin-playwright: ^2.4.0 prettier: ^3.7.4 - rhdh-e2e-test-utils: 1.1.0 typescript: ^5.9.3 typescript-eslint: ^8.50.0 languageName: unknown @@ -793,13 +773,6 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^8.0.0": - version: 8.0.0 - resolution: "camelcase@npm:8.0.0" - checksum: 6da7abe997af29e80052f17aa21628c7cce14af364cef9f07a2a44d59614dd6f361d405f121938e673424d673697a8c53ad17be8c4b03b0a727307c4db8b5b5e - languageName: node - linkType: hard - "camelize-ts@npm:^3.0.0": version: 3.0.0 resolution: "camelize-ts@npm:3.0.0" @@ -817,13 +790,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^5.3.0": - version: 5.6.2 - resolution: "chalk@npm:5.6.2" - checksum: 4ee2d47a626d79ca27cb5299ecdcce840ef5755e287412536522344db0fc51ca0f6d6433202332c29e2288c6a90a2b31f3bd626bc8c14743b6b6ee28abd3b796 - languageName: node - linkType: hard - "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -831,13 +797,6 @@ __metadata: languageName: node linkType: hard -"cli-boxes@npm:^3.0.0": - version: 3.0.0 - resolution: "cli-boxes@npm:3.0.0" - checksum: 637d84419d293a9eac40a1c8c96a2859e7d98b24a1a317788e13c8f441be052fc899480c6acab3acc82eaf1bccda6b7542d7cdcf5c9c3cc39227175dc098d5b2 - languageName: node - linkType: hard - "color-convert@npm:^2.0.1": version: 2.0.1 resolution: "color-convert@npm:2.0.1" @@ -925,20 +884,6 @@ __metadata: languageName: node linkType: hard -"emoji-regex@npm:^10.3.0": - version: 10.6.0 - resolution: "emoji-regex@npm:10.6.0" - checksum: 8785f6a7ec4559c931bd6640f748fe23791f5af4c743b131d458c5551b4aa7da2a9cd882518723cb3859e8b0b59b0cc08f2ce0f8e65c61a026eed71c2dc407d5 - languageName: node - linkType: hard - -"emoji-regex@npm:^8.0.0": - version: 8.0.0 - resolution: "emoji-regex@npm:8.0.0" - checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 - languageName: node - linkType: hard - "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -1319,13 +1264,6 @@ __metadata: languageName: node linkType: hard -"get-east-asian-width@npm:^1.0.0": - version: 1.4.0 - resolution: "get-east-asian-width@npm:1.4.0" - checksum: 1d9a81a8004f4217ebef5d461875047d269e4b57e039558fd65130877cd4da8e3f61e1c4eada0c8b10e2816c7baf7d5fddb7006f561da13bc6f6dd19c1e964a4 - languageName: node - linkType: hard - "get-intrinsic@npm:^1.2.6": version: 1.3.1 resolution: "get-intrinsic@npm:1.3.1" @@ -1525,13 +1463,6 @@ __metadata: languageName: node linkType: hard -"is-fullwidth-code-point@npm:^3.0.0": - version: 3.0.0 - resolution: "is-fullwidth-code-point@npm:3.0.0" - checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 - languageName: node - linkType: hard - "is-glob@npm:^4.0.0, is-glob@npm:^4.0.3": version: 4.0.3 resolution: "is-glob@npm:4.0.3" @@ -1672,6 +1603,13 @@ __metadata: languageName: node linkType: hard +"lodash.clonedeepwith@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.clonedeepwith@npm:4.5.0" + checksum: 9fbf4ebfa04b381df226a2298eba680327bea3d0d5d19c5118de7ae218fd219186e30e9fd0d33b13729f34ffbc83c1cf09cb27aff265ba94cb602b8a2b1e71c9 + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -2132,32 +2070,6 @@ __metadata: languageName: node linkType: hard -"rhdh-e2e-test-utils@npm:1.1.0": - version: 1.1.0 - resolution: "rhdh-e2e-test-utils@npm:1.1.0" - dependencies: - "@axe-core/playwright": ^4.11.0 - "@eslint/js": ^9.39.1 - "@keycloak/keycloak-admin-client": ^26.0.0 - "@kubernetes/client-node": ^1.4.0 - boxen: ^8.0.1 - eslint: ^9.39.1 - eslint-plugin-check-file: ^3.3.1 - eslint-plugin-playwright: ^2.4.0 - fs-extra: ^11.3.2 - js-yaml: ^4.1.1 - lodash.mergewith: ^4.6.2 - otplib: 12.0.1 - prettier: ^3.7.4 - typescript: ^5.9.3 - typescript-eslint: ^8.48.1 - zx: ^8.8.5 - peerDependencies: - "@playwright/test": ^1.57.0 - checksum: 82e0f75e8ef56464d816195e0416be559ea382083a7f4af9ec61456b51244201d84dc6d9ce93f6670b715aa391e93a9394151551e743617c2fd6c23a20d07ce1 - languageName: node - linkType: hard - "safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -2245,46 +2157,6 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^4.1.0": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: ^8.0.0 - is-fullwidth-code-point: ^3.0.0 - strip-ansi: ^6.0.1 - checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb - languageName: node - linkType: hard - -"string-width@npm:^7.0.0, string-width@npm:^7.2.0": - version: 7.2.0 - resolution: "string-width@npm:7.2.0" - dependencies: - emoji-regex: ^10.3.0 - get-east-asian-width: ^1.0.0 - strip-ansi: ^7.1.0 - checksum: 42f9e82f61314904a81393f6ef75b832c39f39761797250de68c041d8ba4df2ef80db49ab6cd3a292923a6f0f409b8c9980d120f7d32c820b4a8a84a2598a295 - languageName: node - linkType: hard - -"strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" - dependencies: - ansi-regex: ^5.0.1 - checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c - languageName: node - linkType: hard - -"strip-ansi@npm:^7.1.0": - version: 7.1.2 - resolution: "strip-ansi@npm:7.1.2" - dependencies: - ansi-regex: ^6.0.1 - checksum: db0e3f9654e519c8a33c50fc9304d07df5649388e7da06d3aabf66d29e5ad65d5e6315d8519d409c15b32fa82c1df7e11ed6f8cd50b0e4404463f0c9d77c8d0b - languageName: node - linkType: hard - "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -2402,13 +2274,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^4.21.0": - version: 4.41.0 - resolution: "type-fest@npm:4.41.0" - checksum: 7055c0e3eb188425d07403f1d5dc175ca4c4f093556f26871fe22041bc93d137d54bef5851afa320638ca1379106c594f5aa153caa654ac1a7f22c71588a4e80 - languageName: node - linkType: hard - "typescript-eslint@npm:^8.48.1, typescript-eslint@npm:^8.50.0": version: 8.54.0 resolution: "typescript-eslint@npm:8.54.0" @@ -2531,15 +2396,6 @@ __metadata: languageName: node linkType: hard -"widest-line@npm:^5.0.0": - version: 5.0.0 - resolution: "widest-line@npm:5.0.0" - dependencies: - string-width: ^7.0.0 - checksum: 07f6527b961b88d40ac250596c06fada00cbe049080c6cc8ef4d7bc4f4ab03d7eb1a1c2e5585dd0d8b6ec99ba6f168d5b236edd8ba9221aeb8d914451f0235f9 - languageName: node - linkType: hard - "word-wrap@npm:^1.2.5": version: 1.2.5 resolution: "word-wrap@npm:1.2.5" @@ -2547,17 +2403,6 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^9.0.0": - version: 9.0.2 - resolution: "wrap-ansi@npm:9.0.2" - dependencies: - ansi-styles: ^6.2.1 - string-width: ^7.0.0 - strip-ansi: ^7.1.0 - checksum: 9827bf8bbb341d2d15f26d8507d98ca2695279359073422fe089d374b30e233d24ab95beca55cf9ab8dcb89face00e919be4158af50d4b6d8eab5ef4ee399e0c - languageName: node - linkType: hard - "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" From f6866f26d259faf9cf340f01bfad7d44bfbf8e7a Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 3 Mar 2026 15:14:06 +0200 Subject: [PATCH 06/26] feat(e2e): skip custom fixture Signed-off-by: Oleksandr Andriienko --- .../bulk-import/e2e-tests/tests/fixtures.ts | 82 ------------------- .../e2e-tests/tests/specs/bulk-import.spec.ts | 12 ++- 2 files changed, 10 insertions(+), 84 deletions(-) delete mode 100644 workspaces/bulk-import/e2e-tests/tests/fixtures.ts diff --git a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts b/workspaces/bulk-import/e2e-tests/tests/fixtures.ts deleted file mode 100644 index d7ab9b23e..000000000 --- a/workspaces/bulk-import/e2e-tests/tests/fixtures.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Custom Playwright test with RHDH fixtures. - * Uses standard @playwright/test so timeout is not overridden by rhdh-e2e-test-utils (500s). - */ -import { test as base } from "@playwright/test"; -import { RHDHDeployment } from "@red-hat-developer-hub/e2e-test-utils/rhdh"; -import { LoginHelper, UIhelper } from "@red-hat-developer-hub/e2e-test-utils/helpers"; -import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils"; - -const BEFORE_ALL_TIMEOUT_MS = 30 * 60 * 1000; // 30 min for orchestrator + RHDH deploy - -export const test = base.extend< - { - rhdh: RHDHDeployment; - uiHelper: UIhelper; - loginHelper: LoginHelper; - baseURL: string; - }, - { - rhdhDeploymentWorker: RHDHDeployment; - } ->({ - rhdhDeploymentWorker: [ - async ({}, use, workerInfo) => { - const projectName = workerInfo.project.name; - console.log( - `Deploying rhdh for plugin ${projectName} in namespace ${projectName}`, - ); - const rhdhDeployment = new RHDHDeployment(projectName); - - try { - base.setTimeout(BEFORE_ALL_TIMEOUT_MS); - - await rhdhDeployment.configure({ auth: "keycloak" }); - - const orchestratorNamespace = "orchestrator"; - await $`bash tests/scripts/install-orchestrator.sh ${orchestratorNamespace}`; - - await rhdhDeployment.deploy(); - - await use(rhdhDeployment); - } finally { - if (process.env.CI) { - console.log(`Deleting namespace ${projectName}`); - await rhdhDeployment.teardown(); - } - } - }, - { scope: "worker", auto: true }, - ], - - rhdh: [ - async ({ rhdhDeploymentWorker }, use) => { - await use(rhdhDeploymentWorker); - }, - { scope: "test", auto: true }, - ], - - uiHelper: [ - async ({ page }, use) => { - await use(new UIhelper(page)); - }, - { scope: "test" }, - ], - - loginHelper: [ - async ({ page }, use) => { - await use(new LoginHelper(page)); - }, - { scope: "test" }, - ], - - baseURL: [ - async ({ rhdhDeploymentWorker }, use) => { - await use(rhdhDeploymentWorker.rhdhUrl); - }, - { scope: "test" }, - ], -}); - -export { expect } from "@playwright/test"; -export type { Page } from "@playwright/test"; diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 51040911d..b59f720ec 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -1,5 +1,6 @@ import { APIHelper } from "@red-hat-developer-hub/e2e-test-utils/helpers"; -import { test, expect, Page } from "../fixtures"; +import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils"; +import { test, expect, Page } from "@red-hat-developer-hub/e2e-test-utils/test"; export const WAIT_OBJECTS = { muiLinearProgress: 'div[class*="MuiLinearProgress-root"]', @@ -16,12 +17,19 @@ test.describe("Bulk import tests", () => { owner: githubOrg, }; - test.beforeAll(async () => { + test.beforeAll(async ({ rhdh }) => { test.info().annotations.push({ type: "component", description: "plugins", }); + await rhdh.configure({ auth: "keycloak" }); + + const orchestratorNamespace = "orchestrator"; + await $`bash tests/scripts/install-orchestrator.sh ${orchestratorNamespace}`; + + await rhdh.deploy(); + // Create the repository with catalog-info.yaml file dynamically await APIHelper.createGitHubRepoWithFile( catalogRepoDetails.owner, From 17028a9713f0ae24d24cd2b5e1183c4c46760e41 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Thu, 5 Mar 2026 11:24:35 +0200 Subject: [PATCH 07/26] feat(e2e): format code Signed-off-by: Oleksandr Andriienko --- .../bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index b59f720ec..b872d6983 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -24,10 +24,10 @@ test.describe("Bulk import tests", () => { }); await rhdh.configure({ auth: "keycloak" }); - + const orchestratorNamespace = "orchestrator"; await $`bash tests/scripts/install-orchestrator.sh ${orchestratorNamespace}`; - + await rhdh.deploy(); // Create the repository with catalog-info.yaml file dynamically From f8ec22a30d3ad66e8a351e9ff65da82a1503f77f Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Thu, 5 Mar 2026 12:47:31 +0200 Subject: [PATCH 08/26] feat(e2e): fix eslint Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/eslint.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspaces/bulk-import/e2e-tests/eslint.config.js b/workspaces/bulk-import/e2e-tests/eslint.config.js index e83fbd7c7..d8d041826 100644 --- a/workspaces/bulk-import/e2e-tests/eslint.config.js +++ b/workspaces/bulk-import/e2e-tests/eslint.config.js @@ -1,3 +1,3 @@ -import { createEslintConfig } from "rhdh-e2e-test-utils/eslint"; +import { createEslintConfig } from "@red-hat-developer-hub/e2e-test-utils/eslint"; export default createEslintConfig(import.meta.dirname); From 03c2d5982ce1b7025536c51296b1c3a39f3d351c Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Thu, 5 Mar 2026 18:26:57 +0200 Subject: [PATCH 09/26] feat(e2e): use newer e2e lib with improved timeout Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/package.json | 2 +- .../e2e-tests/tests/specs/bulk-import.spec.ts | 6 ++++-- workspaces/bulk-import/e2e-tests/yarn.lock | 10 +++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/package.json b/workspaces/bulk-import/e2e-tests/package.json index 121e05da7..6e900080c 100644 --- a/workspaces/bulk-import/e2e-tests/package.json +++ b/workspaces/bulk-import/e2e-tests/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@playwright/test": "^1.56.1", - "@red-hat-developer-hub/e2e-test-utils": "1.1.10", + "@red-hat-developer-hub/e2e-test-utils": "1.1.12", "@types/node": "^24.10.1", "dotenv": "^16.4.7", "eslint": "^9.39.2", diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index b872d6983..341f987d6 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -9,7 +9,7 @@ export const WAIT_OBJECTS = { test.describe("Bulk import tests", () => { const catalogRepoName = `janus-test-1-bulk-import-test-${Date.now()}`; - const githubOrg = "cloud-eda"; + const githubOrg = "janus-test"; const catalogRepoDetails = { name: catalogRepoName, url: `github.com/${githubOrg}/${catalogRepoName}`, @@ -28,7 +28,9 @@ test.describe("Bulk import tests", () => { const orchestratorNamespace = "orchestrator"; await $`bash tests/scripts/install-orchestrator.sh ${orchestratorNamespace}`; - await rhdh.deploy(); + await rhdh.deploy({ + timeout: 20 * 60 * 1000, // 20 min + }); // Create the repository with catalog-info.yaml file dynamically await APIHelper.createGitHubRepoWithFile( diff --git a/workspaces/bulk-import/e2e-tests/yarn.lock b/workspaces/bulk-import/e2e-tests/yarn.lock index 343609eb6..032fd9b80 100644 --- a/workspaces/bulk-import/e2e-tests/yarn.lock +++ b/workspaces/bulk-import/e2e-tests/yarn.lock @@ -293,9 +293,9 @@ __metadata: languageName: node linkType: hard -"@red-hat-developer-hub/e2e-test-utils@npm:1.1.10": - version: 1.1.10 - resolution: "@red-hat-developer-hub/e2e-test-utils@npm:1.1.10" +"@red-hat-developer-hub/e2e-test-utils@npm:1.1.12": + version: 1.1.12 + resolution: "@red-hat-developer-hub/e2e-test-utils@npm:1.1.12" dependencies: "@axe-core/playwright": ^4.11.0 "@eslint/js": ^9.39.1 @@ -315,7 +315,7 @@ __metadata: zx: ^8.8.5 peerDependencies: "@playwright/test": ^1.57.0 - checksum: b12ed69e5b1c9604336e451f50d9e506ef9ba152924927aab4292db39d52e3baecf9d243774b2a4dcd5a325a22c9afa4aa82b9b77463263b3161ab34e941c13d + checksum: 1aabf33e105a39320ab2099db4d8c0b6090374970b0dee0cfe07f54b2f5cab98d309fc1e6ea41f17419454312b9b6905b26909981630c47b89665f213d67cbfa languageName: node linkType: hard @@ -725,7 +725,7 @@ __metadata: dependencies: "@eslint/js": ^9.39.2 "@playwright/test": ^1.56.1 - "@red-hat-developer-hub/e2e-test-utils": 1.1.10 + "@red-hat-developer-hub/e2e-test-utils": 1.1.12 "@types/node": ^24.10.1 dotenv: ^16.4.7 eslint: ^9.39.2 From 15a6d16fa87b390c940c7986060968a92726d982 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Fri, 6 Mar 2026 00:21:26 +0200 Subject: [PATCH 10/26] feat(e2e): use test github credentials Signed-off-by: Oleksandr Andriienko --- .../e2e-tests/tests/config/app-config-rhdh.yaml | 15 ++++++--------- .../e2e-tests/tests/config/rhdh-secrets.yaml | 7 ++++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml index 17f7754be..5607175a9 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml @@ -4,15 +4,12 @@ integrations: github: - host: github.com - token: ${GITHUB_TOKEN} - # apps: - # - appId: ${GITHUB_APP_APP_ID} - # clientId: ${GITHUB_APP_CLIENT_ID} - # clientSecret: ${GITHUB_APP_CLIENT_SECRET} - # webhookUrl: ${GITHUB_APP_WEBHOOK_URL} - # webhookSecret: ${GITHUB_APP_WEBHOOK_SECRET} - # privateKey: | - # ${GITHUB_APP_PRIVATE_KEY} + apps: + - appId: ${VAULT_GITHUB_APP_APP_ID} + clientId: ${VAULT_GITHUB_APP_CLIENT_ID} + clientSecret: ${VAULT_GITHUB_APP_CLIENT_SECRET} + webhookSecret: ${VAULT_GITHUB_APP_WEBHOOK_SECRET} + privateKey: ${VAULT_GITHUB_APP_PRIVATE_KEY} gitlab: - host: gitlab.com token: temp diff --git a/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml b/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml index ef347f301..83964869c 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml @@ -4,4 +4,9 @@ metadata: name: rhdh-secrets type: Opaque stringData: - GITHUB_TOKEN: $GITHUB_TOKEN + VAULT_GITHUB_APP_APP_ID: $VAULT_GITHUB_APP_APP_ID + VAULT_GITHUB_APP_CLIENT_ID: $VAULT_GITHUB_APP_CLIENT_ID + VAULT_GITHUB_APP_CLIENT_SECRET: $VAULT_GITHUB_APP_CLIENT_SECRET + VAULT_GITHUB_APP_WEBHOOK_SECRET: $VAULT_GITHUB_APP_WEBHOOK_SECRET + VAULT_GH_RHDH_QE_USER_TOKEN: $VAULT_GH_RHDH_QE_USER_TOKEN + VAULT_GITHUB_APP_PRIVATE_KEY: $VAULT_GITHUB_APP_PRIVATE_KEY From 0f80f62133e5ba2bba49d43d77e89b65ce2ce8b1 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 10 Mar 2026 11:47:22 +0200 Subject: [PATCH 11/26] feat(e2e): use another github org to fit with credentials Signed-off-by: Oleksandr Andriienko --- .../bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 341f987d6..3bc49d26b 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -9,7 +9,7 @@ export const WAIT_OBJECTS = { test.describe("Bulk import tests", () => { const catalogRepoName = `janus-test-1-bulk-import-test-${Date.now()}`; - const githubOrg = "janus-test"; + const githubOrg = "janus-qe"; const catalogRepoDetails = { name: catalogRepoName, url: `github.com/${githubOrg}/${catalogRepoName}`, From 249d1e98d0c9d31e2393004d279b64436bbdce3f Mon Sep 17 00:00:00 2001 From: Kashish Mittal Date: Tue, 10 Mar 2026 13:09:03 +0200 Subject: [PATCH 12/26] feat(e2e): use custom github helper with token --- .../e2e-tests/tests/specs/bulk-import.spec.ts | 14 ++- .../tests/specs/custon-github-api-helper.ts | 92 +++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 workspaces/bulk-import/e2e-tests/tests/specs/custon-github-api-helper.ts diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 3bc49d26b..4b0678f22 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -1,6 +1,6 @@ -import { APIHelper } from "@red-hat-developer-hub/e2e-test-utils/helpers"; import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils"; import { test, expect, Page } from "@red-hat-developer-hub/e2e-test-utils/test"; +import { CustomAPIHelper } from "./custon-github-api-helper"; export const WAIT_OBJECTS = { muiLinearProgress: 'div[class*="MuiLinearProgress-root"]', @@ -32,12 +32,19 @@ test.describe("Bulk import tests", () => { timeout: 20 * 60 * 1000, // 20 min }); + if (!process.env.VAULT_GH_RHDH_QE_USER_TOKEN) { + throw new Error(`Provide github qe token`); + } + + const token = process.env.VAULT_GH_RHDH_QE_USER_TOKEN; + // Create the repository with catalog-info.yaml file dynamically - await APIHelper.createGitHubRepoWithFile( + await CustomAPIHelper.createGitHubRepoWithFile( catalogRepoDetails.owner, catalogRepoDetails.name, "test", "ABC", + token, ); }); @@ -136,9 +143,10 @@ test.describe("Bulk import tests", () => { test.afterAll(async () => { try { // Delete the dynamically created GitHub repository with catalog-info.yaml - await APIHelper.deleteGitHubRepo( + await CustomAPIHelper.deleteRepo( catalogRepoDetails.owner, catalogRepoDetails.name, + process.env.VAULT_GH_RHDH_QE_USER_TOKEN!, ); console.log( diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/custon-github-api-helper.ts b/workspaces/bulk-import/e2e-tests/tests/specs/custon-github-api-helper.ts new file mode 100644 index 000000000..3f89fc431 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/specs/custon-github-api-helper.ts @@ -0,0 +1,92 @@ +/** + * Helper class for making API calls to GitHub and RHDH + */ +export class CustomAPIHelper { + /** + * Create a GitHub repository with a file + */ + static async createGitHubRepoWithFile( + owner: string, + repo: string, + filePath: string, + content: string, + token: string, + ): Promise { + const createRepoResponse = await fetch( + `https://api.github.com/orgs/${owner}/repos`, + { + method: "POST", + headers: { + Authorization: `token ${token}`, + "Content-Type": "application/json", + Accept: "application/vnd.github+json", + }, + body: JSON.stringify({ + name: repo, + private: false, + // eslint-disable-next-line @typescript-eslint/naming-convention + auto_init: true, + }), + }, + ); + + if (!createRepoResponse.ok) { + const errorText = await createRepoResponse.text(); + throw new Error( + `Failed to create repository: ${createRepoResponse.status} ${errorText}`, + ); + } + + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const createFileResponse = await fetch( + `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`, + { + method: "PUT", + headers: { + Authorization: `token ${token}`, + "Content-Type": "application/json", + Accept: "application/vnd.github+json", + }, + body: JSON.stringify({ + message: `Add ${filePath}`, + content: Buffer.from(content).toString("base64"), + }), + }, + ); + + if (!createFileResponse.ok) { + const errorText = await createFileResponse.text(); + throw new Error( + `Failed to create file: ${createFileResponse.status} ${errorText}`, + ); + } + } + + /** + * Delete a GitHub repository + */ + static async deleteRepo( + owner: string, + repo: string, + token: string, + ): Promise { + const response = await fetch( + `https://api.github.com/repos/${owner}/${repo}`, + { + method: "DELETE", + headers: { + Authorization: `token ${token}`, + Accept: "application/vnd.github+json", + }, + }, + ); + + if (!response.ok && response.status !== 404) { + const errorText = await response.text(); + throw new Error( + `Failed to delete repository: ${response.status} ${errorText}`, + ); + } + } +} From 2b8e24045a83fde51d72c8bd89ae17b7a1c66959 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 10 Mar 2026 15:10:39 +0200 Subject: [PATCH 13/26] feat(e2e): use token for integrations Signed-off-by: Oleksandr Andriienko --- .../e2e-tests/tests/config/app-config-rhdh.yaml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml index 5607175a9..b2d3d45a0 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml @@ -4,12 +4,7 @@ integrations: github: - host: github.com - apps: - - appId: ${VAULT_GITHUB_APP_APP_ID} - clientId: ${VAULT_GITHUB_APP_CLIENT_ID} - clientSecret: ${VAULT_GITHUB_APP_CLIENT_SECRET} - webhookSecret: ${VAULT_GITHUB_APP_WEBHOOK_SECRET} - privateKey: ${VAULT_GITHUB_APP_PRIVATE_KEY} + token: ${VAULT_GH_RHDH_QE_USER_TOKEN} gitlab: - host: gitlab.com token: temp From 1f6189949bc00815b2ef8d8850ccde2d7514f85f Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 10 Mar 2026 16:38:17 +0200 Subject: [PATCH 14/26] feat(e2e): keep just needed secret values Signed-off-by: Oleksandr Andriienko --- .../bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml b/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml index 83964869c..1e86996c5 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml @@ -4,9 +4,4 @@ metadata: name: rhdh-secrets type: Opaque stringData: - VAULT_GITHUB_APP_APP_ID: $VAULT_GITHUB_APP_APP_ID - VAULT_GITHUB_APP_CLIENT_ID: $VAULT_GITHUB_APP_CLIENT_ID - VAULT_GITHUB_APP_CLIENT_SECRET: $VAULT_GITHUB_APP_CLIENT_SECRET - VAULT_GITHUB_APP_WEBHOOK_SECRET: $VAULT_GITHUB_APP_WEBHOOK_SECRET VAULT_GH_RHDH_QE_USER_TOKEN: $VAULT_GH_RHDH_QE_USER_TOKEN - VAULT_GITHUB_APP_PRIVATE_KEY: $VAULT_GITHUB_APP_PRIVATE_KEY From 4961cd8496483c7911395228ba262fee819ebeda Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Tue, 10 Mar 2026 22:57:04 +0200 Subject: [PATCH 15/26] feat(e2e): fix some sonar cloud issues, typos Signed-off-by: Oleksandr Andriienko --- .../tests/scripts/install-orchestrator.sh | 109 +++++++++++++----- .../e2e-tests/tests/specs/bulk-import.spec.ts | 2 +- ...-helper.ts => custom-github-api-helper.ts} | 0 3 files changed, 79 insertions(+), 32 deletions(-) rename workspaces/bulk-import/e2e-tests/tests/specs/{custon-github-api-helper.ts => custom-github-api-helper.ts} (100%) diff --git a/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh b/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh index 6c532d49d..63e6cb0a5 100755 --- a/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh +++ b/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh @@ -12,6 +12,9 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" export NAME_SPACE="${1:-${NAME_SPACE:-orchestrator}}" +LOWER_CASE_CLASS='[:lower:]' +UPPER_CASE_CLASS='[:upper:]' + # --------------------------------------------------------------------------- # Logging # --------------------------------------------------------------------------- @@ -22,21 +25,35 @@ else fi : "${LOG_LEVEL:=INFO}" -log::timestamp() { date -u '+%Y-%m-%dT%H:%M:%SZ'; } +log::timestamp() { + echo "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" + return 0 +} log::level_value() { - local level; level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" + local input="$1" + local level + level="$(echo "$input" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" case "${level}" in DEBUG) echo 0 ;; INFO) echo 1 ;; WARN|WARNING) echo 2 ;; ERROR|ERR) echo 3 ;; *) echo 1 ;; esac + return 0; } log::should_log() { - local requested_level config_level - requested_level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" - config_level="$(echo "${LOG_LEVEL}" | tr '[:lower:]' '[:upper:]')" + local input requested_level config_level + input="$1" + requested_level="$(echo "$input" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" + config_level="$(echo "${LOG_LEVEL}" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" + [[ "$(log::level_value "${requested_level}")" -ge "$(log::level_value "${config_level}")" ]] + return $? +} +log::reset_code() { + [[ "${LOG_NO_COLOR}" == "true" ]] && printf '' || printf '\033[0m' + return 0; } -log::reset_code() { [[ "${LOG_NO_COLOR}" == "true" ]] && printf '' || printf '\033[0m'; } log::color_for_level() { [[ "${LOG_NO_COLOR}" == "true" ]] && { printf ''; return 0; } - local level; level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" + local level input + input="$1" + level="$(echo "$input" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" case "${level}" in DEBUG) printf '\033[36m' ;; INFO) printf '\033[34m' ;; WARN|WARNING) printf '\033[33m' ;; ERROR|ERR) printf '\033[31m' ;; SUCCESS) printf '\033[32m' ;; SECTION) printf '\033[35m\033[1m' ;; @@ -44,8 +61,11 @@ log::color_for_level() { esac } log::icon_for_level() { - local level; level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" + local level input + input="$1" + level="$(echo "$input" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" case "${level}" in DEBUG) printf '🐞' ;; INFO) printf 'ℹ' ;; WARN|WARNING) printf '⚠' ;; ERROR|ERR) printf '❌' ;; SUCCESS) printf '✓' ;; *) printf '-' ;; esac + return 0 } log::emit_line() { local level="$1" icon="$2" line="$3" color reset timestamp @@ -61,17 +81,36 @@ log::emit() { [[ -z "${message}" ]] && return 0 while IFS= read -r line; do log::emit_line "${level}" "${icon}" "${line}"; done <<< "${message}" } -log::debug() { log::emit "DEBUG" "$@"; } -log::info() { log::emit "INFO" "$@"; } -log::warn() { log::emit "WARN" "$@"; } -log::error() { log::emit "ERROR" "$@"; } -log::success() { log::emit "SUCCESS" "$@"; } +log::debug() { + log::emit "DEBUG" "$@" + return 0 +} +log::info() { + log::emit "INFO" "$@" + return 0 +} +log::warn() { + log::emit "WARN" "$@" + return 0 +} +log::error() { + log::emit "ERROR" "$@" + return 0 +} +log::success() { + log::emit "SUCCESS" "$@" + return 0 +} # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- # Escape double quotes for yq string values (yq v4 mikefarah) -escape_yq() { printf '%s' "$1" | sed 's/"/\\"/g'; } +escape_yq() { + local input="$1" + printf '%s' "$input" | sed 's/"/\\"/g' + return 0 +} # --------------------------------------------------------------------------- # Operator subscription and status @@ -91,6 +130,7 @@ spec: source: $source_name sourceNamespace: $source_namespace EOD + return 0 } check_operator_status() { @@ -108,16 +148,20 @@ check_operator_status() { install_serverless_logic_ocp_operator() { install_subscription logic-operator-rhel8 openshift-operators alpha logic-operator-rhel8 redhat-operators openshift-marketplace + return 0 } waitfor_serverless_logic_ocp_operator() { check_operator_status 500 openshift-operators "OpenShift Serverless Logic Operator (Alpha)" Succeeded + return 0 } install_serverless_ocp_operator() { install_subscription serverless-operator openshift-operators stable serverless-operator redhat-operators openshift-marketplace + return 0 } waitfor_serverless_ocp_operator() { check_operator_status 300 openshift-operators "Red Hat OpenShift Serverless" Succeeded + return 0 } # --------------------------------------------------------------------------- @@ -144,6 +188,7 @@ delete_namespace() { force_delete_namespace "$project" fi fi + return 0 } configure_namespace() { @@ -153,6 +198,7 @@ configure_namespace() { oc create namespace "${project}" || { log::error "Failed to create namespace ${project}"; exit 1; } oc config set-context --current --namespace="${project}" || { log::error "Failed to set context"; exit 1; } log::info "Namespace ${project} is ready." + return 0 } # --------------------------------------------------------------------------- @@ -299,7 +345,7 @@ spec: serviceRef: { name: ${postgres_service_name}, namespace: ${namespace}, port: 5432, databaseName: backstage_plugin_orchestrator } EOF local attempt=0 max_attempts=60 - while [ $attempt -lt $max_attempts ]; do + while [[ $attempt -lt $max_attempts ]]; do if oc get deployment sonataflow-platform-data-index-service -n "$namespace" &>/dev/null && \ oc get deployment sonataflow-platform-jobs-service -n "$namespace" &>/dev/null; then log::success "SonataFlowPlatform services created" @@ -309,7 +355,7 @@ EOF return 0 fi attempt=$((attempt + 1)) - [ $((attempt % 10)) -eq 0 ] && log::info "Waiting for SonataFlowPlatform... ($attempt/$max_attempts)" + [[ $((attempt % 10)) -eq 0 ]] && log::info "Waiting for SonataFlowPlatform... ($attempt/$max_attempts)" sleep 5 done log::warn "SonataFlowPlatform services did not appear in time." @@ -335,6 +381,7 @@ print_orchestrator_connection_info() { log::warn "Service '${data_index_service}' not found yet." fi log::info "==========================================" + return 0 } # --------------------------------------------------------------------------- @@ -343,13 +390,13 @@ print_orchestrator_connection_info() { wait_for_sonataflow_crds() { log::info "Waiting for SonataFlow CRDs..." local attempt=0 max_attempts=60 - while [ $attempt -lt $max_attempts ]; do + while [[ $attempt -lt $max_attempts ]]; do if oc get crd sonataflows.sonataflow.org &>/dev/null; then log::success "SonataFlow CRD is available." return 0 fi attempt=$((attempt + 1)) - [ $((attempt % 6)) -eq 0 ] && log::info "Waiting for sonataflows.sonataflow.org... ($attempt/$max_attempts)" + [[ $((attempt % 6)) -eq 0 ]] && log::info "Waiting for sonataflows.sonataflow.org... ($attempt/$max_attempts)" sleep 5 done log::error "Timed out waiting for SonataFlow CRD." @@ -362,9 +409,9 @@ wait_for_sonataflow_crds() { # --------------------------------------------------------------------------- deploy_orchestrator_workflows_operator() { local namespace=$1 - local WORKFLOW_REPO="https://github.com/AndrienkoAleksandr/serverless-workflows.git" - local WORKFLOW_DIR="/tmp/serverless-workflows" - local LOCAL_MANIFESTS="${SCRIPT_DIR}/yaml" + local workflow_repo="https://github.com/AndrienkoAleksandr/serverless-workflows.git" + local workflow_dir="/tmp/serverless-workflows" + local local_manifests="${SCRIPT_DIR}/yaml" # PostgreSQL if ! oc get statefulset backstage-psql -n "$namespace" &>/dev/null && ! oc get deployment backstage-psql -n "$namespace" &>/dev/null; then @@ -412,21 +459,21 @@ deploy_orchestrator_workflows_operator() { # Prefer local yaml/ if it exists and has content - if [[ -d "${LOCAL_MANIFESTS}" ]] && [[ -n "$(ls -A "${LOCAL_MANIFESTS}" 2>/dev/null)" ]]; then - log::info "Using local workflow manifests from ${LOCAL_MANIFESTS}" + if [[ -d "${local_manifests}" ]] && [[ -n "$(ls -A "${local_manifests}" 2>/dev/null)" ]]; then + log::info "Using local workflow manifests from ${local_manifests}" # Apply all YAMLs in yaml/ with correct namespace - for f in "${LOCAL_MANIFESTS}"/*.yaml "${LOCAL_MANIFESTS}"/*.yml; do + for f in "${local_manifests}"/*.yaml "${local_manifests}"/*.yml; do [[ -e "$f" ]] && oc apply -f "$f" -n "$namespace" && log::info "Applied $(basename "$f")" done else log::info "Cloning workflow repo..." - rm -rf "${WORKFLOW_DIR}" - git clone --single-branch --branch bulk-import-workflow-sample "${WORKFLOW_REPO}" "${WORKFLOW_DIR}" - local WORKFLOW_MANIFESTS="${WORKFLOW_DIR}/workflows/experimentals/bulk-import-git-repos/manifests" - if [[ -d "${WORKFLOW_MANIFESTS}" ]]; then + rm -rf "${workflow_dir}" + git clone --single-branch --branch bulk-import-workflow-sample "${workflow_repo}" "${workflow_dir}" + local workflow_manifests="${workflow_dir}/workflows/experimentals/bulk-import-git-repos/manifests" + if [[ -d "${workflow_manifests}" ]]; then log::info "Applying workflow manifests from repo..." - snToDbPatch="${WORKFLOW_MANIFESTS}/04-sonataflow_universal-pr.yaml" + snToDbPatch="${workflow_manifests}/04-sonataflow_universal-pr.yaml" yq eval -i '.spec.persistence.postgresql.secretRef.name = "'"$(escape_yq "$pqsl_secret_name")"'"' "$snToDbPatch" yq eval -i '.spec.persistence.postgresql.secretRef.userKey = "'"$(escape_yq "$pqsl_user_key")"'"' "$snToDbPatch" yq eval -i '.spec.persistence.postgresql.secretRef.passwordKey = "'"$(escape_yq "$pqsl_password_key")"'"' "$snToDbPatch" @@ -434,9 +481,9 @@ deploy_orchestrator_workflows_operator() { yq eval -i '.spec.persistence.postgresql.serviceRef.namespace = "'"$(escape_yq "$namespace")"'"' "$snToDbPatch" yq eval -i '.spec.persistence.postgresql.serviceRef.databaseName = "'"$(escape_yq "$sonataflow_db")"'"' "$snToDbPatch" - oc apply -f "${WORKFLOW_MANIFESTS}" -n "$namespace" + oc apply -f "${workflow_manifests}" -n "$namespace" else - log::warn "Manifests path not found in repo: ${WORKFLOW_MANIFESTS}" + log::warn "Manifests path not found in repo: ${workflow_manifests}" fi fi diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 4b0678f22..d16e74d1a 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -1,6 +1,6 @@ import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils"; import { test, expect, Page } from "@red-hat-developer-hub/e2e-test-utils/test"; -import { CustomAPIHelper } from "./custon-github-api-helper"; +import { CustomAPIHelper } from "./custom-github-api-helper"; export const WAIT_OBJECTS = { muiLinearProgress: 'div[class*="MuiLinearProgress-root"]', diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/custon-github-api-helper.ts b/workspaces/bulk-import/e2e-tests/tests/specs/custom-github-api-helper.ts similarity index 100% rename from workspaces/bulk-import/e2e-tests/tests/specs/custon-github-api-helper.ts rename to workspaces/bulk-import/e2e-tests/tests/specs/custom-github-api-helper.ts From 9481ac4ad60dd0f0f08de8f28f9218e5e07ff627 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 11 Mar 2026 01:56:58 +0200 Subject: [PATCH 16/26] feat(e2e): use origin APIHelper Signed-off-by: Oleksandr Andriienko --- .../e2e-tests/tests/specs/bulk-import.spec.ts | 11 +-- .../tests/specs/custom-github-api-helper.ts | 92 ------------------- 2 files changed, 4 insertions(+), 99 deletions(-) delete mode 100644 workspaces/bulk-import/e2e-tests/tests/specs/custom-github-api-helper.ts diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index d16e74d1a..1e6597391 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -1,6 +1,6 @@ import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils"; import { test, expect, Page } from "@red-hat-developer-hub/e2e-test-utils/test"; -import { CustomAPIHelper } from "./custom-github-api-helper"; +import { APIHelper } from "@red-hat-developer-hub/e2e-test-utils/helpers"; export const WAIT_OBJECTS = { muiLinearProgress: 'div[class*="MuiLinearProgress-root"]', @@ -36,15 +36,13 @@ test.describe("Bulk import tests", () => { throw new Error(`Provide github qe token`); } - const token = process.env.VAULT_GH_RHDH_QE_USER_TOKEN; - + process.env.GH_RHDH_QE_USER_TOKEN = process.env.VAULT_GH_RHDH_QE_USER_TOKEN; // Create the repository with catalog-info.yaml file dynamically - await CustomAPIHelper.createGitHubRepoWithFile( + await APIHelper.createGitHubRepoWithFile( catalogRepoDetails.owner, catalogRepoDetails.name, "test", "ABC", - token, ); }); @@ -143,10 +141,9 @@ test.describe("Bulk import tests", () => { test.afterAll(async () => { try { // Delete the dynamically created GitHub repository with catalog-info.yaml - await CustomAPIHelper.deleteRepo( + await APIHelper.deleteGitHubRepo( catalogRepoDetails.owner, catalogRepoDetails.name, - process.env.VAULT_GH_RHDH_QE_USER_TOKEN!, ); console.log( diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/custom-github-api-helper.ts b/workspaces/bulk-import/e2e-tests/tests/specs/custom-github-api-helper.ts deleted file mode 100644 index 3f89fc431..000000000 --- a/workspaces/bulk-import/e2e-tests/tests/specs/custom-github-api-helper.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Helper class for making API calls to GitHub and RHDH - */ -export class CustomAPIHelper { - /** - * Create a GitHub repository with a file - */ - static async createGitHubRepoWithFile( - owner: string, - repo: string, - filePath: string, - content: string, - token: string, - ): Promise { - const createRepoResponse = await fetch( - `https://api.github.com/orgs/${owner}/repos`, - { - method: "POST", - headers: { - Authorization: `token ${token}`, - "Content-Type": "application/json", - Accept: "application/vnd.github+json", - }, - body: JSON.stringify({ - name: repo, - private: false, - // eslint-disable-next-line @typescript-eslint/naming-convention - auto_init: true, - }), - }, - ); - - if (!createRepoResponse.ok) { - const errorText = await createRepoResponse.text(); - throw new Error( - `Failed to create repository: ${createRepoResponse.status} ${errorText}`, - ); - } - - await new Promise((resolve) => setTimeout(resolve, 2000)); - - const createFileResponse = await fetch( - `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`, - { - method: "PUT", - headers: { - Authorization: `token ${token}`, - "Content-Type": "application/json", - Accept: "application/vnd.github+json", - }, - body: JSON.stringify({ - message: `Add ${filePath}`, - content: Buffer.from(content).toString("base64"), - }), - }, - ); - - if (!createFileResponse.ok) { - const errorText = await createFileResponse.text(); - throw new Error( - `Failed to create file: ${createFileResponse.status} ${errorText}`, - ); - } - } - - /** - * Delete a GitHub repository - */ - static async deleteRepo( - owner: string, - repo: string, - token: string, - ): Promise { - const response = await fetch( - `https://api.github.com/repos/${owner}/${repo}`, - { - method: "DELETE", - headers: { - Authorization: `token ${token}`, - Accept: "application/vnd.github+json", - }, - }, - ); - - if (!response.ok && response.status !== 404) { - const errorText = await response.text(); - throw new Error( - `Failed to delete repository: ${response.status} ${errorText}`, - ); - } - } -} From e597e30baff950adc8392fd7bce698f166146e33 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 25 Mar 2026 22:29:23 +0200 Subject: [PATCH 17/26] feat(e2e): use e2e-test-utils for orchestrator Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/package.json | 2 +- .../tests/scripts/install-orchestrator.sh | 533 ------------------ .../tests/scripts/install-workflow.sh | 88 +++ .../e2e-tests/tests/specs/bulk-import.spec.ts | 4 +- workspaces/bulk-import/e2e-tests/yarn.lock | 42 +- 5 files changed, 128 insertions(+), 541 deletions(-) delete mode 100755 workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh create mode 100755 workspaces/bulk-import/e2e-tests/tests/scripts/install-workflow.sh diff --git a/workspaces/bulk-import/e2e-tests/package.json b/workspaces/bulk-import/e2e-tests/package.json index 6e900080c..711d88be4 100644 --- a/workspaces/bulk-import/e2e-tests/package.json +++ b/workspaces/bulk-import/e2e-tests/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@playwright/test": "^1.56.1", - "@red-hat-developer-hub/e2e-test-utils": "1.1.12", + "@red-hat-developer-hub/e2e-test-utils": "1.1.22", "@types/node": "^24.10.1", "dotenv": "^16.4.7", "eslint": "^9.39.2", diff --git a/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh b/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh deleted file mode 100755 index 63e6cb0a5..000000000 --- a/workspaces/bulk-import/e2e-tests/tests/scripts/install-orchestrator.sh +++ /dev/null @@ -1,533 +0,0 @@ -#!/bin/bash -# -# Standalone script to install the orchestrator (Serverless Logic / SonataFlow) -# on OpenShift. -# -# Usage: ./install-orchestrator.sh [namespace] -# Default namespace: orchestrator -# - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export NAME_SPACE="${1:-${NAME_SPACE:-orchestrator}}" - -LOWER_CASE_CLASS='[:lower:]' -UPPER_CASE_CLASS='[:upper:]' - -# --------------------------------------------------------------------------- -# Logging -# --------------------------------------------------------------------------- -if [[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]]; then - : "${LOG_NO_COLOR:=false}" -else - : "${LOG_NO_COLOR:=true}" -fi -: "${LOG_LEVEL:=INFO}" - -log::timestamp() { - echo "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" - return 0 -} -log::level_value() { - local input="$1" - local level - level="$(echo "$input" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" - case "${level}" in DEBUG) echo 0 ;; INFO) echo 1 ;; WARN|WARNING) echo 2 ;; ERROR|ERR) echo 3 ;; *) echo 1 ;; esac - return 0; -} -log::should_log() { - local input requested_level config_level - input="$1" - requested_level="$(echo "$input" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" - config_level="$(echo "${LOG_LEVEL}" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" - - [[ "$(log::level_value "${requested_level}")" -ge "$(log::level_value "${config_level}")" ]] - return $? -} -log::reset_code() { - [[ "${LOG_NO_COLOR}" == "true" ]] && printf '' || printf '\033[0m' - return 0; -} -log::color_for_level() { - [[ "${LOG_NO_COLOR}" == "true" ]] && { printf ''; return 0; } - local level input - input="$1" - level="$(echo "$input" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" - case "${level}" in - DEBUG) printf '\033[36m' ;; INFO) printf '\033[34m' ;; WARN|WARNING) printf '\033[33m' ;; - ERROR|ERR) printf '\033[31m' ;; SUCCESS) printf '\033[32m' ;; SECTION) printf '\033[35m\033[1m' ;; - *) printf '\033[37m' ;; - esac -} -log::icon_for_level() { - local level input - input="$1" - level="$(echo "$input" | tr "$LOWER_CASE_CLASS" "$UPPER_CASE_CLASS")" - case "${level}" in DEBUG) printf '🐞' ;; INFO) printf 'ℹ' ;; WARN|WARNING) printf '⚠' ;; ERROR|ERR) printf '❌' ;; SUCCESS) printf '✓' ;; *) printf '-' ;; esac - return 0 -} -log::emit_line() { - local level="$1" icon="$2" line="$3" color reset timestamp - log::should_log "${level}" || return 0 - timestamp="$(log::timestamp)" - color="$(log::color_for_level "${level}")" - reset="$(log::reset_code)" - printf '%s[%s] %s %s%s\n' "${color}" "${timestamp}" "${icon}" "${line}" "${reset}" >&2 -} -log::emit() { - local level="$1"; shift - local icon message; icon="$(log::icon_for_level "${level}")"; message="${*:-}" - [[ -z "${message}" ]] && return 0 - while IFS= read -r line; do log::emit_line "${level}" "${icon}" "${line}"; done <<< "${message}" -} -log::debug() { - log::emit "DEBUG" "$@" - return 0 -} -log::info() { - log::emit "INFO" "$@" - return 0 -} -log::warn() { - log::emit "WARN" "$@" - return 0 -} -log::error() { - log::emit "ERROR" "$@" - return 0 -} -log::success() { - log::emit "SUCCESS" "$@" - return 0 -} - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- -# Escape double quotes for yq string values (yq v4 mikefarah) -escape_yq() { - local input="$1" - printf '%s' "$input" | sed 's/"/\\"/g' - return 0 -} - -# --------------------------------------------------------------------------- -# Operator subscription and status -# --------------------------------------------------------------------------- -install_subscription() { - local name=$1 namespace=$2 channel=$3 package=$4 source_name=$5 source_namespace=$6 - oc apply -f - << EOD -apiVersion: operators.coreos.com/v1alpha1 -kind: Subscription -metadata: - name: $name - namespace: $namespace -spec: - channel: $channel - installPlanApproval: Automatic - name: $package - source: $source_name - sourceNamespace: $source_namespace -EOD - return 0 -} - -check_operator_status() { - local timeout=${1:-300} namespace=$2 operator_name=$3 expected_status=${4:-Succeeded} - log::info "Checking operator '${operator_name}' in '${namespace}' (timeout ${timeout}s, expected: ${expected_status})" - timeout "${timeout}" bash -c " - while true; do - CURRENT_PHASE=\$(oc get csv -n '${namespace}' -o jsonpath='{.items[?(@.spec.displayName==\"${operator_name}\")].status.phase}') - echo \"[check_operator_status] Phase: \${CURRENT_PHASE}\" >&2 - [[ \"\${CURRENT_PHASE}\" == \"${expected_status}\" ]] && echo \"[check_operator_status] Operator reached ${expected_status}\" >&2 && break - sleep 10 - done - " || { log::error "Operator did not reach ${expected_status} in time."; return 1; } -} - -install_serverless_logic_ocp_operator() { - install_subscription logic-operator-rhel8 openshift-operators alpha logic-operator-rhel8 redhat-operators openshift-marketplace - return 0 -} -waitfor_serverless_logic_ocp_operator() { - check_operator_status 500 openshift-operators "OpenShift Serverless Logic Operator (Alpha)" Succeeded - return 0 -} - -install_serverless_ocp_operator() { - install_subscription serverless-operator openshift-operators stable serverless-operator redhat-operators openshift-marketplace - return 0 -} -waitfor_serverless_ocp_operator() { - check_operator_status 300 openshift-operators "Red Hat OpenShift Serverless" Succeeded - return 0 -} - -# --------------------------------------------------------------------------- -# Namespace -# --------------------------------------------------------------------------- -force_delete_namespace() { - local project=$1 timeout_seconds=${2:-120} elapsed=0 sleep_interval=2 - log::warn "Force deleting namespace ${project}" - oc get namespace "$project" -o json | jq '.spec = {"finalizers":[]}' | oc replace --raw "/api/v1/namespaces/$project/finalize" -f - - while oc get namespace "$project" &>/dev/null; do - [[ $elapsed -ge $timeout_seconds ]] && { log::warn "Timeout deleting ${project}"; return 1; } - sleep $sleep_interval - elapsed=$((elapsed + sleep_interval)) - done - log::success "Namespace '${project}' deleted." -} - -delete_namespace() { - local project=$1 - if oc get namespace "$project" &>/dev/null; then - log::warn "Deleting namespace ${project}..." - oc delete namespace "$project" --grace-period=0 --force || true - if oc get namespace "$project" -o jsonpath='{.status.phase}' 2>/dev/null | grep -q Terminating; then - force_delete_namespace "$project" - fi - fi - return 0 -} - -configure_namespace() { - local project=$1 - log::warn "Recreating namespace: $project" - delete_namespace "$project" - oc create namespace "${project}" || { log::error "Failed to create namespace ${project}"; exit 1; } - oc config set-context --current --namespace="${project}" || { log::error "Failed to set context"; exit 1; } - log::info "Namespace ${project} is ready." - return 0 -} - -# --------------------------------------------------------------------------- -# Deployment wait -# --------------------------------------------------------------------------- -wait_for_deployment() { - local namespace=$1 resource_name=$2 timeout_minutes=${3:-5} check_interval=${4:-10} - [[ -z "$namespace" || -z "$resource_name" ]] && { log::error "wait_for_deployment: namespace and resource_name required"; return 1; } - local max_attempts=$((timeout_minutes * 60 / check_interval)) - log::info "Waiting for '$resource_name' in '$namespace' (timeout ${timeout_minutes}m)..." - for ((i = 1; i <= max_attempts; i++)); do - local pod_name - pod_name=$(oc get pods -n "$namespace" 2>/dev/null | grep "$resource_name" | awk '{print $1}' | head -n 1) - if [[ -n "$pod_name" ]]; then - local is_ready - is_ready=$(oc get pod "$pod_name" -n "$namespace" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null) - if [[ "$is_ready" == "True" ]] && oc get pod "$pod_name" -n "$namespace" 2>/dev/null | grep -q Running; then - log::success "Pod '$pod_name' is ready" - return 0 - fi - fi - sleep "$check_interval" - done - log::error "Timeout waiting for $resource_name" - return 1 -} - -# --------------------------------------------------------------------------- -# PostgreSQL (simple deployment for orchestrator) -# --------------------------------------------------------------------------- -create_simple_postgres_deployment() { - local namespace=$1 postgres_name="backstage-psql" - if oc get deployment "$postgres_name" -n "$namespace" &>/dev/null; then - log::info "PostgreSQL '$postgres_name' already exists" - return 0 - fi - log::info "Creating PostgreSQL '$postgres_name' in '$namespace'" - oc create secret generic "${postgres_name}-secret" -n "$namespace" \ - --from-literal=POSTGRESQL_USER=postgres \ - --from-literal=POSTGRESQL_PASSWORD=postgres \ - --from-literal=POSTGRESQL_DATABASE=postgres \ - --from-literal=POSTGRES_USER=postgres \ - --from-literal=POSTGRES_PASSWORD=postgres \ - --from-literal=POSTGRES_DB=postgres \ - --dry-run=client -o yaml | oc apply -f - -n "$namespace" || true - - oc apply -f - -n "$namespace" << EOF -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: ${postgres_name}-pvc - namespace: ${namespace} -spec: - accessModes: [ReadWriteOnce] - resources: { requests: { storage: 1Gi } } -EOF - - oc apply -f - -n "$namespace" << EOF -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: ${postgres_name} - namespace: ${namespace} -spec: - serviceName: ${postgres_name} - replicas: 1 - selector: { matchLabels: { app: ${postgres_name} } } - template: - metadata: { labels: { app: ${postgres_name} } } - spec: - containers: - - name: postgres - image: registry.redhat.io/rhel9/postgresql-15:latest - env: - - name: POSTGRESQL_USER - valueFrom: { secretKeyRef: { name: ${postgres_name}-secret, key: POSTGRESQL_USER } } - - name: POSTGRESQL_PASSWORD - valueFrom: { secretKeyRef: { name: ${postgres_name}-secret, key: POSTGRESQL_PASSWORD } } - - name: POSTGRESQL_DATABASE - valueFrom: { secretKeyRef: { name: ${postgres_name}-secret, key: POSTGRESQL_DATABASE } } - ports: [ { containerPort: 5432, name: postgres } ] - volumeMounts: [ { name: postgres-data, mountPath: /var/lib/pgsql/data } ] - livenessProbe: - exec: { command: [ /usr/libexec/check-container, --live ] } - initialDelaySeconds: 120 - periodSeconds: 10 - readinessProbe: - exec: { command: [ /usr/libexec/check-container ] } - initialDelaySeconds: 5 - periodSeconds: 10 - volumes: [ { name: postgres-data, persistentVolumeClaim: { claimName: ${postgres_name}-pvc } } ] -EOF - - oc apply -f - -n "$namespace" << EOF -apiVersion: v1 -kind: Service -metadata: - name: ${postgres_name} - namespace: ${namespace} -spec: - selector: { app: ${postgres_name} } - ports: [ { name: postgres, port: 5432, targetPort: 5432 } ] - type: ClusterIP -EOF - - log::info "Waiting for PostgreSQL StatefulSet..." - oc wait statefulset "$postgres_name" -n "$namespace" --for=jsonpath='{.status.readyReplicas}'=1 --timeout=300s || true - sleep 5 - oc exec -n "$namespace" statefulset/"$postgres_name" -- psql -U postgres -c "CREATE DATABASE backstage_plugin_orchestrator;" 2>/dev/null || log::warn "Orchestrator DB may already exist" - log::success "PostgreSQL deployment created." -} - -# --------------------------------------------------------------------------- -# SonataFlow platform -# --------------------------------------------------------------------------- -create_sonataflow_platform() { - local namespace=$1 postgres_secret_name=$2 postgres_service_name=$3 - if ! oc get crd sonataflowplatforms.sonataflow.org &>/dev/null && ! oc get crd sonataflowplatform.sonataflow.org &>/dev/null; then - log::error "SonataFlowPlatform CRD not found. Install Serverless Logic Operator first." - return 1 - fi - if oc get sonataflowplatform sonataflow-platform -n "$namespace" &>/dev/null || oc get sfp sonataflow-platform -n "$namespace" &>/dev/null; then - log::info "SonataFlowPlatform already exists" - return 0 - fi - log::info "Creating SonataFlowPlatform in '$namespace'" - oc apply -f - -n "$namespace" << EOF -apiVersion: sonataflow.org/v1alpha08 -kind: SonataFlowPlatform -metadata: - name: sonataflow-platform - namespace: ${namespace} -spec: - services: - dataIndex: - persistence: - postgresql: - secretRef: { name: ${postgres_secret_name}, userKey: POSTGRES_USER, passwordKey: POSTGRES_PASSWORD } - serviceRef: { name: ${postgres_service_name}, namespace: ${namespace}, port: 5432, databaseName: backstage_plugin_orchestrator } - jobService: - persistence: - postgresql: - secretRef: { name: ${postgres_secret_name}, userKey: POSTGRES_USER, passwordKey: POSTGRES_PASSWORD } - serviceRef: { name: ${postgres_service_name}, namespace: ${namespace}, port: 5432, databaseName: backstage_plugin_orchestrator } -EOF - local attempt=0 max_attempts=60 - while [[ $attempt -lt $max_attempts ]]; do - if oc get deployment sonataflow-platform-data-index-service -n "$namespace" &>/dev/null && \ - oc get deployment sonataflow-platform-jobs-service -n "$namespace" &>/dev/null; then - log::success "SonataFlowPlatform services created" - wait_for_deployment "$namespace" sonataflow-platform-data-index-service 20 || true - wait_for_deployment "$namespace" sonataflow-platform-jobs-service 20 || true - log::success "SonataFlowPlatform ready." - return 0 - fi - attempt=$((attempt + 1)) - [[ $((attempt % 10)) -eq 0 ]] && log::info "Waiting for SonataFlowPlatform... ($attempt/$max_attempts)" - sleep 5 - done - log::warn "SonataFlowPlatform services did not appear in time." -} - -# --------------------------------------------------------------------------- -# Orchestrator connection info -# --------------------------------------------------------------------------- -print_orchestrator_connection_info() { - local namespace=$1 - local data_index_service="sonataflow-platform-data-index-service" - local service_url="http://${data_index_service}.${namespace}.svc.cluster.local" - log::info "==========================================" - log::info "Orchestrator Plugin Connection Information" - log::info "==========================================" - log::info "Namespace: ${namespace}" - log::info "Internal URL for Orchestrator Backend Plugin: ${service_url}" - log::info "dynamic-plugins.yaml: pluginConfig.orchestrator.dataIndexService.url: ${service_url}" - if oc get svc "${data_index_service}" -n "${namespace}" &>/dev/null; then - local port; port=$(oc get svc "${data_index_service}" -n "${namespace}" -o jsonpath='{.spec.ports[0].port}' 2>/dev/null || echo "8080") - log::info "Service: ${data_index_service}, port: ${port}" - else - log::warn "Service '${data_index_service}' not found yet." - fi - log::info "==========================================" - return 0 -} - -# --------------------------------------------------------------------------- -# Wait for SonataFlow CRDs -# --------------------------------------------------------------------------- -wait_for_sonataflow_crds() { - log::info "Waiting for SonataFlow CRDs..." - local attempt=0 max_attempts=60 - while [[ $attempt -lt $max_attempts ]]; do - if oc get crd sonataflows.sonataflow.org &>/dev/null; then - log::success "SonataFlow CRD is available." - return 0 - fi - attempt=$((attempt + 1)) - [[ $((attempt % 6)) -eq 0 ]] && log::info "Waiting for sonataflows.sonataflow.org... ($attempt/$max_attempts)" - sleep 5 - done - log::error "Timed out waiting for SonataFlow CRD." - return 1 -} - -# --------------------------------------------------------------------------- -# Deploy orchestrator workflows (operator path: git clone + helm greeting) -# Uses local yaml/ if present, otherwise clones repo. -# --------------------------------------------------------------------------- -deploy_orchestrator_workflows_operator() { - local namespace=$1 - local workflow_repo="https://github.com/AndrienkoAleksandr/serverless-workflows.git" - local workflow_dir="/tmp/serverless-workflows" - local local_manifests="${SCRIPT_DIR}/yaml" - - # PostgreSQL - if ! oc get statefulset backstage-psql -n "$namespace" &>/dev/null && ! oc get deployment backstage-psql -n "$namespace" &>/dev/null; then - log::info "Creating simple PostgreSQL deployment..." - create_simple_postgres_deployment "$namespace" - else - log::info "PostgreSQL found, waiting for ready..." - if oc get statefulset backstage-psql -n "$namespace" &>/dev/null; then - oc wait statefulset backstage-psql -n "$namespace" --for=jsonpath='{.status.readyReplicas}'=1 --timeout=300s || true - else - wait_for_deployment "$namespace" backstage-psql 15 || true - fi - fi - - local pqsl_secret_name pqsl_svc_name pqsl_user_key pqsl_password_key sonataflow_db - pqsl_secret_name=$(oc get secrets -n "$namespace" -o name 2>/dev/null | grep "backstage-psql" | grep "secret" | head -1 | sed 's|secret\/||') - pqsl_svc_name=$(oc get svc -n "$namespace" -o name 2>/dev/null | grep "backstage-psql" | grep -v secret | head -1 | sed 's|service\/||') - pqsl_secret_name="${pqsl_secret_name:-backstage-psql-secret}" - pqsl_svc_name="${pqsl_svc_name:-backstage-psql}" - pqsl_user_key="POSTGRES_USER" - pqsl_password_key="POSTGRES_PASSWORD" - sonataflow_db="backstage_plugin_orchestrator" - log::info "PostgreSQL secret: $pqsl_secret_name, service: $pqsl_svc_name" - - if ! oc get sonataflowplatform sonataflow-platform -n "$namespace" &>/dev/null && ! oc get sfp sonataflow-platform -n "$namespace" &>/dev/null; then - create_sonataflow_platform "$namespace" "$pqsl_secret_name" "$pqsl_svc_name" - else - log::info "SonataFlowPlatform already exists" - wait_for_deployment "$namespace" sonataflow-platform-data-index-service 20 || true - wait_for_deployment "$namespace" sonataflow-platform-jobs-service 20 || true - fi - - if ! oc get crd sonataflows.sonataflow.org &>/dev/null; then - log::error "SonataFlow CRD not found." - return 1 - fi - - # Wait for 2 SonataFlow resources - timeout 30s bash -c " - until [[ \$(oc get sf -n $namespace --no-headers 2>/dev/null | wc -l) -ge 2 ]]; do - echo \"Waiting for sf resources... \$(oc get sf -n $namespace --no-headers 2>/dev/null | wc -l)\" - sleep 5 - done - " || true - - - # Prefer local yaml/ if it exists and has content - if [[ -d "${local_manifests}" ]] && [[ -n "$(ls -A "${local_manifests}" 2>/dev/null)" ]]; then - log::info "Using local workflow manifests from ${local_manifests}" - # Apply all YAMLs in yaml/ with correct namespace - for f in "${local_manifests}"/*.yaml "${local_manifests}"/*.yml; do - [[ -e "$f" ]] && oc apply -f "$f" -n "$namespace" && log::info "Applied $(basename "$f")" - done - else - log::info "Cloning workflow repo..." - rm -rf "${workflow_dir}" - git clone --single-branch --branch bulk-import-workflow-sample "${workflow_repo}" "${workflow_dir}" - local workflow_manifests="${workflow_dir}/workflows/experimentals/bulk-import-git-repos/manifests" - if [[ -d "${workflow_manifests}" ]]; then - log::info "Applying workflow manifests from repo..." - - snToDbPatch="${workflow_manifests}/04-sonataflow_universal-pr.yaml" - yq eval -i '.spec.persistence.postgresql.secretRef.name = "'"$(escape_yq "$pqsl_secret_name")"'"' "$snToDbPatch" - yq eval -i '.spec.persistence.postgresql.secretRef.userKey = "'"$(escape_yq "$pqsl_user_key")"'"' "$snToDbPatch" - yq eval -i '.spec.persistence.postgresql.secretRef.passwordKey = "'"$(escape_yq "$pqsl_password_key")"'"' "$snToDbPatch" - yq eval -i '.spec.persistence.postgresql.serviceRef.name = "'"$(escape_yq "$pqsl_svc_name")"'"' "$snToDbPatch" - yq eval -i '.spec.persistence.postgresql.serviceRef.namespace = "'"$(escape_yq "$namespace")"'"' "$snToDbPatch" - yq eval -i '.spec.persistence.postgresql.serviceRef.databaseName = "'"$(escape_yq "$sonataflow_db")"'"' "$snToDbPatch" - - oc apply -f "${workflow_manifests}" -n "$namespace" - else - log::warn "Manifests path not found in repo: ${workflow_manifests}" - fi - fi - - wait_for_deployment "$namespace" universal-pr 5 || true - log::info "Orchestrator workflows deployment done." -} - -# --------------------------------------------------------------------------- -# Main -# --------------------------------------------------------------------------- -main() { - log::info "Starting orchestrator deployment for namespace: ${NAME_SPACE}" - - if ! oc whoami &>/dev/null && ! kubectl cluster-info &>/dev/null; then - log::error "Not logged into OpenShift/Kubernetes cluster" - return 1 - fi - - log::info "Checking Serverless operators..." - if ! oc get subscription serverless-operator -n openshift-operators &>/dev/null; then - log::info "Installing OpenShift Serverless Operator..." - install_serverless_ocp_operator - else - log::info "OpenShift Serverless Operator already installed" - fi - - if ! oc get subscription logic-operator-rhel8 -n openshift-operators &>/dev/null; then - log::info "Installing OpenShift Serverless Logic Operator..." - install_serverless_logic_ocp_operator - else - log::info "OpenShift Serverless Logic Operator already installed" - fi - - log::info "Waiting for operators to be ready..." - waitfor_serverless_ocp_operator - waitfor_serverless_logic_ocp_operator - wait_for_sonataflow_crds - - configure_namespace "${NAME_SPACE}" - log::info "Deploying orchestrator workflows..." - deploy_orchestrator_workflows_operator "${NAME_SPACE}" - print_orchestrator_connection_info "${NAME_SPACE}" - - log::success "Orchestrator deployment completed successfully!" -} - -main "$@" diff --git a/workspaces/bulk-import/e2e-tests/tests/scripts/install-workflow.sh b/workspaces/bulk-import/e2e-tests/tests/scripts/install-workflow.sh new file mode 100755 index 000000000..4b46ae165 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/scripts/install-workflow.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +export NAME_SPACE="${1:-${NAME_SPACE:-orchestrator}}" + +escape_yq() { + local input="$1" + printf '%s' "$input" | sed 's/"/\\"/g' + return 0 +} + +wait_for_deployment() { + local namespace=$1 resource_name=$2 timeout_minutes=${3:-5} check_interval=${4:-10} + [[ -z "$namespace" || -z "$resource_name" ]] && { echo "wait_for_deployment: namespace and resource_name required"; return 1; } + local max_attempts=$((timeout_minutes * 60 / check_interval)) + echo "Waiting for '$resource_name' in '$namespace' (timeout ${timeout_minutes}m)..." + for ((i = 1; i <= max_attempts; i++)); do + local pod_name + pod_name=$(oc get pods -n "$namespace" 2>/dev/null | grep "$resource_name" | awk '{print $1}' | head -n 1) + if [[ -n "$pod_name" ]]; then + local is_ready + is_ready=$(oc get pod "$pod_name" -n "$namespace" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null) + if [[ "$is_ready" == "True" ]] && oc get pod "$pod_name" -n "$namespace" 2>/dev/null | grep -q Running; then + echo "Pod '$pod_name' is ready" + return 0 + fi + fi + sleep "$check_interval" + done + echo "Timeout waiting for $resource_name" + return 1 +} + +deploy_workflows() { + local namespace=$1 + + local psql_secret_name psql_svc_name psql_user_key psql_password_key sonataflow_db + psql_secret_name=$(oc get secrets -n "$namespace" -o name 2>/dev/null | grep "backstage-psql" | grep "secret" | head -1 | sed 's|secret\/||') + psql_svc_name='backstage-psql' + psql_user_key="POSTGRES_USER" + psql_password_key="POSTGRES_PASSWORD" + sonataflow_db="backstage_plugin_orchestrator" + + local workflow_repo="https://github.com/AndrienkoAleksandr/serverless-workflows.git" + local workflow_dir="/tmp/serverless-workflows" + local local_manifests="${SCRIPT_DIR}/yaml" + + # Prefer local yaml/ if it exists and has content + if [[ -d "${local_manifests}" ]] && [[ -n "$(ls -A "${local_manifests}" 2>/dev/null)" ]]; then + echo "Using local workflow manifests from ${local_manifests}" + # Apply all YAMLs in yaml/ with correct namespace + for f in "${local_manifests}"/*.yaml "${local_manifests}"/*.yml; do + [[ -e "$f" ]] && oc apply -f "$f" -n "$namespace" && echo "Applied $(basename "$f")" + done + else + echo "Cloning workflow repo..." + rm -rf "${workflow_dir}" + git clone --single-branch --branch bulk-import-workflow-sample "${workflow_repo}" "${workflow_dir}" + local workflow_manifests="${workflow_dir}/workflows/bulk-import-git-repos/manifests" + if [[ -d "${workflow_manifests}" ]]; then + echo "Applying workflow manifests from repo..." + + snToDbPatch="${workflow_manifests}/05-sonataflow_universal-pr.yaml" + yq eval -i '.spec.persistence.postgresql.secretRef.name = "'"$(escape_yq "$psql_secret_name")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.secretRef.userKey = "'"$(escape_yq "$psql_user_key")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.secretRef.passwordKey = "'"$(escape_yq "$psql_password_key")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.serviceRef.name = "'"$(escape_yq "$psql_svc_name")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.serviceRef.namespace = "'"$(escape_yq "$namespace")"'"' "$snToDbPatch" + yq eval -i '.spec.persistence.postgresql.serviceRef.databaseName = "'"$(escape_yq "$sonataflow_db")"'"' "$snToDbPatch" + + oc apply -f "${workflow_manifests}" -n "$namespace" + else + echo "Manifests path not found in repo: ${workflow_manifests}" + fi + fi + + echo "Waiting for SonataFlow resources..." + timeout 30s bash -c " + until [[ \$(oc get sf -n $namespace --no-headers 2>/dev/null | wc -l) -ge 1 ]]; do + echo \"Waiting for sf resources... \$(oc get sf -n $namespace --no-headers 2>/dev/null | wc -l)\" + sleep 5 + done + " + + wait_for_deployment "$namespace" universal-pr 5 || true + echo "Orchestrator workflows deployment done." +} + +deploy_workflows ${NAME_SPACE} diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 1e6597391..f5024012a 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -1,6 +1,7 @@ import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils"; import { test, expect, Page } from "@red-hat-developer-hub/e2e-test-utils/test"; import { APIHelper } from "@red-hat-developer-hub/e2e-test-utils/helpers"; +import installOrchestrator from "@red-hat-developer-hub/e2e-test-utils/orchestrator"; export const WAIT_OBJECTS = { muiLinearProgress: 'div[class*="MuiLinearProgress-root"]', @@ -26,7 +27,8 @@ test.describe("Bulk import tests", () => { await rhdh.configure({ auth: "keycloak" }); const orchestratorNamespace = "orchestrator"; - await $`bash tests/scripts/install-orchestrator.sh ${orchestratorNamespace}`; + await installOrchestrator(orchestratorNamespace); + await $`bash tests/scripts/install-workflow.sh ${orchestratorNamespace}`; await rhdh.deploy({ timeout: 20 * 60 * 1000, // 20 min diff --git a/workspaces/bulk-import/e2e-tests/yarn.lock b/workspaces/bulk-import/e2e-tests/yarn.lock index 032fd9b80..06fa74fa4 100644 --- a/workspaces/bulk-import/e2e-tests/yarn.lock +++ b/workspaces/bulk-import/e2e-tests/yarn.lock @@ -16,6 +16,16 @@ __metadata: languageName: node linkType: hard +"@backstage-community/plugin-rbac-common@npm:1.23.0": + version: 1.23.0 + resolution: "@backstage-community/plugin-rbac-common@npm:1.23.0" + peerDependencies: + "@backstage/errors": ^1.2.7 + "@backstage/plugin-permission-common": ^0.9.5 + checksum: 8cbb67a4854b9a72d329459cf37d05628da4ca4c665fb1e9c34e2dcbcd8d4399264b00bab8e4ee786717e2c0c5b0e3db7ee4eee6705424047a799fbf311eb5f4 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": version: 4.9.1 resolution: "@eslint-community/eslint-utils@npm:4.9.1" @@ -293,11 +303,12 @@ __metadata: languageName: node linkType: hard -"@red-hat-developer-hub/e2e-test-utils@npm:1.1.12": - version: 1.1.12 - resolution: "@red-hat-developer-hub/e2e-test-utils@npm:1.1.12" +"@red-hat-developer-hub/e2e-test-utils@npm:1.1.22": + version: 1.1.22 + resolution: "@red-hat-developer-hub/e2e-test-utils@npm:1.1.22" dependencies: "@axe-core/playwright": ^4.11.0 + "@backstage-community/plugin-rbac-common": 1.23.0 "@eslint/js": ^9.39.1 "@keycloak/keycloak-admin-client": ^26.0.0 "@kubernetes/client-node": ^1.4.0 @@ -310,12 +321,13 @@ __metadata: lodash.mergewith: ^4.6.2 otplib: 12.0.1 prettier: ^3.7.4 + proper-lockfile: ^4.1.2 typescript: ^5.9.3 typescript-eslint: ^8.48.1 zx: ^8.8.5 peerDependencies: "@playwright/test": ^1.57.0 - checksum: 1aabf33e105a39320ab2099db4d8c0b6090374970b0dee0cfe07f54b2f5cab98d309fc1e6ea41f17419454312b9b6905b26909981630c47b89665f213d67cbfa + checksum: 9f2b83a8a4ee7d1a0ccbbff2250ff75e229525acc0aa62d7d64c5889d6cffeadf105ae28bc54ae57744c9a5014d4850c24618871a32f4f14b15d30a726c85ec0 languageName: node linkType: hard @@ -725,7 +737,7 @@ __metadata: dependencies: "@eslint/js": ^9.39.2 "@playwright/test": ^1.56.1 - "@red-hat-developer-hub/e2e-test-utils": 1.1.12 + "@red-hat-developer-hub/e2e-test-utils": 1.1.22 "@types/node": ^24.10.1 dotenv: ^16.4.7 eslint: ^9.39.2 @@ -1336,7 +1348,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 @@ -2032,6 +2044,17 @@ __metadata: languageName: node linkType: hard +"proper-lockfile@npm:^4.1.2": + version: 4.1.2 + resolution: "proper-lockfile@npm:4.1.2" + dependencies: + graceful-fs: ^4.2.4 + retry: ^0.12.0 + signal-exit: ^3.0.2 + checksum: 00078ee6a61c216a56a6140c7d2a98c6c733b3678503002dc073ab8beca5d50ca271de4c85fca13b9b8ee2ff546c36674d1850509b84a04a5d0363bcb8638939 + languageName: node + linkType: hard + "pump@npm:^3.0.0": version: 3.0.3 resolution: "pump@npm:3.0.3" @@ -2102,6 +2125,13 @@ __metadata: languageName: node linkType: hard +"signal-exit@npm:^3.0.2": + version: 3.0.7 + resolution: "signal-exit@npm:3.0.7" + checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" From 4b5c9a64fe3476f1e6b16525409bc54807ab4d46 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 25 Mar 2026 22:50:35 +0200 Subject: [PATCH 18/26] feat(e2e): fix github token env var name Signed-off-by: Oleksandr Andriienko --- .../bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index f5024012a..5087b30fd 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -38,7 +38,7 @@ test.describe("Bulk import tests", () => { throw new Error(`Provide github qe token`); } - process.env.GH_RHDH_QE_USER_TOKEN = process.env.VAULT_GH_RHDH_QE_USER_TOKEN; + process.env.VAULT_GITHUB_USER_TOKEN = process.env.VAULT_GH_RHDH_QE_USER_TOKEN; // Create the repository with catalog-info.yaml file dynamically await APIHelper.createGitHubRepoWithFile( catalogRepoDetails.owner, From 144a1d23e3203ce8fe1948a89f93c608fb9ced7e Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Wed, 25 Mar 2026 23:49:18 +0200 Subject: [PATCH 19/26] feat(e2e): don't patch env variable Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/package.json | 2 +- .../bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml | 2 +- .../bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml | 2 +- .../bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts | 5 ----- workspaces/bulk-import/e2e-tests/yarn.lock | 4 ++-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/package.json b/workspaces/bulk-import/e2e-tests/package.json index 711d88be4..5b68b80e9 100644 --- a/workspaces/bulk-import/e2e-tests/package.json +++ b/workspaces/bulk-import/e2e-tests/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@playwright/test": "^1.56.1", - "@red-hat-developer-hub/e2e-test-utils": "1.1.22", + "@red-hat-developer-hub/e2e-test-utils": "^1.1.22", "@types/node": "^24.10.1", "dotenv": "^16.4.7", "eslint": "^9.39.2", diff --git a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml index b2d3d45a0..6f481abd0 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml @@ -4,7 +4,7 @@ integrations: github: - host: github.com - token: ${VAULT_GH_RHDH_QE_USER_TOKEN} + token: ${VAULT_GITHUB_USER_TOKEN} gitlab: - host: gitlab.com token: temp diff --git a/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml b/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml index 1e86996c5..44a7a585b 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/rhdh-secrets.yaml @@ -4,4 +4,4 @@ metadata: name: rhdh-secrets type: Opaque stringData: - VAULT_GH_RHDH_QE_USER_TOKEN: $VAULT_GH_RHDH_QE_USER_TOKEN + VAULT_GITHUB_USER_TOKEN: $VAULT_GH_RHDH_QE_USER_TOKEN diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 5087b30fd..a75acf2c6 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -34,11 +34,6 @@ test.describe("Bulk import tests", () => { timeout: 20 * 60 * 1000, // 20 min }); - if (!process.env.VAULT_GH_RHDH_QE_USER_TOKEN) { - throw new Error(`Provide github qe token`); - } - - process.env.VAULT_GITHUB_USER_TOKEN = process.env.VAULT_GH_RHDH_QE_USER_TOKEN; // Create the repository with catalog-info.yaml file dynamically await APIHelper.createGitHubRepoWithFile( catalogRepoDetails.owner, diff --git a/workspaces/bulk-import/e2e-tests/yarn.lock b/workspaces/bulk-import/e2e-tests/yarn.lock index 06fa74fa4..480a49bb0 100644 --- a/workspaces/bulk-import/e2e-tests/yarn.lock +++ b/workspaces/bulk-import/e2e-tests/yarn.lock @@ -303,7 +303,7 @@ __metadata: languageName: node linkType: hard -"@red-hat-developer-hub/e2e-test-utils@npm:1.1.22": +"@red-hat-developer-hub/e2e-test-utils@npm:^1.1.22": version: 1.1.22 resolution: "@red-hat-developer-hub/e2e-test-utils@npm:1.1.22" dependencies: @@ -737,7 +737,7 @@ __metadata: dependencies: "@eslint/js": ^9.39.2 "@playwright/test": ^1.56.1 - "@red-hat-developer-hub/e2e-test-utils": 1.1.22 + "@red-hat-developer-hub/e2e-test-utils": ^1.1.22 "@types/node": ^24.10.1 dotenv: ^16.4.7 eslint: ^9.39.2 From 200088ea2d12dcf14789738ffb92f114f7da301b Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Thu, 26 Mar 2026 15:24:43 +0200 Subject: [PATCH 20/26] feat(e2e): handle code review feedback Signed-off-by: Oleksandr Andriienko --- .../e2e-tests/tests/specs/bulk-import.spec.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index a75acf2c6..b59ed3dce 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -19,16 +19,13 @@ test.describe("Bulk import tests", () => { }; test.beforeAll(async ({ rhdh }) => { - test.info().annotations.push({ - type: "component", - description: "plugins", - }); - await rhdh.configure({ auth: "keycloak" }); - const orchestratorNamespace = "orchestrator"; - await installOrchestrator(orchestratorNamespace); - await $`bash tests/scripts/install-workflow.sh ${orchestratorNamespace}`; + await test.runOnce("install-orchestrator-and-test-workflow", async () => { + const orchestratorNamespace = "orchestrator"; + await installOrchestrator(orchestratorNamespace); + await $`bash tests/scripts/install-workflow.sh ${orchestratorNamespace}`; + }); await rhdh.deploy({ timeout: 20 * 60 * 1000, // 20 min From b090485881ae69c6e34c64fcaac5d664e0dd3230 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Sun, 19 Apr 2026 05:14:38 +0300 Subject: [PATCH 21/26] feat(e2e): migrate bulk-import tests from rhdh repo Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/package.json | 2 +- .../app-config-rhdh-orchestrator-mode.yaml | 14 + .../tests/config/app-config-rhdh.yaml | 45 ++ .../dynamic-plugins-with-orchestrator.yaml | 75 +++ .../tests/config/dynamic-plugins.yaml | 57 -- .../tests/config/rbac-configmap.yaml | 13 + .../e2e-tests/tests/config/values.yaml | 57 ++ .../e2e-tests/tests/specs/bulk-import.spec.ts | 545 +++++++++++++++++- workspaces/bulk-import/e2e-tests/yarn.lock | 513 +++++++++++++++-- 9 files changed, 1185 insertions(+), 136 deletions(-) create mode 100644 workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh-orchestrator-mode.yaml create mode 100644 workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins-with-orchestrator.yaml create mode 100644 workspaces/bulk-import/e2e-tests/tests/config/rbac-configmap.yaml create mode 100644 workspaces/bulk-import/e2e-tests/tests/config/values.yaml diff --git a/workspaces/bulk-import/e2e-tests/package.json b/workspaces/bulk-import/e2e-tests/package.json index 5b68b80e9..190e8c0da 100644 --- a/workspaces/bulk-import/e2e-tests/package.json +++ b/workspaces/bulk-import/e2e-tests/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@playwright/test": "^1.56.1", - "@red-hat-developer-hub/e2e-test-utils": "^1.1.22", + "@red-hat-developer-hub/e2e-test-utils": "AndrienkoAleksandr/rhdh-e2e-test-utils#force-deploy", "@types/node": "^24.10.1", "dotenv": "^16.4.7", "eslint": "^9.39.2", diff --git a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh-orchestrator-mode.yaml b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh-orchestrator-mode.yaml new file mode 100644 index 000000000..948d49dc9 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh-orchestrator-mode.yaml @@ -0,0 +1,14 @@ +# rhdh app config file +# this file is used to merge with the default values of the rhdh app config + +integrations: + github: + - host: github.com + token: ${VAULT_GITHUB_USER_TOKEN} + gitlab: + - host: gitlab.com + token: temp + +bulkImport: + importAPI: orchestrator + orchestratorWorkflow: universal-pr diff --git a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml index 6f481abd0..6e3d025dd 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml @@ -1,6 +1,16 @@ # rhdh app config file # this file is used to merge with the default values of the rhdh app config +catalog: + import: + entityFilename: catalog-info.yaml + pullRequestBranchName: backstage-integration + rules: + - allow: [Component, System, Group, User, Resource, Location, Template, API] + locations: + - type: url + target: https://github.com/cloud-eda/janus-test-3-bulk-import/blob/main/catalog-info.yaml + integrations: github: - host: github.com @@ -8,3 +18,38 @@ integrations: gitlab: - host: gitlab.com token: temp + +permission: + enabled: true + rbac: + maxDepth: 1 + policyFileReload: true + policies-csv-file: "./rbac/rbac-policy.csv" + pluginsWithPermission: + - catalog + - permission + - scaffolder + - bulk-import + # admin: + # users: + # todo remove it. + # - name: user:default/test1 + policyDecisionPrecedence: conditional # default behavior +includeTransitiveGroupOwnership: true + +bulkImport: + instructionsEnabled: true + instructionsDefaultExpanded: true + instructionsSteps: + - id: "step1" + text: "Choose a source control tool for pull request creation" + icon: "approval-tool" + - id: "step2" + text: "Choose which items you want to import" + icon: "search" + - id: "step3" + text: "Generate a catalog-info.yaml file for each selected item" + icon: "build" + - id: "step4" + text: "View the pull/merge request details" + icon: "kind:api" diff --git a/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins-with-orchestrator.yaml b/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins-with-orchestrator.yaml new file mode 100644 index 000000000..2ebe0e459 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins-with-orchestrator.yaml @@ -0,0 +1,75 @@ +plugins: + # bulk-import plugins + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic + disabled: false + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import + disabled: false + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-bulk-import: + appIcons: + - name: bulkImportIcon + importName: BulkImportIcon + dynamicRoutes: + - path: /bulk-import/repositories + importName: BulkImportPage + menuItem: + icon: bulkImportIcon + text: Bulk import + + - package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-github-dynamic + disabled: true + + # Group: Orchestrator + - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator:bs_1.45.3__5.3.1" + disabled: false + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + textKey: menuItem.orchestrator + path: /orchestrator + entityTabs: + - path: /workflows + title: Workflows + titleKey: catalog.entityPage.workflows.title + mountPoint: entity.page.workflows + mountPoints: + - mountPoint: entity.page.workflows/cards + importName: OrchestratorCatalogTab + config: + layout: + gridColumn: 1 / -1 + if: + anyOf: + - IsOrchestratorCatalogTabAvailable + + - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-backend:bs_1.45.3__8.5.1" + disabled: false + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.orchestrator.svc.cluster.local:80 + + - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-scaffolder-backend-module-orchestrator:bs_1.45.3__1.3.1" + disabled: false + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service.orchestrator.svc.cluster.local:80 + + - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-form-widgets:bs_1.45.3__1.6.0" + disabled: false + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: {} diff --git a/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml b/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml index 17f231759..e962dbb70 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/dynamic-plugins.yaml @@ -2,10 +2,6 @@ plugins: # bulk-import plugins - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic disabled: false - pluginConfig: - bulkImport: - importAPI: orchestrator - orchestratorWorkflow: universal-pr - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import disabled: false pluginConfig: @@ -24,56 +20,3 @@ plugins: - package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-github-dynamic disabled: true - - # Group: Orchestrator - - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator:bs_1.45.3__5.3.1" - disabled: false - pluginConfig: - dynamicPlugins: - frontend: - red-hat-developer-hub.backstage-plugin-orchestrator: - appIcons: - - importName: OrchestratorIcon - name: orchestratorIcon - dynamicRoutes: - - importName: OrchestratorPage - menuItem: - icon: orchestratorIcon - text: Orchestrator - textKey: menuItem.orchestrator - path: /orchestrator - entityTabs: - - path: /workflows - title: Workflows - titleKey: catalog.entityPage.workflows.title - mountPoint: entity.page.workflows - mountPoints: - - mountPoint: entity.page.workflows/cards - importName: OrchestratorCatalogTab - config: - layout: - gridColumn: 1 / -1 - if: - anyOf: - - IsOrchestratorCatalogTabAvailable - - - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-backend:bs_1.45.3__8.5.1" - disabled: false - pluginConfig: - orchestrator: - dataIndexService: - url: http://sonataflow-platform-data-index-service.orchestrator.svc.cluster.local:80 - - - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-scaffolder-backend-module-orchestrator:bs_1.45.3__1.3.1" - disabled: false - pluginConfig: - orchestrator: - dataIndexService: - url: http://sonataflow-platform-data-index-service.orchestrator.svc.cluster.local:80 - - - package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-orchestrator-form-widgets:bs_1.45.3__1.6.0" - disabled: false - pluginConfig: - dynamicPlugins: - frontend: - red-hat-developer-hub.backstage-plugin-orchestrator-form-widgets: {} diff --git a/workspaces/bulk-import/e2e-tests/tests/config/rbac-configmap.yaml b/workspaces/bulk-import/e2e-tests/tests/config/rbac-configmap.yaml new file mode 100644 index 000000000..ec98e9f1a --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/config/rbac-configmap.yaml @@ -0,0 +1,13 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: rbac-policy + labels: + backstage.io/kubernetes-id: developer-hub +data: + rbac-policy.csv: | + g, user:default/test1, role:default/bulk-importer + p, role:default/bulk-importer, bulk-import, use, allow + p, role:default/bulk-importer, catalog-entity, read, allow + p, role:default/bulk-importer, catalog.entity.create, create, allow + p, role:default/bulk-importer, catalog.location.create, create, allow diff --git a/workspaces/bulk-import/e2e-tests/tests/config/values.yaml b/workspaces/bulk-import/e2e-tests/tests/config/values.yaml new file mode 100644 index 000000000..e2577d087 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/tests/config/values.yaml @@ -0,0 +1,57 @@ +upstream: + image: + registry: quay.io + repository: rhdh-community/rhdh + tag: next + backstage: + extraVolumeMounts: + # NOTE: Lists will not be merged with the default values file. So need to append all the defaults if you want to add a new item here + # See https://issues.redhat.com/browse/RHDHPLAN-869 + # The initContainer below will install dynamic plugins in this volume mount. + - name: dynamic-plugins-root + mountPath: /opt/app-root/src/dynamic-plugins-root + - name: extensions-catalog + mountPath: /extensions + - name: temp + mountPath: /tmp + - name: rbac-policy + mountPath: /opt/app-root/src/rbac + extraVolumes: + # NOTE: Lists will not be merged with the default values file. So need to append all the defaults if you want to add a new item here + # See https://issues.redhat.com/browse/RHDHPLAN-869 + # -- Ephemeral volume that will contain the dynamic plugins installed by the initContainer below at start. + # To have more control on underlying storage, the [emptyDir](https://docs.openshift.com/container-platform/4.13/storage/understanding-ephemeral-storage.html) + # could be changed to a [generic ephemeral volume](https://docs.openshift.com/container-platform/4.13/storage/generic-ephemeral-vols.html#generic-ephemeral-vols-procedure_generic-ephemeral-volumes). + - name: dynamic-plugins-root + emptyDir: {} + # Volume that will expose the `dynamic-plugins.yaml` file from the `dynamic-plugins` config map. + # The `dynamic-plugins` config map is created by the helm chart from the content of the `global.dynamic` field. + - name: dynamic-plugins + configMap: + defaultMode: 420 + name: '{{ printf "%s-dynamic-plugins" .Release.Name }}' + optional: true + # Optional volume that allows exposing the `.npmrc` file (through a `dynamic-plugins-npmrc` secret) + # to be used when running `npm pack` during the dynamic plugins installation by the initContainer. + - name: dynamic-plugins-npmrc + secret: + defaultMode: 420 + optional: true + secretName: '{{ printf "%s-dynamic-plugins-npmrc" .Release.Name }}' + - name: dynamic-plugins-registry-auth + secret: + defaultMode: 416 + optional: true + secretName: '{{ printf "%s-dynamic-plugins-registry-auth" .Release.Name }}' + - name: npmcacache + emptyDir: {} + # -- Ephemeral volume used by the install-dynamic-plugins init container to extract catalog entities from the catalog index image. + # Mounted at the /extensions path in the backstage-backend main container for automatic discovery by the extension catalog backend providers. + - name: extensions-catalog + emptyDir: {} + - name: temp + emptyDir: {} + - name: rbac-policy + configMap: + defaultMode: 420 + name: rbac-policy diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index b59ed3dce..125814b9f 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -2,16 +2,510 @@ import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils"; import { test, expect, Page } from "@red-hat-developer-hub/e2e-test-utils/test"; import { APIHelper } from "@red-hat-developer-hub/e2e-test-utils/helpers"; import installOrchestrator from "@red-hat-developer-hub/e2e-test-utils/orchestrator"; - +import path from "path"; export const WAIT_OBJECTS = { muiLinearProgress: 'div[class*="MuiLinearProgress-root"]', muiCircularProgress: '[class*="MuiCircularProgress-root"]', }; -test.describe("Bulk import tests", () => { - const catalogRepoName = `janus-test-1-bulk-import-test-${Date.now()}`; - const githubOrg = "janus-qe"; +const DEFAULT_CATALOG_INFO_YAML = ( + componentName: string, + projectSlug: string, + owner: string, +) => `apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: ${componentName} + annotations: + github.com/project-slug: ${projectSlug} +spec: + type: other + lifecycle: unknown + owner: user:default/${owner} +`; + +// Helper function to select a repository in the table +async function selectRepoInTable(page: Page, repoName: string) { + await page + .locator(`tr:has(:text-is("${repoName}"))`) + .getByRole("checkbox") + .check(); +} + +// Helper function to filter/search for added repositories +async function filterAddedRepo(page: Page, uiHelper: any, repoName: string) { + await uiHelper.searchInputPlaceholder(repoName); +} + +// Helper function to wait for page load (wait for progress indicators to disappear) +async function waitForLoad(page: Page) { + for (const item of Object.values(WAIT_OBJECTS)) { + await page + .waitForSelector(item, { + state: "hidden", + timeout: 12000, + }) + .catch(() => { + // Ignore if selector not found + }); + } +} + +/** + * Catalog "Import an existing Git repository" flow without e2e-test-utils + * CatalogImportPage.analyzeAndWait (it ties success to Analyze disappearing, which flakes). + */ +async function catalogImportRegisterFromComponentUrl(page: Page, url: string) { + await page.locator('input[name="url"]').fill(url); + await page.getByRole("button", { name: "Analyze" }).click(); + await waitForLoad(page); + + const importButton = page.getByRole("button", { + name: "Import", + exact: true, + }); + const refreshButton = page.getByRole("button", { + name: "Refresh", + exact: true, + }); + + await expect(importButton.or(refreshButton)).toBeVisible({ timeout: 60_000 }); + + if (await refreshButton.isVisible()) { + return; + } + + await expect(importButton).toBeEnabled({ timeout: 30_000 }); + await importButton.click(); + await waitForLoad(page); +} + +test.describe.serial("Bulk Import plugin", () => { + test.skip( + () => !!process.env.JOB_NAME?.includes("osd-gcp"), + "skipping due to RHDHBUGS-555 on OSD Env", + ); + test.describe.configure({ retries: process.env.CI ? 5 : 0 }); + + const catalogRepoName = `cloud-eda-1-bulk-import-test-${Date.now()}`; const catalogRepoDetails = { + name: catalogRepoName, + url: `github.com/cloud-eda/${catalogRepoName}`, + org: "github.com/cloud-eda", + owner: "cloud-eda", + }; + + const catalogInfoYamlContent = `apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: ${catalogRepoName} + annotations: + github.com/project-slug: cloud-eda/${catalogRepoName} +spec: + type: other + lifecycle: unknown + owner: user:default/rhdh-qe-2`; + + const newRepoName = `bulk-import-${Date.now()}`; + const newRepoDetails = { + owner: "cloud-eda", + repoName: newRepoName, + updatedComponentName: `${newRepoName}-updated`, + labels: `bulkimport1: test1;bulkimport2: test2`, + repoUrl: `github.com/cloud-eda/${newRepoName}`, + }; + + test.beforeAll(async ({ rhdh }) => { + const rbacConfigmapPath = path.resolve( + process.cwd(), + "tests/config/rbac-configmap.yaml", + ); + const namespace = rhdh.deploymentConfig.namespace; + // todo: make it once... + await $`kubectl apply -f ${rbacConfigmapPath} -n ${namespace}`; + await rhdh.configure({ + auth: "keycloak", + appConfig: "tests/config/app-config-rhdh.yaml", + valueFile: "tests/config/values.yaml", + }); + await rhdh.deploy({ + timeout: 20 * 60 * 1000, // 20 min + }); + + // Create the repository with catalog-info.yaml file dynamically + await APIHelper.createGitHubRepoWithFile( + catalogRepoDetails.owner, + catalogRepoDetails.name, + "catalog-info.yaml", + catalogInfoYamlContent, + ); + + // Create new GitHub repository without catalog-info.yaml + await APIHelper.createGitHubRepoWithFile( + newRepoDetails.owner, + newRepoDetails.repoName, + "README.md", + "qa test project", + ); + }); + + test.beforeEach(async ({ loginHelper, uiHelper }) => { + await loginHelper.loginAsKeycloakUser(); + await uiHelper.openSidebar("Bulk import"); + await uiHelper.verifyHeading("Bulk import"); + }); + + test("Bulk import plugin page", async ({ page }) => { + await expect( + page.getByRole("button", { name: "Import to Red Hat Developer" }), + ).toHaveAttribute("aria-expanded", "true"); + await page + .getByRole("button", { name: "Import to Red Hat Developer" }) + .click(); + await expect( + page.getByRole("button", { name: "Import to Red Hat Developer" }), + ).toHaveAttribute("aria-expanded", "false"); + await expect( + page.getByText("Source control tool", { exact: true }), + ).toBeVisible(); + await page + .getByLabel("Importing requires approval.") + .getByTestId("HelpOutlineIcon") + .hover(); + await expect( + page.getByRole("tooltip", { name: "Importing requires approval." }), + ).toBeVisible(); + await expect(page.getByRole("radio", { name: "GitHub" })).toBeChecked(); + await page.getByRole("radio", { name: "GitLab" }).check(); + await expect(page.getByRole("radio", { name: "GitLab" })).toBeChecked(); + await page.getByRole("radio", { name: "GitHub" }).check(); + await expect(page.getByRole("article")).toMatchAriaSnapshot(` + - table: + - rowgroup: + - row "select all repositories Name URL Organization Status": + - columnheader "select all repositories Name": + - checkbox "select all repositories" + - text: Name + - columnheader "URL" + - columnheader "Organization" + - columnheader "Status" + `); + }); + + test("Add a Repository and Confirm its Preview", async ({ + page, + uiHelper, + }) => { + // Wait to ensure the repo will appear in the Bulk Import UI + await expect(async () => { + await page.reload(); + await waitForLoad(page); + await uiHelper.searchInputPlaceholder(catalogRepoDetails.name); + await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, [ + "Ready to import", + ]); + }).toPass({ + intervals: [5_000], + timeout: 40_000, + }); + + await selectRepoInTable(page, catalogRepoDetails.name); + await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, [ + catalogRepoDetails.url, + "Ready to import", + "Preview file", + ]); + + await uiHelper.clickOnLinkInTableByUniqueText( + catalogRepoDetails.name, + "Preview file", + ); + + await expect(await uiHelper.clickButton("Save")).toBeHidden(); + await expect(await uiHelper.clickButton("Import")).toBeDisabled(); + }); + + test("Add a Repository, generate a PR, and confirm its preview", async ({ + page, + uiHelper, + }) => { + // Wait to ensure the repo will appear in the Bulk Import UI + await expect(async () => { + await page.reload(); + await waitForLoad(page); + await uiHelper.searchInputPlaceholder(newRepoDetails.repoName); + await uiHelper.verifyRowInTableByUniqueText(newRepoDetails.repoName, [ + "Ready to import", + ]); + }).toPass({ + intervals: [5_000], + timeout: 40_000, + }); + + await selectRepoInTable(page, newRepoDetails.repoName); + await uiHelper.clickOnLinkInTableByUniqueText( + newRepoDetails.repoName, + "Preview file", + ); + await uiHelper.clickButton("Save"); + await uiHelper.verifyRowInTableByUniqueText(newRepoDetails.repoName, [ + "Ready to import", + ]); + await expect(await uiHelper.clickButton("Import")).toBeDisabled({ + timeout: 10000, + }); + }); + + // todo, plugin changed behavior, maybe bug... + test('Verify that the two selected repositories are listed: one with the status "Already imported" and another with the status "WAIT_PR_APPROVAL."', async ({ + page, + uiHelper, + }) => { + await waitForLoad(page); + await filterAddedRepo(page, uiHelper, catalogRepoDetails.name); + await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, [ + catalogRepoDetails.url, + "Imported", + ]); + await filterAddedRepo(page, uiHelper, newRepoDetails.repoName); + await uiHelper.verifyRowInTableByUniqueText(newRepoDetails.repoName, [ + "Waiting for Approval", + ]); + }); + + test("Verify the Content of catalog-info.yaml in the PR is Correct", async () => { + // First verify that a PR exists + const prs = await APIHelper.getGitHubPRs( + newRepoDetails.owner, + newRepoDetails.repoName, + "open", + ); + expect(prs.length).toBeGreaterThan(0); + + const prCatalogInfoYaml = await APIHelper.getfileContentFromPR( + newRepoDetails.owner, + newRepoDetails.repoName, + 1, + "catalog-info.yaml", + ); + const expectedCatalogInfoYaml = DEFAULT_CATALOG_INFO_YAML( + newRepoDetails.repoName, + `${newRepoDetails.owner}/${newRepoDetails.repoName}`, + "test1", + ); + expect(prCatalogInfoYaml).toEqual(expectedCatalogInfoYaml); + }); + + test("Verify Selected repositories shows catalog-info.yaml status as 'Already imported' and 'WAIT_PR_APPROVAL'", async ({ + uiHelper, + }) => { + await uiHelper.searchInputPlaceholder(catalogRepoDetails.name); + await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, [ + "Imported", + ]); + await uiHelper.searchInputPlaceholder(newRepoDetails.repoName); + await uiHelper.verifyRowInTableByUniqueText(newRepoDetails.repoName, [ + "Waiting for Approval", + ]); + }); + + test("Merge the PR on GitHub and Confirm the Status Updates to 'Already imported'", async ({ + page, + uiHelper, + }) => { + // Merge PR is generated for the repository without the catalog.yaml file. + await APIHelper.mergeGitHubPR( + newRepoDetails.owner, + newRepoDetails.repoName, + 1, + ); + // Ensure that no PR is generated for the repository that already has a catalog.yaml file. + expect( + await APIHelper.getGitHubPRs( + catalogRepoDetails.owner, + catalogRepoDetails.name, + "open", + ), + ).toHaveLength(0); + + // Wait to ensure the repo will appear in the Bulk Import UI + await expect(async () => { + await page.reload(); + await waitForLoad(page); + await filterAddedRepo(page, uiHelper, newRepoDetails.repoName); + // verify that the status has changed to "Already imported." + await uiHelper.verifyRowInTableByUniqueText(newRepoDetails.repoName, [ + "Imported", + ]); + }).toPass({ + intervals: [5_000], + timeout: 40_000, + }); + }); + + test("Verify Added Repositories Appear in the Catalog as Expected", async ({ + uiHelper, + }) => { + await uiHelper.openSidebar("Catalog"); + await uiHelper.selectMuiBox("Kind", "Component"); + await uiHelper.searchInputPlaceholder(catalogRepoDetails.name); + //Wait 60 seconds to observe the result + + await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, [ + "other", + "unknown", + ]); + }); + + test.afterAll(async () => { + try { + // Delete the dynamically created GitHub repository with catalog-info.yaml + await APIHelper.deleteGitHubRepo( + catalogRepoDetails.owner, + catalogRepoDetails.name, + ); + + // Delete the GitHub repository + await APIHelper.deleteGitHubRepo( + newRepoDetails.owner, + newRepoDetails.repoName, + ); + + console.log( + `[Cleanup] Deleted GitHub repositories: ${catalogRepoDetails.name}, ${newRepoDetails.repoName}`, + ); + } catch (error) { + console.error( + `[Cleanup] Final cleanup failed: ${(error as any).message}`, + ); + } + }); +}); + +test.describe + .serial("Bulk Import - Verify existing repo are displayed in bulk import Added repositories", () => { + test.skip( + () => !!process.env.JOB_NAME?.includes("osd-gcp"), + "skipping due to RHDHBUGS-555 on OSD Env", + ); + + const existingRepoFromAppConfig = "janus-test-3-bulk-import"; + + const existingComponentDetails = { + name: "janus-test-2-bulk-import-test", + repoName: "janus-test-2-bulk-import-test", + url: "https://github.com/cloud-eda/janus-test-2-bulk-import-test/blob/main/catalog-info.yaml", + }; + + test.beforeAll(async ({ rhdh }) => { + const rbacConfigmapPath = path.resolve( + process.cwd(), + "tests/config/rbac-configmap.yaml", + ); + const namespace = rhdh.deploymentConfig.namespace; + // todo: make it once... + await $`kubectl apply -f ${rbacConfigmapPath} -n ${namespace}`; + await rhdh.configure({ + auth: "keycloak", + appConfig: "tests/config/app-config-rhdh.yaml", + valueFile: "tests/config/values.yaml", + }); + await rhdh.deploy({ + timeout: 20 * 60 * 1000, // 20 min + }); + }); + + test.beforeEach(async ({ loginHelper }) => { + await loginHelper.loginAsKeycloakUser( + process.env.GH_USER2_ID, + process.env.GH_USER2_PASS, + ); + }); + + test("Verify existing repo from app-config is displayed in bulk import Added repositories", async ({ + page, + uiHelper, + }) => { + await uiHelper.openSidebar("Bulk import"); + await waitForLoad(page); + await filterAddedRepo(page, uiHelper, existingRepoFromAppConfig); + await uiHelper.verifyRowInTableByUniqueText(existingRepoFromAppConfig, [ + "Imported", + ]); + }); + + test('Verify repo from "import an existing git repository" are displayed in bulk import Added repositories', async ({ + page, + uiHelper, + }) => { + // Import an existing Git repository + await uiHelper.openSidebar("Catalog"); + await uiHelper.clickButton("Self-service"); + await uiHelper.clickButton("Import an existing Git repository"); + + await catalogImportRegisterFromComponentUrl( + page, + existingComponentDetails.url, + ); + + // Verify in bulk import's Added Repositories + // Navigate directly to ensure a clean page state (avoids landing on the import tab) + // The backend may take time to sync the import status, so retry with page reload + await expect(async () => { + await page.goto("/bulk-import/repositories"); + await waitForLoad(page); + await filterAddedRepo(page, uiHelper, existingComponentDetails.repoName); + await uiHelper.verifyRowInTableByUniqueText( + existingComponentDetails.repoName, + ["Imported"], + ); + }).toPass({ + intervals: [5_000, 10_000, 15_000], + timeout: 90_000, + }); + }); +}); + +test.describe + .serial("Bulk Import - Ensure users without bulk import permissions cannot access the bulk import plugin", () => { + test.skip( + () => !!process.env.JOB_NAME?.includes("osd-gcp"), + "skipping due to RHDHBUGS-555 on OSD Env", + ); + + test.beforeAll(async ({ rhdh }) => { + await rhdh.configure({ + auth: "keycloak", + appConfig: "tests/config/app-config-rhdh.yaml", + valueFile: "tests/config/values.yaml", + }); + await rhdh.deploy({ + timeout: 20 * 60 * 1000, // 20 min + }); + }); + + test.beforeEach(async ({ loginHelper }) => { + // Second default user password was not exposed with help of framework. So let's hard code it for now... + await loginHelper.loginAsKeycloakUser("test2", "test2@123"); + }); + + test("Bulk Import - Verify users without permission cannot access", async ({ + uiHelper, + }) => { + await uiHelper.openSidebar("Bulk import"); + await uiHelper.verifyText("Permission required"); + expect(await uiHelper.isBtnVisible("Import")).toBeFalsy(); + }); +}); + +// ============================================================================ +// ORCHESTRATOR MODE TESTS (CURRENT TESTS) +// ============================================================================ + +test.describe("Bulk import tests orchestrator mode", () => { + const catalogRepoName = `cloud-eda-1-bulk-import-test-${Date.now()}`; + const githubOrg = "cloud-eda"; + const catalogRepoDetailsForOrchestrator = { name: catalogRepoName, url: `github.com/${githubOrg}/${catalogRepoName}`, org: `github.com/${githubOrg}`, @@ -19,22 +513,25 @@ test.describe("Bulk import tests", () => { }; test.beforeAll(async ({ rhdh }) => { - await rhdh.configure({ auth: "keycloak" }); + await rhdh.configure({ + auth: "keycloak", + appConfig: "tests/config/app-config-rhdh-orchestrator-mode.yaml", + dynamicPlugins: "tests/config/dynamic-plugins-with-orchestrator.yaml", + }); + + const orchestratorNamespace = "orchestrator"; await test.runOnce("install-orchestrator-and-test-workflow", async () => { - const orchestratorNamespace = "orchestrator"; await installOrchestrator(orchestratorNamespace); await $`bash tests/scripts/install-workflow.sh ${orchestratorNamespace}`; }); - await rhdh.deploy({ - timeout: 20 * 60 * 1000, // 20 min - }); + await rhdh.deploy({ forceUpdate: true }); + await rhdh.waitUntilReady(); - // Create the repository with catalog-info.yaml file dynamically await APIHelper.createGitHubRepoWithFile( - catalogRepoDetails.owner, - catalogRepoDetails.name, + catalogRepoDetailsForOrchestrator.owner, + catalogRepoDetailsForOrchestrator.name, "test", "ABC", ); @@ -107,20 +604,26 @@ test.describe("Bulk import tests", () => { }); } - await uiHelper.searchInputPlaceholder(catalogRepoDetails.name); - await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, []); + await uiHelper.searchInputPlaceholder( + catalogRepoDetailsForOrchestrator.name, + ); + await uiHelper.verifyRowInTableByUniqueText( + catalogRepoDetailsForOrchestrator.name, + [], + ); }).toPass({ intervals: [5_000], timeout: 40_000, }); await page - .locator(`tr:has(:text-is("${catalogRepoDetails.name}"))`) + .locator(`tr:has(:text-is("${catalogRepoDetailsForOrchestrator.name}"))`) .getByRole("checkbox") .check(); - await uiHelper.verifyRowInTableByUniqueText(catalogRepoDetails.name, [ - catalogRepoDetails.url, - ]); + await uiHelper.verifyRowInTableByUniqueText( + catalogRepoDetailsForOrchestrator.name, + [catalogRepoDetailsForOrchestrator.url], + ); await expect(await uiHelper.clickButton("Import")).toBeDisabled({ timeout: 10000, @@ -136,12 +639,12 @@ test.describe("Bulk import tests", () => { try { // Delete the dynamically created GitHub repository with catalog-info.yaml await APIHelper.deleteGitHubRepo( - catalogRepoDetails.owner, - catalogRepoDetails.name, + catalogRepoDetailsForOrchestrator.owner, + catalogRepoDetailsForOrchestrator.name, ); console.log( - `[Cleanup] Deleted GitHub repository: ${catalogRepoDetails.name}`, + `[Cleanup] Deleted GitHub repository: ${catalogRepoDetailsForOrchestrator.name}`, ); } catch (error) { console.error( diff --git a/workspaces/bulk-import/e2e-tests/yarn.lock b/workspaces/bulk-import/e2e-tests/yarn.lock index 480a49bb0..ddb2bc4cf 100644 --- a/workspaces/bulk-import/e2e-tests/yarn.lock +++ b/workspaces/bulk-import/e2e-tests/yarn.lock @@ -5,24 +5,24 @@ __metadata: version: 6 cacheKey: 8 -"@axe-core/playwright@npm:^4.11.0": - version: 4.11.0 - resolution: "@axe-core/playwright@npm:4.11.0" +"@axe-core/playwright@npm:4.11.1": + version: 4.11.1 + resolution: "@axe-core/playwright@npm:4.11.1" dependencies: - axe-core: ~4.11.0 + axe-core: ~4.11.1 peerDependencies: playwright-core: ">= 1.0.0" - checksum: 036d13cb73f9c3bdcff039caa8a3f4ae9c2fffeb41855053dd78f72d98d1635b0d7eec38ff5087beaa5e15c99e060a771d1b449f08df58694f02781d54aa155c + checksum: 311f107688bfd930ee201dbab9c75048a6cdd39be7af0d8fdf05e5d01b76ef8d57ca687954102839eefa14ec23f96b6a2833d4bef82c4b280e43b7f4e15a39b3 languageName: node linkType: hard -"@backstage-community/plugin-rbac-common@npm:1.23.0": - version: 1.23.0 - resolution: "@backstage-community/plugin-rbac-common@npm:1.23.0" +"@backstage-community/plugin-rbac-common@npm:1.26.0": + version: 1.26.0 + resolution: "@backstage-community/plugin-rbac-common@npm:1.26.0" peerDependencies: "@backstage/errors": ^1.2.7 - "@backstage/plugin-permission-common": ^0.9.5 - checksum: 8cbb67a4854b9a72d329459cf37d05628da4ca4c665fb1e9c34e2dcbcd8d4399264b00bab8e4ee786717e2c0c5b0e3db7ee4eee6705424047a799fbf311eb5f4 + "@backstage/plugin-permission-common": ^0.9.7 + checksum: 690ea138f77595396a51ad99982d574d3de32b4900f5aba93d0b2ac893e4b918b967efa77c5a9bd7514f8c42e2af1fc6a244debb0253649620ce7cf613599752 languageName: node linkType: hard @@ -55,6 +55,17 @@ __metadata: languageName: node linkType: hard +"@eslint/config-array@npm:^0.23.4": + version: 0.23.5 + resolution: "@eslint/config-array@npm:0.23.5" + dependencies: + "@eslint/object-schema": ^3.0.5 + debug: ^4.3.1 + minimatch: ^10.2.4 + checksum: 2cb8c3d3450f2b1c91dcc21109bfee58356915cbfa1429b9e82efc04c2acf7ccdf12ef20734989afdb1e676b8bf5f2e10548405efc6b8b2c89bbd9e89e5a8e49 + languageName: node + linkType: hard + "@eslint/config-helpers@npm:^0.4.2": version: 0.4.2 resolution: "@eslint/config-helpers@npm:0.4.2" @@ -64,6 +75,15 @@ __metadata: languageName: node linkType: hard +"@eslint/config-helpers@npm:^0.5.4": + version: 0.5.5 + resolution: "@eslint/config-helpers@npm:0.5.5" + dependencies: + "@eslint/core": ^1.2.1 + checksum: 2442c0e5281b0a0733942a439fc3fd18b38bd69c2f49a284ec7cac8658f287c4356f6e83a513efd377c2d9e55c4624d47fa28461fa7d5431eef1f5f0d14f23d0 + languageName: node + linkType: hard + "@eslint/core@npm:^0.17.0": version: 0.17.0 resolution: "@eslint/core@npm:0.17.0" @@ -73,6 +93,15 @@ __metadata: languageName: node linkType: hard +"@eslint/core@npm:^1.2.0, @eslint/core@npm:^1.2.1": + version: 1.2.1 + resolution: "@eslint/core@npm:1.2.1" + dependencies: + "@types/json-schema": ^7.0.15 + checksum: 430f53c5c6bcfabe54d7232d6b74bf9f6f62b0337f73ca0db70a0a0dbe4843243ce24577df61619fcbc0ef45cc6e2872074bed3295538acd72361b69f3b5eb47 + languageName: node + linkType: hard + "@eslint/eslintrc@npm:^3.3.1": version: 3.3.3 resolution: "@eslint/eslintrc@npm:3.3.3" @@ -90,7 +119,19 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.39.2, @eslint/js@npm:^9.39.1, @eslint/js@npm:^9.39.2": +"@eslint/js@npm:10.0.1": + version: 10.0.1 + resolution: "@eslint/js@npm:10.0.1" + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + checksum: 5e60b80ec48d303c9273d5c2803ae3fe12fd5335d57d889d4f9df9249910a97e2921118403765bff26a00b734182437f91a0f6f552654cf12ad73bb49995e22e + languageName: node + linkType: hard + +"@eslint/js@npm:9.39.2, @eslint/js@npm:^9.39.2": version: 9.39.2 resolution: "@eslint/js@npm:9.39.2" checksum: 362aa447266fa5717e762b2b252f177345cb0d7b2954113db9773b3a28898f7cbbc807e07f8078995e6da3f62791f7c5fa2c03517b7170a8e76613cf7fd83c92 @@ -104,6 +145,13 @@ __metadata: languageName: node linkType: hard +"@eslint/object-schema@npm:^3.0.5": + version: 3.0.5 + resolution: "@eslint/object-schema@npm:3.0.5" + checksum: 4e9aee969d73a5c12c06dcf9e3a7903d441cdc946b3768099dba1937f2af58bd8ed4b1bcf34bbc54432440cdd00dfab970edd5ce2b4fb1afd2d0e6018c87aa0b + languageName: node + linkType: hard + "@eslint/plugin-kit@npm:^0.4.1": version: 0.4.1 resolution: "@eslint/plugin-kit@npm:0.4.1" @@ -114,6 +162,16 @@ __metadata: languageName: node linkType: hard +"@eslint/plugin-kit@npm:^0.7.0": + version: 0.7.1 + resolution: "@eslint/plugin-kit@npm:0.7.1" + dependencies: + "@eslint/core": ^1.2.1 + levn: ^0.4.1 + checksum: 4d6c0cc823fb5cca2fa5a7a4fdd32340a5e3c755d639addcb0b53fd8edf94e1b1dbf3aa284e504cc04289980e4c6be726e997dee2ec44c27fa35717a48eafacd + languageName: node + linkType: hard + "@humanfs/core@npm:^0.19.1": version: 0.19.1 resolution: "@humanfs/core@npm:0.19.1" @@ -188,17 +246,17 @@ __metadata: languageName: node linkType: hard -"@keycloak/keycloak-admin-client@npm:^26.0.0": - version: 26.5.2 - resolution: "@keycloak/keycloak-admin-client@npm:26.5.2" +"@keycloak/keycloak-admin-client@npm:26.5.6": + version: 26.5.6 + resolution: "@keycloak/keycloak-admin-client@npm:26.5.6" dependencies: camelize-ts: ^3.0.0 url-template: ^3.1.1 - checksum: 3ceedb4347880c87d93dd3644e062602139959f649ee79e6eeb570814c8c0b640ebff69c682855deba6e311a6f9c7486a88b9ff2cf29d80e9f68539771bfd120 + checksum: 1e996590de8f191dbe616622102895b358c4e31274a7770b3bba2039fc2e64ce9a67ca6cbf6e823d39e061891c25c29b4342b199f20cc3e04937509cf3975843 languageName: node linkType: hard -"@kubernetes/client-node@npm:^1.4.0": +"@kubernetes/client-node@npm:1.4.0": version: 1.4.0 resolution: "@kubernetes/client-node@npm:1.4.0" dependencies: @@ -303,35 +361,42 @@ __metadata: languageName: node linkType: hard -"@red-hat-developer-hub/e2e-test-utils@npm:^1.1.22": - version: 1.1.22 - resolution: "@red-hat-developer-hub/e2e-test-utils@npm:1.1.22" +"@red-hat-developer-hub/e2e-test-utils@AndrienkoAleksandr/rhdh-e2e-test-utils#force-deploy": + version: 1.1.31 + resolution: "@red-hat-developer-hub/e2e-test-utils@https://github.com/AndrienkoAleksandr/rhdh-e2e-test-utils.git#commit=629e8080dc45cafb414bd918391a18893fd38a91" dependencies: - "@axe-core/playwright": ^4.11.0 - "@backstage-community/plugin-rbac-common": 1.23.0 - "@eslint/js": ^9.39.1 - "@keycloak/keycloak-admin-client": ^26.0.0 - "@kubernetes/client-node": ^1.4.0 - eslint: ^9.39.1 - eslint-plugin-check-file: ^3.3.1 - eslint-plugin-playwright: ^2.4.0 - fs-extra: ^11.3.2 - js-yaml: ^4.1.1 - lodash.clonedeepwith: ^4.5.0 - lodash.mergewith: ^4.6.2 + "@axe-core/playwright": 4.11.1 + "@backstage-community/plugin-rbac-common": 1.26.0 + "@eslint/js": 10.0.1 + "@keycloak/keycloak-admin-client": 26.5.6 + "@kubernetes/client-node": 1.4.0 + eslint: 10.2.0 + eslint-plugin-check-file: 3.3.1 + eslint-plugin-playwright: 2.10.1 + fs-extra: 11.3.4 + js-yaml: 4.1.1 + lodash.clonedeepwith: 4.5.0 + lodash.mergewith: 4.6.2 otplib: 12.0.1 - prettier: ^3.7.4 - proper-lockfile: ^4.1.2 - typescript: ^5.9.3 - typescript-eslint: ^8.48.1 - zx: ^8.8.5 + prettier: 3.8.1 + proper-lockfile: 4.1.2 + typescript: 6.0.2 + typescript-eslint: 8.58.1 + zx: 8.8.5 peerDependencies: "@playwright/test": ^1.57.0 - checksum: 9f2b83a8a4ee7d1a0ccbbff2250ff75e229525acc0aa62d7d64c5889d6cffeadf105ae28bc54ae57744c9a5014d4850c24618871a32f4f14b15d30a726c85ec0 + checksum: aa3b22bf71d78ebda9c47864464f2cca30f2d75c97bf5810d7db39a3c64d10a21e5d30db18f8380ecdc8a6c698e1b6ef37fe7bf90b7f02c3ce6f476621fe4b78 + languageName: node + linkType: hard + +"@types/esrecurse@npm:^4.3.1": + version: 4.3.1 + resolution: "@types/esrecurse@npm:4.3.1" + checksum: ada5798554b76ac466e90fff26a769b65f905666f32988dcd1b6cf8288896e0fb53080843fd644bf731d16719a6e09b155d623ce36545b75abdd99bb6dcec114 languageName: node linkType: hard -"@types/estree@npm:^1.0.6": +"@types/estree@npm:^1.0.6, @types/estree@npm:^1.0.8": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" checksum: bd93e2e415b6f182ec4da1074e1f36c480f1d26add3e696d54fb30c09bc470897e41361c8fd957bf0985024f8fbf1e6e2aff977d79352ef7eb93a5c6dcff6c11 @@ -409,6 +474,26 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/eslint-plugin@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.58.1" + dependencies: + "@eslint-community/regexpp": ^4.12.2 + "@typescript-eslint/scope-manager": 8.58.1 + "@typescript-eslint/type-utils": 8.58.1 + "@typescript-eslint/utils": 8.58.1 + "@typescript-eslint/visitor-keys": 8.58.1 + ignore: ^7.0.5 + natural-compare: ^1.4.0 + ts-api-utils: ^2.5.0 + peerDependencies: + "@typescript-eslint/parser": ^8.58.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: d1e858f9a8c07e1c61000d2af9ec8b5e3d7c252ff9239447cdf4a9665b8df4fd47a8f2af0d27fcd99088ec5e35f58cc240a9ac7f4de4f94b835ee1915b1ea1b3 + languageName: node + linkType: hard + "@typescript-eslint/parser@npm:8.54.0": version: 8.54.0 resolution: "@typescript-eslint/parser@npm:8.54.0" @@ -425,6 +510,22 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/parser@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/parser@npm:8.58.1" + dependencies: + "@typescript-eslint/scope-manager": 8.58.1 + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/typescript-estree": 8.58.1 + "@typescript-eslint/visitor-keys": 8.58.1 + debug: ^4.4.3 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 820c13575f5479c1021f0bb7a51d1f42cc1602f2f2606d7f5ddd7f5f54f8814d364c8aa96fe477f2c6eb49f4829ba603840da09885837f70140957d409f8a203 + languageName: node + linkType: hard + "@typescript-eslint/project-service@npm:8.54.0": version: 8.54.0 resolution: "@typescript-eslint/project-service@npm:8.54.0" @@ -438,6 +539,19 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/project-service@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/project-service@npm:8.58.1" + dependencies: + "@typescript-eslint/tsconfig-utils": ^8.58.1 + "@typescript-eslint/types": ^8.58.1 + debug: ^4.4.3 + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: c83fbf0e4c948f39e063b822133b637e43d08445b718782f84dee9e58b11261421ee9fa05a2d879715270ccde1de9eda39be4caaa691334b537e928d155b3260 + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:8.54.0": version: 8.54.0 resolution: "@typescript-eslint/scope-manager@npm:8.54.0" @@ -448,6 +562,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/scope-manager@npm:8.58.1" + dependencies: + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/visitor-keys": 8.58.1 + checksum: f5815899048063b949b97b7c3f756e531a9d67496f21504c7a590ee97aee857a88cef191c91add443cbfa68d081441a08ea650deca6386ed6e1b97654c96a87a + languageName: node + linkType: hard + "@typescript-eslint/tsconfig-utils@npm:8.54.0, @typescript-eslint/tsconfig-utils@npm:^8.54.0": version: 8.54.0 resolution: "@typescript-eslint/tsconfig-utils@npm:8.54.0" @@ -457,6 +581,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/tsconfig-utils@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.58.1" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: e2a6b78ab07e4ac8c5afeefc8dc3658d9529fb57e8913360e1e0b250fefefabc8593954e50ae57a0b53d8d0f0ad8d3d6eeda588e6867e2ef3fb09f8f34979309 + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:^8.58.1": + version: 8.58.2 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.58.2" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 4b01bd4f40830204b6e8ecf576a4038dfcf30db528c60a6e8a683e693ab971110d4669009b43311be49d81020f095c15d84fcf7994d75979018994e72bcca695 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:8.54.0": version: 8.54.0 resolution: "@typescript-eslint/type-utils@npm:8.54.0" @@ -473,6 +615,22 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/type-utils@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/type-utils@npm:8.58.1" + dependencies: + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/typescript-estree": 8.58.1 + "@typescript-eslint/utils": 8.58.1 + debug: ^4.4.3 + ts-api-utils: ^2.5.0 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: ab0ac8adf6e4edc777f1a2bbc4c8e5242d0cf3699ce70549a355d5b09ff024dd1669578b8696b963e1c103d4b9939522d34cb1a53fc36a1e1e449090230fcf3a + languageName: node + linkType: hard + "@typescript-eslint/types@npm:8.54.0, @typescript-eslint/types@npm:^8.54.0": version: 8.54.0 resolution: "@typescript-eslint/types@npm:8.54.0" @@ -480,6 +638,20 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/types@npm:8.58.1" + checksum: 96abd6c72b82885fb83cf2ef1921802b92b82efa228323f2f65acb4c926bc04878d7bf816396d25aaf81159251b9e77344c09ecc9790116d546f206c9d9a8ac6 + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:^8.58.1": + version: 8.58.2 + resolution: "@typescript-eslint/types@npm:8.58.2" + checksum: f703142b5f3568995076e6755c56020c211e2d674c3d83d9ea6e6151fe9b5cb7eb99cc1e0f81a9c94bf698431da1365c241a0507b60fd53d1a5ebd896dff3b8e + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:8.54.0": version: 8.54.0 resolution: "@typescript-eslint/typescript-estree@npm:8.54.0" @@ -499,6 +671,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.58.1" + dependencies: + "@typescript-eslint/project-service": 8.58.1 + "@typescript-eslint/tsconfig-utils": 8.58.1 + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/visitor-keys": 8.58.1 + debug: ^4.4.3 + minimatch: ^10.2.2 + semver: ^7.7.3 + tinyglobby: ^0.2.15 + ts-api-utils: ^2.5.0 + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 6fcaa5995d641b745d581a08f2305a931bfdc293120ced2b604784b2009ab394eda1ed9463d32a5fffcdc66435fbb012588bfe62a18a5e7af0ae62741a86d563 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:8.54.0": version: 8.54.0 resolution: "@typescript-eslint/utils@npm:8.54.0" @@ -514,6 +705,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/utils@npm:8.58.1" + dependencies: + "@eslint-community/eslint-utils": ^4.9.1 + "@typescript-eslint/scope-manager": 8.58.1 + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/typescript-estree": 8.58.1 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: baf40d0dc1c0d36d8d46e4e1a664ea3ebd84d37abd33cd858bd7d8271dd0ba2078b3f3097ec9059c575025e8871889d6796155b1b077234ba644bd598199031b + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:8.54.0": version: 8.54.0 resolution: "@typescript-eslint/visitor-keys@npm:8.54.0" @@ -524,6 +730,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.58.1": + version: 8.58.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.58.1" + dependencies: + "@typescript-eslint/types": 8.58.1 + eslint-visitor-keys: ^5.0.0 + checksum: b028a9fb3f14aa96d350e819ab63b6ca98d5cbb7f40eb4901b13ae7f63c3bf42c24eb52506c27098b94940ea5e515678b83c030cff35bca22b476b76785b0d0a + languageName: node + linkType: hard + "abbrev@npm:^4.0.0": version: 4.0.0 resolution: "abbrev@npm:4.0.0" @@ -549,6 +765,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.16.0": + version: 8.16.0 + resolution: "acorn@npm:8.16.0" + bin: + acorn: bin/acorn + checksum: bbfa466cd0dbd18b4460a85e9d0fc2f35db999380892403c573261beda91f23836db2aa71fd3ae65e94424ad14ff8e2b7bd37c7a2624278fd89137cd6e448c41 + languageName: node + linkType: hard + "agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": version: 7.1.4 resolution: "agent-base@npm:7.1.4" @@ -568,6 +793,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^6.14.0": + version: 6.14.0 + resolution: "ajv@npm:6.14.0" + dependencies: + fast-deep-equal: ^3.1.1 + fast-json-stable-stringify: ^2.0.0 + json-schema-traverse: ^0.4.1 + uri-js: ^4.2.2 + checksum: 7bb3ea97bb8af52521589079f427e799b6561acaa94f50e13410cb87588c51df8db1afe1157b3e48f1a829269adaa11116e0c2cafe2b998add1523789809a3c5 + languageName: node + linkType: hard + "ansi-styles@npm:^4.1.0": version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" @@ -605,10 +842,10 @@ __metadata: languageName: node linkType: hard -"axe-core@npm:~4.11.0": - version: 4.11.1 - resolution: "axe-core@npm:4.11.1" - checksum: 92b3c79af3695bcebac0e7f3f90f4bc11d2b39ccdc670937290e8dacbc943473713cc06b771dea0563c66d57d93d940ed89e082bfdecccf9dd70782d4bb243c0 +"axe-core@npm:~4.11.1": + version: 4.11.3 + resolution: "axe-core@npm:4.11.3" + checksum: b01fc82cd0e809db09166a90bbb8942a978eefab5ba1ebab8faebd94969e0bd36df9848e4e510520cd08cde9800a6d14888ebbca380dfc4594db48e9b494d399 languageName: node linkType: hard @@ -631,6 +868,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: fb07bb66a0959c2843fc055838047e2a95ccebb837c519614afb067ebfdf2fa967ca8d712c35ced07f2cd26fc6f07964230b094891315ad74f11eba3d53178a0 + languageName: node + linkType: hard + "bare-events@npm:^2.5.4, bare-events@npm:^2.7.0": version: 2.8.2 resolution: "bare-events@npm:2.8.2" @@ -722,6 +966,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.5": + version: 5.0.5 + resolution: "brace-expansion@npm:5.0.5" + dependencies: + balanced-match: ^4.0.2 + checksum: 4481b7ffa467b34c14e258167dbd8d9485a2d31d03060e8e8b38142dcde32cdc89c8f55b04d3ae7aae9304fa7eac1dfafd602787cf09c019cc45de3bb6950ffc + languageName: node + linkType: hard + "braces@npm:^3.0.3": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -737,7 +990,7 @@ __metadata: dependencies: "@eslint/js": ^9.39.2 "@playwright/test": ^1.56.1 - "@red-hat-developer-hub/e2e-test-utils": ^1.1.22 + "@red-hat-developer-hub/e2e-test-utils": "AndrienkoAleksandr/rhdh-e2e-test-utils#force-deploy" "@types/node": ^24.10.1 dotenv: ^16.4.7 eslint: ^9.39.2 @@ -970,7 +1223,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-check-file@npm:^3.3.1": +"eslint-plugin-check-file@npm:3.3.1, eslint-plugin-check-file@npm:^3.3.1": version: 3.3.1 resolution: "eslint-plugin-check-file@npm:3.3.1" dependencies: @@ -982,6 +1235,17 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-playwright@npm:2.10.1": + version: 2.10.1 + resolution: "eslint-plugin-playwright@npm:2.10.1" + dependencies: + globals: ^17.3.0 + peerDependencies: + eslint: ">=8.40.0" + checksum: f738f0cd60e0b3a783b2b022b2b90d5e29a9138671e439a7bbe62a647f8a524f4b040f83f2d606d5eeafb33f602d25ac0c6caf8a017f60c2f70d11040b2902c5 + languageName: node + linkType: hard + "eslint-plugin-playwright@npm:^2.4.0": version: 2.5.1 resolution: "eslint-plugin-playwright@npm:2.5.1" @@ -1003,6 +1267,18 @@ __metadata: languageName: node linkType: hard +"eslint-scope@npm:^9.1.2": + version: 9.1.2 + resolution: "eslint-scope@npm:9.1.2" + dependencies: + "@types/esrecurse": ^4.3.1 + "@types/estree": ^1.0.8 + esrecurse: ^4.3.0 + estraverse: ^5.2.0 + checksum: ea1a4333f5912e1ec83328ecf8103b0bb9628beca10d5efc17ce63a825ed3ab0b68c036c2dbd3127cf71f51cc04fb4685a27aac082d55c2faf134391d06443af + languageName: node + linkType: hard + "eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" @@ -1017,7 +1293,59 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^9.39.1, eslint@npm:^9.39.2": +"eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1": + version: 5.0.1 + resolution: "eslint-visitor-keys@npm:5.0.1" + checksum: d6cc6830536ab4a808f25325686c2c27862f27aab0c1ffed39627293b06cee05d95187da113cafd366314ea5be803b456115de71ad625e365020f20e2a6af89b + languageName: node + linkType: hard + +"eslint@npm:10.2.0": + version: 10.2.0 + resolution: "eslint@npm:10.2.0" + dependencies: + "@eslint-community/eslint-utils": ^4.8.0 + "@eslint-community/regexpp": ^4.12.2 + "@eslint/config-array": ^0.23.4 + "@eslint/config-helpers": ^0.5.4 + "@eslint/core": ^1.2.0 + "@eslint/plugin-kit": ^0.7.0 + "@humanfs/node": ^0.16.6 + "@humanwhocodes/module-importer": ^1.0.1 + "@humanwhocodes/retry": ^0.4.2 + "@types/estree": ^1.0.6 + ajv: ^6.14.0 + cross-spawn: ^7.0.6 + debug: ^4.3.2 + escape-string-regexp: ^4.0.0 + eslint-scope: ^9.1.2 + eslint-visitor-keys: ^5.0.1 + espree: ^11.2.0 + esquery: ^1.7.0 + esutils: ^2.0.2 + fast-deep-equal: ^3.1.3 + file-entry-cache: ^8.0.0 + find-up: ^5.0.0 + glob-parent: ^6.0.2 + ignore: ^5.2.0 + imurmurhash: ^0.1.4 + is-glob: ^4.0.0 + json-stable-stringify-without-jsonify: ^1.0.1 + minimatch: ^10.2.4 + natural-compare: ^1.4.0 + optionator: ^0.9.3 + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + bin: + eslint: bin/eslint.js + checksum: b63e89221bd0c05e91378420b501a2de8b192d0fcd307453e72eca5ebf9d8e933c42187e636096d8971ffde61ac7eab4736f441f9c569c2d22d9d5a275077532 + languageName: node + linkType: hard + +"eslint@npm:^9.39.2": version: 9.39.2 resolution: "eslint@npm:9.39.2" dependencies: @@ -1077,7 +1405,18 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.5.0": +"espree@npm:^11.2.0": + version: 11.2.0 + resolution: "espree@npm:11.2.0" + dependencies: + acorn: ^8.16.0 + acorn-jsx: ^5.3.2 + eslint-visitor-keys: ^5.0.1 + checksum: 7545dc501ab5cff558af1aa290c7e586d7d2a83c9ecdcb5f2c8ba7ee6634b70f4083d1bed198ec17ddf11d3265751aa78e315b4d4c7506711066a4ef38c1084a + languageName: node + linkType: hard + +"esquery@npm:^1.5.0, esquery@npm:^1.7.0": version: 1.7.0 resolution: "esquery@npm:1.7.0" dependencies: @@ -1223,14 +1562,14 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.3.2": - version: 11.3.3 - resolution: "fs-extra@npm:11.3.3" +"fs-extra@npm:11.3.4": + version: 11.3.4 + resolution: "fs-extra@npm:11.3.4" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: fb2acabbd1e04bcaca90eadfe98e6ffba1523b8009afbb9f4c0aae5efbca0bd0bf6c9a6831df5af5aaacb98d3e499898be848fb0c03d31ae7b9d1b053e81c151 + checksum: 3d1453db564b20ad58adf7c9583d0821546bd05e158865407fa2767af774ad8353418118655005f48c5bd0dbda19ea4bd053f8a5dcef2ce2e97580f7a039a221 languageName: node linkType: hard @@ -1341,6 +1680,13 @@ __metadata: languageName: node linkType: hard +"globals@npm:^17.3.0": + version: 17.5.0 + resolution: "globals@npm:17.5.0" + checksum: 934130da24e193cb7c6f3011f68818be869d30250b3ec2076dae712e0ce7e95c9d5c943a780e92fe99ecc86ed8be8b014e497354741782b78c21613c85e8a2bd + languageName: node + linkType: hard + "gopd@npm:^1.2.0": version: 1.2.0 resolution: "gopd@npm:1.2.0" @@ -1521,7 +1867,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1": +"js-yaml@npm:4.1.1, js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1": version: 4.1.1 resolution: "js-yaml@npm:4.1.1" dependencies: @@ -1615,7 +1961,7 @@ __metadata: languageName: node linkType: hard -"lodash.clonedeepwith@npm:^4.5.0": +"lodash.clonedeepwith@npm:4.5.0": version: 4.5.0 resolution: "lodash.clonedeepwith@npm:4.5.0" checksum: 9fbf4ebfa04b381df226a2298eba680327bea3d0d5d19c5118de7ae218fd219186e30e9fd0d33b13729f34ffbc83c1cf09cb27aff265ba94cb602b8a2b1e71c9 @@ -1629,7 +1975,7 @@ __metadata: languageName: node linkType: hard -"lodash.mergewith@npm:^4.6.2": +"lodash.mergewith@npm:4.6.2": version: 4.6.2 resolution: "lodash.mergewith@npm:4.6.2" checksum: a6db2a9339752411f21b956908c404ec1e088e783a65c8b29e30ae5b3b6384f82517662d6f425cc97c2070b546cc2c7daaa8d33f78db7b6e9be06cd834abdeb8 @@ -1704,6 +2050,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.2.2, minimatch@npm:^10.2.4": + version: 10.2.5 + resolution: "minimatch@npm:10.2.5" + dependencies: + brace-expansion: ^5.0.5 + checksum: 000423875fecbc7da1d74bf63c9081363a71291ef2588c376c45647ac004582cb5bc8cc09ef84420b26bfb490f4d0818d328e78569c6228e20d90271283f73ba + languageName: node + linkType: hard + "minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -2018,7 +2373,7 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^3.7.4": +"prettier@npm:3.8.1, prettier@npm:^3.7.4": version: 3.8.1 resolution: "prettier@npm:3.8.1" bin: @@ -2044,7 +2399,7 @@ __metadata: languageName: node linkType: hard -"proper-lockfile@npm:^4.1.2": +"proper-lockfile@npm:4.1.2": version: 4.1.2 resolution: "proper-lockfile@npm:4.1.2" dependencies: @@ -2295,6 +2650,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^2.5.0": + version: 2.5.0 + resolution: "ts-api-utils@npm:2.5.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 5b2a2db7aa041d60b040df691ee5e73d534fb4cb3cf4fd6d2c27c584a32836a7ca8272fb23d865e673559ea639fdba35f8623249bf931df22188f0aaef7f0075 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -2304,7 +2668,22 @@ __metadata: languageName: node linkType: hard -"typescript-eslint@npm:^8.48.1, typescript-eslint@npm:^8.50.0": +"typescript-eslint@npm:8.58.1": + version: 8.58.1 + resolution: "typescript-eslint@npm:8.58.1" + dependencies: + "@typescript-eslint/eslint-plugin": 8.58.1 + "@typescript-eslint/parser": 8.58.1 + "@typescript-eslint/typescript-estree": 8.58.1 + "@typescript-eslint/utils": 8.58.1 + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: fba919623d3ea7a4c10289a653f4387542ae4a0b2a15823bb4cf2ca5ce7b4ff0fcbc5868b11129bbd0d7117c4c1a70988e9fdc36ff4e166955e13f404c5fcf45 + languageName: node + linkType: hard + +"typescript-eslint@npm:^8.50.0": version: 8.54.0 resolution: "typescript-eslint@npm:8.54.0" dependencies: @@ -2319,6 +2698,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:6.0.2": + version: 6.0.2 + resolution: "typescript@npm:6.0.2" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: a95632e5e4f7b9e7d9e9e9b79b4f147b45181258e7cae4c9f9f2a24176aff688d25e798cddf78cc39cb95423c8c63b36fd4ed5ac4028d37667dcc62a1e89e9e7 + languageName: node + linkType: hard + "typescript@npm:^5.9.3": version: 5.9.3 resolution: "typescript@npm:5.9.3" @@ -2329,6 +2718,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@6.0.2#~builtin": + version: 6.0.2 + resolution: "typescript@patch:typescript@npm%3A6.0.2#~builtin::version=6.0.2&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 9f29a9339aa66f5e13485c596c8d4d086fb95960f8be8a23ea6ef33afe61f7c465d330a6f77911bb8613f231c2ebf131763a82d277cefa61147b24af2073f0c4 + languageName: node + linkType: hard + "typescript@patch:typescript@^5.9.3#~builtin": version: 5.9.3 resolution: "typescript@patch:typescript@npm%3A5.9.3#~builtin::version=5.9.3&hash=5786d5" @@ -2476,7 +2875,7 @@ __metadata: languageName: node linkType: hard -"zx@npm:^8.8.5": +"zx@npm:8.8.5": version: 8.8.5 resolution: "zx@npm:8.8.5" bin: From b88389b2961c7364f57eeb726f8ff4548ed744a7 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Sun, 19 Apr 2026 15:22:58 +0300 Subject: [PATCH 22/26] feat(e2e): fix org links Signed-off-by: Oleksandr Andriienko --- .../e2e-tests/tests/specs/bulk-import.spec.ts | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 125814b9f..65964c81f 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -80,6 +80,8 @@ async function catalogImportRegisterFromComponentUrl(page: Page, url: string) { await waitForLoad(page); } +export const GITHUB_ORG = 'janus-qe'; + test.describe.serial("Bulk Import plugin", () => { test.skip( () => !!process.env.JOB_NAME?.includes("osd-gcp"), @@ -87,12 +89,12 @@ test.describe.serial("Bulk Import plugin", () => { ); test.describe.configure({ retries: process.env.CI ? 5 : 0 }); - const catalogRepoName = `cloud-eda-1-bulk-import-test-${Date.now()}`; + const catalogRepoName = `${GITHUB_ORG}-1-bulk-import-test-${Date.now()}`; const catalogRepoDetails = { name: catalogRepoName, - url: `github.com/cloud-eda/${catalogRepoName}`, - org: "github.com/cloud-eda", - owner: "cloud-eda", + url: `github.com/${GITHUB_ORG}/${catalogRepoName}`, + org: `github.com/${GITHUB_ORG}`, + owner: GITHUB_ORG, }; const catalogInfoYamlContent = `apiVersion: backstage.io/v1alpha1 @@ -100,7 +102,7 @@ kind: Component metadata: name: ${catalogRepoName} annotations: - github.com/project-slug: cloud-eda/${catalogRepoName} + github.com/project-slug: ${GITHUB_ORG}/${catalogRepoName} spec: type: other lifecycle: unknown @@ -108,11 +110,11 @@ spec: const newRepoName = `bulk-import-${Date.now()}`; const newRepoDetails = { - owner: "cloud-eda", + owner: `${GITHUB_ORG}`, repoName: newRepoName, updatedComponentName: `${newRepoName}-updated`, labels: `bulkimport1: test1;bulkimport2: test2`, - repoUrl: `github.com/cloud-eda/${newRepoName}`, + repoUrl: `github.com/${GITHUB_ORG}/${newRepoName}`, }; test.beforeAll(async ({ rhdh }) => { @@ -394,7 +396,7 @@ test.describe const existingComponentDetails = { name: "janus-test-2-bulk-import-test", repoName: "janus-test-2-bulk-import-test", - url: "https://github.com/cloud-eda/janus-test-2-bulk-import-test/blob/main/catalog-info.yaml", + url: `https://github.com/${GITHUB_ORG}/janus-test-2-bulk-import-test/blob/main/catalog-info.yaml`, }; test.beforeAll(async ({ rhdh }) => { @@ -503,13 +505,12 @@ test.describe // ============================================================================ test.describe("Bulk import tests orchestrator mode", () => { - const catalogRepoName = `cloud-eda-1-bulk-import-test-${Date.now()}`; - const githubOrg = "cloud-eda"; + const catalogRepoName = `${GITHUB_ORG}-1-bulk-import-test-${Date.now()}`; const catalogRepoDetailsForOrchestrator = { name: catalogRepoName, - url: `github.com/${githubOrg}/${catalogRepoName}`, - org: `github.com/${githubOrg}`, - owner: githubOrg, + url: `github.com/${GITHUB_ORG}/${catalogRepoName}`, + org: `github.com/${GITHUB_ORG}`, + owner: GITHUB_ORG, }; test.beforeAll(async ({ rhdh }) => { From 9d60c5499ea8a218fca066b574981e3f01ccfedc Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Sun, 19 Apr 2026 15:44:28 +0300 Subject: [PATCH 23/26] feat(e2e): fix lint Signed-off-by: Oleksandr Andriienko --- .../bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 65964c81f..6a094aeef 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -80,7 +80,7 @@ async function catalogImportRegisterFromComponentUrl(page: Page, url: string) { await waitForLoad(page); } -export const GITHUB_ORG = 'janus-qe'; +export const GITHUB_ORG = "janus-qe"; test.describe.serial("Bulk Import plugin", () => { test.skip( From 93807005a5835764752051f5bb5d375dd9955d75 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Sun, 19 Apr 2026 19:27:35 +0300 Subject: [PATCH 24/26] feat(e2e): fix failing e2e test Signed-off-by: Oleksandr Andriienko --- .../bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml | 2 +- .../bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml index 6e3d025dd..ac091d111 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/app-config-rhdh.yaml @@ -9,7 +9,7 @@ catalog: - allow: [Component, System, Group, User, Resource, Location, Template, API] locations: - type: url - target: https://github.com/cloud-eda/janus-test-3-bulk-import/blob/main/catalog-info.yaml + target: https://github.com/janus-test/janus-test-3-bulk-import/blob/main/catalog-info.yaml integrations: github: diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 6a094aeef..4ace6143c 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -418,10 +418,7 @@ test.describe }); test.beforeEach(async ({ loginHelper }) => { - await loginHelper.loginAsKeycloakUser( - process.env.GH_USER2_ID, - process.env.GH_USER2_PASS, - ); + await loginHelper.loginAsKeycloakUser(); }); test("Verify existing repo from app-config is displayed in bulk import Added repositories", async ({ From 1a69279c711add4390037887f75b054f233d0db0 Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2026 00:38:47 +0300 Subject: [PATCH 25/26] feat(e2e): fix failing test Signed-off-by: Oleksandr Andriienko --- .../bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts index 4ace6143c..fa7857d44 100644 --- a/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts +++ b/workspaces/bulk-import/e2e-tests/tests/specs/bulk-import.spec.ts @@ -396,7 +396,7 @@ test.describe const existingComponentDetails = { name: "janus-test-2-bulk-import-test", repoName: "janus-test-2-bulk-import-test", - url: `https://github.com/${GITHUB_ORG}/janus-test-2-bulk-import-test/blob/main/catalog-info.yaml`, + url: `https://github.com/janus-test/janus-test-2-bulk-import-test/blob/main/catalog-info.yaml`, }; test.beforeAll(async ({ rhdh }) => { From f2b5f55eef5bd537b31caaae7985251d9e91bd0e Mon Sep 17 00:00:00 2001 From: Oleksandr Andriienko Date: Mon, 20 Apr 2026 01:36:46 +0300 Subject: [PATCH 26/26] feat(e2e): remove hard coded image Signed-off-by: Oleksandr Andriienko --- workspaces/bulk-import/e2e-tests/tests/config/values.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/workspaces/bulk-import/e2e-tests/tests/config/values.yaml b/workspaces/bulk-import/e2e-tests/tests/config/values.yaml index e2577d087..c9f50a990 100644 --- a/workspaces/bulk-import/e2e-tests/tests/config/values.yaml +++ b/workspaces/bulk-import/e2e-tests/tests/config/values.yaml @@ -1,8 +1,4 @@ upstream: - image: - registry: quay.io - repository: rhdh-community/rhdh - tag: next backstage: extraVolumeMounts: # NOTE: Lists will not be merged with the default values file. So need to append all the defaults if you want to add a new item here