Evaluate Blocks

Normally, Browser Bots control their browsers with ordinary user inputs, like navigate, click, and type. For most sites these are all you need to build a realistic load test script. But what about more complicated sites with things like drag-and-drop, range sliders, custom drawing, and background recalculations?

To test complicated sites like this, you might sometimes need to directly call JavaScript functions on the page or extract values from within the browser itself.

Evaluate Blocks, or eval blocks, let you execute your own JavaScript code inside the browser. This is a powerful way to do special on-page automation at a lower level than you can with individual steps.

Extracting a Simple Value

The simplest eval blocks grab a single value from the page. You can eval 1 + 1 to get 2, or you can eval navigator.language to get the language preference of the browser, or new Date() to get the current date. The last (or only) statement in your evaluate block is what gets returned.

Extracting a simple value like this is easy, but not particularly useful except for debugging your script.

Calling a JavaScript Function on the Page

From an eval block, you can call any function available to the page, much like you could in your browser’s JavaScript console.

This might be something simple like console.log() which will print a message to the JavaScript console, which you can then see in your script logs in single-user mode. That’s a nice trick for checking what’s happening on the page.

You could also use an eval block to call one of your own functions that’s declared on the page.

recalculateShippingAmounts({
    product: "product-38a64b",
    quantity: 1,
    destination: 92091
});

Most of the time, calling page functions directly isn’t necessary, since you can interact with the site using normal user inputs (navigating, clicking on elements, entering text into fields, and so on). But for some sites, evaluating code inside the browser is essential.

Evaluating a JavaScript Promise

If your eval block returns a promise, the bot will automatically wait for the promise to resolve. This means you can do asynchronous programming and long-running operations within the eval block.

For instance, here is an eval block that calls a function on the page to refresh the products list, and then checks a few seconds later to make sure at least three products have loaded.

new Promise((resolve, reject) => {
    refreshProductsList();

    setTimeout(() => {
        if (document.querySelectorAll('.product').length >= 3) {
            resolve('all products loaded');
        } else {
            reject('products failed to asynchronously load in time!');
        }
    }, 5000);
});

If the products loaded, the promise is resolved. If they failed to load, the promise is rejected, raising a script error.

Throwing an Error for Custom Validation

If your eval block throws an error, the bot will catch the error and raise it as a script error. This is a nice way to add custom validation to your browser scripts, similar to how protocol scripts have validation rules.

For example, your eval block could throw an error if any element matching the selector .error-msg exists on the page.

var errors = document.querySelectorAll('.error-msg');

if (errors.length > 0) {
    throw new Error("The page is showing an error: " + errors[0].innerText);
}

Likewise, you can throw an error if something you expect is not found on the page.

if (!document.getElementById('login-success')) {
    throw new Error("Login failed!");
}

When you’re testing a complicated site, adding manual validation with eval blocks is quite helpful, since errors could otherwise go undetected.

Communicating via WebSockets

Inside an eval block, you can communicate over WebSockets, just as you could with front-end code running on your site. Here’s an example of opening a socket, sending three messages, receiving three responses, and then resolving the promise.

Naturally, sending requests and waiting for responses over WebSockets is an asynchronous operation that could take some time. The eval block won’t conclude until the promise is resolved or rejected.

// Sends 3 pings, gets 3 pongs, and resolves the promise
new Promise((resolve, reject) => {
    let pongs = 0;
    let socket = new WebSocket("wss://echo.websocket.org");

    socket.onopen = function(e) {
        ping();
    };

    socket.onmessage = function(event) {
        pongs++;

        if (pongs === 3) {
            socket.close();

            resolve('got 3 pongs!');
        }
    };

    function ping() {
        if (!socket || socket.readyState !== 1) return;

        socket.send("ping");

        setTimeout(ping, 500);
    }
});

If the promise is never resolved or rejected, it will eventually time out with an error, according to your timeout configuration in Settings.