Wait until specific value appears in selenium excel vba - excel

I have an element with this html
<span id="ContentPlaceHolder1_Label2" designtimedragdrop="1319" style="display:inline-block;color:Firebrick;font-size:Medium;font-weight:bold;width:510px;"></span>
and after clicking Save button on the page this part changes to that
<span id="ContentPlaceHolder1_Label2" designtimedragdrop="1319" style="display:inline-block;color:Firebrick;font-size:Medium;font-weight:bold;width:510px;">تم حفظ التعديل بنجاح</span>
You would notice this value تم حفظ التعديل بنجاح ..
After that I should click another button but the problem appears when the internet connection is slow. I got the other button clicked before saving
How can I wait for the appearance of the value تم حفظ التعديل بنجاح and then after the appearance of that text >> move to the another button
Thanks advanced for help

I have tried this solution and it worked well for me
This was with the help of Ziggus' suggestion
Do Until .FindElementById("ContentPlaceHolder1_Label2").Text = "تم حفظ التعديل بنجاح"
Application.Wait Now() + TimeValue("00:00:01")
Loop

I would re-write this as you risk an infinite loop. Make it a timed loop and add in a DoEvents.
Dim result As String, testElement As Object, t As Date
Const MAX_WAIT_SEC As Long = 10 '<==adjust time here
t = Timer
Do
DoEvents
On Error Resume Next
Set testElement = .FindElementById("ContentPlaceHolder1_Label2")
result = testElement.Text
If Timer - t > MAX_WAIT_SEC Then Exit Do
On Error GoTo 0
Loop While result <> "تم حفظ التعديل بنجاح"

Related

VBA submit MS Form: input value has been filled but not successfully submited

I am using VBA in excel to submit a MS form. It could fill the values to text inputs but after submit, this question is still blank. Any thoughts?
Dim emailDomain As Variant
Dim URL As String
Set IE = GetObject("new:{D5E8041D-920F-45e9-B8FB-B1DEB82C6E5E}")
IE.Visible = True
URL = "https://forms.office.com/Pages/ResponsePage.aspx?id=Aq5v9jZdW0m_4Him_5-ObrQJR7r8UGdPhwKFE494ioxUOEg4M1Q5STRGSzk5Q1VPMTJPRUNLMk5IMi4u"
IE.navigate (URL)
While IE.Busy
DoEvents
Wend
IE.document.getElementsByClassName("office-form-question-textbox office-form-textfield-input form-control office-form-theme-focus-border border-no-radius")(0).Value = "currentUserEmailAddress"
delay 5
IE.document.getElementsByClassName("button-content")(0).Click
IE.Quit
Set IE = Nothing
End Sub
Before submit, the value has been filled correctly
This <div class="button-content">Absenden</div> is not a button but the actual button starts with <button so this is what you need to look for and click on.
<button class="office-form-theme-primary-background office-form-theme-button office-form-bottom-button button-control light-background-button __submit-button__" title="Absenden" role="button">
<div class="button-content">Absenden</div>
</button>
In order to click the send button you can use something like
IE.document.getElementsByClassName("office-form-theme-primary-background office-form-theme-button office-form-bottom-button button-control light-background-button __submit-button__")(0).Click
after you have set the value of the textbox with
IE.document.getElementsByClassName("office-form-question-textbox office-form-textfield-input form-control office-form-theme-focus-border border-no-radius")(0).Value = "currentUserEmailAddress"
I met this kind of issue before. You can also refer to this thread.
The root cause of the issue is .Value can't set the value effectively. In this situation, you can simulate user input using SendKeys to set value to the input box.
Sample code:
IE.document.getElementsByClassName("office-form-question-textbox office-form-textfield-input form-control office-form-theme-focus-border border-no-radius")(0).Focus
SendKeys ("currentUserEmailAddress")

Application Wait time issue

I am stuck on this bit and not sure how to get past it. I need to be able to pause and the resume my code. I went with this idea to place a value into a cell, which the Application Wait time will use.
This bit is fine and works
Dim i As Integer
Dim Response As Integer
Sheets("Sheet3").Range("Z1").Value = "01:00:00" 'will place 1hr wait time in Sheet3 Z1
t = CStr(Range("Z1"))
Application.Wait (Now + TimeValue(t))
I then have a CommandButton2 on my userform, which will reset the wait time from 1hr wait to 2 seconds. In theory this should then resume the code from where it left off. The wait time is between the IE opening and loading and the FOR EACH loop running, so once it has resumed it would action the FOR EACH loop of the code.
Sheets("Sheet3").Range("Z1").Value = "00:00:02" `Reset time to 2 sec from 1hr
MY PROBLEM
I can not click on button2 when the wait time is running. Is there a way I could do this so that when the wait time code is running, button2 is still enabled to use?
BACKGROUND
User click commandbutton1 and code runs, IE opens and navigates to the correct page. NOW the code waits. As it gives the user time to select from several options (Too many to put in a code). Once done the user click the second command button which resets the code to 2sec and they can extract their information. In theory it should continue, however I can not click on the second button while the wait code is running. Is there a work around?
I have tried to split the code into subs, but I was having several error messages and I gave up on that method. I did look at a doEvent loop, but I am limitted in coding and was not sure how it would work and if it would continue the code OR start from the begining. As starting from the begining is not how I want to do it.
Thanks in advance.
POST EDITED
This is the code now, not 100% sure if it is correct, it now allows me to change the value of Z1 via button2 but the FOR LOOP does not run
Before
After button2 is clicked
'#################### APPLICATION WAIT ################
Dim i As Integer
Dim Response As Integer
Sheets("Sheet3").Range("Z1").Value = "01:00:00" 'will place the 1hr wait time in Sheet3 Z1
t = CStr(Range("Z1"))
'Application.Wait (Now + TimeValue(t)) 'commented out or it freezes excel
WaitUntil = Now + TimeValue(t)
Do While Now <= WaitUntil
DoEvents
Loop
'#################### APPLICATION WAIT ################
'FOR LOOP
For Each element In elements
DoEvents
```
Application.Wait() pauses the entire application, not just your code. You should be able to achieve the intended result using a loop with DoEvents:
...
WaitUntil = Now + TimeValue(t)
Do While Now <= WaitUntil
DoEvents
Loop
...
EDIT:
The DoEvents loop doesn't work because you set the value of t only once, so when your button2 changes value of Z1 to 00:00:02, the value of t is still 01:00:00. To fix this, you can reset value of t inside the loop:
...
WaitUntil = Now + TimeValue(t)
Do While Now <= WaitUntil
DoEvents
If t <> CStr(Range("Z1")) Then 'If value of Z1 changed, update t
t = CStr(Range("Z1"))
WaitUntil = Now + TimeValue(t)
End If
Loop
...
I have managed to fix the problem, should anyone ever need this in the future here is the work around. Also a very big Thankyou to Kirill Tkachenko for putting me on the right path
I done it with a DoWhile Loop. This part of the code went in between browser (IE) is fully loaded and before the FOR EACH LOOP, then this code is triggered.
THE CODE
It places a 1hr wait time in Z1, then THE DoWhile loop runs.
When Button2 is clicked The time changes to under 3sec and the sheet recalclates
column Z.
Then the if statement kicks in and as the DoWhile states the time should be more
than 3sec and Z1 now is 2sec the GOto runs next and goes to the lable I
created to tell the code to start here. That is just before the FOR
EACH loop
'#################### APPLICATION WAIT ################
Dim i As Integer
Dim Response As Integer
Sheets("Sheet3").Range("Z1").Value = "01:00:00" 'will place the 1hr wait time in Sheet3 Z1
t = CStr(Range("Z1"))
WaitUntil = Now + TimeValue(t)
Do While Now > "00:00:03"
Sheets("Sheet3").Columns("Z").Calculate
If Sheets("Sheet3").Range("Z1").Value = "00:00:02" Then
GoTo StartForLoop_Restart ' use GOTo command and label to reinitiate the sub
End If
DoEvents
Loop
'#################### APPLICATION WAIT ################
'Restart the code HERE, this is the key part
StartForLoop_Restart:
'FOR LOOP
For Each element In elements
DoEvents
This is what I put in Button2
Sheets("Sheet3").Range("Z1").Value = "00:00:02"
Sheets("Sheet3").Columns("Z").Calculate
I know its not the best code in the world and could be written better, but the workaround works and hopefully someone can do a much better job in writing it in the future.

How to select each option in dropdown list and click a button (same origin policy issue - permission denied on the second iteration)

I need a code that loops through options in a < select > object on a web page, selects each option and clicks "Show" button to show some data related to the selected option. I started with this code:
Set periodSelector = ie.document.getElementById("period")
For Each Period In periodSelector.Options
Period.Selected = True
Application.Wait (Now + TimeValue(waittime))
Next Period
It works well - the browser selects each option just fine. But when I add button.click to show the data related to the selected option, an error "Permission denied" occurs on the second selector loop (seems like it cannot use .select command anymore).
Set periodSelector = ie.document.getElementById("period")
For Each Period In periodSelector.Options
Period.Selected = True
Application.Wait (Now + TimeValue(waittime))
ie.document.getElementsByTagName("input")(17).Click
Application.Wait (Now + TimeValue(waittime))
Next Period
I guess it is due to same origin policy. Probably, when I click on the "Show" button, the page gets refreshed (although it is not really reloaded - the button uses scripts to retrieve some information and show it in a table below the button).
How can I avoid this same origin policy issue and loop through the dropdown options?
In cases like this I try a slightly different approach which is to work off the page and not a variable. You can get your number of options from an initial variable, but after that keep working off the current document which may have refreshed. Amongst other things, you want to avoid underlying stale element exceptions.
Dim periodSelector As Object, i As Long, optionsLength As Long
Set periodSelector = ie.document.querySelectorAll("#period option")
optionsLength = periodSelector.Length -1
For i = 0 to optionsLength
ie.document.querySelectorAll("#period option").item(i).Selected = True
Application.Wait Now + TimeValue(waittime)
ie.document.getElementsByTagName("input")(17).Click
Application.Wait Now + TimeValue(waittime) '<== this I would replace with proper page load wait While ie.Busy Or ie.readyState < 4: DoEvents: Wend
Next

Wait until specific elements loaded

I am trying to scrape a website and there are many elements I am dealing with. I need to wait for elements to be loaded.
This is my try till now but I am waiting a long time and sometimes I get errors.
.FindElementById("ContentPlaceHolder1_Button1").Click
.Wait 2000
GoBack1:
Set elePrint = .FindElementById("IconImg_CrystalReportViewer1_toptoolbar_print", timeout:=20000, Raise:=False)
If elePrint Is Nothing Then
Application.Wait Now() + TimeValue("00:00:01"): GoTo GoBack1
Else
elePrint.Click
End If
GoBack2:
Set eleExport = .FindElementById("theBttnbobjid_1545642213647_dialog_submitBtn", timeout:=20000, Raise:=False)
If eleExport Is Nothing Then
Application.Wait Now() + TimeValue("00:00:01"): GoTo GoBack2
Else
eleExport.Click
End If
Is there a better way for doing this?
This is the html part
<tbody><tr valign="middle"><td height="21" width="5" style="background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px 0px;"></td><td id="theBttnCenterImgbobjid_1545656314367_dialog_submitBtn" align="center" class="wizbutton" style="padding-left:3px;padding-right:3px;background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px -42px;"><nobr><a id="theBttnbobjid_1545656314367_dialog_submitBtn" href="javascript:void(0)" class="wizbutton" role="button">Export</a></nobr></td><td height="21" width="5" style="background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px -21px;"></td></tr></tbody>
You can try looping your elements until they become available.
On Error Resume Next
Do While .FindElementById("theBttnbobjid_1545642213647_dialog_submitBtn") Is Nothing
DoEvents
Loop
On Error Goto 0
I would also consider adding a timer to your loop if for some reason the the webpage hangs - which if this is the case you could reload the webpage or perform some other error handling operation.
You can shorten this to
Do
Loop While .FindElementsByCss("#theBttnbobjid_1545642213647_dialog_submitBtn").Count = 0
Though you should set a timeout
Const MAX_WAIT_SEC As Long = 10
Dim t
t = Timer
Do
If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop While .FindElementsByCss("#theBttnbobjid_1545642213647_dialog_submitBtn").Count = 0
There won't be an error and there is no need to yield control. If the item is not found the .Count will be zero.
If the id is dynamic and you have a constant substring (which only occurs for one id attribute on the page (to be safe - may not be essential) you can use ^, *, $ operators in css). For example, if the start string is constant across pages change the css selector to
[id^='theBttnbobjid']
If occurs more than once, and the index is constant across pages then use the index to interact later e.g.
.FindElementsByCss("[id^='theBttnbobjid']")(2)

Excel-IE Automation w/VBA on Dynamic Tables

Good Morning,
I’m hoping that some kind soul out there can help me with a roadblock I’ve encountered in my quest to manipulate a website with VBA. I am using MS Excel 2010 and Internet Explorer 11.0.56.
I’m somewhat comfortable with VBA but have never used it to navigate to a website, enter information and click on buttons. I’ve managed to muddle through as follows:
In Column A of my Excel spreadsheet, I have a list of 10 digit case numbers.
The code below will open IE, navigate to the desired website, pause while I log in, then navigate to the search screen, enter in the first case number and press the SEARCH button (yes, I have the case number in this example hard coded in with no looping, but that stuff I can handle so please ignore):
Sub Button_Click()
Dim objIE As Object
Set objIE = New InternetExplorerMedium
objIE.Top = 0
objIE.Left = 0
objIE.Width = 800
objIE.Height = 600
objIE.AddressBar = 0
objIE.StatusBar = 0
objIE.Toolbar = 0
objIE.Visible = True
objIE.Navigate ("https://somewebsite.com")
MsgBox ("Please log in and then press OK")
objIE.Navigate ("https://somewebsite.com/docs")
Do
DoEvents
Loop Until objIE.ReadyState = 4
objIE.Document.all("caseNumber").Value = "1234567890"
objIE.Document.getElementById("SearchButton").Click
Exit Sub
Do
DoEvents
Loop Until objIE.ReadyState = 4
MsgBox ("Done")
End Sub
That will bring me to this screen
The file number entered in the search field will return any number of files in a dynamic table with a checkbox to the left of each file.
For this example, let’s say I am ONLY concerned with the file called “CC8” under the “Type” column. There will only ever be one instance of “CC8” for a given file number.
What I need help with is, through VBA, how do I search through this table, find the “CC8” line, and then have the checkbox to the left automatically checked?
When I inspect the “CC8” element in IE, this is the HTML associated with it (highlighted in gray; the entire table is under class “listing list-view clearfix”)
see here
The HTML for the checkbox related to the “CC8” item is below:
HTML code here
The “id” for both has the same sequence of numbers, but one starts with “viewPages” and the other “doc”.
Can anyone help me out as to what I need to add to my code to get this checkbox checked? Thank you!
Note:
Please post the actual HTML using the snippet tool.
Generally:
Without HTML to properly test, I am assuming that the following 2 nodeLists are the same length, meaning that when the search text is found in aNodeList then the assumption is the same index can be used to target the corresponding checkbox in the bNodeList:
Dim aNodeList As Object, i As Long
With objIE.document
Set aNodeList = .querySelectorAll("a[target='_blank']")
Set bNodeList = .querySelectorAll("[title='Search Result: Checkbox']")
End With
For i = 0 To aNodeList.Length - 1
If aNodeList.item(i).innerText = "CC8" Then
bNodeList.item(i).Click
Exit For
End If
Next
You could also potentially use the following instead as you say the viewPages prefixes each item:
Set aNodeList = .querySelectorAll("a[id^='viewPages']")
Other observations:
Traditional checkboxes would have a checked attribute and syntax of
bNodeList.item(i).Checked = True, but as I can't see that attribute in your element I am assuming a .Click suffices.

Resources