Mapping Objects From Tests

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

You can add test objects to (and remove them from) the Name Mapping repository from tests. This can be useful, for example, if you want to get the name mapping data from an external source, such as an Excel file.

Requirements
  • The object you want to map must exist in the system.

  • The parent object of the object you want to map must be already mapped.

About

To map an object from tests, use the Mapped_Object.AddNamedChild method. This method is available for all the mapped objects to which you refer by their alias or by a mapped name (that is, using the NameMapping.Object or Aliases.Object syntax).

For each object you are mapping, you can specify the following:

  • A mapped name to assign to the object.

  • The properties and values to use to map the object.

  • Whether to store information on the properties and methods of the mapped object. This way, you will be able to view the object’s properties and methods in test editors even if the tested application is not running.

  • An object description.

To learn more, see the method description.

Mapping objects from tests does not create aliases for these objects.

Mapping all application objects

Suppose, you want to map the application’s entire object hierarchy. To do this:

  1. Map the Sys or the Mobile root object manually.

  2. Create a keyword test or script that:

    • Calls the AddNamedChild method of the mapped root object (Sys or Mobile) to map the application process.

    • Calls the AddNamedChild method of the mapped process object to map each of the windows opened in the application.

    • Calls the AddNamedChild method of each mapped window object to map controls in that window.

    • And so on.

Note: The sequence of mapping operations must comply with the actual object hierarchy that you can see in the Object Browser.
You cannot skip intermediate hierarchy levels and, say, map a window as a child of the Sys object.

Example

The following sample script demonstrates how you can create mapped names for objects in an MFC application. Objects in MFC applications usually have static control IDs that are defined by application developers, so you can use the ControlId property as an identification property for mapped objects.

The script works with the Visual C++ version of the Orders sample application that can be found in the <TestComplete Samples>\Open Apps\OrdersDemo\MSVC folder. The application must already be running before you launch the script, and the Sys and application process objects must be mapped to the Sys and Orders names respectively beforehand. The script maps the application objects (except for invisible objects and text labels) by using a combination of the ControlId and WndClass properties and generates mapped object names based on the object class and either its caption or index.

If you do not have the sample, download the TestComplete Samples installation package from the https://support.smartbear.com/testcomplete/downloads/samples/ page of our website and run it.

JavaScript

function Main()
{
  var p, props;

  // Specify an array of identification properties
  props = new Array(2);
  props[0] = "ControlId";
  props[1] = "WndClass";

  // Obtain the running application process by its mapped name
  p = NameMapping.Sys.Orders;

  // Map objects in all of the application's opened windows
  for (let i = 0; i < p.ChildCount; i++)
    MapObject(p.Child(i), props, p)
}

// Maps Obj using the specified properties as a child of the MappedParent objects
function MapObject(Obj, PropArray, MappedParent)
{
  var ValidProps, MappedName, MappedObj;

  // Filter out unneeded objects
  // (in this example - text labels and invisible Objs)
  if (equal(Obj.WndClass, "Static") || !Obj.Visible) return;

  // Check if the object supports any of the specified identification properties
  ValidProps = new Array();
  for (let i = 0; i < PropArray.length; i++)
  {
    if (aqObject.IsSupported(Obj, PropArray[i]))
      ValidProps.push(PropArray[i]);
  }

  // If the object supports at least one of the specified identification properties, map it
  if (ValidProps.length > 0)
  {
    // Generate the mapped object name based on the object class
    // and either its caption or (if the caption is empty) index
    MappedName = Obj.WndClass + (Obj.WndCaption != "" ? Obj.WndCaption : Obj.Index);
    MappedName = GetValidIdentifier(MappedName);

    // Map the object
    MappedObj = MappedParent.AddNamedChild(Obj, MappedName, "", ConvertJavaScriptArray(ValidProps));

    // Map the object's child objects
    for (let i = 0; i < Obj.ChildCount; i++)
      MapObject(Obj.Child(i), PropArray, MappedObj);
  }
  else
  {
    Log.Error(aqString.Format("The %s object was not mapped because it does not support " +
              "any of the specified identification properties.", Obj.FullName));
  }
}

// Creates and returns a valid identifier based on the specified string
function GetValidIdentifier(str)
{
  // Remove invalid characters and leading numbers and underscores from the string
  return str.replace(/\W+/g, "").replace(/^[\d_]+/g, "");
}

// Convert a JavaScript array to a Variant-compatible array
function ConvertJavaScriptArray(JavaScriptArray)
{
  var oDict = getActiveXObject("Scripting.Dictionary");
  oDict.RemoveAll();

  for (var i in JavaScriptArray)
    oDict.Add(i, JavaScriptArray[i]);

  return oDict.Items();
}

JScript

function Main()
{
  var p, props, i;

  // Specify an array of identification properties
  props = new Array(2);
  props[0] = "ControlId";
  props[1] = "WndClass";

  // Obtain the running application process by its mapped name
  p = NameMapping.Sys.Orders;

  // Map objects in all of the application's opened windows
  for (i = 0; i < p.ChildCount; i++)
    MapObject(p.Child(i), props, p)
}

// Maps Obj using the specified properties as a child of the MappedParent objects
function MapObject(Obj, PropArray, MappedParent)
{
  var ValidProps, MappedName, MappedObj, i;

  // Filter out unneeded objects
  // (in this example - text labels and invisible Objs)
  if ((Obj.WndClass == "Static") || ! Obj.Visible) return;

  // Check if the object supports any of the specified identification properties
  ValidProps = new Array();
  for (i = 0; i < PropArray.length; i++)
  {
    if (aqObject.IsSupported(Obj, PropArray[i]))
      ValidProps.push(PropArray[i]);
  }

  // If the object supports at least one of the specified identification properties, map it
  if (ValidProps.length > 0)
  {
    // Generate the mapped object name based on the object class
    // and either its caption or (if the caption is empty) index
    MappedName = Obj.WndClass + (Obj.WndCaption != "" ? Obj.WndCaption : Obj.Index);
    MappedName = GetValidIdentifier(MappedName);

    // Map the object
    MappedObj = MappedParent.AddNamedChild(Obj, MappedName, "", ConvertJScriptArray(ValidProps));

    // Map the object's child objects
    for (i = 0; i < Obj.ChildCount; i++)
      MapObject(Obj.Child(i), PropArray, MappedObj);
  }
  else
  {
    Log.Error(aqString.Format("The %s object was not mapped because it does not support " +
              "any of the specified identification properties.", Obj.FullName));
  }
}

// Creates and returns a valid identifier based on the specified string
function GetValidIdentifier(str)
{
  // Remove invalid characters and leading numbers and underscores from the string
  return str.replace(/\W+/g, "").replace(/^[\d_]+/g, "");
}

// Convert a JScript array to a Variant-compatible array
function ConvertJScriptArray(JScriptArray)
{
  var oDict = new ActiveXObject("Scripting.Dictionary");
  oDict.RemoveAll();

  for (var i in JScriptArray)
    oDict.Add(i, JScriptArray[i]);

  return oDict.Items();
}

VBScript

Sub Main
  Dim p, wnd, props(2)

  ' Specify an array of identification properties
  props(0) = "ControlId"
  props(1) = "WndClass"

  ' Obtain the running application process by its mapped name
  Set p = NameMapping.Sys.Orders

  ' Map objects in all of the application's opened windows
  For Each wnd In p
    Call MapAppStructure(wnd, props, p)
  Next
End Sub

Sub MapAppStructure(Obj, PropArray, MappedParent)
  Dim ValidProps, i, cnt, MappedName, MappedObj, ChildObj

  ' Filter out unneeded objects
  ' (in this example - text labels and invisible objects)
  If (Obj.WndClass = "Static") Or Not Obj.Visible Then Exit Sub

  ' Check if the object supports any of the specified identification properties
  ValidProps = Array()
  cnt = 0
  For i = 0 To UBound(PropArray)
    If aqObject.IsSupported(Obj, PropArray(i)) Then
      ReDim Preserve ValidProps(cnt)
      ValidProps(cnt) = PropArray(i)
      cnt = cnt + 1
    End If
  Next

  ' If the object supports at least one specified identification property, map it
  If UBound(ValidProps) >= 0 Then
    ' Generate the mapped object name based on the object class
    ' and either its caption or (if the caption is empty) index
    MappedName = Obj.WndClass
    If Obj.WndCaption <> "" Then
      MappedName = MappedName & Obj.WndCaption
    Else
      MappedName = MappedName & Obj.Index
    End If
    MappedName = GetValidIdentifier(MappedName)

    ' Map the object
    Set MappedObj = MappedParent.AddNamedChild(Obj, MappedName, "", ValidProps)

    ' Map the object's child objects
    For Each ChildObj in Obj
      Call MapAppStructure(ChildObj, PropArray, MappedObj)
    Next
  Else
    Log.Error aqString.Format("The %s Obj was not mapped because it does not support " & _
      "any of the specified identification properties.", Obj.FullName)
  End If
End Sub

' Creates and returns a valid identifier based on the specified string
Function GetValidIdentifier(str)
  Dim re, str2

  Set re = New RegExp
  re.Global = True

  'Remove invalid characters from the string
  re.Pattern = "\W+"
  str2 = re.Replace(str, "")

  'Remove leading numbers and underscores
  re.Pattern = "^[\d_]+"
  str2 = re.Replace(str2, "")

  GetValidIdentifier = str2
End Function

DelphiScript

procedure MapAppStructure(Obj, PropArray, MappedParent); forward;
function GetValidIdentifier(str); forward;

procedure Main;
var p, props, i;
begin
  // Specify an array of identification properties
  props := CreateVariantArray(0, 1);
  props[0] := 'ControlId';
  props[1] := 'WndClass';

  // Obtain the running application process by its mapped name
  p := NameMapping.Sys.Orders;

  // Map objects in all of the application's opened windows
  for i := 0 to p.ChildCount - 1 do
    MapAppStructure(p.Child(i), props, p);
end;

// Maps an object using the specified identification properties
procedure MapAppStructure(Obj, PropArray, MappedParent);
var ValidProps, i, cnt, MappedName, MappedObj;
begin
  // Filter out unneeded objects
  // (in this example - text labels and invisible Objs)
  if (Obj.WndClass = 'Static') or not Obj.Visible then Exit;

  // Check if the object supports any of the specified identification properties
  ValidProps := CreateVariantArray(0, 0);
  cnt := 0;
  for i := 0 to VarArrayHighBound(PropArray, 1) do
  begin
    if aqObject.IsSupported(Obj, PropArray[i]) then
    begin
      ValidProps[cnt] := PropArray[i];
      cnt := cnt + 1;
      VarArrayRedim(ValidProps, cnt);
    end
  end;
  VarArrayRedim(ValidProps, cnt-1); // Remove the extra element

  // If the object supports at least one specified identification property, map it
  if VarArrayHighBound(ValidProps, 1) > 0 then
  begin
    // Generate the mapped object name based on the object class
    // and either its caption or (if the caption is empty) index
    MappedName := Obj.WndClass;
    if Obj.WndCaption <> '' then
      MappedName := MappedName + Obj.WndCaption
    else
      MappedName := MappedName + VarToStr(Obj.Index);
    MappedName := GetValidIdentifier(MappedName);

    // Map the object
    MappedObj := MappedParent.AddNamedChild(Obj, MappedName, '', ValidProps);

    // Map the object's child objects
    for i := 0 to Obj.ChildCount-1 do
      MapAppStructure(Obj.Child(i), PropArray, MappedObj)
  end
  else
    Log.Error(aqString.Format('The %s Obj was not mapped because it does not support ' +
      'any of the specified identification properties.', Obj.FullName));
end;

// Creates and returns a valid identifier based on the specified string
function GetValidIdentifier(str);
var re, str2;
begin
  re := HISUtils.RegExpr;

  // Remove invalid characters from the string
  re.Expression := '\W+';
  str2 := re.Replace(str, '');

  // Remove leading numbers and underscores
  re.Expression := '^[\d_]+';
  str2 := re.Replace(str2, '');

  Result := str2;
end;

C++Script, C#Script

function Main()
{
  var p, props, i;

  // Specify an array of identification properties
  props = new Array(2);
  props[0] = "ControlId";
  props[1] = "WndClass";

  // Obtain the running application process by its mapped name
  p = NameMapping["Sys"]["Orders"];

  // Map objects in all of the application's opened windows
  for (i = 0; i < p["ChildCount"]; i++)
    MapObject(p["Child"](i), props, p)
}

// Maps Obj using the specified properties as a child of the MappedParent objects
function MapObject(Obj, PropArray, MappedParent)
{
  var ValidProps, MappedName, MappedObj, i;

  // Filter out unneeded objects
  // (in this example - text labels and invisible Objs)
  if ((Obj["WndClass"] == "Static") || ! Obj["Visible"]) return;

  // Check if the object supports any of the specified identification properties
  ValidProps = new Array();
  for (i = 0; i < PropArray["length"]; i++)
  {
    if (aqObject["IsSupported"](Obj, PropArray[i]))
      ValidProps["push"](PropArray[i]);
  }

  // If the object supports at least one of the specified identification properties, map it
  if (ValidProps["length"] > 0)
  {
    // Generate the mapped object name based on the object class
    // and either its caption or (if the caption is empty) index
    MappedName = Obj["WndClass"] + (Obj["WndCaption"] != "" ? Obj["WndCaption"] : Obj["Index"]);
    MappedName = GetValidIdentifier(MappedName);

    // Map the object
    MappedObj = MappedParent["AddNamedChild"](Obj, MappedName, "", ConvertJScriptArray(ValidProps));

    // Map the object's child objects
    for (i = 0; i < Obj["ChildCount"]; i++)
      MapObject(Obj["Child"](i), PropArray, MappedObj);
  }
  else
  {
    Log["Error"](aqString["Format"]("The %s object was not mapped because it does not support " +
              "any of the specified identification properties.", Obj["FullName"]));
  }
}

// Creates and returns a valid identifier based on the specified string
function GetValidIdentifier(str)
{
  // Remove invalid characters and leading numbers and underscores from the string
  return str["replace"](/\W+/g, "")["replace"](/^[\d_]+/g, "");
}

// Convert a JScript array to a Variant-compatible array
function ConvertJScriptArray(JScriptArray)
{
  var oDict = new ActiveXObject("Scripting.Dictionary");
  oDict["RemoveAll"]();

  for (var i in JScriptArray)
    oDict["Add"](i, JScriptArray[i]);

  return oDict["Items"]();
}

To call this script from a keyword test, you can use the Run Script Routine operation.

See Also

Adding Objects to the Name Mapping Repository
Name Mapping
Mapping Objects Automatically
Mapping Objects Manually

Highlight search results