JScript, C#Script and C++Script - Specifics of Usage

Applies to TestComplete 14.92, last modified on September 16, 2021

TestComplete supports the JScript engine version 5.8. C#Script and C++Script are based on JScript, so all of these languages have the same specifics of usage.

Note: JScript, C#Script, and C++Script are rarely used and are legacy languages. We do not recommend that you create script tests with them. We recommend that you use JavaScript (or Python) to create tests. See Selecting the Scripting Language.

Variable Scope

Variables that are declared with the var statement within a function are only visible within this function. Variables that are used without declaring them with var, become global variables. This behavior may cause hidden influence of functions onto other functions. For instance, the execution of the following code never ends:

JScript, C#Script, C++Script

function ChildFunc()
{
  for(i = 0; i < 5; i++)
    …
}
  
function ParentFunc()
{
  for(i = 0; i < 10; i++)
    ChildFunc();
}

This happens because after a call to ChildFunc, the i variable is always 5 and never 10, so the loop within the ParentFunc function becomes an infinite loop.

Arrays

Some TestComplete methods that accept arrays (for example, AddNamedChild), do not support native JScript arrays. You need to convert JScript arrays to TestComplete-compatible arrays using the following function:

JScript

function ConvertJScriptArray(JScriptArray)
{
  // Uses the Dictionary object to convert a JScript array
  var objDict = Sys.OleObject("Scripting.Dictionary");
  objDict.RemoveAll();
  for (var i in JScriptArray)
    objDict.Add(i, JScriptArray[i]);
  return objDict.Items();
}

C++Script, C#Script

function ConvertJScriptArray(JScriptArray)
{
  // Uses the Dictionary object to convert a JScript array
  var objDict = Sys["OleObject"]("Scripting.Dictionary");
  objDict["RemoveAll"]();
  for (var i in JScriptArray)
    objDict["Add"](i, JScriptArray[i]);
  return objDict["Items"]();
}

Similarly, arrays returned from TestComplete methods (FindAllChildren, EvaluateXPath and others) need to be converted to the JScript array format:

JScript, C#Script, C++Script

var jscript_array = original_array.toArray();

This may also apply to arrays passed to and returned from COM objects.

JSON

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

Passing Parameters by Reference

JScript, C#Script and C++Script do 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 JScript, 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 JScript, C#Script or C++Script 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 JScript, C#Script and C++Script tests.

No Circular References Allowed Between Units

In TestComplete script units can refer to each other, so you can call routines and variables defined in other units. But JScript, C#Script and C++Script do not support circular references between units.

Using Objects Defined in Other Units

In TestComplete, if a script calls an object method that is defined in another unit, and this method throws an exception, the exception cannot be caught and handled by the try {} block in the main script. Keep this in mind when developing object-oriented test scripts using native JScript functionality.

The following code demonstrates the issue:

JScript, C#Script, C++Script

[Unit1]
//USEUNIT Unit2
function Main()
{
  try
  {
    var obj = new MyObject();
    obj.SomeMethod();   // This causes an error that won't be caught
  }
  catch (e)
  {
    Log.Error(e);
  }
}

[Unit2]
function MyObject()
{
  this.SomeMethod = function()
  {
    1 = 2;   // Raises an error
  }
}

Prototype Methods

JScript, C#Script and C++Script support 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 JScript, C#Script and C++Script 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.

A workaround for working with built-in objects is to insert code that creates an object instance to the same unit that contains the prototype method declaration. In this case, you will be able to use this object and its prototype methods in other units. For example, you can define a function that creates and returns an object instance and use this function instead of the object constructor in other units. The following code illustrates this approach:

JScript, C#Script, C++Script

[MainUnit]
//USEUNIT CommonUnit
function Main()
{
  // This won’t work:
  var arr = new Array(6, 7, 8, 9);
  Log.Message(arr.indexOf(7)); // Error: "Object doesn't support this property or method"

  // This will work:
  var arr2 = NewArray().concat([1, 2, 3, 4, 5]);
  Log.Message(arr2.indexOf(3));
}


[CommonUnit]
// Prototype method declaration
Array.prototype.indexOf = function(item)
{
  for (var i = 0; i < this.length; i++)
  {
    if ((i in this) && (this[i] == item)) return i;
  }
  return -1;
}

// Creates and returns a new Array object
function NewArray()
{
  return new Array();
}

Releasing COM Objects

When a variable goes out of scope, it is released. However, if the variable held a reference to an object, the JScript engine does not release this object until the entire script run is over. This happens because the JScript engine cleans up memory and references at a certain point and not when a variable is destroyed or you set it to null.

This restriction may cause problems when working with certain program objects and COM servers. Suppose you have the following code:

JScript, C#Script, C++Script

function Foo()
{
  Obj = new ActiveXObject("MyCOMObject.MyCOMObject");
  …
  Obj = null;
}

function MainFoo()
{
  …
  Foo();
  // MyCOMObject still exists
  …
}

In this example, MyCOMObject exists after a call to Foo. Assigning null to the Obj variable marks it as a candidate for releasing, but the object still exists in memory. To force the releasing of all such objects and variables, call JScript’s CollectGarbage method:

JScript, C#Script, C++Script

function Foo()
{
  Obj = new ActiveXObject("MyCOMObject.MyComObject");
  …
  Obj = null;
  CollectGarbage();
}

instanceof Operator

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

JScript, C#Script, C++Script

function fooA()
{
  try
  {
    throw new Error(10, "This is an error."); // Simulates exception
  }
  catch(err)
  {
    var res = err instanceof Error;
    Log.Message("fooA: err instanceof Error = " + res);
    fooB(err);
  }
}
 
function fooB(err)
{
  var res = err instanceof Error;
  Log.Message("fooB: err instanceof Error = " + res);
}

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

caller Property

In TestComplete, when a routine is called from another script unit, the caller property of the Function object that corresponds to the current routine returns null. Normally, when a function is called by another routine declared in the same script unit, the caller property returns a reference to the function that invoked the current function. If the caller property is used in a string context, it contains the text of the function that called the current routine. See the description of the Function object and its caller property in the MSDN Library.

debugger Statement

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

Accessing the script engine after fixing the script engine vulnerability

A critical security vulnerability has been reported in the script engine. To fix it, you can use the instructions at portal.msrc.microsoft.com/en-us/security-guidance/advisory/ADV200001.

However, applying this fix prevents TestComplete from accessing the engine and running tests. If you have already applied the fix and are facing the situation when your TestComplete tests have stopped running correctly, for instance, the tests stop immediately after the start generating an empty test log, you can do the following:

If you use a 32-bit version of Windows
  1. Temporarily revert the fix by executing the following command:

    cacls %windir%\system32\jscript.dll /E /R everyone
  2. Copy the C:\Windows\system32\jscript.dll file to the <TestComplete>\Bin folder.

  3. Reapply the security vulnerability fix again as described at portal.msrc.microsoft.com/en-us/security-guidance/advisory/ADV200001.

  4. Restart TestComplete.

If you use a 64-bit version of Windows
  1. Temporarily revert the fix by executing the following commands:

    cacls %windir%\system32\jscript.dll /E /R everyone
    cacls %windir%\syswow64\jscript.dll /E /R everyone
  2. Copy the C:\Windows\system32\jscript.dll file to the <TestComplete>\x64\Bin folder.

  3. Copy the C:\Windows\syswow64\jscript.dll file to the <TestComplete>\Bin folder.

  4. Reapply the security vulnerability fix again as described at portal.msrc.microsoft.com/en-us/security-guidance/advisory/ADV200001.

  5. Restart TestComplete.

See Also

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

Highlight search results