UNPKG

@serenity-js/core

Version:

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

274 lines 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Serenity = void 0; const tiny_types_1 = require("tiny-types"); const errors_1 = require("./errors"); const io_1 = require("./io"); const screenplay_1 = require("./screenplay"); const Extras_1 = require("./stage/Extras"); const Stage_1 = require("./stage/Stage"); const StageManager_1 = require("./stage/StageManager"); /** * @group Serenity */ class Serenity { clock; static defaultCueTimeout = screenplay_1.Duration.ofSeconds(5); static defaultInteractionTimeout = screenplay_1.Duration.ofSeconds(5); static defaultActors = new Extras_1.Extras(); stage; fileSystem; outputStream = process.stdout; classLoader; workingDirectory; /** * @param clock * @param cwd */ constructor(clock = new screenplay_1.Clock(), cwd = process.cwd()) { this.clock = clock; this.stage = new Stage_1.Stage(Serenity.defaultActors, new StageManager_1.StageManager(Serenity.defaultCueTimeout, clock), new errors_1.ErrorFactory(), clock, Serenity.defaultInteractionTimeout); this.classLoader = new io_1.ClassLoader(new io_1.ModuleLoader(cwd), new io_1.ClassDescriptionParser()); this.workingDirectory = new io_1.Path(cwd); this.fileSystem = new io_1.FileSystem(this.workingDirectory); } /** * Configures Serenity/JS. Every call to this function * replaces the previous configuration provided, * so this function should be called exactly once * in your test suite. * * @param config */ configure(config) { const looksLikeBuilder = (0, io_1.has)({ build: 'function' }); const looksLikeStageCrewMember = (0, io_1.has)({ assignedTo: 'function', notifyOf: 'function' }); const cueTimeout = config.cueTimeout ? (0, tiny_types_1.ensure)('cueTimeout', config.cueTimeout, (0, tiny_types_1.isInstanceOf)(screenplay_1.Duration)) : Serenity.defaultCueTimeout; const interactionTimeout = config.interactionTimeout ? (0, tiny_types_1.ensure)('interactionTimeout', config.interactionTimeout, (0, tiny_types_1.isInstanceOf)(screenplay_1.Duration)) : Serenity.defaultInteractionTimeout; if (config.outputStream) { this.outputStream = config.outputStream; } this.stage = new Stage_1.Stage(Serenity.defaultActors, new StageManager_1.StageManager(cueTimeout, this.clock), new errors_1.ErrorFactory(config.diffFormatter ?? new errors_1.NoOpDiffFormatter()), this.clock, interactionTimeout); if (config.actors) { this.engage(config.actors); } if (Array.isArray(config.crew)) { this.stage.assign(...config.crew.map((stageCrewMemberDescription, i) => { const stageCrewMember = this.classLoader.looksLoadable(stageCrewMemberDescription) ? this.classLoader.instantiate(stageCrewMemberDescription) : stageCrewMemberDescription; if (looksLikeBuilder(stageCrewMember)) { return stageCrewMember.build({ stage: this.stage, fileSystem: this.fileSystem, outputStream: this.outputStream, }); } if (looksLikeStageCrewMember(stageCrewMember)) { return stageCrewMember.assignedTo(this.stage); } throw new errors_1.ConfigurationError((0, io_1.d) `Entries under \`crew\` should implement either StageCrewMember or StageCrewMemberBuilder interfaces, \`${stageCrewMemberDescription}\` found at index ${i}`); })); } } /** * Re-configures Serenity/JS with a new [cast](https://serenity-js.org/api/core/class/Cast/) of [actors](https://serenity-js.org/api/core/class/Actor/) * you want to use in any subsequent calls to [`actorCalled`](https://serenity-js.org/api/core/function/actorCalled/). * * For your convenience, use [`engage`](https://serenity-js.org/api/core/function/engage/) function instead, * which provides an alternative to calling [`Actor.whoCan`](https://serenity-js.org/api/core/class/Actor/#whoCan) directly in your tests * and is typically invoked in a "before all" or "before each" hook of your test runner of choice. * * If your implementation of the [cast](https://serenity-js.org/api/core/class/Cast/) interface is stateless, * you can invoke this function just once before your entire test suite is executed, see * - [`beforeAll`](https://jasmine.github.io/api/3.6/global.html#beforeAll) in Jasmine, * - [`before`](https://mochajs.org/#hooks) in Mocha, * - [`BeforeAll`](https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/hooks.md#beforeall--afterall) in Cucumber.js * * However, if your [cast](https://serenity-js.org/api/core/class/Cast/) holds state that you want to reset before each scenario, * it's better to invoke `engage` before each test using: * - [`beforeEach`](https://jasmine.github.io/api/3.6/global.html#beforeEach) in Jasmine * - [`beforeEach`](https://mochajs.org/#hooks) in Mocha, * - [`Before`](https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/hooks.md#hooks) in Cucumber.js * * ## Engaging a cast of actors * * ```ts * import { Actor, Cast } from '@serenity-js/core'; * * class Actors implements Cast { * prepare(actor: Actor) { * return actor.whoCan( * // ... abilities you'd like the Actor to have * ); * } * } * * engage(new Actors()); * ``` * * ### Using with Mocha test runner * * ```ts * import { beforeEach } from 'mocha' * * beforeEach(() => engage(new Actors())) * ``` * * ### Using with Jasmine test runner * * ```ts * import 'jasmine' * * beforeEach(() => engage(new Actors())) * ``` * * ### Using with Cucumber.js test runner * * ```ts * import { Before } from '@cucumber/cucumber' * * Before(() => engage(new Actors())) * ``` * * ## Learn more * - [`Actor`](https://serenity-js.org/api/core/class/Actor/) * - [`Cast`](https://serenity-js.org/api/core/class/Cast/) * - [`engage`](https://serenity-js.org/api/core/function/engage/) * * @param actors */ engage(actors) { this.stage.engage((0, tiny_types_1.ensure)('actors', actors, (0, tiny_types_1.property)('prepare', (0, tiny_types_1.isDefined)()))); } /** * Instantiates or retrieves an [`Actor`](https://serenity-js.org/api/core/class/Actor/) * called `name` if one has already been instantiated. * * For your convenience, use [`actorCalled`](https://serenity-js.org/api/core/function/actorCalled/) function instead. * * ## Usage with Mocha * * ```typescript * import { describe, it } from 'mocha'; * import { actorCalled } from '@serenity-js/core'; * * describe('Feature', () => { * * it('should have some behaviour', () => * actorCalled('James').attemptsTo( * // ... activities * )) * }) * ``` * * ## Usage with Jasmine * * ```typescript * import 'jasmine'; * import { actorCalled } from '@serenity-js/core'; * * describe('Feature', () => { * * it('should have some behaviour', () => * actorCalled('James').attemptsTo( * // ... activities * )) * }) * ``` * * ## Usage with Cucumber * * ```typescript * import { actorCalled } from '@serenity-js/core'; * import { Given } from '@cucumber/cucumber'; * * Given(/(.*?) is a registered user/, (name: string) => * actorCalled(name).attemptsTo( * // ... activities * )) * ``` * * ## Learn more * * - [`engage`](https://serenity-js.org/api/core/function/engage/) * - [`Actor`](https://serenity-js.org/api/core/class/Actor/) * - [`Cast`](https://serenity-js.org/api/core/class/Cast/) * - [`actorCalled`](https://serenity-js.org/api/core/function/actorCalled/) * * @param name * The name of the actor to instantiate or retrieve */ theActorCalled(name) { return this.stage.theActorCalled(name); } /** * Retrieves an actor who was last instantiated or retrieved * using [`Serenity.theActorCalled`](https://serenity-js.org/api/core/class/Serenity/#theActorCalled). * * This function is particularly useful when automating Cucumber scenarios. * * For your convenience, use [`actorInTheSpotlight`](https://serenity-js.org/api/core/function/actorInTheSpotlight/) function instead. * * ## Usage with Cucumber * * ```ts * import { actorCalled } from '@serenity-js/core'; * import { Given, When } from '@cucumber/cucumber'; * * Given(/(.*?) is a registered user/, (name: string) => * actorCalled(name).attemptsTo( * // ... activities * )) * * When(/(?:he|she|they) browse their recent orders/, () => * actorInTheSpotlight().attemptsTo( * // ... activities * )) * ``` * * ## Learn more * * - [`engage`](https://serenity-js.org/api/core/function/engage/) * - [`actorCalled`](https://serenity-js.org/api/core/function/actorCalled/) * - [`actorInTheSpotlight`](https://serenity-js.org/api/core/function/actorInTheSpotlight/) * - [`Actor`](https://serenity-js.org/api/core/class/Actor/) * - [`Cast`](https://serenity-js.org/api/core/class/Cast/) */ theActorInTheSpotlight() { return this.stage.theActorInTheSpotlight(); } announce(...events) { this.stage.announce(...events); } currentTime() { return this.stage.currentTime(); } assignNewSceneId() { return this.stage.assignNewSceneId(); } currentSceneId() { return this.stage.currentSceneId(); } assignNewActivityId(activityDetails) { return this.stage.assignNewActivityId(activityDetails); } createError(errorType, options) { return this.stage.createError(errorType, options); } /** * @package */ waitForNextCue() { return this.stage.waitForNextCue(); } cwd() { return this.workingDirectory; } } exports.Serenity = Serenity; //# sourceMappingURL=Serenity.js.map