diff --git a/.github/workflows/frontend-pr-workflow-v1.yml b/.github/workflows/frontend-pr-workflow-v1.yml index 96ced1f..41d4830 100644 --- a/.github/workflows/frontend-pr-workflow-v1.yml +++ b/.github/workflows/frontend-pr-workflow-v1.yml @@ -296,7 +296,6 @@ jobs: unit-tests: name: 🧪 Unit Tests if: inputs.run-unit-tests - needs: build runs-on: ${{ fromJSON(inputs.runner) }} timeout-minutes: ${{ inputs.test-timeout }} diff --git a/.github/workflows/frontend-pr-workflow-v2.yml b/.github/workflows/frontend-pr-workflow-v2.yml index 0bcaaa3..1352c0f 100644 --- a/.github/workflows/frontend-pr-workflow-v2.yml +++ b/.github/workflows/frontend-pr-workflow-v2.yml @@ -324,7 +324,6 @@ jobs: unit-tests: name: 🧪 Unit Tests if: inputs.run-unit-tests - needs: build runs-on: ${{ fromJSON(inputs.runner) }} timeout-minutes: ${{ inputs.test-timeout }} diff --git a/.github/workflows/frontend-pr-workflow.yml b/.github/workflows/frontend-pr-workflow.yml index 5f88563..9a1f4a9 100644 --- a/.github/workflows/frontend-pr-workflow.yml +++ b/.github/workflows/frontend-pr-workflow.yml @@ -281,6 +281,7 @@ on: permissions: id-token: write contents: read + checks: write # Concurrency control: Only cancels runs within the SAME PR concurrency: @@ -354,7 +355,6 @@ jobs: unit-tests: name: 🧪 Unit Tests if: inputs.run-unit-tests - needs: build runs-on: ${{ fromJSON(inputs.runner) }} timeout-minutes: ${{ inputs.test-timeout }} @@ -383,10 +383,83 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_TOKEN }} + - name: Install JUnit reporter + run: npm install --no-save --ignore-scripts --legacy-peer-deps jest-junit@^16.0.0 + - name: Run unit tests - run: ${{ inputs.unit-test-command }} + run: ${{ inputs.unit-test-command }} --reporters=default --reporters=jest-junit env: GH_TOKEN: ${{ secrets.GH_TOKEN }} + JEST_JUNIT_OUTPUT_DIR: ./test-results + JEST_JUNIT_OUTPUT_NAME: junit.xml + JEST_JUNIT_CLASSNAME: '{classname}' + JEST_JUNIT_TITLE: '{title}' + + - name: Test Summary + if: always() + run: | + if [ -f test-results/junit.xml ]; then + # Parse JUnit XML for summary stats + TESTS=$(grep -o 'tests="[0-9]*"' test-results/junit.xml | head -1 | grep -o '[0-9]*') + FAILURES=$(grep -o 'failures="[0-9]*"' test-results/junit.xml | head -1 | grep -o '[0-9]*') + ERRORS=$(grep -o 'errors="[0-9]*"' test-results/junit.xml | head -1 | grep -o '[0-9]*') + TIME=$(grep -o 'time="[0-9.]*"' test-results/junit.xml | head -1 | grep -o '[0-9.]*') + PASSED=$((TESTS - FAILURES - ERRORS)) + + if [ "$FAILURES" -gt 0 ] || [ "$ERRORS" -gt 0 ]; then + echo "## ❌ Unit Test Results" >> $GITHUB_STEP_SUMMARY + else + echo "## ✅ Unit Test Results" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Metric | Count |" >> $GITHUB_STEP_SUMMARY + echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| ✅ Passed | $PASSED |" >> $GITHUB_STEP_SUMMARY + echo "| ❌ Failed | $FAILURES |" >> $GITHUB_STEP_SUMMARY + echo "| ⚠️ Errors | $ERRORS |" >> $GITHUB_STEP_SUMMARY + echo "| 📊 Total | $TESTS |" >> $GITHUB_STEP_SUMMARY + echo "| ⏱️ Duration | ${TIME}s |" >> $GITHUB_STEP_SUMMARY + + # List failed tests using awk (POSIX-compatible, no grep -P needed) + if [ "$FAILURES" -gt 0 ] || [ "$ERRORS" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Failed Tests" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + # Extract testcase names that contain or child elements + awk ' + // { in_testcase = 0; } + ' test-results/junit.xml | head -20 >> $GITHUB_STEP_SUMMARY + fi + else + echo "## ⚠️ No test results found" >> $GITHUB_STEP_SUMMARY + echo "JUnit XML was not generated at test-results/junit.xml" >> $GITHUB_STEP_SUMMARY + fi + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results-${{ github.run_id }} + path: test-results/junit.xml + retention-days: 7 + if-no-files-found: warn + + - name: Test Report + if: always() + uses: dorny/test-reporter@v1 + with: + name: Unit Test Results + path: test-results/junit.xml + reporter: jest-junit + fail-on-error: false - name: Upload coverage if: always()