From 95f669d0fd78967091cd27fab0f5ae175c76df84 Mon Sep 17 00:00:00 2001 From: Abimael Martell Date: Thu, 26 Feb 2026 10:37:58 -0800 Subject: [PATCH] ci(tests): add JUnit XML reporting for test server (#2918) * ci(tests): add JUnit XML reporting for test server CI Add jest-junit reporter to produce JUnit XML test results, and configure the test-server workflow to upload reports as artifacts and render them in the GitHub PR checks UI via dorny/test-reporter. * ci(tests): remove dorny/test-reporter in favor of Blacksmith auto-detection Blacksmith automatically detects and parses JUnit XML files written to disk during the job. The jest-junit reporter already writes the file, so the dorny/test-reporter step and artifact upload are unnecessary. * ci(tests): restore dorny/test-reporter and artifact upload Blacksmith auto-detects JUnit XML files on disk (no config needed), but didn't trigger on the first run. Restore the dorny reporter and artifact upload so we get GitHub check annotations either way. * ci(tests): match original working JUnit config from PR #2462 Align jest-junit and dorny/test-reporter config to match the original implementation that worked with Blacksmith: use /test-results, addFileAttribute, suiteNameTemplate, and fail-on-error: true. --- .github/workflows/test-server.yml | 9 +++++++++ apps/api/jest.config.ts | 12 ++++++++++++ apps/api/package.json | 1 + apps/api/pnpm-lock.yaml | 26 ++++++++++++++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/.github/workflows/test-server.yml b/.github/workflows/test-server.yml index 078b4831d..20259d02c 100644 --- a/.github/workflows/test-server.yml +++ b/.github/workflows/test-server.yml @@ -203,6 +203,15 @@ jobs: env: npm_config_ignore_scripts: 'true' # required currently to prevent re-building cached native lib + - name: Publish test report + if: always() + uses: dorny/test-reporter@v1 + with: + name: Test Report (${{ matrix.engine }}, ${{ matrix.proxy }}, ${{ matrix.search }}, ${{ matrix.ai }}) + path: apps/api/test-results/junit.xml + reporter: jest-junit + fail-on-error: true + - name: Copy log files if: always() run: | diff --git a/apps/api/jest.config.ts b/apps/api/jest.config.ts index 027f247f4..e219dd5ba 100644 --- a/apps/api/jest.config.ts +++ b/apps/api/jest.config.ts @@ -8,6 +8,18 @@ const config: JestConfigWithTsJest = { detectOpenHandles: true, openHandlesTimeout: 120000, watchAll: false, + reporters: [ + "default", + [ + "jest-junit", + { + outputDirectory: "/test-results", + outputName: "junit.xml", + addFileAttribute: true, + suiteNameTemplate: "{filepath}", + }, + ], + ], }; export default config; diff --git a/apps/api/package.json b/apps/api/package.json index f846778e3..b026bdad9 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -58,6 +58,7 @@ "@types/tough-cookie": "^4.0.5", "husky": "^9.1.7", "jest": "^30.2.0", + "jest-junit": "^16.0.0", "knip": "^5.70.1", "lint-staged": "^16.1.6", "supertest": "^6.3.3", diff --git a/apps/api/pnpm-lock.yaml b/apps/api/pnpm-lock.yaml index 34311e4a3..c17b345d1 100644 --- a/apps/api/pnpm-lock.yaml +++ b/apps/api/pnpm-lock.yaml @@ -288,6 +288,9 @@ importers: jest: specifier: ^30.2.0 version: 30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.8.3)) + jest-junit: + specifier: ^16.0.0 + version: 16.0.0 knip: specifier: ^5.70.1 version: 5.70.1(@types/node@22.19.1)(typescript@5.8.3) @@ -4135,6 +4138,10 @@ packages: resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-junit@16.0.0: + resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==} + engines: {node: '>=10.12.0'} + jest-leak-detector@30.2.0: resolution: {integrity: sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4481,6 +4488,11 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} @@ -5651,6 +5663,9 @@ packages: resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} engines: {node: '>=4.0.0'} + xml@1.0.1: + resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + xmlbuilder@11.0.1: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} @@ -9914,6 +9929,13 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + jest-junit@16.0.0: + dependencies: + mkdirp: 1.0.4 + strip-ansi: 6.0.1 + uuid: 8.3.2 + xml: 1.0.1 + jest-leak-detector@30.2.0: dependencies: '@jest/get-type': 30.1.0 @@ -10365,6 +10387,8 @@ snapshots: minipass@7.1.2: {} + mkdirp@1.0.4: {} + module-details-from-path@1.0.4: {} ms@2.0.0: {} @@ -11514,6 +11538,8 @@ snapshots: sax: 1.4.1 xmlbuilder: 11.0.1 + xml@1.0.1: {} + xmlbuilder@11.0.1: {} xmlchars@2.2.0: {}