Hi @jan, firstly thank you so much for making code stuff so readable and giving us a chance to work on Serenity-JS. I was wondering, how can we perform similar type of actions in a single line instead using multiple clicks. Do we have loops in serenity?
Actor.attemptsTo(
Click.on(Test.proceed),
Click.on(Test.proceed),
Click.on(Test.proceed),
Click.on(Test.ok));
Is there any possibility in serenity to write it up something like,
Clicks.on(Test.proceed).while.isPresent() ,
Not sure for syntax,need your help ..
Hi @AbhineetSharmax! Thanks for your kind words, I'm glad that you find Serenity/JS useful!
If you wanted to execute a certain task several times, you could approach it as follows:
Serenity/JS is all about composition, so I'd start with creating a "repeater" task that will execute the task I pass in a specified number of times:
import { Activity } from '@serenity-js/core/lib/screenplay';
export const Repeat = (times: number, activity: Activity) =>
Task.where(`#actor repeats the activity to ${ activity } ${ times } times`,
...Array.from({ length: times }).map(() => activity)
);
There's a little bit of array magic going on above, so let me walk you through that:
Array.from({ length: n }) // creates an array of n elements
Since we don't care about the values the array is populated with, just that it has n elements, we can then map that to an activity, therefore receiving an array of n activities:
Array.from({ length: times }).map(() => task)
// if times === 3, the result is:
// [ activity, activity, activity ]
To make the above result compatible with Task.where(description: string, ...activities: Activity[]), I use the spread operator:
Task.where(`#actor repeats the activity to ${ activity } ${ times } times`,
...Array.from({ length: times }).map(() => activity)
);
Next, I wrap above construct in a function so that it can be configured at runtime:
export const Repeat = (times: number, activity: Activity) =>
Task.where(`#actor repeats the task to ${ activity } ${ times } times`,
...Array.from({ length: times }).map(() => activity)
);
With that in place, we can make the actor repeat a specific task several times:
actor.attemptsTo(
Repeat(3, Click.on(Test.proceed))
);
Let me know if that helps!
Jan
Edit: Updated the code samples to make them compatible with both Task and Interaction interfaces as per the @AbhineetSharmax's suggestion.
@AbhineetSharmax thanks for writing me. I really appreciate the attention. :-)
Greetings, @jan-molak ..
There is a very small change, that i would like to mention in above implementation..
For the Repeater task, instead of taking a Task, we may switch to Activity.
So, for each Task Actor would performs some activity.
export const Repeat = (times: number, activity : Activity) =>
Task.where(#actor repeats the task to ${ activity } ${ times } times,
...Array.from({ length: times }).map(() => activity)
);
Above changes is done to resolve incompatibility issue. Argument 'Click' is not assignable to parameter of type 'Task'.
Once again, @jan-molak Thank you so much for the super quick solutions, you are really awesome :)
You're welcome! And you're right - the samples should've used the Activity interface instead. I've updated them now to incorporate your suggestion.