From 31842f771459cdcb88b655183712dc55858271a3 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Wed, 20 May 2026 19:58:27 +0200 Subject: [PATCH] Ci server custom jest reporter (#20765) # Introduction Only display failing unit tests trace in the ci-server server-test jobs So it's possible to identify what unit test are failing without having to re run them locally --- .../jest-failures-only-reporter.js | 88 +++++++++++++++++++ packages/twenty-server/jest.config.mjs | 3 + 2 files changed, 91 insertions(+) create mode 100644 packages/twenty-server/jest-failures-only-reporter.js diff --git a/packages/twenty-server/jest-failures-only-reporter.js b/packages/twenty-server/jest-failures-only-reporter.js new file mode 100644 index 0000000000..4b652da3e3 --- /dev/null +++ b/packages/twenty-server/jest-failures-only-reporter.js @@ -0,0 +1,88 @@ +class JestFailuresOnlyReporter { + constructor() { + this._failures = []; + } + + onTestResult(_test, testResult) { + if (testResult.numFailingTests === 0 && !testResult.testExecError) { + return; + } + + const relativePath = testResult.testFilePath.replace( + process.cwd() + '/', + '', + ); + + process.stderr.write(`\n\x1b[31mFAIL\x1b[0m ${relativePath}\n`); + + if (testResult.failureMessage) { + process.stderr.write(testResult.failureMessage + '\n'); + } + + if (testResult.testExecError) { + process.stderr.write( + `\n Runtime error: ${testResult.testExecError.message}\n`, + ); + } + + this._failures.push({ path: relativePath, testResult }); + } + + onRunComplete(_testContexts, aggregatedResults) { + const { + numPassedTestSuites, + numFailedTestSuites, + numTotalTestSuites, + numPassedTests, + numFailedTests, + numTotalTests, + } = aggregatedResults; + + process.stderr.write('\n'); + + if (this._failures.length > 0) { + process.stderr.write( + `\x1b[31m${'='.repeat(60)}\x1b[0m\n` + + `\x1b[31m FAILED TEST SUITES SUMMARY\x1b[0m\n` + + `\x1b[31m${'='.repeat(60)}\x1b[0m\n\n`, + ); + + for (const failure of this._failures) { + process.stderr.write(` \x1b[31m✕\x1b[0m ${failure.path}\n`); + + const failedTests = failure.testResult.testResults.filter( + (result) => result.status === 'failed', + ); + + for (const failedTest of failedTests) { + process.stderr.write(` \x1b[31m→\x1b[0m ${failedTest.fullName}\n`); + } + + process.stderr.write('\n'); + } + + process.stderr.write(`\x1b[31m${'='.repeat(60)}\x1b[0m\n\n`); + } + + const suiteSummary = + `Test Suites: ` + + (numFailedTestSuites > 0 + ? `\x1b[31m${numFailedTestSuites} failed\x1b[0m, ` + : '') + + (numPassedTestSuites > 0 + ? `\x1b[32m${numPassedTestSuites} passed\x1b[0m, ` + : '') + + `${numTotalTestSuites} total`; + + const testSummary = + `Tests: ` + + (numFailedTests > 0 ? `\x1b[31m${numFailedTests} failed\x1b[0m, ` : '') + + (numPassedTests > 0 ? `\x1b[32m${numPassedTests} passed\x1b[0m, ` : '') + + `${numTotalTests} total`; + + process.stderr.write(suiteSummary + '\n'); + process.stderr.write(testSummary + '\n'); + } +} + +module.exports = JestFailuresOnlyReporter; diff --git a/packages/twenty-server/jest.config.mjs b/packages/twenty-server/jest.config.mjs index 450340282d..1a39609770 100644 --- a/packages/twenty-server/jest.config.mjs +++ b/packages/twenty-server/jest.config.mjs @@ -1,12 +1,15 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); +const isCI = process.env.CI === 'true'; + const jestConfig = { // For more information please have a look to official docs https://jestjs.io/docs/configuration/#prettierpath-string // Prettier v3 should be supported in jest v30 https://github.com/jestjs/jest/releases/tag/v30.0.0-alpha.1 prettierPath: null, // to enable logs, comment out the following line silent: true, + ...(isCI && { reporters: ['./jest-failures-only-reporter.js'] }), errorOnDeprecated: true, clearMocks: true, displayName: 'twenty-server',