Skip to main content

externalabstractQuestion <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`, async actor => {
const status = await actor.answer(LastResponse.status());

return status === 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

Methods

staticexternalabout

  • 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>>

staticexternalfromObject

  • 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>>

staticexternalfromArray

staticexternalisAQuestion

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


    Type parameters

    • T

    Parameters

    • externalmaybeQuestion: unknown

      The value to check

    Returns maybeQuestion is Question<T>

staticexternalisAMetaQuestion

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


    Type parameters

    Parameters

    • externalmaybeMetaQuestion: unknown

      The value to check

    Returns maybeMetaQuestion is MetaQuestion<CT, RQT>

staticexternalformattedValue

  • Creates a MetaQuestion that can be composed with any Answerable to produce a single-line description of its value.

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

    const accountDetails = () =>
    Question.about('account details', actor => ({ name: 'Alice', age: 28 }))

    await actorCalled('Alice').attemptsTo(
    Ensure.that(
    Question.formattedValue().of(accountDetails()),
    equals('{ name: "Alice", age: 28 }'),
    ),
    )

    Parameters

    Returns MetaQuestion<any, Question<Promise<string>>>

staticexternalvalue

  • Creates a MetaQuestion that can be composed with any Answerable to return its value when the answerable is a Question, or the answerable itself otherwise.

    The description of the resulting question is produced by calling Question.describedBy on the provided answerable.

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

    const accountDetails = () =>
    Question.about('account details', actor => ({ name: 'Alice', age: 28 }))

    await actorCalled('Alice').attemptsTo(
    Ensure.that(
    Question.description().of(accountDetails()),
    equals('account details'),
    ),
    Ensure.that(
    Question.value().of(accountDetails()),
    equals({ name: 'Alice', age: 28 }),
    ),
    )

    Type parameters

    • Answer_Type

    Returns MetaQuestion<Answer_Type, Question<Promise<Answer_Type>>>

externalabstractansweredBy

externaldescribedAs

  • Changes the description of this object, as returned by Describable.describedBy and Describable.toString.


    Parameters

    • externaldescription: Answerable<string> | MetaQuestion<Awaited<T>, Question<Promise<string>>>

      Replaces the current description according to the following rules:

      • If description is an Answerable, it replaces the current description
      • If description is a MetaQuestion, the current description is passed as context to description.of(context), and the result replaces the current description

    Returns this

publicexternalas

  • 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

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

      Returns QuestionAdapter<O>

    externaldescribedBy

    • Resolves the description of this object in the context of the provided actor.


      Parameters

      Returns Promise<string>

    externaltoString

    • toString(): string
    • Returns a human-readable description of this object.


      Returns string