Skip to content

Commit

Permalink
[rb] implement initial support for Firefox BiDi
Browse files Browse the repository at this point in the history
  • Loading branch information
titusfortner committed Feb 15, 2021
1 parent f93fe94 commit 790f604
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 43 deletions.
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ task test_rb: ['//rb:unit-test', :test_rb_local, :test_rb_remote]
task test_rb_local: [
'//rb:chrome-test',
'//rb:firefox-test',
('//rb:firefox-nightly-test' if ENV['FIREFOX_NIGHTLY_BINARY']),
('//rb:safari-preview-test' if SeleniumRake::Checks.mac?),
('//rb:safari-test' if SeleniumRake::Checks.mac?),
('//rb:ie-test' if SeleniumRake::Checks.windows?),
Expand All @@ -266,6 +267,7 @@ task test_rb_local: [
task test_rb_remote: [
'//rb:remote-chrome-test',
'//rb:remote-firefox-test',
('//rb:remote-firefox-nightly-test' if ENV['FIREFOX_NIGHTLY_BINARY']),
('//rb:remote-safari-test' if SeleniumRake::Checks.mac?),
# BUG - https://github.com/SeleniumHQ/selenium/issues/6791
# ('//rb:remote-safari-preview-test' if SeleniumRake::Checks.mac?),
Expand Down
32 changes: 32 additions & 0 deletions rb/build.desc
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,21 @@ ruby_test(name = "firefox",
deps = [":firefox"]
)

ruby_test(name = "firefox-nightly",
srcs = [
"spec/integration/selenium/webdriver/*_spec.rb",
"spec/integration/selenium/webdriver/firefox/**/*_spec.rb"
],
include = [
"rb/spec/integration",
"build/rb/lib"
],
deps = [
":firefox",
":devtools"
]
)

ruby_test(name = "remote-firefox",
srcs = [
"spec/integration/selenium/webdriver/*_spec.rb",
Expand Down Expand Up @@ -279,6 +294,7 @@ ruby_library(name = "devtools",
],
deps = [
":common",
":cdp-v85",
":cdp-v86",
":cdp-v87",
":cdp-v88",
Expand Down Expand Up @@ -306,6 +322,22 @@ ruby_test(name = "unit",
]
)

ruby_class_call(name = "cdp-v85",
klass = "Selenium::WebDriver::Support::CDPClientGenerator",
require = "rb/lib/selenium/webdriver/support/cdp_client_generator",
output_dir = "rb/lib/selenium/webdriver/devtools/v85",
version = "v85",
srcs = [
"lib/selenium/webdriver/support/cdp",
"lib/selenium/webdriver/support/cdp/**/*",
"lib/selenium/webdriver/support/cdp_client_generator.rb"
],
resources = [
{ "//common/devtools/chromium/v85:browser_protocol": "rb/lib/selenium/webdriver/support/cdp/browser_protocol.json" },
{ "//common/devtools/chromium/v85:js_protocol": "rb/lib/selenium/webdriver/support/cdp/js_protocol.json" }
]
)

ruby_class_call(name = "cdp-v86",
klass = "Selenium::WebDriver::Support::CDPClientGenerator",
require = "rb/lib/selenium/webdriver/support/cdp_client_generator",
Expand Down
4 changes: 4 additions & 0 deletions rb/lib/selenium/webdriver/chrome/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ def execute_cdp(cmd, **params)

private

def devtools_version
Integer(capabilities.browser_version.split('.').first)
end

def debugger_address
capabilities['goog:chromeOptions']['debuggerAddress']
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ module HasDevTools
#

def devtools
version = Integer(capabilities.browser_version.split('.').first)
WebDriver.logger.info "Using devtools version: #{version}"
@devtools ||= DevTools.new(url: debugger_address, version: version)
@devtools ||= DevTools.new(url: debugger_address, version: devtools_version)
end

end # HasDevTools
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,14 @@ def log_console_events

def log_exception_events
devtools.runtime.on(:exception_thrown) do |params|
description = if params.dig('exceptionDetails', 'exception')
params.dig('exceptionDetails', 'exception', 'description')
else
params.dig('exceptionDetails', 'text')
end

event = DevTools::ExceptionEvent.new(
description: params.dig('exceptionDetails', 'exception', 'description'),
description: description,
timestamp: params['timestamp'],
stacktrace: params.dig('exceptionDetails', 'stackTrace', 'callFrames')
)
Expand Down
5 changes: 4 additions & 1 deletion rb/lib/selenium/webdriver/devtools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ def attach_socket_listener
incoming_frame << socket.readpartial(1024)

while (frame = incoming_frame.next)
# Firefox will periodically fail on unparsable empty frame
break if frame.to_s.empty?

message = JSON.parse(frame.to_s)
@messages << message
WebDriver.logger.debug "DevTools <- #{message}"
Expand Down Expand Up @@ -112,7 +115,7 @@ def ws

def page_target
@page_target ||= begin
response = Net::HTTP.get(@uri.hostname, '/json', @uri.port)
response = Net::HTTP.get(@uri.hostname, '/json/list', @uri.port)
targets = JSON.parse(response)
targets.find { |target| target['type'] == 'page' }
end
Expand Down
12 changes: 12 additions & 0 deletions rb/lib/selenium/webdriver/firefox/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ module Firefox

class Driver < WebDriver::Driver
include DriverExtensions::HasAddons
include DriverExtensions::HasDevTools
include DriverExtensions::HasLogEvents
include DriverExtensions::HasWebStorage
include DriverExtensions::PrintsPage

Expand All @@ -38,6 +40,16 @@ def browser
def bridge_class
Bridge
end

private

def devtools_version
85
end

def debugger_address
capabilities['moz:debuggerAddress']
end
end # Driver
end # Firefox
end # WebDriver
Expand Down
5 changes: 5 additions & 0 deletions rb/lib/selenium/webdriver/firefox/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ module Selenium
module WebDriver
module Firefox
class Options < WebDriver::Options
attr_accessor :debugger_address

KEY = 'moz:firefoxOptions'

# see: https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html
Expand Down Expand Up @@ -50,6 +52,8 @@ class Options < WebDriver::Options
#

def initialize(log_level: nil, **opts)
@debugger_address = opts.delete(:debugger_address)

super(**opts)

@options[:args] ||= []
Expand Down Expand Up @@ -130,6 +134,7 @@ def log_level=(level)
private

def process_browser_options(browser_options)
browser_options['moz:debuggerAddress'] = true if @debugger_address
options = browser_options[KEY]
options['binary'] ||= Firefox.path if Firefox.path
options['profile'] = @profile if @profile
Expand Down
65 changes: 48 additions & 17 deletions rb/spec/integration/selenium/webdriver/devtools_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@

module Selenium
module WebDriver
describe DevTools, exclusive: {driver: %i[chrome edge]} do
describe DevTools, exclusive: {driver: %i[chrome edge firefox_nightly]} do
let(:username) { SpecSupport::RackServer::TestApp::BASIC_AUTH_CREDENTIALS.first }
let(:password) { SpecSupport::RackServer::TestApp::BASIC_AUTH_CREDENTIALS.last }

before(:all) { quit_driver }

after { reset_driver! }
after { quit_driver }

it 'sends commands' do
driver.devtools.page.navigate(url: url_for('xhtmlTest.html'))
Expand All @@ -42,25 +42,28 @@ module WebDriver
driver.navigate.to url_for('xhtmlTest.html')
sleep 0.5

expect(callback).to have_received(:call)
expect(callback).to have_received(:call).at_least(:once)
end

it 'authenticates on any request' do
driver.register(username: username, password: password)
context 'authentication', except: {browser: :firefox_nightly,
reason: 'Fetch.enable is not yet supported'} do
it 'on any request' do
driver.register(username: username, password: password)

driver.navigate.to url_for('basicAuth')
expect(driver.find_element(tag_name: 'h1').text).to eq('authorized')
end
driver.navigate.to url_for('basicAuth')
expect(driver.find_element(tag_name: 'h1').text).to eq('authorized')
end

it 'authenticates based on URL' do
auth_url = url_for('basicAuth')
driver.register(username: username, password: password, uri: /localhost/)
it 'based on URL' do
auth_url = url_for('basicAuth')
driver.register(username: username, password: password, uri: /localhost/)

driver.navigate.to auth_url.sub('localhost', '127.0.0.1')
expect { driver.find_element(tag_name: 'h1') }.to raise_error(Error::NoSuchElementError)
driver.navigate.to auth_url.sub('localhost', '127.0.0.1')
expect { driver.find_element(tag_name: 'h1') }.to raise_error(Error::NoSuchElementError)

driver.navigate.to auth_url
expect(driver.find_element(tag_name: 'h1').text).to eq('authorized')
driver.navigate.to auth_url
expect(driver.find_element(tag_name: 'h1').text).to eq('authorized')
end
end

it 'notifies about log messages' do
Expand All @@ -83,11 +86,38 @@ module WebDriver
an_object_having_attributes(type: :log, args: ['I like cheese']),
an_object_having_attributes(type: :log, args: [true]),
an_object_having_attributes(type: :log, args: [nil]),
an_object_having_attributes(type: :log, args: [{'type' => 'undefined'}]),
an_object_having_attributes(type: :log, args: [{'type' => 'undefined'}])
)
end

it 'notifies about document log messages', except: {browser: :firefox_nightly,
reason: 'Firefox & Chrome parse document differently'} do
logs = []
driver.on_log_event(:console) { |log| logs.push(log) }
driver.navigate.to url_for('javascriptPage.html')

driver.execute_script("console.log(document);")
wait.until { !logs.empty? }

expect(logs).to include(
an_object_having_attributes(type: :log, args: [hash_including('type' => 'object')])
)
end

it 'notifies about document log messages', only: {browser: :firefox_nightly,
reason: 'Firefox & Chrome parse document differently'} do
logs = []
driver.on_log_event(:console) { |log| logs.push(log) }
driver.navigate.to url_for('javascriptPage.html')

driver.execute_script("console.log(document);")
wait.until { !logs.empty? }

expect(logs).to include(
an_object_having_attributes(type: :log, args: [hash_including('location')])
)
end

it 'notifies about exceptions' do
exceptions = []
driver.on_log_event(:exception) { |exception| exceptions.push(exception) }
Expand All @@ -101,7 +131,8 @@ module WebDriver
expect(exception.stacktrace).not_to be_empty
end

it 'notifies about DOM mutations' do
it 'notifies about DOM mutations', except: {browser: :firefox_nightly,
reason: 'Runtime.addBinding not yet supported'} do
mutations = []
driver.on_log_event(:mutation) { |mutation| mutations.push(mutation) }
driver.navigate.to url_for('dynamic.html')
Expand Down
16 changes: 8 additions & 8 deletions rb/spec/integration/selenium/webdriver/edge/options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ module Edge
end
end

# it 'should be able to run in headless mode with #headless!' do
# options.headless!
#
# create_driver!(capabilities: options) do |driver|
# ua = driver.execute_script 'return window.navigator.userAgent'
# expect(ua).to match(/HeadlessChrome/)
# end
# end
it 'should be able to run in headless mode with #headless!' do
options.headless!

create_driver!(capabilities: options) do |driver|
ua = driver.execute_script 'return window.navigator.userAgent'
expect(ua).to match(/HeadlessChrome/)
end
end
end
end # Edge
end # WebDriver
Expand Down
2 changes: 1 addition & 1 deletion rb/spec/integration/selenium/webdriver/element_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module WebDriver
end

# https://github.com/mozilla/geckodriver/issues/245
it 'should send key presses chords', except: {browser: %i[firefox safari safari_preview]} do
it 'should send key presses chords', except: {browser: %i[firefox firefox_nightly safari safari_preview]} do
driver.navigate.to url_for('javascriptPage.html')
key_reporter = driver.find_element(id: 'keyReporter')

Expand Down
14 changes: 7 additions & 7 deletions rb/spec/integration/selenium/webdriver/manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ module WebDriver
end

it 'should not allow domain to be set for localhost',
except: [{browser: %i[chrome edge],
reason: "https://bugs.chromium.org/p/chromedriver/issues/detail?id=3733"},
{browser: %i[safari safari_preview]}] do
exclude: [{browser: %i[chrome edge],
reason: "https://bugs.chromium.org/p/chromedriver/issues/detail?id=3733"}],
except: {browser: %i[safari safari_preview]} do
expect {
driver.manage.add_cookie name: 'domain',
value: 'localhost',
Expand Down Expand Up @@ -127,7 +127,7 @@ module WebDriver
end

it 'should not add secure cookie when http',
except: {browser: :firefox,
except: {browser: %i[firefox firefox_nightly],
reason: 'https://github.com/mozilla/geckodriver/issues/1840'} do
driver.manage.add_cookie name: 'secure',
value: 'http',
Expand All @@ -149,15 +149,15 @@ module WebDriver
end

context 'sameSite' do
it 'should allow adding with value Strict', only: {browser: %i[chrome edge firefox]} do
it 'should allow adding with value Strict', only: {browser: %i[chrome edge firefox firefox_nightly]} do
driver.manage.add_cookie name: 'samesite',
value: 'strict',
same_site: 'Strict'

expect(driver.manage.cookie_named('samesite')[:same_site]).to eq('Strict')
end

it 'should allow adding with value Lax', only: {browser: %i[chrome edge firefox]} do
it 'should allow adding with value Lax', only: {browser: %i[chrome edge firefox firefox_nightly]} do
driver.manage.add_cookie name: 'samesite',
value: 'lax',
same_site: 'Lax'
Expand All @@ -178,7 +178,7 @@ module WebDriver
end

it 'should not allow adding with value None when secure is false',
except: [{browser: :firefox,
except: [{browser: %i[firefox firefox_nightly],
reason: "https://github.com/mozilla/geckodriver/issues/1842"},
{browser: %i[safari safari_preview]}] do
expect {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
# under the License.

shared_examples_for 'driver that can be started concurrently' do |guard|
before(:all) { quit_driver }
let(:drivers) { [] }
let(:threads) { [] }

before { quit_driver }

after do
drivers.each(&:quit)
threads.select(&:alive?).each(&:kill)
create_driver!
end

let(:drivers) { [] }
let(:threads) { [] }

it 'starts multiple drivers sequentially', guard do
expected_count = WebDriver::Platform.ci ? 2 : 4
expected_count.times do
Expand Down
Loading

0 comments on commit 790f604

Please sign in to comment.