Skip to content

Commit e76d670

Browse files
authored
Merge pull request #352 from AET-DevOps26/test/client-unit-tests
Test: add client Vitest unit tests and coverage badge
2 parents 7aa292a + f2a0f68 commit e76d670

37 files changed

Lines changed: 4929 additions & 165 deletions

.github/workflows/ci.yml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,19 @@ jobs:
397397
working-directory: services/client
398398
run: npm run lint
399399

400+
- name: Run Unit Tests
401+
working-directory: services/client
402+
run: npm run test:coverage
403+
404+
- name: Upload Coverage Report
405+
if: always()
406+
uses: actions/upload-artifact@v7
407+
with:
408+
name: coverage-client
409+
path: services/client/coverage/cobertura-coverage.xml
410+
if-no-files-found: ignore
411+
retention-days: 7
412+
400413
- name: Build
401414
working-directory: services/client
402415
run: npm run build
@@ -695,6 +708,8 @@ jobs:
695708
- name: Bring Stack Up
696709
# --wait gates on healthchecks; Keycloak's reports ready only once the
697710
# realm is imported, so no separate realm-polling step is needed.
711+
# The compose client build is istanbul-instrumented, so this always
712+
# collects client-side e2e coverage.
698713
run: docker compose --profile e2e up -d --build --wait --wait-timeout 360
699714

700715
- name: Provision Test User via Keycloak Admin API
@@ -749,9 +764,19 @@ jobs:
749764
run: npm ci
750765

751766
- name: Run Playwright Tests
767+
# test:e2e runs Playwright and then writes the coverage report.
752768
working-directory: services/client
753769
run: npm run test:e2e
754770

771+
- name: Upload E2E Coverage Report
772+
if: always()
773+
uses: actions/upload-artifact@v7
774+
with:
775+
name: coverage-client-e2e
776+
path: services/client/coverage-e2e/cobertura-coverage.xml
777+
if-no-files-found: ignore
778+
retention-days: 7
779+
755780
- name: Upload Playwright Report
756781
if: always()
757782
uses: actions/upload-artifact@v7
@@ -777,7 +802,7 @@ jobs:
777802
# ---------------------------------------------------------------------
778803
coverage-badges:
779804
name: Publish Coverage Badges
780-
needs: [build-spring, build-genai]
805+
needs: [build-spring, build-client, build-genai, e2e]
781806
if: github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
782807
runs-on: ubuntu-latest
783808
permissions:
@@ -807,6 +832,8 @@ jobs:
807832
[artifacts/coverage-knowledgebase-service/jacocoTestReport.xml]=team-bnd-knowledgebase-service-coverage.xml
808833
[artifacts/coverage-qa-service/jacocoTestReport.xml]=team-bnd-qa-service-coverage.xml
809834
[artifacts/coverage-genai/coverage.xml]=team-bnd-genai-coverage.xml
835+
[artifacts/coverage-client/cobertura-coverage.xml]=team-bnd-client-coverage.xml
836+
[artifacts/coverage-client-e2e/cobertura-coverage.xml]=team-bnd-client-e2e-coverage.xml
810837
)
811838
for report in "${!reports[@]}"; do
812839
gist_file="${reports[$report]}"

README.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
[![CI](https://github.com/AET-DevOps26/team-bnd/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/AET-DevOps26/team-bnd/actions/workflows/ci.yml)
44
[![CodeQL](https://github.com/AET-DevOps26/team-bnd/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/AET-DevOps26/team-bnd/actions/workflows/codeql.yml)
5+
56
[![Azure](https://img.shields.io/badge/Live%20on%20Azure-alexandria.ddnss.de-0078D4?logo=terraform&logoColor=white)](https://alexandria.ddnss.de)
67
[![Kubernetes](https://img.shields.io/badge/Live%20on%20Kubernetes-alexandria.stud.k8s.aet.cit.tum.de-326CE5?logo=kubernetes&logoColor=white)](https://alexandria.stud.k8s.aet.cit.tum.de)
78

9+
![user-service coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-user-service-coverage.xml&query=round%28100%20%2A%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20div%20%28number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20%2B%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40missed%29%29%29&label=Spring%20user-service%20Coverage&suffix=%25&color=228B22)
10+
![knowledgebase-service coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-knowledgebase-service-coverage.xml&query=round%28100%20%2A%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20div%20%28number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20%2B%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40missed%29%29%29&label=Spring%20knowledgebase-service%20Coverage&suffix=%25&color=228B22)
11+
![qa-service coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-qa-service-coverage.xml&query=round%28100%20%2A%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20div%20%28number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20%2B%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40missed%29%29%29&label=Spring%20qa-service%20Coverage&suffix=%25&color=228B22)
12+
![genai coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-genai-coverage.xml&query=round%28100%20%2A%20number%28%2Fcoverage%2F%40line-rate%29%29&label=GenAI%20Coverage&suffix=%25&color=228B22)
13+
![client coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-client-coverage.xml&query=round%28100%20%2A%20number%28%2Fcoverage%2F%40line-rate%29%29&label=Client%20Coverage&suffix=%25&color=228B22)
14+
![E2E client coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-client-e2e-coverage.xml&query=round%28100%20%2A%20number%28%2Fcoverage%2F%40line-rate%29%29&label=E2E%20Client%20Coverage&suffix=%25&color=228B22)
15+
816
## Project summary
917

1018
Alexandria is a document management and knowledge extraction platform. Users upload documents, e.g., research papers, reports, manuals, meeting notes, and the system automatically organizes, tags, and summarizes them.
@@ -24,15 +32,6 @@ Alexandria consists of three main subsystems orchestrated via Docker Compose and
2432

2533
Each subsystem lists its responsible team member in the [System Overview](docs/System_Overview.md).
2634

27-
## Test coverage
28-
29-
| Service | Coverage |
30-
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31-
| Spring user-service | ![user-service coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-user-service-coverage.xml&query=round%28100%20%2A%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20div%20%28number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20%2B%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40missed%29%29%29&label=coverage&suffix=%25&color=228B22) |
32-
| Spring knowledgebase-service | ![knowledgebase-service coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-knowledgebase-service-coverage.xml&query=round%28100%20%2A%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20div%20%28number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20%2B%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40missed%29%29%29&label=coverage&suffix=%25&color=228B22) |
33-
| Spring qa-service | ![qa-service coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-qa-service-coverage.xml&query=round%28100%20%2A%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20div%20%28number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40covered%29%20%2B%20number%28%2Freport%2Fcounter%5B%40type%3D%27LINE%27%5D%2F%40missed%29%29%29&label=coverage&suffix=%25&color=228B22) |
34-
| Python GenAI | ![genai coverage](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fgist.githubusercontent.com%2FDoPri%2F81deec5c116cb700b4445f9d9fe1706a%2Fraw%2Fteam-bnd-genai-coverage.xml&query=round%28100%20%2A%20number%28%2Fcoverage%2F%40line-rate%29%29&label=coverage&suffix=%25&color=228B22) |
35-
3635
## Local Quickstart
3736

3837
Our `docker-compose.yml` includes both pre-built image references and local build contexts. You can choose to pull images for instant startup or build them locally.

docker-compose.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ services:
44
build:
55
context: services/client/
66
dockerfile: Dockerfile
7+
args:
8+
# Built istanbul-instrumented so the e2e profile always collects client
9+
# coverage. The published ghcr image is built without this by CI, so the
10+
# k8s and Azure deployments (which pull, never build) stay uninstrumented.
11+
COVERAGE: "true"
712
container_name: client
813
restart: unless-stopped
914
networks:

services/client/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ yarn-error.log*
2727

2828
# Coverage
2929
coverage/
30+
coverage-e2e/
31+
.nyc_output/
3032

3133
# Misc
3234
.Dockerfile.dev

services/client/.nycrc.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"temp-dir": ".nyc_output",
3+
"report-dir": "coverage-e2e",
4+
"reporter": ["cobertura", "html", "text-summary"]
5+
}

services/client/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
# Multi-stage Dockerfile for building and serving the Vite app
44
FROM node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd AS build
5+
# COVERAGE=true instruments the build with istanbul for the e2e coverage run.
6+
ARG COVERAGE=false
7+
ENV COVERAGE=$COVERAGE
58
WORKDIR /app
69
COPY package.json ./
710
COPY package-lock.json* ./

services/client/README.md

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,53 @@ docker compose up -d client --build
9191

9292
## Testing
9393

94-
Playwright is used for end-to-end tests. The tests run against a remote browser served by a Playwright Docker container.
94+
Install dependencies once, from `services/client`:
9595

96-
Include the Playwright server when starting services:
96+
```bash
97+
npm ci
98+
```
99+
100+
### Unit tests (Vitest)
101+
102+
Component and utility tests run in jsdom with Vitest and React Testing Library,
103+
no backend required:
104+
105+
```bash
106+
npm run test:unit # run once
107+
npm run test:unit -- --watch # re-run on change while developing
108+
npm run test:coverage # with a coverage report -> coverage/index.html
109+
```
110+
111+
### End-to-end tests (Playwright)
112+
113+
The e2e suite drives the real built client in a browser. The browser runs inside
114+
a Playwright container (the `e2e` compose profile) and the test process connects
115+
to it from the host. The `client` image is built istanbul-instrumented, so an
116+
e2e run always collects and reports client coverage.
117+
118+
From the repo root, bring the stack up with the `e2e` profile. Build the client
119+
(`--build`) so it is the instrumented image:
120+
121+
```bash
122+
docker compose --profile e2e up -d --build --wait
123+
```
124+
125+
Then run the suite from `services/client`:
97126

98127
```bash
99-
docker compose --profile e2e up
128+
npm run test:e2e # runs Playwright, then writes the coverage report
129+
npm run test:e2e -- --project=chromium # one browser only (faster)
100130
```
101131

102-
Run the tests:
132+
`test:e2e` reports coverage automatically once the run finishes. Outputs:
133+
134+
- `playwright-report/` -- Playwright HTML report (`npx playwright show-report`)
135+
- `coverage-e2e/index.html` -- e2e coverage report (plus a cobertura XML for CI)
136+
137+
Tear the stack down when finished:
103138

104139
```bash
105-
cd services/client
106-
npm run test:e2e
140+
docker compose --profile e2e down -v
107141
```
108142

109143
## Notes

services/client/eslint.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import reactHooks from "eslint-plugin-react-hooks";
44
import globals from "globals";
55

66
export default tseslint.config(
7-
// schema.d.ts is generated from the OpenAPI spec; dist is build output.
8-
{ ignores: ["dist/**", "src/api/schema.d.ts"] },
7+
// schema.d.ts is generated from the OpenAPI spec; dist and coverage are build output.
8+
{ ignores: ["dist/**", "coverage/**", "coverage-e2e/**", ".nyc_output/**", "src/api/schema.d.ts"] },
99

1010
// Application source: strict TypeScript rules plus the core hooks checks.
1111
{

0 commit comments

Comments
 (0)