Skip to main content

@serenity-js/core

Follow Serenity/JS on LinkedIn Watch Serenity/JS on YouTube Join Serenity/JS Community Chat Support Serenity/JS on GitHub

Serenity/JS is an innovative open-source framework designed to make acceptance and regression testing of complex software systems faster, more collaborative and easier to scale.

⭐️ Get started with Serenity/JS!

πŸ‘‹ Join the Serenity/JS Community!

Serenity/JS Core​

@serenity-js/core is the heart of the Serenity/JS framework. It enables you to configure the framework, manage actors, and provides basic building blocks to help you design high-quality acceptance tests.

Installation​

To install this module, run the following command in your computer terminal:

npm install --save-dev @serenity-js/core

To learn more about Serenity/JS and how to use it on your project, follow the Serenity/JS Getting Started guide.

πŸ“£ Stay up to date​

New features, tutorials, and demos are coming soon! Follow Serenity/JS on LinkedIn, subscribe to Serenity/JS channel on YouTube and join the Serenity/JS Community Chat to stay up to date! Please also make sure to star ⭐️ Serenity/JS on GitHub to help others discover the framework!

Follow Serenity/JS on LinkedIn Watch Serenity/JS on YouTube Join Serenity/JS Community Chat GitHub stars

πŸ’› Support Serenity/JS​

If you appreciate all the effort that goes into making sophisticated tools easy to work with, please support our work and become a Serenity/JS GitHub Sponsor today!

GitHub Sponsors

Index

Abilities

AbilityType

AbilityType<A>: abstract new (...args: any[]) => A

Type parameters

Type declaration

    • abstract new (...args: any[]): A
    • An interface describing the static access method that every Ability class needs to provide in order to be accessible from within the interactions.

      Retrieving an ability from an interaction

      import { Ability, actorCalled, Interaction } from '@serenity-js/core';

      class MakePhoneCalls extends Ability {
      static using(phone: Phone) {
      return new MakePhoneCalls(phone);
      }

      protected constructor(private readonly phone: Phone) {
      }

      // some method that allows us to interact with the external interface of the system under test
      dial(phoneNumber: string): Promise<void> {
      // ...
      }
      }

      const Call = (phoneNumber: string) =>
      Interaction.where(`#actor calls ${ phoneNumber }`, async actor => {
      await MakePhoneCalls.as(actor).dial(phoneNumber)
      });

      await actorCalled('Connie')
      .whoCan(MakePhoneCalls.using(phone))
      .attemptsTo(
      Call(phoneNumber),
      )

      Learn more


      Parameters

      • rest...args: any[]

      Returns A

Expectations

Predicate

Predicate<Actual>: (actor: AnswersQuestions, actual: Answerable<Actual>) => Promise<ExpectationOutcome> | ExpectationOutcome

Type parameters

  • Actual

Type declaration

Questions

Answerable

Answerable<T>: Question<Promise<T>> | Question<T> | Promise<T> | T

A union type that provides a convenient way to represent any value that can be resolved by Actor.answer.


Type parameters

  • T

Answered

Answered<T>: T extends null | undefined ? T : T extends Question<Promise<infer A>> | Question<infer A> | Promise<infer A> ? Awaited<A> : T

Describes the type of answer a given Answerable would resolve to when given to Actor.answer.

Answered<Answerable<T>> === T

Type parameters

  • T

QuestionAdapterFieldDecorator

QuestionAdapterFieldDecorator<Original_Type>: { [ Field in keyof Omit<Original_Type, keyof QuestionStatement<Original_Type>> ]: Original_Type[Field] extends (...args: infer OriginalParameters) => infer OriginalMethodResult ? Field extends replace | replaceAll ? (searchValue: Answerable<string | RegExp>, replaceValue: Answerable<string>) => QuestionAdapter<string> : (...args: { [ P in keyof OriginalParameters ]: Answerable<Awaited<OriginalParameters[P]>> }) => QuestionAdapter<Awaited<OriginalMethodResult>> : Original_Type[Field] extends number | bigint | boolean | string | symbol | object ? QuestionAdapter<Awaited<Original_Type[Field]>> : any }

Describes an object recursively wrapped in QuestionAdapter proxies, so that:

  • both methods and fields of the wrapped object can be used as questions or interactions
  • method parameters of the wrapped object will accept Answerable
    <T>

Type parameters

  • Original_Type

QuestionAdapter

QuestionAdapter<Answer_Type>: Question<Promise<Answer_Type>> & Interaction & { isPresent: any } & QuestionAdapterFieldDecorator<Answer_Type>

A union type representing a proxy object returned by Question.about.

QuestionAdapter proxies the methods and fields of the wrapped object recursively, allowing them to be used as either a Question or an Interaction.


Type parameters

  • Answer_Type

MetaQuestionAdapter

MetaQuestionAdapter<Context_Type, Answer_Type>: QuestionAdapter<Answer_Type> & MetaQuestion<Context_Type, QuestionAdapter<Answer_Type>>

An extension of QuestionAdapter, that in addition to proxying methods and fields of the wrapped object can also act as a MetaQuestion.


Type parameters

  • Context_Type
  • Answer_Type

RecursivelyAnswered

RecursivelyAnswered<T>: T extends null | undefined ? T : T extends Question<Promise<infer A>> | Question<infer A> | Promise<infer A> ? RecursivelyAnswered<Awaited<A>> : T extends object ? { [ K in keyof T ]: RecursivelyAnswered<T[K]> } : T

Describes a recursively resolved plain JavaScript WithAnswerableProperties.

Typically, used in conjunction with Question.fromObject.

Using RecursivelyAnswered

import {
actorCalled, notes, q, Question, QuestionAdapter, WithAnswerableProperties
} from '@serenity-js/core';

interface RequestConfiguration {
headers: Record<string, string>;
}

const requestConfiguration: WithAnswerableProperties<RequestConfiguration> = {
headers: {
Authorization: q`Bearer ${ notes().get('authDetails').token }`
}
}

const question: QuestionAdapter<RequestConfiguration> =
Question.fromObject<RequestConfiguration>(requestConfiguration)

const answer = await actorCalled('Annie').answer(question);

const a1: RequestConfiguration = answer;
const a2: RecursivelyAnswered<WithAnswerableProperties<RequestConfiguration>> = answer;

// RequestConfiguration === RecursivelyAnswered<WithAnswerableProperties<RequestConfiguration>>

Type parameters

  • T

WithAnswerableProperties

WithAnswerableProperties<T>: T extends null | undefined ? T : T extends Question<Promise<infer A>> | Question<infer A> | Promise<infer A> ? Answerable<A> : T extends object ? { [ K in keyof T ]: WithAnswerableProperties<T[K]> } : Answerable<T>

Describes a plain JavaScript object with Answerable properties. Typically, used in conjunction with RecursivelyAnswered and Question.fromObject.

import {
actorCalled, notes, q, Question, QuestionAdapter, WithAnswerableProperties
} from '@serenity-js/core';

interface RequestConfiguration {
headers: Record<string, string>;
}

const requestConfiguration: WithAnswerableProperties<RequestConfiguration> = {
headers: {
Authorization: q`Bearer ${ notes().get('authDetails').token }`
}
}

const question: QuestionAdapter<RequestConfiguration> =
Question.fromObject<RequestConfiguration>(requestConfiguration)

const answer: RequestConfiguration = await actorCalled('Annie').answer(question);

Type parameters

  • T

Serenity

ClassDescription

ClassDescription: string | [string] | [string, any]

ClassDescription describes the Node module ID and optionally:

  • a named export that you want to import
  • a parameter that should be passed to the static fromJSON method if the imported type provides it.

ClassDescription is used to describe the StageCrewMembers passed to SerenityConfig.

The most basic class description is the name of a Node module that must provide a default export. For example, below definition would be interpreted as a request to import the default export from the @serenity-js/serenity-bdd module and instantiate it using its no-arg constructor:

import { configure } from '@serenity-js/core'

configure({
crew: [
`@serenity-js/serenity-bdd`
]
})

Class description can also include a named export to be imported. For example, below definition would be interpreted as a request to import the SerenityBDDReporter type from @serenity-js/serenity-bdd and instantiate it using its no-arg constructor:

import { configure } from '@serenity-js/core'

configure({
crew: [
`@serenity-js/serenity-bdd:SerenityBDDReporter`
]
})

However, not all types have no-arg constructors. In those cases, a type offering a static fromJSON(configParam) method can be described using a tuple where the first item describes the Node module and optionally the class name, and the second item describes the configParam.

import { configure } from '@serenity-js/core'

configure({
crew: [
[ `@serenity-js/core:ArtifactArchiver`, { outputDirectory: './target/site/serenity' } ]
]
})

Note that the class description could also describe a local Node module. This can be useful when you’re writing a custom StageCrewMember implementation. For example, ./my-reporter:MyReporter would be interpreted as a request to load the MyReporter type from ./my-reporter file, located relative to the working directory of the current Node.js process.

constserenity

serenity: Serenity = ...

Serenity object is the root object of the Serenity/JS framework.