Code Blocks

So far, we’ve mostly been working with steps that perform a single action at a time, like HTTP steps in Protocol Scripts or navigate, click, and type steps in Browser Scripts.

Those single steps can accomplish a lot if your script proceeds in a linear fashion, as most test scripts should… but what if you need special control flow or conditional logic or looping?

Loadster’s answer is Code Blocks.

JavaScript Code Blocks

Code blocks can exist anywhere in your script. In fact, if you wanted to you could make a script that’s just a single code block, and do everything in JavaScript.

A code block
A code block

Code blocks are more flexible than ordinary step-by-step scripting because you have the control flow of an actual programming language: looping, conditionals, functions, etc.

To add a code block to your script, select Add Code Block from the top bar.

Code Block Variable and Function Scoping

Code blocks are individually scoped to the bot that is executing them. That means that if you declare a variable or function in a code block, it will exist only for that bot running the script, and not for any other bots that might also be running the script.

Just like a real human user, each bot interacts with your site independently of all the others.

Also, since the scripting language is JavaScript, ordinary JavaScript variables (declared with let or const or var) within a code block might be undefined outside that code block.

If you need a variable to persist between steps, set it as a special bot variable with bot.setVariable("v", v) so that the value ${v} remains in scope throughout the script.

Global JavaScript Objects

Beyond all the standard JavaScript language constructs, code blocks expose a few important objects specific to Loadster that you can use in your scripts.

Available Objects

For detailed information about each object and its methods, see the dedicated pages:

  • Bot Reference - The bot object represents the currently executing bot and provides methods for timing, variables, iteration control, and bot identification.

  • HTTP Reference - The http object provides methods for making HTTP requests programmatically, including GET, POST, PUT, PATCH, DELETE, and header management.

  • Browser Reference - The browser object (available in Browser Scripts) provides methods for page interaction, window management, authentication, and browser configuration.

  • Formats Reference - The formats object provides methods for encoding, decoding, timestamps, UUIDs, and random data generation.

  • XML Reference - The XML object provides methods for parsing and working with XML data using the xmldoc library.

  • JSON Reference - The JSON object provides standard JavaScript methods for parsing and working with JSON data.

  • Crypto Reference - The crypto object provides methods for hashing, HMAC generation, and cryptographic functions.

Console

Code blocks also expose a simple console for logging:

console.log(message);
console.warn(message);
console.error(message);

Messages written to the console show up in the script logs and assist you in debugging the script.

Important Notes

All object methods are synchronous - there’s no need to use await or promise chains because the actual processing is done behind the scenes by multi-threaded workers. This makes synchronous programming safe and efficient in Loadster code blocks.

Examples of Things to Do in Code Blocks

There’s nothing like learning by example! Here are a few contrived examples of things you can do with code blocks.

Example 1: Execute chained programmatic HTTP requests

The http object (representing the HTTP user agent belonging to the currently running bot) has methods for all of the common HTTP methods (GET, POST, etc).

// GET
http.get("https://petstore.loadster.app");

// GET with additional page resources
http.get("https://petstore.loadster.app", {
    resources: [
        "/style.css",
        "/favicon.ico"
    ]
});

// POST with a JSON body
http.post(
    "https://petstore.loadster.app/api/bag",
    {
        id: 16,
        name: 'Pocket Rex'
    },
    {
        headers: {
            "Content-Type": "application/json"
        }
    }
);

// POST with a form body
http.post(
    "https://petstore.loadster.app/api/login",
    "username=sloth&password=chunk",
    {
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    }
);

// DELETE
http.delete("https://petstore.loadster.app/bag/16");

Example 2: Pass custom request headers with an HTTP request

You can pass custom request headers with each request, either as an object with key-value pairs, or in an array.

// Pass request headers in an object
http.get("https://petstore.loadster.app/api/bag", {
    headers: {
        "Accept": "application/json"
    }
});

// Pass request headers in an array
http.get("https://petstore.loadster.app/api/bag", {
    headers: [
        { name: "Accept", value: "application/json" }
    ]
});

Example 3: Validate HTTP responses with validator functions

Validators (similar to the Validation Rules that you can use with ordinary HTTP steps) call a JavaScript function to examine the response and return true if it’s valid, or false if it’s not.

Validator functions can be normal JavaScript functions or the newer ES2016+ arrow functions.

You can specify multiple validator functions for a single response.

// A POST with a JSON body and validator function that requires an HTTP 201 status
http.post(
    "https://petstore.loadster.app/api/purchase",
    {
        // items were added to the cart previously, so no body required
    },
    {
        headers: [
            { name: "Content-Type", value: "application/json" }
        ],
        validators: [
            // regular function validator syntax
            function (response) {
                return response.status == 201;
            },
            // arrow function validator syntax works too!
            response => response.status === 201
        ]
    }
);

Example 4: Capture variables programmatically from an HTTP response

Often, the server will send you some data that you need to save and use later in your script.

In code blocks, you can capture these from the response and store them using a validator function. Note that we use the validators for capturing too; there is no separate property for Capturing Rules as there is with ordinary HTTP steps.

Simply call bot.setVariable(name, value) anywhere in your code block to set a bot-scoped variable. These special variables are available for the bot’s entire iteration of the script, unlike ordinary JavaScript variables which are scoped to the individual code block and may not be available to subsequent steps.

// Create a random numeric string between 0-999999
let random = String(Math.floor(1000000 * Math.random()))

// POST to register an account with the random username and capture a user token
http.post(
    "https://petstore.loadster.app/api/register",
    {
        username: "sloth_" + random,
        password: "chunk"
    },
    {
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json"
        },
        validators: [
            response => {
                var body = response.json();

                if (body.token) {
                    bot.setVariable("token", body.token); // save the auth token for later

                    return true; // valid response! treat as a success
                } else {
                    return false; // invalid response! treat as a failure
                }
            }
        ]
    }
);

Example 5: Accept HTTP 4xx/5xx status codes

By default, Loadster automatically interprets any HTTP 400 or higher status code as an error, and reports it as such.

But there are times when you might actually want an HTTP 4xx or 5xx status code. For example, you might be testing a REST API and expect it to return HTTP 409 Conflict when the resource already exists.

In such cases, you can use ignoreHttpErrors: true with your request, so that Loadster will ignore the HTTP status codes and you can interpret the status code yourself.

// Make sure we get HTTP 409 when we try to register a duplicate user
http.post(
    "https://petstore.loadster.app/api/register",
    {
        username: "admin",
        password: "admin"
    },
    {
        ignoreHttpErrors: true,
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json"
        },
        validators: [
            response => {
                return response.status === 409;
            }
        ]
    }
);

Example 6: Encoding and Decoding with Base64

Base64 encoding is often used to encode non-string data as an ASCII string, so it can be sent through text-based protocols or compared in a text editor. In code blocks, we expose the formats library to handle Base64 encoding and decoding.

const originalBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAZAAAACQCAIAAAA=';
const byteArray = formats.base64decode(originalBase64); // returns a Uint8Array
const encodedBase64 = formats.base64encode(byteArray);

if (encodedBase64 === originalBase64) {
    console.log('Good! Re-encoding the byte array got the same Base64 string we started with.');
}

If the Base64 encoded data is actually a string, you’ll need to convert the byte array into a string afterwards using String.fromCharCode, like this:

const decodedByteArray = formats.base64decode('dXNlcjpwYXNz');
const decodedString = String.fromCharCode.apply(null, decodedByteArray);
const [username, password] = decodedString.split(/:/, 2);

console.log(username);
console.log(password);

Control Flow, Looping, and Conditional Logic

Looping and conditionals can be done with all the usual JavaScript language constructs. This trivial example shows looping with a for loop, the % modulus function, an if statement, and setting and getting special bot variables with variable interpolation of ${random}.

console.log("We're in a nonsensical code step.");
console.log("It exists only to demonstrates random numbers, bot vars, and conditionals.");

// Set a bot variable to a random integer from 0 to 19
bot.setVariable("random", Math.floor(Math.random() * 20));

// Loop and make 20 requests, with a random value and random wait times
for (var i = 0; i < 20; i++) {
    if (i % 2 == 0) {
        http.get("https://example.com/evens/${random}");
    } else {
        http.get("https://example.com/odds/${random}");
    }

    http.wait(Math.random() * 2000);
}

Limitations of Code Blocks

Keep in mind that code blocks do not run in the browser, so they don’t have direct access to the live DOM the way browser-based JavaScript does. Be careful not to confuse them with on-page JavaScript (jQuery, React, etc). Rather, the scripts are executed by your bots running on the load engine, and they have access to objects like bot, http, response, console, etc.

If you need to run custom code inside the bot’s browser, check out Evaluate Blocks.

Getting Help with Your Code Blocks

If you get stuck with code blocks or want to request another helper library, we’re happy to help. We’re always interested in how you’re using code blocks in Loadster.