Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python ExpectedCondition "Or" "And" "Not" #7121

Closed
GQAssurance opened this issue Apr 20, 2019 · 9 comments
Closed

Python ExpectedCondition "Or" "And" "Not" #7121

GQAssurance opened this issue Apr 20, 2019 · 9 comments
Labels

Comments

@GQAssurance
Copy link
Contributor

🚀 Feature Proposal

Copy the ExpectedConditions "Or", "And", and "Not" from the Java library to the Python library.

Motivation

  • "Or" will simplify logic & speed up execution in tests when we need only 1 of several possible expected conditions to appear.
  • "And" may simplify logic in tests where we need all of several conditions to appear.
  • "Not" may increase code readability

Example

e.g. "Or": During A/B testing of a website, it is unknown which of 2 elements will be served every page load. The "Or" expected condition can ensure the test is unblocked as soon as either of them displays.

e.g. "Or": A user may or may not be shown a splash screen before the login-screen. Using "Or", a single test can begin execution as soon as it confirms that either a splash element or a login element is visible.

e.g. "And": A test may wish to confirm the visibility of multiple elements before continuing. (This functionality can be easily synthesized in test code, but using the "And" expected condition might create clearer code.)

e.g. "Not": A test may wish to wait for an element to disappear before continuing.
(This functionality can be easily synthesized in test code, AND it's often better to use the inverse ExpectedCondition, but using the "Not" expected condition might make clearer code.)

@GQAssurance
Copy link
Contributor Author

I could probably write & submit this PR; but I'd like guidance on whether such a change would be approved by the owners

@isaulv
Copy link
Contributor

isaulv commented Apr 22, 2019

Python is a far more flexible language to write compared to Java, so I am not really on board with this. That being said if you do the work with tests, we can approve it since not everyone has the same mental model of working with code.

Remember you can always write a complex condition as a function. Here's an example.

def elements_are_in_stable_condition(element1, element2):
    def wait_for_condition(driver):
        attribute = driver.find_element(*element1).get_attribute('my_condition')
        if attribute:
            return True
        else:
            return driver.find_element(*element2).is_enabled()
    return wait_for_condition
WebDriverWait(driver, timeout).until(elements_are_in_stable_condition(element_a, element_b))

@GQAssurance
Copy link
Contributor Author

My thinking for suggesting at least the "OR" version was:

  1. Increase parity with the Java code (personally, i was frustrated to discover that tutorials etc. use java-specific selenium functions not ported to Python)
  2. Allow reusability for mix-n-matching expected conditions of any quantity & type (e.g., some places you test for visibility, others for clickability, etc.)

I don't know if #1 is at all a guiding principal for the project, which is why I asked :)
#2 is obviously useful for my cases and probably others which is why I think the java side has it implemented.

Only peripherally related: I don't recognize attribute 'my_condition' in your example above. Is this a foo-style example?

Of course if I were to submit a PR, I'd add some tests, which is why I am testing the water first.

@isaulv
Copy link
Contributor

isaulv commented Apr 22, 2019

My example code is just a foo style example. By writing a closure, you can define all kinds of logic.

  1. Although the Python API tries to follow the Java type hierarchy in terms of commands, it doesn't follow it 100% due to the fact Python is not statically typed, and doesn't require work arounds to deal with Java's limited typing system. Originally the Python bindings did not even have canned expected conditions. I had to write them so that there is parity with the Java code, but the Java binding maintainers wish they never wrote expected conditions in the first place, to allow the end user to figure out the best conditions. However, Java back then didn't have lambda functions, so they had to write some canned expected conditions to make it easier for Java novices to plug in these conditions. Expected conditions are nice boilerplate for people to quickly have something to use.

  2. My only concern is how easy it is to debug an error with a construct that uses a fluent API. Also the until method and not until method has a pretty simple interface for checking an action, which means updating it or writing a fancy wrapper.

@GQAssurance
Copy link
Contributor Author

Well Java DID implement it, so until they deprecate it, they have to live with their choices :)

However, if the guiding voices say ExpectedConditions are in the sunset stage, and we should encourage individual python testers to roll their own conditional handling, then I'm not one to argue.!

@isaulv
Copy link
Contributor

isaulv commented Apr 23, 2019

I don't want to discourage you from doing the work. If anything if you do it and it seems to have a good enough API then we can refine it.

@GQAssurance
Copy link
Contributor Author

GQAssurance commented Apr 23, 2019

This was the class I made to implement 'OR'. (Ironically, we refactored and I don't actually need it)

If you guys think it adds value to the project, I'd be happy to flesh it into a proper PR & submit it:

class expected_or(object):
    """ perform a logical 'OR' check on multiple expected conditions """
    def __init__(self, *args):
        self.expected_conditions = args

    def __call__(self, driver):
        for expected_condition in self.expected_conditions:
            try:
                result = expected_condition(driver)
                if result:
                    return result
            except NoSuchElementException:
                pass

In particular, I was toying with the idea of having the call raise an informative NoSuchElementException (e.g., "None of the X conditions became true") when all conditions were false; this would be elegantly consumed by the Wait.

@GQAssurance GQAssurance reopened this Apr 23, 2019
@GQAssurance
Copy link
Contributor Author

def elements_are_in_stable_condition(element1, element2):
    def wait_for_condition(driver):
        attribute = driver.find_element(*element1).get_attribute('my_condition')
        if attribute:
            return True
        else:
            return driver.find_element(*element2).is_enabled()
    return wait_for_condition
WebDriverWait(driver, timeout).until(elements_are_in_stable_condition(element_a, element_b))

I actually expect this example to fail; the until method requires a callable object. (Passing a function will likely return E TypeError: 'bool' object is not callable exception.)

@p0deje p0deje added the C-py label May 7, 2019
GQAssurance added a commit to GQAssurance/selenium that referenced this issue Jul 29, 2019
Replicates the functionality of Java's AND / OR / NOT expected conditions.
Because of reserved word constraints in Python, these are named:

OR:  `any_of(*expected_condition)`
AND: `all_of(*expected_condition)`
NOT: `none_of(*expected_condition)`

Each function takes an unlimited number of expected_conditions as arguments.

Implements SeleniumHQ#7121
AutomatedTester pushed a commit that referenced this issue Aug 28, 2019
Replicates the functionality of Java's AND / OR / NOT expected conditions.
Because of reserved word constraints in Python, these are named:

OR:  `any_of(*expected_condition)`
AND: `all_of(*expected_condition)`
NOT: `none_of(*expected_condition)`

Each function takes an unlimited number of expected_conditions as arguments.

Implements #7121
jimevans pushed a commit to jimevans/selenium that referenced this issue Sep 8, 2019
Replicates the functionality of Java's AND / OR / NOT expected conditions.
Because of reserved word constraints in Python, these are named:

OR:  `any_of(*expected_condition)`
AND: `all_of(*expected_condition)`
NOT: `none_of(*expected_condition)`

Each function takes an unlimited number of expected_conditions as arguments.

Implements SeleniumHQ#7121
jimevans pushed a commit to jimevans/selenium that referenced this issue Sep 8, 2019
Replicates the functionality of Java's AND / OR / NOT expected conditions.
Because of reserved word constraints in Python, these are named:

OR:  `any_of(*expected_condition)`
AND: `all_of(*expected_condition)`
NOT: `none_of(*expected_condition)`

Each function takes an unlimited number of expected_conditions as arguments.

Implements SeleniumHQ#7121
@lock
Copy link

lock bot commented Sep 27, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked and limited conversation to collaborators Sep 27, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants