diff --git a/Jenkinsfile b/Jenkinsfile index 306c282..6677827 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -433,6 +433,104 @@ printf '%s\n' "${frontend_rc}" > ../build/frontend-tests.rc } } + stage('Refresh SonarQube evidence') { + when { + expression { fileExists('build/backend-coverage.xml') || fileExists('frontend/coverage/lcov.info') } + } + steps { + container('quality-tools') { + sh '''#!/usr/bin/env bash + set -euo pipefail + args=( + "-Dsonar.host.url=${SONARQUBE_HOST_URL}" + "-Dsonar.login=${SONARQUBE_TOKEN}" + "-Dsonar.projectKey=${SONARQUBE_PROJECT_KEY}" + "-Dsonar.projectName=${SONARQUBE_PROJECT_KEY}" + "-Dsonar.sources=." + "-Dsonar.exclusions=**/.git/**,**/build/**,**/dist/**,**/node_modules/**,**/.venv/**,**/__pycache__/**,**/coverage/**,**/test-results/**,**/playwright-report/**,frontend/public/media/**" + "-Dsonar.test.inclusions=**/tests/**,**/testing/**,**/*_test.go,**/*.test.ts,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx" + ) + [ -f build/backend-coverage.xml ] && args+=("-Dsonar.python.coverage.reportPaths=build/backend-coverage.xml") + [ -f frontend/coverage/lcov.info ] && args+=("-Dsonar.javascript.lcov.reportPaths=frontend/coverage/lcov.info") + set +e + sonar-scanner "${args[@]}" | tee build/sonar-scanner.log + rc=${PIPESTATUS[0]} + set -e + printf '%s\n' "${rc}" > build/sonarqube-analysis.rc + ''' + } + container('tester') { + sh ''' + set -euo pipefail + python3 - <<'PY' +import base64 +import json +import os +import time +import urllib.parse +import urllib.request + +host = os.getenv('SONARQUBE_HOST_URL', '').strip().rstrip('/') +project_key = os.getenv('SONARQUBE_PROJECT_KEY', '').strip() +token = os.getenv('SONARQUBE_TOKEN', '').strip() +report_path = os.getenv('QUALITY_GATE_SONARQUBE_REPORT', 'build/sonarqube-quality-gate.json') +payload = {"status": "ERROR", "note": "missing SONARQUBE_HOST_URL and/or SONARQUBE_PROJECT_KEY"} +analysis_id = "" + + +def request_json(url: str) -> dict: + request = urllib.request.Request(url, method="GET") + if token: + encoded = base64.b64encode(f"{token}:".encode("utf-8")).decode("utf-8") + request.add_header("Authorization", f"Basic {encoded}") + with urllib.request.urlopen(request, timeout=12) as response: + return json.loads(response.read().decode("utf-8")) + + +if host and project_key: + task_path = next( + ( + candidate + for candidate in ( + os.path.join('.scannerwork', 'report-task.txt'), + os.path.join('build', '.scannerwork', 'report-task.txt'), + ) + if os.path.exists(candidate) + ), + "", + ) + if os.path.exists(task_path): + ce_task_id = "" + with open(task_path, encoding="utf-8") as handle: + for line in handle: + if line.startswith("ceTaskId="): + ce_task_id = line.strip().split("=", 1)[1] + break + for _ in range(30): + if not ce_task_id: + break + query = urllib.parse.urlencode({"id": ce_task_id}) + task = request_json(f"{host}/api/ce/task?{query}").get("task", {}) + if task.get("status") == "SUCCESS": + analysis_id = str(task.get("analysisId") or "") + break + if task.get("status") in {"FAILED", "CANCELED"}: + break + time.sleep(2) + query = urllib.parse.urlencode({"analysisId": analysis_id} if analysis_id else {"projectKey": project_key}) + try: + payload = request_json(f"{host}/api/qualitygates/project_status?{query}") + except Exception as exc: # noqa: BLE001 + payload = {"status": "ERROR", "error": str(exc)} +with open(report_path, "w", encoding="utf-8") as handle: + json.dump(payload, handle, indent=2, sort_keys=True) + handle.write("\\n") +PY + ''' + } + } + } + stage('Publish test metrics') { steps { container('tester') { diff --git a/testing/frontend/e2e/home.spec.js b/testing/frontend/e2e/home.spec.js index bb1885e..2074700 100644 --- a/testing/frontend/e2e/home.spec.js +++ b/testing/frontend/e2e/home.spec.js @@ -57,7 +57,7 @@ test("shows the overview and opens the diagram overlay", async ({ page }) => { const firstCard = page.locator(".mermaid-card").first(); await expect(firstCard).toBeVisible(); - await firstCard.getByRole("button", { name: "Full screen" }).click(); + await firstCard.locator(".diagram").click(); await expect(page.locator(".overlay")).toBeVisible(); await page.keyboard.press("Escape"); await expect(page.locator(".overlay")).toHaveCount(0);