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

Recognize typical assertion errors and use their formatting #3187

Merged
merged 13 commits into from May 24, 2023
2 changes: 1 addition & 1 deletion lib/assert.js
Expand Up @@ -27,7 +27,7 @@ function formatWithLabel(label, value) {
return formatDescriptorWithLabel(label, concordance.describe(value, concordanceOptions));
}

const hasOwnProperty = (object, prop) => Object.prototype.hasOwnProperty.call(object, prop);
export const hasOwnProperty = (object, prop) => Object.prototype.hasOwnProperty.call(object, prop);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure we want to expose that here or place this in a more appropriate file (or duplicate worst case)

Copy link
Member

Choose a reason for hiding this comment

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

It's available as Object.hasOwn() in Node.js 16 which will be the minimal required version soon enough. Repeating it is fine for now, I'm sure https://github.com/sindresorhus/eslint-plugin-unicorn will flag it soon enough 😉

const noop = () => {};
const notImplemented = () => {
throw new Error('not implemented');
Expand Down
48 changes: 37 additions & 11 deletions lib/test.js
Expand Up @@ -2,11 +2,21 @@ import concordance from 'concordance';
import isPromise from 'is-promise';
import plur from 'plur';

import {AssertionError, Assertions, checkAssertionMessage} from './assert.js';
import {AssertionError, Assertions, checkAssertionMessage, hasOwnProperty} from './assert.js';
import concordanceOptions from './concordance-options.js';
import nowAndTimers from './now-and-timers.cjs';
import parseTestArgs from './parse-test-args.js';

function isExternalAssertError(error) {
// Match errors thrown by <https://www.npmjs.com/package/expect>.
if (error.matcherResult) {
return true;
}

// Match errors thrown by <https://www.npmjs.com/package/chai> and <https://nodejs.org/api/assert.html>.
return hasOwnProperty(error, 'actual') && hasOwnProperty(error, 'expected');
}

function formatErrorValue(label, error) {
const formatted = concordance.format(error, concordanceOptions);
return {label, formatted};
Expand Down Expand Up @@ -519,11 +529,19 @@ export default class Test {

const result = this.callFn();
if (!result.ok) {
this.saveFirstError(new AssertionError({
message: 'Error thrown in test',
savedError: result.error instanceof Error && result.error,
values: [formatErrorValue('Error thrown in test:', result.error)],
}));
if (isExternalAssertError(result.error)) {
this.saveFirstError(new AssertionError({
message: 'Assertion failed',
savedError: result.error instanceof Error && result.error,
values: [{label: 'Assertion failed: ', formatted: result.error.message}],
}));
} else {
this.saveFirstError(new AssertionError({
message: 'Error thrown in test',
savedError: result.error instanceof Error && result.error,
values: [formatErrorValue('Error thrown in test:', result.error)],
}));
}

return this.finish();
}
Expand Down Expand Up @@ -564,11 +582,19 @@ export default class Test {

promise
.catch(error => {
this.saveFirstError(new AssertionError({
message: 'Rejected promise returned by test',
savedError: error instanceof Error && error,
values: [formatErrorValue('Rejected promise returned by test. Reason:', error)],
}));
if (isExternalAssertError(error)) {
this.saveFirstError(new AssertionError({
message: 'Assertion failed',
savedError: error instanceof Error && error,
values: [{label: 'Assertion failed: ', formatted: error.message}],
}));
} else {
this.saveFirstError(new AssertionError({
message: 'Rejected promise returned by test',
savedError: error instanceof Error && error,
values: [formatErrorValue('Rejected promise returned by test. Reason:', error)],
}));
}
})
.then(() => resolve(this.finish()));
});
Expand Down