Skip to main content

abstractQuestion <T>

Questions describe how actors should query the system under test or the test environment to retrieve some information.

Questions are the core building block of the Screenplay Pattern, along with Actors, Abilities, Interactions, and Tasks.

Learn more about:

Implementing a basic custom Question

 import { actorCalled, AnswersQuestions, UsesAbilities, Question } from '@serenity-js/core'
import { Ensure, equals } from '@serenity-js/assertions'

const LastItemOf = <T>(list: T[]): Question<T> =>
Question.about('last item from the list', (actor: AnswersQuestions & UsesAbilities) => {
return list[list.length - 1]
});

await actorCalled('Quentin').attemptsTo(
Ensure.that(LastItemFrom([1,2,3]), equals(3)),
)

Implementing a Question that uses an Ability

Just like the interactions, a Question also can use actor’s abilities.

Here, we use the ability to CallAnApi to retrieve a property of an HTTP response.

 import { AnswersQuestions, UsesAbilities, Question } from '@serenity-js/core'
import { CallAnApi } from '@serenity-js/rest'

const TextOfLastResponseStatus = () =>
Question.about<number>(`the text of the last response status`, actor => {
return CallAnApi.as(actor).mapLastResponse(response => response.statusText)
})

Learn more

Mapping answers to other questions

Apart from retrieving information, questions can be used to transform information retrieved by other questions.

Here, we use the factory method Question.about to produce a question that makes the received actor answer LastResponse.status and then compare it against some expected value.

import { actorCalled, AnswersQuestions, UsesAbilities, Question } from '@serenity-js/core'
import { CallAnApi, LastResponse } from '@serenity-js/rest'
import { Ensure, equals } from '@serenity-js/assertions'

const RequestWasSuccessful = () =>
Question.about<number>(`the text of the last response status`, actor => {
return LastResponse.status().answeredBy(actor) === 200;
});

await actorCalled('Quentin')
.whoCan(CallAnApi.at('https://api.example.org/'));
.attemptsTo(
Send.a(GetRequest.to('/books/0-688-00230-7')),
Ensure.that(RequestWasSuccessful(), isTrue()),
);

Note that the above example is for demonstration purposes only, Serenity/JS provides an easier way to verify the response status of the LastResponse:

import { actorCalled } from '@serenity-js/core'
import { CallAnApi, LastResponse } from '@serenity-js/rest'
import { Ensure, equals } from '@serenity-js/assertions'

await actorCalled('Quentin')
.whoCan(CallAnApi.at('https://api.example.org/'));
.attemptsTo(
Send.a(GetRequest.to('/books/0-688-00230-7')),
Ensure.that(LastResponse.status(), equals(200)),
);

Hierarchy

Index

Constructors

constructor

Methods

staticabout

  • Factory method that simplifies the process of defining custom questions.

    Defining a custom question

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

    const EnvVariable = (name: string) =>
    Question.about(`the ${ name } env variable`, actor => process.env[name])

    Type parameters

    • Answer_Type
    • Supported_Context_Type

    Parameters

    Returns MetaQuestionAdapter<Supported_Context_Type, Awaited<Answer_Type>>

staticfromObject

  • Generates a QuestionAdapter that recursively resolves any Answerable fields of the provided object, including Answerable fields of nested objects.

    Optionally, the method accepts overrides to be shallow-merged with the fields of the original source, producing a new merged object.

    Overrides are applied from left to right, with subsequent objects overwriting property assignments of the previous ones.

    Resolving an object recursively using Question.fromObject

    import { actorCalled, Question } from '@serenity-js/core'
    import { Send, PostRequest } from '@serenity-js/rest'
    import { By, Text, PageElement } from '@serenity-js/web'

    await actorCalled('Daisy')
    .whoCan(CallAnApi.at('https://api.example.org'))
    .attemptsTo(
    Send.a(
    PostRequest.to('/products/2')
    .with(
    Question.fromObject({
    name: Text.of(PageElement.located(By.css('.name'))),
    })
    )
    )
    );

    Merging objects using Question.fromObject

     import { actorCalled, Question } from '@serenity-js/core'
    import { Send, PostRequest } from '@serenity-js/rest'
    import { By, Text, PageElement } from '@serenity-js/web'

    await actorCalled('Daisy')
    .whoCan(CallAnApi.at('https://api.example.org'))
    .attemptsTo(
    Send.a(
    PostRequest.to('/products/2')
    .with(
    Question.fromObject({
    name: Text.of(PageElement.located(By.css('.name'))),
    quantity: undefined,
    }, {
    quantity: 2,
    })
    )
    )
    );

    Learn more


    Type parameters

    • Source_Type: object

    Parameters

    Returns QuestionAdapter<RecursivelyAnswered<Source_Type>>

staticisAQuestion

  • isAQuestion<T>(maybeQuestion: unknown): maybeQuestion is Question<T>
  • Checks if the value is a Question.


    Type parameters

    • T

    Parameters

    • maybeQuestion: unknown

      The value to check

    Returns maybeQuestion is Question<T>

staticisAMetaQuestion

  • isAMetaQuestion<CT, RQT>(maybeMetaQuestion: unknown): maybeMetaQuestion is MetaQuestion<CT, RQT>
  • Checks if the value is a MetaQuestion.


    Type parameters

    Parameters

    • maybeMetaQuestion: unknown

      The value to check

    Returns maybeMetaQuestion is MetaQuestion<CT, RQT>

abstracttoString

  • toString(): string
  • Returns the description of the subject of this Question.


    Returns string

abstractdescribedAs

  • describedAs(subject: string): Question<T>
  • Changes the description of this question’s subject.


    Parameters

    • subject: string

    Returns Question<T>

abstractansweredBy

publicas

  • Maps this question to one of a different type.

    Question.about('number returned as string', actor => '42')   // returns: QuestionAdapter<string>
    .as(Number) // returns: QuestionAdapter<number>

    Type parameters

    • O

    Parameters

    • mapping: (answer: Awaited<T>) => O | Promise<O>

    Returns QuestionAdapter<O>