Tests failing
Hi! Maybe I've done something wrong, but I've tried building this from source and running the tests, but they're both failing:
$ nyc mocha
make error cause
1) should render the cause
2) should render extra properties in the full stack
0 passing (13ms)
2 failing
1) make error cause
should render the cause:
AssertionError: expected 'TestError: test boom!\n at Context…' to equal 'TestError: test boom!\n at Context…'
+ expected - actual
TestError: test boom!
at Context.<anonymous> (/home/jdg/debian/spyder-packages/jupyter-notebook/make-error-cause/node-make-error-cause-2.3.0/src/index.spec.ts:12:23)
at callFn (/usr/share/nodejs/mocha/lib/runnable.js:364:21)
- ... 7 lines matching cause stack trace ...
- at processImmediate (node:internal/timers:483:21) {
- [cause]: Error: boom!
- at Context.<anonymous> (/home/jdg/debian/spyder-packages/jupyter-notebook/make-error-cause/node-make-error-cause-2.3.0/src/index.spec.ts:11:19)
- at callFn (/usr/share/nodejs/mocha/lib/runnable.js:364:21)
- at Test.Runnable.run (/usr/share/nodejs/mocha/lib/runnable.js:352:5)
- at Runner.runTest (/usr/share/nodejs/mocha/lib/runner.js:677:10)
- at /usr/share/nodejs/mocha/lib/runner.js:800:12
- at next (/usr/share/nodejs/mocha/lib/runner.js:592:14)
- at /usr/share/nodejs/mocha/lib/runner.js:602:7
- at next (/usr/share/nodejs/mocha/lib/runner.js:485:14)
- at Immediate._onImmediate (/usr/share/nodejs/mocha/lib/runner.js:570:5)
- at processImmediate (node:internal/timers:483:21)
-}
+ at Test.Runnable.run (/usr/share/nodejs/mocha/lib/runnable.js:352:5)
+ at Runner.runTest (/usr/share/nodejs/mocha/lib/runner.js:677:10)
+ at /usr/share/nodejs/mocha/lib/runner.js:800:12
+ at next (/usr/share/nodejs/mocha/lib/runner.js:592:14)
+ at /usr/share/nodejs/mocha/lib/runner.js:602:7
+ at next (/usr/share/nodejs/mocha/lib/runner.js:485:14)
+ at Immediate._onImmediate (/usr/share/nodejs/mocha/lib/runner.js:570:5)
+ at processImmediate (node:internal/timers:483:21)
The following exception was the direct cause of the above exception:
Error: boom!
at Context.<anonymous> (src/index.spec.ts:19:37)
at callFn (/usr/share/nodejs/mocha/lib/runnable.js:364:21)
at Test.Runnable.run (/usr/share/nodejs/mocha/lib/runnable.js:352:5)
at Runner.runTest (/usr/share/nodejs/mocha/lib/runner.js:677:10)
at /usr/share/nodejs/mocha/lib/runner.js:800:12
at next (/usr/share/nodejs/mocha/lib/runner.js:592:14)
at /usr/share/nodejs/mocha/lib/runner.js:602:7
at next (/usr/share/nodejs/mocha/lib/runner.js:485:14)
at Immediate._onImmediate (/usr/share/nodejs/mocha/lib/runner.js:570:5)
at processImmediate (node:internal/timers:483:21)
2) make error cause
should render extra properties in the full stack:
AssertionError: expected 'TestDataError: test boom!\n at Con…' to include 'TestDataError: test boom!\n at Con…'
at Context.<anonymous> (src/index.spec.ts:58:15)
at callFn (/usr/share/nodejs/mocha/lib/runnable.js:364:21)
at Test.Runnable.run (/usr/share/nodejs/mocha/lib/runnable.js:352:5)
at Runner.runTest (/usr/share/nodejs/mocha/lib/runner.js:677:10)
at /usr/share/nodejs/mocha/lib/runner.js:800:12
at next (/usr/share/nodejs/mocha/lib/runner.js:592:14)
at /usr/share/nodejs/mocha/lib/runner.js:602:7
at next (/usr/share/nodejs/mocha/lib/runner.js:485:14)
at Immediate._onImmediate (/usr/share/nodejs/mocha/lib/runner.js:570:5)
at processImmediate (node:internal/timers:483:21)
I wonder whether it's because make-error has changed its behaviour or something like that?
I can confirm the same. Here is a visual comparison of the test failure:
At first, I though that it's due to a difference in Node.js, so I tested with Node.js 16, 18, 20, 22, and 24, and got the same results every time.
The next thing to test is whether a newer version of make-error is causing this. I doubt it, because the latest release of make-error was v1.3.7 in 2020 according to github (5 years ago), although strangely enough, the latest version shown on npm is v1.3.6, also in 2020. I tested with every version of make-error from 1.2.3 to 1.3.6 and they all give the same test failure. Anything older than 1.2.3 results in a Typescript error.
I still can't figure out when this changed, but since it did, we probably no longer need the custom inspect method, and this whole package could be replaced by a simple class like this:
/**
* This is similar to https://github.com/blakeembrey/make-error-cause but improved and simplified:
* - It does not depend on Node.js. See https://github.com/blakeembrey/make-error-cause/pull/61
* - It supports `unknown` as the type of the `cause` property, since that is what the specification says.
* - It does not have the custom inspect method, because the default `inspect` already shows the `cause` property.
* - It does not define the cause property at all when it is `undefined` or `null`
* to prevent showing unnecessary `[cause]: undefined` in the stack trace.
*/
import { BaseError } from "make-error";
/**
* Like `BaseError`, but supports the `cause` property.
*/
export class BaseErrorCause extends BaseError {
public readonly cause?: unknown;
constructor(message?: string, cause?: unknown) {
super(message);
if (cause !== undefined && cause !== null) {
Object.defineProperty(this, "cause", {
value: cause,
writable: false,
enumerable: false,
configurable: false,
});
}
}
}
And here is a unit test file with 100% coverage to go with it:
import { describe, expect, test } from "vitest";
import { BaseErrorCause } from "../src/baseErrorCause";
import { inspect } from "util";
class MyError extends BaseErrorCause {}
function hideStackTraceDetailsForUnitTesting(stack: string): string {
return (
stack
// Remove the file paths, line numbers, and column numbers from the stack trace.
.replace(/^(\s*at) .*$/gm, "$1 …")
// Deduplicate lines.
.split("\n")
.filter((line, index, lines) => index === 0 || line !== lines[index - 1])
.join("\n")
);
}
describe("BaseErrorCause", () => {
test("MyError without cause", () => {
const err1 = new MyError("First error");
expect(err1.message).toBe("First error");
expect(err1.cause).toBeUndefined();
expect(hideStackTraceDetailsForUnitTesting(inspect(err1))).toEqual(`\
MyError: First error
at …`);
});
test("MyError caused by MyError", () => {
const err1 = new MyError("First error");
const err2 = new MyError("Second error", err1);
expect(err2.message).toBe("Second error");
expect(err2.cause).toBe(err1);
expect(hideStackTraceDetailsForUnitTesting(inspect(err2))).toEqual(`\
MyError: Second error
at …
... 7 lines matching cause stack trace ...
at …
[cause]: MyError: First error
at …
}`);
});
test("MyError caused by MyError caused by MyError", () => {
const err1 = new MyError("First error");
const err2 = new MyError("Second error", err1);
const err3 = new MyError("Third error", err2);
expect(err3.message).toBe("Third error");
expect(err3.cause).toBe(err2);
expect(hideStackTraceDetailsForUnitTesting(inspect(err3))).toEqual(`\
MyError: Third error
at …
... 7 lines matching cause stack trace ...
at …
[cause]: MyError: Second error
at …
... 7 lines matching cause stack trace ...
at …
[cause]: MyError: First error
at …
}
}`);
});
test("MyError caused by string", () => {
const err1 = new MyError("The error", "The cause");
expect(err1.message).toBe("The error");
expect(err1.cause).toBe("The cause");
expect(hideStackTraceDetailsForUnitTesting(inspect(err1))).toEqual(`\
MyError: The error
at …
[cause]: 'The cause'
}`);
});
});
So I guess there is no longer a real reason to use this package. Just copy my code above and have fun. :)