We value your privacy
We use necessary cookies to make Loadero work. We ask for your permission to set additional cookies to understand site usage, make site improvements and to remember your settings. We also use cookies set by other sites to help deliver content from their services. See Cookie Policy for more info.
Skip to main content

Custom commands

Loadero provides several predefined custom commands you can use in your test scripts which extend the built-in commands of the frameworks themselves. These commands provide additional functionality for various scenarios.

note

There are also packages available to support local script development by providing equivalent commands to the ones described on this page. However, not all of these commands will work the exact same way as they would on Loadero (e.g., the "Update Network" command is implemented as a no-op locally, so that using the command does not fail the test, if the script is copied over from Loadero). Make sure to check the documentation of the package. A link to the local development package for each language is provided below:

Set File

This custom command allows uploading a file to a file upload element.

Loadero sets up every participant's browser so that there are five files already prepared for use. These are intended for cases where the file content itself is not of importance and the actual objective is to simply check whether uploading a file of that size works at all or to evaluate how long it takes.

client.setFile(selector: string, fileName: string);

The selector parameter specifies the selector of the input element, e.g. input[type="file"], input#file-upload.


The fileName parameter specifies the name of the file you would like to upload. To use files names more safely in code it's recommended to use the predefined constants.


Sample fileConstantString value
100 KB (PNG)png100KB"loaderoSample100KB.png"
1 MB (PNG)png1MB"loaderoSample1MB.png"
5 MB (PNG)png5MB"loaderoSample5MB.png"
30 MB (PNG)png30MB"loaderoSample30MB.png"
100 MB (TXT)txt100MB"loaderoSample100MB.txt"

In case there is a need for other file format or size, feel free to contact us.


client => {
// Example of uploading sample file into file input field using constants
client
// Open DemoQA upload/download page
.url("https://demoqa.com/upload-download")

// Wait for up to 10s for "Choose file" button to be visible
.waitForElementVisible("#uploadFile", 10 * 1000)

// Upload Loadero sample image
.setFile("#uploadFile", loaderoConstants.sampleFiles.png100KB)
.saveScreenshot("file_set.png");
}
caution

Nightwatch.js has its own file upload method - uploadFile(), which relies on access to the file system. Participants do not have access to the file system of the server on which they are executing the test - they only have access to their specific browser's context. The prepared files mentioned above and any files downloaded during the test will be accessible within the browser's context, but these files will only be accessible via the custom setFile() command.

tip

You can also host your own custom file somewhere that is accessible on the web, navigate to that URL in the test and download the file from there. You will then have that file accessible in the browser's downloads and be able to upload it elsewhere (e.g., a mock ID photo), as long as you predict what that file's name will be and reference it with the /home/seluser/Downloads/ path prefix.

Set User Agent

This command changes the User-Agent header for outgoing requests and navigator.userAgent.

client.setUserAgent(userAgent: string);

The only parameter of this function is a string value that you want to have your user agent changed to.


info

To reset User-Agent to its default value, pass null as the new value.

info

Setting User-Agent to an empty string will actually update the User-Agent to empty string.

client => {
// Example of setting custom user-agent
client
// Open page and create screenshot with default User-Agent
.url("https://www.bing.com/")
.waitForElementVisible("[type=search]", 10 * 1000)
.saveScreenshot("default_user_agent.png")

// Set custom User-Agent value
.setUserAgent("Custom User Agent")

// Refresh the page
.refresh()

// Wait for the page to load and create screenshot with changed User-Agent
.waitForElementVisible("[type=search]", 10 * 1000)
.saveScreenshot("custom_user_agent.png")

// Set User-Agent to empty string value
.setUserAgent("")

// Refresh the page
.refresh()

// Wait for the page to load and create screenshot with empty User-Agent
.waitForElementVisible("[type=search]", 10 * 1000)
.saveScreenshot("empty_user_agent.png")

// Reset User-Agent to original value
.setUserAgent(null)

// Refresh the page
.refresh()

// Wait for the page to load and create screenshot with reset User-Agent
.waitForElementVisible("[type=search]", 10 * 1000)
.saveScreenshot("reverted_user_agent.png");
}
danger

User-Agent will revert to its original value in browser built-in pages and the WebRTC dump.

Update Network

This command allows updating network conditions dynamically during the test.

Predefined network condition usage

client.updateNetwork(networkMode: string);

The networkMode parameter specifies the network conditions that you would like to switch to. Available network configurations and their associated networkMode parameter values can be found here. These network configurations can be provided as a string value or as a constant. Constants pertaining to this command can be accessed under loaderoConstants.network. Refer to the table below for a full reference of the available constants.


Network modeConstantString value
Defaultdefault"default"
4Gmobile_4g"4g"
3.5G/HSPDAmobile_hsdpa"hsdpa"
3Gmobile_3g"3g"
GPRSmobile_gprs"gprs"
Edgemobile_edge"edge"
High jitterjitter"jitter"
High latencylatency"latency"
Asymmetricasymmetric"asymmetric"
Satellite phonesatellite"satellite"
5% packetlosspacketloss5"5packet"
10% packetlosspacketloss10"10packet"
20% packetlosspacketloss20"20packet"
50% packetlosspacketloss50"50packet"
100% packetlosspacketloss100"100packet"
Customcustom"custom"
Using a constant
client => {
// Example of updating network conditions via constant
client
.url('https://www.bing.com')
.waitForElementVisible('[type=search]', 10000)
.updateNetwork(loaderoConstants.network.mobile_3g);
}
Using a string value
client => {
// Example of updating network conditions via string value
client
.url('https://www.bing.com')
.waitForElementVisible('[type=search]', 10000)
.updateNetwork("3g");
}

Custom network condition usage

It is possible to also supply the command with a configuration object that explicitly defines various aspects of network quality. This object is considered only if the provided networkMode value is "custom", otherwise it will be ignored.


client.updateNetwork(networkMode: string, config: object);

The config object is effectively a map between configuration fields and the values you want to set for them. The table below lists the fields you can configure, where the JSON key column refers to the name of the field.

Network parameterTraffic directionJSON keyUnit of measureMin valueMax value
BandwidthOutgoingrate_upmbit0
Incomingrate_downmbit0
LatencyOutgoinglatency_upms0
Incominglatency_downms0
JitterOutgoingjitter_upms0
Incomingjitter_downms0
Packet lossOutgoingloss_up%0100
Incomingloss_down%0100
caution

For custom network configuration jitter parameters must be defined together with latency. If jitter parameters are set without also defining latency parameters, then jitter configuration will not be applied.

info

The network parameters not defined in custom network configuration will be reset to their default values.

client => {
// Example of updating network conditions using custom values
let customNetworkConfig = {
latency_up: 100,
latency_down: 50,
jitter_up: 20,
jitter_down: 10,
rate_up: 50,
rate_down: 80,
loss_up: 5,
loss_down: 75,
};

client
.url('https://www.bing.com')
.waitForElementVisible('[type=search]', 10000)
.updateNetwork(loaderoConstants.network.custom, customNetworkConfig);
}

Wait for Download Finished

This command checks if a file exists in the browser's Downloads directory, and if it does not exist, then the command will wait for the given amount of time. During this time period the command will check every 0.5s whether the file has appeared, and if it has, then the command will complete. If the file does not appear within the allotted time, then the command and test will both fail.

tip

Since the download completion check is done every 0.5s, it can be used for crude download time measurement as well.

client.waitForDownloadFinished(fileName: string, timeout = 1000: int);

The fileName parameter specifies the name of the file that should appear in the browser's Downloads directory. This parameter cannot be null or empty, otherwise an Error will be thrown.


The timeout parameter specifies time in milliseconds, how long to wait for the file to appear. If no timeout parameter is provided, then a default value of 1000 ms is used. When the timeout is exceeded an Error is thrown.


client => {
// Example of waiting for file to be downloaded
client
// Open DemoQA upload/donwload page
.url('https://demoqa.com/upload-download')
.waitForElementVisible('#app', 1000)

// Start download
.waitForElementVisible('#downloadButton', 10 * 1000)
.click('#downloadButton')

// Wait for download to finish
.waitForDownloadFinished('sampleFile.jpeg', 5 * 60 * 1000)
.saveScreenshot('after_download.png');
}

Set Request Header

This function allows updating the header values for any requests that the browser sends out.

client.setRequestHeader(headerName: string, headerValue: string);

The headerName parameter is the name of the request header to be changed. The headerValue parameter is used to set a new value for the specified header.


info

To reset a request header to its default value, pass null as the new header value.

info

Setting headerValue to an empty string will actually update the request header value to an empty string.

client => {
// Example of setting custom request header value
client
// Open page and create screenshot with default request header
.url('https://myhttpheader.com/')
.waitForElementVisible('#ua_table', 10 * 1000)
.saveScreenshot('default_headers.png')

// Set custom request header, reload the page and create screenshot
.setRequestHeader('Accept-Language', 'lv-LV')
.pause(1000)
.refresh()
.useXpath()
.waitForElementVisible(
'//div[contains(text(), "Accept-Language:")]/following-sibling::div[contains(text(), "lv-LV")]',
10 * 1000
).saveScreenshot('custom_headers.png')

// Reset request header, reload the page and create screenshot
.setRequestHeader('Accept-Language', null)
.pause(1000)
.refresh()
.waitForElementNotPresent(
'//div[contains(text(), "Accept-Language:")]/following-sibling::div[contains(text(), "lv-LV")]',
10 * 1000
).saveScreenshot('reset_headers.png')
}
note

The "Set User Agent" custom command is equivalent to using the "Set Request Header" command to set the value of the "User-Agent" header. Either approach may be used.

Ignore Alert

This command will dismiss an alert opened by alert(), prompt(), or confirm() JavaScript functions. Calling this command when an alert is not present will log an error but it will not fail the test.

client.ignoreAlert();

This function does not require any parameters.


client => {
client
// Example of closing an alert before taking a screenshot
.url("https://www.bing.com")
.waitForElementVisible("[type=search]", 10000)
.execute(function () {
prompt("prompt!");
})
.ignoreAlert()
.saveScreenshot("screenshot.png");
}
tip

A typical use case, as shown in the example, is to close an alert if there is one before taking a screenshot.

Time Execution

This custom command measures and reports the execution time of the provided function. It allows timing the execution of some flow in a website, e.g., logging in, checking out in an e-commerce site, joining a WebRTC call, etc. Results from this command can be used in post-run analysis.

client.timeExecution(name: string, timedCommand: Function, timeout?: number);

The name parameter defines the name that you would like to provide to the associated execution time metric that will be generated as a result of this function. E.g., providing the value openURL will cause the execution time metric to be labeled as openURL in the run report. The name may only contain alphanumeric characters, underscores and hyphens, otherwise the command and test will fail. The name must not exceed 150 characters in length.


The timedCommand parameter is the function that will have its execution timed. This can both be provided as the name of a declared function within the script or as an anonymous function that is defined within the parameter itself. Examples of both approaches are provided below. The provided function will be executed as part of the NightWatch command queue.


note

If name is left blank, then:

  • if the name of a declared function is provided as the value of timedCommand, the associated execution time metric will be labeled by using the name of the function;
  • if the value of timedCommand is an anonymous function, then the metric will be labeled as anonymous.

timedCommand can have up to two parameters of its own which follow the exact same pattern and logic as the built-in .perform() command:

  • no parameters - function runs and execution timing completes immediately at the end of the execution of the function;
  • one parameter - allows for asynchronous execution within the function. The parameter must be a done callback function that will indicate the completion of the timedCommand function. If done callback is not invoked, function execution will last indefinitely.
  • two parameters - allows for asynchronous execution with the Nightwatch API object passed in as the first argument, followed by the done callback.

timeout is an optional parameter which specifies the time in milliseconds, how long to wait for the provided timedCommand to execute. The timeout value must be positive. If the execution time exceeds the timeout value, then an error will be thrown. If the timeout parameter is not provided, then no time limit is placed on the execution time of timedCommand.


Usage of this custom command will generate an execution time metric in the run report. That metric will also be output to the Selenium log at the end of the command's execution. This log entry's structure is shown below.


[LOADERO] Execution time for '${NAME}': ${DURATION}ms (start: ${START}; end: ${END}).
  • ${NAME} has the value that was supplied to the name parameter.
  • ${DURATION} is the execution time in milliseconds of the function provided to the timedCommand parameter
  • ${START}, ${END} are Unix timestamp values in milliseconds.
Timeout omitted; function is defined anonymously
client => {
client
.url("https://duckduckgo.com/")
.timeExecution("locate_search_bar", () => {
client
.waitForElementVisible("#searchbox_input", 10 * 1000)
.sendKeys("#searchbox_input", "QA Processes")
.click("[aria-label='Search']")
.waitForElementVisible("#r1-0 > div > h2", 10 * 1000)
})
.saveScreenshot("screenshot.png");
}
Timeout provided; function is defined anonymously
client => {
client
.url("https://duckduckgo.com/")
.timeExecution(
"locate_search_bar",
() => {
client
.waitForElementVisible(
"#searchbox_input",
10 * 1000
)
.sendKeys("#searchbox_input", "QA Processes")
.click("[aria-label='Search']")
.waitForElementVisible("#r1-0 > div > h2", 10 * 1000);
},
10 * 1000
)
.saveScreenshot("screenshot.png");
}
Timeout provided; function is declared and then passed by name
client => {
const locateSearchBar = () => {
client
.waitForElementVisible(
"#searchbox_input",
10 * 1000
)
.sendKeys("#searchbox_input", "QA Processes")
.click("[aria-label='Search']")
.waitForElementVisible("#r1-0 > div > h2", 10 * 1000);
};

client
.url("https://duckduckgo.com/")
.timeExecution(
"locate_search_bar",
locateSearchBar,
10 * 1000
)
.saveScreenshot("screenshot.png");
}

Generate Email Address

This custom command creates an email address which can then be used to receive emails, such as verifications emails in sign-up processes.

The generated email is generated based on a prefix string provided as a parameter to the command, which will be added to the beginning of the generated email address. The generated email address will have the format of {prefix}-{runID}@{domain}, where "runID" and "domain" will be automatically filled in by Loadero.

tip

If you want to generate an email address for multiple participants in the test, then it is best to supply something that uniquely identifies the participant as the prefix, such as their global ID.

caution

Manually formatting an email address from scratch to meet the {prefix}-{runID}@{domain} format is not recommended, as it may lead to errors in the test. Use this custom command instead.

client.genEmail(prefix: string, callback: Function);

The prefix parameter specifies the value that you want to provide at the beginning of the email, which the command will then append with the remaining -{runID}@{domain} component.


The callback parameter contains a callback function (either by passing a name of an existing function or defining it inline). The callback can be supplied with an argument, whose value will be the generated email address. Refer to the example below


client => {
client.genEmail("test", addr => {
console.log(`Generated email address: ${addr}`);
});
}

To save the generated email address for later use, you can use the following code snippet:


client => {
let email;

// Generate the email address and save it as a variable
client.genEmail("test", addr => {
email = addr;
});

// Alternatively, generate a unique email address for each participant
client.genEmail(`test${client.globals.participant.globalID}`, addr => {
email = addr;
});

// Use the email address here
client.perform(() => {
console.log(`Generated email address: ${email}`);
});
}
caution

Keep in mind, that because of the asynchronous nature of Nightwatch, the email address will not be available immediately after calling the genEmail() function, so it's recommended to use the saved value within a perform() command. Otherwise you may encounter an error that the email variable is undefined.

Receive Email

This custom command will return a collection of email messages that have been received by the specified email address. To designate the email address that will receive these messages, you should use the Generate Email Address custom command. You can not use this command on your own personal email address, for example. It must be a testing email generated by Loadero itself.

The command itself will not wait for any email messages to arrive at the specified email adrress - if an email message has not yet arrived by the time this command is called, then the returned collection will not contain this email message. The returned data functions as a snapshot of all received email messages at that point in time in which the command was executed. As such, it is recommended to use the command within a loop that will wait for the email message to be visible within the collection.

client.receiveEmail(address: string, callback: Function);

The address parameter specifies the email address whose received messages you would like to inspect. The value provided to this parameter should be generated by the "Generate Email Address" custom command and should not be written out manually.


The callback parameter accepts a callback function (either by passing a name of an existing function or defining it inline). The callback can be supplied with an argument, whose value will be the array of email messages that had been returned by the command, so that they can be further processed within the callback.

The email messages within the array will have the following properties:

  • from - The email address of the sender.
  • subject - The subject of the email.
  • headers - The headers of the email.
  • text/plain - The plain text content of the email.
  • text/html - The HTML content of the email.
info

The email message object is a JavaScript object, so it is possible to access the properties using the dot notation, for example: email.from, but since the property names contain special characters, it is not recommended. Instead, it is recommended to access the properties using the array notation, for example: email["from"], by doing this you will avoid any potential issues with the special characters.

Simple example of checking an email's received messages
client => {
client.genEmail("test", addr => {
client.receiveEmail(addr, emails => {
console.log(`Amount of received emails: ${emails.length}`)

for (let email of emails) {
console.log(`From: ${email["from"]}`)
console.log(`Subject: ${email["subject"]}`)
console.log(`Plain text content: ${email["text/plain"]}`)
}
});
})
}
Checking whether the email message has arrived every 5s
client => {
let email;

// Create the address to use first
client.genEmail("test", addr => {
email = addr;
});

/*
Some UI interactions that cause an email message to be sent
*/

// Loops work funny in Nightwatch.js due to the command queue
// A recursive approach instead helps things go a little more... synchronously
const retry = (retries, emailReceived) => {
// If the email has been received already, continue
// If retrying has already occurred 10 times, give up
// If a retry was called, wait 5 seconds before checking again
if (emailReceived) return
else if (retries >= 10) throw Error("Email did not arrive.")
else if (retries != 0) client.pause(5*1000);

client.perform(()=> {
console.log("Iteration No. ", retries);
client
.receiveEmail(email, messages => {
console.log(`Amount of received emails: ${messages.length}`);

for (let email of messages) {
console.log(`From: ${email["from"]}`);
console.log(`Subject: ${email["subject"]}`);
console.log(`Plain text content: ${email["text/plain"]}`);

//If the email has arrived, we can stop retrying
if (email["from"]=="expected@email.com") emailReceived = true;
}

})
retries++;
}).perform(()=> {
// Since this is encased in a .perform(),
// the function call will be inserted into the queue with updated parameter values
retry(retries, emailReceived);
});
}

// Wrapping the first call of the recursive function inside another function
// Calling a recursive function directly from the main body is uncommon
const waitForEmail = () => {
retry(0, false);
};

waitForEmail();

/*
Further actions after successfully retrieving the email
Such as navigating to a URL sent in the email, etc.
*/
}

Perform Timed

This custom command is exclusive to Nightwatch.js, and there is no equivalent for the TestUI and Py-TestUI frameworks.

The built-in .perform() command in Nightwatch.js uses an asyncHookTimeout value for the done invocation timeout. By default the asyncHookTimeout value is set to 10 seconds, but there may be cases where this value should be customized in order to control the maximum execution time of the callback. So to allow defining this value for each case separately and avoid having to redefine asyncHookTimeout, the .performTimed() command was created.


client.performTimed(callback: function, timeout: int);

The callback parameter defines the function that will be executed as part of the command queue. This callback is defined the same way as in the built-in .perform() command. The callback signature can also have up to two parameters whose logic is also the exact same as for the original .perform() command:

  • no parameters - callback runs and perform completes immediately at the end of the execution of the callback.
  • one parameter - allows for asynchronous execution within the callback. The parameter must be a done callback function that will indicate the completion of performTimed callback function.
  • two parameters - allows for asynchronous execution with the Nightwatch API object passed in as the first argument, followed by the done callback.

The timeout parameter defines the done invocation timeout and is specified in milliseconds.


tip

The performTimed() command will be useful in cases where the done callback is used within a custom callback function in order to notify the Nightwatch command queue about the end of the command's execution. Without the done callback invocation performTimed() command behaves the same as the built-in perform() command and completes immediately after the callback is executed.

client => {
let version = "";

client
.url("https://www.bing.com")
.performTimed((done) => {
// Get latest ChromeDriver version
getChromeDriverVersion(done);
}, 20000)

// Insert Bing search query for ChromeDriver version
.perform(() => {
client.setValue("[type=search]", [`chromedriver ${version}`, client.Keys.ENTER])
})

// Wait up to 10 seconds until search results are visible
.waitForElementVisible("[aria-label='Search Results']", 10 * 1000);

function sleepFor(sleepDuration) {
const now = new Date().getTime();
while (new Date().getTime() < now + sleepDuration) { }
}

function getChromeDriverVersion(done) {
retry = 60;
request(
{
url: `https://chromedriver.storage.googleapis.com/LATEST_RELEASE`,
},
function (error, response, body) {
if (error) {
throw new Error(error);
}

if (response.statusCode != 200) {
sleepFor(1000);

retry--;
if (retry > 0) {
getChromeDriverVersion(done, retry);
} else {
done();
}
} else {
version = body;

done();
}
}
);
}
}

Screenshot (deprecated)

Deprecation notice (October 31st, 2024): users are recommended to use the built-in functionality of the frameworks for taking a screenshot (saveScreenshot() for Nightwatch.js and TestUI; save_screenshot() for Py-TestUI).

client.takeScreenshot(filename: string, exitOnFail: boolean = false);

The filename parameter specifies the name that will be given to the saved file containing the captured screenshot.


The exitOnFail parameter determines whether the participant should terminate test execution if there was a problem taking the screenshot. This parameter can be omitted and will default to false.


client => {
// Example of taking a browser screenshot
client
.url('https://www.bing.com')
.waitForElementVisible('[type=search]', 10000)
.takeScreenshot('screenshot.png');
}
caution

Screenshot creation is impossible in some cases, for example, when an alert is open. If there are concerns that an alert might get in the way, then the Ignore Alert command can be used - this command will close an alert if it is open, and if there is no alert to close, it won't impact test execution.