Storylines

The Storylines DSL allows users to define stories that are performed in sessions and woven in a way that is appropriate, for example, to create an activity in a user interface where only one action can be performed per session but the sessions themselves are activated in parallel.

Let’s explain how to use the library with the following example:

story('high level story', function () {
    with (new Session("session name").start()) {
        doA({parameter1: 'string', parameter2: 5});
        doB({parameter1: 2, parameter2: {name: 'David', age: 23}});
    }
});

defineEvent(Session, "DoA", function (session, event) {
    with (session) {
      request(Event("A1", event.parameter1))
      request(Event("A2", event.parameter2))
    }
});

defineEvent(Session, "DoB", function (session, event) {
    with (session) {
      request(Event("B1", event.parameter1))
      request(Event("B2" + event.parameter2.name, event.parameter2.age))
    }
});

To run the code above with the storylines DSL activated, we will save it in a file with the story.js extension.

This example is also available as a colab notebook.

We, next, explain the ingredients of this example:

The Session class

All events run under some session. The general rule is that you can run multiple sessions at the same time, but you cannot perform several actions simultaneously in the same session.

When creating a new session, you can name it. The name is intended to allow different stories to refer to the same session. If no explicit name is given, the system automatically determines the name of the session as the name of the story in which it was established.

The first action we put on the session in the program was a start action that caused the session to open. This creates an opening session event and returns an object that can be performed on within the session. Only one story starts the session. If other stories want to affect a session, they should wait for it to start, create a Session with the name of the session that started and request events in this session. The execution mechanism guarantees that events in a session are executed sequentially.

The Session class serves also as a base for other classes like SeleniumSession that allows you to also use selenium operations within the session. In the child class, you can also pass a parameter to the start action of the session that allows you to determine the URL in which the session will be run. The SeleniumSession class opens a new browser windows when a session starts.

The defineEvent method

In the code presented above, the story uses the events doA and doB that are defined, using the defineEvent method below it.

The defineEvent method encapsulates a sequence of events as a high-level event that runs atomically is a session. The term "atomically" here, means that even if several high-level events are requested at the same time, only one is executed. The others, are automatically blocked. When an high-level event finishes, i.e., when the function given as a prameter to defieEvent returns, an EndOfAction event is triggered. Specifically, the output of the above program is:

  /\
 /XX\
{XXXX#####################################
 \XX/ / _ \_______ _  _____ ___  ___  ___
  \/ / ___/ __/ _ \ |/ / -_) _ \/ _ \/ _ \
    /_/  /_/  \___/___/\__/_//_/\_, /\___/
                               /___/
[SETUP] INFO Input path: /content
[EXEC] INFO Preparing to run
[EXEC>rnd] INFO B-program started
[EXEC>rnd] INFO Selected: [StartSession {JS_Obj name:"session_name", param:">>"}]
[EXEC>rnd] INFO Selected: [DoA {JS_Obj session:{JS_Obj name:"session_name"}, parameter1:"string", parameter2:5.0}]
[EXEC>rnd] INFO Selected: [A1 string]
[EXEC>rnd] INFO Selected: [A2 5]
[EXEC>rnd] INFO Selected: [EndOfAction {JS_Obj eventName:"DoA", session:{JS_Obj name:"session_name"}, parameter1:"string", parameter2:5.0, lib:"bp-base"}]
[EXEC>rnd] INFO Selected: [DoB {JS_Obj session:{JS_Obj name:"session_name"}, parameter1:2.0, parameter2:{JS_Obj name:"David", age:23.0}}]
[EXEC>rnd] INFO Selected: [B1 2]
[EXEC>rnd] INFO Selected: [B2David 23]
[EXEC>rnd] INFO Selected: [EndOfAction {JS_Obj eventName:"DoB", session:{JS_Obj name:"session_name"}, parameter1:2.0, parameter2:{JS_Obj name:"David", age:23.0}, lib:"bp-base"}]
[EXEC>rnd] INFO B-program ended
[EXEC] INFO Test Result: SUCCESS

Notice the two EndOfAction events. Note also that the triggering of the high-level triggers, in turn, the function that requests a sequence of low-level events.

The first parameter to defineEvent is a session class. This allows to define high-level events that connect to specific type of sessions such as the SeleniumSession.

The second parameter to defineEvent is the name of the event. This is translated to two automatically defined javascript objects: 1) A function of the session prototype object whose name is like the name of the event, but with the first letter in lower case; 2) An event object whose name is like the name of the event, but with the first letter in upper case.