SeleniumBase icon indicating copy to clipboard operation
SeleniumBase copied to clipboard

how method chaining works in seleniumbase?

Open anilreddy opened this issue 3 years ago • 6 comments

Hi, I am using seleniumbase to automate our applications and it is very useful but i came across a scenario where i need to chain elements to click on element.

Here how to use method chaining or is there any solution for these kind of scenarios

# in GetModifyPage has this element: //span[contains(@id,'getTrainScheduleGrid')]/descendant::table[2]/tbody/tr/td[7]/span/span[not(text()='...')]
def add_etc_at_location(self):
        etcs = self.find_elements(GetModifyPage.check_etc)
        for i in range(len(etcs) - 1):
            if (Utils.get_difference(etcs[i].text, etcs[i+1].text) >= 12):
                etcs[i+1].find_element('preceding::tr[6]/td[7]/span/span').click() #its failing with InvalidArgument Error

anilreddy avatar Aug 09 '22 14:08 anilreddy

Hi @anilreddy,

Your last line, etcs[i+1].find_element('preceding::tr[6]/td[7]/span/span').click() is failing because you switched to using a non-SeleniumBase / raw-selenium method.

self.find_element(selector) is a SeleniumBase call. element.find_element(by, selector) is a raw Selenium call.

element.find_element(selector) will fail with InvalidArgumentException because you need to specify the by in this case.

In your case, you need to specify xpath as an argument first:

etcs[i+1].find_element('xpath', 'preceding::tr[6]/td[7]/span/span').click()

mdmintz avatar Aug 09 '22 14:08 mdmintz

Also, SeleniumBase autodetects selector types (there's a detectable difference between css selectors and xpath selectors). Raw selenium methods do not have this auto-detection ability.

mdmintz avatar Aug 09 '22 14:08 mdmintz

@mdmintz It is now click on the element i needed after clicking on element am getting StaleElementReferenceException at if condition above if (Utils.get_difference(etcs[i].text, etcs[i+1].text) >= 12): Is there any alternative to this

support\base_testcase.py:313: in add_etc_at_location
    if (Utils.get_difference(etcs[i].text, etcs[i+1].text) >= 12):
C:\software\Python310\lib\site-packages\selenium\webdriver\remote\webelement.py:84: in text
    return self._execute(Command.GET_ELEMENT_TEXT)['value']
C:\software\Python310\lib\site-packages\selenium\webdriver\remote\webelement.py:396: in _execute
    return self._parent.execute(command, params)
C:\software\Python310\lib\site-packages\selenium\webdriver\remote\webdriver.py:435: in execute
    self.error_handler.check_response(response)

anilreddy avatar Aug 09 '22 15:08 anilreddy

You'll get StaleElementReferenceException if one of the clicks you've chained together navigates the browser to a new page. You shouldn't have to use element.find_element() or element.click(), which are raw Selenium methods. You might be able to generate the selectors you need by using the SeleniumBase Recorder: https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/recorder_mode.md If you need to click several similar elements, but you want the clicking to stop if the browser navigates to a new page, you can use self.click_visible_elements(selector), which will click all elements sequentially that match the selector.

mdmintz avatar Aug 09 '22 15:08 mdmintz

I cannot use direct selector as I need to iterate through each element and get difference of time in hrs and if is >=12 then i need to click on that element.

anilreddy avatar Aug 09 '22 15:08 anilreddy

That can be done without calling element.find_element(). Eg:

text1 = self.get_text(xpath1)
text2 = self.get_text(xpath2)
if int(text2) - int(text1) >= 12:
    self.click(xpath2 + ' preceding::tr[6]/td[7]/span/span')

Add in the loop as needed, but you can avoid using element.find_element(by, selector).click(), which uses the raw selenium methods.

If you're still going to use element.find_element(by, selector).click(), you need to stop the clicking if there's a page load, because otherwise you'll get the StaleElementReferenceException.

mdmintz avatar Aug 09 '22 16:08 mdmintz