JavaScript - Specifics of Usage

Applies to TestComplete 14.60, last modified on April 22, 2021

TestComplete lets you write tests in JavaScript. JavaScript in TestComplete is powered by V8 engine v. 5.8. This topic describes the specifics of using JavaScript in TestComplete.

Not Available on Windows XP and Windows Server 2003

The TestComplete JavaScript engine does not support Windows XP and Windows Server 2003, so the JavaScript modules are not installed on these Windows versions. You can still open and view your JavaScript projects on Windows XP and Windows Server 2003, but TestComplete will not run JavaScript code -- both the code in the script units and in the Run Code Snippet operations in keyword tests.

Unsupported Features

JavaScript in TestComplete does not support the ECMAScript Internationalization API.

var vs. let

In JavaScript, you can declare variables using var and let.

let declares a block-scope variable:

JavaScript

function Test()
{
  let number = 5;

  {
    let number = 42;    // This variable is different from the one above
    let str = "test";
    Log.Message(number); // 42
    Log.Message(str);    // "test"
  }

  Log.Message(number);  // 5
  Log.Message(str);      // ReferenceError: str is not defined
}

You would typically use let for variables local to a loop or a branch in if...then...else:

JavaScript

for (let i = 0; i < 5; i++)
  Log.Message(i);

var declares a function-level variable, that is, a variable that is visible throughout the entire function:

JavaScript

function Test()
{
  for (var i = 0; i < 5; i++)
  {
    // do something
  }

  // "i" is still visible here
  Log.Message(i);
}

When used outside functions, or at the top of a function, var and let work in the same way.

JavaScript

var str1 = "hello";
let str2 = "world";

function main()
{
  Log.Message(str1 + " " + str2);
}

Any variables declared without var or let are implicitly global, as if they were defined outside of any function.

JavaScript

function main()
{
  a = 5;     // global variable
  var b = 7; // local variable

  foo();
}

function foo()
{
  Log.Message(a); // 5
  Log.Message(b); // ReferenceError: b is not defined
}

Implicit global variables may have unwanted side effects in your code, so we recommend that you define all global variables explicitly.

Equality Comparison

JavaScript equality operations, == and ===, may lead to incorrect results when comparing values obtained from:

  • Objects in a tested application,
  • TestComplete scripting objects (such as aqEnvironment),
  • COM objects.

For example (but not limited to), when comparing these values to null, undefined or objects.

To avoid issues, use the equal() and strictEqual() functions instead of == and ===.

Instead of ... Use ...
if (something == null)
if (something != null)
if (something === null)
if (something !== null)
if (equal(something, null))
if (!equal(something, null))
if (strictEqual(something, null))
if (!strictEqual(something, null))
if (typeof something == "undefined")
if (typeof something != "undefined")
if (typeof something === "undefined")
if (typeof something !== "undefined")
if (equal(something, undefined))
if (!equal(something, undefined))
if (strictEqual(something, undefined))
if (!strictEqual(something, undefined))
if (objA == objB)
if (objA != objB)
if (objA === objB)
if (objA !== objB)
if (equal(objA, objB))
if (!equal(objA, objB))
if (strictEqual(objA, objB))
if (!strictEqual(objA, objB))

Indexed Properties

In TestComplete, the properties of some test objects have parameters, such as list.wItem(index) or grid.wValue(row, column). JavaScript cannot assign a value to an indexed property directly. You need to use the $set method instead:

JavaScript

// This won't work
gridObj.wValue(0, "Customer Name") = "Samuel Clemens";

// This works
gridObj.$set("wValue", 0, "Customer Name", "Samuel Clemens");

To get an indexed property with the default parameter values, use empty parentheses after the property name:

JavaScript

let value1 = obj.property(param1, param2);
let value2 = obj.property(); // with default parameters

Working With COM Objects

To obtain COM objects in JavaScript tests, use the getActiveXObject function. This function was introduced in TestComplete 12.4. It works faster than the Sys.OleObject property that you used in earlier versions.

If you have multiple (more than several hundred) calls to methods and properties of COM objects, and you experience performance issues, you can use the special $get, $set and $call methods (see below). They will make the calls faster. Otherwise, use the usual object-name.method-name syntax. This way, your code will be easier to read and maintain.

$get, $set and $call

TestComplete adds extra methods $get, $set, and $call to objects in JavaScript tests. These methods are added to:

  • Test objects (objects in your tested application),
  • TestComplete scripting objects (aqFileSystem and others),
  • COM objects.

Normally, you do not need to use these methods, only for assigning an indexed property (see above).

Referencing Other Script Units

You can use the require() function to import functions and variables from other JavaScript units in your test project. The referenced unit must explicitly export the needed functions and variables via module.exports. For details, see JavaScript - Calling Routines and Variables Declared in Another Unit.

JavaScript

// [CommonUnit]
var answer = 42;
function hello()
{
  Log.Message("Hello, world!");
}

module.exports.hello = hello;
module.exports.answer = answer;


// [MainUnit]
var common = require("CommonUnit");

function main()
{
  common.hello();
  Log.Message(common.answer);
}

The TestComplete //USEUNIT directive is also supported, but with some limitations.

instanceof Operator

The instanceof operator returns an incorrect value if the checked objects are passed between script units. Suppose you have the following code:

JavaScript

function fooA()
{
  throw new Error(10, "This is an error."); // Simulates exception
}

function fooB()
{
  try
  {
    fooA();
  }
  catch (err)
  {
    let res = err instanceof Error;
    Log.Message("err instanceof Error = " + res);
  }
}

If both fooA and fooB reside in the same script unit, the res variable in the fooB function will be true. If you place fooB to another unit, then the res variable will be false.

typeof Operator

In TestComplete, when the operand of the typeof operator is a regular expression, you have to use parentheses:

JavaScript

// Log.Message(typeof /s/); Syntax error
Log.Message(typeof(/s/));

Using Iterable Objects

You can use the for...of loop to iterate over an iterable object:

JavaScript

for (let process of Sys)
  Log.Message(process.ProcessName);

The for...of loop works with any test object that has the _NewEnum property in the Object Browser.

Note: The Enumerator object is also supported for compatibility with code ported from JScript.

Accessing Collection Items

COM objects can have properties that return a collection object. Some scripting languages like VBScript allow accessing collection items directly, for example, obj.Cells(i, j) rather than obj.Cells.Item(i, j). JavaScript does not support this. To access items of a collection object, you should specify the appropriate property (Item, Items) explicitly:

JavaScript

Log.Message(Excel.Cells.Item(i, j));

JSON

The JSON.stringify, JSON.parse and toJSON methods work only with native JavaScript objects. They are not supported for TestComplete objects, for example, Aliases.browser.MyPage.

Passing Parameters by Reference

JavaScript does not support passing function parameters by reference. However, you may be able to call such functions by using the OutParameterWrapper extension.

This restriction also applies to calling DLL functions. However, when an object is passed through a function’s parameters in JavaScript, the function actually obtains a reference to the object. If the callee routine modifies some members of such an object passed through its parameters, the caller routine can access the updated object after the routine execution is over. Therefore, when a custom structure, array or string value created by the DLL.New or IDLLAccessProcess.New method (see Using Custom Data Structures in DLL Function Calls, Using Arrays in DLL Function Calls and Using String Parameters in DLL Function Calls) is passed from your JavaScript test to a DLL routine, you can access the modified structure, array or string after the routine execution is over. The point is that the New method creates a wrapper object for such structures, arrays and strings and such an object is passed to a DLL routine by reference. So, such structures, arrays and strings can be modified and returned from the routine to which they are passed from JavaScript tests.

Prototype Methods

JavaScript supports extending objects with custom methods declared as prototype methods. The way you call prototype methods of such objects from TestComplete units depends on the corresponding object. If you created a custom scripting object manually, you can call its prototype methods from any unit in a project. Prototype methods of JavaScript built-in objects (such as Array) can be called only in the same unit where these methods are declared; these custom methods are not available in other units.

To work around this limitation, export the constructor of an object from the unit that contains the prototype method declaration. The code below demonstrates this approach:

JavaScript

// [MainUnit]
var CommonUnit = require("CommonUnit")
function Main()
{
  // This won’t work:
  let arr = new Array(6, 7, 8, 9);
  arr.logIndexByItem(7); // Error: "The object does not support this property or method"

  // This will work:
  let arr2 = new CommonUnit.NewArray(1, 2, 3, 4, 5);
  arr2.logIndexByItem(3);
}


// [CommonUnit]
// Prototype method declaration
Array.prototype.logIndexByItem = function(item)
{
  for (let i = 0; i < this.length; i++)
  {
    if ((i in this) && (equal(this[i], item)))
    {
      Log.Message(i);
      return;
    }
  }
  Log.Message("Item was not find");
}

// Exports the constructor of the modified Array object
exports.NewArray = Array;

Debugging

  • JavaScript’s debugger statement has no effect in TestComplete. To pause the script execution and activate the debugger, use the Runner.Pause method.

  • The Set Next Statement command is not supported for JavaScript.

  • You cannot change the values of local variables in the Evaluate dialog and in the Watch List.

  • If arrays and typed arrays contain many items, the debugger panels and dialogs group them by 100 for convenient presentation.

See Also

Specifics of Usage Common for All Languages
JavaScript for JScript Users
Script Tests
About Script Tests
Selecting the Scripting Language

Highlight search results