diff --git a/MANIFEST.in b/MANIFEST.in index b8052b8082a6b..5e4989cbf16f0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,7 +9,8 @@ recursive-include py/selenium/webdriver/phantomjs *.py recursive-include py/selenium/webdriver/firefox *.py *.xpi *.json recursive-include py/selenium/webdriver/firefox/x86 *.so recursive-include py/selenium/webdriver/firefox/amd64 *.so -recursive-include py/selenium/webdriver/ie *.py +recursive-include py/selenium/webdriver/ie *.py +recursive-include py/selenium/webdriver/edge *.py recursive-include py/selenium/webdriver/remote *.py recursive-include py/selenium/webdriver/support *.py include py/selenium/selenium.py @@ -17,4 +18,3 @@ include py/selenium/__init__.py include py/CHANGES include py/README recursive-include selenium.egg-info * - diff --git a/py/build.desc b/py/build.desc index c6dda5d9ccd98..a0ca87e58d4df 100644 --- a/py/build.desc +++ b/py/build.desc @@ -40,6 +40,11 @@ py_test( deps = [ ":test_ie" ], browsers = [ "ie" ]) +py_test( + name = "edge_test", + deps = [":test_edge"], + browsers = [ "edge"]) + py_test( name = "remote_firefox_test", remote_firefox_specific_tests = [ "test/selenium/webdriver/remote/*_tests.py" ], @@ -69,6 +74,7 @@ py_test( "chrome", "ff", "ie", + "edge", "blackberry", "phantomjs", "remote_firefox", diff --git a/py/selenium/webdriver/__init__.py b/py/selenium/webdriver/__init__.py index e1798168d7804..5a96fa5ab0805 100644 --- a/py/selenium/webdriver/__init__.py +++ b/py/selenium/webdriver/__init__.py @@ -20,6 +20,7 @@ from .chrome.webdriver import WebDriver as Chrome from .chrome.options import Options as ChromeOptions from .ie.webdriver import WebDriver as Ie +from .edge.webdriver import WebDriver as Edge from .opera.webdriver import WebDriver as Opera from .safari.webdriver import WebDriver as Safari from .blackberry.webdriver import WebDriver as BlackBerry diff --git a/py/selenium/webdriver/common/desired_capabilities.py b/py/selenium/webdriver/common/desired_capabilities.py index 0afad2ed6bcf0..6af531e4687af 100755 --- a/py/selenium/webdriver/common/desired_capabilities.py +++ b/py/selenium/webdriver/common/desired_capabilities.py @@ -62,6 +62,12 @@ class DesiredCapabilities(object): "javascriptEnabled": True, } + EDGE = { + "browserName": "MicrosoftEdge", + "version": "", + "platform": "WINDOWS" + } + CHROME = { "browserName": "chrome", "version": "", diff --git a/py/selenium/webdriver/edge/__init__.py b/py/selenium/webdriver/edge/__init__.py new file mode 100644 index 0000000000000..a5b1e6f85a09e --- /dev/null +++ b/py/selenium/webdriver/edge/__init__.py @@ -0,0 +1,16 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/py/selenium/webdriver/edge/options.py b/py/selenium/webdriver/edge/options.py new file mode 100644 index 0000000000000..24052a8c54bef --- /dev/null +++ b/py/selenium/webdriver/edge/options.py @@ -0,0 +1,45 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities + + +class Options(object): + + def __init__(self): + self._page_load_strategy = "normal" + + @property + def page_load_strategy(self): + return self._page_load_strategy + + @page_load_strategy.setter + def page_load_strategy(self, value): + if value not in ['normal', 'eager', 'none']: + raise ValueError("Page Load Strategy should be 'normal', 'eager' or 'none'.") + self._page_load_strategy = value + + def to_capabilities(self): + """ + Creates a capabilities with all the options that have been set and + + returns a dictionary with everything + """ + edge = DesiredCapabilities.EDGE.copy() + edge['pageLoadStrategy'] = self._page_load_strategy + + return edge diff --git a/py/selenium/webdriver/edge/service.py b/py/selenium/webdriver/edge/service.py new file mode 100644 index 0000000000000..7159d962775f2 --- /dev/null +++ b/py/selenium/webdriver/edge/service.py @@ -0,0 +1,99 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import subprocess +from subprocess import PIPE +import time +from selenium.common.exceptions import WebDriverException +from selenium.webdriver.common import utils + +class Service(object): + """ + Object that manages the starting and stopping of the EdgeDriver + """ + + def __init__(self, executable_path, port=0): + """ + Creates a new instance of the Service + + :Args: + - executable_path : Path to the EdgeDriver + - port : Port the service is running on + """x + + self.path = executable_path + + self.port = port + if self.port == 0: + self.port = utils.free_port() + + def start(self): + """ + Starts the EdgeDriver Service. + + :Exceptions: + - WebDriverException : Raised either when it can't start the service + or when it can't connect to the service + """ + try: + cmd = [self.path, "--port=%d" % self.port] + self.process = subprocess.Popen(cmd, + stdout=PIPE, stderr=PIPE) + except TypeError: + raise + except: + raise WebDriverException( + "The EdgeDriver executable needs to be available in the path. " + "Please download from http://go.microsoft.com/fwlink/?LinkId=619687 ") + count = 0 + while not utils.is_url_connectable(self.port): + count += 1 + time.sleep(1) + if count == 30: + raise WebDriverException("Can not connect to the EdgeDriver") + + def stop(self): + """ + Tells the EdgeDriver to stop and cleans up the process + """ + #If its dead dont worry + if self.process is None: + return + + #Tell the Server to die! + try: + from urllib import request as url_request + except ImportError: + import urllib2 as url_request + + url_request.urlopen("http://127.0.0.1:%d/shutdown" % self.port) + count = 0 + while utils.is_connectable(self.port): + if count == 30: + break + count += 1 + time.sleep(1) + + #Tell the Server to properly die in case + try: + if self.process: + self.process.stdout.close() + self.process.stderr.close() + self.process.kill() + self.process.wait() + except WindowsError: + # kill may not be available under windows environment + pass diff --git a/py/selenium/webdriver/edge/webdriver.py b/py/selenium/webdriver/edge/webdriver.py new file mode 100644 index 0000000000000..22206c7b2442e --- /dev/null +++ b/py/selenium/webdriver/edge/webdriver.py @@ -0,0 +1,46 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from selenium.webdriver.common import utils +from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from .service import Service + + +class WebDriver(RemoteWebDriver): + + def __init__(self, executable_path='MicrosoftWebDriver.exe', + capabilities=None, port=0): + self.port = port + if self.port == 0: + self.port = utils.free_port() + + self.edge_service = Service(executable_path, port=self.port) + self.edge_service.start() + + if capabilities is None: + capabilities = DesiredCapabilities.EDGE + + RemoteWebDriver.__init__( + self, + command_executor='http://localhost:%d' % self.port, + desired_capabilities=capabilities) + self._is_remote = False + + def quit(self): + RemoteWebDriver.quit(self) + self.edge_service.stop() diff --git a/rake-tasks/browsers.rb b/rake-tasks/browsers.rb index 5025554945423..abf9fed20fa69 100644 --- a/rake-tasks/browsers.rb +++ b/rake-tasks/browsers.rb @@ -35,6 +35,16 @@ :browser_name => "internet explorer", :available => windows? }, + "edge" => { + :python => { + :ignore => "edge", + :dir => "edge", + :file_string => "edge", + :class => "Edge" + }, + :browser_name => "MicrosoftEdge", + :available => windows? + }, "chrome" => { :python => { :ignore => "chrome", diff --git a/setup.py b/setup.py index 78d17353f1feb..e710cf90f75ef 100755 --- a/setup.py +++ b/setup.py @@ -72,6 +72,7 @@ 'selenium.webdriver.support', 'selenium.webdriver.firefox', 'selenium.webdriver.ie', + 'selenium.webdriver.edge', 'selenium.webdriver.opera', 'selenium.webdriver.phantomjs', 'selenium.webdriver.remote',