Make console logs visible on test failures
MODIFY
packages/@romejs/cli-diagnostics/DiagnosticsPrinter.ts
packages/@romejs/cli-diagnostics/DiagnosticsPrinter.ts
@@ -632,10 +632,7 @@ export default class DiagnosticsPrinter extends Error {
}
const displayableProblems = this.getDisplayedProblemsCount();
- let str = `Found <number emphasis>${displayableProblems}</number> problem`;
- if (displayableProblems > 1 || displayableProblems === 0) {
- str += 's';
- }
+ let str = `Found <number emphasis>${displayableProblems}</number> <grammarNumber plural="problems" singular="problem">${displayableProblems}</grammarNumber>`;
if (filteredCount > 0) {
str += `<dim> (${filteredCount} filtered)</dim>`;

MODIFY
packages/@romejs/cli-diagnostics/printAdvice.ts
packages/@romejs/cli-diagnostics/printAdvice.ts
@@ -347,11 +347,12 @@ function printStacktrace(
diagnostic.description.advice !== undefined &&
diagnostic.description.advice[0] === item;
if (!isFirstPart) {
- opts.reporter.info(
- item.title === undefined ? 'Stack trace' : escapeMarkup(item.title),
- );
+ const {title} = item;
+ if (title !== undefined) {
+ opts.reporter.info(escapeMarkup(title));
opts.reporter.forceSpacer();
}
+ }
opts.reporter.processedList(
frames,

MODIFY
packages/@romejs/core/common/utils/executeMain.ts
packages/@romejs/core/common/utils/executeMain.ts
@@ -40,7 +40,6 @@ export default async function executeMain(
// Create global context
const sandbox: UnknownObject = {
- ...globals,
process: {
argv: [process.argv[0], filename],
__proto__: process,
@@ -58,6 +57,7 @@ export default async function executeMain(
console,
__dirname: path.getParent().join(),
__filename: filename,
+ ...globals,
};
sandbox.global = sandbox;
const context = vm.createContext(sandbox);

MODIFY
packages/@romejs/core/test-worker/TestWorkerRunner.ts
packages/@romejs/core/test-worker/TestWorkerRunner.ts
@@ -9,12 +9,15 @@ import {UnknownObject} from '@romejs/typescript-helpers';
import {
Diagnostic,
DiagnosticAdvice,
+ DiagnosticLogCategory,
+ Diagnostics,
INTERNAL_ERROR_LOG_ADVICE,
catchDiagnostics,
createBlessedDiagnosticMessage,
createSingleDiagnosticError,
deriveDiagnosticFromError,
descriptions,
+ getErrorStackAdvice,
} from '@romejs/diagnostics';
import {
GlobalTestOptions,
@@ -35,9 +38,57 @@ import {
} from '../common/types/files';
import {AbsoluteFilePath, createAbsoluteFilePath} from '@romejs/path';
import {escapeMarkup, markup} from '@romejs/string-markup';
-
+import util = require('util');
+import {
+ ErrorFrames,
+ getErrorStructure,
+ getSourceLocationFromErrorFrame,
+} from '@romejs/v8';
const MAX_RUNNING_TESTS = 20;
+function cleanFrames(frames: ErrorFrames): ErrorFrames {
+ // TODO we should actually get the frames before module init and do it that way
+ // Remove everything before the original module factory
+ let latestTestWorkerFrame = frames.find((frame, i) => {
+ if (
+ frame.typeName === 'global' &&
+ frame.methodName === undefined &&
+ frame.functionName === undefined
+ ) {
+ // We are the global.<anonymous> frame
+ // Now check for Script.runInContext
+ const nextFrame = frames[i + 1];
+ if (
+ nextFrame !== undefined &&
+ nextFrame.typeName === 'Script' &&
+ nextFrame.methodName === 'runInContext'
+ ) {
+ // Yes!
+ // TODO also check for ___$romejs$core$common$utils$executeMain_ts$default (packages/romejs/core/common/utils/executeMain.ts:69:17)
+ return true;
+ }
+ }
+
+ return false;
+ });
+
+ // And if there was no module factory frame, then we must be inside of a test
+ if (latestTestWorkerFrame === undefined) {
+ latestTestWorkerFrame = frames.find((frame) => {
+ return (
+ frame.typeName !== undefined &&
+ frame.typeName.includes('$TestWorkerRunner')
+ );
+ });
+ }
+
+ if (latestTestWorkerFrame === undefined) {
+ return frames;
+ }
+
+ return frames.slice(0, frames.indexOf(latestTestWorkerFrame));
+}
+
export default class TestWorkerRunner {
constructor(opts: TestWorkerBridgeRunOptions, bridge: TestWorkerBridge) {
this.opts = opts;
@@ -52,6 +103,8 @@ export default class TestWorkerRunner {
createAbsoluteFilePath(opts.file.real),
);
+ this.emitConsoleDiagnostics = false;
+ this.consoleAdvice = [];
this.hasFocusedTest = false;
this.foundTests = new Map();
}
@@ -65,7 +118,6 @@ export default class TestWorkerRunner {
}
>;
hasFocusedTest: boolean;
-
bridge: TestWorkerBridge;
projectFolder: AbsoluteFilePath;
file: FileReference;
@@ -73,6 +125,72 @@ export default class TestWorkerRunner {
snapshotManager: SnapshotManager;
opts: TestWorkerBridgeRunOptions;
locked: boolean;
+ consoleAdvice: DiagnosticAdvice;
+ emitConsoleDiagnostics: boolean;
+
+ createConsole(): Partial<Console> {
+ const addDiagnostic = (
+ category: DiagnosticLogCategory,
+ args: Array<unknown>,
+ ) => {
+ const text = escapeMarkup(util.format(args[0], ...args.slice(1)));
+
+ const err = new Error();
+
+ // Remove the first two frames to get to the actual source
+ const frames = cleanFrames(getErrorStructure(err).frames.slice(2));
+
+ this.consoleAdvice.push({
+ type: 'log',
+ category,
+ text,
+ });
+ this.consoleAdvice = this.consoleAdvice.concat(
+ getErrorStackAdvice(err, undefined, frames),
+ );
+ };
+
+ function logInfo(...args: Array<unknown>): void {
+ addDiagnostic('info', args);
+ }
+
+ return {
+ assert(expression: unknown, ...args: Array<unknown>): void {
+ if (!expression) {
+ args[0] = `Assertion failed${args.length === 0 ? '' : `: ${args[0]}`}`;
+ addDiagnostic('warn', args);
+ }
+ },
+ dir(obj: unknown): void {
+ addDiagnostic('info', [obj]);
+ },
+ error: (...args: Array<unknown>): void => {
+ addDiagnostic('error', args);
+ },
+ warn: (...args: Array<unknown>): void => {
+ addDiagnostic('warn', args);
+ },
+ dirxml: logInfo,
+ debug: logInfo,
+ info: logInfo,
+ log: logInfo,
+ trace: logInfo,
+ // Noop
+ count(): void {},
+ countReset(): void {},
+ table(): void {},
+ time(): void {},
+ timeEnd(): void {},
+ timeLog(): void {},
+ clear(): void {},
+ group(): void {},
+ groupCollapsed(): void {},
+ groupEnd(): void {},
+ profile(): void {},
+ profileEnd(): void {},
+ timeStamp(): void {},
+ };
+ }
// Global variables to expose to tests
getEnvironment(): UnknownObject {
@@ -89,6 +207,7 @@ export default class TestWorkerRunner {
return {
__ROME__TEST_OPTIONS__: testOptions,
+ console: this.createConsole(),
};
}
@@ -188,48 +307,7 @@ export default class TestWorkerRunner {
category: 'tests/failure',
label: escapeMarkup(testName),
filename,
- cleanFrames(frames) {
- // TODO we should actually get the frames before module init and do it that way
- // Remove everything before the original module factory
- let latestTestWorkerFrame = frames.find((frame, i) => {
- if (
- frame.typeName === 'global' &&
- frame.methodName === undefined &&
- frame.functionName === undefined
- ) {
- // We are the global.<anonymous> frame
- // Now check for Script.runInContext
- const nextFrame = frames[i + 1];
- if (
- nextFrame !== undefined &&
- nextFrame.typeName === 'Script' &&
- nextFrame.methodName === 'runInContext'
- ) {
- // Yes!
- // TODO also check for ___$romejs$core$common$utils$executeMain_ts$default (packages/romejs/core/common/utils/executeMain.ts:69:17)
- return true;
- }
- }
-
- return false;
- });
-
- // And if there was no module factory frame, then we must be inside of a test
- if (latestTestWorkerFrame === undefined) {
- latestTestWorkerFrame = frames.find((frame) => {
- return (
- frame.typeName !== undefined &&
- frame.typeName.includes('$TestWorkerRunner')
- );
- });
- }
-
- if (latestTestWorkerFrame === undefined) {
- return frames;
- }
-
- return frames.slice(0, frames.indexOf(latestTestWorkerFrame));
- },
+ cleanFrames,
});
diagnostic = {
@@ -244,6 +322,7 @@ export default class TestWorkerRunner {
},
};
+ this.emitConsoleDiagnostics = true;
this.bridge.testError.send({
ref,
diagnostic,
@@ -372,6 +451,15 @@ export default class TestWorkerRunner {
// Save the snapshot
await this.snapshotManager.save();
+
+ if (this.emitConsoleDiagnostics) {
+ await this.emitDiagnostic({
+ description: descriptions.TESTS.LOGS(this.consoleAdvice),
+ location: {
+ filename: this.file.uid,
+ },
+ });
+ }
}
async emitFoundTests() {

MODIFY
packages/@romejs/diagnostics/categories.ts
packages/@romejs/diagnostics/categories.ts
@@ -93,6 +93,7 @@ export type DiagnosticCategory =
| 'tests/disabled'
| 'tests/failure'
| 'tests/fixtureOptions'
+ | 'tests/logs'
| 'tests/noneDeclared'
| 'tests/snapshots/incorrect'
| 'tests/snapshots/missing'

MODIFY
packages/@romejs/diagnostics/descriptions.ts
packages/@romejs/diagnostics/descriptions.ts
@@ -696,6 +696,18 @@ export const descriptions = createMessages({
message: 'No tests declared in this file',
category: 'tests/noneDeclared',
},
+ LOGS: (advice: DiagnosticAdvice) => ({
+ message: 'File produced console logs',
+ category: 'tests/logs',
+ advice: [
+ ...advice,
+ {
+ type: 'log',
+ category: 'info',
+ text: 'Only visible when this test file contains failures',
+ },
+ ],
+ }),
},
SUPPRESSIONS: {
UNUSED: {

Make console logs visible on test failures