Skip to content

Commit a5bd76e

Browse files
authored
chore: re-enable console output in tests (#32883)
Remove `silent: true` from the test config. `console.log()` is a time-honored debugging tradition, and the `silent: true` config makes that not work in a non-obvious way. A nice behavior that many other testing frameworks have is to only print the console output of tests that fail. Use the "Jest Environments" feature to build an environment that replaces `console.log()` etc and buffers their output to only print it for failing tests. This achieves the same goals of not polluting the terminal too much while running tests, while still allowing the ability to see the output of `console.log`. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent e7e908b commit a5bd76e

File tree

4 files changed

+78
-6
lines changed

4 files changed

+78
-6
lines changed

packages/aws-cdk-lib/jest.config.js

+2
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ module.exports = {
1919
statements: 55,
2020
},
2121
},
22+
23+
testEnvironment: './testhelpers/jest-bufferedconsole.ts',
2224
};

packages/aws-cdk-lib/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@
176176
"fast-check": "^3.22.0",
177177
"jest": "^29.7.0",
178178
"jest-each": "^29.7.0",
179+
"jest-environment-node": "^29.7.0",
179180
"lambda-tester": "^4.0.1",
180181
"lodash": "^4.17.21",
181182
"nock": "^13.5.5",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* eslint-disable import/no-extraneous-dependencies */
2+
/**
3+
* A Jest environment that buffers outputs to `console.log()` and only shows it for failing tests.
4+
*/
5+
import type { EnvironmentContext, JestEnvironment, JestEnvironmentConfig } from '@jest/environment';
6+
import { Circus } from '@jest/types';
7+
import { TestEnvironment as NodeEnvironment } from 'jest-environment-node';
8+
9+
interface ConsoleMessage {
10+
type: 'log' | 'error' | 'warn' | 'info' | 'debug';
11+
message: string;
12+
}
13+
14+
export default class TestEnvironment extends NodeEnvironment implements JestEnvironment<unknown> {
15+
private log = new Array<ConsoleMessage>();
16+
private originalConsole!: typeof console;
17+
18+
constructor(config: JestEnvironmentConfig, context: EnvironmentContext) {
19+
super(config, context);
20+
21+
// We need to set the event handler by assignment in the constructor,
22+
// because if we declare it as an async member TypeScript's type derivation
23+
// doesn't work properly.
24+
(this as JestEnvironment<unknown>).handleTestEvent = (async (event, _state) => {
25+
if (event.name === 'test_done' && event.test.errors.length > 0 && this.log.length > 0) {
26+
this.originalConsole.log(`[Console output] ${fullTestName(event.test)}\n`);
27+
for (const item of this.log) {
28+
this.originalConsole[item.type](' ' + item.message);
29+
}
30+
this.originalConsole.log('\n');
31+
}
32+
33+
if (event.name === 'test_done') {
34+
this.log = [];
35+
}
36+
}) satisfies Circus.EventHandler;
37+
}
38+
39+
async setup() {
40+
await super.setup();
41+
42+
this.log = [];
43+
this.originalConsole = console;
44+
45+
this.global.console = {
46+
...console,
47+
log: (message) => this.log.push({ type: 'log', message }),
48+
error: (message) => this.log.push({ type: 'error', message }),
49+
warn: (message) => this.log.push({ type: 'warn', message }),
50+
info: (message) => this.log.push({ type: 'info', message }),
51+
debug: (message) => this.log.push({ type: 'debug', message }),
52+
};
53+
}
54+
55+
async teardown() {
56+
this.global.console = this.originalConsole;
57+
await super.teardown();
58+
}
59+
}
60+
61+
// DescribeBlock is not exported from `@jest/types`, so we need to build the parts we are interested in
62+
type TestDescription = PartialBy<Pick<Circus.TestEntry, 'name' | 'parent'>, 'parent'>;
63+
64+
// Utility type to make specific fields optional
65+
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
66+
67+
function fullTestName(test: TestDescription) {
68+
let ret = test.name;
69+
while (test.parent != null && test.parent.name !== 'ROOT_DESCRIBE_BLOCK') {
70+
ret = test.parent.name + ' › ' + fullTestName;
71+
test = test.parent;
72+
}
73+
return ret;
74+
}
75+

tools/@aws-cdk/cdk-build-tools/config/jest.config.js

-6
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,4 @@ module.exports = {
3535
],
3636
coveragePathIgnorePatterns: ['\\.generated\\.[jt]s$', '<rootDir>/test/', '.warnings.jsii.js$', '/node_modules/'],
3737
reporters: ['default', ['jest-junit', { suiteName: 'jest tests', outputDirectory: 'coverage' }]],
38-
/**
39-
* This will still show us helpful information when running tests but remove console statements.
40-
* The exception is when we use our custom logger in the CLI or when other processes are spun up
41-
* within tests. It has no impact there.
42-
*/
43-
silent: true,
4438
};

0 commit comments

Comments
 (0)