mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 12:50:09 +00:00
d67915b7bc
Update the workflow to extract test statistics from vitest's default reporter output in test-output.txt, replacing the previous JSON reporter parsing. This ensures test metrics are still collected and reported even when the JSON reporter is not used.
243 lines
9.3 KiB
YAML
243 lines
9.3 KiB
YAML
name: Backend Tests
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
workflow_call:
|
|
inputs:
|
|
send_notification:
|
|
description: "Whether to send Discord notification"
|
|
type: boolean
|
|
default: true
|
|
secrets:
|
|
WEBHOOK_URL:
|
|
description: "Discord webhook URL"
|
|
required: false
|
|
outputs:
|
|
success:
|
|
description: "Whether tests passed"
|
|
value: ${{ jobs.test.outputs.success }}
|
|
total:
|
|
description: "Total number of tests"
|
|
value: ${{ jobs.test.outputs.total }}
|
|
passed:
|
|
description: "Number of passed tests"
|
|
value: ${{ jobs.test.outputs.passed }}
|
|
failed:
|
|
description: "Number of failed tests"
|
|
value: ${{ jobs.test.outputs.failed }}
|
|
duration:
|
|
description: "Test duration in seconds"
|
|
value: ${{ jobs.test.outputs.duration }}
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
success: ${{ steps.test-results.outputs.success }}
|
|
total: ${{ steps.test-results.outputs.total }}
|
|
passed: ${{ steps.test-results.outputs.passed }}
|
|
failed: ${{ steps.test-results.outputs.failed }}
|
|
duration: ${{ steps.test-results.outputs.duration }}
|
|
test_files: ${{ steps.test-results.outputs.test_files }}
|
|
test_files_passed: ${{ steps.test-results.outputs.test_files_passed }}
|
|
test_files_failed: ${{ steps.test-results.outputs.test_files_failed }}
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
cache: 'npm'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Run tests
|
|
id: run-tests
|
|
continue-on-error: true
|
|
run: |
|
|
START_TIME=$(date +%s)
|
|
npm test 2>&1 | tee test-output.txt
|
|
TEST_EXIT_CODE=${PIPESTATUS[0]}
|
|
END_TIME=$(date +%s)
|
|
echo "exit_code=$TEST_EXIT_CODE" >> $GITHUB_OUTPUT
|
|
echo "start_time=$(date -u +"%H:%M:%S")" >> $GITHUB_OUTPUT
|
|
echo "elapsed=$((END_TIME - START_TIME))" >> $GITHUB_OUTPUT
|
|
|
|
- name: Parse test results
|
|
id: test-results
|
|
if: always()
|
|
run: |
|
|
# Parse the test output from vitest's default reporter
|
|
if [ -f test-output.txt ]; then
|
|
# Extract test file count (e.g., "Test Files 81 passed (81)")
|
|
TEST_FILES_LINE=$(grep -E "Test Files\s+" test-output.txt || echo "")
|
|
if [ -n "$TEST_FILES_LINE" ]; then
|
|
TEST_FILES_PASSED=$(echo "$TEST_FILES_LINE" | grep -oE "[0-9]+ passed" | grep -oE "[0-9]+" || echo "0")
|
|
TEST_FILES_FAILED=$(echo "$TEST_FILES_LINE" | grep -oE "[0-9]+ failed" | grep -oE "[0-9]+" || echo "0")
|
|
TEST_FILES=$(echo "$TEST_FILES_LINE" | grep -oE "\([0-9]+\)" | grep -oE "[0-9]+" || echo "0")
|
|
else
|
|
TEST_FILES_PASSED=0
|
|
TEST_FILES_FAILED=0
|
|
TEST_FILES=0
|
|
fi
|
|
|
|
# Extract test count (e.g., "Tests 708 passed (708)")
|
|
TESTS_LINE=$(grep -E "^\s*Tests\s+" test-output.txt || echo "")
|
|
if [ -n "$TESTS_LINE" ]; then
|
|
PASSED=$(echo "$TESTS_LINE" | grep -oE "[0-9]+ passed" | grep -oE "[0-9]+" || echo "0")
|
|
FAILED=$(echo "$TESTS_LINE" | grep -oE "[0-9]+ failed" | grep -oE "[0-9]+" || echo "0")
|
|
TOTAL=$(echo "$TESTS_LINE" | grep -oE "\([0-9]+\)" | grep -oE "[0-9]+" || echo "0")
|
|
else
|
|
PASSED=0
|
|
FAILED=0
|
|
TOTAL=0
|
|
fi
|
|
|
|
# Extract duration (e.g., "Duration 10.44s")
|
|
DURATION_LINE=$(grep -E "Duration\s+" test-output.txt || echo "")
|
|
if [ -n "$DURATION_LINE" ]; then
|
|
DURATION=$(echo "$DURATION_LINE" | grep -oE "[0-9]+\.[0-9]+s" | head -1 | sed 's/s//' || echo "0")
|
|
else
|
|
DURATION="${{ steps.run-tests.outputs.elapsed }}"
|
|
fi
|
|
|
|
# Determine success based on exit code and failed count
|
|
if [ "${{ steps.run-tests.outputs.exit_code }}" = "0" ] && [ "${FAILED:-0}" = "0" ]; then
|
|
SUCCESS="true"
|
|
else
|
|
SUCCESS="false"
|
|
fi
|
|
else
|
|
TOTAL=0
|
|
PASSED=0
|
|
FAILED=1
|
|
TEST_FILES=0
|
|
TEST_FILES_PASSED=0
|
|
TEST_FILES_FAILED=1
|
|
DURATION=0
|
|
SUCCESS="false"
|
|
fi
|
|
|
|
# Set defaults for empty values
|
|
TOTAL=${TOTAL:-0}
|
|
PASSED=${PASSED:-0}
|
|
FAILED=${FAILED:-0}
|
|
TEST_FILES=${TEST_FILES:-0}
|
|
TEST_FILES_PASSED=${TEST_FILES_PASSED:-0}
|
|
TEST_FILES_FAILED=${TEST_FILES_FAILED:-0}
|
|
DURATION=${DURATION:-0}
|
|
|
|
echo "total=$TOTAL" >> $GITHUB_OUTPUT
|
|
echo "passed=$PASSED" >> $GITHUB_OUTPUT
|
|
echo "failed=$FAILED" >> $GITHUB_OUTPUT
|
|
echo "duration=$DURATION" >> $GITHUB_OUTPUT
|
|
echo "success=$SUCCESS" >> $GITHUB_OUTPUT
|
|
echo "test_files=$TEST_FILES" >> $GITHUB_OUTPUT
|
|
echo "test_files_passed=$TEST_FILES_PASSED" >> $GITHUB_OUTPUT
|
|
echo "test_files_failed=$TEST_FILES_FAILED" >> $GITHUB_OUTPUT
|
|
|
|
echo "## 🧪 Test Results" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
if [ "$SUCCESS" = "true" ]; then
|
|
echo "### ✅ All tests passed!" >> $GITHUB_STEP_SUMMARY
|
|
else
|
|
echo "### ❌ Some tests failed" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Test Files | $TEST_FILES_PASSED passed ($TEST_FILES) |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Tests | $PASSED passed ($TOTAL) |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Duration | ${DURATION}s |" >> $GITHUB_STEP_SUMMARY
|
|
|
|
- name: Send Discord notification
|
|
if: always() && (inputs.send_notification != false)
|
|
env:
|
|
WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }}
|
|
run: |
|
|
if [ -z "$WEBHOOK_URL" ]; then
|
|
echo "No webhook URL provided, skipping notification"
|
|
exit 0
|
|
fi
|
|
|
|
SUCCESS="${{ steps.test-results.outputs.success }}"
|
|
TOTAL="${{ steps.test-results.outputs.total }}"
|
|
PASSED="${{ steps.test-results.outputs.passed }}"
|
|
FAILED="${{ steps.test-results.outputs.failed }}"
|
|
DURATION="${{ steps.test-results.outputs.duration }}"
|
|
TEST_FILES="${{ steps.test-results.outputs.test_files }}"
|
|
TEST_FILES_PASSED="${{ steps.test-results.outputs.test_files_passed }}"
|
|
TEST_FILES_FAILED="${{ steps.test-results.outputs.test_files_failed }}"
|
|
|
|
if [ "$SUCCESS" = "true" ]; then
|
|
COLOR=5763719
|
|
TITLE="✅ Tests Passed"
|
|
DESCRIPTION="All tests completed successfully for **ReadMeABook**"
|
|
TEST_FILES_VALUE="$TEST_FILES_PASSED passed ($TEST_FILES)"
|
|
TESTS_VALUE="$PASSED passed ($TOTAL)"
|
|
else
|
|
COLOR=15548997
|
|
TITLE="❌ Tests Failed"
|
|
DESCRIPTION="Some tests failed for **ReadMeABook**"
|
|
TEST_FILES_VALUE="$TEST_FILES_PASSED passed, $TEST_FILES_FAILED failed ($TEST_FILES)"
|
|
TESTS_VALUE="$PASSED passed, $FAILED failed ($TOTAL)"
|
|
fi
|
|
|
|
curl -H "Content-Type: application/json" \
|
|
-X POST \
|
|
-d "{
|
|
\"embeds\": [{
|
|
\"title\": \"$TITLE\",
|
|
\"description\": \"$DESCRIPTION\",
|
|
\"color\": $COLOR,
|
|
\"fields\": [
|
|
{
|
|
\"name\": \"📁 Test Files\",
|
|
\"value\": \"\`$TEST_FILES_VALUE\`\",
|
|
\"inline\": true
|
|
},
|
|
{
|
|
\"name\": \"🧪 Tests\",
|
|
\"value\": \"\`$TESTS_VALUE\`\",
|
|
\"inline\": true
|
|
},
|
|
{
|
|
\"name\": \"⏱️ Duration\",
|
|
\"value\": \"\`${DURATION}s\`\",
|
|
\"inline\": true
|
|
},
|
|
{
|
|
\"name\": \"🌿 Branch\",
|
|
\"value\": \"\`${{ github.ref_name }}\`\",
|
|
\"inline\": true
|
|
},
|
|
{
|
|
\"name\": \"📋 Commit\",
|
|
\"value\": \"[\`$(echo ${{ github.sha }} | cut -c1-7)\`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})\",
|
|
\"inline\": true
|
|
},
|
|
{
|
|
\"name\": \"🔗 Workflow\",
|
|
\"value\": \"[View Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\",
|
|
\"inline\": true
|
|
}
|
|
],
|
|
\"footer\": {
|
|
\"text\": \"ReadMeABook CI/CD • Test Suite\"
|
|
},
|
|
\"timestamp\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"
|
|
}]
|
|
}" \
|
|
"$WEBHOOK_URL"
|
|
|
|
- name: Fail if tests failed
|
|
if: steps.test-results.outputs.success != 'true'
|
|
run: exit 1
|