@serenity-js/core
Version:
The core Serenity/JS framework, providing the Screenplay Pattern interfaces, as well as the test reporting and integration infrastructure
174 lines • 6.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExecutionSuccessful = exports.ExecutionSkipped = exports.ExecutionIgnored = exports.ImplementationPending = exports.ExecutionFailedWithAssertionError = exports.ExecutionFailedWithError = exports.ExecutionCompromised = exports.ProblemIndication = exports.Outcome = void 0;
const tiny_types_1 = require("tiny-types");
const index_js_1 = require("../errors/index.js");
class Outcome extends tiny_types_1.TinyType {
code;
/**
* Symbol used to brand Outcome 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/Outcome');
/**
* 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 an Outcome instance using the brand
if (instance[Outcome.TYPE_BRAND] !== true) {
return false;
}
// When checking against the base Outcome class, any branded instance qualifies
if (this.name === 'Outcome') {
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;
}
static fromJSON = (o) => (0, tiny_types_1.match)(o.code)
.when(ExecutionCompromised.Code, _ => ExecutionCompromised.fromJSON(o))
.when(ExecutionFailedWithError.Code, _ => ExecutionFailedWithError.fromJSON(o))
.when(ExecutionFailedWithAssertionError.Code, _ => ExecutionFailedWithAssertionError.fromJSON(o))
.when(ImplementationPending.Code, _ => ImplementationPending.fromJSON(o))
.when(ExecutionIgnored.Code, _ => ExecutionIgnored.fromJSON(o))
.when(ExecutionSkipped.Code, _ => ExecutionSkipped.fromJSON(o))
.when(ExecutionSuccessful.Code, _ => ExecutionSuccessful.fromJSON(o))
.else(_ => { throw new Error(`Outcome could not be deserialised: ${JSON.stringify(o)}`); });
constructor(code) {
super();
this.code = code;
// Brand the instance for cross-module instanceof checks
this[Outcome.TYPE_BRAND] = true;
}
isWorseThan(another) {
const code = (another instanceof Outcome)
? another.code
: another.Code;
return this.code < code;
}
toJSON() {
return {
code: this.code,
};
}
}
exports.Outcome = Outcome;
class ProblemIndication extends Outcome {
error;
constructor(error, code) {
super(code);
this.error = error;
}
toJSON() {
return {
code: this.code,
error: index_js_1.ErrorSerialiser.serialise(this.error),
};
}
}
exports.ProblemIndication = ProblemIndication;
/**
* Indicates a failure due to external events or systems that compromise the validity of the test.
*/
class ExecutionCompromised extends ProblemIndication {
static Code = 1 << 0;
static fromJSON = (o) => new ExecutionCompromised(index_js_1.ErrorSerialiser.deserialise(o.error));
constructor(error) {
super(error, ExecutionCompromised.Code);
}
}
exports.ExecutionCompromised = ExecutionCompromised;
/**
* Indicates a failure due to an error other than recognised external system and assertion failures
*/
class ExecutionFailedWithError extends ProblemIndication {
static Code = 1 << 1;
static fromJSON = (o) => new ExecutionFailedWithError(index_js_1.ErrorSerialiser.deserialise(o.error));
constructor(error) {
super(error, ExecutionFailedWithError.Code);
}
}
exports.ExecutionFailedWithError = ExecutionFailedWithError;
/**
* Execution of an Activity or Scene has failed due to an assertion error;
*/
class ExecutionFailedWithAssertionError extends ProblemIndication {
static Code = 1 << 2;
static fromJSON = (o) => new ExecutionFailedWithAssertionError(index_js_1.ErrorSerialiser.deserialise(o.error));
constructor(error) {
super(error, ExecutionFailedWithAssertionError.Code);
}
}
exports.ExecutionFailedWithAssertionError = ExecutionFailedWithAssertionError;
/**
* A pending Activity is one that has been specified but not yet implemented.
* A pending Scene is one that has at least one pending Activity.
*/
class ImplementationPending extends ProblemIndication {
static Code = 1 << 3;
static fromJSON = (o) => new ImplementationPending(index_js_1.ErrorSerialiser.deserialise(o.error));
constructor(error) {
super(error, ImplementationPending.Code);
}
}
exports.ImplementationPending = ImplementationPending;
/**
* The result of the scenario should be ignored, most likely because it's going to be retried.
*/
class ExecutionIgnored extends ProblemIndication {
static Code = 1 << 4;
static fromJSON = (o) => new ExecutionIgnored(index_js_1.ErrorSerialiser.deserialise(o.error));
constructor(error) {
super(error, ExecutionIgnored.Code);
}
}
exports.ExecutionIgnored = ExecutionIgnored;
/**
* The Activity was not executed because a previous one has failed.
* A whole Scene can be marked as skipped to indicate that it is currently "work-in-progress"
*/
class ExecutionSkipped extends Outcome {
error;
static Code = 1 << 5;
static fromJSON = (o) => new ExecutionSkipped(o.error && index_js_1.ErrorSerialiser.deserialise(o.error));
constructor(error) {
super(ExecutionSkipped.Code);
this.error = error;
}
toJSON() {
return {
code: this.code,
error: this.error && index_js_1.ErrorSerialiser.serialise(this.error),
};
}
}
exports.ExecutionSkipped = ExecutionSkipped;
/**
* Scenario or activity ran as expected.
*/
class ExecutionSuccessful extends Outcome {
static Code = 1 << 6;
static fromJSON = (o) => new ExecutionSuccessful();
constructor() {
super(ExecutionSuccessful.Code);
}
}
exports.ExecutionSuccessful = ExecutionSuccessful;
//# sourceMappingURL=outcomes.js.map