How to use multiple conditions in one locator? - python

One of several buttons may appear on the page, I have to wait for the first one and perform the action, with Selenium I did it this way:
element = WebDriverWait(driver, 20).until(
lambda driver: \
driver.find_element(By.NAME, "Confirm") or \
driver.find_element(By.NAME, "Edit") or \
driver.find_element(By.NAME, "Submit")
)
Is there any equivalent at playwright?

Well, I haven't figured it out how to do this natively with Playwright but in js you can use
await Promise.race([selector1, slector2, selector3])
and the test will proceed with the first one that resolves. Probably you can adopt a similar concept in python with
asyncio.wait(selectors_set, return_when=asyncio.FIRST_COMPLETED)

Related

Click multiple elements at the same time selenium Python

I am trying to click multiple elements at the same time without any delay in between.
For example, instead of 1 then 2, it should be 1 and 2.
this is the 1st element that I want to click:
WebDriverWait(driver, 1).until(EC.element_to_be_clickable(
(By.XPATH,
"//div[contains(#class, 'item-button')]//div[contains(#class, 'button-game')]"))).click()
this is the 2nd elements that I want to click:
(run the first line then second line)
WebDriverWait(driver, 1).until(
EC.frame_to_be_available_and_switch_to_it((By.XPATH, "/html/body/div[4]/div[4]/iframe")))
WebDriverWait(driver, 1.4).until(EC.element_to_be_clickable(
(By.XPATH, "//*[#id='rc-imageselect']/div[3]/div[2]/div[1]/div[1]/div[4]"))).click()
Basically, Click 1st element and 2nd elements' first line then second line.
I have tried this, but did not work:
from threading import Thread
def func1():
WebDriverWait(driver, 1).until(EC.element_to_be_clickable(
(By.XPATH,
"//div[contains(#class, 'item-button')]//div[contains(#class, 'button-game')]"))).click()
def func2():
WebDriverWait(driver, 1).until(
EC.frame_to_be_available_and_switch_to_it((By.XPATH, "/html/body/div[4]/div[4]/iframe")))
WebDriverWait(driver, 1.4).until(EC.element_to_be_clickable(
(By.XPATH, "//*[#id='rc-imageselect']/div[3]/div[2]/div[1]/div[1]/div[4]"))).click()
if __name__ == '__main__':
Thread(target = func1).start()
Thread(target = func2).start()
Use case: I am trying to automate a website and I need to be fast. Sometimes, element1 is not showing, the website shows element2 instead and vice versa. If I did not check element1 and element2 at the same time, I will be late. The code above starts function1 before function2 and not at the same time. Thank you
You can make it simultaneous if you use jQuery:
click_script = """jQuery('%s').click();""" % css_selector
driver.execute_script(click_script)
That's going to click all elements that match that selector at the same time, assuming you can find a common selector between all the elements that you want to click. You may also need to escape quotes before feeding that selector into the execute_script. And you may need to load jQuery if it wasn't already loaded. If there's no common selector between the elements that you want to click simultaneously, then you can use javascript to set a common attribute between them.
You can also try it with JS execution, which might be fast enough:
script = (
"""var simulateClick = function (elem) {
var evt = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
var canceled = !elem.dispatchEvent(evt);
};
var $elements = document.querySelectorAll('%s');
var index = 0, length = $elements.length;
for(; index < length; index++){
simulateClick($elements[index]);}"""
% css_selector
)
driver.execute_script(script)
As before, you'll need to escape quotes and special characters first.
Using import re; re.escape(STRING) can be used for that.
All of this will be made easier if you use the Selenium Python framework: SeleniumBase, which has build-in methods for simultaneous clicking:
self.js_click_all(selector)
self.jquery_click_all(selector)
And each of those above will automatically escape quotes of selectors before running driver.execute_script(SCRIPT), and will also load jQuery if it wasn't already loaded on the current page. If the elements above didn't already have a common selector, you can use self.set_attribute(selector, attribute, value) in order to create a common one before running one of the simultaneous click methods.
Simultaneous clicking is possible with selenium ActionChains class.
Reference:
https://github.com/SeleniumHQ/selenium/blob/64447d4b03f6986337d1ca8d8b6476653570bcc1/py/selenium/webdriver/common/actions/pointer_input.py#L24
And here is code example, in which 2 clicks will be performed on 2 different elements at the same time:
from selenium.webdriver.common.actions.mouse_button import MouseButton
from selenium.webdriver.common.action_chains import ActionChains
b1 = driver.find_element(By.ID, 'SomeButtonId')
b2 = driver.find_element(By.ID, 'btnHintId')
location1 = b1.rect
location2 = b2.rect
actions = ActionChains(driver)
actions.w3c_actions.devices = []
new_input = actions.w3c_actions.add_pointer_input('touch', 'finger1')
new_input.create_pointer_move(x=location1['x'] + 1, y=location1['y'] + 2)
new_input.create_pointer_down(MouseButton.LEFT)
new_input.create_pointer_up(MouseButton.LEFT)
new_input2 = actions.w3c_actions.add_pointer_input('touch', 'finger2')
new_input2.create_pointer_move(x=location2['x'] + 1, y=location2['y'] + 2)
new_input2.create_pointer_down(MouseButton.LEFT)
new_input2.create_pointer_up(MouseButton.LEFT)
actions.perform()
You can use ActionChains to perform multiple actions almost immediately.
actions = ActionChains(driver)
actions.move_to_element(element1).click()
actions.move_to_element(element2).click()
actions.perform()
Depending on the use case, you could also use click_and_hold which is also available using ActionChains.

Instagram Following Selenium Python

I am trying to figure out how to get Selenium to click the following section of an Instagram account, here is the html for it.
<a class=" _81NM2" href="/peacocktv/following/" tabindex="0"><span class="g47SY lOXF2">219</span> following</a>
When using instagram, the direct /username/following link does not actually bring up the following list, you would have to click the field manually on the website for it to appear.
log_in = WebDriverWait(driver, 8).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[type = 'submit']"))).click()
not_now = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, '//button[contains(text(), "Not Now")]')))
not_now.click()
'''not_now2 = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, '//button[contains(text(), "Not Now")]')))
not_now2.click()'''
search_select = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//input[#placeholder = 'Search']")))
search_select.clear()
'''search_select.send_keys("russwest44", Keys.ENTER)
search_select.send_keys(Keys.ENTER)'''
# Clicks who the person is following:
following = driver.find_elements_by_xpath("//button[contains(text(),'following')]")
following.click()
Which is the best way to come up with the solution
Seems like couple of issues with your current code:
# Clicks who the person is following:
following = driver.find_elements_by_xpath("//button[contains(text(),'following')]")
following.click()
if you pay attention, you've shared HTML with a tag, so your xpath should be
//a[contains(text(),'following')]
not
//button[contains(text(),'following')]
also, you are using find_elements_by_xpath it would return a list of web elements. you can not directly trigger .click on it.
either switch to find_element or
following[0].click()

How to click the icon using selenium

I am trying to automate my certain activities using selenium. I was launching a webpage and a security windows popup appears for the login credentials.
I have automated that using Autoit. Now after login I need to click on the option and I have tried it based on the find_element_by_text and find_element_by_id.
I was getting an error as Unable to find the element with CSS selector and I have seen some other post in the StackOverflow with the same issue but I could not able to fix it by myself.
Here is how my HTML looks like. Could you please guide me on this and also please share any document for further checking. Thanks.
driver = webdriver.Ie(r"C:\\IEDriverServer\\IEDriverServer.exe")
driver.get('URL of the page')
#driver.implicitly_wait(10) # seconds
autoit.win_wait("Windows Security")
# now make that crap active so we can do stuff to it
autoit.win_activate("Windows Security")
# type in the username
autoit.send('username')
# tab to the password field
autoit.send("{TAB}")
# type in the password
autoit.send('password')
# kill!
autoit.send("{ENTER}")
driver.maximize_window()
driver.implicitly_wait(120) # seconds
#submit_button_incidents = driver.find_element_by_link_text("3-Normal Incidents")
submit_button_incidents= driver.find_element_by_id("nodeImgs13pm")
submit_button_incidents.click()
driver.implicitly_wait(10)
++ updating the info
I have tried to copy the whole HTML but the page was restricted so I cant able to view the full HTML page other than the basic templates. Adding some more screenshots of the developer tools.
Here how my webpage looks like.
try with this code :
submit_button_incidents = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//span[contains(text(),'My Group'])")))
submit_button_incidents.click()
and do not use implicit wait too many times in code.
Implicit wait is set for life time of web driver instance.
Reference :
Selenium official document for wiats
Xpath tutorial
cssSelector tutorial
UPDATE :
As OP has shared the HTML code. As per the requirement you can go ahead with this code.
As there are two elements with text as My Groups's queue.
For first one you can write XPATH as :
//a[#class='scbdtext']/span[contains(text(),'My Group')]
Code:
submit_button_incidents = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='scbdtext']/span[contains(text(),'My Group')]")))
submit_button_incidents.click()
For second one you can write XPATH as :
//a[#class='scbdtextfoc']/span[contains(text(),'My Group')]
Code :
submit_button_incidents = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//a[#class='scbdtextfoc']/span[contains(text(),'My Group')]")))
submit_button_incidents.click()
Hope this will help.
Use ActionChains with double click for this to work in Python.
from selenium.webdriver import ActionChains
# Get the element however you want
element = driver.find_element_by_xpath("//a[#class='scbdtextfoc']/span[contains(text(),'My Group')]")
ActionChains(driver).double_click(settings_icon).perform()

Not able to click a button in Selenium (Booking.com)

I am programming a python scraper with help of Selenium. The first few steps are:
goes on booking.com, insert a city name, selects the first date and then tries to open the check-out calendar.
Here is where my problem occurs. I am not able to click the check-out calendar button (The important are of the website).
I tried to click every element regarding to the to check-out calendar (The elements of check-out calendar) with element.click(). I also tried the method
element = self.browser.find_element_by_xpath('(//div[contains(#class,"checkout-field")]//button[#aria-label="Open calendar"])[1]') self.browser.execute_script("arguments[0].click();", element)
It either does nothing (in case of execute.script() and click() on div elements) or it throws following exception when directly clicking the button:
Element <button class="sb-date-field__icon sb-date-field__icon-btn bk-svg-wrapper"
type="button"> is not clickable at point (367.5,316.29998779296875)
because another element <div class="sb-date-field__display"> obscures it
Here is a short code to test it:
browser = webdriver.Firefox()
browser.get("https://www.booking.com/")
wait = WebDriverWait(browser, 5)
element = wait.until(EC.presence_of_element_located((
By.XPATH, '(//div[contains(#class,"checkout-field")]//button[#aria-label="Open calendar"])[1]')))
element = wait.until(EC.element_to_be_clickable((
By.XPATH, '(//div[contains(#class,"checkout-field")]//button[#aria-label="Open calendar"])[1]')))
element.click()
I have a temporarily solution for my problem but I am not satisfied with it.
element = browser.find_element_by_xpath('(//div[contains(#class,"checkout-field")]//button[#aria-label="Open calendar"])[1]')
hov = ActionChains(browser).move_to_element(element)
hov.click().perform()
This will open the calendar by hovering over the object and clicking it. This strangely opens the calendar.
The methods mentioned above still don't work.
Define clicka as an xpath. Now use executescript to click the element.
driver.execute_script("arguments[0].click();", clicka)
I'm not 100% sure that I got everything you posted, because the layout is a bit messy.
However, I tried to test the issue with both Selenium Java and Firefox Scratchpad (a Web Developer tool that allows to run JavaScript scripts) and it worked perfectly - the button was clickable on both of them.
If you're interested in further testing using this tool, this is the code I've used:
In JavaScript:
function getElementByXpath(path) {
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
var myElement = getElementByXpath('(//div[contains(#class,"checkout-field")]//button[#aria-label="Open calendar"])[1]')
myElement.click()
and in Java:
FirefoxDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 10);
driver.navigate().to("https://www.booking.com");
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("(//div[contains(#class,'checkout-field')]//button[#aria-label='Open calendar'])[1]")));
driver.findElement(By.xpath("(//div[contains(#class,'checkout-field')]//button[#aria-label='Open calendar'])[1]")).click();
System.out.println("success");
if your are having the control check out button on all the web site managing with explicit wait required lots of codding you can use implicit wait below is in the java.
System.setProperty("webdriver.chrome.driver",
"G:\\TopsAssignment\\SampleJavaExample\\lib\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

Python3 Selenium: Wait for a Page Load in New Window

I've looked all over and found some very nice solutions to wait for page load and window load (and yes I know there are a lot of Stack questions regarding them in isolation). However, there doesn't seem to be a way to combine these two waits effectively.
Taking my primary inspiration from the end of this post, I came up with these two functions to be used in a "with" statement:
#This is within a class where the browser is at self.driver
#contextmanager
def waitForLoad(self, timeout=60):
oldPage = self.driver.find_element_by_tag_name('html')
yield
WebDriverWait(self.driver, timeout).until(staleness_of(oldPage))
#contextmanager
def waitForWindow(self, timeout=60):
oldHandles = self.driver.window_handles
yield
WebDriverWait(self.driver, timeout).until(
lambda driver: len(oldHandles) != len(self.driver.window_handles)
)
#example
button = self.driver.find_element_by_xpath(xpath)
if button:
with self.waitForPage():
button.click()
These work great in isolation. However, they can't be combined due to the way each one checks their own internal conditions. For example, this code will fail to wait until the second page has loaded because the act of switching windows causes "oldPage" to become stale:
#contextmanager
def waitForWindow(self, timeout=60):
with self.waitForLoad(timeout):
oldHandles = self.driver.window_handles
yield
WebDriverWait(self.driver, timeout).until(
lambda driver: len(oldHandles) != len(self.driver.window_handles)
)
self.driver.switch_to_window(self.driver.window_handles[-1])
Is there some Selenium method that will allow these to work together?

Categories

Resources