seleniumbase / SeleniumBase

📊 Python's all-in-one framework for web crawling, scraping, testing, and reporting. Supports pytest. UC Mode provides stealth. Includes many tools.
https://seleniumbase.io
MIT License
5.11k stars 951 forks source link

Is there any method like is_selected() for checkbox and radio? #525

Closed arantir53 closed 4 years ago

arantir53 commented 4 years ago

I now have two problems. I need to determine whether a checkbox is selected, but I find that this method is not available in seleniumbase. what should I do? The second problem is that I find that seleniumbase cannot locate the checkbox I want to find, it always reports an error like "Element {//ux-table-body//tr[34]//span//input} was not visible after 6 seconds!" The strange thing is, if the locator I use is "//ux-table-body//tr [34]//span", this checkbox can be found, what is the reason for this? I tried it in Chrome's console, Both $x("//ux-table-body//tr[34]//span")[0].click() and $x("//ux-table- body//tr[34]//span//input")[0].click() can operate elements correctly, why is it not possible in seleniumbase? Even if I use self. js_click("//ux-table-body//tr[34]//span//input"), still didn't work.

mdmintz commented 4 years ago

@arantir53 You can probably use: self.find_element(CSS_SELECTOR).is_selected() to determine if a checkbox is checked. Or if an attribute of the checkbox changes while it's checked to indicate that, you can use: self.is_element_visible().

For your second question, I'd need to see the HTML in order to help. Also, try it with CSS Selectors instead of XPath.

arantir53 commented 4 years ago

I tried the self.find_element(CSS_SELECTOR).is_selected() method and it really doesn't work. Here are two html codes that use checkbox.

    <ux-checkbox _ngcontent-eay-c71="" class="ng-untouched ng-pristine ng-valid" formcontrolname="pwd_check">
        <span class="ux-checkbox">
            <span class="ux-checkbox-inner">
                ::before
            </span>
            <input class="ux-checkbox-input" name="my-checkbox" type="checkbox"/>
        </span>
        <span class="ux-checkbox-label"></span>
    </ux-checkbox>

another one:

    <ux-table-header _nghost-aqe-c48="">
        <table _ngcontent-cyg-c48="" class="table table-bordered" style="min-width: 440px;">
            <thead _ngcontent-cyg-c48="">
                <tr _ngcontent-cyg-c48="" class="header-bg-color">
                    <!-- -->
                    <th _ngcontent -cyg-c48="" class="ng-star-inserted" scope="col" style="width: 32px">
                        <!-- -->
                    <label _ngcontent-cyg-c48="" class="ng-star-inserted" uxcheckbox="">
                        <span class="ux-checkbox">
                            <span class="ux-checkbox-inner">
                                ::after
                            </span>
                            <input class="ux-checkbox-input" name="my-checkbox" type="checkbox" />
                            </span>
                        <span class="ux-checkbox-label"></span>
                    </label>
                    </th>
                    <!-- -->
                    <!-- -->
                    <!-- -->
                </tr>
            </thead>
        </table>
    </ux-table-header>

I want to know why the selectorself.find_element(""ux-checkbox[formcontrolname='pwd_check']>span>input"").is_selected() and self.find_element("//ux-table-header//thead//span//input").is_selected() doesn't work. The error message is “ Element {//ux-table-header//thead//span//input} was not visible after 6 seconds!”.
My pycharm is not auto-completed when I enter .is_selected(). Is there more than one option to import? The imported package is a bit problematic? This is just my guess

mdmintz commented 4 years ago

@arantir53 Is there a public-facing web page where I can test out a solution? Also, looks like there's a problem with your first selector, as you have double double-quotes inside the find_element. And I'm not sure about your second selector either. I'd use CSS Selectors instead of XPath. The CSS Selector for the input would be input[name="my-checkbox"].

arantir53 commented 4 years ago

Obvious, error message is "Element {input[name='my-checkbox']} was not visible after 10 seconds!". The first I misused double double-quotes, but in the code, it was right.
The reason why I use such a complex selector is because we are a large project. I used the POM design pattern. If only the input tag is used for positioning, when the first HTML code and the second HTML code are on the same page. The selector input[name = "my-checkbox"] is difficult to telling the different. In addition, I'm accessing my computer remotely, and the contents are encrypted, so I may need to ask our programmers if you can access it from the outside.

If I want to determine the selection status of the checkbox, can it be achieved by the JavaScript self.execute_script () code? If so, how?

mdmintz commented 4 years ago

@arantir53 Here's an example of that:

from seleniumbase import BaseCase

class MyTestClass(BaseCase):

    def test_checkboxes(self):
        self.open("https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_checkbox")
        self.switch_to_frame("iframeResult")
        css_selector = "input#vehicle1"
        script = ("""return document.querySelector('%s').checked;""" % css_selector)
        checked = self.execute_script(script)
        self.assert_false(checked)
        self.click(css_selector)
        checked = self.execute_script(script)
        self.assert_true(checked)

(Assuming your CSS Selectors are unique, this should work in your case too.)

arantir53 commented 4 years ago

Despite some clunkiness, it was works fine. In addition, how to solve it if it is xpath selector? I tried using script = ("""return $x('%s').checked;""" % xpath_selector), but it didn't work. Error message:"Message: javascript error: $x is not defined." After all, some table positioning is more convenient with xpath expressions, and css expressions are a bit cumbersome to locate the specific tr tag among 20 same tags.

mdmintz commented 4 years ago

@arantir53 querySelector is a Javascript command, and only works with CSS Selectors, but I can create a method within SeleniumBase that automatically converts XPath to CSS Selectors before passing it into that method. I'll do that and add the translations to https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/translate Once the new is_checked(SELECTOR) method is created, I'll update the translations and create a new SeleniumBase release.

mdmintz commented 4 years ago

@arantir53 I also noticed that is_selected() works just fine instead of using the Javascript: Here's a working example:

from seleniumbase import BaseCase

class MyTestClass(BaseCase):

    def test_checkboxes(self):
        self.open("https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_checkbox")
        self.switch_to_frame("iframeResult")
        css_selector = "input#vehicle1"
        checked = self.find_element(css_selector).is_selected()
        self.assert_false(checked)
        self.click(css_selector)
        checked = self.find_element(css_selector).is_selected()
        self.assert_true(checked)

Does your XPath not work for that if you use that instead of CSS in your example?

mdmintz commented 4 years ago

And here's a 3rd way:

checked = self.find_element(SELECTOR).get_attribute("checked")
mdmintz commented 4 years ago

Details here: http://allselenium.info/working-with-checkboxes-using-python-selenium-webdriver/

mdmintz commented 4 years ago

@arantir53 See https://github.com/seleniumbase/SeleniumBase/releases/tag/v1.36.8

self.select_if_unselected(selector, by=By.CSS_SELECTOR)

Duplicates: self.check_if_unchecked(selector, by=By.CSS_SELECTOR)

self.unselect_if_selected(selector, by=By.CSS_SELECTOR)

Duplicates: self.uncheck_if_checked(selector, by=By.CSS_SELECTOR)


And here’s the example test that goes with v1.36.8 -> https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_checkboxes.py
arantir53 commented 4 years ago

The new self.is_selected() method works perfectly. The reason I don't use the find_element.is_selected () method is because I can't use it, I can only use "ux-checkbox [formcontrolname='pwd_check']>span" and "//ux-table-header//thead//span" for checkbox selection. I can't use input tags to make selections. This may be a web design issue. Anyway, I'm fine.