chore: use custom jest reporter to achieve sane jest logs (#8607)

The default jest log reporter is garbage. Webstorm replaces it with
their own (which is pretty good), but vscode unfortunately uses the
default one.

This PR does the following to the jest reporter

**1. Replace the default reporter with the jest-ci-spec-reporter
reporter.**

The default reporter is hiding console logs and incorrectly rewriting
console history. Now, logs like these:

```
[20:56:16] INFO: ---- DROPPING DATABASE ----
[20:56:17] INFO: ---- DROPPED DATABASE ----
```

will be visible again. The default reporter was showing them for half a
second, then rewrites log history and hides them.

**2. add custom logger to showcase hidden error messages**

Some error messages are hidden and are only displayed at the end of the
test, in a very ugly way. If the test hangs, you might have to wait a
long time to see those errors. This PR makes sure that errors are logged
when they were intended to be logged.

They will not be printed in a pretty way (Webstorm for example prints
them in red and clickable, like a proper error message) - but at least
they will be printed instead of leaving you in the dark

**Override console global in jest setup to hide console log spam**

This turns logs like

```
  console.log
    initPayloadInt done

      at log (helpers/initPayloadInt.ts:27:11)
```
      
 into
 
`initPayloadInt done`

## CI

Yes, CI logs are actually usable now. We no longer have random console
logs floating around! It was horrible!

Finally, you can actually see which console logs belong to which test.

Before:
https://github.com/payloadcms/payload/actions/runs/11241674859/job/31253918825
After:
https://github.com/payloadcms/payload/actions/runs/11242035327/job/31255031760?pr=8607

**BEFORE**
![CleanShot 2024-10-08 at 21 27
23@2x](https://github.com/user-attachments/assets/7c83ced7-b4fd-4e90-95ff-2c240829c3cd)

What test triggered this "ValidationError: The following field is
invalid: filteredRelation" error? Who knows!! Could have been any test.
We will never know...

**AFTER:**

Finally, clarity 

![CleanShot 2024-10-08 at 21 28
15@2x](https://github.com/user-attachments/assets/a259950e-3213-4faa-9f87-e54fd970f6dc)

## Screenshots - Passing database test suite

## Passing database test suite

### Before
![CleanShot 2024-10-08 at 21 07
39@2x](https://github.com/user-attachments/assets/00a07d30-fbeb-4a52-8982-3e0bc198e278)
![CleanShot 2024-10-08 at 21 08
05@2x](https://github.com/user-attachments/assets/0bc02982-83e4-4205-a1e9-0c0277390ab2)

### After
![CleanShot 2024-10-08 at 21 06
52@2x](https://github.com/user-attachments/assets/cd1e6ac1-17c0-4859-a374-2176e698784e)

## Screenshots - Failing test

### Before - that's where it hangs
![CleanShot 2024-10-08 at 21 09
52@2x](https://github.com/user-attachments/assets/088b1074-bd57-4d9d-8de4-81f1a5edf407)

### After - that's where it hangs

Actually shows me the error without having to wait 3 minutes for test to
timeout:

![CleanShot 2024-10-08 at 21 13
13@2x](https://github.com/user-attachments/assets/ec91f530-2f5e-4b6d-872a-f483b9a421f4)


### Before - after waiting for 3 minutes for test to timeout:

![CleanShot 2024-10-08 at 21 12
08@2x](https://github.com/user-attachments/assets/64ac9945-3a3c-4eb5-991c-943859500bb5)
(1000 lines of same error spam...)
![CleanShot 2024-10-08 at 21 19
28@2x](https://github.com/user-attachments/assets/ccd33c38-f6d9-47a8-8c5a-41c118cfe849)

### After - after waiting for 3 minutes for test to timeout:

![CleanShot 2024-10-08 at 21 14
54@2x](https://github.com/user-attachments/assets/c2240305-21da-4b4c-9e28-ee68f8b2899d)
![CleanShot 2024-10-08 at 21 15
09@2x](https://github.com/user-attachments/assets/d6f7fab6-acd4-4bcc-a560-9e86792fdbbf)
(Error spam)
![CleanShot 2024-10-08 at 21 15
20@2x](https://github.com/user-attachments/assets/6be43e88-f881-4598-bb32-d7cfc90ef710)
This commit is contained in:
Alessio Gravili
2024-10-11 12:54:39 -06:00
committed by GitHub
parent 21606ded08
commit c731940239
7 changed files with 133 additions and 6 deletions

View File

@@ -15,6 +15,12 @@ const esModules = [
'uint8array-extras',
].join('|')
import path from 'path'
import { fileURLToPath } from 'url'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
/** @type {import('jest').Config} */
const baseJestConfig = {
extensionsToTreatAsEsm: ['.ts', '.tsx'],
@@ -36,10 +42,14 @@ const baseJestConfig = {
'^.+\\.(t|j)sx?$': ['@swc/jest'],
},
verbose: true,
reporters: [
path.resolve(dirname, './test/jest-spec-reporter.cjs'),
path.resolve(dirname, './test/jestreporter.cjs'),
],
}
if (process.env.CI) {
baseJestConfig.reporters = [['github-actions', { silent: false }], 'summary']
baseJestConfig.reporters.push(['github-actions', { silent: false }], 'summary')
}
export default baseJestConfig

2
pnpm-lock.yaml generated
View File

@@ -2787,9 +2787,11 @@ packages:
'@esbuild-kit/core-utils@3.3.2':
resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
deprecated: 'Merged into tsx: https://tsx.is'
'@esbuild-kit/esm-loader@2.6.5':
resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==}
deprecated: 'Merged into tsx: https://tsx.is'
'@esbuild/aix-ppc64@0.19.12':
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}

View File

@@ -273,4 +273,4 @@ export interface Auth {
declare module 'payload' {
// @ts-ignore
export interface GeneratedTypes extends Config {}
}
}

100
test/jest-spec-reporter.cjs Normal file
View File

@@ -0,0 +1,100 @@
// From https://github.com/robertbradleyux/jest-ci-spec-reporter/blob/main/src/jest-ci-spec-reporter.ts
/*
MIT License
Copyright (c) 2023 Robert Bradley
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
class JestCiSpecReporter {
onRunStart({ numTotalTestSuites }) {
console.log()
console.log(`Found ${numTotalTestSuites} test suites.`)
console.log()
}
onRunComplete(_, results) {
const {
numFailedTests,
numPassedTests,
numPendingTests,
testResults,
numTotalTests,
startTime,
} = results
testResults.forEach(({ failureMessage }) => {
if (failureMessage) {
console.log(failureMessage)
}
})
const testResultText = numFailedTests === 0 ? 'SUCCESS' : 'FAILED'
const numNotSkippedTests = numPassedTests + numFailedTests
const runDuration = this._getRunDuration(startTime)
console.log()
console.log(
`Executed ${numNotSkippedTests} of ${numTotalTests} (skipped ${numPendingTests}) ${testResultText} (${runDuration})`,
)
console.log(`TOTAL: ${numNotSkippedTests} ${testResultText}`)
}
onTestResult(test, { testResults }) {
testResults.forEach((result) => {
var _a, _b
const { title, duration, status, ancestorTitles } = result
const { name } =
(_b = (_a = test.context.config) === null || _a === void 0 ? void 0 : _a.displayName) !==
null && _b !== void 0
? _b
: {}
if (name) {
ancestorTitles.unshift(name)
}
const breadcrumbs = `${ancestorTitles.join(' > ')} >`
console.log(
` ${this._getTestStatus(status)} ${breadcrumbs} ${title} ${this._getTestDuration(duration)}`,
)
})
}
getLastError() {
return undefined
}
_getRunDuration(startTime) {
const deltaInMillis = new Date().getTime() - new Date(startTime).getTime()
const seconds = ((deltaInMillis % 60000) / 1000).toFixed(3)
return `${seconds} secs`
}
_getTestDuration(duration) {
return `\x1b[1m\x1b[30m(${duration !== null && duration !== void 0 ? duration : 0}ms)\x1b[0m`
}
_getTestStatus(status) {
switch (status) {
case 'passed':
return '\x1b[1m\x1b[32m[PASS]\x1b[0m'
case 'pending':
return '\x1b[1m\x1b[33m[SKIP]\x1b[0m'
case 'todo':
return '\x1b[1m\x1b[34m[TODO]\x1b[0m'
case 'failed':
default:
return '\x1b[1m\x1b[31m[FAIL]\x1b[0m'
}
}
}
exports.default = JestCiSpecReporter

View File

@@ -22,8 +22,4 @@ const customJestConfig = {
},
}
if (process.env.CI) {
customJestConfig.reporters = [['github-actions', { silent: false }], 'summary']
}
export default customJestConfig

View File

@@ -1,3 +1,6 @@
import console from 'console'
global.console = console
import { generateDatabaseAdapter } from './generateDatabaseAdapter.js'
process.env.PAYLOAD_DISABLE_ADMIN = 'true'

16
test/jestreporter.cjs Normal file
View File

@@ -0,0 +1,16 @@
class CustomReporter {
constructor(globalConfig, reporterOptions, reporterContext) {
this._globalConfig = globalConfig
this._options = reporterOptions
this._context = reporterContext
}
onTestCaseResult(test, testCaseResult) {
if (testCaseResult.status === 'passed') {
return
}
console.log('Test case result:', testCaseResult)
}
}
module.exports = CustomReporter