Selenium VBA - Runtime Error 11 ElementyNotVisible Error element not interactable - vba

I am using the below mentioned code for automation of Edge Browser
The code is working fine except for "If then Else" block.
The complete script is as follows
Dim Obj As New WebDriver
' search for company - SF 1cr and above
Sub EdgeAutoSF1CRA0()
Set Obj = New Selenium.EdgeDriver
Dim ele As WebElement
Dim By As New Selenium.By
Obj.SetCapability "ms:edgeOptions", "{""excludeSwitches"":[""enable-automation""]}"
Obj.Start "edge", ""
Obj.Get "https://*********************"
Obj.Window.Maximize
Obj.FindElementByName("croreAccount").SendKeys ("Search")
Obj.FindElementByXPath("//*[#id='loadSuitFiledDataSearchAction']/div[1]/div[3]/div[4]/img").Click
Obj.FindElementById("borrowerName").SendKeys (ThisWorkbook.Sheets("Sheet1").Range("C5").Value)
Obj.FindElementByXPath("//*[#id='search-button']/ul/li[1]/div/input").Click
Obj.Wait 30000
If Obj.FindElementByCss("#downloadReport").Attribute("Style" = "display") = "none" Then
Obj.FindElementByXPath("//*[#id='three-icons']/ul/li[3]/a/div").Click
Else
Obj.FindElementByXPath("//*[#id='downloadReport']/div").Click
End If
End Sub
In the If then Else statement I want to search for the style attribute of the id "downloadReport" for "display :none"
The code on website is < a href="downloadStatusReport" id="downloadReport" style="display: none;"><div class="download-icon">Download</div></a>
However, code always evaluate the statement as False and proceeds to execute the command "Obj.FindElementByXPath("//*[#id='downloadReport']/div").Click"

The attribute name is style, not capitalized Style.
You can construct the locator so that the desired style value will be a part of locator itself.
You can use FindElements instead of FindElement so it will return you a list of matching elements so as if such element found it will return a non-empty list, otherwise it would be an empty list. With this you can check if returned list is empty or not, as following:
If Not IsEmpty(Obj.FindElementsByCss("[id='downloadReport'][style*='none']")) Then
Obj.FindElementByXPath("//*[#id='three-icons']/ul/li[3]/a/div").Click
Else
Obj.FindElementByXPath("//*[#id='downloadReport']/div").Click
End If

In short, there is no merit in locating an element with style attribute set as display: none;. Even through you locate the element, you won't be able to click on it. So the If-Then-Else logic won't work as intended.
Instead find the desired element which is visible and enabled element, so you can invoke click on it.

Related

How to find element by style?

Element:
<div class="rsl-MeetingHeader_RaceName " style="">Moe</div>
Attempt at code:
from selenium import webdriver
chromeOptions = webdriver.ChromeOptions()
chromedriver = r"C:\Users\\Downloads\Python\chromedriver_win32\chromedriver.exe"
driver = webdriver.Chrome(executable_path=r"C:\Users\\Downloads\Python\chromedriver_win32\chromedriver.exe",chrome_options=chromeOptions)
driver.get("https://www.bet365.com.au/#/AS/B2/")
track_button = driver.findElement(By.xpath("//div[#class='rsl-MeetingHeader_RaceName' and style='Moe']"
track_button.click()
track_button.click()
^
SyntaxError: invalid syntax
Keep getting a syntax error
You are missing the closing parens in this line
track_button = driver.findElement(By.xpath("//div[#class='rsl-MeetingHeader_RaceName' and style='Moe']"))
The next issue is going to be that the element is not located. Your XPath is looking for an element with style='Moe' but your element has style=""... the contained text is 'Moe'.
The class name in your element also contains a space at the end so you will need to use
#class='rsl-MeetingHeader_RaceName '
^
You can either look for the empty style like
track_button = driver.findElement(By.xpath("//div[#class='rsl-MeetingHeader_RaceName '][#style='']"))
or ignore style and look for contained text like
track_button = driver.findElement(By.xpath("//div[#class='rsl-MeetingHeader_RaceName '][text()='Moe']"))
If that still doesn't work, it's possible the page is still loading so you will need to add a WebDriverWait and wait until the element is clickable, then click it.
track_button = new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(By.xpath("//div[#class='rsl-MeetingHeader_RaceName '][text()='Moe']"));
Below are some of the suggestions:
If the style is blank i.e. "", then you can use the below XPath:
//div[#style='']
If the style is not blank, then you can use the below XPath:
//div[#style='height: 0px;'][#class='fixed-nav-element-offset']
As per the code/HTML shared by you. You can use the below XPath as well:
//div[#class='rsl-MeetingHeader_RaceName ' and contains(text(),'Moe')]
OR
//div[#class='rsl-MeetingHeader_RaceName ' and contains(text(),'Moe')][#style='']

selenium element.click() not working (doesn't click)

String selector = ".rmcAlertDialog .buttons :first-child";
RemoteWebElement selection = (RemoteWebElement) driver.findElement(By.cssSelector(selector));
WebDriverWait wait = new WebDriverWait(driver, 60);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(selection));
if (element == selection) selection.click();
But the element in question (a button) is not responding to the click.
If I click the button manually it works so its not the web page at fault, but the automation.
I have verified the button is there by comparing it's text content.
updated for clarification
This code works (or worked) for most buttons. The code is from a script interpreter which is parsing:-
select ".rmcAlertDialog .buttons :first-child" click
This code was working prior to more recent versions of chrome/selenium/chromedriver.
The code now doesn't work for some buttons.
selection.click() IS being called (verified in a debugger), as element will always equal selection, it just is not working.
.buttons is the class name of the container div for the button(s)
The selector is not directing to the element with button class. You have a space between .button and :first-child in the selector. Remove the space. The given selector is searching for a child element of the tag with button class. But I'm assuming you are trying to click on the first element with button class not the child node of the button class element.
Use this:
String selector = ".rmcAlertDialog .buttons:first-child";
I think the main reason it's failing is because your if statement will never be true. I've never done any comparisons like this but you can simplify your code significantly and still get the desired effect.
A few suggestions:
Don't define locators as Strings, define them as Bys. The By class is defined for just such a task and makes using and passing them around MUCH easier.
String selector = ".rmcAlertDialog .buttons:first-child";
would turn into
By locator = By.cssSelector(".rmcAlertDialog .buttons:first-child");
Note the correction that S Ahmed pointed out in his answer.
You don't need to find the element to wait for it to be clickable. There is an overload that takes a By locator, use that instead.
RemoteWebElement selection = (RemoteWebElement) driver.findElement(By.cssSelector(selector));
WebDriverWait wait = new WebDriverWait(driver, 60);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(selection));
becomes
WebElement element = new WebDriverWait(driver, 60).until(ExpectedConditions.elementToBeClickable(locator));
Skip the RemoteWebElement and WebElement comparison. I don't think this will work and there's no need for it anyway. Your locator will locate the same element consistently.
So your final code should look something like
By locator = By.cssSelector(".rmcAlertDialog .buttons:first-child");
new WebDriverWait(driver, 60).until(ExpectedConditions.elementToBeClickable(locator)).click();

How to send values to textboxes that doesn't have input ids in selenium webdriver? I am using phptravles.net for testing?

phptravels location text field
Have attached a image with Html code.
I couldn't able to write a xpath to recognise and sendkeys.
The text field has different xpath when it is focused and when it is not.
Its throwing me NoSuchElementFound exception.
driver.get("http://www.phptravels.net");
WebElement e = driver.findElement(By.xpath("//*[#id='select2-search']/input"));
e.click();
e.sendKeys(city);
You can create xpath using other attributes of that element like
//input[contains(#attribute,'attribute_value')]
Eg. For a input element with class attribute "textbox", xpath can be written like
//input[contains(#class,'textBox')]
I tried with below two points and it worked for me,
1) modified xpath with span text()
2) Instead of e.sendKeys(), tried actions.sendKeys() as the former throwing 'cannot focus' error. Credit to link
driver.get("http://www.phptravels.net");
WebElement e = driver.findElement(By.xpath("//span[text()='Search by Hotel or City Name']"));
/* e.click();
e.sendKeys("GOA");
*/
Actions actions = new Actions(driver);
actions.moveToElement(e);
actions.click();
actions.sendKeys("GOA");
actions.build().perform();

Cant change element value Gecko browser (v45)

Im trying to change the HTML element value in a gecko browser (v45.0.32) but it is saying invoke member is not a member of gecko element,
For Each solo As Gecko.GeckoHtmlElement In GeckoWebBrowser1.Document.text
If GeckoWebBrowser1.Document.GetElementById("order_status_id").GetAttribute("value") = "1" Then
GeckoWebBrowser1.Document.GetElementById("order_status_id").SetAttribute("value", "2")
If GeckoWebBrowser1.Document.GetElementById("notify").GetAttribute("value") = "1" Then
GeckoWebBrowser1.Document.GetElementById("notify").InvokeMember("click")
GeckoWebBrowser1.Document.GetElementById("button-history").InvokeMember("click")
End If
End If
Next
GeckoWebBrowser1.GoBack()
There is a 'Click()' method available on GeckoHtmlElement, so you can do something like below:
(cast and invoke Click();)
((GeckoHtmlElement)GeckoWebBrowser1.Document.GetElementById("notify")).Click();
Hope this helps.
There is no method called InvokeMember in GeckoHtmlElement, use Click.
The code you have specified should be called only from main thread. Use Invoke on GeckoWebBrowser's parent, if necessary.
To make sure, that one of your elements has an attribute, first check if this element exists in current document, like this:
Dim element = GeckoWebBrowser1.Document.GetElementById("")
If element IsNot Nothing AndAlso element.GetAttribute("value") = "1" Then
element.SetAttribute("value", "2")
End If

Using VBA, how do I input a value to a <input> text box on a webpage that uses Knockout JS?

I'm trying to populate a form on a webpage that uses Knockout JS. When I insert a value into a particular text box it appears to have taken the value but when I submit the form it retains it's previous value. With a bit of digging around I've discovered that Knockout JS uses data-binding to update the underlying value. When I insert a value into the input text box the Knockout JS is not being triggered and therefore not updating the underlying value. This post is for something similar but with a check box and dropdown box rather than a text box. But I can't figure out how to get it working with a text box.
The actual website I'm trying update requires a login, however I think this page on the Knockout JS Site http://knockoutjs.com/examples/helloWorld.html will suffice for testing purposes.
Basically what I need is, using VBA, to enter a FirstName and LastName in the 2 text boxes (where it currently says 'Planet' and 'Earth'). You should see the 2 input values appear immediately below where it says 'Hello Planet Earth'. Typing it manually into the text boxes works fine. But I just can't get it to work in VBA.
Here' what I have so far...
Sub KnockoutTest()
Dim IE As New InternetExplorer
Dim hDoc As HTMLDocument
IE.Visible = True
'Load webpage and loop until loading is complete.
IE.Navigate "http://knockoutjs.com/examples/helloWorld.html"
Do While (IE.Busy Or IE.ReadyState <> READYSTATE_COMPLETE)
DoEvents
Loop
Set hDoc = IE.Document
Set firstName = hDoc.getElementsByClassName("liveExample").Item(0).all.Item(1)
Set lastName = hDoc.getElementsByClassName("liveExample").Item(0).all.Item(3)
firstName.Click
firstName.Value = "Joe"
lastName.Click
lastName.Value = "Bloggs"
'Close ie object
Set IE = Nothing
Application.ScreenUpdating = True
End Sub
Anyone got any ideas on how I get the knockout events to fire?
The actual html from the text box on the page I'm trying to update is this....
<input type="text" ondrop="return false;" onpaste="return false;" data-bind="numericValue: Markup, visible: IsCellEditable(), selected: IsCellEditable(), event: { blur: blurZoneMarkup, keypress: function (data, event) { return $parent.isDecimal(data, event, hdnIsDiscount) }, keydown: function (data, event) { $(event.target).data('tabcode', event.which || event.keyCode); { return true; } } }" style="width: 90%; display: none;">
Any help or advice would be much appreciated. I've been trying to solve this for 3 days now!
Basically, you would need to handle such automated inputs as 'HTMLevents' and trigger them. An updated snippet from your original code:
With hDoc
Set evt = hDoc.createEvent("HTMLEvents")
evt.initEvent "change", True, False
firstName.Value = "Joe"
firstName.dispatchEvent evt
End With
Tested this code and works fine for the website that link that you have provided.
I have done tons of website automations with VBA and the most robust approach is to create one event, specific to each field that you would want to fill, assign a value to the field, and dispatch the event that you have created.
For further information, refer to the documentation on MSDN https://msdn.microsoft.com/en-us/ie/ff975247(v=vs.94)