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

feat(testrunner): better matchers #1077

Merged
merged 9 commits into from
Feb 21, 2020

Conversation

aslushnikov
Copy link
Collaborator

@aslushnikov aslushnikov commented Feb 21, 2020

This patch re-implements matching and reporting for test runner.
Among other improvements:

  • test failures now show a short snippet from test
  • test failures now explicitly say what received and what was expected
  • expect.toBe() now does text diff when gets strings as input
  • expect.toEqual now does object diff

Example output of a failed test:

image

This patch re-implements matching and reporting for test runner.
Among other improvements:
- test failures now show a short snippet from test
- test failures now explicitly say what received and what was expected
- `expect.toBe()` now does text diff when gets strings as input
- `expect.toEqual` now does object diff
Copy link
Contributor

@JoelEinbinder JoelEinbinder left a comment

Choose a reason for hiding this comment

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

Looks exciting!

@@ -20,6 +20,7 @@ const Diff = require('text-diff');
const PNG = require('pngjs').PNG;
const jpeg = require('jpeg-js');
const pixelmatch = require('pixelmatch');
const c = require('colors/safe');
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: s/c/colors
You taught me that we don't abbreviate like that :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

super(message);
this.name = this.constructor.name;
this.formatter = formatter;
this.location = getLocation(__filename);
Copy link
Contributor

Choose a reason for hiding this comment

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

How does this work? Won't __filename be the Matcher.js file? Why do we care about that file, rather than the spec.js that threw this error.

Update: I got to the getLocation code. It makes more sense. Would it be fair to call this method "getParentFile" or something? I assumed it was doing something very different. I guess its fine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

getCallerLocation!

this.name = this.constructor.name;
this.formatter = formatter;
this.location = getLocation(__filename);
Error.captureStackTrace(this, this.constructor);
Copy link
Contributor

Choose a reason for hiding this comment

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

What does this line do? Shouldn't calling super already capture a stack trace?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's to filter out the custom error constructor from the stack trace. That's a canonical way to create a custom error in node, e.g. see here

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not in firefox, which hurts the portability of the test runner, but I can probably polyfill it.

}
};

class MatchError extends Error {
constructor(message, formatter = null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: formatter = defaultFormatter
Then you can avoid a null check later.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

formatter is bound at least to the received argument; but the = null part is actually never needed. Thanks!

@@ -422,7 +395,10 @@ class TestRunner extends EventEmitter {
const runnableTests = this._runnableTests();
this.emit(TestRunner.Events.Started, runnableTests);
this._runningPass = new TestPass(this, this._rootSuite, runnableTests, this._parallel, this._breakOnFailure);
const termination = await this._runningPass.run();
const termination = await this._runningPass.run().catch(e => {
console.error(e);
Copy link
Contributor

Choose a reason for hiding this comment

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

this looks strange? We log an error and then throw it again? Is something consuming errors or something? Leftover debug code?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if testrunner.run() is not awaited and there's an issue with testrunner itself, then it crashes silently with an unhandled promise rejection that doesn't have any stack.

This gives the stack.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a case where this gives you a double error message? Or this is always just problems with the test runner itself, to avoid unhandledpromiserejection woes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, that's always for the bugs in the testrunner itself

@aslushnikov aslushnikov merged commit 0ded511 into microsoft:master Feb 21, 2020
@aslushnikov aslushnikov deleted the better-matchers branch February 21, 2020 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants