Relevant Codes

A Test Development Resource for HP QuickTest Professional.

QTP: Synchronization for AJAX Applications

by Anshoo Arora on August 10, 2009


Creative Commons License photo credit: luisvilla

Ajax has been extremely popular amongst developers these days and is being integrated with hundreds of websites. We have also been recently working on a light-weight Ajax application – and its not being very easy, especially when the time it takes for all elements to load is high. I have also read on several forums about automation developers facing issue with Ajax synchronization, and obviously its quite challenging.

There are several ways this class can be used to synchronize with Ajax applications. It can be used to synchronize with either changes in WebTables or with changes in the number of objects existing at any given time (WebElement, WebCheckBox etc). I wanted to record a video of this class in action, but because of other obligations, this has not been possible. However, below you will find a textual representation of how this class will work to synchronize Ajax applications:

ObjectSync: Set this to ensure that the object exists on the page before verifying if its Ajax elements have completed loading.

'Setting the object sync:
AjaxUtil.ObjectSync = 20

StatusSync: Set this to ensure that after the object exists, code waits for StatusSync max seconds for all elements to load. The class will keep on waiting until no changes in the application occur for StatusSync seconds.

'Setting the status sync:
AjaxUtil.StatusSync = 20

Loading Objects: You can load the objects initially (or right before you need to execute them). This makes it easy to call your objects anywhere in the script. Also, it is mandatory to add the objects where synchronization occurs (only the WebTables and the Browsers need to be added here, since that’s where all the action takes place). When you’re checking if any elements are added or removed from the page, you only have to add the Browser object in the collection and use AjaxUtil.SyncObjects to sync with the changes. However, if you’re synchronizing your script with (text) changes in the WebTable, and would like to see what text changed, you would have to add the WebTable in the collection also (AjaxUtil.SyncWebTable). But, please note that if the only concern is to check for changes in the WebTable, you can simply add the browser and check for changes in the WebElements.

' Page 1
AjaxUtil.AddObject "MyBrowser1", Browser("title:=Browser1")
' Page 2
AjaxUtil.AddObject "MyBrowser2", Browser("title:=Browser2")
' Synchronize with changes in WebTable text in Page 3
AjaxUtil.AddObject "MyTable1", Browser("title:=Browser3").WebTable("innertext:=WebTable")
' Synchronize with changes in another WebTable text in Page 4
AjaxUtil.AddObject "MyTable2", Browser("title:=Browser4").WebTable("innertext:=WebTable")

How To: Synchronize with changes in object count

AjaxUtil.SetBrowser "MyBrowser1"   ' Mandatory step; must set the browser each time.
AjaxUtil.SyncObjects               ' This step will synchronize with WebElements in "MyBrowser1"

'Synchronize with changes in Page 2
AjaxUtil.SetBrowser "MyBrowser2"
AjaxUtil.SyncObjects

How To: Synchronize changes in WebTable

'Synchronize with "MyTable1" in Page 3
AjaxUtil.SyncWebTable "MyTable1"
 
'Synchronize with "MyTable2" in Page 4
AjaxUtil.SyncWebTable "MyTable2"

Simple! Isn’t it?

Working example:

SystemUtil.Run "iexplore.exe", "www.kayak.com"
 
If Not Browser("title:=Cheap Flights.*").Exist(10) Then ExitTest
 
AjaxUtil.ObjectSync = 30  ' Check if the added objects exist before we synchronize
AjaxUtil.StatusSync = 10    ' Wait for a max 10 seconds if there is no status change

'Add Objects to collection (Its mandatory that you add objects to the class)
AjaxUtil.AddObject "MyBrowser1", Browser("title:=Cheap Flights.*")
AjaxUtil.AddObject "MyTable", Browser("title:=Kayak.com Search Results")_
    .WebTable("innertext:=.*of.*roundtrips shown.*|.*forget all.*", "index:=0")
AjaxUtil.AddObject "MyBrowser2", Browser("title:=Kayak.com Search Results")
 
Browser("title:=Cheap Flights.*").WebEdit("html id:=destination")_
    .Set "Atlanta, GA - Hartsfield-Jackson (ATL)"
 
' Its mandatory that you set a browser for which you will run the test for changes in elements
AjaxUtil.SetBrowser "MyBrowser1"        ' Required Step

' Synchronize for changes in objects:
AjaxUtil.SyncObjects "WebElement"
 
Browser("title:=Cheap Flights.*").WebButton("html id:=fdimgbutton").Click
 
'You can modify the ObjectSync and StatusSync times here for the next operations.
'Example: StatusSync is increased
AjaxUtil.StatusSync = 15       ' increase the StatusSync

'Synchronize with the flights table
AjaxUtil.SyncWebTable "MyTable"
 
'Test Complete - release dictionary
AjaxUtil.DestroyDict


Notice the results below. When StatusSync was 10, the changes in Page verified was 10 times. Similarly, in the case of the WebTable, the statusSync was 15, thus, the changes were verified 15 times over a course of 15 seconds.

How its done:

You can download the code or copy it to your clipboard using the code below:

Public oGlobalDict
 
'—————————————————————————————————————————————
''
' Class clsAjaxUtil
'
' Methods:
'    AddObject
'    DestroyDict
'    RemoveObject
'    SetBrowser
'    SyncObjects *
'    SyncWebTable *
'
' Author: Anshoo Arora
'
' Version: v1.0
''
'—————————————————————————————————————————————
Class clsAjaxSync
'—————————————————————————————————————————————
 
    Public StatusSync       ' Synchronization with changes in application
    Public ObjectSync       ' To synchronize while the object loads  
    Public oBrowser         ' Reference to the target browser: SetBrowser "MyBrowser"
    Public oTable           ' Reference to the target table: SyncWebTable "MyTable"
    Public intVisCnt
 
    Private sClass          ' Class used in SyncObjects
    Private intObjectCount  ' Changes in Object Counts in SyncObjects
    Private oLocalDict      ' Dictionary

 
    '—————————————————————————————————————————
    ' Name: Class_Initialize (Private)
    ' 
    ' Purpose: Scripting.Dictionary Singleton
    ' 
    ' Parameters: 
    ' 
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Private Sub Class_Initialize
    '—————————————————————————————————————————
        Dim bInit 'As Boolean
    
        bInit = False
 
        'Singleton Pattern (see references in remarks above)
        If IsObject(oGlobalDict) = True Then
            If Not oGlobalDict is Nothing Then
                bInit = True
            End If
        End If
 
        'Only create new object if needed
        If bInit = False Then Set oGlobalDict = CreateObject( "Scripting.Dictionary" )
 
        'Set the local class excel reference to the global Singleton object
        Set oLocalDict = oGlobalDict
    End Sub 
 
    '—————————————————————————————————————————
    ' Name: AddObject (Public)
    ' 
    ' Purpose: Add object to collection
    ' 
    ' Parameters:
    '    sObjectName: Name of the object stored as refereces
    '    oObject: Reference of the object
    ' 
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Public Sub AddObject( sObjectName, ByRef oObject )
    '—————————————————————————————————————————
        If oLocalDict.Exists(sObjectName) Then
            RemoveObject sObjectName
        End If
 
        oLocalDict.Add sObjectName, oObject    
    End Sub 
 
    '—————————————————————————————————————————
    ' Name: SyncWebTable (Public)
    ' 
    ' Purpose: Synchronize the table for StatusSync seconds to make sure no text has changed.
    ' 
    ' Parameters:
    ' 
    ' Author: 
    ' 
    ' Date: 
    '
    ' Remarks:
    '    Uses recursion
    '—————————————————————————————————————————
    Public Sub SyncWebTable( sTableName )
    '—————————————————————————————————————————
        If IsObject(Me.oTable) Then Set Me.oTable = Nothing
        Set Me.oTable = oLocalDict.Item( sTableName )
 
        If ObjectSync = "" Then ObjectSync = 1
 
        ' Check if the Table exists
        If oTable.Exist(ObjectSync) Then
            ' Make sure the table is visible
            If oTable.GetROProperty( "x" ) <> 0 Then
                ' Sync for changes in Table
                SyncText
            Else
                Reporter.ReportEvent micWarning, sTableName, "Object not visible."
                Me.intVisCnt = Me.intVisCnt + 1
                ' Maybe the table is still loading its elements, use recursion and check again.
                If Not Me.intVisCnt >= 2 Then SyncWebTable sTableName
            End If
        Else
            Reporter.ReportEvent micWarning, sTableName, "Object not found."
        End If
    End Sub
 
    '—————————————————————————————————————————
    ' Name: SyncText
    ' 
    ' Purpose: Verify if the table text has changed. If the text
    ' changes, the original text string is reset. The loop runs again until it 
    ' reaches its max: StatusSync.
    ' 
    ' Parameters:
    ' 
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Private Sub SyncText
    '—————————————————————————————————————————
        Dim intWait, sNewText, sReportChanges
        Dim sDivider: sDivider = "================================"
        Dim sRefText: sRefText = GetTableText
 
        If StatusSync = 0 Then StatusSync = 1
 
        Do
            ' Get the updated innerText from the Table
            sNewText = GetTableText
 
            sReportChanges = sRefText &vbLf&sDivider& sNewText &vbLf&sDivider& sReportChanges
 
            ' Verify if there were changes
            If sRefText <> sNewText Then
                intWait = 0
                sRefText = sNewText
            Else
                intWait = intWait + 1
            End If
            Wait(1)
        Loop Until intWait = StatusSync
 
        Reporter.ReportEvent micInfo, "WebTable Sync", sReportChanges
    End Sub 
 
    '—————————————————————————————————————————
    ' Name:SyncObjects (Public)
    ' 
    ' Purpose: Checks whether the object count has changed.
    ' 
    ' Parameters:
    '  
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Public Sub SyncObjects( ClassName )
    '—————————————————————————————————————————
        Dim intWait, intNewCount, sReportChanges
        Dim sDivider: sDivider = "================================"
 
        sClass = ClassName
        Dim intObjectCount: intObjectCount = CountObjects
 
        If StatusSync = 0 Then StatusSync = 1
 
        Do 
            ' Get the current number of objects count for ClassName
            intNewCount = CountObjects
 
            sReportChanges = "Old: " & intObjectCount &vbLf& "New: " &_
                            intNewCount &vbLf&sDivider&vbLf& sReportChanges &vbLf
 
            ' Verify if there were any changes in the count for ClassName
            If intObjectCount <> intNewCount Then
                intWait = 0
                intObjectCount = intNewCount
            Else
                intWait = intWait + 1
            End If
            Wait(1)
        Loop Until intWait = StatusSync
 
        Reporter.ReportEvent micInfo, "Sync Objects", sReportChanges
    End Sub    
 
    '—————————————————————————————————————————
    ' Name: SetBrowser (Public)
    ' 
    ' Purpose: Set the browser in which the objects count is being searched for
    ' 
    ' Parameters:
    ' 
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Public Sub SetBrowser( sBrowserName )
    '—————————————————————————————————————————
        Set Me.oBrowser = oLocalDict.Item( sBrowserName )
    End Sub    
 
    '—————————————————————————————————————————
    ' Name: RemoveObject (Public)
    ' 
    ' Purpose: Remove object from collection
    ' 
    ' Parameters:
    ' 
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Public Sub RemoveObject( sObjectName )
    '—————————————————————————————————————————
        If oLocalDict.Exists(sObjectName) Then
            oLocalDict.Remove(sObjectName)
        End If
    End Sub
 
    '—————————————————————————————————————————
    ' Name: DestroyDict (Public)
    ' 
    ' Purpose: Releases Dictionary
    ' 
    ' Parameters:
    ' 
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Public Sub DestroyDict
    '—————————————————————————————————————————
        oLocalDict.RemoveAll
        Set oLocalDict = Nothing
        Set oGlobalDict = Nothing
    End Sub
 
    '—————————————————————————————————————————
    ' Name: CountObjects (Private)
    ' 
    ' Purpose: Internal Function to retrieve visible object count
    ' 
    ' Parameters: 
    ' 
    ' Return:
    '    Integer
    '   
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Private Function CountObjects
    '—————————————————————————————————————————
        Dim pDesc: Set pDesc = Description.Create
        pDesc( "micclass" ).Value = sClass
 
        '* Get all objects with micClass = sClass
        CountObjects = oBrowser.Page("micclass:=Page").ChildObjects(pDesc).Count
 
        '* Get only visible objects
        pDesc( "x" ).Value = 0
        CountObjects = CountObjects - oBrowser.Page("micclass:=Page").ChildObjects(pDesc).Count
    End Function
 
    '—————————————————————————————————————————
    ' Name: GetTableText (Private)
    ' 
    ' Purpose: Retrieves the innertext of the table
    ' 
    ' Parameters:
    ' 
    ' Return:
    '    String
    '
    ' Author: 
    ' 
    ' Date: 
    '—————————————————————————————————————————
    Private Function GetTableText
    '—————————————————————————————————————————
        Reporter.Filter = rfDisableAll
        On Error Resume Next
            oTable.Init
            GetTableText = oTable.object.innerText
 
            If Err.Number <> 0 Then
                Err.Clear
            End If
        On Error Goto 0
        Reporter.Filter = rfEnableAll
    End Function
 
End Class
 
Set Ajax = New clsAjaxSync
 
Public Function AjaxUtil
    Set AjaxUtil = Ajax
End Function

I hope you find this useful.

References

  1. Singleton Pattern, by Yaron Assa
  2. Microsoft Developer Networks – Implementing Singleton in C#

If you have any questions, please ask them in the comments section. If your query is confidential, please use the Contact Form to send me an e-mail instead.

{ 11 comments… read them below or add one }

1 Polprav October 16, 2009 at 12:04 pm

Hello from Russia!
Can I quote a post in your blog with the link to you?

Reply

2 Anshoo Arora October 16, 2009 at 6:57 pm

Sure. Thank you.

Reply

3 Vishal Singh April 11, 2010 at 6:41 pm

Hi Sir,
Please can you send me a QTP framework where i can write scripte and can prepare data sheet.
I want this for Understanding the QTP Framework
Email ID: vishal04061986@gmail.com
Thanks & Regards
Vishal Singh

Reply

4 ramani April 14, 2010 at 1:45 pm

Hello Sir,

I have used above code in my script. But it is giving error “Object required” on the line AjaxUtil.AddObject “MyBrowser1″, Browser(“title:=Cheap Flights.*”)

Here is the line after I changed it to my application : AjaxUtil.AddObject “MyBrowser”, Browser(” mic:”)

Any help is appreciated on this issue.
Thanks

Reply

5 Anshoo Arora April 14, 2010 at 3:30 pm

Ramani,

This approach may not prove to be very helpful if you’re using a custom toolkit.

To answer your question though, have you associated the AjaxUtil class with your test?

Reply

6 Jiten April 17, 2010 at 2:26 am

Thanks Anshoo… This example would help me a lot in solving synchronisation problem in current project.
I would use it with your reference… thanks.

I have always found advanced qtp guyz going ahead in time, and help us with innovations like these.

Jiten

Reply

7 Anshoo Arora April 17, 2010 at 10:53 am

Thanks, Jiten for your kind words. But, please note that this approach may not prove to be very helpful if you’re using a custom toolkit.

Reply

8 Sunil July 17, 2010 at 9:33 am

Hi Anshoo,

I was wondering if I need to install ajax addin for your sample code.

Sunil

Reply

9 Anshoo Arora July 30, 2010 at 8:42 am

Sunil,

Please see here. There is no AJAX add-in in particular, but you can use the Web 2.0 extension instead.

Reply

10 Eshwari August 11, 2010 at 1:08 am

Hi Anshoo,
Thank you for the utility that you have provided for using Ajax controls. Could you please help me in using the same Ajax controls/DOJO controls with QTP Web 2.0 Extensibility Accelerator as well. I am able to recognize the controls as Dojo controls with this Add-In, but didn’t find any clue in using them programmatically in QTP.

Reply

11 Anshoo Arora August 11, 2010 at 3:22 pm

Eshwari,

I still need to learn to configure and use the Web 2.0 Accelerator :)

I’ve done some automation with Dojo toolkit without its Extensibility support, and I could automate a lot by using mostly DOM. Maybe you can share some techniques you can used with us? I would love to share your findings with the readers.

Reply

Leave a Comment

Previous post:

Next post: