Skip to content
This repository has been archived by the owner on Jan 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #171 from edx/profile_preferences
Browse files Browse the repository at this point in the history
Firefox profile preferences customization
  • Loading branch information
Jeremy Bowman committed Jun 22, 2016
2 parents 66d4db3 + 0afb723 commit dfc44b2
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 36 deletions.
91 changes: 56 additions & 35 deletions bok_choy/browser.py
Expand Up @@ -50,6 +50,9 @@

FIREFOX_PROFILE_ENV_VAR = 'FIREFOX_PROFILE_PATH'

# A list of functions accepting one FirefoxProfile argument
FIREFOX_PROFILE_CUSTOMIZERS = []


class BrowserConfigError(Exception):

Expand Down Expand Up @@ -248,6 +251,56 @@ def browser_check_func():
return browser_instance


def add_profile_customizer(func):
"""Add a new function that modifies the preferences of the firefox profile object it receives as an argument"""
FIREFOX_PROFILE_CUSTOMIZERS.append(func)


def clear_profile_customizers():
"""Remove any previously-configured functions for customizing the firefox profile"""
FIREFOX_PROFILE_CUSTOMIZERS[:] = []


def _firefox_profile():
"""Configure the Firefox profile, respecting FIREFOX_PROFILE_PATH if set"""
profile_dir = os.environ.get(FIREFOX_PROFILE_ENV_VAR)

if profile_dir:
LOGGER.info("Using firefox profile: %s", profile_dir)
try:
firefox_profile = webdriver.FirefoxProfile(profile_dir)
except OSError as err:
if err.errno == errno.ENOENT:
raise BrowserConfigError(
"Firefox profile directory {env_var}={profile_dir} does not exist".format(
env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir))
elif err.errno == errno.EACCES:
raise BrowserConfigError(
"Firefox profile directory {env_var}={profile_dir} has incorrect permissions. It must be \
readable and executable.".format(env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir))
else:
# Some other OSError:
raise BrowserConfigError(
"Problem with firefox profile directory {env_var}={profile_dir}: {msg}"
.format(env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir, msg=str(err)))
else:
LOGGER.info("Using default firefox profile")
firefox_profile = webdriver.FirefoxProfile()

# Bypasses the security prompt displayed by the browser when it attempts to
# access a media device (e.g., a webcam)
firefox_profile.set_preference('media.navigator.permission.disabled', True)

# Disable the initial url fetch to 'learn more' from mozilla (so you don't have to
# be online to run bok-choy on firefox)
firefox_profile.set_preference('browser.startup.homepage', 'about:blank')
firefox_profile.set_preference('startup.homepage_welcome_url', 'about:blank')
firefox_profile.set_preference('startup.homepage_welcome_url.additional', 'about:blank')
for function in FIREFOX_PROFILE_CUSTOMIZERS:
function(firefox_profile)
return firefox_profile


def _local_browser_class(browser_name):
"""
Returns class, kwargs, and args needed to instantiate the local browser.
Expand All @@ -264,43 +317,9 @@ def _local_browser_class(browser_name):
name=browser_name, options=", ".join(list(BROWSERS.keys()))))
else:
if browser_name == 'firefox':
profile_dir = os.environ.get(FIREFOX_PROFILE_ENV_VAR)

if profile_dir:
LOGGER.info("Using firefox profile: %s", profile_dir)
try:
firefox_profile = webdriver.FirefoxProfile(profile_dir)
except OSError as err:
if err.errno == errno.ENOENT:
raise BrowserConfigError(
"Firefox profile directory {env_var}={profile_dir} does not exist".format(
env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir))
elif err.errno == errno.EACCES:
raise BrowserConfigError(
"Firefox profile directory {env_var}={profile_dir} has incorrect permissions. It must be \
readable and executable.".format(env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir))
else:
# Some other OSError:
raise BrowserConfigError(
"Problem with firefox profile directory {env_var}={profile_dir}: {msg}"
.format(env_var=FIREFOX_PROFILE_ENV_VAR, profile_dir=profile_dir, msg=str(err)))
else:
LOGGER.info("Using default firefox profile")
firefox_profile = webdriver.FirefoxProfile()

# Bypasses the security prompt displayed by the browser when it attempts to
# access a media device (e.g., a webcam)
firefox_profile.set_preference('media.navigator.permission.disabled', True)

# Disable the initial url fetch to 'learn more' from mozilla (so you don't have to
# be online to run bok-choy on firefox)
firefox_profile.set_preference('browser.startup.homepage', 'about:blank')
firefox_profile.set_preference('startup.homepage_welcome_url', 'about:blank')
firefox_profile.set_preference('startup.homepage_welcome_url.additional', 'about:blank')

browser_args = []
browser_kwargs = {
'firefox_profile': firefox_profile,
'firefox_profile': _firefox_profile(),
}
if os.environ.get('SELENIUM_FIREFOX_PATH', None):
binary_kwarg = {
Expand Down Expand Up @@ -359,6 +378,8 @@ def _remote_browser_class(env_vars, tags=None):
'command_executor': url,
'desired_capabilities': caps,
}
if caps['browserName'] == 'firefox':
browser_kwargs['browser_profile'] = _firefox_profile()

return webdriver.Remote, browser_args, browser_kwargs

Expand Down
44 changes: 44 additions & 0 deletions docs/customization.rst
@@ -0,0 +1,44 @@
Browser Customization
=====================

Although the default browser configurations provided by bok-choy should be
sufficient for most needs, sometimes you'll need to customize it a little
for particular tests or even an entire test suite. Here are some of the
options bok-choy provides for doing that.

Firefox Profile Preferences
---------------------------

Whether you use a custom profile or not, you can customize the profile's
preferences before the browser is launched. To do this, create a function
which takes a
`FirefoxProfile <https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.firefox_profile.html#selenium.webdriver.firefox.firefox_profile.FirefoxProfile>`_
as a parameter and add it via the
``bok_choy.browser.add_profile_customizer()`` function. For example,
to suppress the "unresponsive script" warning dialog that normally interrupts
a test case in Firefox when running accessibility tests on a particularly long
page:

.. code-block:: python
def customize_preferences(profile):
profile.set_preference('dom.max_chrome_script_run_time', 0)
profile.set_preference('dom.max_script_run_time', 0)
bok_choy.browser.add_profile_customizer(customize_preferences)
This customization can be done in any of the normal places that test setup
occurs: ``setUpClass()``, a pytest fixture, the test case itself, etc. You
can clear any previously-added profile customizers via the
``bok_choy.browser.clear_profile_customizers()`` function.

Firefox Profile Directory
-------------------------

Normally, selenium launches Firefox using a new, anonymous user profile. If
you have a specific Firefox profile that you'd like to use instead, you can
specify the path to its directory in the ``FIREFOX_PROFILE_PATH`` environment
variable anytime before the call to ``bok_choy.browser.browser()``. This
passes the path to the
`FirefoxProfile constructor <https://seleniumhq.github.io/selenium/docs/api/py/webdriver_firefox/selenium.webdriver.firefox.firefox_profile.html#selenium.webdriver.firefox.firefox_profile.FirefoxProfile>`_
so the browser can be launched with any customizations that have been made to
that profile.
1 change: 1 addition & 0 deletions docs/index.rst
Expand Up @@ -12,6 +12,7 @@ UI-level acceptance test framework.
accessibility
visual_diff
xss
customization
api_reference


Expand Down
18 changes: 17 additions & 1 deletion tests/test_browser.py
Expand Up @@ -31,14 +31,30 @@ def test_local_browser(self):
def test_firefox_preferences(self):
browser = bok_choy.browser.browser()
self.addCleanup(browser.quit)
# In-spite of the name, 'default_preferences' represents the preferences
# In spite of the name, 'default_preferences' represents the preferences
# that are in place on the browser. (The underlying preferences start
# with default_preferences and are updated in-place.)
preferences = browser.profile.default_preferences
self.assertEqual(preferences['browser.startup.homepage'], 'about:blank')
self.assertEqual(preferences['startup.homepage_welcome_url'], 'about:blank')
self.assertEqual(preferences['startup.homepage_welcome_url.additional'], 'about:blank')

@patch.dict(os.environ, {'SELENIUM_BROWSER': 'firefox'})
def test_customize_firefox_preferences(self):
def customize_preferences(profile):
profile.set_preference('dom.max_chrome_script_run_time', 0)
profile.set_preference('dom.max_script_run_time', 0)
bok_choy.browser.add_profile_customizer(customize_preferences)
self.addCleanup(bok_choy.browser.clear_profile_customizers)
browser = bok_choy.browser.browser()
self.addCleanup(browser.quit)
# In spite of the name, 'default_preferences' represents the preferences
# that are in place on the browser. (The underlying preferences start
# with default_preferences and are updated in-place.)
preferences = browser.profile.default_preferences
assert preferences['dom.max_chrome_script_run_time'] == 0
assert preferences['dom.max_script_run_time'] == 0

@patch.dict(os.environ, {'SELENIUM_BROWSER': 'phantomjs'})
def test_phantom_browser(self):
browser = bok_choy.browser.browser()
Expand Down

0 comments on commit dfc44b2

Please sign in to comment.