How can I click on the <li class> element to give a star rating? - python-3.x

I want to give a random star rating in selenium the HTML looks like the following
<li class="wf-rate__star ng-star-inserted">
<mat-icon role="img"
class="mat-icon notranslate
text-3xl material-icons mat-icon-no-color"
aria-hidden="true" data-mat-icon-type="font">
star_outline
</mat-icon>
</li>
This is how one unselected star looks like
And this is the python code i tried to randomly click on one star
options = driver.find_elements_by_xpath(
"//li[#class='wf-rate__star ng-star-inserted']")
option = random.choice(options)
option.click()
How can i achieve this?

Related

SEO help: Lists do not contain only <li> elements and script supporting elements (<script> and <template>)

Page Speed Insights is giving me the following error message for Accessibility:
Lists do not contain only li elements and script supporting elements (script and template)
Below are the code snippets that are generating this message. I'm not quite sure what needs to be done here to stop generating the errors, any advice would be much appreciated!
<ul class="slide_tit swiper-pagination-clickable swiper-pagination-bullets">
<li class="swiper-pagination-bullet" tabindex="0" role="button" aria-label="Go to slide 1">
<li class="swiper-pagination-bullet" tabindex="0" role="button" aria-label="Go to slide 2">
<li class="swiper-pagination-bullet" tabindex="0" role="button" aria-label="Go to slide 3">

How to identify the multiple button elements having same text using xpath in the below HTML code?

'''
<span class="Text xyz med">234</span>
<span>
<button class="btn-link" type="button">Hide</button>
</span>
<span class="Text xyz med">2456</span>
<span>
<button class="btn-link" type="button">Hide</button>
</span>
My code:
numbers=driver.find_element_by_xpath('//button[text()="Hide"]'
Clicks on the "Hide" for the first element having the text "234"
Can anyone suggest if there an any option to loop through this hide links using xpath.
The only unique identifier here is the numeric value.
You can try using index like this:
driver.find_element_by_xpath('(.//button[text()="Hide"])[1]') - to select 234
driver.find_element_by_xpath('(.//button[text()="Hide"])[2]') - to select 2456

How to hover then click a button in selenium?

I have the following portion of html code in a web page
<div class="action">
<div class="double-button">
<button class="widget-button" title="2 people liked this post">2</button>
<button class="widget-button like" title="like this post">
<svg class="fa d-icon d-icon-d-unliked svg-icon svg-node" aria-hidden="true">
<use xlink:href="#far-heart"></use>
</svg>
</button>
</div>
</div>
After hovering over the first button the class name change and the code transform to
<div class="action">
<div class="double-button">
<button class="widget-button d-hover" title="2 people liked this post">2</button>
<button class="widget-button like" title="like this post">
<svg class="fa d-icon d-icon-d-unliked svg-icon svg-node" aria-hidden="true">
<use xlink:href="#far-heart"></use>
</svg>
</button>
</div>
</div>
After clicking over the first button a new div appear that contains people who like the post
<div class="action">
<div class="double-button">
<button class="widget-button d-hover" title="2 people liked this post">2</button>
<button class="widget-button like" title="like this post">
<svg class="fa d-icon d-icon-d-unliked svg-icon svg-node" aria-hidden="true">
<use xlink:href="#far-heart"></use>
</svg>
</button>
</div>
<div class="likes">
<a class="trigger-user" href="/USER1" name="USER1">USER1</a>
<a class="trigger-user" href="/USER2" name="USER2">USER2</a>
</div>
</div>
My objective is to select all those users using selenium and python3 so I tried the following code inspired from other stack overflow questions like link1 and link2
driver = webdriver.Chrome(executable_path='./chromedriver_83') #this works fine
driver.get("link_to_the_page") #also I get the link and all contents without problems
likes_button=driver.find_elements_by_xpath("//button[#class='widget-button']") # works fine too
print(likes_button[0].text) # This gives '2', it the right value
hover = ActionChains(driver).move_to_element(likes_button[0]) #select only the first element in the page for testing
hover.perform() # I think the hover does not work even if this is the right way
likes_button_hover=driver.find_elements_by_xpath("//button[#class='widget-button d-hover']") # now select the hovered button to be clicked, since I hovered only one button in the whole page the result shoud be one
print(len(likes_button_hover)) # this gives 0 while it should give 1
likes_button_hover[0].click() # this throw an error
I get the following error which means the button did not change the class ( the hover did not work)
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element <button class="widget-button" title="2 people liked this post">2</button> is not clickable at point (537, 17). Other element would receive the click: <span>...</span>
(Session info: chrome=83.0.4103.116)
I tried to get into the first button in many means using for example
driver.find_elements_by_css_selector("[title*='people liked this post']")
but in vain, I think the problem occurs in the hover but I can not see why, and of course I can not get the button and click it without hovering it first.
What Am I doing wrong?
I can see two solution here.
1: As button (One you need to click to see all users) is always there but only class is changing. So you can do a direct click without hovering on it.
likes_button=driver.find_elements_by_xpath("//button[contains(#title,'people liked this post')]") # Used contains as number of people liked might change
print(likes_button[0].text) # This gives '2', it the right value
likes_button[0].click() # If not working try javaScript : driver.execute_script("arguments[0].click();", likes_button[0])
2: If you really want to click only after hovering, i guess you might need to to pause method of Actionchain class. As java script might take some time to load. In your case it is trying to find button with class widget-button d-hover immediately after hovering the mouse.
likes_button=driver.find_elements_by_xpath("//button[#class='widget-button']")
print(likes_button[0].text)
ActionChains(driver).move_to_element(likes_button[0]).pause(1).perform()
likes_button_hover=driver.find_elements_by_xpath("//button[#class='widget-button d-hover']")
print(len(likes_button_hover))
likes_button_hover[0].click()

Prestashop 1.7 attribute groups - check stock and apply css to unavailable combination

Prestashop option to hide unavailable attributes on product page doesn't work when attribute groups are used, for example color and size (for clothing shops).
I need to keep showing all possible combinations, but grey out (or strikethrough) the combinations with no stock.
Like this:
I tried several things.
In Prestashop 1.6 the following piece of code worked to apply css class (.out-of-stock-float-left) to unavailable combinations:
{elseif $group.group_type == 'radio'}
<ul id="group_{$id_attribute_group}">
{foreach from=$group.attributes key=id_attribute item=group_attribute}
{if {$group.attributes_quantity[{$id_attribute|intval}]} > 1} <!-- product in stock -->
<li class="input-container float-left">
<input class="input-radio" type="radio" data-product-attribute="{$id_attribute_group}"
name="group[{$id_attribute_group}]"
value="{$id_attribute}"{if $group_attribute.selected} checked="checked"{/if}>
<span class="radio-label">{$group_attribute.name}</span>
</li>
{else} <!-- product out of stock -->
<li class="input-container out-of-stock-float-left">
<input class="input-radio" type="radio" data-product-attribute="{$id_attribute_group}"
name="group[{$id_attribute_group}]"
value="{$id_attribute}"{if $group_attribute.selected} checked="checked"{/if}>
<span class="radio-label">{$group_attribute.name}</span>
</li>
{/if}
{/foreach}
</ul>
{/if}
When changing combinations there is an ajax request. I don't know how to grey out combinations with no stock and make them not clickable.
Thanks

How can I get texts with certain criteria in python with selenium? (texts with certain siblings)

It's really tricky one for me so I'll describe the question as detail as possible.
First, let me show you some example of html.
....
....
<div class="lawcon">
<p>
<span class="b1">
<label> No.1 </label>
</span>
</p>
<p>
"I Want to get 'No.1' label in span if the div[#class='lawcon'] has a certain <a> tags with "bb" title, and with a string of 'Law' in the text of it."
<a title="bb" class="link" onclick="javascript:blabla('12345')" href="javascript:;">Law Power</a>
</p>
</div>
<div class="lawcon">
<p>
<span class="b1">
<label> No.2 </label>
</p>
<p>
"But I don't want to get No.2 label because, although it has <a> tag with "bb" title, but it doesn't have a text of law in it"
<a title="bb" class="link" onclick="javascript:blabla('12345')" href="javascript:;">Just Power</a>
</p>
</div>
<div class="lawcon">
<p>
<span class="b1">
<label> No.3 </label>
</p>
<p>
"If there are multiple <a> tags with the right criteria in a single div, I want to get span(No.3) for each of those" <a>
<a title="bb" class="link" onclick="javascript:blabla('12345')" href="javascript:;">Lawyer</a>
<a title="bb" class="link" onclick="javascript:blabla('12345')" href="javascript:;">By the Law</a>
<a title="bb" class="link" onclick="javascript:blabla('12345')" href="javascript:;">But not this one</a>
...
...
...
So, here is the thing. I want to extract the text of (e.g. No.1) in div[#class='lawcon'] only if the div has a tag with "bb" title, with a string of 'Law' in it.
If inside of the div, if there isn't any tag with "bb" title, or string of "Law" in it, the span should not be collected.
What I tried was
div_list = [div.text for div in driver.find_elements_by_xpath('//span[following-sibling::a[#title="bb"]]')]
But the problem is, when it has multiple tag with right criteria in a single div, it only return just one div.
What I want to have is a location(: span numbers) list(or tuple) of those text of tags
So it should be like
[[No.1 - Law Power], [No.3 - Lawyer], [No.3 - By the Law]]
I'm not sure I have explained enough. Thank you for your interests and hopefully, enlighten me with your knowledge! I really appreciate it in advance.
Here is the simple python script to get your desired output.
links = driver.find_elements_by_xpath("//a[#title='bb' and contains(.,'Law')]")
linkData = []
for link in links:
currentList = []
currentList.append(link.find_element_by_xpath("./ancestor::div[#class='lawcon']//label").text + '-' + link.text)
linkData.append(currentList)
print(linkData)
Output:
[['No.1-Law Power'], ['No.3-Lawyer'], ['No.3-By the Law']]
I am not sure why you want the output in that format. I would prefer the below approach, so that you will get to know how many divs have the matching links and then you can access the links from the output based on the divs. Just a thought.
divs = driver.find_elements_by_xpath("//a[#title='bb' and contains(.,'Law')]//ancestor::div[#class='lawcon']")
linkData = []
for div in divs:
currentList = []
for link in div.find_elements_by_xpath(".//a[#title='bb' and contains(.,'Law')]"):
currentList.append(div.find_element_by_xpath(".//label").text + '-' + link.text)
linkData.append(currentList)
print(linkData)
Output:
[['No.1-Law Power'], ['No.3-Lawyer', 'No.3-By the Law']]
As your requirement is to extract the texts No.1 and so on, which are within a <label> tag, you have to induce WebDriverWait for the visibility_of_all_elements_located() and you will have only 2 matches (against your expectation of 3) and you can use the following Locator Strategy:
Using XPATH:
print([my_elem.get_attribute("innerHTML") for my_elem in WebDriverWait(driver, 5).until(EC.visibility_of_all_elements_located((By.XPATH, "//div[#class='lawcon']//a[#title='bb' and contains(.,'Law')]//preceding::label[1]")))])

Resources