Skip to content

test(blocs): Restore bloc property tests with @GenerateMocks scaffolding #14

test(blocs): Restore bloc property tests with @GenerateMocks scaffolding

test(blocs): Restore bloc property tests with @GenerateMocks scaffolding #14

Workflow file for this run

name: Tests
# ========================================================================
# Workflow Triggers Configuration
# ========================================================================
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
# Cancel in-flight runs for the same ref when a newer commit arrives.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
cache: true
- name: Prepare .env from template
run: cp .env.example .env
- name: Install dependencies
run: flutter pub get
- name: Verify formatting
run: dart format --set-exit-if-changed .
continue-on-error: false
- name: Analyze code
run: flutter analyze
continue-on-error: false
- name: Run tests
timeout-minutes: 15
run: flutter test --coverage --timeout=5m
continue-on-error: false
# ========================================================================
# Coverage Processing - Critical Step
# ========================================================================
- name: Install lcov
run: |
sudo apt-get update
sudo apt-get install -y lcov
- name: Remove generated files from coverage
run: |
# Remove generated and non-testable files to match codecov.yml
lcov --remove coverage/lcov.info --ignore-errors unused \
'**/*.g.dart' \
'**/*.freezed.dart' \
'**/*.config.dart' \
'**/generated/**' \
'**/l10n/**' \
'**/main.dart' \
'test/**' \
'**/*_test.dart' \
'**/test_helpers.dart' \
'**/test_fixtures.dart' \
-o coverage/lcov.info
echo "✅ Removed generated files from coverage report"
- name: Generate coverage report
run: |
genhtml coverage/lcov.info -o coverage/html --no-function-coverage
- name: Calculate coverage
id: coverage
run: |
COVERAGE=$(lcov --summary coverage/lcov.info 2>&1 | grep "lines.*:" | head -1 | grep -oP '\d+\.\d+%' | head -1 | sed 's/%//' || echo "0")
echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT
echo "Coverage: ${COVERAGE}%"
- name: Calculate coverage by layer
id: layer_coverage
run: |
chmod +x scripts/linux/testing/calculate_layer_coverage.sh
./scripts/linux/testing/calculate_layer_coverage.sh coverage/lcov.info > /tmp/layer_coverage.txt
# Read key=value pairs and write to GITHUB_OUTPUT
while IFS='=' read -r key value; do
echo "${key}=${value}" >> $GITHUB_OUTPUT
done < /tmp/layer_coverage.txt
cat /tmp/layer_coverage.txt
# Coverage thresholds are disabled while the app is still in active
# development (features land faster than tests right now). The job
# below still measures coverage and reports it on PRs — re-enable the
# gate once feature work stabilises by uncommenting this step.
#
# Targets when re-enabled: total 80%, domain 100%, data 90%,
# presentation 80%, core 80%.
#
# - name: Check coverage threshold
# run: |
# COVERAGE=${{ steps.coverage.outputs.coverage }}
# DOMAIN_COV=${{ steps.layer_coverage.outputs.domain }}
# DATA_COV=${{ steps.layer_coverage.outputs.data }}
# PRESENTATION_COV=${{ steps.layer_coverage.outputs.presentation }}
# CORE_COV=${{ steps.layer_coverage.outputs.core }}
#
# echo "Checking thresholds..."
# FAILED=0
#
# check_threshold() {
# local name=$1
# local current=$2
# local target=$3
# if (( $(echo "$current < $target" | bc -l) )); then
# echo "❌ $name: ${current}% < ${target}%"
# return 1
# else
# echo "✅ $name: ${current}% >= ${target}%"
# return 0
# fi
# }
#
# check_threshold "Total Coverage" "$COVERAGE" 80 || exit 1
# check_threshold "Domain Layer" "$DOMAIN_COV" 100 || FAILED=1
# check_threshold "Data Layer" "$DATA_COV" 90 || FAILED=1
# check_threshold "Presentation Layer" "$PRESENTATION_COV" 80 || FAILED=1
# check_threshold "Core Layer" "$CORE_COV" 80 || FAILED=1
#
# if [ $FAILED -eq 1 ]; then
# echo "⚠️ One or more architectural layers did not meet coverage targets."
# exit 1
# fi
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
- name: Upload coverage HTML report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/html
retention-days: 30
- name: Comment PR with coverage
if: github.event_name == 'pull_request'
uses: marocchino/sticky-pull-request-comment@v2
with:
recreate: true
message: |
## 📊 Test Coverage Report
**Coverage:** ${{ steps.coverage.outputs.coverage }}%
**Status:** ${{ steps.coverage.outputs.coverage >= 80 && '✅ Passing' || '❌ Failing' }}
Coverage report is available in the [workflow artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).
### Coverage by Layer
| Layer | Coverage | Target | Status |
|-------|----------|--------|--------|
| **Domain** | ${{ steps.layer_coverage.outputs.domain_display }} | 100% | ${{ steps.layer_coverage.outputs.domain >= 100 && '✅' || '❌' }} |
| **Data** | ${{ steps.layer_coverage.outputs.data_display }} | 90% | ${{ steps.layer_coverage.outputs.data >= 90 && '✅' || '❌' }} |
| **Presentation** | ${{ steps.layer_coverage.outputs.presentation_display }} | 80% | ${{ steps.layer_coverage.outputs.presentation >= 80 && '✅' || '❌' }} |
| **Core** | ${{ steps.layer_coverage.outputs.core_display }} | 80% | ${{ steps.layer_coverage.outputs.core >= 80 && '✅' || '❌' }} |