Add Vitest frontend tests to CI with JUnit reporting and coverage (#2151)

Co-authored-by: acx10 <acx10@users.noreply.github.com>
This commit is contained in:
ACX
2026-01-04 22:55:13 -07:00
committed by GitHub
parent e8de8adf6f
commit cee7e96c63
7 changed files with 255 additions and 19 deletions

View File

@@ -20,31 +20,20 @@ jobs:
base_ref: 'origin/develop'
head_ref: 'HEAD'
build-and-push:
backend-tests:
name: Backend Tests
needs: [ migration-check ]
if: needs.migration-check.result == 'success' || needs.migration-check.result == 'skipped'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
checks: write
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v6
with:
fetch-depth: 0
# ----------------------------------------
# Environment setup
# ----------------------------------------
- name: Set Up QEMU for Multi-Arch Builds
uses: docker/setup-qemu-action@v3
- name: Set Up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set Up JDK 21
uses: actions/setup-java@v5
@@ -53,9 +42,6 @@ jobs:
distribution: 'temurin'
cache: gradle
# ----------------------------------------
# Backend tests
# ----------------------------------------
- name: Execute Backend Tests
id: backend_tests
working-directory: ./booklore-api
@@ -75,7 +61,7 @@ jobs:
uses: actions/upload-artifact@v6
if: always()
with:
name: test-reports
name: backend-test-reports
path: |
booklore-api/build/reports/tests/
booklore-api/build/test-results/
@@ -87,6 +73,86 @@ jobs:
echo "❌ Backend tests failed"
exit 1
frontend-tests:
name: Frontend Tests
needs: [ migration-check ]
if: needs.migration-check.result == 'success' || needs.migration-check.result == 'skipped'
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v6
- name: Set Up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: booklore-ui/package-lock.json
- name: Install Frontend Dependencies
working-directory: ./booklore-ui
run: npm ci --force
- name: Execute Frontend Tests
id: frontend_tests
working-directory: ./booklore-ui
run: |
echo "Running frontend tests..."
npx ng test
continue-on-error: true
- name: Publish Frontend Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: booklore-ui/test-results/vitest-results.xml
check_name: Frontend Test Results
- name: Upload Frontend Test Reports
uses: actions/upload-artifact@v6
if: always()
with:
name: frontend-test-reports
path: |
booklore-ui/test-results/vitest-results.xml
retention-days: 30
- name: Validate Frontend Test Results
if: steps.frontend_tests.outcome == 'failure'
run: |
echo "❌ Frontend tests failed"
exit 1
build-and-push:
name: Build and Push Container
needs: [ backend-tests, frontend-tests ]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout Repository
uses: actions/checkout@v6
with:
fetch-depth: 0
# ----------------------------------------
# Environment setup
# ----------------------------------------
- name: Set Up QEMU for Multi-Arch Builds
uses: docker/setup-qemu-action@v3
- name: Set Up Docker Buildx
uses: docker/setup-buildx-action@v3
# ----------------------------------------
# Image tagging
# ----------------------------------------

View File

@@ -69,6 +69,21 @@ jobs:
distribution: 'temurin'
cache: 'gradle'
- name: Set Up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: booklore-ui/package-lock.json
- name: Install Frontend Dependencies
working-directory: ./booklore-ui
run: npm ci --force
- name: Execute Frontend Tests
working-directory: ./booklore-ui
run: npm run test:ci
- name: Retrieve Latest Master Version Tag
id: get_version
run: |
@@ -166,4 +181,3 @@ jobs:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release edit ${{ env.new_tag }} --draft=true

1
.gitignore vendored
View File

@@ -42,5 +42,6 @@ out/
local/
### Dev config, books, and data ###
booklore-ui/test-results/
booklore-api/src/main/resources/application-local.yaml
/shared/

View File

@@ -106,7 +106,10 @@
"builder": "@angular/build:extract-i18n"
},
"test": {
"builder": "@angular/build:unit-test"
"builder": "@angular/build:unit-test",
"options": {
"runnerConfig": "vitest-base.config.ts"
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",

View File

@@ -51,6 +51,7 @@
"@types/jasmine": "^5.1.13",
"@types/node": "^25.0.3",
"@types/showdown": "^2.0.6",
"@vitest/coverage-v8": "^4.0.16",
"angular-eslint": "^21.1.0",
"autoprefixer": "^10.4.23",
"eslint": "^9.39.2",
@@ -1170,6 +1171,16 @@
"node": ">=6.9.0"
}
},
"node_modules/@bcoe/v8-coverage": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",
"integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@colors/colors": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
@@ -4942,6 +4953,38 @@
"vite": "^6.0.0 || ^7.0.0"
}
},
"node_modules/@vitest/coverage-v8": {
"version": "4.0.16",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.16.tgz",
"integrity": "sha512-2rNdjEIsPRzsdu6/9Eq0AYAzYdpP6Bx9cje9tL3FE5XzXRQF1fNU9pe/1yE8fCrS0HD+fBtt6gLPh6LI57tX7A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^1.0.2",
"@vitest/utils": "4.0.16",
"ast-v8-to-istanbul": "^0.3.8",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
"istanbul-lib-source-maps": "^5.0.6",
"istanbul-reports": "^3.2.0",
"magicast": "^0.5.1",
"obug": "^2.1.1",
"std-env": "^3.10.0",
"tinyrainbow": "^3.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "4.0.16",
"vitest": "4.0.16"
},
"peerDependenciesMeta": {
"@vitest/browser": {
"optional": true
}
}
},
"node_modules/@vitest/expect": {
"version": "4.0.16",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.16.tgz",
@@ -5354,6 +5397,25 @@
"node": ">=12"
}
},
"node_modules/ast-v8-to-istanbul": {
"version": "0.3.10",
"resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.10.tgz",
"integrity": "sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.31",
"estree-walker": "^3.0.3",
"js-tokens": "^9.0.1"
}
},
"node_modules/ast-v8-to-istanbul/node_modules/js-tokens": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
"dev": true,
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.4.23",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
@@ -7906,6 +7968,13 @@
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true,
"license": "MIT"
},
"node_modules/htmlparser2": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz",
@@ -8347,6 +8416,50 @@
"node": ">=10"
}
},
"node_modules/istanbul-lib-report": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
"integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"istanbul-lib-coverage": "^3.0.0",
"make-dir": "^4.0.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/istanbul-lib-source-maps": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
"integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.23",
"debug": "^4.1.1",
"istanbul-lib-coverage": "^3.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/istanbul-reports": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
"integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"html-escaper": "^2.0.0",
"istanbul-lib-report": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/jasmine-core": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.13.0.tgz",
@@ -9309,6 +9422,34 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/magicast": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz",
"integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@babel/types": "^7.28.5",
"source-map-js": "^1.2.1"
}
},
"node_modules/make-dir": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
"integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.5.3"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-fetch-happen": {
"version": "15.0.3",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz",

View File

@@ -55,6 +55,7 @@
"@types/jasmine": "^5.1.13",
"@types/node": "^25.0.3",
"@types/showdown": "^2.0.6",
"@vitest/coverage-v8": "^4.0.16",
"angular-eslint": "^21.1.0",
"autoprefixer": "^10.4.23",
"eslint": "^9.39.2",

View File

@@ -0,0 +1,10 @@
import {defineConfig} from 'vitest/config';
export default defineConfig({
test: {
reporters: [
['default', {summary: false}],
['junit', {outputFile: 'test-results/vitest-results.xml'}]
]
},
});