Fix failing tests due to failed error logging

Unit and integration tests have been failing due to failed logging of
`Error` objects. These were creating an issue where `mocha` was not
properly returning right exit codes, leading to test pipelines
incorrectly passing despite test failures.

- Fix runtime behavior of failing to retrieve error stacks.
- Add tests for error handling.
- Add more robust custom error handling.

Related issues: babel/babel#14273, vuejs/vue-cli#6994.
This commit is contained in:
undergroundwires
2023-08-10 19:49:08 +02:00
parent 061afad967
commit 986ba078a6
5 changed files with 232 additions and 12 deletions

View File

@@ -0,0 +1,174 @@
// eslint-disable-next-line max-classes-per-file
import 'mocha';
import { expect } from 'chai';
import { CustomError, Environment } from '@/application/Common/CustomError';
describe('CustomError', () => {
afterEach(() => {
Environment.getSetPrototypeOf = () => Object.setPrototypeOf;
Environment.getCaptureStackTrace = () => Error.captureStackTrace;
});
describe('sets members as expected', () => {
it('`name`', () => {
// arrange
const expectedName = CustomErrorConcrete.name;
// act
const sut = new CustomErrorConcrete();
// assert
expect(sut.name).to.equal(expectedName);
});
it('`message`', () => {
// arrange
const expectedMessage = 'expected message';
// act
const sut = new CustomErrorConcrete(expectedMessage);
// assert
expect(sut.message).to.equal(expectedMessage);
});
it('`cause`', () => {
// arrange
const expectedCause = new Error('expected cause');
// act
const sut = new CustomErrorConcrete(undefined, {
cause: expectedCause,
});
// assert
expect(sut.cause).to.equal(expectedCause);
});
describe('`stack`', () => {
it('sets using `getCaptureStackTrace` if available', () => {
// arrange
const mockStackTrace = 'mocked stack trace';
Environment.getCaptureStackTrace = () => (error) => {
(error as Error).stack = mockStackTrace;
};
// act
const sut = new CustomErrorConcrete();
// assert
expect(sut.stack).to.equal(mockStackTrace);
});
it('defined', () => {
// arrange
const customError = new CustomErrorConcrete();
// act
const { stack } = customError;
// assert
expect(stack).to.not.equal(undefined);
});
});
});
describe('retains correct prototypes', () => {
it('instance of `Error`', () => {
// arrange
const expected = Error;
// act
const sut = new CustomErrorConcrete();
// assert
expect(sut).to.be.an.instanceof(expected);
});
it('instance of `CustomErrorConcrete`', () => {
// arrange
const expected = CustomErrorConcrete;
// act
const sut = new CustomErrorConcrete();
// assert
expect(sut).to.be.an.instanceof(expected);
});
it('instance of `CustomError`', () => {
// arrange
const expected = CustomError;
// act
const sut = new CustomErrorConcrete();
// assert
expect(sut).to.be.an.instanceof(expected);
});
it('thrown error retains `CustomError` type', () => {
// arrange
const expected = CustomError;
let thrownError: unknown;
// act
try {
throw new CustomErrorConcrete('message');
} catch (e) {
thrownError = e;
}
// assert
expect(thrownError).to.be.an.instanceof(expected);
});
});
describe('environment compatibility', () => {
describe('Object.setPrototypeOf', () => {
it('does not throw if unavailable', () => {
// arrange
Environment.getSetPrototypeOf = () => undefined;
// act
const act = () => new CustomErrorConcrete();
// assert
expect(act).to.not.throw();
});
it('calls if available', () => {
// arrange
let wasCalled = false;
const setPrototypeOf = () => { wasCalled = true; };
Environment.getSetPrototypeOf = () => setPrototypeOf;
// act
// eslint-disable-next-line no-new
new CustomErrorConcrete();
// assert
expect(wasCalled).to.equal(true);
});
});
describe('Error.captureStackTrace', () => {
it('does not throw if unavailable', () => {
// arrange
Environment.getCaptureStackTrace = () => undefined;
// act
const act = () => new CustomErrorConcrete();
// assert
expect(act).to.not.throw();
});
it('calls if available', () => {
// arrange
let wasCalled = false;
const captureStackTrace = () => { wasCalled = true; };
Environment.getCaptureStackTrace = () => captureStackTrace;
// act
// eslint-disable-next-line no-new
new CustomErrorConcrete();
// assert
expect(wasCalled).to.equal(true);
});
});
});
describe('runtime behavior sanity checks', () => {
/*
* These tests are intended to verify the behavior of the JavaScript runtime or environment,
* rather than specific application logic. Typically, we avoid such tests because we
* trust the behavior of the underlying platform. However, they've been included here
* due to previous unexpected issues, specifically failures when trying to log
* `new Error().stack`. These issues arose because of factors like transpilation,
* source-mapping, and variances in JavaScript engine behaviors.
*/
it('`Error.stack` is defined', () => {
const error = new Error();
// act
const { stack } = error;
// assert
expect(stack).to.not.equal(undefined);
});
});
});
class CustomErrorConcrete extends CustomError { }