[Tutorial] Building a QTP Add-In for jQueryUI Widget using HP Web Extensibility Accelerator

jQuery UI is a jQuery user interface library. It provides interactions, widgets, effects, and theming for building Rich Internet Applications. jQuery UI provides a number of UI widgets such as Accordion, Datepicker, Slider, Dialog, Tabs etc.

These widgets are built using a number of low-level HTML elements such as DIVs, Unordered Lists, and Input Tags etc. While QTP can recognize these elements individually, we can build an add-in to recognize these controls as native jQuery UI widgets using HP Extensibility Accelerator for QTP. We can then perform native operations supported by jQuery framework.

In this post I’ll explain step by step instructions to build an add-in which will support identification of jQuery UI Tab widget. For more information on jQuery UI Tab visit http://jqueryui.com/demos/tabs/

Step 1 – Create a Web Extensibility Project

  1. Open HP Extensibility Accelerator
  2. Select File → New → Project option from HP Extensibility Accelerator main menu
  3. On New Project dialog box select “Web Add-in Extensibility” in Project types: list
  4. In Templates: select “Web Add-In Extensibility Project”
  5. Enter “jQueryUI” in Name field
  6. Click on OK button

A new Web Add-In Extensibility Project will be created in HP Extensibility Accelerator

Step 2 – Define a new Test Object Class

Click Add [+] button in Test Object Classes list. A new entry will be added to Test Object Classes list as “Control1” and a new Tab will be added with “Control1” title under the Workflow Section

Step 3 – General Settings

  1. On “Control1” tab select the General Tab
  2. Enter “jQueryUITab” in Name field. This will change the Tab name as well as entry in Test Object Classes List
  3. Enter “jQuery UI Tab Control” in Description field
  4. You can provide a custom icon using Icon File options. I have kept this as default icon displayed for WebElement Test Object
  5. In Class Type section, select “WebElement” in Base class dropdown
  6. Click on Advanced Options Section
  7. Go to the Record Options Section
  8. Enter “AddEventHandler” in Event registration function name: field
  9. Uncheck Handle events using standard Web event configuration checkbox
  10. Check Handle events on child elements using standard Web event configuration checkbox

Step 4 – Identification
Next, we need to configure the identification of this control type. Before performing below steps, open the Page containing jQueryUI Tab widget in the Internet Explorer. I have built this example on jQuery UI Tab demo page at http://jqueryui.com/demos/tabs/default.html

  1. Select the Map to Controls tab
  2. Under the Identify Control section, click on Select Controls… button. This will hide the HP Extensibility Accelerator window and a small panel will appear in the center of Desktop with Create Rules and Cancel Button
  3. Activate the Internet Explorer window where the http://jqueryui.com/demos/tabs/default.html  page is displayed
  4. Move the mouse over jQuery UI Tab widget displayed on the page. This control will be heighted by a Box with red border and small box containing tag and class information
  5. Hover mouse until the box shows following details
tag: DIV
class: ui-tabs ui-widget ui-widget-content…
  1. Once you locate the object with above details click on the Red box. A small popup will be displayed with Node hierarchy and classname, id, tagname details. Click on Select button. Tab control will be highlighted
  2. Click on Create Rules button
  3. Following rule will be displayed in the Identify Control Area


We need unique attributes to identify this control. Let’s remove attribute which are not unique or whose value will change based on instance of the widget.

Select the “id Equal tabs” node in the Rule and click on Delete [X] button above.  Value for id attribute will be provided by developers and will change for each instance.

Select the jQuery node in the Rule and click on Delete [X] button above.  Value for jQuery attribute is assigned dynamically.

Following will be the final rule to identify jQuery UI Tab widget

Test this rule by clicking Test Rule button. Widget will be highlighted in light green in Internet Explorer, this means that the rule is correct.QTP will use these attributes and their values to identify jQuery UI Tab widget

Step 5 – Adding Operations

We will add following operations to the jQuery UI Tab Test Object Class

Operation Description Parameter Return Value
Select(Item) Selects a specified Tab on jQuery UI Tab widget Index or Title of the Tab True
GetItems() Returns title for all the Tabs separated by semicolon None String
GetSelectedItem() Returns title of selected Tab None String
GetItemCount() Returns number of Tabs on widget None Int

To add operation to this control, click on Operations tab

Select Operation

  1. Click Add [+] button in Operations list
  2. Enter “Select” in Name field
  3. Enter “Select a specified Tab” in Description field
  4. Check Default Operation checkbox. This will be the default operation for Tab widget
  5. Select “Boolean” in Return Type dropdown
  6. In Operation Arguments list add new “Item” argument with type as “String”. Uncheck the Optional checkbox
  7. We need to write JavaScript code for this operation. Click on Implementation Code… button next to the Name field. This will open the Code editor. A empty function for Select operation will be added by the code editor
  8. Copy following code to the Select function
////////////////////////////////////////////////////////////////////////////////
// Selects specified Tab on a widget
//
// Parameters: Item (Index or Title of the Tab, 
//					 while passing Index prefix # before index value)
// Returns:   Boolean
///////////////////////////////////////////////////////////////////////////////////
function Select(Item) {
	var details = null;
	var success = false;

	if (!Item || typeof (Item) != "string") {
		details = "Cannot select invalid item: " + Item;
		_util.Report(micFail, "Select", toSafeArray(new Array()), details);
		throw Error(details);
	}

	var tabToSelect = -1;

	// Retrieve tab elements.
	var tabs = window.$(_elem).find(".ui-tabs-nav > li");

	if(Item.charAt(0) == '#') {
	// The tab item is specified by index.
	var index = parseInt(Item.substring(1)) - 1;
	if (index < tabs.length && index >= 0)
		tabToSelect = index;
	}
	else {
		// The tab item is specified by name.
		for(var i = 0; i < tabs.length; i++) {
			if (Item == tabs[i].innerText) { 
				tabToSelect = i; 
				break; 
			} 
		} 
	} 
	
	if (tabToSelect >= 0) {
		// Call the Select method of jQueryUI Tab.
		window.$(_elem).tabs().tabs('select',tabToSelect);
		details = "Selected " + Item;
	_util.Report(micDone, "Select", toSafeArray(new Array(Item)), details);
	}
	else {
		details = "Item " + Item + " does not exist.";
		_util.Report(micFail, "Select", toSafeArray(new Array(Item)), details);
		throw Error(details);
	}
	return true;
}

GetItems Operation

  1. Click Add [+] button in Operations list
  2. Enter “GetItems” in Name field
  3. Enter “Returns name of all the Tabs” in Description field
  4. Select “String” in Return Type dropdown
  5. Click on Implementation Code… button next to the Name field. This will open the Code editor. A empty function for Select operation will be added by the code editor
  6. Add following code to the GetItems function
////////////////////////////////////////////////////////////////////////////////
// This returns title of all the Tab on a widget separated by a semicolon 
//
// Parameters: None
// Returns:   String
///////////////////////////////////////////////////////////////////////////////////
function GetItems() {
	var tabLabels = new Array();
	var tabs = window.$(_elem).find(".ui-tabs-nav > li");
	for (var i = 0; i < tabs.length; i++)
		tabLabels.push(tabs[i].innerText);
	return tabLabels.join(";");
}

GetSelectedItem Operation

  1. Click Add [+] button in Operations list
  2. Enter “GetSelectedItem” in Name field
  3. Enter “Returns the name of selected Tab” in Description field
  4. Select “String” in Return Type dropdown
  5. Click on Implementation Code… button next to the Name field. This will open the Code editor. A empty function for Select operation will be added by the code editor
  6. Add following code to the GetSelectedItem function
////////////////////////////////////////////////////////////////////////////////
// This returns title of current selected Tab on widget
//
// Parameters: None
// Returns:   String
///////////////////////////////////////////////////////////////////////////////////
function GetSelectedItem() {
	return window.$(_elem).find(".ui-tabs-nav > li[class*='ui-tabs-selected']")[0].innerText;
}

GetItemCount Operation

  1. Click Add [+] button in Operations list
  2. Enter “GetItemCount” in Name field
  3. Enter “Returns count of Tabs” in Description field
  4. Select “Integer” in Return Type dropdown
  5. Click on Implementation Code… button next to the Name field. This will open the Code editor. A empty function for Select operation will be added by the code editor
  6. Add following code to the GetItemCount function
////////////////////////////////////////////////////////////////////////////////
// This returns count of Tabs on widget
//
// Parameters: None
// Returns:   Integer
///////////////////////////////////////////////////////////////////////////////////
function GetItemCount() {
	return window.$(_elem).find(".ui-tabs-nav > li").length;
}

Step 6 – Adding Properties
To add properties to this control, click on Properties tab

Property Description
all items Gets all tab titles separated by semicolon
items count Gets number of tabs
logical name Gets id of the DIV tag containing Tab widget
selected Gets the title of selected Tab
  1. Click Add [+] button in Properties list. Enter the name of Property. Repeat this for all the properties.
  2. Select the “logical name” property and open Object Identification – Mandatory Section. Click  >> button in middle to move this property in mandatory section
  3. Click on Implementation Code… button . This will open the Code editor. A empty function for Select operation will be added by the code editor
////////////////////////////////////////////////////////////////////////////////
// This returns property value for specified property
//
// Parameters: Name of the Property
// Returns:   String
///////////////////////////////////////////////////////////////////////////////////
function get_property_value(property) {
	if (property == "selected") {
		return window.$(_elem).find(".ui-tabs-nav > li[class*='ui-tabs-selected']")[0].innerText;
	}

	if (property == "items count") {
		return window.$(_elem).find(".ui-tabs-nav > li").length;
	}

	if (property == "all items") {
		var tabLabels = new Array();
		var tabs = window.$(_elem).find(".ui-tabs-nav > li");
		for (var i = 0; i < tabs.length; i++)
			tabLabels.push(tabs[i].innerText);
		return tabLabels.join(";");
	}

	if (property == "logical name")	{
		return _elem.id;
	}
}

Step 7 – Adding functions for Recording Select Operation
In Code editor create following functions. These function will add support for recording Select operation on Tab widget in QTP Record Mode

////////////////////////////////////////////////////////////////////////////////
// This function registers to listen for events that are specific to this
// control to support recording.
// It registers for clicks on all tabs.
//
// Parameters: none.
// Returns:   Boolean. (In this implementation always true.)
///////////////////////////////////////////////////////////////////////////////////
function AddEventHandler() {
	// Retrieve all of the tabs in the tab strip.
	var tabs = window.$(_elem).find(".ui-tabs-nav > li");

	for (var i = 0; i < tabs.length; i++) {
		var tab = window.$(tabs[i]);
		_util.RegisterForEvent(tab.find("a")[0], "onclick", "onTabClick", tabs[i]);
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////////////
// This is the event handler function called during a recording session when a
// user clicks a tab.
// It retrieves the name of the tab that was clicked and records a step that
// selects the tab.
//
// Name:        onTabClick
// Parameters:  selectedItem - The parameter passed to the RegisterForEvent function,
//              in this case the tab that the user clicked.
//              eventObj - The event object for the onclick event.
// Returns:     Boolean. true if the operation was recorded, otherwise false.
///////////////////////////////////////////////////////////////////////////////////
function onTabClick(selectedItem, eventObj) {
	var tabLabel = trim(window.$(selectedItem).text());
	if (!tabLabel)
		return false;
	
	var arr = new Array();
	arr.push(tabLabel);
	_util.Record("Select", toSafeArray(arr), 0);
	return true;
}

Step 8 – Wrap-up and Deployment

  1. Save the Project
  2. We need to deploy this project in QTP so that we can use the newly created add-in for jQuery UI Controls
  3. To deploy on a local machine Select Project Deploy Deploy to Quick Test Professional from Main Menu. A confirmation dialog will be displayed with “Toolkit support set was successfully deployed” message
  4. Restart QTP if it’s open.
  5. In Add-In Manager you will see a new “jQueryUI” Add-In under Web Add-In. Select the Web and jQueryUI Add-In
  6. Open Internet Explorer and navigate to http://jqueryui.com/demos/tabs/default.html
  7. In QTP open the Object Spy and spy on the Tab control. Object Spy will identify this control as jQueryUITab Test Object. It will display properties and operations we defined earlier.
  8. Start recording in QTP and click on one of Tabs, QTP should record the Select operation as follows
Browser("jQuery UI Tabs - Default").Page("jQuery UI Tabs - Default").jQueryUITab("jQueryUITab").Select "Proin dolor"

Here is a sample QTP test

Set objTabWidget =  Browser("jQuery UI Tabs - Default").Page("jQuery UI Tabs - Default").jQueryUITab("tabs")

Print objTabWidget.GetItemCount()
Print objTabWidget.GetItems()

'Select by Index
objTabWidget.Select "#1"
Print objTabWidget.GetSelectedItem()

'Select by Title
objTabWidget.Select "Aenean lacinia"
Print objTabWidget.GetSelectedItem()

Using above steps you can add support for other jQuery UI Widgets such as Accordion, Date Picker, Track Bar etc. For more details visit http://jqueryui.com/demos/

Completed Web Extensbility Project is available at my GitHub Page https://github.com/upgundecha/testomatic/tree/master/Qtp/Web%20Extensibility

Using Google Charts API for Test Results Dashboard

[This post was originally written in May 2008]

Image Charts

I am using Google Charts API to generate charts to visualize test results in Dashboard. Image charts are generated on the fly based on dynamic data and then embedded in to test results generated in HTML format. This feature can be used with any tool/framework where test results are generated in HTML format.  This does not require any software/dependency installed on user’s desktop and comes free from Google.

There are different types of charts you can generate with variety of options. Just hit http://code.google.com/apis/chart/ for API documentation.

Here is an example of Pie and Google-O-Meter types:

<a href="https://unmesh.files.wordpress.com/2012/05/google-charts2.png"><img class="aligncenter size-medium wp-image-333" title="Google Charts" src="https://unmesh.files.wordpress.com/2012/05/google-charts2.png?w=300" alt="" width="300" height="119" /></a>

You can try Pie chart example by simply copying following HTTP request in your Browser’s address bar. A chart will appear in your Browser window once you hit Enter key.

<a href="http://chart.apis.google.com/chart?chtt=Test+Results&amp;chts=000000,12&amp;chs=450x150&amp;chf=bg,s,ffffff&amp;cht=p3&amp;chd=t:66.66,33.33&amp;chl=Tests+Passed(2)|Tests+Failed(1)&amp;chco=006600,990000">http://chart.apis.google.com/chart?chtt=Test+Results&amp;chts=000000,12&amp;chs=450x150&amp;chf=bg,s,ffffff&amp;cht=p3&amp;chd=t:66.66,33.33&amp;chl=Tests+Passed(2)|Tests+Failed(1)&amp;chco=006600,990000</a>

Now, how do we implement this in QTP/VBScript or any other tool of your choice? Here is an example of VBScript function which accepts Number of Test Executed, Passed, and Failed and then generates a nicely formatted HTML report:

Public Function GenerateTestSummaryReport(ByVal intTestsExecuted, ByVal intTestsPassed, ByVal intTestsFailed)

	Set objFSO = CreateObject("Scripting.FileSystemObject")
	Set objFile = objFSO.CreateTextFile("C:\TestSummary.html",True)

	objFile.WriteLine "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
	objFile.WriteLine "<html xmlns='http://www.w3.org/1999/xhtml' >"
	objFile.WriteLine "<head><title>Test Execution Summary Report</title></head>"
	objFile.WriteLine "<body style='font-weight: bold; font-size: 10pt; color: black; font-family: Tahoma'>"
	objFile.WriteLine "<br />Test Execution Summary"
	objFile.WriteLine "<br /><br />Test Suite Summary<table><tr><td style='width: 200px; height: 56px; background-color: #669918'>Total Tests Executed</td><td style='height: 56px; background-color: #669918'>Passed</td><td style='height: 56px; background-color: #669918'>Failed</td></tr>"
	objFile.WriteLine "<tr><td style='width: 200px; background-color: #D0F0A2'>" & intTestsExecuted & "</td><td style='background-color: #D0F0A2'>" & intTestsPassed & "</td><td style='background-color: #D0F0A2'>" & intTestsFailed & "</td></tr>"
	objFile.WriteLine "<tr><td><img src='http://chart.apis.google.com/chart?chtt=Test+Results&amp;chts=000000,12&amp;chs=450x150&amp;chf=bg,s,ffffff&amp;cht=p3&amp;chd=t:" & intTestsPassed & "," & intTestsFailed & "&amp;chl=Tests+Passed(" & intTestsPassed &")|Tests+Failed(" & intTestsFailed & ")&amp;chco=006600,990000' alt='Test Results Chart'/></td></tr>"
	objFile.WriteLine"</table>"

	Set objFile = Nothing
	Set objFSO = Nothing

End Function

'Call Function
Call GenerateTestSummaryReport(5,3,2)

In above code, highlighted IMG tag will make a request to Google Chart API and will show a nicely built chart on the fly when user opens this file in a Web Browser or email client.

If you find API documentation not working for you, there is a very interesting service available at http://dexautomation.com/googlechartgenerator.php

This will enable you to design Charts using guided steps and then it will generate IMG tag which you can embed in your code. Using Google Charting API you could build a nice dashboard for you test suite result reporting.

However Image charts have some limitation on amount of data we can pass. Other alternative is Interactive Chart feature.

Interactive Charts

Unlike image charts which are static, Interactive are charts more advanced. Interactive charts support user interaction and event handling. These are built using JavaScript.

Google Visualization API provides different types of charts for data visualization. Please see following page for sample charts.

http://code.google.com/apis/visualization/documentation/gallery.html

Here is an example in VBScript on how to use Gauge charts for showing test results/build status:

Public Function GenerateTestSummaryReport(ByVal intTestsPassed, ByVal intTestsFailed)
	Set objFSO = CreateObject("Scripting.FileSystemObject")
	Set objFile = objFSO.CreateTextFile("C:\TestSummary.html",True)
	objFile.WriteLine "<html><head><title> Test Execution Summary Report </title>"
	objFile.WriteLine "<script type='text/javascript' src='http://www.google.com/jsapi'></script>"
	objFile.WriteLine "<script type='text/javascript'>"
	objFile.WriteLine "google.load('visualization', '1', {packages:['gauge']});"
	objFile.WriteLine "google.setOnLoadCallback(drawChart);"
	objFile.WriteLine "function drawChart() { var passedData = new google.visualization.DataTable();"
	objFile.WriteLine "passedData.addColumn('string', 'Label'); passedData.addColumn('number', 'Value');"
	objFile.WriteLine "passedData.addRows(1); passedData.setValue(0, 0, 'Tests Passed');"
	objFile.WriteLine "passedData.setValue(0, 1, " & intTestsPassed & ");"
	objFile.WriteLine "var passedChart = new google.visualization.Gauge(document.getElementById('chart_passed'));"
	objFile.WriteLine "var passedOptions = {width: 400, height: 120, redFrom: 0, redTo: 30, yellowFrom: 31, yellowTo: 60, greenFrom: 61, greenTo: 100, minorTicks: 5};"
	objFile.WriteLine "passedChart.draw(passedData, passedOptions);"
	objFile.WriteLine "var failedData = new google.visualization.DataTable();"
	objFile.WriteLine "failedData.addColumn('string', 'Label');"
	objFile.WriteLine "failedData.addColumn('number', 'Value');"
	objFile.WriteLine "failedData.addRows(1);"
	objFile.WriteLine "failedData.setValue(0, 0, 'Tests Failed');"
	objFile.WriteLine "failedData.setValue(0, 1, " & intTestsFailed & ");"
	objFile.WriteLine "var failedChart = new google.visualization.Gauge(document.getElementById('chart_failed'));"
	objFile.WriteLine "var failedOptions = {width: 400, height: 120, redFrom: 31, redTo: 100, yellowFrom: 11, yellowTo: 30, greenFrom: 0, greenTo: 10, minorTicks: 5};"
	objFile.WriteLine "failedChart.draw(failedData, failedOptions);}"
	objFile.WriteLine "</script></head>"
	objFile.WriteLine "<body style='font-weight: bold; font-size: 10pt; color: black; font-family: Tahoma'><br />Test Execution Summary"
	objFile.WriteLine "<br /><br />Test Suite Summary<table><tr><td style='width: 200px; height: 56px; background-color: #669918'>Total Tests Executed</td><td style='height: 56px; background-color: #669918'>Passed</td><td style='height: 56px; background-color: #669918'>Failed</td></tr>"
	objFile.WriteLine "<tr><td style='width: 200px; background-color: #D0F0A2'>100</td><td style='background-color: #D0F0A2'>85</td><td style='background-color: #D0F0A2'>15</td></tr>"
	objFile.WriteLine "<tr><td><div id='chart_passed'></div></td><td><div id='chart_failed'></div></td></tr></table></body></html>"
	Set objFile = Nothing
	Set objFSO = Nothing
End Function

'Call Function
Call GenerateTestSummaryReport(15,85)

Here is interactive Chart Generated:

You can add more metrics to your reports and build more meaningful Charts.

Using DotNetFactory Utility Object in QTP

[This post was originally written in March 2007]

Using DotNetFactory object you can access .NET base classes into your QTP scripts. This also supports using DLLs compiled from any .NET language. This features works on .NET Interop which means you can call .NET components in COM environment and vice versa.

Using ArrayList Class

Here is an example where we have a test scenario which requires sorting and searching of an array of values. We will use ArrayList class from System.Collections namespace. Collections namespace has variety of classes like Array, HashTable, Stack, LinkedList etc.

ArrayList class is enriched with various ready to use methods for Sorting, Reversing, and Searching items.


'//Create an instance of System.Collections.ArrayList using DotnetFactory.CreateInstance method

Set myList = DotnetFactory.CreateInstance("System.Collections.ArrayList")

'//Add items to the List Array
myList.Add("American")
myList.Add("Ukrainian")
myList.Add("bElarusian")
myList.Add("Indian")
myList.Add("123")
myList.Add("$@%^%@")

'//ArrayList.Capacity/ArrayList.Count shows the number of elements it have

Print "Capacity of ArrayList is: " & myList.Capacity
Print "Count of ArrayList is: " & myList.Count

'//ArrayList adds empty memory locations when new items are added to the list
'//using TrimToSize you can remove these empty locations from the list
myList.TrimToSize

'//SORTING
'//You can sort the contents of an ArrayList using Sort method, for Ascending sort:

myList.Sort

'//For displaying array contents in a String
strMsg = ""
For intCnt = 0 To myList.Count - 1
 strMsg = strMsg & myList.Item(CInt(intCnt)) & vbCrLf
Next

Print "Sorted Array List: Asecending" & vbCrLf & strMsg

'//You can Sort an array Descending with Reverse method. But first you need to sort the list
'//using Sort method

myList.Reverse

'//For displaying array contents in a String
strMsg = ""
For intCnt = 0 To myList.Count - 1
 strMsg = strMsg & myList.Item(CInt(intCnt)) & vbCrLf
Next

Print "Sorted Array List: Descending" & vbCrLf & strMsg

'//SEARCHING
'//You can search an item using IndexOf and BinarySearch method. These method return zero based index
'//of an item for its first occurrence

Print "Indian item found at: " & myList.IndexOf("Indian") & vbCrLf & strMsg,,"IndexOf Search"
Print "indian item found at: " & myList.IndexOf("indian") & vbCrLf & strMsg,,"IndexOf Search"
'//For BinarySearch ArrayList should be sorted in Ascending Order
myList.Sort

'//For displaying array contents in a String
strMsg = ""
For intCnt = 0 To myList.Count - 1
 strMsg = strMsg & myList.Item(CInt(intCnt)) & vbCrLf
Next

Print ,"Binary Search: " & vbCrLf & "bElarusian item found at: " & myList.BinarySearch("bElarusian") & vbCrLf & strMsg
Print ,"Binary Search: " & vbCrLf & "Belarusian item found at: " & myList.BinarySearch("Belarusian") & vbCrLf & strMsg

'//ArrayList.Contains method searches for given item and returns True/False
Print myList.Contains("Indian")

Set myList = Nothing

This approach is much faster and reliable than writing code our own code.

Using Clipboard Object

You can access Clipboard object using System.Windows.Forms.Clipboard class. Here is an example showing how to Clear, Set and Get contents from Clipboard.


'//This is not supported in 8x versions

Set oClip = DotNetFactory.CreateInstance("System.Windows.Forms.Clipboard")

'//Clears the Clipboard
oClip.Clear

'//Sets sample text on Clipboard
oClip.SetText "DotNetFactory Rocks!!"

'//Retrieves text from Clipboard
strContents = oClip.GetText

Print strContents

File Input/Output

Using System.IO.File class you can create, read and write to files. Here is an example on writing to and reading from a file.


'//This is not supported in 8x versions

Set objFile = DotNetFactory.CreateInstance("System.IO.File")

'//Creates a new file with given text
objFile.WriteAllText "C:\Test.txt","DotNetFactory Rocks!!"

'//Retrieves text from given file
strContents = objFile.ReadAllText("C:\Test.txt")

Print strContents

This way you can use 100s of classes available in .NET Framework in your QTP Scripts.

TestComplete Running on 64Bit?

Here is a Quick Tip to find out if TestComplete is running on 64 Bit or 32 Bit OS Platform.

If dotNET.System.Environment.Is64BitOperatingSystem = True Then
 Log.Message("64Bit")
Else
 Log.Message("32Bit")
End If

You can access the .NET Framework System.Environment class’s property Is64BitOperatingSystem to find out if the OS Platform is 64 Bit or 32 Bit using the TestComplete dotNET object. The dotNET object allows access to .NET Framework classes as well as custom assemblies through TestComplete.

Recording Screencast of TestComplete Test Runs

In this post we’ll explore the possibility of recording a screencast of TestComplete test runs, the other test tool I use extensively at work.

TestComplete does not have features to record screencast of test execution. However we can use Microsoft Expression Encoder 4 SDK to record videos programmatically from test scripts.

A recoded screencast of test run can help you in investigation of unattended execution or even demo the software or create training videos.

Microsoft Expression Encoder 4 has basic recording features with limited codec support. You can download it free from http://www.microsoft.com/expression/products/encoder4_overview.aspx

While the commercial version Microsoft Expression Encoder 4 Pro provides number of additional features and codecs.

I will use the CLR Bridge & dotNET object available in TestComplete to call the Encoder SDK functions to create the screencast.

Using CLR Bridge in TestComplete we can use .NET assemblies in test scripts and extend the test script code to use features from standard .NET classes as well as custom libraries.

Setting up Encoder SDK in CLR Bridge

Let’s add the Encoder SDK library to the CLR Bridge in TestComplete Project Settings by following steps:

1. Go to TestComplete Project Setting by selecting Current Project Settings from Tools Menu
2. Select CLR Bridge option on left side of Project Properties Pane
3. Click Browse Files button to select the Encoder SDK DLL file. This will display Select Assembly dialog box
4. On Select Assembly dialog navigate to C:\Program Files\Microsoft Expression\Encoder 4\SDK folder and select the Microsoft.Expression.Encoder.dll file

This will add Encoder library to the TestComplete Project and provide access to the internal classes and functions using the dotNET object.

Using ScreenCaptureJob Class in TestComplete Script

Let’s create a instance of ScreenCaptureJob class from the Encoder SDK and call methods to start & stop video recording:

Sub NotePadEditCutTest()
	Dim wndNotepad, objScreenCaptureJob

	'Create a new ScreenCapture Job using it's Constructor
	Set objScreenCaptureJob = dotNET.Microsoft_Expression_Encoder_ScreenCapture.ScreenCaptureJob.zctor

	'Specify the path & name of the Output File
	objScreenCaptureJob.OutputScreenCaptureFileName = "C:\Data\NotePadEditCutTest.wmv"

	'Start the Video Recording using ScreenCaptureJob's Start Method
	Call objScreenCaptureJob.Start()

	'Test Code
	Call TestedApps.notepad.Run(1, True)
	Set wndNotepad = Aliases.notepad.wndNotepad
	Call wndNotepad.Edit.Keys("SOme Text")
	Call wndNotepad.MainMenu.Click("Edit|Select All")
	Call wndNotepad.MainMenu.Click("Edit|Cut")
	Call aqObject.CheckProperty(Aliases.notepad.wndNotepad.Edit, "wText", cmpEqual, "")

	'Stop the Video Recording using ScreenCaptureJob's Stop Method
	Call objScreenCaptureJob.Stop()
End Sub

The output screencast will be created in wmv format. This screencast can be edited in Expression Encoder UI.

Recording Screencast of Selenium Tests

I was looking for ways to record a video/screencast of Selenium Test Run in Java and came across this brilliant tool called Monte Media Library developed by Werner Randelshofer. This post describes using the ScreenRecorder class from Monte Media Library for recording screencast of Selenium Tests in Java.

Little about ScreenRecorder

ScreenRecoder supports “AVI” and “QuickTime” format for recording the video. For “AVI” format you need to install TSCC Codec (Techsmith Screen Capture Codec) while “QuickTime” format is supported by Apple’s QuickTime Player. ScreenRecorder provides multiple configurations for colors, mouse cursor, screen rate, mouse rate, audio etc. on GUI as well as programmatically.

You need to download ScreenRecorder.jar file from Monte’s Home Page. ScreenRecorder.jar can be launched as a standalone GUI for recording actions from Desktop window or you can add this to your project and import ScreenRecorder class for recording screen video programmatically.

Using ScreenRecorder Class

Following example is created in Eclipse and you need to add ScreenRecorder.jar to the build path of Project.

ScreenRecorder.jar contains ScreenRecorder class which can be called from a Selenium Script for recording the test session in following way:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.monte.media.math.Rational;
import org.monte.media.Format;
import org.monte.screenrecorder.ScreenRecorder;
import static org.monte.media.AudioFormatKeys.*;
import static org.monte.media.VideoFormatKeys.*;
import java.awt.*;

public class SeScreenCastDemo {
	
	public static void main(String[] args) throws Exception {

		//Create a instance of GraphicsConfiguration to get the Graphics configuration
		//of the Screen. This is needed for ScreenRecorder class.
		GraphicsConfiguration gc = GraphicsEnvironment//
		.getLocalGraphicsEnvironment()//
		.getDefaultScreenDevice()//
		.getDefaultConfiguration();

		//Create a instance of ScreenRecorder with the required configurations
		ScreenRecorder screenRecorder = new ScreenRecorder(gc,
		new Format(MediaTypeKey, MediaType.FILE, MimeTypeKey, MIME_AVI),
		new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
		CompressorNameKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE,
		DepthKey, (int)24, FrameRateKey, Rational.valueOf(15),
		QualityKey, 1.0f,
		KeyFrameIntervalKey, (int) (15 * 60)),
		new Format(MediaTypeKey, MediaType.VIDEO, EncodingKey,"black",
		FrameRateKey, Rational.valueOf(30)),
		null);

	//Create a new instance of the Firefox driver
	WebDriver driver = new FirefoxDriver();

	//Call the start method of ScreenRecorder to begin recording
	screenRecorder.start();

	//And now use this to visit Google
	driver.get("http://www.google.com");

	//Find the text input element by its name
	WebElement element = driver.findElement(By.name("q"));

	//Enter something to search for
	element.sendKeys("Cheese!");

	//Now submit the form. WebDriver will find the form for us from the element
	element.submit();

	//Check the title of the page
	System.out.println("Page title is: " + driver.getTitle());

	//Google's search is rendered dynamically with JavaScript.
	//Wait for the page to load, timeout after 10 seconds
	(new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() {
	public Boolean apply(WebDriver d) {
	return d.getTitle().toLowerCase().startsWith("cheese!");
	}});

	//Should see: "cheese! - Google Search"
	System.out.println("Page title is: " + driver.getTitle());

	//Close the browser
	driver.quit();

	//Call the stop method of ScreenRecorder to end the recording
	screenRecorder.stop();
	}
}

The ScreenRecorder class will create recordings in the home folder i.e. “Videos” on Windows, and “Movies” on Mac OS X by default. The ScreenRecorder class needs first argument as GraphicsConfiguration. You can get this by accessing GraphicsEnvironment member of AWT class by following way:

GraphicsConfiguration gc = GraphicsEnvironment//
 .getLocalGraphicsEnvironment()//
 .getDefaultScreenDevice()//
 .getDefaultConfiguration();

The ScreenRecorder captures screen interactions like a charm which can be very useful for analysing Selenium Tests.

Update:

Same can be achieved with Selenium .NET Binding (in C#/VB.NET) by using the Microsoft Expression Encoder 4 SDK. You can download the Encoder SDK from http://www.microsoft.com/expression/products/encoder4_overview.aspx. Use the ScreenCaptureJob class for recording video of Selenium Tests in C#/VB.NET.

Update:
This code has been tested on ScreenRecorder Version 0.6.6

Using DOM, XPath & CSS for Object Identification in QTP – Part 3

We saw how to use HTML DOM and XPath for Web Object Identification in Part 1 & Part 2. In this final Part we will explore using CSS for Object Identification in QTP 11.

Introduction to CSS (Cascading Style Sheets)

CSS (Cascading Style Sheets) is a language used for formatting and rendering of HTML and XML documents.

CSS uses Selectors for binding style properties to elements in the document. These Selectors can be used by QTP as another object identification strategy.

Finding elements with XPath can be costly in terms of performance. CSS Selectors can be more efficient as they are forward only. However they do not provide backword search flexibility as XPaths.

Using CSS for Web Object Indetification

Along with XPath, QTP 11 has added CSS Object Identification Property to all Test Objects in Web Add-In. You can specify CSS Selectors by using css Property.

You can enable and use css Property in Object Repository or in Descriptive Programming. In following example CSS class attribute is used identify Sign In Button on Gmail Home Page:


Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").WebButton("css:=input.g-button").Click

Using CSS class attribute is most common and simplest method to indetify the elements. You can identify the elements using class attribute in following way:


element.class_attribute

In above example we identified Sign In Button on Gmail Home Page. Here is the HTML Syntax for Sign In Button. You can see developers have assigned class attribute as g-button:

<input id="signIn" class="g-button" type="submit" value="Sign in" name="signIn" style="background-color: rgb(77, 144, 254);">

Identifying Elements using ID

As we saw in XPath, we can identify elements using their IDs, CSS also has similar way to identify elements using ID. Following is the syntax for identifying elements using ID:

element#id

We need to specify a # between element type and its ID. In following example we will identify User Name Text Box on Gmail Home Page using its ID:


Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").WebEdit("css:=input#Email").Set "myname"

Identifying Elements using Attributes

Similar to XPath, we can also use element attributes to identify objects. Following is the syntax for identifying elements using their attributes:

element[attribute=value]

In following example, we will identify and check Stay signed in Checkbox on Gmail Home Page using type attribute:


Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").WebCheckBox("css:=input[type='checkbox']").Set "ON"

We can also perform partial match on attribute values using following operators:

Operator Description
^=attribute_value Finds the element attribute starting with the value passed. This is similar to starts-with() function in XPath
*= attribute_value Finds the element attribute which contains the value passed. This is similar to contains() function in XPath
$= attribute_value Finds the element attribute ending with the value passed. This is similar to ends-with() function in XPath

In following example, developers have assigned dynamic attribute values for all the input elements in following way:

<div id='login_area'>
<input type='text' name='text_1'>
</div>

We can use either ^= or *= function to identify this object in following way:


Browser("title:= Test App").Page("title:= test App").WebEdit("css:=div#login_area input[name^='text_']").Set "somevalue"

Browser("title:= Test App").Page("title:= test App").WebEdit("css:=div#login_area input[name*='text_']").Set "somevalue"

Identifying Elements using Text Contents

Locating elements by the text they contain can be quite useful. To identify elements, we need to use CSS contains pseudo class. This will match the entire contents of the element.


element:contains('text')

In following example, we will use contains pseudo class to identify the Create an account link on Gmail Home Page:


Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").Link("css:=a:contains('Create an account')","index:=0").Click

Identifying Elements using Relationships

Similar to XPath Axes, we can identify elements using relationships in CSS. Following are some exmaples  for identifying elements by their relationships:

Child/Descendent Elements nth-child orE1 >E2
Element <E1> following some sibling <E2> E2 ~ E1
Element <E1> immediately following sibling <E2> E2 + E1
Element <E1> following sibling <E2> with one intermediary E2 + * + E1

For more combinations please refer here.

We will use the Shopping Cart example from Part 2 to understand identifying elements using relationships in CSS. In following example we select an item by entering the quantity and click the Shopping Cart image to add the item in Cart:


Browser("title:= ShopX").Page("title:= ShopX ").WebEdit("xpath:=td:contains('0002')+td+td+td>div>form>div>input[name='qty']").Set "10"
Browser("title:= ShopX").Page("title:= ShopX ").Image("xpath:=td:contains('0002')+td+td+td>div>form>div>input[name='add']").Click

Here is another example I created for a custom Dropdown control. This control is created with combination of a Text Box, a Button and a DIV containing multiple LI elements. QTP identify this control in following way:

Custom Dropdown Control

When clicked on the Dropdown Button which is identified as WebButton, a dynamic DIV is displayed with values available for selection in HTML LI elements. This is automated using CSS in following way:

Browser(title:=MyApp").Page("title:=MyApp).WebButton("id:=item_Dropdown").Click
Browser(title:=MyApp").Page("title:=MyApp).WebElement("css:=div#item_Dropdown>li:contains('Value2')").Click

There are endless possibilities to identify objects using CSS and XPath. However these are some of the example to understand the basics.

I will close this series with a caution that DOM, XPath, CSS type of identifiers are dependent on locations and page structure and may not work always where developers change the UI frequently.