diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3e81ea150324..3feb7ef866dd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -654,6 +654,7 @@ jobs: env: ROCKETCHAT_IMAGE: ghcr.io/${{ needs.release-versions.outputs.lowercase-repo }}/rocket.chat:${{ needs.release-versions.outputs.gh-docker-tag }} ENTERPRISE_LICENSE_RC1: ${{ secrets.ENTERPRISE_LICENSE_RC1 }} + QASE_TESTOPS_JEST_API_TOKEN: ${{ secrets.QASE_TESTOPS_JEST_API_TOKEN }} run: yarn test:integration --image "${ROCKETCHAT_IMAGE}" report-coverage: diff --git a/ee/packages/federation-matrix/jest.config.federation.ts b/ee/packages/federation-matrix/jest.config.federation.ts index f123c918481dd..b1dbdc1d1c83a 100644 --- a/ee/packages/federation-matrix/jest.config.federation.ts +++ b/ee/packages/federation-matrix/jest.config.federation.ts @@ -21,6 +21,27 @@ export default { forceExit: true, // Force Jest to exit after tests complete detectOpenHandles: true, // Detect open handles that prevent Jest from exiting globalTeardown: '/tests/teardown.ts', + // To disable Qase integration, remove this line or comment it out + setupFilesAfterEnv: ['/tests/setup-qase.ts'], verbose: false, silent: false, + reporters: [ + 'default', + ...(process.env.QASE_TESTOPS_JEST_API_TOKEN + ? [ + [ + 'jest-qase-reporter', + { + mode: 'testops', + testops: { + api: { token: process.env.QASE_TESTOPS_JEST_API_TOKEN }, + project: 'RC', + run: { complete: true }, + }, + debug: true, + }, + ] as [string, { [x: string]: unknown }], + ] + : []), + ] as Config['reporters'], } satisfies Config; diff --git a/ee/packages/federation-matrix/tests/end-to-end/room.spec.ts b/ee/packages/federation-matrix/tests/end-to-end/room.spec.ts index 3987331816fd1..56afb52f716c5 100644 --- a/ee/packages/federation-matrix/tests/end-to-end/room.spec.ts +++ b/ee/packages/federation-matrix/tests/end-to-end/room.spec.ts @@ -176,8 +176,6 @@ import { SynapseClient } from '../helper/synapse-client'; config: rc1AdminRequestConfig, }); - console.log('response', response.body); - expect(response.body).toHaveProperty('success', true); expect(response.body).toHaveProperty('message'); diff --git a/ee/packages/federation-matrix/tests/helper/ddp-listener.ts b/ee/packages/federation-matrix/tests/helper/ddp-listener.ts index c71930c80b015..a40b61d883a78 100644 --- a/ee/packages/federation-matrix/tests/helper/ddp-listener.ts +++ b/ee/packages/federation-matrix/tests/helper/ddp-listener.ts @@ -84,7 +84,6 @@ export class DDPListener { return new Promise((resolve, reject) => { // Check if message already exists const existingMessage = this.ephemeralMessages.find((msg) => { - console.log('msg', msg); const contentMatches = msg.msg?.includes(expectedContent); const roomMatches = !roomId || msg.rid === roomId; return contentMatches && roomMatches; @@ -107,7 +106,6 @@ export class DDPListener { const checkMessages = () => { const message = this.ephemeralMessages.find((msg) => { - console.log('msg', msg); const contentMatches = msg.msg?.includes(expectedContent); const roomMatches = !roomId || msg.rid === roomId; return contentMatches && roomMatches; diff --git a/ee/packages/federation-matrix/tests/setup-qase.ts b/ee/packages/federation-matrix/tests/setup-qase.ts new file mode 100644 index 0000000000000..cb6b7619e030b --- /dev/null +++ b/ee/packages/federation-matrix/tests/setup-qase.ts @@ -0,0 +1,107 @@ +/** + * Jest setup file that automatically wraps describe and it/test functions + * to register suites and tests with Qase. + * + * The Qase Jest reporter reports the directory structure up from the tests directory, + * making it not consistent with the test suite structure we currently follow in Qase. + * The solution is to wrap describe and it/test functions to automatically set the suite + * at the very start of the test to what we really want the reporting structure to be. + * + * This file is loaded via setupFilesAfterEnv in jest.config.federation.ts. + * Qase integration is only enabled when QASE_TESTOPS_JEST_API_TOKEN is set. + */ + +import { qase } from 'jest-qase-reporter/jest'; + +const ROOT_SUITE = 'Rocket.Chat Federation Automation'; + +/** + * Stack to track the current suite path hierarchy + */ +const suitePathStack: string[] = []; + +/** + * Store the original Jest describe function before we replace it + */ +const originalDescribe = global.describe; + +/** + * Gets the full suite path including root + */ +function getFullSuitePath(): string { + return [ROOT_SUITE, ...suitePathStack].join('\t'); +} + +/** + * Wraps describe to automatically track suite hierarchy and set suite for tests + */ +function describeImpl(name: string, fn: () => void): void { + suitePathStack.push(name); + const currentPath = getFullSuitePath(); + + originalDescribe(name, () => { + // Add beforeEach to set suite for all tests in this describe block + // This must be called before the test runs so the reporter picks it up + global.beforeEach(() => { + qase.suite(currentPath); + }); + + // Store current it and test wrappers (they might be wrapped by parent describe) + const currentIt = global.it; + const currentTest = global.test; + + // Wrap it() to automatically set suite at the very start + global.it = ((testName: any, fn?: any, timeout?: number) => { + // Handle qase-wrapped test names (qase returns a string) + if (typeof testName === 'string' && fn) { + return currentIt( + testName, + async () => { + // Set suite immediately at the start of the test + qase.suite(currentPath); + // Call the original test function and return the result + return fn(); + }, + timeout, + ); + } + // Handle cases where testName might be a number or other type + return currentIt(testName, fn, timeout); + }) as typeof global.it; + + // Wrap test() to automatically set suite at the very start + global.test = ((testName: any, fn?: any, timeout?: number) => { + if (typeof testName === 'string' && fn) { + return currentTest( + testName, + async () => { + // Set suite immediately at the start of the test + qase.suite(currentPath); + // Call the original test function and return the result + return fn(); + }, + timeout, + ); + } + return currentTest(testName, fn, timeout); + }) as typeof global.test; + + // Execute the describe block + fn(); + + // Restore previous wrappers + global.it = currentIt; + global.test = currentTest; + }); + + suitePathStack.pop(); +} + +// Only apply qase wrapping if the environment variable is set +if (process.env.QASE_TESTOPS_JEST_API_TOKEN) { + // Replace global describe with our wrapper + (global as any).describe = Object.assign(describeImpl, { + skip: (name: string, fn: () => void) => originalDescribe.skip(name, fn), + only: (name: string, fn: () => void) => originalDescribe.only(name, fn), + }) as typeof global.describe; +} diff --git a/package.json b/package.json index e120cba1f7132..673cc91fccad5 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@types/chart.js": "^2.9.41", "@types/js-yaml": "^4.0.9", "@types/node": "~22.16.5", + "jest-qase-reporter": "^2.1.3", "ts-node": "^10.9.2", "turbo": "~2.6.1", "typescript": "~5.9.3" diff --git a/yarn.lock b/yarn.lock index eedc1d0a51b67..26fdc90f6ec3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25698,6 +25698,20 @@ __metadata: languageName: node linkType: hard +"jest-qase-reporter@npm:^2.1.3": + version: 2.1.3 + resolution: "jest-qase-reporter@npm:2.1.3" + dependencies: + lodash.get: "npm:^4.4.2" + lodash.has: "npm:^4.5.2" + qase-javascript-commons: "npm:~2.4.2" + uuid: "npm:^9.0.0" + peerDependencies: + jest: ">=28.0.0" + checksum: 10/1c19643adaffd514674d1dbdc92d6377beb859d4036228133a8ad2dadadbc879bc65b74df5f14ad371b5e269976720da3b787afbfba034ff8be0f1a1792324c7 + languageName: node + linkType: hard + "jest-regex-util@npm:30.0.1": version: 30.0.1 resolution: "jest-regex-util@npm:30.0.1" @@ -26973,6 +26987,13 @@ __metadata: languageName: node linkType: hard +"lodash.has@npm:^4.5.2": + version: 4.5.2 + resolution: "lodash.has@npm:4.5.2" + checksum: 10/35c0862e715bc22528dd3cd34f1e66d25d58f0ecef9a43aa409fb7ddebaf6495cb357ae242f141e4b2325258f4a6bafdd8928255d51f1c0a741ae9b93951c743 + languageName: node + linkType: hard + "lodash.includes@npm:^4.3.0": version: 4.3.0 resolution: "lodash.includes@npm:4.3.0" @@ -32894,6 +32915,7 @@ __metadata: "@types/js-yaml": "npm:^4.0.9" "@types/node": "npm:~22.16.5" "@types/stream-buffers": "npm:^3.0.8" + jest-qase-reporter: "npm:^2.1.3" node-gyp: "npm:^10.2.0" ts-node: "npm:^10.9.2" turbo: "npm:~2.6.1"