From f375493bc9c94a9ebd45b39676c911ca2ddd8cc8 Mon Sep 17 00:00:00 2001 From: Dmitry Ermakovich Date: Tue, 19 May 2026 15:13:50 +0300 Subject: [PATCH] fix: selenide pipeline issue --- .github/workflows/jreleaser-common.yml | 6 +-- .github/workflows/local-test.yml | 4 +- .github/workflows/test.yml | 44 +++++++++++++++---- testit-adapter-cucumber7/Readme.md | 2 +- .../resources/features/parameterized.feature | 2 +- .../java/ru/testit/clients/Converter.java | 7 ++- .../main/java/ru/testit/services/Utils.java | 27 ++++++++++++ .../NormalizeWebDriverMessageTest.java | 37 ++++++++++++++++ 8 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 testit-java-commons/src/test/java/ru/testit/services/NormalizeWebDriverMessageTest.java diff --git a/.github/workflows/jreleaser-common.yml b/.github/workflows/jreleaser-common.yml index 9b49d2b7..18b2825b 100644 --- a/.github/workflows/jreleaser-common.yml +++ b/.github/workflows/jreleaser-common.yml @@ -34,18 +34,18 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 # Full history for JReleaser - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' - name: Cache Gradle dependencies - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | ~/.gradle/caches diff --git a/.github/workflows/local-test.yml b/.github/workflows/local-test.yml index 6ce7a532..589f4345 100644 --- a/.github/workflows/local-test.yml +++ b/.github/workflows/local-test.yml @@ -19,10 +19,10 @@ jobs: steps: - name: Checkout adapters-java - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: ${{ env.JAVA_DISTRIBUTION }} java-package: jdk diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2e4221b7..dc301a0d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -121,6 +121,7 @@ jobs: - project_name: selenide-gradle-junit5 configuration_id: SELENIDE_GRADLE_JUNIT5_CONFIGURATION_ID project_id: SELENIDE_GRADLE_JUNIT5_PROJECT_ID + needs_chrome: true - project_name: serenity2-gradle-junit4 configuration_id: SERENITY2_GRADLE_JUNIT4_CONFIGURATION_ID project_id: SERENITY2_GRADLE_JUNIT4_PROJECT_ID @@ -133,32 +134,45 @@ jobs: TMS_TEST_RUN_NAME: ${{ matrix.project_name }} TestRun steps: - name: Checkout adapters-java - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Checkout api-validator-dotnet - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: testit-tms/api-validator-dotnet token: ${{ env.GITHUB_PAT }} path: api-validator-dotnet - name: Checkout java-examples - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: testit-tms/java-examples path: java-examples - name: Setup dotnet - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Setup java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: ${{ env.JAVA_DISTRIBUTION }} java-package: jdk java-version: ${{ env.JAVA_VERSION }} - name: Setup python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} + - name: Setup Chrome + if: matrix.needs_chrome == true + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable + - name: Configure Selenide for CI + if: matrix.needs_chrome == true + run: | + CHROME_BASE="/tmp/chrome-${{ github.run_id }}-${{ matrix.project_name }}" + CHROME_ARGS="--no-sandbox,--disable-dev-shm-usage,--disable-gpu,--remote-allow-origins=*" + echo "SELENIDE_CHROME_BASE=${CHROME_BASE}" >> $GITHUB_ENV + echo "SELENIDE_OPTS=-Dselenide.headless=true -Dchromeoptions.args=${CHROME_ARGS},--user-data-dir=${CHROME_BASE}-run1" >> $GITHUB_ENV + echo "SELENIDE_OPTS_MODE2=-Dselenide.headless=true -Dchromeoptions.args=${CHROME_ARGS},--user-data-dir=${CHROME_BASE}-run2" >> $GITHUB_ENV - name: Setup environment run: | ./gradlew --no-daemon -DdisableSign=true publishToMavenLocal @@ -188,8 +202,15 @@ jobs: curl -v http://127.0.0.1:49152/health || true + if [ -n "${SELENIDE_CHROME_BASE:-}" ]; then + pkill -f chromedriver || true + pkill -f "chrome.*--user-data-dir" || true + rm -rf "${SELENIDE_CHROME_BASE}"-* || true + sleep 1 + fi + chmod +x ./gradlew - ./gradlew test --no-daemon -DtmsUrl=${{ env.TMS_URL }} -DtmsPrivateToken=${{ env.TMS_PRIVATE_TOKEN }} \ + ./gradlew test --no-daemon ${SELENIDE_OPTS:-} -DtmsUrl=${{ env.TMS_URL }} -DtmsPrivateToken=${{ env.TMS_PRIVATE_TOKEN }} \ -DtmsProjectId=${{ env.TMS_PROJECT_ID }} -DtmsConfigurationId=${{ env.TMS_CONFIGURATION_ID }} \ -DtmsAdapterMode=${{ env.TMS_ADAPTER_MODE }} -DtmsTestRunId=${{ env.TMS_TEST_RUN_ID }} \ -DtmsCertValidation=${{ env.TMS_CERT_VALIDATION }} || true # ignore error code @@ -208,9 +229,16 @@ jobs: run: | export TMS_ADAPTER_MODE=2 cd java-examples/${{ matrix.project_name }} + + if [ -n "${SELENIDE_CHROME_BASE:-}" ]; then + pkill -f chromedriver || true + pkill -f "chrome.*--user-data-dir" || true + rm -rf "${SELENIDE_CHROME_BASE}"-* || true + sleep 2 + fi chmod +x ./gradlew - ./gradlew test --no-daemon -DtmsUrl=${{ env.TMS_URL }} -DtmsPrivateToken=${{ env.TMS_PRIVATE_TOKEN }} \ + ./gradlew test --no-daemon ${SELENIDE_OPTS_MODE2:-${SELENIDE_OPTS:-}} -DtmsUrl=${{ env.TMS_URL }} -DtmsPrivateToken=${{ env.TMS_PRIVATE_TOKEN }} \ -DtmsProjectId=${{ env.TMS_PROJECT_ID }} -DtmsConfigurationId=${{ env.TMS_CONFIGURATION_ID }} \ -DtmsAdapterMode=${{ env.TMS_ADAPTER_MODE }} \ -DtmsCertValidation=${{ env.TMS_CERT_VALIDATION }} || true # ignore error code diff --git a/testit-adapter-cucumber7/Readme.md b/testit-adapter-cucumber7/Readme.md index 4bbd7516..54f7c6a2 100644 --- a/testit-adapter-cucumber7/Readme.md +++ b/testit-adapter-cucumber7/Readme.md @@ -584,7 +584,7 @@ Feature: Rule Tests that use Rule @DisplayName=sum:{left}+{right}={result} - @ExternalId={result} + @ExternalId=Summing_{left}_{right}_{result} Scenario Outline: Summing When Summing + Then Result is diff --git a/testit-adapter-cucumber7/src/test/resources/features/parameterized.feature b/testit-adapter-cucumber7/src/test/resources/features/parameterized.feature index 5db013ce..180bf72e 100644 --- a/testit-adapter-cucumber7/src/test/resources/features/parameterized.feature +++ b/testit-adapter-cucumber7/src/test/resources/features/parameterized.feature @@ -2,7 +2,7 @@ Feature: Rule Tests that use Rule @DisplayName=sum:{left}+{right}={result} - @ExternalId={result} + @ExternalId=Summing_{left}_{right}_{result} Scenario Outline: Summing When Summing + Then Result is diff --git a/testit-java-commons/src/main/java/ru/testit/clients/Converter.java b/testit-java-commons/src/main/java/ru/testit/clients/Converter.java index 67311c63..994fa297 100644 --- a/testit-java-commons/src/main/java/ru/testit/clients/Converter.java +++ b/testit-java-commons/src/main/java/ru/testit/clients/Converter.java @@ -10,6 +10,7 @@ import ru.testit.models.StepResult; import ru.testit.models.Label; import ru.testit.services.HtmlEscapeUtils; +import ru.testit.services.Utils; import java.time.OffsetDateTime; import java.time.ZoneOffset; @@ -122,8 +123,10 @@ public static AutoTestResultsForTestRunModel testResultToAutoTestResultsForTestR Throwable throwable = result.getThrowable(); if (throwable != null) { - model.setMessage(HtmlEscapeUtils.escapeHtmlTags(throwable.getMessage())); - model.setTraces(HtmlEscapeUtils.escapeHtmlTags(ExceptionUtils.getStackTrace(throwable))); + String message = Utils.normalizeWebDriverMessage(throwable.getMessage()); + String traces = Utils.normalizeWebDriverMessage(ExceptionUtils.getStackTrace(throwable)); + model.setMessage(HtmlEscapeUtils.escapeHtmlTags(message)); + model.setTraces(HtmlEscapeUtils.escapeHtmlTags(traces)); } return model; diff --git a/testit-java-commons/src/main/java/ru/testit/services/Utils.java b/testit-java-commons/src/main/java/ru/testit/services/Utils.java index 4a11bb39..cb27230a 100644 --- a/testit-java-commons/src/main/java/ru/testit/services/Utils.java +++ b/testit-java-commons/src/main/java/ru/testit/services/Utils.java @@ -190,4 +190,31 @@ private static String convertToHex(final byte[] messageDigest) { } return hexText.toUpperCase(); } + + private static final Pattern WEB_DRIVER_HOST_INFO = Pattern.compile( + "\\n?Host info: host: '[^']*', ip: '[^']*'" + ); + private static final Pattern WEB_DRIVER_SESSION_NOT_CREATED = Pattern.compile( + "session not created: [^\\n]+", + Pattern.CASE_INSENSITIVE + ); + private static final Pattern WEB_DRIVER_OS_VERSION = Pattern.compile("os\\.version: '[^']*'"); + private static final Pattern WEB_DRIVER_JAVA_VERSION = Pattern.compile("java\\.version: '[^']*'"); + + /** + * Stabilizes Selenium/Selenide session startup errors for CI: host, Java/OS versions + * and Chrome failure reasons vary between runners. + */ + public static String normalizeWebDriverMessage(String message) { + if (message == null || !message.contains("Could not start a new session")) { + return message; + } + + String normalized = WEB_DRIVER_HOST_INFO.matcher(message).replaceAll(""); + normalized = WEB_DRIVER_SESSION_NOT_CREATED.matcher(normalized) + .replaceAll("session not created: "); + normalized = WEB_DRIVER_OS_VERSION.matcher(normalized).replaceAll("os.version: '*'"); + normalized = WEB_DRIVER_JAVA_VERSION.matcher(normalized).replaceAll("java.version: '*'"); + return normalized; + } } diff --git a/testit-java-commons/src/test/java/ru/testit/services/NormalizeWebDriverMessageTest.java b/testit-java-commons/src/test/java/ru/testit/services/NormalizeWebDriverMessageTest.java new file mode 100644 index 00000000..80cf5652 --- /dev/null +++ b/testit-java-commons/src/test/java/ru/testit/services/NormalizeWebDriverMessageTest.java @@ -0,0 +1,37 @@ +package ru.testit.services; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class NormalizeWebDriverMessageTest { + + @Test + void normalizeWebDriverMessage_unifiesDifferentChromeStartupErrors() { + String chromeExited = "Could not start a new session. Response code 500. Message: session not created: " + + "Chrome instance exited. Examine ChromeDriver verbose log to determine the cause. \n" + + "Host info: host: 'runnervmrw5os', ip: '10.1.0.196'\n" + + "System info: os.name: 'Linux', os.version: '6.17.0-1013-azure', java.version: '1.8.0_492'\n" + + "selenide.url: https://testit.software/"; + + String userDataDirInUse = "Could not start a new session. Response code 500. Message: session not created: " + + "probably user data directory is already in use, please specify a unique value for --user-data-dir " + + "argument, or don't use --user-data-dir \n" + + "Host info: host: 'pkrvm7jw40e0xgp', ip: '10.1.0.4'\n" + + "System info: os.name: 'Linux', os.version: '6.11.0-1018-azure', java.version: '17.0.16'\n" + + "selenide.url: https://testit.software/"; + + String normalizedChromeExited = Utils.normalizeWebDriverMessage(chromeExited); + String normalizedUserDataDir = Utils.normalizeWebDriverMessage(userDataDirInUse); + + Assertions.assertEquals(normalizedChromeExited, normalizedUserDataDir); + Assertions.assertFalse(normalizedChromeExited.contains("Host info:")); + Assertions.assertTrue(normalizedChromeExited.contains("session not created: ")); + Assertions.assertTrue(normalizedChromeExited.contains("java.version: '*'")); + } + + @Test + void normalizeWebDriverMessage_leavesAssertionErrorsUntouched() { + String assertionMessage = "Element should have text \"Система управления тестированием\""; + Assertions.assertEquals(assertionMessage, Utils.normalizeWebDriverMessage(assertionMessage)); + } +}