Skip to content

Commit 24e20e0

Browse files
committed
rb: Initial attempt at supporting Microsoft Edge
1 parent e37ad82 commit 24e20e0

File tree

11 files changed

+312
-3
lines changed

11 files changed

+312
-3
lines changed

Rakefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,9 @@ task :test_rb => [
252252
"//rb:remote-test",
253253
"//rb:rc-client-integration-test",
254254
("//rb:ie-test" if windows?),
255+
("//rb:edge-test" if windows?),
255256
"//rb:chrome-test",
256-
"//rb:safari-test",
257+
("//rb:safari-test" if mac?),
257258
"//rb:phantomjs-test"
258259
].compact
259260

rb/build.desc

+22
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ rubygem(
55
"//rb:chrome",
66
"//rb:common",
77
"//rb:support",
8+
"//rb:edge",
89
"//rb:firefox",
910
"//rb:ie",
1011
"//rb:iphone",
@@ -90,6 +91,26 @@ ruby_test(name = "chrome",
9091
deps = [ ":chrome" ]
9192
)
9293

94+
ruby_library(name = "edge",
95+
srcs = [
96+
"lib/selenium/webdriver/edge/**/*.rb",
97+
"lib/selenium/webdriver/edge.rb"
98+
],
99+
deps = [
100+
":common",
101+
":remote"
102+
]
103+
)
104+
105+
ruby_test(name = "edge",
106+
srcs = [
107+
"spec/integration/selenium/webdriver/*_spec.rb",
108+
"spec/integration/selenium/webdriver/edge/**/*_spec.rb"
109+
],
110+
include = ["rb/spec/integration", "build/rb/lib"],
111+
deps = [ ":edge" ]
112+
)
113+
93114
ruby_library(name = "firefox",
94115
srcs = [
95116
"lib/selenium/webdriver/firefox/**/*.rb",
@@ -245,6 +266,7 @@ ruby_test(name = "unit",
245266
":android",
246267
":chrome",
247268
":common",
269+
":edge",
248270
":firefox",
249271
":ie",
250272
":iphone",

rb/lib/selenium/webdriver.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module WebDriver
3535

3636
autoload :Android, 'selenium/webdriver/android'
3737
autoload :Chrome, 'selenium/webdriver/chrome'
38+
autoload :Edge, 'selenium/webdriver/edge'
3839
autoload :Firefox, 'selenium/webdriver/firefox'
3940
autoload :IE, 'selenium/webdriver/ie'
4041
autoload :IPhone, 'selenium/webdriver/iphone'
@@ -52,7 +53,7 @@ def self.root
5253
#
5354
# Create a new Driver instance with the correct bridge for the given browser
5455
#
55-
# @param browser [:ie, :internet_explorer, :remote, :chrome, :firefox, :ff, :android, :iphone, :phantomjs, :safari]
56+
# @param browser [:ie, :internet_explorer, :edge, :remote, :chrome, :firefox, :ff, :android, :iphone, :phantomjs, :safari]
5657
# the driver type to use
5758
# @param *rest
5859
# arguments passed to Bridge.new
@@ -62,6 +63,7 @@ def self.root
6263
# @see Selenium::WebDriver::Remote::Bridge
6364
# @see Selenium::WebDriver::Firefox::Bridge
6465
# @see Selenium::WebDriver::IE::Bridge
66+
# @see Selenium::WebDriver::Edge::Bridge
6567
# @see Selenium::WebDriver::Chrome::Bridge
6668
# @see Selenium::WebDriver::Android::Bridge
6769
# @see Selenium::WebDriver::IPhone::Bridge

rb/lib/selenium/webdriver/common/driver.rb

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def for(browser, opts = {})
5454
IE::Bridge.new(opts)
5555
when :chrome
5656
Chrome::Bridge.new(opts)
57+
when :edge
58+
Edge::Bridge.new(opts)
5759
when :android
5860
Android::Bridge.new(opts)
5961
when :iphone

rb/lib/selenium/webdriver/edge.rb

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# encoding: utf-8
2+
#
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
require 'net/http'
21+
22+
require 'selenium/webdriver/edge/service'
23+
require 'selenium/webdriver/edge/bridge'
24+
25+
module Selenium
26+
module WebDriver
27+
28+
module Edge
29+
def self.driver_path=(path)
30+
Service.executable_path = path
31+
end
32+
33+
def self.path=(path)
34+
Platform.assert_executable path
35+
@path = path
36+
end
37+
38+
def self.path
39+
@path ||= nil
40+
end
41+
42+
end # Edge
43+
end # WebDriver
44+
end # Selenium
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# encoding: utf-8
2+
#
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
module Selenium
21+
module WebDriver
22+
module Edge
23+
24+
# @api private
25+
class Bridge < Remote::Bridge
26+
27+
def initialize(opts = {})
28+
http_client = opts.delete(:http_client)
29+
30+
if opts.has_key?(:url)
31+
url = opts.delete(:url)
32+
else
33+
@service = Service.default_service(*extract_service_args(opts))
34+
@service.start
35+
36+
url = @service.uri
37+
end
38+
39+
caps = create_capabilities(opts)
40+
41+
remote_opts = {
42+
:url => url,
43+
:desired_capabilities => caps
44+
}
45+
46+
remote_opts.merge!(:http_client => http_client) if http_client
47+
48+
super(remote_opts)
49+
end
50+
51+
def browser
52+
:edge
53+
end
54+
55+
def driver_extensions
56+
[
57+
DriverExtensions::TakesScreenshot,
58+
DriverExtensions::HasInputDevices
59+
]
60+
end
61+
62+
def capabilities
63+
@capabilities ||= Remote::Capabilities.edge
64+
end
65+
66+
def quit
67+
super
68+
ensure
69+
@service.stop if @service
70+
end
71+
72+
private
73+
74+
def create_capabilities(opts)
75+
caps = opts.delete(:desired_capabilities) { Remote::Capabilities.edge }
76+
page_load_strategy = opts.delete(:page_load_strategy) || "normal"
77+
78+
unless opts.empty?
79+
raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
80+
end
81+
82+
edge_options = caps['edgeOptions'] || {}
83+
caps['edgeOptions'] = edge_options
84+
caps['page_load_strategy'] = page_load_strategy
85+
86+
caps
87+
end
88+
89+
def extract_service_args(opts)
90+
args = []
91+
92+
if opts.has_key?(:service_log_path)
93+
args << "--log-path=#{opts.delete(:service_log_path)}"
94+
end
95+
96+
args
97+
end
98+
99+
end # Bridge
100+
end # Edge
101+
end # WebDriver
102+
end # Selenium
+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# encoding: utf-8
2+
#
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
module Selenium
21+
module WebDriver
22+
module Edge
23+
24+
#
25+
# @api private
26+
#
27+
class Service
28+
START_TIMEOUT = 20
29+
SOCKET_LOCK_TIMEOUT = 45
30+
STOP_TIMEOUT = 5
31+
DEFAULT_PORT = 17556
32+
MISSING_TEXT = "Unable to find MicrosoftWebDriver. Please download the server from https://www.microsoft.com/en-us/download/details.aspx?id=48212. More info at https://github.com/SeleniumHQ/selenium/wiki/MicrosoftWebDriver."
33+
34+
def self.executable_path
35+
@executable_path ||= (
36+
path = Platform.find_binary "MicrosoftWebDriver"
37+
path or raise Error::WebDriverError, MISSING_TEXT
38+
Platform.assert_executable path
39+
40+
path
41+
)
42+
end
43+
44+
def self.executable_path=(path)
45+
Platform.assert_executable path
46+
@executable_path = path
47+
end
48+
49+
def self.default_service(*extra_args)
50+
new executable_path, DEFAULT_PORT, *extra_args
51+
end
52+
53+
def initialize(executable_path, port, *extra_args)
54+
@executable_path = executable_path
55+
@host = Platform.localhost
56+
@port = Integer(port)
57+
58+
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
59+
60+
@extra_args = extra_args
61+
end
62+
63+
def start
64+
Platform.exit_hook { stop } # make sure we don't leave the server running
65+
66+
socket_lock.locked do
67+
find_free_port
68+
start_process
69+
connect_until_stable
70+
end
71+
end
72+
73+
def stop
74+
return if @process.nil? || @process.exited?
75+
76+
Net::HTTP.start(@host, @port) do |http|
77+
http.open_timeout = STOP_TIMEOUT / 2
78+
http.read_timeout = STOP_TIMEOUT / 2
79+
80+
http.head("/shutdown")
81+
end
82+
83+
@process.poll_for_exit STOP_TIMEOUT
84+
rescue ChildProcess::TimeoutError
85+
# ok, force quit
86+
@process.stop STOP_TIMEOUT
87+
end
88+
89+
def uri
90+
URI.parse "http://#{@host}:#{@port}"
91+
end
92+
93+
def find_free_port
94+
@port = PortProber.above @port
95+
end
96+
97+
def start_process
98+
server_command = [@executable_path, "--port=#{@port}", *@extra_args]
99+
@process = ChildProcess.build(*server_command)
100+
101+
@process.io.inherit! if $DEBUG == true
102+
@process.start
103+
end
104+
105+
def connect_until_stable
106+
@socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
107+
108+
unless @socket_poller.connected?
109+
raise Error::WebDriverError, "unable to connect to MicrosoftWebDriver #{@host}:#{@port}"
110+
end
111+
end
112+
113+
def socket_lock
114+
@socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
115+
end
116+
117+
end # Service
118+
end # Edge
119+
end # WebDriver
120+
end # Service

rb/lib/selenium/webdriver/remote/capabilities.rb

+7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ def chrome(opts = {})
7878
}.merge(opts))
7979
end
8080

81+
def edge(opts = {})
82+
new({
83+
:browser_name => "edge",
84+
:platform => :windows
85+
}.merge(opts))
86+
end
87+
8188
def firefox(opts = {})
8289
new({
8390
:browser_name => "firefox",

rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb

-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ def create_chrome_driver
205205
WebDriver::Driver.for :chrome,
206206
:native_events => native_events?,
207207
:args => args
208-
# :http_client => keep_alive_client || http_client
209208
end
210209

211210
def create_phantomjs_driver

rb/spec/unit/selenium/client/base_spec.rb

+5
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ class BaseClient
228228
client.chrome_backend?.should be true
229229
end
230230

231+
it "returns true when the browser string is *edge" do
232+
client = BaseClient.new :host, 24, "*edge", :url
233+
client.chrome_backend?.should be true
234+
end
235+
231236
it "returns true when the browser string is *firefox2" do
232237
client = BaseClient.new :host, 24, "*firefox2", :url
233238
client.chrome_backend?.should be true

0 commit comments

Comments
 (0)