Skip to content

Commit

Permalink
Update edge testing for Chrome based Edge on MacOS
Browse files Browse the repository at this point in the history
  • Loading branch information
twalpole committed Jun 3, 2019
1 parent 951e4ce commit 40e3abd
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 8 deletions.
13 changes: 13 additions & 0 deletions gemfiles/Gemfile.chrome_edge
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
source "https://rubygems.org"

gem 'bundler', '< 3.0'
gemspec path: '..'

gem 'xpath', github: 'teamcapybara/xpath'

gem 'selenium-webdriver', github: 'seleniumhq/selenium', glob: 'rb/*.gemspec'
gem 'webdrivers', github: 'twalpole/webdrivers', branch: 'selenium_4'
gem 'rack', github: 'rack/rack'
gem 'sinatra', github: 'sinatra/sinatra', branch: 'master'

gem 'puma', github: 'puma/puma'
4 changes: 2 additions & 2 deletions gemfiles/Gemfile.edge-firefox
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ gemspec path: '..'
gem 'xpath', github: 'teamcapybara/xpath'

gem 'selenium-webdriver', :path => '../../selenium/build/rb'
gem 'webdrivers', github: 'twalpole/webdrivers', branch: 'selenium_4' if ENV['CI']
gem 'webdrivers', 'twalpole/webdrivers', branch: 'selenium_4' if ENV['CI']
gem 'rack', github: 'rack/rack'
gem 'sinatra', github: 'sinatra/sinatra', branch: 'master'

gem 'puma', github: 'puma/puma'
gem 'puma', github: 'puma/puma'
1 change: 1 addition & 0 deletions lib/capybara/selenium/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -475,3 +475,4 @@ def accept_unhandled_reset_alert
require 'capybara/selenium/driver_specializations/firefox_driver'
require 'capybara/selenium/driver_specializations/internet_explorer_driver'
require 'capybara/selenium/driver_specializations/safari_driver'
require 'capybara/selenium/driver_specializations/edge_driver'
119 changes: 119 additions & 0 deletions lib/capybara/selenium/driver_specializations/edge_driver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# frozen_string_literal: true

require 'capybara/selenium/nodes/edge_node'

module Capybara::Selenium::Driver::EdgeDriver
def fullscreen_window(handle)
return super if edgedriver_version < 75

within_given_window(handle) do
begin
super
rescue NoMethodError => e
raise unless e.message.match?(/full_screen_window/)

result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
result['value']
end
end
end

def resize_window_to(handle, width, height)
super
rescue Selenium::WebDriver::Error::UnknownError => e
raise unless e.message.match?(/failed to change window state/)

# Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
# and raises unnecessary error. Wait a bit and try again.
sleep 0.25
super
end

def reset!
return super if edgedriver_version < 75
# Use instance variable directly so we avoid starting the browser just to reset the session
return unless @browser

switch_to_window(window_handles.first)
window_handles.slice(1..-1).each { |win| close_window(win) }

timer = Capybara::Helpers.timer(expire_in: 10)
begin
@browser.navigate.to('about:blank')
clear_storage unless uniform_storage_clear?
wait_for_empty_page(timer)
rescue *unhandled_alert_errors
accept_unhandled_reset_alert
retry
end

execute_cdp('Storage.clearDataForOrigin', origin: '*', storageTypes: storage_types_to_clear)
end

def download_path=(path)
if @browser.respond_to?(:download_path=)
@browser.download_path = path
else
# Not yet implemented in seleniun-webdriver for edge so do it ourselves
execute_cdp('Page.setDownloadBehavior', behavior: 'allow', downloadPath: path)
end
end

private

def storage_types_to_clear
types = ['cookies']
types << 'local_storage' if clear_all_storage?
types.join(',')
end

def clear_all_storage?
options.values_at(:clear_session_storage, :clear_local_storage).none? { |s| s == false }
end

def uniform_storage_clear?
clear = options.values_at(:clear_session_storage, :clear_local_storage)
clear.all? { |s| s == false } || clear.none? { |s| s == false }
end

def clear_storage
# Chrome errors if attempt to clear storage on about:blank
# In W3C mode it crashes chromedriver
url = current_url
super unless url.nil? || url.start_with?('about:')
end

def delete_all_cookies
execute_cdp('Network.clearBrowserCookies')
rescue *cdp_unsupported_errors
# If the CDP clear isn't supported do original limited clear
super
end

def cdp_unsupported_errors
@cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
end

def execute_cdp(cmd, params = {})
args = { cmd: cmd, params: params }
result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args)
result['value']
end

def build_node(native_node, initial_cache = {})
::Capybara::Selenium::EdgeNode.new(self, native_node, initial_cache)
end

def bridge
browser.send(:bridge)
end

def edgedriver_version
@edgedriver_version ||= begin
caps = browser.capabilities
caps['chrome']&.fetch('chromedriverVersion', nil).to_f
end
end
end

Capybara::Selenium::Driver.register_specialization :edge, Capybara::Selenium::Driver::EdgeDriver
92 changes: 92 additions & 0 deletions lib/capybara/selenium/nodes/edge_node.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# frozen_string_literal: true

require 'capybara/selenium/extensions/html5_drag'

class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
include Html5Drag

def set_text(value, clear: nil, **_unused)
return super unless chrome_edge?

super.tap do
# React doesn't see the chromedriver element clear
send_keys(:space, :backspace) if value.to_s.empty? && clear.nil?
end
end

def set_file(value) # rubocop:disable Naming/AccessorMethodName
# In Chrome 75+ files are appended (due to WebDriver spec - why?) so we have to clear here if its multiple and already set
if chrome_edge?
driver.execute_script(<<~JS, self)
if (arguments[0].multiple && (arguments[0].files.length > 0)){
arguments[0].value = null;
}
JS
end
super
rescue *file_errors => e
raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" if e.message.match?(/File not found : .+\n.+/m)

raise
end

def drag_to(element)
return super unless chrome_edge? && html5_draggable?

html5_drag_to(element)
end

def drop(*args)
return super unless chrome_edge?

html5_drop(*args)
end

# def click(*)
# super
# rescue ::Selenium::WebDriver::Error::WebDriverError => e
# # chromedriver 74 (at least on mac) raises the wrong error for this
# raise ::Selenium::WebDriver::Error::ElementClickInterceptedError, e.message if e.message.match?(/element click intercepted/)
#
# raise
# end

def disabled?
return super unless chrome_edge?

driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
end

def select_option
return super unless chrome_edge?

# To optimize to only one check and then click
selected_or_disabled = driver.evaluate_script(<<~JS, self)
arguments[0].matches(':disabled, select:disabled *, :checked')
JS
click unless selected_or_disabled
end

private

def file_errors
@file_errors = ::Selenium::WebDriver.logger.suppress_deprecations do
[::Selenium::WebDriver::Error::ExpectedError]
end
end

def bridge
driver.browser.send(:bridge)
end

def browser_version
@browser_version ||= begin
caps = driver.browser.capabilities
(caps[:browser_version] || caps[:version]).to_f
end
end

def chrome_edge?
browser_version >= 75
end
end
5 changes: 3 additions & 2 deletions lib/capybara/spec/session/window/window_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,12 @@ def win_size
other_window = @session.window_opened_by do
@session.find(:css, '#openWindow').click
end
other_window.resize_to(600, 300)

other_window.resize_to(600, 400)
expect(@session.current_window).to eq(orig_window)

@session.within_window(other_window) do
expect(@session.current_window.size).to eq([600, 300])
expect(@session.current_window.size).to eq([600, 400])
end
end
end
Expand Down
15 changes: 13 additions & 2 deletions spec/selenium_spec_edge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,34 @@
require 'shared_selenium_node'
require 'rspec/shared_spec_matchers'

Selenium::WebDriver::Edge::Service.driver_path = '/usr/local/bin/msedgedriver'
# Not yet implemented in the selenium-webdriver edge driver
# Selenium::WebDriver::Edge.path = '/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary'

Capybara.register_driver :selenium_edge do |app|
# ::Selenium::WebDriver.logger.level = "debug"
Capybara::Selenium::Driver.new(app, browser: :edge)
Capybara::Selenium::Driver.new(app, browser: :edge).tap do |driver|
driver.browser
driver.download_path = Capybara.save_path
end
end

module TestSessions
SeleniumEdge = Capybara::Session.new(:selenium_edge, TestApp)
end

skipped_tests = %i[response_headers status_code trigger modals]
skipped_tests = %i[response_headers status_code trigger]

Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::Edge) if ENV['CI']

Capybara::SpecHelper.run_specs TestSessions::SeleniumEdge, 'selenium', capybara_skip: skipped_tests do |example|
case example.metadata[:description]
when /#refresh it reposts$/
skip 'Edge insists on prompting without providing a way to suppress'
when /should be able to open non-http url/
skip 'Crashes'
when /when Capybara.always_include_port is true/
skip 'Crashes'
end
end

Expand Down
3 changes: 3 additions & 0 deletions spec/shared_selenium_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@

it 'should have return code 1 when running selenium_driver_rspec_failure.rb' do
skip 'only setup for local non-headless' if headless_or_remote?
skip 'Not setup for edge' if edge?(session)

system(env, 'rspec spec/fixtures/selenium_driver_rspec_failure.rb', out: File::NULL, err: File::NULL)
expect($CHILD_STATUS.exitstatus).to eq(1)
end

it 'should have return code 0 when running selenium_driver_rspec_success.rb' do
skip 'only setup for local non-headless' if headless_or_remote?
skip 'Not setup for edge' if edge?(session)

system(env, 'rspec spec/fixtures/selenium_driver_rspec_success.rb', out: File::NULL, err: File::NULL)
expect($CHILD_STATUS.exitstatus).to eq(0)
Expand Down Expand Up @@ -311,6 +313,7 @@
pending "IE doesn't support uploading a directory" if ie?(session)
pending 'Chrome/chromedriver 73 breaks this' if chrome?(session) && !chrome_lt?(73, session)
pending "Safari doesn't support uploading a directory" if safari?(session)
# pending "Edge/msedgedriver doesn't support directory upload" if edge?(session) && edge_gte?(75, session)

session.visit('/form')
test_file_dir = File.expand_path('./fixtures', File.dirname(__FILE__))
Expand Down
17 changes: 15 additions & 2 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,31 @@ def chrome?(session)
browser_name(session) == :chrome
end

def chrome_version(session)
(session.driver.browser.capabilities[:browser_version] ||
session.driver.browser.capabilities[:version]).to_f
end

def chrome_lt?(version, session)
chrome?(session) && (session.driver.browser.capabilities[:version].to_f < version)
chrome?(session) && (chrome_version(session) < version)
end

def chrome_gte?(version, session)
chrome?(session) && (session.driver.browser.capabilities[:version].to_f >= version)
chrome?(session) && (chrome_version(session) < version)
end

def edge?(session)
browser_name(session) == :edge
end

def edge_lt?(version, session)
edge?(session) && (chrome_version(session) < version)
end

def edge_gte?(version, session)
edge?(session) && (chrome_version(session) >= version)
end

def ie?(session)
%i[internet_explorer ie].include?(browser_name(session))
end
Expand Down

0 comments on commit 40e3abd

Please sign in to comment.