import {Loop} from '@serenity-js/core/lib/screenplay/tasks'
public class | source

Loop

Enables the Actor to iterate over a list of items produced by any Answerable.

You can think of Loop as a more sophisticated Screenplay-style equivalent of Array.forEach. Loop is capable of working with both synchronous data structures, such as Array and Question<Array<T>>, as well as asynchronous ones, so Promise<Array<T>> and Question<Promise<Array<T>>>.

Use Loop.item to access the current item being processed by Loop, and Loop.index to access the index of Loop.item in the list.

Extends:

Task → Loop

Indirect Implements:

Examples:

Basic scenario - Iterating over a static list of items
 import { actorCalled, Loop, Log } from '@serenity-js/core';

 actorCalled('Joe').attemptsTo(
     Loop.over([ 'apple', 'banana', 'candy' ]).to(
         Log.the('current element', Loop.item<string>()),
         Log.the('current index', Loop.index()),
     ),
 );
API scenario - Iterating over items in an API response

 import { actorCalled, Loop } from '@serenity-js/core';
 import { Send, GetRequest, CallAnApi, LastResponse } from '@serenity-js/rest';
 import { Ensure, property, isGreaterThan } from '@serenity-js/assertions';

 interface TodoItem {
     userId: number;
     id: number;
     title: string;
     completed: boolean;
 }

 actorCalled('Joe').whoCan(
     CallAnApi.at('https://jsonplaceholder.typicode.com')
 ).attemptsTo(
     Send.a(GetRequest.to('/todos')),
     Loop.over(LastResponse.body<TodoItem[]>()).to(
         Ensure.that(
             Loop.item<TodoItem>(),
             property('userId', isGreaterThan(0)),
         ),
     )
 );
UI scenario - Example widget
 <nav>
     <div data-test="cookies">
         <label for="functional-cookies">
             <input type="checkbox" id="functional-cookies" />Allow functional cookies
         </label>
         <label for="performance-cookies">
             <input type="checkbox" id="performance-cookies" />Allow performance cookies
         </label>
         <label for="advertising-cookies">
             <input type="checkbox" id="advertising-cookies" />Allow advertising cookies
         </label>
     </div>
 </nav>
UI scenario - Lean Page Object
 import { Target } from '@serenity-js/protractor';
 import { browser, by } from 'protractor';

 class Cookies {
     static labels = Target.all('cookie options')
         .located(by.css('[data-test="cookies"]'));

     static checkbox = Target.the('checkbox')
         .located(by.tagName('input')),
 }
UI scenario - Performing the same set of activities with each element
 import { actorCalled, Loop } from '@serenity-js/core';
 import { Click, Text, isSelected } from '@serenity-js/protractor';
 import { Ensure, startsWith } from '@serenity-js/assertions';
 import { protractor } from 'protractor';

 actorCalled('Joe')
     .whoCan(BrowseTheWeb.using(protractor.browser))
     .attemptsTo(
         Loop.over(Cookies.labels).to(
              Ensure.that(
                 Text.of(Loop.item<ElementFinder>()),
                 startsWith('Allow'),
             ),

             Click.on(Loop.item<ElementFinder>()),
             Ensure.that(
                 Cookies.checkbox.of(Loop.item<ElementFinder>()),
                 isSelected(),
             ),
         ),
     );

Tests:

Static Method Summary

Static Public Methods
public static

index(): Question<number>

Returns the index of current Loop.item in the Answerable<Array> given to Loop.over.

public static

item(): Question<ExpectedType>

Returns the current item being processed by Loop.over.

public static

over(items: items: Answerable<ReducibleCollection>): LoopBuilder

Instantiates a Task to Loop that enables the Actor to iterate over items to perform some activities.

Constructor Summary

Public Constructor
public

constructor(items: Answerable<Reducible>, activities: Activity[])

Method Summary

Public Methods
public

performAs(actor: PerformsActivities & UsesAbilities & AnswersQuestions): Promise<void>

Makes the provided Actor perform this Task.

public

toString(): string

Generates a description to be used when reporting this Activity.

Inherited Summary

From class Task
public static

where(description: string, activities: Activity[]): Task

A factory method to make defining the Tasks more convenient.

Static Public Methods

public static index(): Question<number> source

Returns the index of current Loop.item in the Answerable<Array> given to Loop.over.

The index starts at 0.

Returns:

Question<number>

public static item(): Question<ExpectedType> source

Returns the current item being processed by Loop.over.

Please note that in order for the TypeScript transpiler to understand the exact ExpectedType of the Question produced by this method you can optionally configure it with a type variable.

For example, configuring the method with type variable of string, so Loop.item<string>(), tells the transpiler that a Question<string> will be returned.

If the type variable is not configured, the transpiler assumes that returned type is a Question<any>. This means that while your code could still work, you'd miss out on checking provided by TypeScript.

Returns:

Question<ExpectedType>

public static over(items: items: Answerable<ReducibleCollection>): LoopBuilder source

Instantiates a Task to Loop that enables the Actor to iterate over items to perform some activities.

Params:

NameTypeAttributeDescription
items items: Answerable<ReducibleCollection>

Returns:

LoopBuilder

Public Constructors

public constructor(items: Answerable<Reducible>, activities: Activity[]) source

Params:

NameTypeAttributeDescription
items Answerable<Reducible>
activities Activity[]

Public Methods

public performAs(actor: PerformsActivities & UsesAbilities & AnswersQuestions): Promise<void> source

Makes the provided Actor perform this Task.

Params:

NameTypeAttributeDescription
actor PerformsActivities & UsesAbilities & AnswersQuestions

Returns:

Promise<void>

public toString(): string source

Generates a description to be used when reporting this Activity.

Returns:

string