diff --git a/examples/test_double_click.py b/examples/test_double_click.py index 66465659442..cace69301de 100755 --- a/examples/test_double_click.py +++ b/examples/test_double_click.py @@ -3,9 +3,18 @@ class MyTestClass(BaseCase): - def test_double_click(self): + def test_double_click_and_switch_to_frame(self): self.open("https://www.w3schools.com/jsref" "/tryit.asp?filename=tryjsref_ondblclick") - self.switch_to_frame("iframeResult") + self.ad_block() + self.switch_to_frame("#iframeResult") + self.double_click('[ondblclick="myFunction()"]') + self.assert_text("Hello World", "#demo") + + def test_double_click_and_switch_to_frame_of_element(self): + self.open("https://www.w3schools.com/jsref" + "/tryit.asp?filename=tryjsref_ondblclick") + self.ad_block() + self.switch_to_frame_of_element('[ondblclick="myFunction()"]') self.double_click('[ondblclick="myFunction()"]') self.assert_text("Hello World", "#demo") diff --git a/requirements.txt b/requirements.txt index 222f08f8be4..98c0d118259 100755 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ requests==2.22.0 selenium==3.141.0 pluggy>=0.13.1 attrs>=19.3.0 -pytest>=4.6.8;python_version<"3" +pytest>=4.6.9;python_version<"3" pytest>=5.3.2;python_version>="3" pytest-cov>=2.8.1 pytest-forked>=1.1.3 @@ -29,7 +29,7 @@ beautifulsoup4==4.8.2 atomicwrites==1.3.0 portalocker==1.5.2 cryptography==2.8 -asn1crypto==1.2.0 +asn1crypto==1.3.0 pyopenssl==19.1.0 pygments==2.5.2 colorama==0.4.3 @@ -41,4 +41,5 @@ cffi>=1.13.2 tqdm>=4.41.1 flake8==3.7.9 certifi>=2019.11.28 -pdfminer.six==20191110 +pdfminer.six==20191110;python_version<"3.5" +pdfminer.six==20200104;python_version>="3.5" diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index 649b812f286..0731f4bf253 100755 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -1061,7 +1061,7 @@ def is_element_in_an_iframe(self, selector, by=By.CSS_SELECTOR): return False def switch_to_frame_of_element(self, selector, by=By.CSS_SELECTOR): - """ Set driver control to the iframe of the element (assuming the + """ Set driver control to the iframe containing element (assuming the element is in a single-nested iframe) and returns the iframe name. If element is not in an iframe, returns None, and nothing happens. May not work if multiple iframes are nested within each other. """ @@ -1076,13 +1076,26 @@ def switch_to_frame_of_element(self, selector, by=By.CSS_SELECTOR): iframe_identifier = iframe['name'] elif iframe.has_attr('id') and len(iframe['id']) > 0: iframe_identifier = iframe['id'] + elif iframe.has_attr('class') and len(iframe['class']) > 0: + iframe_class = " ".join(iframe["class"]) + iframe_identifier = '[class="%s"]' % iframe_class else: continue - self.switch_to_frame(iframe_identifier) - if self.is_element_present(selector, by=by): - return iframe_identifier + try: + self.switch_to_frame(iframe_identifier, timeout=1) + if self.is_element_present(selector, by=by): + return iframe_identifier + except Exception: + pass self.switch_to_default_content() - return None + try: + self.switch_to_frame(selector, timeout=1) + return selector + except Exception: + if self.is_element_present(selector, by=by): + return "" + raise Exception("Could not switch to iframe containing " + "element {%s}!" % selector) def hover_on_element(self, selector, by=By.CSS_SELECTOR): selector, by = self.__recalculate_selector(selector, by) @@ -1310,7 +1323,13 @@ def maximize_window(self): self.__demo_mode_pause_if_active() def switch_to_frame(self, frame, timeout=None): - """ Sets driver control to the specified browser frame. """ + """ + Wait for an iframe to appear, and switch to it. This should be + usable as a drop-in replacement for driver.switch_to.frame(). + @Params + frame - the frame element, name, id, index, or selector + timeout - the time to wait for the alert in seconds + """ if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: diff --git a/seleniumbase/fixtures/page_actions.py b/seleniumbase/fixtures/page_actions.py index 52f359999ec..5b15e4d33a2 100755 --- a/seleniumbase/fixtures/page_actions.py +++ b/seleniumbase/fixtures/page_actions.py @@ -33,6 +33,7 @@ from selenium.webdriver.remote.errorhandler import NoSuchWindowException from seleniumbase.config import settings from seleniumbase.core import log_helper +from seleniumbase.fixtures import page_utils def is_element_present(driver, selector, by=By.CSS_SELECTOR): @@ -604,11 +605,11 @@ def wait_for_and_switch_to_alert(driver, timeout=settings.LARGE_TIMEOUT): def switch_to_frame(driver, frame, timeout=settings.SMALL_TIMEOUT): """ - Wait for an iframe to appear, and switch to it. This should be usable - as a drop-in replacement for driver.switch_to.frame(). + Wait for an iframe to appear, and switch to it. This should be + usable as a drop-in replacement for driver.switch_to.frame(). @Params driver - the webdriver object (required) - frame - the frame element, name, or index + frame - the frame element, name, id, index, or selector timeout - the time to wait for the alert in seconds """ start_ms = time.time() * 1000.0 @@ -618,6 +619,19 @@ def switch_to_frame(driver, frame, timeout=settings.SMALL_TIMEOUT): driver.switch_to.frame(frame) return True except NoSuchFrameException: + if type(frame) is str: + by = None + if page_utils.is_xpath_selector(frame): + by = By.XPATH + else: + by = By.CSS_SELECTOR + if is_element_visible(driver, frame, by=by): + try: + element = driver.find_element(by=by, value=frame) + driver.switch_to.frame(element) + return True + except Exception: + pass now_ms = time.time() * 1000.0 if now_ms >= stop_ms: break diff --git a/setup.py b/setup.py index 0a466c7bd08..37a483b2853 100755 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ setup( name='seleniumbase', - version='1.34.11', + version='1.34.12', description='Fast, Easy, and Reliable Browser Automation & Testing.', long_description=long_description, long_description_content_type='text/markdown', @@ -95,7 +95,7 @@ 'selenium==3.141.0', 'pluggy>=0.13.1', 'attrs>=19.3.0', - 'pytest>=4.6.8;python_version<"3"', # For Python 2 compatibility + 'pytest>=4.6.9;python_version<"3"', # For Python 2 compatibility 'pytest>=5.3.2;python_version>="3"', 'pytest-cov>=2.8.1', 'pytest-forked>=1.1.3', @@ -112,7 +112,7 @@ 'atomicwrites==1.3.0', 'portalocker==1.5.2', 'cryptography==2.8', - 'asn1crypto==1.2.0', + 'asn1crypto==1.3.0', 'pyopenssl==19.1.0', 'pygments>=2.5.2', 'colorama==0.4.3', @@ -124,7 +124,8 @@ 'tqdm>=4.41.1', 'flake8==3.7.9', 'certifi>=2019.11.28', - 'pdfminer.six==20191110', + 'pdfminer.six==20191110;python_version<"3.5"', + 'pdfminer.six==20200104;python_version>="3.5"', ], packages=[ 'seleniumbase',