Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: re-enable console output in tests #32883

Merged
merged 4 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/aws-cdk-lib/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ module.exports = {
statements: 55,
},
},

testEnvironment: './testhelpers/jest-bufferedconsole.ts',
};
1 change: 1 addition & 0 deletions packages/aws-cdk-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
"fast-check": "^3.22.0",
"jest": "^29.7.0",
"jest-each": "^29.7.0",
"jest-environment-node": "^29.7.0",
"lambda-tester": "^4.0.1",
"lodash": "^4.17.21",
"nock": "^13.5.5",
Expand Down
75 changes: 75 additions & 0 deletions packages/aws-cdk-lib/testhelpers/jest-bufferedconsole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-disable import/no-extraneous-dependencies */
/**
* A Jest environment that buffers outputs to `console.log()` and only shows it for failing tests.
*/
import type { EnvironmentContext, JestEnvironment, JestEnvironmentConfig } from '@jest/environment';
import { Circus } from '@jest/types';
import { TestEnvironment as NodeEnvironment } from 'jest-environment-node';

interface ConsoleMessage {
type: 'log' | 'error' | 'warn' | 'info' | 'debug';
message: string;
}

export default class TestEnvironment extends NodeEnvironment implements JestEnvironment<unknown> {
private log = new Array<ConsoleMessage>();
private originalConsole!: typeof console;

constructor(config: JestEnvironmentConfig, context: EnvironmentContext) {
super(config, context);

// We need to set the event handler by assignment in the constructor,
// because if we declare it as an async member TypeScript's type derivation
// doesn't work properly.
(this as JestEnvironment<unknown>).handleTestEvent = (async (event, _state) => {
if (event.name === 'test_done' && event.test.errors.length > 0 && this.log.length > 0) {
this.originalConsole.log(`[Console output] ${fullTestName(event.test)}\n`);
for (const item of this.log) {
this.originalConsole[item.type](' ' + item.message);
}
this.originalConsole.log('\n');
}

if (event.name === 'test_done') {
this.log = [];
}
}) satisfies Circus.EventHandler;
}

async setup() {
await super.setup();

this.log = [];
this.originalConsole = console;

this.global.console = {
...console,
log: (message) => this.log.push({ type: 'log', message }),
error: (message) => this.log.push({ type: 'error', message }),
warn: (message) => this.log.push({ type: 'warn', message }),
info: (message) => this.log.push({ type: 'info', message }),
debug: (message) => this.log.push({ type: 'debug', message }),
};
}

async teardown() {
this.global.console = this.originalConsole;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this collide when running tests in parallel? or do they not run in the same process?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside one process tests run sequentially, except when using test.concurrent(). But we only use that in the CLI integ tests, and we're only using this environment in aws-cdk-lib.

await super.teardown();
}
}

// DescribeBlock is not exported from `@jest/types`, so we need to build the parts we are interested in
type TestDescription = PartialBy<Pick<Circus.TestEntry, 'name' | 'parent'>, 'parent'>;

// Utility type to make specific fields optional
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>

function fullTestName(test: TestDescription) {
let ret = test.name;
while (test.parent != null && test.parent.name !== 'ROOT_DESCRIBE_BLOCK') {
ret = test.parent.name + ' › ' + fullTestName;
test = test.parent;
}
return ret;
}

6 changes: 0 additions & 6 deletions tools/@aws-cdk/cdk-build-tools/config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,4 @@ module.exports = {
],
coveragePathIgnorePatterns: ['\\.generated\\.[jt]s$', '<rootDir>/test/', '.warnings.jsii.js$', '/node_modules/'],
reporters: ['default', ['jest-junit', { suiteName: 'jest tests', outputDirectory: 'coverage' }]],
/**
* This will still show us helpful information when running tests but remove console statements.
* The exception is when we use our custom logger in the CLI or when other processes are spun up
* within tests. It has no impact there.
*/
silent: true,
};
Loading