UNPKG

@serenity-js/core

Version:

The core Serenity/JS framework, providing the Screenplay Pattern interfaces, as well as the test reporting and integration infrastructure

142 lines 4.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RuntimeError = void 0; const tiny_types_1 = require("tiny-types"); /** * Base class for custom errors that may occur during execution of a test scenario. * * ## Defining a custom error * * ```ts * import { RuntimeError } from '@serenity-js/core' * * export class CustomError extends RuntimeError { * constructor(message: string, cause?: Error) { * super(CustomError, message, cause); * } * } * ``` * * ## Wrapping a sync error * * ```ts * try { * operationThatMightThrowAnError(); * } catch(error) { * // catch and re-throw * throw new CustomError('operationThatMightThrowAnError has failed', error); * } * ``` * * ## Wrapping an async error * * ```ts * operationThatMightRejectAPromise() * .catch(error => { * // catch and re-throw * throw new CustomError('operationThatMightThrowAnError has failed', error) * }) * ``` * * ## Registering a custom error with [`ErrorSerialiser`](https://serenity-js.org/api/core/class/ErrorSerialiser/) * * ```ts * import { RuntimeError } from '@serenity-js/core' * import { ErrorSerialiser } from '@serenity-js/core/io' * * export class CustomError extends RuntimeError { * * static fromJSON(serialised: JSONObject): CustomError { * const error = new CustomError( * serialised.message as string, * ErrorSerialiser.deserialise(serialised.cause as string | undefined), * ); * * error.stack = serialised.stack as string; * * return error; * } * * constructor(message: string, cause?: Error) { * super(CustomError, message, cause); * } * } * * ErrorSerialiser.registerErrorTypes(CustomError) * ``` * * @group Errors */ class RuntimeError extends Error { cause; /** * Symbol used to brand RuntimeError instances for cross-module instanceof checks. * This addresses the dual-package hazard where the same class loaded from * both ESM and CJS creates distinct constructor functions. */ static TYPE_BRAND = Symbol.for('@serenity-js/core/RuntimeError'); /** * Custom instanceof check that works across module boundaries. * This addresses the dual-package hazard where the same class loaded from * both ESM and CJS creates distinct constructor functions. * * The check walks up the prototype chain of the instance and compares * constructor names, which remain consistent across module boundaries. */ static [Symbol.hasInstance](instance) { if (instance === null || typeof instance !== 'object') { return false; } // First, verify this is a RuntimeError instance using the brand if (instance[RuntimeError.TYPE_BRAND] !== true) { return false; } // When checking against the base RuntimeError class, any branded instance qualifies if (this.name === 'RuntimeError') { return true; } // For subclass checks, walk the prototype chain and compare by name // This works across ESM/CJS boundaries where constructor references differ let proto = Object.getPrototypeOf(instance); while (proto !== null) { if (proto.constructor?.name === this.name) { return true; } proto = Object.getPrototypeOf(proto); } return false; } /** * @param type - Constructor function used to instantiate a subclass of a RuntimeError * @param message - Human-readable description of the error * @param [cause] - The root cause of this [`RuntimeError`](https://serenity-js.org/api/core/class/RuntimeError/), if any */ constructor(type, message, cause) { const errorMessage = message || ''; const isMultiline = errorMessage.includes('\n'); const causeAlreadyIncluded = cause?.message && errorMessage.includes(cause.message); super(isMultiline || causeAlreadyIncluded ? errorMessage : errorMessage + (cause && cause?.message ? `; ${cause.message}` : '')); this.cause = cause; Object.setPrototypeOf(this, type.prototype); this.name = this.constructor.name; // Brand the instance for cross-module instanceof checks this[RuntimeError.TYPE_BRAND] = true; Error.captureStackTrace(this, type); if (cause) { this.stack = `${this.stack}\nCaused by: ${cause.stack}`; } } /** * Human-readable description of the error */ toString() { return `${this.constructor.name}: ${this.message}`; } toJSON() { return tiny_types_1.TinyType.prototype.toJSON.apply(this); } } exports.RuntimeError = RuntimeError; //# sourceMappingURL=RuntimeError.js.map