From 16ac381fbad0bc430f4bea0fe57f35e41d20f381 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 13 May 2026 14:35:14 +0200 Subject: [PATCH 1/4] ci: migrate validation to GitHub Actions --- .github/workflows/validation.yml | 596 ++++++++++++++++++ .gitignore | 4 +- package-lock.json | 521 +++++++++++++++ package.json | 2 +- scripts/build.sh | 236 ------- scripts/wtr.js | 27 +- .../charts/tests/AbstractChartIT.java | 44 ++ .../vaadin-charts-flow-svg-generator/pom.xml | 4 +- .../com/vaadin/tests/AbstractComponentIT.java | 435 ++++++++++++- .../data/renderer/tests/LitRendererIT.java | 4 +- .../tests/TimePickerLocalizationIT.java | 5 - 11 files changed, 1619 insertions(+), 259 deletions(-) create mode 100644 .github/workflows/validation.yml create mode 100644 package-lock.json delete mode 100755 scripts/build.sh create mode 100644 vaadin-charts-flow-parent/vaadin-charts-flow-integration-tests/src/test/java/com/vaadin/flow/component/charts/tests/AbstractChartIT.java diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml new file mode 100644 index 00000000000..8bce102c2d4 --- /dev/null +++ b/.github/workflows/validation.yml @@ -0,0 +1,596 @@ +name: Validation + +on: + pull_request: + branches: ['main', '25.*'] + merge_group: + workflow_dispatch: + inputs: + components: + description: 'Space-separated component names (e.g. "grid combo-box"), empty for all' + required: false + default: '' + shards: + description: 'Number of parallel IT shards' + required: false + default: '5' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_ref || github.ref }} + cancel-in-progress: true + +env: + FORK_COUNT: 4 + +jobs: + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + outputs: + skip: ${{ steps.skip-ci.outputs.skip }} + components: ${{ steps.components.outputs.elements }} + build-cache-key: ${{ steps.build-key.outputs.key }} + steps: + - name: Set Maven args + run: | + args="-ntp -B" + [ "${{ runner.debug }}" != "1" ] && args="$args -q" + echo "MAVEN_ARGS=$args" >> "$GITHUB_ENV" + + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} + fetch-depth: ${{ github.event_name == 'merge_group' && 50 || 1 }} + + - name: Check for skip-ci + id: skip-ci + if: github.event_name == 'pull_request' || github.event_name == 'merge_group' + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + if [ "${{ github.event_name }}" = "merge_group" ]; then + echo "skip=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + if echo "$PR_TITLE" | grep -q '\[skip ci\]'; then + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Compute build cache key + id: build-key + if: steps.skip-ci.outputs.skip != 'true' + env: + KEY: ${{ runner.os }}-build-${{ hashFiles('**/pom.xml', '**/src/main/java/**', '**/src/main/resources/**', '!integration-tests/**', '!**/frontend/generated/**', 'vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/*.js') }} + run: echo "key=$KEY" >> "$GITHUB_OUTPUT" + + - name: Restore build cache + id: build-cache + if: steps.skip-ci.outputs.skip != 'true' + uses: actions/cache@v5 + with: + path: | + ~/.m2/repository/com/vaadin + vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/main/resources/META-INF/frontend/generated/ + key: ${{ steps.build-key.outputs.key }} + + - name: Setup JDK 21 + if: steps.skip-ci.outputs.skip != 'true' && steps.build-cache.outputs.cache-hit != 'true' + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Setup Node + if: steps.skip-ci.outputs.skip != 'true' && steps.build-cache.outputs.cache-hit != 'true' + uses: actions/setup-node@v6 + with: + node-version: '20' + + - name: Show environment info + if: steps.skip-ci.outputs.skip != 'true' && steps.build-cache.outputs.cache-hit != 'true' + run: | + java -version + mvn -version + node --version + npm --version + google-chrome --version + chromedriver --version + + - name: Install artifacts + if: steps.skip-ci.outputs.skip != 'true' && steps.build-cache.outputs.cache-hit != 'true' + run: | + mvn install -Dmaven.test.skip=true -Drelease -T $FORK_COUNT $MAVEN_ARGS || { + echo "::warning::First attempt failed (Maven multi-thread race). Retrying..." + sleep 15 + mvn install -Dmaven.test.skip=true -Drelease -T $FORK_COUNT $MAVEN_ARGS + } + + - name: Detect modified components + id: components + if: steps.skip-ci.outputs.skip != 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + elements="${{ github.event.inputs.components || '' }}" + changed_files="" + if [ -n "$elements" ]; then + echo "elements=$elements" >> "$GITHUB_OUTPUT" + exit 0 + fi + if [ "${{ github.event_name }}" = "pull_request" ]; then + pr_number="${{ github.event.pull_request.number }}" + changed_files=$(gh api "repos/${{ github.repository }}/pulls/$pr_number/files" --jq '.[].filename') + elif [ "${{ github.event_name }}" = "merge_group" ]; then + changed_files=$(git diff --name-only "${{ github.event.merge_group.base_sha }}" "${{ github.event.merge_group.head_sha }}") + fi + if [ -n "$changed_files" ]; then + modified=$(echo "$changed_files" \ + | grep 'vaadin.*flow-parent' \ + | sed 's,^vaadin-\(.*\)-flow-parent.*,\1,' \ + | sort -u) + nmods=$(echo "$modified" | wc -w | tr -d ' ') + all_files=$(echo "$changed_files" | sort -u | tr -d '[:space:]') + component_files=$(echo "$changed_files" | grep 'vaadin.*flow-parent' | sort -u | tr -d '[:space:]') + if [ "$nmods" -lt 5 ] && [ ${#all_files} -eq ${#component_files} ]; then + elements=$(echo $modified | tr '\n' ' ') + echo "Running partial build for: $elements" + fi + fi + echo "elements=$elements" >> "$GITHUB_OUTPUT" + + fast-tests: + name: ${{ matrix.name }} + needs: build + if: needs.build.outputs.skip != 'true' + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + outputs: + matrix: ${{ steps.shards.outputs.matrix }} + has-tests: ${{ steps.shards.outputs.has-tests }} + war-cache-key: ${{ steps.war-key.outputs.key }} + strategy: + fail-fast: true + matrix: + include: + - name: Unit tests + type: unit + - name: WTR tests + type: wtr + - name: Package WAR + type: war + steps: + - name: Set Maven args + run: | + args="-ntp -B" + [ "${{ runner.debug }}" != "1" ] && args="$args -q" + echo "MAVEN_ARGS=$args" >> "$GITHUB_ENV" + + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} + fetch-depth: 1 + + - name: Setup Node + if: matrix.type != 'unit' + uses: actions/setup-node@v6 + with: + node-version: '20' + + - name: npm install + if: matrix.type != 'unit' + run: npm ci --ignore-scripts + + - name: Merge ITs + if: matrix.type == 'war' + run: node scripts/mergeITs.js ${{ needs.build.outputs.components }} + + - name: Compute WAR cache key + if: matrix.type == 'war' + id: war-key + env: + KEY: ${{ runner.os }}-war-${{ hashFiles('**/pom.xml', '**/src/main/java/**', '**/src/main/resources/**', 'integration-tests/src/test/java/**') }} + run: echo "key=$KEY" >> "$GITHUB_OUTPUT" + + - name: Restore WAR cache + if: matrix.type == 'war' + id: war-cache + uses: actions/cache@v5 + with: + path: | + integration-tests/pom.xml + integration-tests/target + key: ${{ steps.war-key.outputs.key }} + + - name: Setup JDK 21 + if: steps.war-cache.outputs.cache-hit != 'true' + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Install TestBench license + if: env.TB_LICENSE != '' && steps.war-cache.outputs.cache-hit != 'true' + env: + TB_LICENSE: ${{ secrets.TB_LICENSE }} + run: | + mkdir -p ~/.vaadin + user="${TB_LICENSE%%/*}" + key="${TB_LICENSE#*/}" + echo "{\"username\":\"${user}\",\"proKey\":\"${key}\"}" > ~/.vaadin/proKey + + - name: Restore build cache + if: steps.war-cache.outputs.cache-hit != 'true' + id: build-cache + uses: actions/cache@v5 + with: + path: | + ~/.m2/repository/com/vaadin + vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/main/resources/META-INF/frontend/generated/ + key: ${{ needs.build.outputs.build-cache-key }} + fail-on-cache-miss: true + + - name: Run unit tests + if: matrix.type == 'unit' + run: mvn test -Drelease -T $FORK_COUNT -Dsurefire.parallel=classes -Dsurefire.threadCount=2 -DskipSvgChartsBuild $MAVEN_ARGS + + - name: Upload unit test reports + if: always() && matrix.type == 'unit' + uses: actions/upload-artifact@v6 + with: + name: surefire-reports + path: '**/target/surefire-reports/TEST-*.xml' + retention-days: 1 + if-no-files-found: ignore + + - name: Run WTR tests + if: matrix.type == 'wtr' + run: node scripts/wtr.js ${{ needs.build.outputs.components }} + + - name: Upload WTR reports + if: always() && matrix.type == 'wtr' + uses: actions/upload-artifact@v6 + with: + name: wtr-reports + path: '**/wtr-results.xml' + retention-days: 1 + if-no-files-found: ignore + + - name: Package integration-tests WAR + if: matrix.type == 'war' && steps.war-cache.outputs.cache-hit != 'true' + run: | + mvn package -pl integration-tests \ + -Dvaadin.pnpm.enable -Drun-it -Drelease \ + -Dvaadin.productionMode -Dvaadin.force.production.build=true \ + -Dmaven.test.skip=true -DskipJetty $MAVEN_ARGS + + - name: Compile IT test sources + if: matrix.type == 'war' && steps.war-cache.outputs.cache-hit != 'true' + run: mvn test-compile -pl integration-tests -Drun-it -DskipFrontend -DskipJetty -DskipUnitTests $MAVEN_ARGS + + - name: Compute IT shards + if: matrix.type == 'war' + id: shards + env: + SHARDS: ${{ github.event.inputs.shards || '0' }} + TARGET_PER_SHARD: 35 + MAX_SHARDS: 10 + run: | + mapfile -t its < <(find integration-tests/src/test/java -name '*IT.java' -printf '%P\n' | sed -e 's|/|.|g' -e 's|\.java$||' | sort) + count=${#its[@]} + echo "Found $count IT classes" + if [ "$count" -eq 0 ]; then + echo "has-tests=false" >> "$GITHUB_OUTPUT" + echo 'matrix={"include":[]}' >> "$GITHUB_OUTPUT" + exit 0 + fi + n=$SHARDS + if [ "$n" -eq 0 ]; then + n=$(( (count + TARGET_PER_SHARD - 1) / TARGET_PER_SHARD )) + [ "$n" -lt 1 ] && n=1 + [ "$n" -gt "$MAX_SHARDS" ] && n=$MAX_SHARDS + fi + if [ "$n" -gt "$count" ]; then n=$count; fi + echo "$n shards for $count classes (~$(( count / n )) per shard)" + declare -a buckets + for i in $(seq 1 $n); do buckets[$i]=""; done + i=1 + for t in "${its[@]}"; do + if [ -n "${buckets[$i]}" ]; then buckets[$i]+=","; fi + buckets[$i]+="$t" + i=$((i+1)) + [ $i -gt $n ] && i=1 + done + json='{"include":[' + for k in $(seq 1 $n); do + [ $k -gt 1 ] && json+=',' + json+='{"shard":"'"$k"'","tests":"'"${buckets[$k]}"'"}' + done + json+=']}' + echo "$json" + echo "matrix=$json" >> "$GITHUB_OUTPUT" + echo "has-tests=true" >> "$GITHUB_OUTPUT" + + its: + name: ITs (${{ matrix.shard }}) + needs: [build, fast-tests] + if: needs.fast-tests.outputs.has-tests == 'true' + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: read + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.fast-tests.outputs.matrix) }} + steps: + - name: Set Maven args + run: | + args="-ntp -B" + [ "${{ runner.debug }}" != "1" ] && args="$args -q" + echo "MAVEN_ARGS=$args" >> "$GITHUB_ENV" + + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} + fetch-depth: 1 + + - name: Setup JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + + - name: Install TestBench license + if: env.TB_LICENSE != '' + env: + TB_LICENSE: ${{ secrets.TB_LICENSE }} + run: | + mkdir -p ~/.vaadin + user="${TB_LICENSE%%/*}" + key="${TB_LICENSE#*/}" + echo "{\"username\":\"${user}\",\"proKey\":\"${key}\"}" > ~/.vaadin/proKey + + - name: Restore build cache + uses: actions/cache@v5 + with: + path: | + ~/.m2/repository/com/vaadin + vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/src/main/resources/META-INF/frontend/generated/ + key: ${{ needs.build.outputs.build-cache-key }} + fail-on-cache-miss: true + + - name: Restore WAR cache + uses: actions/cache@v5 + with: + path: | + integration-tests/pom.xml + integration-tests/target + key: ${{ needs.fast-tests.outputs.war-cache-key }} + fail-on-cache-miss: true + + - name: Run integration tests (shard ${{ matrix.shard }}) + env: + SHARD_TESTS: ${{ matrix.tests }} + run: | + mvn -pl integration-tests -Drun-it \ + jetty:start-war failsafe:integration-test jetty:stop failsafe:verify \ + -Dvaadin.productionMode \ + -DskipFrontend \ + -Dfailsafe.forkCount=$FORK_COUNT \ + -Dcom.vaadin.testbench.Parameters.testsInParallel=2 \ + -Dfailsafe.rerunFailingTestsCount=2 \ + -Dmaven.test.redirectTestOutputToFile=true \ + -Dtest.reuseDriver=true \ + -Dit.test="$SHARD_TESTS" \ + -B + + - name: Upload error screenshots + if: failure() + uses: actions/upload-artifact@v6 + with: + name: error-screenshots-${{ matrix.shard }} + path: integration-tests/error-screenshots/ + retention-days: 5 + if-no-files-found: ignore + + - name: Upload failsafe reports + if: always() + uses: actions/upload-artifact@v6 + with: + name: failsafe-reports-${{ matrix.shard }} + path: integration-tests/target/failsafe-reports/ + retention-days: 1 + if-no-files-found: ignore + + results: + name: Collect tests + needs: [build, fast-tests, its] + if: always() && needs.build.result == 'success' + runs-on: ubuntu-latest + permissions: + contents: read + checks: write + actions: write + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} + fetch-depth: 1 + + - uses: actions/download-artifact@v8 + continue-on-error: true + with: + name: surefire-reports + path: surefire-reports + + - uses: actions/download-artifact@v8 + continue-on-error: true + with: + pattern: failsafe-reports-* + merge-multiple: true + path: failsafe-reports + + - uses: actions/download-artifact@v8 + continue-on-error: true + with: + name: wtr-reports + path: wtr-reports + + - name: Remove passing test reports + run: | + # Detect real failures via testsuite attributes (flaky retries leave + # tags but keep failures="0" errors="0" on the suite). + has_failures=false + for dir in surefire-reports failsafe-reports wtr-reports; do + [ -d "$dir" ] || continue + if find "$dir" -name '*.xml' | xargs grep -lE 'failures="[1-9]|errors="[1-9]' 2>/dev/null | grep -q .; then + has_failures=true + break + fi + done + echo "has_failures=$has_failures" >> $GITHUB_ENV + # Red build: keep only failing reports so dorny focuses on failures. + # Green build: keep all reports so every suite shows as passed. + if [ "$has_failures" = "true" ]; then + for dir in surefire-reports failsafe-reports wtr-reports; do + [ -d "$dir" ] || continue + find "$dir" -name '*.xml' | while read -r f; do + grep -qE 'failures="[1-9]|errors="[1-9]' "$f" || rm -f "$f" + done + done + fi + + - name: Check for unit test reports + id: unit-check + run: | + count=$(find surefire-reports -name 'TEST-*.xml' 2>/dev/null | wc -l | tr -d ' ') + echo "count=$count" >> $GITHUB_OUTPUT + + - name: Unit Tests header + if: steps.unit-check.outputs.count != '0' + run: echo "## Unit Tests" >> $GITHUB_STEP_SUMMARY + + - name: Publish unit test results + id: unit-dorny + if: steps.unit-check.outputs.count != '0' + uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 + with: + name: Unit Tests + path: surefire-reports/**/TEST-*.xml + reporter: java-junit + list-tests: ${{ env.has_failures == 'true' && 'failed' || 'all' }} + list-suites: ${{ env.has_failures == 'true' && 'failed' || 'all' }} + fail-on-error: 'false' + + - name: Unit test compilation error + if: steps.unit-check.outputs.count == '0' && needs.fast-tests.result == 'failure' + run: | + echo ":x: Compilation failed. Check the **Unit tests** job for details." >> $GITHUB_STEP_SUMMARY + + - name: Check for IT reports + id: it-check + run: | + count=$(find failsafe-reports -name 'TEST-*.xml' 2>/dev/null | wc -l | tr -d ' ') + echo "count=$count" >> $GITHUB_OUTPUT + + - name: Integration Tests header + if: steps.it-check.outputs.count != '0' + run: echo "## Integration Tests" >> $GITHUB_STEP_SUMMARY + + - name: Publish integration test results + id: it-dorny + if: steps.it-check.outputs.count != '0' + uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 + with: + name: Integration Tests + path: failsafe-reports/TEST-*.xml + reporter: java-junit + list-tests: ${{ env.has_failures == 'true' && 'failed' || 'all' }} + list-suites: ${{ env.has_failures == 'true' && 'failed' || 'all' }} + fail-on-error: 'false' + + - name: IT compilation error + if: steps.it-check.outputs.count == '0' && needs.fast-tests.result == 'failure' + run: | + echo ":x: Compilation failed. Check the **Package WAR** job for details." >> $GITHUB_STEP_SUMMARY + + - name: Check for WTR reports + id: wtr-check + run: | + count=$(find wtr-reports -name 'wtr-results.xml' 2>/dev/null | wc -l | tr -d ' ') + echo "count=$count" >> $GITHUB_OUTPUT + + - name: WTR Tests header + if: steps.wtr-check.outputs.count != '0' + run: echo "## WTR Tests" >> $GITHUB_STEP_SUMMARY + + - name: Publish WTR results + id: wtr-dorny + if: steps.wtr-check.outputs.count != '0' + uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 + with: + name: WTR Tests + path: wtr-reports/**/wtr-results.xml + reporter: java-junit + list-tests: ${{ env.has_failures == 'true' && 'failed' || 'all' }} + list-suites: ${{ env.has_failures == 'true' && 'failed' || 'all' }} + fail-on-error: 'false' + + - name: WTR setup error + if: steps.wtr-check.outputs.count == '0' && needs.fast-tests.result == 'failure' + run: | + echo ":x: WTR setup or compilation failed. Check the **WTR tests** job for details." >> $GITHUB_STEP_SUMMARY + + - uses: actions/download-artifact@v8 + if: needs.its.result != 'success' + continue-on-error: true + with: + pattern: error-screenshots-* + merge-multiple: true + path: error-screenshots + + - name: Upload merged error screenshots + if: needs.its.result != 'success' + uses: actions/upload-artifact@v6 + with: + name: error-screenshots + path: error-screenshots/ + retention-days: 5 + if-no-files-found: ignore + + - name: Delete intermediate artifacts + if: always() + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + run: | + gh api --paginate "repos/$GH_REPO/actions/runs/$RUN_ID/artifacts" \ + --jq '.artifacts[] | select( + (.name | startswith("error-screenshots-")) or + (.name | startswith("failsafe-reports-")) or + .name == "wtr-reports" or + .name == "surefire-reports" + ) | "\(.id) \(.name)"' \ + | while read -r id name; do + echo "Deleting artifact $name (id=$id)" + gh api -X DELETE "repos/$GH_REPO/actions/artifacts/$id" || true + done + + - name: Fail if any test failed + if: always() + run: | + failed=false + [[ "${{ steps.unit-dorny.outputs.conclusion }}" == "failure" ]] && failed=true + [[ "${{ steps.wtr-dorny.outputs.conclusion }}" == "failure" ]] && failed=true + [[ "${{ steps.it-dorny.outputs.conclusion }}" == "failure" ]] && failed=true + [[ "$failed" == "true" ]] && exit 1 || exit 0 diff --git a/.gitignore b/.gitignore index 518cf97b21d..994c46bcbbb 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,9 @@ vite.generated.ts /vite.config.ts node_modules .driver -package*json +**/package*json +!/package.json +!/package-lock.json pnpm* .npmrc .pnpmfile.cjs diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..11adfb53464 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,521 @@ +{ + "name": "vaadin-flow-components", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vaadin-flow-components", + "version": "1.0.0", + "license": "APACHE", + "devDependencies": { + "replace-in-file": "6.1.0", + "xml2js": "^0.6.2" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/replace-in-file": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.1.0.tgz", + "integrity": "sha512-URzjyF3nucvejuY13HFd7O+Q6tFJRLKGHLYVvSh+LiZj3gFXzSYGnIkQflnJJulCAI2/RTZaZkpOtdVdW0EhQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "glob": "^7.1.6", + "yargs": "^15.3.1" + }, + "bin": { + "replace-in-file": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json index a77258905c0..2bcad45c4ce 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "devDependencies": { "replace-in-file": "6.1.0", - "xml2js": "^0.4.23" + "xml2js": "^0.6.2" }, "license": "APACHE" } diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index 5a364ea8983..00000000000 --- a/scripts/build.sh +++ /dev/null @@ -1,236 +0,0 @@ -#!/bin/bash - -## Read Arguments -if [ -n "$1" ] -then - for i in "$@" - do - case $i in - processors=*) - FORK_COUNT=`echo $i | cut -d = -f2`;; - parallel=*) - TESTS_IN_PARALLEL=`echo $i | cut -d = -f2`;; - pr=*) - PR=`echo $i | cut -d = -f2`;; - quiet) - quiet="-q";; - hub=*) - TBHUB=`echo $i | cut -d = -f2`;; - image=*) - SELENIUM_IMAGE=`echo $i | cut -d = -f2`;; - *) - modules="vaadin-$i-flow-parent/vaadin-$i-flow-integration-tests,$modules" - elements="$elements $i" - ;; - esac - done -fi - -args="$args -ntp -B $quiet" -verify="verify -Dvaadin.pnpm.enable" - -## compute modules that were modified in this PR -if [ -z "$modules" -a -n "$PR" ] -then - title=`curl -s "https://api.github.com/repos/vaadin/flow-components/pulls/$PR" | jq -r '. | .title'` - ## need to check whether changes in the root or not - modifiedAll=`curl -s "https://api.github.com/repos/vaadin/flow-components/pulls/$PR/files" \ - | jq -r '.[] | .filename' | sort -u | tr -d '[:space:]'` - modifiedComponent=`curl -s "https://api.github.com/repos/vaadin/flow-components/pulls/$PR/files" \ - | jq -r '.[] | .filename' | grep 'vaadin.*flow-parent' | sort -u | tr -d '[:space:]'` - modified=`curl -s "https://api.github.com/repos/vaadin/flow-components/pulls/$PR/files" \ - | jq -r '.[] | .filename' | grep 'vaadin.*flow-parent' | perl -pe 's,^vaadin-(.*)-flow-parent.*,$1,g' | sort -u` - - nmods=`echo "$modified" | wc -w` - [ "$nmods" -eq 1 ] && echo "$title" | grep -q " vaadin-$modified-flow " && partial=yes - [ -z "$partial" ] && [ $nmods -lt 5 ] && [ `echo ${#modifiedAll}` = `echo ${#modifiedComponent}` ] && partial=yes - - if [ -n "$partial" ] - then - for i in $modified - do - modules="vaadin-$i-flow-parent/vaadin-$i-flow-integration-tests,$modules" - elements="$elements $i" - done - fi -fi - -tcMsg() ( - echo "##teamcity[$1]" -) -# open a block in the TC tree output -tcLog() { - [ -n "$inblock" ] && tcMsg "blockClosed name='$inblock'" - inblock="$1" - tcMsg "blockOpened name='$inblock'" -} -# log in TC -tcStatus() { - [ "$1" = "0" ] && status=SUCCESS || status=FAILURE - [ "$1" = "0" ] && text="$3" || text="$2" - tcMsg "buildStatus status='$status' text='$text'" - exit $1 -} - -saveFailedTests() { - try=$1 - failedMethods=`egrep '<<< ERROR!$|<<< FAILURE!$' integration-tests/target/failsafe-reports/*txt | perl -pe 's,.*(com.vaadin[\.\w\d]+).*,$1,g' | sort -u` - failed=$(echo "$failedMethods" | perl -pe 's,.*(com\.vaadin[.\w\d]+)\.[\w\d]+,$1,g') - nfailed=`echo "$failed" | wc -w` - ### collect tests numbers for TC status - ncompleted=`grep 'Tests run: ' vaadin*/*flow/target/surefire-reports/*.txt integration-tests/target/failsafe-reports/*txt | awk '{SUM+=$3} END { print SUM }'` - nskipped=`grep 'Tests run: ' vaadin*/*flow/target/surefire-reports/*.txt integration-tests/target/failsafe-reports/*txt | awk '{SUM+=$9} END { print SUM }'` - if [ "$nfailed" -ge 1 ] - then - mkdir -p integration-tests/error-screenshots/$try - mv integration-tests/error-screenshots/*.png integration-tests/error-screenshots/$try - for i in $failed - do - cp integration-tests/target/failsafe-reports/$i.txt integration-tests/error-screenshots/$try - done - fi -} - -computeFastBuild() { - [ -z "$PR" ] && return 1 - ghUrl="https://api.github.com/repos/vaadin/flow-components/pulls/$PR" - prTitle=`curl -s $ghUrl | jq -r .title` - echo "$prTitle" | grep -v '\[skip ci\]' >/dev/null || return 0 - prMessages=`curl -s $ghUrl/commits | jq -r '.[] | .commit.message'` - echo "$prMessages" | grep -v '\[skip ci\]' >/dev/null || return 0 - return 1 -} - -## Set default build paramters -[ -z "$TESTS_IN_PARALLEL" ] && TESTS_IN_PARALLEL=1 -[ -z "$FORK_COUNT" ] && FORK_COUNT="5" -[ -z "$SELENIUM_IMAGE" ] && SELENIUM_IMAGE="latest" - -## Show info about environment -tcLog "Show info (forks=$FORK_COUNT parallel=$TESTS_IN_PARALLEL)" -echo $SHELL -type java && java -version -type mvn && mvn -version -type node 2>/dev/null && node --version -type npm 2>/dev/null && npm --version -type pnpm 2>/dev/null && pnpm --version -uname -a -[ -n "$elements" ] && echo "Run: $elements" || echo "Run all" - -## Compile all java files including tests in ITs modules -cmd="mvn clean test-compile -DskipFrontend $args" -tcLog "Compiling flow components - $cmd" -$cmd || tcStatus 1 "Compilation failed" - -## Notify TC that we are going to run maven tests -tcLog "Running report watcher for Tests " -tcMsg "importData type='surefire' path='**/*-reports/TEST*xml'"; - -## Compile and install all modules excluding ITs -cmd="mvn install -Drelease -T $FORK_COUNT $args" -tcLog "Unit-Testing and Installing flow components - $cmd" -if ! $cmd -then - ## Some times install fails because of maven multithread race condition - ## running a second time it is mitigated - tcLog "Unit-Testing and Installing flow components (2nd try) - $cmd" - sleep 15 - $cmd || tcStatus 1 "Unit-Testing failed" -fi - -## Skip IT's if developer passed [skip ci] labels in commit messages -tcLog "Checking for skip-ci labels" -if computeFastBuild -then - echo "$prTitle" - echo "$prMessages" - tcStatus 0 "" "Success - skip-ci" -fi - -## Install node modules used for merging ITs -cmd="npm install --silent --quiet --no-progress" -tcLog "Install NPM packages - $cmd" -$cmd || exit 1 - -## Create the integration-tests by coping all module ITs -cmd="node scripts/mergeITs.js "`echo $elements` -tcLog "Merge IT modules - $cmd" -$cmd || tcStatus 1 "Merging ITs failed" - -## Compute variable to run tests -[ -n "$TBLICENSE" ] && args="$args -Dvaadin.testbench.developer.license=$TBLICENSE" -[ -n "$TBHUB" ] && args="$args -Dtest.use.hub=true -Dcom.vaadin.testbench.Parameters.hubHostname=$TBHUB" -if [ -n "$SAUCE_USER" ] -then - test -n "$SAUCE_ACCESS_KEY" || { echo "\$SAUCE_ACCESS_KEY needs to be defined to use Saucelabs" >&2 ; exit 1; } - args="$args -P saucelabs -Dtest.use.hub=true -Dsauce.user=$SAUCE_USER -Dsauce.sauceAccessKey=$SAUCE_ACCESS_KEY" -fi - -args="$args -Dfailsafe.rerunFailingTestsCount=2 -Dmaven.test.redirectTestOutputToFile=true" - -## Install a selenium hub in local host to run tests against chrome -if [ "$TBHUB" = "localhost" ] -then - DOCKER_CONTAINER_NAME="selenium-container" - [ -n "$SELENIUM_DOCKER_IMAGE" ] || SELENIUM_DOCKER_IMAGE="selenium/standalone-chrome:$SELENIUM_IMAGE" - tcLog "Starting docker container using the $SELENIUM_DOCKER_IMAGE image" - set -x - trap "echo Terminating docker; docker stop $DOCKER_CONTAINER_NAME" EXIT - docker pull "$SELENIUM_DOCKER_IMAGE" || exit 1 - docker image prune -f || exit 1 - docker run --name "$DOCKER_CONTAINER_NAME" --net=host --rm -d -v /dev/shm:/dev/shm "$SELENIUM_DOCKER_IMAGE" || exit 1 - set +x -fi - -## Run web-test-runner tests -cmd="node scripts/wtr.js" -tcLog "Running web-test-runner tests - $cmd" -$cmd || exit 1 - -if [ -n "$modules" ] && [ -z "$USE_MERGED_MODULE" ] -then - ### Run IT's in original modules - cmd="mvn clean $verify -Dfailsafe.forkCount=$FORK_COUNT $args -pl $modules -DskipUnitTests" - tcLog "Running module ITs ($elements) - mvn clean $verify -pl ..." - echo $cmd - $cmd -else - mode="-Dfailsafe.forkCount=$FORK_COUNT -Dcom.vaadin.testbench.Parameters.testsInParallel=$TESTS_IN_PARALLEL" - ### Run IT's in merged module - cmd="mvn $verify -Drun-it -Drelease -Dvaadin.productionMode -Dfailsafe.rerunFailingTestsCount=2 $mode $args -pl integration-tests -DskipUnitTests" - tcLog "Running merged ITs - mvn $verify -B -Drun-it -Drelease -pl integration-tests ..." - echo $cmd - $cmd - error=$? - - [ ! -d integration-tests/target/failsafe-reports ] && exit 1 - saveFailedTests run-1 - - if [ "$nfailed" -gt 0 ] - then - ## Give a second try to failed tests - tcLog "There were $nfailed failed IT classes in first round. (error=$error)" - echo "$failedMethods" - - rerunFailed=$nfailed - if [ "$nfailed" -le 15 ] - then - failed=`echo "$failed" | tr '\n' ','` - mode="-Dfailsafe.forkCount=2 -Dcom.vaadin.testbench.Parameters.testsInParallel=3" - cmd="mvn $verify -Drun-it -Drelease -Dvaadin.productionMode -DskipFrontend $mode $args -pl integration-tests -DskipUnitTests -Dit.test=$failed" - tcLog "Re-Running $nfailed failed IT classes ..." - echo $cmd - $cmd - error=$? - saveFailedTests run-2 - - tcLog "There where $nfailed failed IT classes in second round. (error=$error)" - echo "$failedMethods" - - tcStatus $error "(IT2)Test failed: $nfailed" "(IT2)Tests passed: $ncompleted ($rerunFailed retried, $nfailed failed), ignored: $nskipped" - else - tcStatus $error "(IT1)Test failed: $nfailed" "(IT1)Tests passed: $ncompleted (more than 15 failed), ignored: $nskipped" - fi - fi - exit $error -fi diff --git a/scripts/wtr.js b/scripts/wtr.js index 122558cbb55..7a30a428238 100755 --- a/scripts/wtr.js +++ b/scripts/wtr.js @@ -53,7 +53,7 @@ function runTests() { }); // Install dependencies required to run the web-test-runner tests - execSync(`npm install @open-wc/testing @web/dev-server-esbuild @web/test-runner @web/test-runner-playwright @types/mocha sinon @vaadin/testing-helpers --save-dev --legacy-peer-deps`, { + execSync(`npm install --ignore-scripts @open-wc/testing @web/dev-server-esbuild @web/test-runner @web/test-runner-playwright @web/test-runner-junit-reporter @types/mocha sinon @vaadin/testing-helpers --save-dev --legacy-peer-deps`, { cwd: itFolder, stdio: 'inherit' }); @@ -64,12 +64,29 @@ function runTests() { stdio: 'inherit' }); + // Generate a CI config that adds the JUnit reporter on top of the existing config + const hasBaseConfig = fs.existsSync(`${itFolder}/web-test-runner.config.mjs`); + const ciConfigPath = `${itFolder}/wtr-ci.config.mjs`; + fs.writeFileSync(ciConfigPath, [ + `import { defaultReporter, summaryReporter } from '@web/test-runner';`, + `import { junitReporter } from '@web/test-runner-junit-reporter';`, + hasBaseConfig ? `import baseConfig from './web-test-runner.config.mjs';` : '', + `export default {`, + hasBaseConfig ? ` ...baseConfig,` : '', + ` reporters: [defaultReporter(), summaryReporter(), junitReporter({ outputPath: 'wtr-results.xml', reportLogs: true })],`, + `};`, + ].filter(Boolean).join('\n')); + // Run the tests console.log(`Running tests in ${itFolder}`); - execSync(`npx web-test-runner --playwright ${wtrTestsFolderName}/**/*.test.ts --node-resolve`, { - cwd: itFolder, - stdio: 'inherit' - }); + try { + execSync(`npx web-test-runner --playwright ${wtrTestsFolderName}/**/*.test.ts --node-resolve --config wtr-ci.config.mjs`, { + cwd: itFolder, + stdio: 'inherit' + }); + } finally { + fs.unlinkSync(ciConfigPath); + } } } } diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-integration-tests/src/test/java/com/vaadin/flow/component/charts/tests/AbstractChartIT.java b/vaadin-charts-flow-parent/vaadin-charts-flow-integration-tests/src/test/java/com/vaadin/flow/component/charts/tests/AbstractChartIT.java new file mode 100644 index 00000000000..51b58f6df27 --- /dev/null +++ b/vaadin-charts-flow-parent/vaadin-charts-flow-integration-tests/src/test/java/com/vaadin/flow/component/charts/tests/AbstractChartIT.java @@ -0,0 +1,44 @@ +/** + * Copyright 2000-2026 Vaadin Ltd. + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See {@literal } for the full + * license. + */ +package com.vaadin.flow.component.charts.tests; + +import org.junit.Before; + +import com.vaadin.flow.component.charts.testbench.ChartElement; +import com.vaadin.testbench.ElementQuery; +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.tests.AbstractComponentIT; + +public abstract class AbstractChartIT extends AbstractComponentIT { + + @Before + public void setup() { + open(); + } + + protected ChartElement getChartElement() { + return $(ChartElement.class).waitForFirst(); + } + + protected TestBenchElement getElementFromShadowRoot( + TestBenchElement shadowRootOwner, String selector) { + return shadowRootOwner.$(selector).first(); + } + + protected TestBenchElement getElementFromShadowRoot( + TestBenchElement shadowRootOwner, String selector, int index) { + ElementQuery elements = shadowRootOwner.$(selector); + if (elements.all().size() > index) { + return elements.get(index); + } + + throw new AssertionError( + "Could not find required element in the shadowRoot"); + } +} diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/pom.xml b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/pom.xml index 0ce9c21ce96..6dc6e4f9974 100644 --- a/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/pom.xml +++ b/vaadin-charts-flow-parent/vaadin-charts-flow-svg-generator/pom.xml @@ -56,6 +56,7 @@ install + ${skipSvgChartsBuild} @@ -70,6 +71,7 @@ run build + ${skipSvgChartsBuild} @@ -83,7 +85,7 @@ test - ${skipTests} + ${skipSvgChartsBuild} diff --git a/vaadin-flow-components-shared-parent/vaadin-flow-components-test-util/src/main/java/com/vaadin/tests/AbstractComponentIT.java b/vaadin-flow-components-shared-parent/vaadin-flow-components-test-util/src/main/java/com/vaadin/tests/AbstractComponentIT.java index 8d794ea2ce7..99aee832717 100644 --- a/vaadin-flow-components-shared-parent/vaadin-flow-components-test-util/src/main/java/com/vaadin/tests/AbstractComponentIT.java +++ b/vaadin-flow-components-shared-parent/vaadin-flow-components-test-util/src/main/java/com/vaadin/tests/AbstractComponentIT.java @@ -15,18 +15,435 @@ */ package com.vaadin.tests; -public abstract class AbstractComponentIT - extends com.vaadin.flow.testutil.AbstractComponentIT { +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URI; +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.stream.Collectors; - protected int getDeploymentPort() { - return 8080; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.openqa.selenium.By; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.logging.LogEntry; +import org.openqa.selenium.logging.LogType; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.vaadin.flow.testutil.TestPath; +import com.vaadin.testbench.Parameters; +import com.vaadin.testbench.ScreenshotOnFailureRule; +import com.vaadin.testbench.TestBench; +import com.vaadin.testbench.TestBenchTestCase; + +/** + * Base class for Flow component integration tests. + *

+ * Extends {@link TestBenchTestCase} directly and manages a headless Chrome + * driver that is reused across all test methods in the same test class. By + * default the driver is started locally; setting {@code -Dtest.use.hub=true} + * routes driver creation to a Selenium Grid endpoint configured via + * {@code -Dtest.hub.url} (default {@code http://localhost:4444/wd/hub}). + *

+ * This test setup does not support running tests in parallel. The only way to + * parallelize tests is forking separate JVMs that run one suite at a time, for + * example using {@code failsafe.forkCount}. + *

+ * Test classes must be annotated with {@link TestPath} to specify the URL path + * of the test view. + */ +public abstract class AbstractComponentIT extends TestBenchTestCase { + + private static final Logger logger = LoggerFactory + .getLogger(AbstractComponentIT.class); + + private static final boolean REUSE_DRIVER = Boolean + .getBoolean("test.reuseDriver"); + + private static final boolean USE_HUB = Boolean.getBoolean("test.use.hub"); + + private static final String HUB_URL = System.getProperty("test.hub.url", + "http://" + Parameters.getHubHostname() + ":4444/wd/hub"); + + private static final ThreadLocal sharedDriver = new ThreadLocal<>(); + + @Rule + public ScreenshotOnFailureRule screenshotOnFailure = new ScreenshotOnFailureRule( + this, false); + + @BeforeClass + public static void createDriver() { + if (!REUSE_DRIVER) { + return; + } + WebDriver driver = createWebDriver(); + driver.manage().window().setSize(new Dimension(1024, 800)); + driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10)); + sharedDriver.set(driver); + } + + @Before + public void resetDriver() throws Exception { + if (!REUSE_DRIVER) { + WebDriver driver = createWebDriver(); + driver.manage().window().setSize(new Dimension(1024, 800)); + driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10)); + sharedDriver.set(driver); + } + setDriver(sharedDriver.get()); + getDriver().manage().deleteAllCookies(); + getDriver().navigate().to("about:blank"); + } + + @After + public void quitDriverPerMethod() { + if (!REUSE_DRIVER) { + tryQuitDriver(sharedDriver.get()); + sharedDriver.remove(); + } + } + + @AfterClass + public static void quitDriver() { + if (!REUSE_DRIVER) { + return; + } + tryQuitDriver(sharedDriver.get()); + sharedDriver.remove(); + } + + // ----- Test path and URL resolution ----- + + protected String getTestPath() { + TestPath annotation = getClass().getAnnotation(TestPath.class); + if (annotation == null) { + throw new IllegalStateException( + "The test class should be annotated with @TestPath"); + } + String path = annotation.value(); + if (!path.startsWith("/")) { + path = "/" + path; + } + return path; + } + + protected String getRootURL() { + String host = "localhost"; + if (USE_HUB) { + host = findHostAddress(); + } + return "http://" + host + ":8080"; + } + + private String findHostAddress() { + try { + return NetworkInterface.networkInterfaces() + .filter((networkInterface) -> { + try { + return networkInterface.isUp() + && !networkInterface.isLoopback() + && !networkInterface.isVirtual(); + } catch (SocketException e) { + return false; + } + }).flatMap(NetworkInterface::inetAddresses) + .filter(InetAddress::isSiteLocalAddress) + .map(InetAddress::getHostAddress).findFirst() + .orElseThrow(() -> { + return new RuntimeException( + "No compatible (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) ip address found."); + }); + } catch (SocketException e) { + throw new RuntimeException("Could not find the host name", e); + } + } + + protected String getTestURL(String... parameters) { + return getTestURL(getRootURL(), getTestPath(), parameters); + } + + public static String getTestURL(String rootUrl, String testPath, + String... parameters) { + while (rootUrl.endsWith("/")) { + rootUrl = rootUrl.substring(0, rootUrl.length() - 1); + } + rootUrl = rootUrl + testPath; + + if (parameters != null && parameters.length != 0) { + if (!rootUrl.contains("?")) { + rootUrl += "?"; + } else { + rootUrl += "&"; + } + rootUrl += Arrays.stream(parameters) + .collect(Collectors.joining("&")); + } + + return rootUrl; + } + + protected void open() { + open((String[]) null); + } + + protected void open(String... parameters) { + String url = getTestURL(parameters); + TimeoutException lastTimeout = null; + for (int attempt = 1; attempt <= 3; attempt++) { + try { + getDriver().get(url); + waitForDevServer(); + return; + } catch (TimeoutException e) { + lastTimeout = e; + logger.warn( + "Page load timed out for {} (attempt {}/3), retrying", + url, attempt); + } + } + throw lastTimeout; + } + + // ----- Driver management ----- + + private static void tryQuitDriver(WebDriver driver) { + try { + driver.quit(); + } catch (Exception e) { + // Ignore - driver may already be dead + } + } + + private static boolean isJavaInDebugMode() { + return ManagementFactory.getRuntimeMXBean().getInputArguments() + .toString().contains("jdwp"); + } + + private static WebDriver createWebDriver() { + for (int i = 0; i < 3; i++) { + try { + if (USE_HUB) { + return tryCreateRemoteDriver(); + } + + return tryCreateChromeDriver(); + } catch (Exception e) { + logger.warn("Unable to create driver on attempt " + i, e); + } + } + throw new RuntimeException( + "Gave up trying to create a driver instance"); } - @Override - public void setup() throws Exception { - super.setup(); + private static ChromeOptions buildChromeOptions() { + ChromeOptions options = new ChromeOptions(); + if (!isJavaInDebugMode()) { + options.addArguments("--headless=new", "--disable-gpu", + "--disable-backgrounding-occluded-windows", + "--no-sandbox", "--disable-dev-shm-usage"); + } - // Set a default window size - testBench().resizeViewPortTo(1024, 800); + String extraArgs = System.getenv("TESTBENCH_CHROME_EXTRA_ARGS"); + if (extraArgs != null && !extraArgs.isBlank()) { + options.addArguments(extraArgs.split("\\s+")); + } + + String chromeBinary = System.getenv("TESTBENCH_CHROME_BINARY"); + if (chromeBinary != null && !chromeBinary.isBlank()) { + options.setBinary(chromeBinary); + } + return options; + } + + private static WebDriver tryCreateChromeDriver() { + ChromeOptions options = buildChromeOptions(); + ChromeDriver chromeDriver = new ChromeDriver(options); + return TestBench.createDriver(chromeDriver); } + + private static WebDriver tryCreateRemoteDriver() + throws MalformedURLException { + ChromeOptions options = buildChromeOptions(); + RemoteWebDriver remoteDriver = new RemoteWebDriver( + URI.create(HUB_URL).toURL(), options); + return TestBench.createDriver(remoteDriver); + } + + // ----- Test helper methods ----- + + /** + * Waits up to 10s for the given condition to become false. + */ + protected void waitUntilNot(ExpectedCondition condition) { + waitUntilNot(condition, 10); + } + + /** + * Waits the given number of seconds for the given condition to become + * false. + */ + protected void waitUntilNot(ExpectedCondition condition, + long timeoutInSeconds) { + waitUntil(ExpectedConditions.not(condition), timeoutInSeconds); + } + + /** + * Returns true if an element can be found from the driver with given + * selector. + */ + public boolean isElementPresent(By by) { + try { + WebElement element = getDriver().findElement(by); + return element != null; + } catch (Exception e) { + return false; + } + } + + /** + * Clicks on the element, using JS. This method is more convenient than + * Selenium {@code findElement(By.id(urlId)).click()}, because Selenium + * method changes scroll position, which is not always needed. + */ + protected void clickElementWithJs(String elementId) { + executeScript(String.format("document.getElementById('%s').click();", + elementId)); + } + + /** + * Clicks on the element, using JS. + */ + protected void clickElementWithJs(WebElement element) { + executeScript("arguments[0].click();", element); + } + + protected void waitForElementPresent(final By by) { + waitUntil(ExpectedConditions.presenceOfElementLocated(by)); + } + + protected void waitForElementNotPresent(final By by) { + waitUntil(input -> input.findElements(by).isEmpty()); + } + + protected void waitForElementVisible(final By by) { + waitUntil(ExpectedConditions.visibilityOfElementLocated(by)); + } + + /** + * Scrolls the page to the element given using javascript. + */ + protected void scrollToElement(WebElement element) { + Objects.requireNonNull(element, + "The element to scroll to should not be null"); + getCommandExecutor().executeScript("arguments[0].scrollIntoView(true);", + element); + } + + /** + * Scrolls the page to the element specified and clicks it. + */ + protected void scrollIntoViewAndClick(WebElement element) { + scrollToElement(element); + element.click(); + } + + /** + * Gets the log entries from the browser that have the given logging level + * or higher. + */ + protected List getLogEntries(Level level) { + getCommandExecutor().waitForVaadin(); + + return getDriver().manage().logs().get(LogType.BROWSER).getAll() + .stream() + .filter(logEntry -> logEntry.getLevel().intValue() >= level + .intValue()) + .filter(logEntry -> !logEntry.getMessage() + .contains("favicon.ico")) + .collect(Collectors.toList()); + } + + private static final String WEB_SOCKET_CONNECTION_ERROR_PREFIX = "WebSocket connection to "; + + /** + * Checks browser's log entries, throws an error for any client-side error + * and logs any client-side warnings. + */ + protected void checkLogsForErrors( + Predicate acceptableMessagePredicate) { + getLogEntries(Level.WARNING).forEach(logEntry -> { + if (logEntry.getMessage().contains( + "Lit is in dev mode. Not recommended for production")) { + return; + } + if ((Objects.equals(logEntry.getLevel(), Level.SEVERE) + || logEntry.getMessage().contains(" 404 ")) + && !logEntry.getMessage() + .contains(WEB_SOCKET_CONNECTION_ERROR_PREFIX) + && !acceptableMessagePredicate + .test(logEntry.getMessage())) { + throw new AssertionError(String + .format("Error message in browser log: %s", logEntry)); + } else { + LoggerFactory.getLogger(AbstractComponentIT.class.getName()) + .warn("This message in browser log console may be a potential error: '{}'", + logEntry); + } + }); + } + + /** + * Checks browser's log entries, throws an error for any client-side error + * and logs any client-side warnings. + */ + protected void checkLogsForErrors() { + checkLogsForErrors(msg -> false); + } + + /** + * If dev server start in progress wait until it's started. Otherwise return + * immediately. + */ + protected void waitForDevServer() { + Object result; + do { + getCommandExecutor().waitForVaadin(); + result = getCommandExecutor().executeScript( + "return window.Vaadin && window.Vaadin.Flow && window.Vaadin.Flow.devServerIsNotLoaded;"); + } while (Boolean.TRUE.equals(result)); + } + + public void blur() { + executeScript( + "!!document.activeElement ? document.activeElement.blur() : 0"); + } + + /** + * Gets a property value from a web element using JavaScript. + */ + public String getProperty(WebElement element, String propertyName) { + Object result = executeScript( + "return arguments[0]." + propertyName + ";", element); + return result == null ? null : String.valueOf(result); + } + } diff --git a/vaadin-renderer-flow-parent/vaadin-renderer-flow-integration-tests/src/test/java/com/vaadin/flow/data/renderer/tests/LitRendererIT.java b/vaadin-renderer-flow-parent/vaadin-renderer-flow-integration-tests/src/test/java/com/vaadin/flow/data/renderer/tests/LitRendererIT.java index 51a464ea762..8b02af81d6d 100644 --- a/vaadin-renderer-flow-parent/vaadin-renderer-flow-integration-tests/src/test/java/com/vaadin/flow/data/renderer/tests/LitRendererIT.java +++ b/vaadin-renderer-flow-parent/vaadin-renderer-flow-integration-tests/src/test/java/com/vaadin/flow/data/renderer/tests/LitRendererIT.java @@ -99,7 +99,9 @@ public void shouldNotInvokeCallableFromEventWhenContentDiscardedAfterRefresh() { @Test public void shouldNotIncludeEventInCallableArguments() { WebElement itemContent = findElement(By.id("content-0")); - drag(itemContent); + executeScript( + "arguments[0].dispatchEvent(new DragEvent('dragstart', { bubbles: true, cancelable: true }))", + itemContent); Assert.assertEquals("event: dragged, item: 0, argument count: 0", getFirstClientCallableLogMessage()); } diff --git a/vaadin-time-picker-flow-parent/vaadin-time-picker-flow-integration-tests/src/test/java/com/vaadin/flow/component/timepicker/tests/TimePickerLocalizationIT.java b/vaadin-time-picker-flow-parent/vaadin-time-picker-flow-integration-tests/src/test/java/com/vaadin/flow/component/timepicker/tests/TimePickerLocalizationIT.java index 574ac98477f..6a61726aff3 100644 --- a/vaadin-time-picker-flow-parent/vaadin-time-picker-flow-integration-tests/src/test/java/com/vaadin/flow/component/timepicker/tests/TimePickerLocalizationIT.java +++ b/vaadin-time-picker-flow-parent/vaadin-time-picker-flow-integration-tests/src/test/java/com/vaadin/flow/component/timepicker/tests/TimePickerLocalizationIT.java @@ -48,11 +48,6 @@ public void init() { $(TimePickerElement.class).waitForFirst(); } - @Override - protected int getDeploymentPort() { - return super.getDeploymentPort(); - } - @Test public void testAllAvailableLocalesWhenValueChangedFromDropDown_stepOneHourAndFormatHourMinute_pickerValuesMatchesBrowserFormatted() { // select locale based on locale string From 04e6b1ad6e1698c025ceac53d3c3cf57b77823e5 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 13 May 2026 18:16:12 +0200 Subject: [PATCH 2/4] ci: fix Chrome setup for GHA integration test shards Revert the AbstractComponentIT rewrite from #9217 back to the original 25.1 implementation that delegates to flow-test-util. This restores the known-working TestBench setup used by TeamCity. Add two complementary Chrome flags in the GHA its job so Chrome can start inside the GHA sandbox environment: - TESTBENCH_CHROME_EXTRA_ARGS: picked up by updateHeadlessChromeOptions() for tests going through flow-test-util AbstractComponentIT - -DchromeOptions: picked up by TestBench Parameters.getChromeOptions() for tests going through ParallelRunner / LocalDriver.createDriver() (Charts tests and any test annotated with RunWith(ParallelRunner)) Also revert the parallelism settings introduced in #9217 (testsInParallel=2, test.reuseDriver, unit test thread parallelism) since the original AbstractComponentIT does not implement thread-local driver management. Restore the four source files changed in #9217: - AbstractComponentIT.java: back to simple wrapper extending flow-test-util - AbstractChartIT.java: @Override setup() instead of @Before setup() - LitRendererIT.java: drag(itemContent) instead of executeScript - TimePickerLocalizationIT.java: restore getDeploymentPort() override surefire-junit47 is kept in the failsafe plugin (from previous fix) as it is required for correct test discovery with TestBench ParallelRunner when using -Dit.test= shard filtering. --- .github/workflows/validation.yml | 7 +- pom.xml | 12 +- .../charts/tests/AbstractChartIT.java | 7 +- .../com/vaadin/tests/AbstractComponentIT.java | 429 +----------------- .../data/renderer/tests/LitRendererIT.java | 4 +- .../tests/TimePickerLocalizationIT.java | 5 + 6 files changed, 34 insertions(+), 430 deletions(-) diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml index 8bce102c2d4..6a8a310e239 100644 --- a/.github/workflows/validation.yml +++ b/.github/workflows/validation.yml @@ -240,7 +240,7 @@ jobs: - name: Run unit tests if: matrix.type == 'unit' - run: mvn test -Drelease -T $FORK_COUNT -Dsurefire.parallel=classes -Dsurefire.threadCount=2 -DskipSvgChartsBuild $MAVEN_ARGS + run: mvn test -Drelease -T $FORK_COUNT -DskipSvgChartsBuild $MAVEN_ARGS - name: Upload unit test reports if: always() && matrix.type == 'unit' @@ -380,16 +380,17 @@ jobs: - name: Run integration tests (shard ${{ matrix.shard }}) env: SHARD_TESTS: ${{ matrix.tests }} + TESTBENCH_CHROME_EXTRA_ARGS: --no-sandbox --disable-dev-shm-usage run: | mvn -pl integration-tests -Drun-it \ jetty:start-war failsafe:integration-test jetty:stop failsafe:verify \ -Dvaadin.productionMode \ -DskipFrontend \ -Dfailsafe.forkCount=$FORK_COUNT \ - -Dcom.vaadin.testbench.Parameters.testsInParallel=2 \ + -Dcom.vaadin.testbench.Parameters.testsInParallel=1 \ -Dfailsafe.rerunFailingTestsCount=2 \ -Dmaven.test.redirectTestOutputToFile=true \ - -Dtest.reuseDriver=true \ + -DchromeOptions="--no-sandbox,--disable-dev-shm-usage" \ -Dit.test="$SHARD_TESTS" \ -B diff --git a/pom.xml b/pom.xml index cade5ef814d..a10e5fc7f98 100644 --- a/pom.xml +++ b/pom.xml @@ -416,12 +416,16 @@ - - + org.apache.maven.surefire - surefire-junit4 - ${maven.surefire.plugin.version} + surefire-junit47 + ${maven.failsafe.plugin.version} diff --git a/vaadin-charts-flow-parent/vaadin-charts-flow-integration-tests/src/test/java/com/vaadin/flow/component/charts/tests/AbstractChartIT.java b/vaadin-charts-flow-parent/vaadin-charts-flow-integration-tests/src/test/java/com/vaadin/flow/component/charts/tests/AbstractChartIT.java index 51b58f6df27..2b6a5935470 100644 --- a/vaadin-charts-flow-parent/vaadin-charts-flow-integration-tests/src/test/java/com/vaadin/flow/component/charts/tests/AbstractChartIT.java +++ b/vaadin-charts-flow-parent/vaadin-charts-flow-integration-tests/src/test/java/com/vaadin/flow/component/charts/tests/AbstractChartIT.java @@ -8,8 +8,6 @@ */ package com.vaadin.flow.component.charts.tests; -import org.junit.Before; - import com.vaadin.flow.component.charts.testbench.ChartElement; import com.vaadin.testbench.ElementQuery; import com.vaadin.testbench.TestBenchElement; @@ -17,8 +15,9 @@ public abstract class AbstractChartIT extends AbstractComponentIT { - @Before - public void setup() { + @Override + public void setup() throws Exception { + super.setup(); open(); } diff --git a/vaadin-flow-components-shared-parent/vaadin-flow-components-test-util/src/main/java/com/vaadin/tests/AbstractComponentIT.java b/vaadin-flow-components-shared-parent/vaadin-flow-components-test-util/src/main/java/com/vaadin/tests/AbstractComponentIT.java index 99aee832717..d0b94b25dd2 100644 --- a/vaadin-flow-components-shared-parent/vaadin-flow-components-test-util/src/main/java/com/vaadin/tests/AbstractComponentIT.java +++ b/vaadin-flow-components-shared-parent/vaadin-flow-components-test-util/src/main/java/com/vaadin/tests/AbstractComponentIT.java @@ -15,435 +15,32 @@ */ package com.vaadin.tests; -import java.lang.management.ManagementFactory; -import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.URI; -import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.function.Predicate; -import java.util.logging.Level; -import java.util.stream.Collectors; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.openqa.selenium.By; -import org.openqa.selenium.Dimension; -import org.openqa.selenium.TimeoutException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.logging.LogEntry; -import org.openqa.selenium.logging.LogType; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.support.ui.ExpectedCondition; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.vaadin.flow.testutil.TestPath; -import com.vaadin.testbench.Parameters; -import com.vaadin.testbench.ScreenshotOnFailureRule; -import com.vaadin.testbench.TestBench; -import com.vaadin.testbench.TestBenchTestCase; - -/** - * Base class for Flow component integration tests. - *

- * Extends {@link TestBenchTestCase} directly and manages a headless Chrome - * driver that is reused across all test methods in the same test class. By - * default the driver is started locally; setting {@code -Dtest.use.hub=true} - * routes driver creation to a Selenium Grid endpoint configured via - * {@code -Dtest.hub.url} (default {@code http://localhost:4444/wd/hub}). - *

- * This test setup does not support running tests in parallel. The only way to - * parallelize tests is forking separate JVMs that run one suite at a time, for - * example using {@code failsafe.forkCount}. - *

- * Test classes must be annotated with {@link TestPath} to specify the URL path - * of the test view. - */ -public abstract class AbstractComponentIT extends TestBenchTestCase { - - private static final Logger logger = LoggerFactory - .getLogger(AbstractComponentIT.class); - - private static final boolean REUSE_DRIVER = Boolean - .getBoolean("test.reuseDriver"); - - private static final boolean USE_HUB = Boolean.getBoolean("test.use.hub"); - - private static final String HUB_URL = System.getProperty("test.hub.url", - "http://" + Parameters.getHubHostname() + ":4444/wd/hub"); - - private static final ThreadLocal sharedDriver = new ThreadLocal<>(); - - @Rule - public ScreenshotOnFailureRule screenshotOnFailure = new ScreenshotOnFailureRule( - this, false); - - @BeforeClass - public static void createDriver() { - if (!REUSE_DRIVER) { - return; - } - WebDriver driver = createWebDriver(); - driver.manage().window().setSize(new Dimension(1024, 800)); - driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10)); - sharedDriver.set(driver); - } - - @Before - public void resetDriver() throws Exception { - if (!REUSE_DRIVER) { - WebDriver driver = createWebDriver(); - driver.manage().window().setSize(new Dimension(1024, 800)); - driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(10)); - sharedDriver.set(driver); - } - setDriver(sharedDriver.get()); - getDriver().manage().deleteAllCookies(); - getDriver().navigate().to("about:blank"); - } - - @After - public void quitDriverPerMethod() { - if (!REUSE_DRIVER) { - tryQuitDriver(sharedDriver.get()); - sharedDriver.remove(); - } - } - - @AfterClass - public static void quitDriver() { - if (!REUSE_DRIVER) { - return; - } - tryQuitDriver(sharedDriver.get()); - sharedDriver.remove(); - } - - // ----- Test path and URL resolution ----- - - protected String getTestPath() { - TestPath annotation = getClass().getAnnotation(TestPath.class); - if (annotation == null) { - throw new IllegalStateException( - "The test class should be annotated with @TestPath"); - } - String path = annotation.value(); - if (!path.startsWith("/")) { - path = "/" + path; - } - return path; - } - - protected String getRootURL() { - String host = "localhost"; - if (USE_HUB) { - host = findHostAddress(); - } - return "http://" + host + ":8080"; - } - - private String findHostAddress() { - try { - return NetworkInterface.networkInterfaces() - .filter((networkInterface) -> { - try { - return networkInterface.isUp() - && !networkInterface.isLoopback() - && !networkInterface.isVirtual(); - } catch (SocketException e) { - return false; - } - }).flatMap(NetworkInterface::inetAddresses) - .filter(InetAddress::isSiteLocalAddress) - .map(InetAddress::getHostAddress).findFirst() - .orElseThrow(() -> { - return new RuntimeException( - "No compatible (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) ip address found."); - }); - } catch (SocketException e) { - throw new RuntimeException("Could not find the host name", e); - } - } - - protected String getTestURL(String... parameters) { - return getTestURL(getRootURL(), getTestPath(), parameters); - } - - public static String getTestURL(String rootUrl, String testPath, - String... parameters) { - while (rootUrl.endsWith("/")) { - rootUrl = rootUrl.substring(0, rootUrl.length() - 1); - } - rootUrl = rootUrl + testPath; - - if (parameters != null && parameters.length != 0) { - if (!rootUrl.contains("?")) { - rootUrl += "?"; - } else { - rootUrl += "&"; - } - rootUrl += Arrays.stream(parameters) - .collect(Collectors.joining("&")); - } - - return rootUrl; - } - - protected void open() { - open((String[]) null); - } - - protected void open(String... parameters) { - String url = getTestURL(parameters); - TimeoutException lastTimeout = null; - for (int attempt = 1; attempt <= 3; attempt++) { - try { - getDriver().get(url); - waitForDevServer(); - return; - } catch (TimeoutException e) { - lastTimeout = e; - logger.warn( - "Page load timed out for {} (attempt {}/3), retrying", - url, attempt); - } - } - throw lastTimeout; - } - - // ----- Driver management ----- - private static void tryQuitDriver(WebDriver driver) { - try { - driver.quit(); - } catch (Exception e) { - // Ignore - driver may already be dead - } - } - - private static boolean isJavaInDebugMode() { - return ManagementFactory.getRuntimeMXBean().getInputArguments() - .toString().contains("jdwp"); - } - - private static WebDriver createWebDriver() { - for (int i = 0; i < 3; i++) { - try { - if (USE_HUB) { - return tryCreateRemoteDriver(); - } +public abstract class AbstractComponentIT + extends com.vaadin.flow.testutil.AbstractComponentIT { - return tryCreateChromeDriver(); - } catch (Exception e) { - logger.warn("Unable to create driver on attempt " + i, e); - } - } - throw new RuntimeException( - "Gave up trying to create a driver instance"); + protected int getDeploymentPort() { + return 8080; } - private static ChromeOptions buildChromeOptions() { - ChromeOptions options = new ChromeOptions(); - if (!isJavaInDebugMode()) { - options.addArguments("--headless=new", "--disable-gpu", - "--disable-backgrounding-occluded-windows", - "--no-sandbox", "--disable-dev-shm-usage"); - } - + @Override + protected void updateHeadlessChromeOptions(ChromeOptions chromeOptions) { String extraArgs = System.getenv("TESTBENCH_CHROME_EXTRA_ARGS"); if (extraArgs != null && !extraArgs.isBlank()) { - options.addArguments(extraArgs.split("\\s+")); + chromeOptions.addArguments(extraArgs.split("\\s+")); } String chromeBinary = System.getenv("TESTBENCH_CHROME_BINARY"); if (chromeBinary != null && !chromeBinary.isBlank()) { - options.setBinary(chromeBinary); - } - return options; - } - - private static WebDriver tryCreateChromeDriver() { - ChromeOptions options = buildChromeOptions(); - ChromeDriver chromeDriver = new ChromeDriver(options); - return TestBench.createDriver(chromeDriver); - } - - private static WebDriver tryCreateRemoteDriver() - throws MalformedURLException { - ChromeOptions options = buildChromeOptions(); - RemoteWebDriver remoteDriver = new RemoteWebDriver( - URI.create(HUB_URL).toURL(), options); - return TestBench.createDriver(remoteDriver); - } - - // ----- Test helper methods ----- - - /** - * Waits up to 10s for the given condition to become false. - */ - protected void waitUntilNot(ExpectedCondition condition) { - waitUntilNot(condition, 10); - } - - /** - * Waits the given number of seconds for the given condition to become - * false. - */ - protected void waitUntilNot(ExpectedCondition condition, - long timeoutInSeconds) { - waitUntil(ExpectedConditions.not(condition), timeoutInSeconds); - } - - /** - * Returns true if an element can be found from the driver with given - * selector. - */ - public boolean isElementPresent(By by) { - try { - WebElement element = getDriver().findElement(by); - return element != null; - } catch (Exception e) { - return false; + chromeOptions.setBinary(chromeBinary); } } - /** - * Clicks on the element, using JS. This method is more convenient than - * Selenium {@code findElement(By.id(urlId)).click()}, because Selenium - * method changes scroll position, which is not always needed. - */ - protected void clickElementWithJs(String elementId) { - executeScript(String.format("document.getElementById('%s').click();", - elementId)); - } - - /** - * Clicks on the element, using JS. - */ - protected void clickElementWithJs(WebElement element) { - executeScript("arguments[0].click();", element); - } - - protected void waitForElementPresent(final By by) { - waitUntil(ExpectedConditions.presenceOfElementLocated(by)); + @Override + public void setup() throws Exception { + super.setup(); + // Set a default window size + testBench().resizeViewPortTo(1024, 800); } - - protected void waitForElementNotPresent(final By by) { - waitUntil(input -> input.findElements(by).isEmpty()); - } - - protected void waitForElementVisible(final By by) { - waitUntil(ExpectedConditions.visibilityOfElementLocated(by)); - } - - /** - * Scrolls the page to the element given using javascript. - */ - protected void scrollToElement(WebElement element) { - Objects.requireNonNull(element, - "The element to scroll to should not be null"); - getCommandExecutor().executeScript("arguments[0].scrollIntoView(true);", - element); - } - - /** - * Scrolls the page to the element specified and clicks it. - */ - protected void scrollIntoViewAndClick(WebElement element) { - scrollToElement(element); - element.click(); - } - - /** - * Gets the log entries from the browser that have the given logging level - * or higher. - */ - protected List getLogEntries(Level level) { - getCommandExecutor().waitForVaadin(); - - return getDriver().manage().logs().get(LogType.BROWSER).getAll() - .stream() - .filter(logEntry -> logEntry.getLevel().intValue() >= level - .intValue()) - .filter(logEntry -> !logEntry.getMessage() - .contains("favicon.ico")) - .collect(Collectors.toList()); - } - - private static final String WEB_SOCKET_CONNECTION_ERROR_PREFIX = "WebSocket connection to "; - - /** - * Checks browser's log entries, throws an error for any client-side error - * and logs any client-side warnings. - */ - protected void checkLogsForErrors( - Predicate acceptableMessagePredicate) { - getLogEntries(Level.WARNING).forEach(logEntry -> { - if (logEntry.getMessage().contains( - "Lit is in dev mode. Not recommended for production")) { - return; - } - if ((Objects.equals(logEntry.getLevel(), Level.SEVERE) - || logEntry.getMessage().contains(" 404 ")) - && !logEntry.getMessage() - .contains(WEB_SOCKET_CONNECTION_ERROR_PREFIX) - && !acceptableMessagePredicate - .test(logEntry.getMessage())) { - throw new AssertionError(String - .format("Error message in browser log: %s", logEntry)); - } else { - LoggerFactory.getLogger(AbstractComponentIT.class.getName()) - .warn("This message in browser log console may be a potential error: '{}'", - logEntry); - } - }); - } - - /** - * Checks browser's log entries, throws an error for any client-side error - * and logs any client-side warnings. - */ - protected void checkLogsForErrors() { - checkLogsForErrors(msg -> false); - } - - /** - * If dev server start in progress wait until it's started. Otherwise return - * immediately. - */ - protected void waitForDevServer() { - Object result; - do { - getCommandExecutor().waitForVaadin(); - result = getCommandExecutor().executeScript( - "return window.Vaadin && window.Vaadin.Flow && window.Vaadin.Flow.devServerIsNotLoaded;"); - } while (Boolean.TRUE.equals(result)); - } - - public void blur() { - executeScript( - "!!document.activeElement ? document.activeElement.blur() : 0"); - } - - /** - * Gets a property value from a web element using JavaScript. - */ - public String getProperty(WebElement element, String propertyName) { - Object result = executeScript( - "return arguments[0]." + propertyName + ";", element); - return result == null ? null : String.valueOf(result); - } - } diff --git a/vaadin-renderer-flow-parent/vaadin-renderer-flow-integration-tests/src/test/java/com/vaadin/flow/data/renderer/tests/LitRendererIT.java b/vaadin-renderer-flow-parent/vaadin-renderer-flow-integration-tests/src/test/java/com/vaadin/flow/data/renderer/tests/LitRendererIT.java index 8b02af81d6d..51a464ea762 100644 --- a/vaadin-renderer-flow-parent/vaadin-renderer-flow-integration-tests/src/test/java/com/vaadin/flow/data/renderer/tests/LitRendererIT.java +++ b/vaadin-renderer-flow-parent/vaadin-renderer-flow-integration-tests/src/test/java/com/vaadin/flow/data/renderer/tests/LitRendererIT.java @@ -99,9 +99,7 @@ public void shouldNotInvokeCallableFromEventWhenContentDiscardedAfterRefresh() { @Test public void shouldNotIncludeEventInCallableArguments() { WebElement itemContent = findElement(By.id("content-0")); - executeScript( - "arguments[0].dispatchEvent(new DragEvent('dragstart', { bubbles: true, cancelable: true }))", - itemContent); + drag(itemContent); Assert.assertEquals("event: dragged, item: 0, argument count: 0", getFirstClientCallableLogMessage()); } diff --git a/vaadin-time-picker-flow-parent/vaadin-time-picker-flow-integration-tests/src/test/java/com/vaadin/flow/component/timepicker/tests/TimePickerLocalizationIT.java b/vaadin-time-picker-flow-parent/vaadin-time-picker-flow-integration-tests/src/test/java/com/vaadin/flow/component/timepicker/tests/TimePickerLocalizationIT.java index 6a61726aff3..574ac98477f 100644 --- a/vaadin-time-picker-flow-parent/vaadin-time-picker-flow-integration-tests/src/test/java/com/vaadin/flow/component/timepicker/tests/TimePickerLocalizationIT.java +++ b/vaadin-time-picker-flow-parent/vaadin-time-picker-flow-integration-tests/src/test/java/com/vaadin/flow/component/timepicker/tests/TimePickerLocalizationIT.java @@ -48,6 +48,11 @@ public void init() { $(TimePickerElement.class).waitForFirst(); } + @Override + protected int getDeploymentPort() { + return super.getDeploymentPort(); + } + @Test public void testAllAvailableLocalesWhenValueChangedFromDropDown_stepOneHourAndFormatHourMinute_pickerValuesMatchesBrowserFormatted() { // select locale based on locale string From ef5f8ce8a44fce4ec06cb4e0ea8cbfc3e0022f5b Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 13 May 2026 18:36:26 +0200 Subject: [PATCH 3/4] ci: propagate chromeOptions to Failsafe forked JVMs via systemPropertyVariables Maven -D properties set on the command line belong to the Maven JVM and are NOT automatically forwarded to the JVM processes forked by Failsafe. The correct mechanism is systemPropertyVariables. Add a profile that activates when -DchromeOptions is explicitly passed and injects the value into Failsafe systemPropertyVariables so that TestBench's Parameters.getChromeOptions() receives the flags in the forked test JVM. The GHA its job already passes -DchromeOptions=--no-sandbox,--disable-dev-shm-usage. This profile makes that flag actually reach Chrome inside the forked JVM. TeamCity is unaffected because it does not pass -DchromeOptions. --- pom.xml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pom.xml b/pom.xml index a10e5fc7f98..54fe73728e4 100644 --- a/pom.xml +++ b/pom.xml @@ -605,6 +605,35 @@ + + + ci-chrome-options + + + chromeOptions + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${chromeOptions} + + + + + + + merged-it From f32c33c15a94c4e298e37b23e7a2627687ed2ab8 Mon Sep 17 00:00:00 2001 From: Manolo Carrasco Date: Wed, 13 May 2026 18:39:29 +0200 Subject: [PATCH 4/4] fix: remove double-dash from XML comment in pom.xml (invalid XML) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 54fe73728e4..91db87ac4e0 100644 --- a/pom.xml +++ b/pom.xml @@ -607,11 +607,11 @@ + is explicitly set (e.g. no-sandbox,disable-dev-shm-usage in GHA). --> ci-chrome-options