diff --git a/.github/workflows/run-nala-default.yml b/.github/workflows/run-nala-default.yml index ddf2f9dd4..85dc8826f 100644 --- a/.github/workflows/run-nala-default.yml +++ b/.github/workflows/run-nala-default.yml @@ -17,17 +17,25 @@ permissions: jobs: run-nala-tests: - name: Running Nala E2E UI Tests + name: Running Nala E2E UI Tests (${{ matrix.lane_id }}) runs-on: ubuntu-latest timeout-minutes: 30 strategy: - # Don't cancel the other shards when one fails — we want the full picture. + # Don't cancel the other lanes when one fails — we want the full picture. fail-fast: false matrix: - node-version: [24.x] - # Split the suite across parallel runner jobs. Playwright's --shard - # deterministically partitions tests, so 4 shards ≈ 4x throughput. - shard: [1, 2, 3, 4] + # Lanes run as parallel runner jobs. The non-color suite is split across + # 4 shards (Playwright's --shard deterministically partitions tests). The + # heavy color-shared blocks are pulled OUT of the shards (each shard lane + # excludes @color) and run in their own serial lane (workers=1) so they + # never cluster on one shard and starve each other's decoration — the + # cause of the recurring color-block waitReady timeouts. + include: + - { lane_id: shard1, node: 24.x, shard: '1/4', exclude: '@color', grep: '', workers: '' } + - { lane_id: shard2, node: 24.x, shard: '2/4', exclude: '@color', grep: '', workers: '' } + - { lane_id: shard3, node: 24.x, shard: '3/4', exclude: '@color', grep: '', workers: '' } + - { lane_id: shard4, node: 24.x, shard: '4/4', exclude: '@color', grep: '', workers: '' } + - { lane_id: color, node: 24.x, shard: '', exclude: '', grep: '@color', workers: '1' } steps: - name: Checkout repository @@ -35,10 +43,10 @@ jobs: with: fetch-depth: 2 - - name: Set up Node.js ${{ matrix.node-version }} + - name: Set up Node.js ${{ matrix.node }} uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: - node-version: ${{ matrix.node-version }} + node-version: ${{ matrix.node }} - name: Cache node_modules uses: actions/cache@v4 @@ -46,13 +54,13 @@ jobs: path: | ~/.npm **/node_modules - key: npm-${{ runner.os }}-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }} + key: npm-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/package-lock.json') }} - name: Cache Playwright browsers uses: actions/cache@v4 with: path: ~/.cache/ms-playwright - key: ms-playwright-${{ runner.os }}-${{ matrix.node-version }}-chromium + key: ms-playwright-${{ runner.os }}-${{ matrix.node }}-chromium - name: Install dependencies run: npm ci --no-fund --prefer-offline @@ -75,7 +83,12 @@ jobs: # PR gate: full cross-browser matrix, sharded across parallel jobs # so wall-clock stays low even with all three browsers. NALA_PROJECTS: --project=express-live-chromium --project=express-live-firefox --project=express-live-webkit - SHARD: ${{ matrix.shard }}/4 + # Sharded lanes: SHARD set + exclude @color. Color lane: no shard, + # grep only @color, workers=1 (serial). + SHARD: ${{ matrix.shard }} + NALA_EXTRA_GREP_INVERT: ${{ matrix.exclude }} + NALA_GREP: ${{ matrix.grep }} + NALA_WORKERS: ${{ matrix.workers }} labels: ${{ join(github.event.pull_request.labels.*.name, ' ') }} branch: ${{ github.event.pull_request.head.ref }} repoName: ${{ github.repository }} @@ -92,7 +105,7 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: nala-test-results-${{ github.run_number }}-shard${{ matrix.shard }} + name: nala-test-results-${{ github.run_number }}-${{ matrix.lane_id }} path: | test-results/ test-html-results/ diff --git a/nala/utils/pr.run.sh b/nala/utils/pr.run.sh index 6f966da35..f7ff72922 100755 --- a/nala/utils/pr.run.sh +++ b/nala/utils/pr.run.sh @@ -3,8 +3,14 @@ set -e TAGS="" REPORTER="" -EXCLUDE_TAGS="--grep-invert nopr" -EXCLUDE_MONITORING="--grep-invert @monitoring" +# Single combined --grep-invert: Playwright applies only the LAST --grep-invert +# flag, so every exclusion must live in one regex. nopr = never run in PR/CI; +# @monitoring = synthetic monitors. Sharded lanes additionally pass +# NALA_EXTRA_GREP_INVERT="@color" to hand the heavy color blocks off to their +# own serial lane (they starve each other's decoration when clustered). +GREP_INVERT="nopr|@monitoring" +[[ -n "$NALA_EXTRA_GREP_INVERT" ]] && GREP_INVERT="${GREP_INVERT}|${NALA_EXTRA_GREP_INVERT}" +EXCLUDE_TAGS="--grep-invert ${GREP_INVERT}" EXIT_STATUS=0 echo "GITHUB_REF: $GITHUB_REF" @@ -74,13 +80,18 @@ done # Remove first pipe if TAGS not empty [[ ! -z "$TAGS" ]] && TAGS="${TAGS:1}" && TAGS="-g $TAGS" +# Positive grep. A lane override (NALA_GREP, e.g. "@color" for the serial color +# lane) takes precedence over PR-label tags. +GREP_ARG="$TAGS" +[[ -n "$NALA_GREP" ]] && GREP_ARG="-g $NALA_GREP" + # Reporter (override if provided) REPORTER=$reporter [[ ! -z "$REPORTER" ]] && REPORTER="--reporter $REPORTER" echo "Running Nala on branch: $FEATURE_BRANCH" echo "Tags: ${TAGS:-"No @tags or annotations on this PR"}" -echo "Run Command: npx playwright test ${TAGS} ${EXCLUDE_TAGS} ${EXCLUDE_MONITORING} ${REPORTER}" +echo "Run Command: npx playwright test ${GREP_ARG} ${EXCLUDE_TAGS} ${REPORTER}" echo -e "\n" echo "*******************************" @@ -102,10 +113,11 @@ SHARD_ARG="" # Run Playwright tests echo "*** Running tests on projects: ${PROJECTS} ${SHARD_ARG:-"(no shard)"} ***" -echo "*** Excluding monitoring tests (@monitoring) ***" +echo "*** grep-invert: ${GREP_INVERT} ***" +[[ -n "$NALA_GREP" ]] && echo "*** grep (include only): ${NALA_GREP} — workers: ${NALA_WORKERS:-config default} ***" npx playwright test \ --config=./playwright.config.cjs \ - ${TAGS} ${EXCLUDE_TAGS} ${EXCLUDE_MONITORING} ${REPORTER} \ + ${GREP_ARG} ${EXCLUDE_TAGS} ${REPORTER} \ ${PROJECTS} ${SHARD_ARG} || EXIT_STATUS=$? # Exit status