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

New FirefoxDriver ctor precedence logic and moz:firefoxOptions su… #2882

Merged
merged 1 commit into from
Oct 10, 2016
Merged

New FirefoxDriver ctor precedence logic and moz:firefoxOptions su… #2882

merged 1 commit into from
Oct 10, 2016

Conversation

andreastt
Copy link
Member

@andreastt andreastt commented Oct 6, 2016

This change tries to make sense of the keyword arguments accepted by
FirefoxDriver's constructor in the transition phase between the Firefox
add-on driver and the Mozilla supported geckodriver HTTP service.

Prior to this patch, which arguments were consumed and respected varied
greatly on whether the capabilities["marionette"] key was set to true.
Following this change, the options passed on to geckodriver will
consistently come out of firefox.Options.

The patch implements precedence logic for capabilities,
firefox_options, and firefox_binary/firefox_profile. They will
populate the underlying data model in that order, which means a binary-
or profile location set in firefox_options will override one set in the
capabilities dictionary, and that firefox_binary and firefox_profile
will override both of these.

The goal is to make it predictable what key is chosen, and to make the
underlying data model somewhat easier to understand. The latter is
particularly important because the extension connection used for the old
Firefox add-on does not accept a binary, profile, or arguments through
capabilities since it is the Python bindings themselves that manage the
lifetime of the browser.

In the case of geckodriver, which is an external HTTP service, it
manages the lifetime and accepts a moz:firefoxOptions dictionary that
is not unlike chromeOptions as of version 0.11.0. Versions prior to
geckodriver 0.11.0 accepted firefox_binary, firefox_profile, and
firefox_args as top-level entries on the capabilities JSON Object.
This is no longer supported behaviour by either the Python bindings
or geckodriver.

Fixes mozilla/geckodriver#228.

@andreastt
Copy link
Member Author

r? @davehunt @AutomatedTester

@andreastt
Copy link
Member Author

I found the tests can be run like this:

 % tox -e py27-marionette py/test/selenium/webdriver/marionette/mn_options_tests.py -- -r=aPp -n0

Copy link
Contributor

@davehunt davehunt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, with a few comments. I'd like to get @AutomatedTester to look over it too, as the maintainer of the python bindings.

this would rank below `firefox_options.profile`.

:param firefox_profile: Instance of ``FirefoxProfile`` object.
:param firefox_binary: Full path to the Firefox binary to use.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation supports either a string or a FirefoxBinary instance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

# firefox_options overrides capabilities
if firefox_options is not None:
self.binary = firefox_options.binary or self.binary
self.profile = firefox_options.profile or self.profile
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.profile will be None

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed this, in a few other bugs that were if firefox_options was not set.

# disable native events if globally disabled
if self.profile is not None:
self.profile.native_events_enabled = (
self.NATIVE_EVENTS_ALLOWED and self.profile.native_events_enabled)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously if no profile was specified then we created a default one, which meant we were able to set the native events for the profile. By leaving GeckoDriver to create a default profile, we won't have the native events preferences set. I'm not overly familiar with these preferences, so I'm not sure if it matters.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you’re right this might matter when you’re not using geckodriver. It is an intentional change not to pass a profile to geckodriver as it is able to create one itself.

self.NATIVE_EVENTS_ALLOWED and self.profile.native_events_enabled)

# W3C remote
# TODO(ato): Perform conformance negotiation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean as an alternative to checking the marionette capability? If so, +1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one possible approach is to look at what version the system Firefox is, and if it is less than or equal to 47.0.1 default to the old add-on approach, otherwise implicitly go for geckodriver.

I’ll file this under “possible future improvements” (-:

driver = Firefox(
capabilities=capabilities,
firefox_options=options)
def driver(options = None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the options = None doing? I'm not sure it's needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted.

opts.profile = other_profile
assert other_profile == opts.profile

with TemporaryDirectory() as path:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to use the built-in pytest fixture tmpdir instead: http://docs.pytest.org/en/latest/tmpdir.html

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using tmpdir_factory now.

@shs96c
Copy link
Member

shs96c commented Oct 6, 2016

FYI, the java bindings throw an exception if the user has set profiles in more than one place that aren't the same profile. Silently overwriting a user choice seemed less than idea.

@andreastt
Copy link
Member Author

It is however the only backwards compatible solution since the Python bindings have supported that behaviour for years.

Returns the location of the binary otherwise an empty string
"""
return self._binary_location
def binary(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have the original method names back with deprecation warnings in addition to this change. These are in Selenium 2 and Selenium 3 is supposed to be purely removing RC, as requested by @shs96c

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

executor = FirefoxRemoteConnection(
remote_server_addr=self.service.service_url)
RemoteWebDriver.__init__(
self,
command_executor=executor,
desired_capabilities=capabilities,
keep_alive=True)

self._is_remote = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be false.

self._is_remote is used to know when we need to ship a file over to a selenium server on a different machine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@@ -15,25 +15,97 @@
# specific language governing permissions and limitations
# under the License.


import errno
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import doesnt appear to be used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

…pport

This change tries to make sense of the keyword arguments accepted by
FirefoxDriver's constructor in the transition phase between the Firefox
add-on driver and the Mozilla supported geckodriver HTTP service.

Prior to this patch, which arguments were consumed and respected varied
greatly on whether the `capabilities["marionette"]` key was set to true.
Following this change, the options passed on to geckodriver will
consistently come out of firefox.Options.

The patch implements precedence logic for `capabilities`,
`firefox_options`, and `firefox_binary`/`firefox_profile`.  They will
populate the underlying data model in that order, which means a binary-
or profile location set in `firefox_options` will override one set in the
`capabilities` dictionary, and that `firefox_binary` and `firefox_profile`
will override both of these.

The goal is to make it predictable what key is chosen, and to make the
underlying data model somewhat easier to understand.  The latter is
particularly important because the extension connection used for the old
Firefox add-on does not accept a binary, profile, or arguments through
capabilities since it is the Python bindings themselves that manage the
lifetime of the browser.

In the case of geckodriver, which is an external HTTP service, it
manages the lifetime and accepts a `moz:firefoxOptions` dictionary that
is not unlike `chromeOptions` as of version 0.11.0.  Versions prior to
geckodriver 0.11.0 accepted `firefox_binary`, `firefox_profile`, and
`firefox_args` as top-level entries on the capabilities JSON Object.
This is no longer supported behaviour by either the Python bindings
or geckodriver.
@AutomatedTester AutomatedTester merged commit 9706bb7 into SeleniumHQ:master Oct 10, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants