diff --git a/dash/CHANGELOG.md b/dash/CHANGELOG.md index bfaafb1f05..444db0060c 100644 --- a/dash/CHANGELOG.md +++ b/dash/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +### Fixed + +- [#829](https://github.com/plotly/dash/issues/829) Fixes the `--remote` pytest argument which was not effective in the code, adding a new argument `--remote-url` to support the selenium grid usage in the cloud. + ## [1.2.0] - 2019-08-27 ### Added - [#860](https://github.com/plotly/dash/pull/860) Adds a new arg `dev_tools_prune_errors` to `app.run_server` and `app.enable_dev_tools`. Default `True`, tracebacks only include user code and below. Set it to `False` for the previous behavior showing all the Dash and Flask parts of the stack. diff --git a/dash/testing/browser.py b/dash/testing/browser.py index 85f3fd678c..0283845202 100644 --- a/dash/testing/browser.py +++ b/dash/testing/browser.py @@ -23,6 +23,7 @@ ) from dash.testing.dash_page import DashPageMixin from dash.testing.errors import DashAppLoadingError, BrowserError +from dash.testing.consts import SELENIUM_GRID_DEFAULT logger = logging.getLogger(__name__) @@ -32,21 +33,28 @@ class Browser(DashPageMixin): def __init__( self, browser, + remote=False, + remote_url=None, headless=False, options=None, - remote=None, download_path=None, percy_finalize=True, wait_timeout=10, ): self._browser = browser.lower() + self._remote_url = remote_url + self._remote = ( + True + if remote_url and remote_url != SELENIUM_GRID_DEFAULT + else remote + ) self._headless = headless self._options = options self._download_path = download_path self._wait_timeout = wait_timeout self._percy_finalize = percy_finalize - self._driver = until(lambda: self.get_webdriver(remote), timeout=1) + self._driver = until(self.get_webdriver, timeout=1) self._driver.implicitly_wait(2) self._wd_wait = WebDriverWait(self.driver, wait_timeout) @@ -285,21 +293,11 @@ def open_new_tab(self, url=None): ) ) - def get_webdriver(self, remote): + def get_webdriver(self): try: - return ( - getattr(self, "_get_{}".format(self._browser))() - if remote is None - else webdriver.Remote( - command_executor=remote, - desired_capabilities=getattr( - DesiredCapabilities, self._browser.upper() - ), - ) - ) + return getattr(self, "_get_{}".format(self._browser))() except WebDriverException: logger.exception("<<>>") - return None def _get_wd_options(self): options = ( @@ -333,8 +331,16 @@ def _get_chrome(self): }, ) - chrome = webdriver.Chrome( - options=options, desired_capabilities=capabilities + chrome = ( + webdriver.Remote( + command_executor=self._remote_url, + options=options, + desired_capabilities=capabilities, + ) + if self._remote + else webdriver.Chrome( + options=options, desired_capabilities=capabilities + ) ) # https://bugs.chromium.org/p/chromium/issues/detail?id=696481 @@ -372,8 +378,16 @@ def _get_firefox(self): "browser.helperApps.neverAsk.saveToDisk", "application/octet-stream", # this MIME is generic for binary ) - return webdriver.Firefox( - firefox_profile=fp, options=options, capabilities=capabilities + return ( + webdriver.Remote( + command_executor=self._remote_url, + options=options, + desired_capabilities=capabilities, + ) + if self._remote + else webdriver.Firefox( + firefox_profile=fp, options=options, capabilities=capabilities + ) ) @staticmethod diff --git a/dash/testing/consts.py b/dash/testing/consts.py new file mode 100644 index 0000000000..ea62a2971b --- /dev/null +++ b/dash/testing/consts.py @@ -0,0 +1 @@ +SELENIUM_GRID_DEFAULT = "http://localhost:4444/wd/hub" diff --git a/dash/testing/plugin.py b/dash/testing/plugin.py index b6b8d48f10..847928b7bc 100644 --- a/dash/testing/plugin.py +++ b/dash/testing/plugin.py @@ -1,5 +1,7 @@ # pylint: disable=missing-docstring,redefined-outer-name import warnings +from .consts import SELENIUM_GRID_DEFAULT + try: import pytest @@ -14,12 +16,10 @@ except ImportError: warnings.warn("run `pip install dash[testing]` if you need dash.testing") -WEBDRIVERS = {"Chrome", "Firefox", "Remote"} +WEBDRIVERS = {"Chrome", "Firefox"} def pytest_addoption(parser): - # Add options to the pytest parser, either on the commandline or ini - # TODO add more options for the selenium driver. dash = parser.getgroup("Dash", "Dash Integration Tests") dash.addoption( @@ -29,6 +29,19 @@ def pytest_addoption(parser): help="Name of the selenium driver to use", ) + dash.addoption( + "--remote", + action="store_true", + help="instruct pytest to use selenium grid", + ) + + dash.addoption( + "--remote-url", + action="store", + default=SELENIUM_GRID_DEFAULT, + help="set a different selenium grid remote url if other than default", + ) + dash.addoption( "--headless", action="store_true", @@ -99,6 +112,8 @@ def dashr_server(): def dash_br(request, tmpdir): with Browser( browser=request.config.getoption("webdriver"), + remote=request.config.getoption("remote"), + remote_url=request.config.getoption("remote_url"), headless=request.config.getoption("headless"), options=request.config.hook.pytest_setup_options(), download_path=tmpdir.mkdir("download").strpath, @@ -112,6 +127,8 @@ def dash_duo(request, dash_thread_server, tmpdir): with DashComposite( dash_thread_server, browser=request.config.getoption("webdriver"), + remote=request.config.getoption("remote"), + remote_url=request.config.getoption("remote_url"), headless=request.config.getoption("headless"), options=request.config.hook.pytest_setup_options(), download_path=tmpdir.mkdir("download").strpath, @@ -125,6 +142,8 @@ def dashr(request, dashr_server, tmpdir): with DashRComposite( dashr_server, browser=request.config.getoption("webdriver"), + remote=request.config.getoption("remote"), + remote_url=request.config.getoption("remote_url"), headless=request.config.getoption("headless"), options=request.config.hook.pytest_setup_options(), download_path=tmpdir.mkdir("download").strpath, diff --git a/tests/unit/test_browser.py b/tests/unit/test_browser.py new file mode 100644 index 0000000000..a7aff954bb --- /dev/null +++ b/tests/unit/test_browser.py @@ -0,0 +1,44 @@ +import pytest +from dash.testing.browser import Browser +from dash.testing.consts import SELENIUM_GRID_DEFAULT + + +@pytest.mark.parametrize("browser_type", ("Chrome", "Firefox")) +def test_browser_smoke(browser_type, tmpdir): + + browser = Browser( + browser=browser_type, + remote=False, + remote_url=SELENIUM_GRID_DEFAULT, + headless=True, + options=None, + download_path=tmpdir.mkdir("download").strpath, + percy_finalize=True, + ) + assert browser.driver.name == browser_type.lower() + + +def test_browser_use_remote_webdriver(tmpdir): + # test creation with remote=True + with pytest.raises(Exception): + Browser( + browser="Chrome", + remote=True, + remote_url=SELENIUM_GRID_DEFAULT, + headless=True, + options=None, + download_path=tmpdir.mkdir("download").strpath, + percy_finalize=True, + ) + + # test creation with remote_url other than default + with pytest.raises(Exception): + Browser( + browser="Chrome", + remote=False, + remote_url="http://token@any.selenium.grid:3333", + headless=True, + options=None, + download_path=tmpdir.mkdir("download").strpath, + percy_finalize=True, + )