import {Ability} from '@serenity-js/core/lib/screenplay'
public interface | source

Ability

An Ability enables the Actor to interact with an external interface of the system under test. Technically speaking, it's a wrapper around a client of said interface.

Direct Implemented:

Indirect Implemented:

  • src/screenplay/abilities/TakeNotes.ts~TakeNotesUsingASharedNotepad
  • ,
  • src/screenplay/abilities/TakeNotes.ts~TakeNotesUsingAnEmptyNotepad

Examples:

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

 class MakePhoneCalls implements Ability {
     static as(actor: UsesAbilities): MakesPhoneCalls {
         return actor.abilityTo(MakePhoneCalls);
     }

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

     constructor(private readonly phone: Phone) {}

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

 const Connie = Actor.named('Connie').whoCan(MakePhoneCalls.using(phone));

 const Call = (phoneNumber: string) => Interaction.where(`#actor calls ${ phoneNumber }`, actor =>
     MakePhoneCalls.as(actor).dial(phoneNumber);
 );
Ability that's automatically initialised and discarded
 import {
     Ability, actorCalled, Discardable, Initialisable,
     Question, UsesAbilities
 } from '@serenity-js/core';
 import { Ensure, equals } from '@serenity-js/assertions';

 // A low-level client we want the Actor to use, i.e. a database client:
 const { Client } = require('pg');

 // A custom Ability to give an Actor access to the low-level client:
 class QueryPostgresDB implements Initialisable, Discardable, Ability {
    static as(actor: UsesAbilities) {
        return actor.abilityTo(QueryPostgresDB);
    }

    constructor(private readonly client) {
    }

    // invoked by Serenity/JS when actor.attemptsTo is first invoked
    initialise(): Promise<void> | void {
        return this.client.connect();
    }

    // Helps to ensure that the Ability is not initialised more than once
    isInitialised(): boolean {
        return this.client._connected;
    }

    // Discards any resources the Ability uses when the Actor is dismissed
    discard(): Promise<void> | void {
        return this.client.end();
    }

    // Any custom integration APIs the custom Ability
    // should make available to the Actor.
    query(query: string) {
        return this.client.query(query);
    }

    // ... other custom integration APIs
 }

 // A custom Question to allow the Actor query the system
 const CurrentDBUser = () =>
     Question.about('current db user', actor =>
         QueryPostgresDB.as(actor)
             .query('SELECT current_user')
             .then(result => result.rows[0].current_user)
     );

 // Example test scenario where the Actor uses an Ability to QueryPostgresDB
 // to assert on the username the connection has been established with
 describe('Serenity/JS', () => {
    it('can initialise and discard abilities automatically', () =>
        actorCalled('Debbie')
            .whoCan(new QueryPostgresDB(new Client()))
            .attemptsTo(
                Ensure.that(CurrentDBUser(), equals('jan'))
            ));
 });