QTP Object Required Error due to dynamic change in Web - automation

When an action is performed, it will last for a while to get a message showing that this action is successful. Here I use a function to test if the message comes out:
For i = 0 To NumberOfiframe - 1
Set objPage = iframe(i).Object
Set objTag = objPage.GetElementsByTagName("span")
intTotalLink = objTag.Length - 1
For intCtr = 0 to intTotalLink
If objTag(intCtr) = null Then
Exit Function
End If
strContent = objTag(intCtr).InnerText
endPosition = InStr(1, strContent, "Not all transport requests yet")
If endPosition > 0 then
TRNotReleased = FALSE
Exit for
End If
Next
Next
while when the message comes out, QTP will pop up a window showing "Object Required: objTag(..)", my point is that QTP is not able to find the set object when Web UI changes.
As the message contains different information for each operation, is there any solutions for me to get over from this issue?
Thanks in advance.

When you use objPage.GetElementsByTagName you're getting a reference to a DOM object on the page, this is not a QTP test object but rather an object belonging to the browser. When the DOM in the browser changes the object you're holding onto ceases to be valid.
If you want to access the object after the HTML changes you'll have to ask QTP to retrieve it again. Perhaps like this:
For i = 0 To NumberOfiframe - 1
Set objPage = iframe(i).Object
Set objTag = objPage.GetElementsByTagName("span")
intTotalLink = objTag.Length - 1
For intCtr = 0 to intTotalLink
' Get the collection of objects again
Set objTagCurr = objPage.GetElementsByTagName("span")
Set currObj = objTagCurr(intCtr)

Related

Is there a better way to accomplish this loop?

I'm pretty new with VBA, but I've been muddling through to make a program for my team.
This piece of code works most of the time, but tends to hang on occasion. I can't figure out why it would hang sometimes, and work perfectly most of the time, so I'm now trying to figure out a better way to accomplish this loop. I know that this method of looping isn't the best way to do things, but not sure how to accomplish the task.
My webpage operates in a PEGA Web application, and the native IE ready state indicators are always 'ready' so I have to use the web application's ready state markers.
Can anyone help me out?
Public Sub WaitingForRS()
' FIND THE C360 WINDOW
Marker = 0
Set objShell = CreateObject("Shell.Application")
IE_count = objShell.Windows.Count
For x = 0 To (IE_count - 1)
On Error Resume Next
my_url = objShell.Windows(x).Document.Location
my_title = objShell.Windows(x).Document.title
If my_title Like "Coverage User" & "*" Then
Set C360Window = objShell.Windows(x)
Marker = 1
Exit For
Else
End If
Next
If Marker = 0 Then
MsgBox ("C360 window is not found. Please ensure C360 is open in Internet Explorer and try again")
Else
End If
'FIND THE READY STATE INDICATOR
RSIndicatorDocMarker = 0
RSIndicatorDataMarker = 0
Set RSIndicatorPage = C360Window.Document
Set RSIndicatorClass = RSIndicatorPage.getelementsbyclassname("document-statetracker")(0)
RSIndicatorCheck:
'CHECK THE READY STATE DOC STATUS
If RSIndicatorClass.getattribute("data-state-doc-status") = "ready" Then
RSIndicatorDocMarker = 1
Else: RSIndicatorDocMarker = 0
End If
'CHECK THE READY STATE
If RSIndicatorClass.getattribute("data-state-busy-status") = "none" Then
RSIndicatorDataMarker = 1
Else: RSIndicatorDataMarker = 0
End If
'Compare the RSIndicators
If RSIndicatorDataMarker = 1 And RSIndicatorDocMarker = 1 Then
Else: GoTo RSIndicatorCheck
End If
End Sub
Maybe try using OnTime instead of the tight loop you currently have:
Public Sub WaitingForRS()
Dim win As Object
Dim w As Object, el, ready As Boolean, idle As Boolean
For Each w In CreateObject("Shell.Application").Windows
If w.Name Like "*Internet*" Then
If w.Title Like "Coverage user*" Then
Set win = w
Exit For
End If
End If
Next
If Not win Is Nothing Then
Set el = win.document.getelementsbyclassname("document-statetracker")(0)
ready = (el.getattribute("data-state-doc-status") = "ready")
idle = (el.getattribute("data-state-busy-status") = "none")
If ready And idle Then
ProceedWithNextSteps win 'do whatever comes next: pass in the window
Else
'wait for a while then try again
Application.OnTime Now + TimeSerial(0, 0, 1), "WaitingForRS"
End If
Else
MsgBox "Window not found!"
End If
End Sub
Might want to add a time limit so it doesn't keep looping forever if the page isn't "ready".
Check out my answer on this post Excel VBA Submitting data via IE on an online MS Forms not working
# Idea 5 subheading. The WaitForLoadSETx() function has a manual time over-ride and double loop to help catch the state.
This was the best I could do in VBA with IE object. Ideally you want to learn Selenium, PhantomJS, or Puppeteer for actual browser manipulation. There are of course cool apps, folks have built on top of these libraries to help (puppeteer recorder chrome add in)
Or to keep things in Excel, use a simple XMLHTTPRequest object to cut the browser out of the equation, and deal solely with the request / response from the web page. This is a good alternative because it lets you focus on scraping the html content, in text form without the Javascript or waiting for page to load.

OTA - ALM 11.52 - Building Graphs through OTA

I am trying to create reports in the 'Analysis View' using OTA and HP ALM 11.52.
I've searched the OTA Reference Documentation and looked for samples online and I've found a few samples, but none seem to work.
There seem to be three methods utilised:
TDConnection.GraphBuilder.BuildGraph(GraphDefinition)
TDConnection.testFactory.BuildSummaryGraph("TS_STATUS", "TS_STATUS", "", 0, myFilter, False, False)
and a third method involving an AnalysisItemFactory object that I can't find anywhere in the OTA documentation.
I've tried the first two and they seem to run without triggering an error, however, no graph appears in ALM.
Is there a difference between these methods and which is the cleanest method?
Here are my attempts so far:
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'Method 1: GraphBuilder
'Set GB = QCConnection.GraphBuilder
'Set G1 = GB.CreateGraphDefinition(2, 0)
'G1.Property(0) = "TS_NAME"
'G1.Property(1) = "TC_STATUS"
'Set tsf = QCConnection.TestSetFactory
'Set myFilter = tsf.Filter
'myFilter.Filter ("TC_STATUS") = "Not(N/A)"
'G1.Filter = "Filter: Status[Not N/A]"
'Set g = GB.BuildGraph(G1)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'Method 2: BuildSummaryGraph
'Dim testF
'Dim graph1
'Dim Filter
'Set testF = QCConnection.testFactory
'Set myFilter = testF.Filter
'myFilter.Filter("TS_STATUS") = "Not(N/A)"
'Set graph1 = _
'testF.BuildSummaryGraph("TC_NAME", "TS_STATUS", "", 0, myFilter, False, False)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'Method 3: AnalysisItemsFactory? I can't find any documentation on this object, yet I've seen it referenced in other code samples.
'Set aiFolderFact = QCConnection.AnalysisItemFolderFactory
'Set aiFact = QCConnection.AnalysisItemFactory ~~~ This line actually runs fine so I know it at least exists. But I am definitely not using the proper methods below.
'Set G1 = aiFact.AddItem("")
'G1.Field("AI_PARENT_ID") = 1001 'Public
'G1.Field("AI_TYPE") = "Graph"
'G1.Field("AI_SUB_TYPE") = "Progress Graph"
'G1.Field("AI_OWNER") = qcUserName.Value
'G1.Field("AI_MODULE") = "requirement"
'G1.Field("AI_NAME") = "test graph"
'G1.Post
As I mentioned previously, all of these scripts run error free, but I see no graph in the Analysis View. I've also noticed that there seem to be no "Name" or "Path" fields.
I've taken a look at the tables, and there seems to be 'Analysis_Item_Folder' and 'Analysis Items' tables so It's know it's possible to do this through the OTA client. Is there an AnalysisItemFactory and could someone please kindly provide a sample script of what I'm looking for?
I was able to generate a report with the help of this HP ALM forum entry.
As in the forum mentioned it is not an official documented feature of HP ALM. Therefore it can be that in the future it won't work without replacement. Please keep that in mind.
In case the forum entry may get deleted I copied the answer by a user called "delarosa62" here (date of copy 2015/9/8):
Hi MichaelMotes and the rest of the community members.
I developed a VBA code to generate dashboard standard reports automatically. I get the "successful exception" you have mentioned. However my report does not get generated in my hard disk.
I don't get any errors.
I have adapted your Visial Basic Code to VBA using OTA. I have the otareport 1.0 Type Library and otaxml type lib registered in the tools/reference option in the VBA module window.
I am pasting my code below hoping you guys can give me some insight on this. I am not getting any errors. Just the exception which includes a successful completion message.
Sub externalSTDReports()
Dim reqFact
Dim reqFilter
Dim reqList
Dim gTDConn As Object
Set gTDConn = CreateObject("TDApiOle80.TDConnection")
'QC Connection data
login_id = ActiveWorkbook.Sheets("CONFIG").Cells(9, 3).value
login_passwd = ActiveWorkbook.Sheets("CONFIG").Cells(10, 3).value
domain_name = ActiveWorkbook.Sheets("CONFIG").Cells(11, 3).value
project_name = ActiveWorkbook.Sheets("CONFIG").Cells(12, 3).value
server_name = ActiveWorkbook.Sheets("CONFIG").Cells(13, 3).value
gTDConn.InitConnectionEx server_name
gTDConn.login login_id, login_passwd
gTDConn.Connect domain_name, project_name
Set Rep = New OTAREPORTLib.Reporter
Call Rep.SetConnection(gTDConn, 0) ' This line doesn´t return errors. But I don´t know if it is correct
Set RepConf = Rep.ReportConfig
Rep.File = "C:\Users\cris\AppData\Local\Temp\TD_80\4c223b57\Reports\std.html"
Rep.Template = "C:\Users\cris\AppData\Local\Temp\TD_80\4c223b57\Reports\default.xsl"
'******************************************************** filter Reports
Set aiFact = gTDConn.AnalysisItemFolderFactory
Set reportFact = gTDConn.AnalysisItemFactory
Set aiFilter = aiFact.Filter
Set aiList = aiFilter.NewList
Set anf = reportFact.Filter
Dim FilterStr As String
For Each ai In anf.NewList
reportName = ai.Name
reportID = ai.id
If reportName = "tmp" Then
FilterStr = ai.Field("AI_FILTER_DATA")
RepConf.Filter = FilterStr
On Error Resume Next
'i is empty. Don´t know why
i = Rep.Generate(0, 0) MsgBox i & " --- " & Rep.File Debug.Print Rep.File '-------------------- Exit For
End If
Next
Set gTDConn = Nothing
Set aiFact = Nothing
Set reportFact = Nothing
Set aiFilter = Nothing
Set aiList = Nothing Set anf = Nothing
Set RepConfig = Nothing
Set Rep = Nothing
MsgBox "END "
End Sub 'Pls HELP!!
Graphs can be generated under analysis folder, its a bit of a process because you need a sound understanding of the database, XML and OTA API. There is no direct API available for building graphs, I have created the code samples below
https://github.com/sumeet-kushwah/ALM_OTA_Wrapper/blob/master/ALM_Wrapper/Analysis.cs
Check the following functions
CreateDefectAgeGraph
CreateExcelReport
CreateDefectSummaryGraph
CreateSummaryGraph
These functions are called from the tests available below
https://github.com/sumeet-kushwah/ALM_OTA_Wrapper/blob/master/ALM_Wrapper_Tests/ALM_Wrapper_Test.cs
Look for test function
Test_AnalysisAndDashboardScripts
If you have any questions regarding the process, please let me know.

Connect to a specific lotus notes database/documen via vba

I can connect to lotus notes via the following code. So basically I connect to the database: CLASTNAME/O=TEST/C=US.nsf
Set oSession = CreateObject("Notes.NotesSession")
Server = oSession.GetEnvironmentString("MailServer", True)
UserName = oSession.UserName
MailDbName = Left$(UserName, 1) & Right$(UserName, (Len(UserName) - InStr(1, UserName, " "))) & ".nsf"
'Open the mail database in notes
Set db = oSession.GETDATABASE("", MailDbName)
Set view = db.GetView("Customers") //ERROR
However, I want to connect to a specific database, which I have. Here is the document link:
<NDL>
<REPLICA C1451C8A:00575D55>
<VIEW OD3B89A25B:7D1FR7SA-OM4923732F:011L111C>
<NOTE OFAAAA64WE:GH1Q0W0W-IUZ0987MNB:2222F4LÖ>
<HINT>CN=ZZZUSDMS09/O=ZZZ/C=US</HINT>
<REM>Database 'UserName', View 'Customers', Document 'AG: A list of all company customers, Jannuary 9, 2009'</REM>
</NDL>
This is what I tried:
Sub notesBB()
'Const DATABASE = ""
Dim r As Integer
Dim i As Integer
Dim db As Object
Dim view As Object
Dim Entry As Object
Dim nav As Object
Dim oSession As Object 'The notes session
Dim nam As Object ' notes username
Dim v() As Variant ' to hold the subtotal values
Dim bills(12, 16) ' 12 months, 16 departments
r = 1
Worksheets(1).Range("A1:Z99").Clear
'##############################
'Start a session to notes
Set oSession = CreateObject("Notes.NotesSession")
Server = oSession.GetEnvironmentString("MailServer", True)
UserName = "CN=ZZZUSDMS09/O=ZZZ/C=US" 'oSession.UserName
CustomerDbName = "CZZZUSDMS09/O=ZZZ/C=US" & ".nsf"
'Open the mail database in notes
Set db = oSession.GETDATABASE("", CustomerDbName)
Set view = db.GetView("OD3B89A25B:7D1FR7SA-OM4923732F:011L111C")
view.AutoUpdate = True // here I get an error
Set nav = view.CreateViewNav
Set Entry = nav.GetFirst
Do Until Entry Is Nothing
If Entry.isCategory Then
r = r + 1
v = Entry.ColumnValues
For i = 1 To 16
bills(v(0), i) = v(4 + i)
Cells(4 + r, 2 + i) = bills(v(0), i)
Next
End If
Set Entry = nav.getNextCategory(Entry)
DoEvents
Loop
End Sub
However, as you can see I get an error hee: view.AutoUpdate = True // here I get an error
How to connect to this database via vba?
I really appreciate your answer!
Well, this doesn't look right:
CustomerDbName = "CZZZUSDMS09/O=ZZZ/C=US" & ".nsf"
'Open the mail database in notes
Set db = oSession.GETDATABASE("", CustomerDbName)
You're just appending ".nsf" to the user's fully disntinguished name in canonical form, and that would be an extremely unusual naming convention for databases on a server. Also, earlier in your code you retrieved the server name, but here you're specifying "" for the server name instead of using what you had retrieved, so the result is that the code will try to open the database on the local machine.
The NDL file is giving you the ReplicaID of the database here:
<REPLICA C1451C8A:00575D55>
You can use the OpenByReplicalID method instead:
repID = "C1451C8A00575D55" ' note that the : is removed
'Open the database by replica id
set db = new NotesDatabase("","")
If db.OpenByReplicaID( , "85255FA900747B84" ) Then
Print( db.Title & " was successfully opened" )
Else
Print( "Unable to open database" )
End If
The next problem, though, is that the NDL file is giving the view's UNID, not its name
<VIEW OD3B89A25B:7D1FR7SA-OM4923732F:011L111C>
There is no call in the Notes COM API that can retrieve a view by its UNID; you need the name for that. But do you really need to get the view? The NDL gives you the UNID of the document, here:
<NOTE OFAAAA64WE:GH1Q0W0W-IUZ0987MNB:2222F4LÖ>
So if your goal is to just get the specific document specified in the NDL, you can use a call to GetDocumentByUNID. Note, however that the actual UNID consists of only the 17 chars to the right of OF in the above tag, minus the : char. So your code would look like this:
unid = "AAAA64WEGH1Q0W0W" ' see note below!!
Set doc = db.GetDocumentByUnid(unid)
BTW, that UNID does not look legal. The chars should be hex, and the W, Q, and G are not. I'm going on the assumption that you (or someone) deliberately obfuscated the data in your NDL file.
If you do need to access the view, the NotesNoteCollection class may provide a way to get there, but it will not be trivial.
Finally, you may find this code from Stepehn Wissel helpful.
Specify the name of the view instead:
Set view = db.GetView("Customer")
Nice to see my code getting recycled ! it seems to me that you aren't using the correct database and view names. Are they not
Database 'UserName', and View 'Customers' ? (from your link). Whatever - when your XL VBA crashes out, in the Debug - Locals window, look for the object(s) that you have tried to instantiate with the SET command. If they show up as "nothing", you've got the SET command(s) wrong and trying to use the failed object (view) then crashes on the following line.
If your servername is "Yoda" I'd guess you need
Set db = session.getdatabase("Yoda", "Username.nsf")
Set view = db.GetView("Customers")

QTP: HTML Tag is recognized, but click function does not have response

In QTP11, I have a function as below to handle a drop-down list button.
The HTML structure is as follows:
Sub FindDropdown(text)
' get page and text as parameter
counter = 0
Set oDesc = Description.Create()
oDesc("html tag").Value = "tr"
Set trContent = Browser("Change Management - SAP").Page("Change Management - SAP").ChildObjects(oDesc)
TRSum = trContent.Count()
For i = 0 To TRSum - 1
Set objPage = trContent(i).Object
Set objTag = objPage.GetElementsByTagName("td")
spanSum = objTag.Length - 1
For intCtr = 0 to spanSum
strLink = objTag(intCtr).InnerText
If strLink = text Then
trContent(i).Object.click()
End If
Next
Next
Set oDesc=nothing
End Sub
While I have tested, and the with inner element Select All could be recognized, I could not perform an action (like click), and in fact, the code:
trContent(i).Object.click()
seems having no effect.
Does this have anything to do with the listener/event handler place? such as the listener is not is the TR or TD element?
First validate if you have a click listener attached to it or not. It is needed.
You can try the below code
trContent(i).Object.FireEvent("onclick")
I tried totally four method to trigger the click event listener:
1. QTP recognization:
Just Using the TO contained in Object Repositery, and .click to fire the click listener. No Response;
2. Using the SendKeys method:
It works in this Action, while when I called the Action from my main action, it does not work;
3. Using DOM call:
Just as the scripts above in the question, I could not fire the click handler;
Finally, I turned to the devicereplay. The idea get the element's run time position, and click on that position. This is somehow low level function, and it runs smoothly for my part.
Here is my working script:
Set objReleaseTR = Browser("Change Management - SAP").Page("Change Management - SAP_3").WebElement("Release all Transport")
Set objDeviceReplay = CreateObject("mercury.devicereplay")
x = objReleaseTR.GetROProperty("abs_x")
y = objReleaseTR.GetROProperty("abs_y")
objDeviceReplay.MouseClick x + 5, y + 5, 0
Set objDeviceReplay = nothing
Hope this could be helpful for guys that encounter the same problem.

how to refer back to created browser instance

I create a browser like so, and manually navigate to the web page I need to be. I intend to automatically pull certain elements once I get to the page I need to be on via a seperate macro
Sub Test()
Set CAS = New SHDocVw.InternetExplorer ' create a browser
CAS.Visible = True ' make it visible
CAS.navigate "http://intraneturl"
Do Until CAS.readyState = 4
DoEvents
Loop
This works fine, then I do
Public Sub Gather
Set HTMLDoc2 = CAS.document.frames("top").document
Call Timer1
With HTMLDoc2
.getElementById("tab4").FirstChild.Click
End With
Call Timer2
Dim fir, las, add1, add2, cit, stat, zi As String
Dim First As Variant
Dim Last As Variant
Dim addr1 As Variant
Dim addr2 As Variant
Dim city As Variant
Dim Thisstate As Variant
Dim Zip As Variant
Call Timer2
Set HTMLDoc = CAS.document.frames("MainFrame").document
Call Timer2
With HTMLDoc
First = .getElementsByName("IndFirst")
Last = .getElementsByName("IndLast")
addr1 = .getElementsByName("txtAdd_Line1")
addr2 = .getElementsByName("txtAdd_Line2")
city = .getElementsByName("txtAdd_City")
Thisstate = .getElementsByName("cmb_Add_State")
Zip = .getElementsByName("txtAdd_Zip")
End With
fir = First.Value
las = Last.Value
add1 = addr1.Value
add2 = addr2.Value
cit = city.Value
stat = Thisstate.Value
zi = Zip.Value
'navigate back to start page
With HTMLDoc2
.getElementById("tab3").FirstChild.Click
End With
End Sub
This works the first time, but after the first time, I get "Object variable or with block variable not set" when trying to run the gather() sub again, on a different web page that contains similar information. Any Ideas as to what im doing wrong?
"The error "object variable or with block variable not set" occurs on: Set HTMLDoc2 = CAS.document.frames("top").document the second time i try running Gather()."
This is probably one of three things:
CAS is no longer an object
To check this, set a breakpoint on the line, press ctr+G in the VBA Editor and type ?CAS Is Nothing in the Immediate Window; the result should be False; if it is True CAS is no longer an object
Like Daniel Dusek suggested, make sure CAS.document.frames("top") is an actual element on the page.
To check this, open the webpage you are trying to script, press F12 in Internet Explorer, click on the arrow in the toolbar and click on the "top" frame element in the webpage, switch back to the Developer Tool and look at the line highlighted. Make sure the frame element is named "top".
The HTML hasn't fully loaded when you try to reference the frame element. Set a longer delay or a loop.
i.e. (untested):
Do Until HtmlDoc2 Is Nothing = false
Set HTMLDoc2 = CAS.document.frames("top").document
Loop
Maybe the more important question is why manually navigate to another page? Can't you automate that part of your process too?