Cybersecurity
DevOps Cloud
IT Operations Cloud
Asynchronous JavaScript APIs In TruClient – Part I
Asynchronous JavaScript APIs In TruClient – Part III
Obviously, asynchronous APIs are different to synchronous APIs. Below I detail some of the main differences which will help you better understand how to use to use TruClient’s asynchronous APIs.
TruClient runs steps one at a time. It needs to know when a step finishes, so that the next step can be started. When executing synchronous JavaScript code, it’s clear when the code execution has finished. But TruClient can’t know when asynchronous JavaScript code execution has finished, because asynchronous means something was not executed in the main thread. So you need to call TCA.done or TCA.doneWithError to tell TruClient that the current JavaScript code execution has finished at every JavaScript code exit point. Otherwise, a timeout error is likely to occur.
Call TCA.done(result) to tell TruClient the current JavaScript code execution has finished successfully.
The result argument is optional. Usually, when called in the “Code” field in the “Arguments” section of an “Evaluate JavaScript” step, there is no need to provide a result. But the result is required when the JavaScript code is expected to return a result. For example, when called in the “Location” field in the “Arguments” section of a “Navigate” step, or in the “Value” field in the “Arguments” section of a “Type” step.
The code below is an example used in the “Location” field in the “Arguments” section of a “Navigate” step. Suppose the “URL” parameter provides the URL to navigate. The TCA.getParam API gets the URL and sets it to “content”, which is returned by the TCA.done(content) API. The URL is then used by the “Navigate” step.
TCA.getParam("URL").then( function(content) { TCA.done(content); }).catch( function(error) { TCA.doneWithError(error); }); |
Call TCA.doneWithError(error) to tell TruClient that the current JavaScript code execution has finished with an error, and that the current step failed.
The error argument is required. TruClient displays this error in the UI, and reports this error in the execution log.
There are times that people forget to use the TCA.done or TCA.doneWithError call to tell TruClient that the current JavaScript code execution has finished. Since TruClient cannot wait indefinitely, it sets a time limit for JavaScript code execution (the default value is 20 seconds). This means, for example, that if an “Evaluate JavaScript” step doesn’t stop within 20 seconds, TruClient will stop it and report a timeout error.
A timeout error can be caused by a missing TCA.done or TCA.doneWithError call, or by a step taking longer to run than the 20 second time limit (what a long time!). If it’s the latter case, we suggest re-writing the JavaScript code to shorten execution time. For example, split the “Evaluate JavaScript” step into multiple steps so that each step doesn’t take too long to run. If a step must run more than 20 seconds, you can increase the timeout value by modifying the EvalJavaScriptTimeout item in the default.cfg file in the script folder. Avoid increasing the timeout value too much (try to keep it as small as possible).
TruClient automatically detects when an asynchronous API is invoked in the main thread, and waits for TCA.done or TCA.doneWithError invocation to identify when the current JavaScript has finished, or for the step to time out. However, TruClient cannot detect invocation to asynchronous APIs in callback functions that are not executed in the main thread.
For example, if an “Evaluate JavaScript” step contains the below code (where the asynchronous APIs are invoked in the callback function outside the main thread), TruClient treats it as finished immediately after the setTimeout function returns. It then continues to execute the subsequent steps. Even if callbackFunc is executed a second later, this causes unpredictable results. Any subsequent steps that depend on the directory being created by this step may fail.
setTimeout(function callbackFunc() { IOA.createDir("C:\\Demo\\TestDir").then(function(result) { TCA.outputMessage("Create dir successfully"); TCA.done(result); }).catch(function(error) { TCA.outputMessage(error); TCA.doneWithError(error); }); }, 1000); |
To avoid this problem, use TCA.useAsyncAPI to notify TruClient that asynchronous APIs are being used, as shown below:
TCA.useAsyncAPI();
setTimeout(function callbackFunc() { IOA.createDir("C:\\Demo\\TestDir").then(function(result) { TCA.outputMessage("Create dir successfully"); TCA.done(result); }).catch(function(error) { TCA.outputMessage(error); TCA.doneWithError(error); }); }, 1000); |
Exception handling of asynchronous JavaScript code is a little tricky. The difficulty is the try/catch method in the main thread cannot catch exceptions that occur in asynchronous code. Take a look at the example code below:
try { var fileToRead = "C:\\Demo\\1.txt"; IOA.read(fileToRead).then(function(result) { TCA.outputMessage(result);
TCA.done(); }); } catch(error) { TC.outputMessage(error); TCA.doneWithError(error); } |
If an exception occurs when reading the file by asynchronous API IOA.read, the exception won’t be caught, and the sentences in the catch block won’t be executed. The final result is a timeout error because TCA.doneWithError won’t be executed. The reason is because the IO exception occurs in the asynchronous API, while the try/catch method is in the main thread.
To avoid this problem in asynchronous APIs, use a catch to capture exceptions from promises, as shown below:
var fileToRead = "C:\\Demo\\1.txt"; IOA.read(fileToRead).then( function(content) { TC.outputMessage(content);
TCA.done(); }).catch( function(error) { TCA.doneWithError(error); }); |
Every TruClient asynchronous API returns a promise object, including IOA.read here. Use the catch method to capture possible exceptions that occur inside the API.
If you prefer the async/await pattern, use the wrapper template mentioned in my previous blog to capture exceptions in asynchronous APIs, as shown below. The wrapper async arrow function also returns a promise object. It can work with TruClient Chromium and TruClient Browser only.
((async () => { // your code here })()).then( (result) => { TCA.done(result); } ).catch( (error) => { TCA.doneWithError(error); } ); |
If exceptions in asynchronous APIs are not caught, either because there is no catch, or because the catch is not used correctly, the result is unpredictable-possibly a timeout or unexpected error. To check what exceptions actually occurred, set the Log Level in Runtime Settings to “Extended”. Then, most of time, the log will contain information of the exceptions that occurred in asynchronous APIs.
Extended log can only show exceptions inside asynchronous APIs.
When working with TruClient Chromium, use Chrome DevTools to help capture and show exceptions inside or outside TruClient asynchronous APIs. To view logged messages, open DevTools of the AUT (application under test) tab, and switch to the Console tab.
When working with TruClient Browser, use the Mozilla Browser Console to help capture and show exceptions inside or outside TruClient asynchronous APIs. For details on how to view logged messages, see Open the Browser Console.
This blog took a closer look at how to use the TruClient asynchronous APIs, and how to handle exceptions.
In my next blog, I will provide examples of TruClient asynchronous functions.
For the latest information on using TruClient asynchronous APIs, see the TruClient Help Center.