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 = 20StatusSync: 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 = 20Loading 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
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 }
Hello from Russia!
Can I quote a post in your blog with the link to you?
Sure. Thank you.
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
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
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?
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
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.
Hi Anshoo,
I was wondering if I need to install ajax addin for your sample code.
Sunil
Sunil,
Please see here. There is no AJAX add-in in particular, but you can use the Web 2.0 extension instead.
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.
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.