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

Improve standard logger by printing stack on error #81

Merged
merged 4 commits into from
Jul 18, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
37 changes: 27 additions & 10 deletions e2e/expect.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,38 @@ function slice(str, center, width = 20) {
return str.slice(Math.max(min, 0), Math.min(max, len - 1));
}

function expect(actual) {
function expect(actualOutput) {
return {
toEqual: (expected) => {
actual = sanitize(actual);
expected = sanitize(expected);
toEqual: (expectedOutput) => {
const expectedLines = expectedOutput
.toString()
.split(/\r?\n/)
.map((x) => sanitize(x))
.filter((x) => x.match(/^ *$/) === null);

const actualLines = actualOutput
.toString()
.split(/\r?\n/)
.map((x) => sanitize(x))
.filter((x) => x.match(/^ *$/) === null);
let isValid = true;
for (let i = 0; i < actual.length; ++i) {
if (expected[i] !== '#' && actual[i] !== expected[i]) {
log.error(`Expected string did not match: ${slice(actual, i)} != ${slice(expected, i)}`);
isValid = false;
break;

for (let i = 0; i < actualLines.length; ++i) {
let actual = actualLines[i];
let expected = expectedLines[i];

if (expected.split('').every((c) => c === '#' || c === ' ')) {
continue;
}
}

for (let i = 0; i < actual.length; ++i) {
if (expected[i] !== '#' && actual[i] !== expected[i]) {
log.error(`Expected string did not match: ${slice(actual, i)} != ${slice(expected, i)}`);
isValid = false;
break;
}
}
}
return isValid;
},
};
Expand Down
33 changes: 33 additions & 0 deletions e2e/testy.json -- reporter -- standard/.expected_stdout
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,41 @@ Root
MyTestSuite
√ pass1
x fail1 - Expected 2 to equal 3.
Error: Expected 2 to equal 3.
############################################################################
##############################################################################################################
###############################
###################################################################################
############################
##############################################################################################
#######################################################################################################
###################################################################
###############################
#################################################################
x fail2 - Expected 2 to equal 3.
Error: Expected 2 to equal 3.
############################################################################
##############################################################################################################
###############################
###################################################################################
############################
##############################################################################################
#######################################################################################################
###################################################################
###############################
#################################################################
x fail3 - Expected 2 to equal 3.
Error: Expected 2 to equal 3.
############################################################################
##############################################################################################################
###############################
###################################################################################
############################
##############################################################################################
#######################################################################################################
###################################################################
###############################
#################################################################
! skip1
! skip2

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
Root
MyTestSuite
x test - Expected NaN to equal 0.
Error: Expected NaN to equal 0.
####################################################################
###############################################################################################################
###################################################################
##############################
############################################################
############################
############################################################################
#######################################################################################
################################################################################
##############################

Summary: 0/1 passed, 1/1 failed, 0/1 skipped. (#####s)
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "testyts",
"version": "1.5.0",
"version": "2.0.0-beta.1",
"icon": "img/test-icon-128x128.png",
"description": "Modern test framework for TypeScript.",
"main": "build/testyCore.js",
Expand Down
53 changes: 43 additions & 10 deletions src/lib/logger/consoleLogger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Logger, Color } from './logger';
import { TextDecoder } from 'util';
import { Logger, Color, TextDecoration } from './logger';

export class ConsoleLogger extends Logger {
private readonly reset = '\x1b[0m';
Expand All @@ -12,36 +13,68 @@ export class ConsoleLogger extends Logger {
}

public warn(message: string = ''): void {
console.log(this.color(this.indentation + message, Color.Yellow));
console.log(this.format(this.indentation + message, Color.Yellow));
}

public error(message: string = ''): void {
console.log(this.color(this.indentation + message, Color.Red));
console.log(this.format(this.indentation + message, Color.Red));
}

public color(message: string, color: Color) {
public format(message: string, color: Color, textDecorations: TextDecoration[] = []): string {
const colorCode = this.getColorCode(color);
const textDecorationsCodes = textDecorations.map((x) => this.getTextDecorationCode(x));

return message
.split(' ')
.map((x) => colorCode + textDecorationsCodes.join() + x + this.reset)
.join(' ');
}

private getColorCode(color: Color) {
switch (color) {
case Color.Black:
return ForegroundColorCodes.Black;

case Color.Green:
return ForegroundColors.Green + message + this.reset;
return ForegroundColorCodes.Green;

case Color.Yellow:
return ForegroundColors.Yellow + message + this.reset;
return ForegroundColorCodes.Yellow;

case Color.Red:
return ForegroundColors.Red + message + this.reset;
return ForegroundColorCodes.Red;

case Color.Grey:
return ForegroundColors.Grey + message + this.reset;
return ForegroundColorCodes.Grey;

case Color.LightGrey:
return ForegroundColorCodes.LightGrey;

default:
return '';
}
}

private getTextDecorationCode(textDecoration: TextDecoration) {
switch (textDecoration) {
case TextDecoration.Bold:
return TextDecorationsCodes.Bold;

default:
return message + this.reset;
return '';
}
}
}

enum ForegroundColors {
enum TextDecorationsCodes {
Bold = '\x1b[1m',
}

enum ForegroundColorCodes {
Black = '\x1b[30m',
Red = '\x1b[31m',
Green = '\x1b[32m',
Yellow = '\x1b[93m',
Grey = '\x1b[90m',
LightGrey = '\x1b[37m',
}
8 changes: 7 additions & 1 deletion src/lib/logger/logger.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
export enum Color {
Black,
Red,
Green,
Yellow,
Grey,
LightGrey,
}

export enum TextDecoration {
Bold,
}

export abstract class Logger {
Expand All @@ -25,5 +31,5 @@ export abstract class Logger {
abstract info(message?: string): void;
abstract warn(message?: string): void;
abstract error(message?: string): void;
abstract color(message: string, color: Color): void;
abstract format(message: string, color: Color, textDecorations?: TextDecoration[]): string;
}
6 changes: 5 additions & 1 deletion src/lib/reporting/report/failedTestReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ export class FailedTestReport extends LeafReport {
return this._message;
}

constructor(name: string, private _message: string, duration: number) {
public get stack() {
return this._stack;
}

constructor(name: string, private _message: string, private _stack: string, duration: number) {
super(name, TestResult.Failure, duration);
}
}
2 changes: 1 addition & 1 deletion src/lib/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class TestInstance {
) {}

public async run(context, config: TestyConfig) {
return await new Promise(async (resolve, reject) => {
return await new Promise<void>(async (resolve, reject) => {
try {
// The timeout is determined like so:
// 1. If there is a test-level timeout, we use it
Expand Down
40 changes: 27 additions & 13 deletions src/lib/tests/visitors/decorators/loggerTestReporterDecorator.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Logger, Color } from '../../../logger/logger';
import { Color, Logger, TextDecoration } from '../../../logger/logger';
import { CompositeReport } from '../../../reporting/report/compositeReport';
import { FailedTestReport } from '../../../reporting/report/failedTestReport';
import { Report } from '../../../reporting/report/report';
import { TestResult } from '../../../reporting/report/testResult';
import { RootTestSuite } from '../../rootTestSuite';
import { TestInstance } from '../../test';
import { TestSuiteInstance } from '../../testSuite';
import { TestVisitor } from '../testVisitor';
import { TestsVisitorDecorator } from './testsVisitorDecorator';
import { RootTestSuite } from '../../rootTestSuite';

export class LoggerTestReporterDecorator extends TestsVisitorDecorator<Report> {
private justPrintedTestSuite: boolean;
Expand All @@ -20,16 +20,30 @@ export class LoggerTestReporterDecorator extends TestsVisitorDecorator<Report> {
this.justPrintedTestSuite = false;
const report = await this.baseVisitTest(test);

let msg;
let title;
const details: string[] = [];
if (report.result === TestResult.Success) {
msg = `${this.logger.color('√', Color.Green)} ${this.logger.color(test.name, Color.Grey)}`;
title = `${this.logger.format('√', Color.Green)} ${this.logger.format(test.name, Color.Grey)}`;
} else if (report instanceof FailedTestReport) {
msg = this.logger.color(`x ${test.name} - ${report.message}`, Color.Red);
title = this.logger.format(`x ${test.name} - ${report.message}`, Color.Red, [TextDecoration.Bold]);

if (report.stack?.length) {
details.push(...report.stack.split(/[\r\n|\n]/).map((x) => this.logger.format(x, Color.Grey)));
}
} else {
msg = `${this.logger.color('!', Color.Yellow)} ${this.logger.color(`${test.name}`, Color.Grey)}`;
title = `${this.logger.format('!', Color.Yellow)} ${this.logger.format(`${test.name}`, Color.Grey)}`;
}

this.logger.info(msg);
this.logger.info(title);

if (details.length) {
this.logger.increaseIndentation();
for (const detail of details) {
this.logger.info(detail);
}

this.logger.decreaseIndentation();
}

return report;
}
Expand All @@ -40,7 +54,7 @@ export class LoggerTestReporterDecorator extends TestsVisitorDecorator<Report> {
this.justPrintedTestSuite = true;
}

this.logger.info(tests.name);
this.logger.info(this.logger.format(tests.name, Color.Black, [TextDecoration.Bold]));
this.logger.increaseIndentation();

const returnValue = await this.baseVisitTestSuite(tests);
Expand All @@ -65,10 +79,10 @@ export class LoggerTestReporterDecorator extends TestsVisitorDecorator<Report> {
const skipped = tests.numberOfSkippedTests;
const total = tests.numberOfTests;

this.logger.info(
`Summary: ${success}/${total} passed, ${failed}/${total} failed, ${skipped}/${total} skipped. (${
tests.duration / 1000
}s)`
);
const successMsg = `${success}/${total} ${this.logger.format('passed', success > 0 ? Color.Green : null)}`;
const failedMsg = `${failed}/${total} ${this.logger.format('failed', failed > 0 ? Color.Red : null)}`;
const skippedMsg = `${skipped}/${total} ${this.logger.format('skipped', skipped > 0 ? Color.Yellow : null)}`;

this.logger.info(`Summary: ${successMsg}, ${failedMsg}, ${skippedMsg}. (${tests.duration / 1000}s)`);
}
}
2 changes: 1 addition & 1 deletion src/lib/tests/visitors/failedTestsReportVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class FailedTestsReportVisitor implements TestVisitor<Report> {
const report: LeafReport =
test.status === TestStatus.Ignored
? new SkippedTestReport(test.name)
: new FailedTestReport(test.name, this.reason, 0);
: new FailedTestReport(test.name, this.reason, '', 0);

return report;
}
Expand Down
7 changes: 6 additions & 1 deletion src/lib/tests/visitors/testRunnerVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ export class TestRunnerVisitor implements TestVisitor<Report> {
report = new SuccessfulTestReport(test.name, Math.round(time));
} catch (err) {
this.process.exitCode = 1;
report = new FailedTestReport(test.name, typeof err === typeof '' ? err : err.message, 0);
report = new FailedTestReport(
test.name,
typeof err === typeof '' ? err : err.message,
typeof err === typeof '' ? null : err.stack,
0
);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/spec/decorators/baseTestSuite/testWithBase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { BaseTestSuite, TestSuiteWithBase } from './testSuiteWithBase';
@TestSuite('Test Suite With Base Test Suite Tests')
export class BeforeAfterDecoratorsTestSuite extends TestSuiteTestsBase {
@Test('the base and the actual test suite before and after methods are called.')
private async trivialCase() {
public async trivialCase() {
// Arrange
const testSuite = TestUtils.getInstance(TestSuiteWithBase);

Expand All @@ -27,7 +27,7 @@ export class BeforeAfterDecoratorsTestSuite extends TestSuiteTestsBase {
}

@Test('base with multiple children')
private async baseWithMultipleChildren() {
public async baseWithMultipleChildren() {
// Arrange
const a = TestUtils.getInstance(TestSuiteA);
const b = TestUtils.getInstance(TestSuiteB);
Expand Down
8 changes: 4 additions & 4 deletions src/spec/tests/testRunnerVisitor.errorMessages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export class TestRunnerVisitorErrorMessagesTests {
const testSuite = TestUtils.getInstance(AsyncTestsFailures);

const expectedReport = new CompositeReport('AsyncTestsFailures');
expectedReport.addReport(new FailedTestReport('testA', 'Test has timed out.', 0));
expectedReport.addReport(new FailedTestReport('testC', 'Some rejection message!', 0));
expectedReport.addReport(new FailedTestReport('testD', 'Some error!', 0));
expectedReport.addReport(new FailedTestReport('testA', 'Test has timed out.', null, 0));
expectedReport.addReport(new FailedTestReport('testC', 'Some rejection message!', null, 0));
expectedReport.addReport(new FailedTestReport('testD', 'Some error!', null, 0));

// Act
const actualReport = await testSuite.accept(this.testRunnerVisitor);
Expand All @@ -42,7 +42,7 @@ export class TestRunnerVisitorErrorMessagesTests {
const testSuite = TestUtils.getInstance(SyncTestsFailures);

const expectedReport = new CompositeReport('SyncTestsFailures');
expectedReport.addReport(new FailedTestReport('error', 'Some error!', 0));
expectedReport.addReport(new FailedTestReport('error', 'Some error!', null, 0));

// Act
const actualReport = await testSuite.accept(this.testRunnerVisitor);
Expand Down
Loading