Find element using parent/sibling in python selenium - python

I am using selenium in python to scrape a web page designed with angular js and hence has no robust identifiers for elements such as id etc. I am completely reliant on CSS selectors (which are dynamic) and xpaths.
I have the following code -
<div class="dpm-form-row ng-star-inserted">
<dpm-input-number class="flex-6">
<dpm-input-label>
<label>Fixed Rate</label>
</dpm-input-label>
<dpm-input-number-bare>
<input size="1" type="text" placeholder="" class="ng-pristine ng-valid ng-touched">
</dpm-input-number-bare>
</dpm-input-number>
<div class="flex-6">
</div>
</div>
It's basically a label called "Fixed Rate" followed by an input text box. Its that box I am trying to grab.
I have managed to get the label using the following code, I am having trouble using the parent/sibling logic to get the box -
element = driver.find_element_by_xpath('//*[contains(text(),"Fixed Rate")]')

Repost from your preceding question. Original solution :
//input[#class="ng-pristine ng-valid ng-touched"][../preceding-sibling::dpm-input-label[1]/label[.="Fixed Rate"]]
3 XPath using following-sibling axis :
//dpm-input-label[label[.="Fixed Rate"]]/following-sibling::dpm-input-number-bare[1]/input
//dpm-input-label[label[contains(.,"Fixed Rate")]]/following-sibling::dpm-input-number-bare[1]/input
//dpm-input-label[contains(.,"Fixed Rate")]/following-sibling::dpm-input-number-bare[1]/input
3 XPath using preceding-sibling axis and multiple contains for the input element :
//input[contains(#class,"ng-pristine") and contains(#class,"ng-valid") and contains(#class,"ng-touched")][../preceding-sibling::dpm-input-label[1]/label[.="Fixed Rate"]]
//input[contains(#class,"ng-pristine") and contains(#class,"ng-valid") and contains(#class,"ng-touched")][../preceding-sibling::dpm-input-label[1]/label[contains(.,"Fixed Rate")]]
//input[contains(#class,"ng-pristine") and contains(#class,"ng-valid") and contains(#class,"ng-touched")][../preceding-sibling::dpm-input-label[1][contains(.,"Fixed Rate")]]
4 XPath using preceding axis :
//input[#class="ng-pristine ng-valid ng-touched"][preceding::label[1][.="Fixed Rate"]]
//input[#class="ng-pristine ng-valid ng-touched"][preceding::label[1][contains(.,"Fixed Rate")]]
//input[contains(#class,"ng-pristine") and contains(#class,"ng-valid") and contains(#class,"ng-touched")][preceding::label[1][.="Fixed Rate"]]
//input[contains(#class,"ng-pristine") and contains(#class,"ng-valid") and contains(#class,"ng-touched")][preceding::label[1][contains(.,"Fixed Rate")]]

Related

Cannot input date to field with shadow-root (user-agent) with Selenium Python

I am trying to write a stable code to input a date into calendar input field.
Native Selenium inputs work only locally, but when I run this code in CICD I see that numbers are sometime inputted into wrong places.
For example, the timestamp is 2022-07-22 00:56:42.
Expected input by user is 220720220056.
By experimenting I found out that year works if there are two zeros in front of the year. Therefore, what I actually send is 22070020220056
input_field = driver.find_element(By.XPATH, "//div[#role='dialog']//input[#type='datetime-local']")
input_field.click()
input_field.clear()
input_field.send_keys(22070020220056)
<input _ngcontent-pau-c188="" clrinput="" type="datetime-local" name="expiry" aria-describedby="clr-form-control-9-helper" id="clr-form-control-9" class="clr-input ng-pristine ng-valid ng-touched">
#shadow-root (user-agent) == $0
<div pseudo="-webkit-datetime-edit" id="date-time-edit" datetimeformat="dd/MM/y, HH:mm">
<div pseudo="-webkit-datetime-edit-fields-wrapper">
<span role="spinbutton" aria-placeholder="dd" aria-valuemin="1" aria-valuemax="31" aria-label="Day" pseudo="-webkit-datetime-edit-day-field" aria-valuenow="25" aria-valuetext="25">25</span>
<div pseudo="-webkit-datetime-edit-text">/</div>
<span role="spinbutton" aria-placeholder="mm" aria-valuemin="1" aria-valuemax="12" aria-label="Month" pseudo="-webkit-datetime-edit-month-field" aria-valuenow="7" aria-valuetext="07">07</span>
<div pseudo="-webkit-datetime-edit-text">/</div>
<span role="spinbutton" aria-placeholder="yyyy" aria-valuemin="1" aria-valuemax="275760" aria-label="Year" pseudo="-webkit-datetime-edit-year-field" aria-valuenow="2022" aria-valuetext="2022">2022</span>
<div pseudo="-webkit-datetime-edit-text">, </div>
<span role="spinbutton" aria-placeholder="--" aria-valuemin="0" aria-valuemax="23" aria-label="Hours" pseudo="-webkit-datetime-edit-hour-field" aria-valuenow="0" aria-valuetext="00">00</span>
<div pseudo="-webkit-datetime-edit-text">:</div>
<span role="spinbutton" aria-placeholder="--" aria-valuemin="0" aria-valuemax="59" aria-label="Minutes" pseudo="-webkit-datetime-edit-minute-field" aria-valuenow="1" aria-valuetext="01">01</span>
</div>
</div>
</input>
Field looks like this:
I tried few other options to make the test more stable.
Option 1.
Tried to set the value directly.
driver.execute_script(f"arguments[0].setAttribute('value', '{my_timestamp}')", input_field)
Option 2
Tried to access shadow root:
date_field = driver.execute_script("return document.querySelector('input[type=datetime-local]').shadowRoot.getElementById('#date-time-edit')")
date_field.send_keys(my_timestamp)
Option 3
Tried to get parent element with execute_script instead of Selenium and send value:
date_field = driver.execute_script("return document.querySelector('input[type=datetime-local]')")
date_field.send_keys(my_timestamp)
Option 4
Tried to send text with a small delay before each letter:
for letter in timestamp:
time.sleep(0.1)
print(f"Sending letter {letter}")
date_field.send_keys(letter)
None of these methods worked on CICD, Only initial one and Option 3 always work locally.
I think the only stable solution would be the use of Javascipt code, but still not sure.
Any suggestion would be greatly appreciated.
Ah yes, the shadow root. For me, what works is something like the following:
shadowRootElement = driver.execute_script('''return document.querySelector("selector for shadowRoot's parent").shadowRoot''')
shadowRootChild = driver.execute_script('''return arguments[0].querySelector("selector for shadowRoot's parent").shadowRoot''', shadowRootElement)
shadowRootChild.find_element(By.CSS_SELECTOR, 'input[type=datetime-local]').send_keys("This is a test");
The <input> looks perfectly out of #shadow-root (user-agent) ambit.
<input _ngcontent-pau-c188="" clrinput="" type="datetime-local" name="expiry" aria-describedby="clr-form-control-9-helper" id="clr-form-control-9" class="clr-input ng-pristine ng-valid ng-touched">
#shadow-root (user-agent) == $0
<div pseudo="-webkit-datetime-edit" id="date-time-edit" datetimeformat="dd/MM/y, HH:mm">
<div pseudo="-webkit-datetime-edit-fields-wrapper">
.
.
.
</div>
</div>
</input>
However, the desired element is a Angular element, so ideally to send a character sequence to the element you need to induce WebDriverWait for the element_to_be_clickable() and you can use the following locator strategy:
Using XPATH:
input_field = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[#class='clr-input ng-pristine ng-valid ng-touched' and #name='expiry'][#type='datetime-local']")))
driver.execute_script(f"arguments[0].setAttribute('value', '{my_timestamp}')", input_field)
Note: You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

How do you change element attributes using Python?

I was wondering how to use python to change an element in a page's HTML code:
Specifically, from:
<input _ngcontent-mcp-c552="" type="number" name="bpm" placeholder="0" min="0" max="999" step="0.01" class="ng-valid ng-dirty ng-touched">
to
<input _ngcontent-mcp-c552="" type="number" name="bpm" placeholder="0" min="0" max="999" step="1" class="ng-valid ng-dirty ng-touched">
(Changing the step-size value)
The HTML I'm attempting to edit would not be of my own HTML file, but a public website. As such, the change would only be temporary; but I'm okay with that. Any help would be greatly appreciated. Thank you.
CONTEXT: Using automation, I'm trying to input a value (a number) into a textbox; but for some reason the send_keys function from selenium isn't sending any keys. So, I found that I could just select the element and hold the up arrow key until I attain the value I'd like. Problem is, the element's current step-size of 0.01 makes attaining the values I want (varying between 60-180) take very long. And now that's the problem I'm trying to sort out now.
To change the attribute from:
step="0.01"
to:
step="1"
you need to use setAttribute() as follows:
my_element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[#class='ng-valid ng-dirty ng-touched' and #name='bpm'][#type='number']")))
driver.execute_script("arguments[0].setAttribute('step','1')", my_element)

Message - "unknown error: cannot focus element" - in python selenium driver

<div id="ccodeinput">
<input class="dropdownheader2" type="TEXT" name="CCODE" value="435435" size="14" maxlength="22">
</div>
This is the code for the Search field(see screenshot - upper right side). I am unable to pass values from a list to this search bar. But I am able to press the search button beside it.
How can I enter values in the search field?
This is what I have tried:
inputElement = chrome_driver.find_element_by_id('ccodeinput')
inputElement.send_keys(435435)
As per the HTML you have shared the <input> tag is a child of <div> tag, so to put text within the intended element you can use :
chrome_driver.find_element_by_xpath("//input[#class='dropdownheader2' and #name='CCODE']").send_keys("435435")

Getting text from angular element with no text value

I've bumped into a text field in a simple table where the text I want to check/extract is not part of any value in the element.
Using Python and Selenium I was trying to get the value by using get_value, get_text or get_attribute but with no success. Everything returns
AttributeError: 'WebElement' object has no attribute 'get_text'
which is not surprising as there is no real value in the element. It's an AngularJS based website.
The element looks like this and the text inside is "France":
<span class="keyword-autocomplete-container" model="flavor.subregion" keyword-autocomplete="subregion" filter="{ancestorId: flavor.regionId || flavor.countryId}" with-aliases="true" replace-alias="true"> <input ng-model="localModel" ng-disabled="ngDisabled" mass-autocomplete-item="createOptions()" ng-keypress="onKeyPressed($event)" class="ng-pristine ng-valid ng-touched" autocomplete="off"></span>
with this children:
<input ng-model="localModel" ng-disabled="ngDisabled" mass- autocomplete-item="createOptions()" ng-keypress="onKeyPressed($event)" class="ng-pristine ng-valid ng-touched" autocomplete="off">
The selector I have pointed to that element works fine. I can type some text or click it with no issues. Anyone can help how to read a text from such element? Thank you.

Setting the value of an <input> dropdown in Selenium with Python

I have a HTML page containing a form with an tag. I want to set the value of the drop down in this tag using Selenium.
This is how I retrieve the input element:
driver.find_element_by_xpath("/html/body/div[2]/div/div/form/div/div[1]/div[3]/div[1]/div/div[1]/input")
I tried to set the value using select_month.send_keys("09") but this is not accepted by the web page when I try to submit the form so I need to find another method.
EDIT: Here is the HTML of the form, I have ensured that it is the right element in my x-path:
<input autocomplete="off" tabindex="-1" class="ui-select-search ui-select-toggle ng-pristine ng-valid ng-touched" ng-click="$select.toggle($event)" placeholder="Select month" ng-model="$select.search" ng-hide="!$select.searchEnabled || ($select.selected && !$select.open)" ng-disabled="$select.disabled" type="text">
After messing around a bit and incorporating the better practice presented by alecxe, this solution worked...
driver.find_element_by_xpath("//input[#placeholder='Select month']").click()
driver.find_element_by_xpath("//*[contains(text(), '09')]").click()

Categories

Resources