rubocop driven style cleanup
This commit is contained in:
parent
5233962c7c
commit
24eb7a0702
1206
.rubocop.yml
1206
.rubocop.yml
File diff suppressed because it is too large
Load Diff
4
Gemfile
4
Gemfile
|
@ -3,8 +3,8 @@ source 'https://rubygems.org'
|
||||||
gem 'bundler', '~> 1.1'
|
gem 'bundler', '~> 1.1'
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
gem 'xpath', :git => 'git://github.com/teamcapybara/xpath.git'
|
gem 'xpath', git: 'git://github.com/teamcapybara/xpath.git'
|
||||||
|
|
||||||
group :doc do
|
group :doc do
|
||||||
gem 'redcarpet', :platforms => :mri
|
gem 'redcarpet', platforms: :mri
|
||||||
end
|
end
|
||||||
|
|
6
Rakefile
6
Rakefile
|
@ -20,11 +20,11 @@ RSpec::Core::RakeTask.new(:spec_rack) do |t|
|
||||||
t.pattern = './spec{,/*/**}/*{_spec.rb}'
|
t.pattern = './spec{,/*/**}/*{_spec.rb}'
|
||||||
end
|
end
|
||||||
|
|
||||||
task :spec => [:spec_marionette]
|
task spec: [:spec_marionette]
|
||||||
|
|
||||||
YARD::Rake::YardocTask.new do |t|
|
YARD::Rake::YardocTask.new do |t|
|
||||||
t.files = ['lib/**/*.rb']
|
t.files = ['lib/**/*.rb']
|
||||||
t.options = %w(--markup=markdown)
|
t.options = %w[--markup=markdown]
|
||||||
end
|
end
|
||||||
|
|
||||||
Cucumber::Rake::Task.new(:cucumber) do |task|
|
Cucumber::Rake::Task.new(:cucumber) do |task|
|
||||||
|
@ -52,4 +52,4 @@ task :release do
|
||||||
'git push --tags'
|
'git push --tags'
|
||||||
end
|
end
|
||||||
|
|
||||||
task :default => %i[spec cucumber]
|
task default: %i[spec cucumber]
|
||||||
|
|
|
@ -21,6 +21,5 @@ When(/^I use a matcher that fails$/) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Then(/^the failing exception should be nice$/) do
|
Then(/^the failing exception should be nice$/) do
|
||||||
expect(@error_message).to match %r(expected to find visible css \"h1#doesnotexist\")
|
expect(@error_message).to match %r{expected to find visible css \"h1#doesnotexist\"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,4 +20,3 @@ Capybara.javascript_driver = :javascript_test
|
||||||
Capybara.register_driver :named_test do |app|
|
Capybara.register_driver :named_test do |app|
|
||||||
Capybara::RackTest::Driver.new(app)
|
Capybara::RackTest::Driver.new(app)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ module Capybara
|
||||||
class WindowError < CapybaraError; end
|
class WindowError < CapybaraError; end
|
||||||
class ReadOnlyElementError < CapybaraError; end
|
class ReadOnlyElementError < CapybaraError; end
|
||||||
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
|
|
||||||
|
@ -359,7 +358,7 @@ module Capybara
|
||||||
# @param [String] html The raw html
|
# @param [String] html The raw html
|
||||||
# @return [Nokogiri::HTML::Document] HTML document
|
# @return [Nokogiri::HTML::Document] HTML document
|
||||||
#
|
#
|
||||||
def HTML(html)
|
def HTML(html) # rubocop:disable Naming/MethodName
|
||||||
Nokogiri::HTML(html).tap do |document|
|
Nokogiri::HTML(html).tap do |document|
|
||||||
document.xpath('//textarea').each do |textarea|
|
document.xpath('//textarea').each do |textarea|
|
||||||
textarea['_capybara_raw_value'] = textarea.content.sub(/\A\n/, '')
|
textarea['_capybara_raw_value'] = textarea.content.sub(/\A\n/, '')
|
||||||
|
@ -377,6 +376,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def config
|
def config
|
||||||
@config ||= Capybara::Config.new
|
@config ||= Capybara::Config.new
|
||||||
end
|
end
|
||||||
|
@ -439,7 +439,7 @@ end
|
||||||
|
|
||||||
Capybara.register_server :webrick do |app, port, host, **options|
|
Capybara.register_server :webrick do |app, port, host, **options|
|
||||||
require 'rack/handler/webrick'
|
require 'rack/handler/webrick'
|
||||||
Rack::Handler::WEBrick.run(app, {Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log::new(nil, 0)}.merge(options))
|
Rack::Handler::WEBrick.run(app, { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options))
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.register_server :puma do |app, port, host, **options|
|
Capybara.register_server :puma do |app, port, host, **options|
|
||||||
|
@ -476,11 +476,11 @@ Capybara.register_driver :selenium do |app|
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.register_driver :selenium_chrome do |app|
|
Capybara.register_driver :selenium_chrome do |app|
|
||||||
Capybara::Selenium::Driver.new(app, :browser => :chrome)
|
Capybara::Selenium::Driver.new(app, browser: :chrome)
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara.register_driver :selenium_chrome_headless do |app|
|
Capybara.register_driver :selenium_chrome_headless do |app|
|
||||||
browser_options = ::Selenium::WebDriver::Chrome::Options.new()
|
browser_options = ::Selenium::WebDriver::Chrome::Options.new
|
||||||
browser_options.args << '--headless'
|
browser_options.args << '--headless'
|
||||||
browser_options.args << '--disable-gpu'
|
browser_options.args << '--disable-gpu'
|
||||||
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
|
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
|
||||||
|
|
|
@ -22,9 +22,7 @@ module Capybara
|
||||||
@session_options = Capybara::SessionConfig.new
|
@session_options = Capybara::SessionConfig.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def reuse_server=(bool)
|
attr_writer :reuse_server
|
||||||
@reuse_server = bool
|
|
||||||
end
|
|
||||||
|
|
||||||
def threadsafe=(bool)
|
def threadsafe=(bool)
|
||||||
warn "Capybara.threadsafe == true is a BETA feature and may change in future minor versions" if bool
|
warn "Capybara.threadsafe == true is a BETA feature and may change in future minor versions" if bool
|
||||||
|
@ -60,7 +58,7 @@ module Capybara
|
||||||
def server=(name)
|
def server=(name)
|
||||||
name, options = *name if name.is_a? Array
|
name, options = *name if name.is_a? Array
|
||||||
@server = if options
|
@server = if options
|
||||||
Proc.new { |app, port, host| Capybara.servers[name.to_sym].call(app,port,host,options) }
|
proc { |app, port, host| Capybara.servers[name.to_sym].call(app, port, host, options) }
|
||||||
else
|
else
|
||||||
Capybara.servers[name.to_sym]
|
Capybara.servers[name.to_sym]
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,8 +22,6 @@ end
|
||||||
Before do |scenario|
|
Before do |scenario|
|
||||||
scenario.source_tag_names.each do |tag|
|
scenario.source_tag_names.each do |tag|
|
||||||
driver_name = tag.sub(/^@/, '').to_sym
|
driver_name = tag.sub(/^@/, '').to_sym
|
||||||
if Capybara.drivers.has_key?(driver_name)
|
Capybara.current_driver = driver_name if Capybara.drivers.key?(driver_name)
|
||||||
Capybara.current_driver = driver_name
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -103,7 +103,6 @@ class Capybara::Driver::Base
|
||||||
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#no_such_window_error'
|
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#no_such_window_error'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Execute the block, and then accept the modal opened.
|
# Execute the block, and then accept the modal opened.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Capybara
|
module Capybara
|
||||||
|
|
||||||
# @api private
|
# @api private
|
||||||
module Helpers
|
module Helpers
|
||||||
extend self
|
extend self
|
||||||
|
|
|
@ -42,9 +42,8 @@ module Capybara
|
||||||
# @!method assert_no_current_path
|
# @!method assert_no_current_path
|
||||||
# see {Capybara::SessionMatchers#assert_no_current_path}
|
# see {Capybara::SessionMatchers#assert_no_current_path}
|
||||||
|
|
||||||
|
%w[assert_text assert_no_text assert_title assert_no_title assert_current_path assert_no_current_path].each do |assertion_name|
|
||||||
%w(assert_text assert_no_text assert_title assert_no_title assert_current_path assert_no_current_path).each do |assertion_name|
|
class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
|
||||||
self.class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
|
|
||||||
def #{assertion_name} *args
|
def #{assertion_name} *args
|
||||||
self.assertions +=1
|
self.assertions +=1
|
||||||
subject, args = determine_subject(args)
|
subject, args = determine_subject(args)
|
||||||
|
@ -82,10 +81,10 @@ module Capybara
|
||||||
# @!method assert_xpath
|
# @!method assert_xpath
|
||||||
# see {Capybara::Node::Matchers#assert_not_matches_selector}
|
# see {Capybara::Node::Matchers#assert_not_matches_selector}
|
||||||
|
|
||||||
%w(assert_selector assert_no_selector
|
%w[assert_selector assert_no_selector
|
||||||
assert_all_of_selectors assert_none_of_selectors
|
assert_all_of_selectors assert_none_of_selectors
|
||||||
assert_matches_selector assert_not_matches_selector).each do |assertion_name|
|
assert_matches_selector assert_not_matches_selector].each do |assertion_name|
|
||||||
self.class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
|
class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
|
||||||
def #{assertion_name} *args, &optional_filter_block
|
def #{assertion_name} *args, &optional_filter_block
|
||||||
self.assertions +=1
|
self.assertions +=1
|
||||||
subject, args = determine_subject(args)
|
subject, args = determine_subject(args)
|
||||||
|
@ -99,7 +98,7 @@ module Capybara
|
||||||
alias_method :refute_selector, :assert_no_selector
|
alias_method :refute_selector, :assert_no_selector
|
||||||
alias_method :refute_matches_selector, :assert_not_matches_selector
|
alias_method :refute_matches_selector, :assert_not_matches_selector
|
||||||
|
|
||||||
%w(xpath css link button field select table).each do |selector_type|
|
%w[xpath css link button field select table].each do |selector_type|
|
||||||
define_method "assert_#{selector_type}" do |*args, &optional_filter_block|
|
define_method "assert_#{selector_type}" do |*args, &optional_filter_block|
|
||||||
subject, args = determine_subject(args)
|
subject, args = determine_subject(args)
|
||||||
locator, options = extract_locator(args)
|
locator, options = extract_locator(args)
|
||||||
|
@ -114,7 +113,7 @@ module Capybara
|
||||||
alias_method "refute_#{selector_type}", "assert_no_#{selector_type}"
|
alias_method "refute_#{selector_type}", "assert_no_#{selector_type}"
|
||||||
end
|
end
|
||||||
|
|
||||||
%w(checked unchecked).each do |field_type|
|
%w[checked unchecked].each do |field_type|
|
||||||
define_method "assert_#{field_type}_field" do |*args, &optional_filter_block|
|
define_method "assert_#{field_type}_field" do |*args, &optional_filter_block|
|
||||||
subject, args = determine_subject(args)
|
subject, args = determine_subject(args)
|
||||||
locator, options = extract_locator(args)
|
locator, options = extract_locator(args)
|
||||||
|
@ -129,7 +128,7 @@ module Capybara
|
||||||
alias_method "refute_#{field_type}_field", "assert_no_#{field_type}_field"
|
alias_method "refute_#{field_type}_field", "assert_no_#{field_type}_field"
|
||||||
end
|
end
|
||||||
|
|
||||||
%w(xpath css).each do |selector_type|
|
%w[xpath css].each do |selector_type|
|
||||||
define_method "assert_matches_#{selector_type}" do |*args, &optional_filter_block|
|
define_method "assert_matches_#{selector_type}" do |*args, &optional_filter_block|
|
||||||
subject, args = determine_subject(args)
|
subject, args = determine_subject(args)
|
||||||
assert_matches_selector(subject, selector_type.to_sym, *args, &optional_filter_block)
|
assert_matches_selector(subject, selector_type.to_sym, *args, &optional_filter_block)
|
||||||
|
|
|
@ -3,21 +3,22 @@ require 'minitest/spec'
|
||||||
module Capybara
|
module Capybara
|
||||||
module Minitest
|
module Minitest
|
||||||
module Expectations
|
module Expectations
|
||||||
%w(text content title current_path).each do |assertion|
|
%w[text content title current_path].each do |assertion|
|
||||||
infect_an_assertion "assert_#{assertion}", "must_have_#{assertion}", :reverse
|
infect_an_assertion "assert_#{assertion}", "must_have_#{assertion}", :reverse
|
||||||
infect_an_assertion "refute_#{assertion}", "wont_have_#{assertion}", :reverse
|
infect_an_assertion "refute_#{assertion}", "wont_have_#{assertion}", :reverse
|
||||||
end
|
end
|
||||||
|
|
||||||
(%w(selector xpath css link button field select table checked_field unchecked_field).flat_map do |assertion|
|
# rubocop:disable Style/MultilineBlockChain
|
||||||
[["assert_#{assertion}", "must_have_#{assertion}"],
|
(%w[selector xpath css link button field select table checked_field unchecked_field].flat_map do |assertion|
|
||||||
["refute_#{assertion}", "wont_have_#{assertion}"]]
|
[%W[assert_#{assertion} must_have_#{assertion}],
|
||||||
end + [["assert_all_of_selectors", "must_have_all_of_selectors"],
|
%W[refute_#{assertion} wont_have_#{assertion}]]
|
||||||
["assert_none_of_selectors", "must_have_none_of_selectors"]] +
|
end + [%w[assert_all_of_selectors must_have_all_of_selectors],
|
||||||
%w(selector xpath css).flat_map do |assertion|
|
%w[assert_none_of_selectors must_have_none_of_selectors]] +
|
||||||
[["assert_matches_#{assertion}", "must_match_#{assertion}"],
|
%w[selector xpath css].flat_map do |assertion|
|
||||||
["refute_matches_#{assertion}", "wont_match_#{assertion}"]]
|
[%W[assert_matches_#{assertion} must_match_#{assertion}],
|
||||||
|
%W[refute_matches_#{assertion} wont_match_#{assertion}]]
|
||||||
end).each do |(meth, new_name)|
|
end).each do |(meth, new_name)|
|
||||||
self.class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
|
class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
|
||||||
def #{new_name} *args, &block
|
def #{new_name} *args, &block
|
||||||
::Minitest::Expectation.new(self, ::Minitest::Spec.current).#{new_name}(*args, &block)
|
::Minitest::Expectation.new(self, ::Minitest::Spec.current).#{new_name}(*args, &block)
|
||||||
end
|
end
|
||||||
|
@ -29,6 +30,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
ASSERTION
|
ASSERTION
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Style/MultilineBlockChain
|
||||||
|
|
||||||
##
|
##
|
||||||
# Expectation that there is xpath
|
# Expectation that there is xpath
|
||||||
|
@ -159,7 +161,6 @@ module Capybara
|
||||||
#
|
#
|
||||||
# @!method wont_have_current_path
|
# @!method wont_have_current_path
|
||||||
# see {Capybara::SessionMatchers#assert_no_current_path}
|
# see {Capybara::SessionMatchers#assert_no_current_path}
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
module Capybara
|
module Capybara
|
||||||
module Node
|
module Node
|
||||||
module Actions
|
module Actions
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Finds a button or link and clicks it. See {Capybara::Node::Actions#click_button} and
|
# Finds a button or link and clicks it. See {Capybara::Node::Actions#click_button} and
|
||||||
|
@ -82,7 +81,7 @@ module Capybara
|
||||||
#
|
#
|
||||||
# @return [Capybara::Node::Element] The element filled_in
|
# @return [Capybara::Node::Element] The element filled_in
|
||||||
def fill_in(locator = nil, with:, fill_options: {}, **options)
|
def fill_in(locator = nil, with:, fill_options: {}, **options)
|
||||||
options[:with] = options.delete(:currently_with) if options.has_key?(:currently_with)
|
options[:with] = options.delete(:currently_with) if options.key?(:currently_with)
|
||||||
find(:fillable_field, locator, options).set(with, fill_options)
|
find(:fillable_field, locator, options).set(with, fill_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -225,30 +224,30 @@ module Capybara
|
||||||
# @option options [true, Hash] make_visible A Hash of CSS styles to change before attempting to attach the file, if `true` { opacity: 1, display: 'block', visibility: 'visible' } is used (may not be supported by all drivers)
|
# @option options [true, Hash] make_visible A Hash of CSS styles to change before attempting to attach the file, if `true` { opacity: 1, display: 'block', visibility: 'visible' } is used (may not be supported by all drivers)
|
||||||
#
|
#
|
||||||
# @return [Capybara::Node::Element] The file field element
|
# @return [Capybara::Node::Element] The file field element
|
||||||
def attach_file(locator=nil, path, make_visible: nil, **options)
|
def attach_file(locator = nil, path, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments
|
||||||
Array(path).each do |p|
|
Array(path).each do |p|
|
||||||
raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
|
raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
|
||||||
end
|
end
|
||||||
# Allow user to update the CSS style of the file input since they are so often hidden on a page
|
# Allow user to update the CSS style of the file input since they are so often hidden on a page
|
||||||
if make_visible
|
if make_visible
|
||||||
make_visible = { opacity: 1, display: 'block', visibility: 'visible' } if make_visible == true
|
make_visible = { opacity: 1, display: 'block', visibility: 'visible' } if make_visible == true
|
||||||
ff = find(:file_field, locator, options.merge({visible: :all}))
|
ff = find(:file_field, locator, options.merge(visible: :all))
|
||||||
_update_style(ff, make_visible)
|
_update_style(ff, make_visible)
|
||||||
if ff.visible?
|
|
||||||
|
raise ExpectationNotMet, "The style changes in :make_visible did not make the file input visible" unless ff.visible?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
ff.set(path)
|
ff.set(path)
|
||||||
ensure
|
ensure
|
||||||
_reset_style(ff)
|
_reset_style(ff)
|
||||||
end
|
end
|
||||||
else
|
|
||||||
raise ExpectationNotMet, "The style changes in :make_visible did not make the file input visible"
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
find(:file_field, locator, options).set(path)
|
find(:file_field, locator, options).set(path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def _update_style(element, style)
|
def _update_style(element, style)
|
||||||
script = <<-JS
|
script = <<-JS
|
||||||
var el = arguments[0];
|
var el = arguments[0];
|
||||||
|
@ -277,12 +276,12 @@ module Capybara
|
||||||
JS
|
JS
|
||||||
begin
|
begin
|
||||||
session.execute_script(script, element)
|
session.execute_script(script, element)
|
||||||
rescue
|
rescue # swallow extra errors
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def _check_with_label(selector, checked, locator, allow_label_click: session_options.automatic_label_click, **options)
|
def _check_with_label(selector, checked, locator, allow_label_click: session_options.automatic_label_click, **options)
|
||||||
synchronize(Capybara::Queries::BaseQuery::wait(options, session_options.default_max_wait_time)) do
|
synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
|
||||||
begin
|
begin
|
||||||
el = find(selector, locator, options)
|
el = find(selector, locator, options)
|
||||||
el.set(checked)
|
el.set(checked)
|
||||||
|
@ -291,14 +290,13 @@ module Capybara
|
||||||
begin
|
begin
|
||||||
el ||= find(selector, locator, options.merge(visible: :all))
|
el ||= find(selector, locator, options.merge(visible: :all))
|
||||||
label = find(:label, for: el, visible: true)
|
label = find(:label, for: el, visible: true)
|
||||||
label.click unless (el.checked? == checked)
|
label.click unless el.checked? == checked
|
||||||
rescue
|
rescue # swallow extra errors - raise original
|
||||||
raise e
|
raise e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
module Capybara
|
module Capybara
|
||||||
module Node
|
module Node
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# A {Capybara::Node::Base} represents either an element on a page through the subclass
|
# A {Capybara::Node::Base} represents either an element on a page through the subclass
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
module Capybara
|
module Capybara
|
||||||
module Node
|
module Node
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# A {Capybara::Document} represents an HTML document. Any operation
|
# A {Capybara::Document} represents an HTML document. Any operation
|
||||||
|
|
|
@ -64,7 +64,6 @@ module Capybara
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
module Capybara
|
module Capybara
|
||||||
module Node
|
module Node
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# A {Capybara::Node::Element} represents a single element on the page. It is possible
|
# A {Capybara::Node::Element} represents a single element on the page. It is possible
|
||||||
|
@ -23,7 +22,6 @@ module Capybara
|
||||||
# @see Capybara::Node
|
# @see Capybara::Node
|
||||||
#
|
#
|
||||||
class Element < Base
|
class Element < Base
|
||||||
|
|
||||||
def initialize(session, base, query_scope, query)
|
def initialize(session, base, query_scope, query)
|
||||||
super(session, base)
|
super(session, base)
|
||||||
@query_scope = query_scope
|
@query_scope = query_scope
|
||||||
|
@ -397,18 +395,15 @@ module Capybara
|
||||||
rescue NotSupportedByDriverError
|
rescue NotSupportedByDriverError
|
||||||
%(#<Capybara::Node::Element tag="#{base.tag_name}">)
|
%(#<Capybara::Node::Element tag="#{base.tag_name}">)
|
||||||
rescue => e
|
rescue => e
|
||||||
if session.driver.invalid_element_errors.any? { |et| e.is_a?(et)}
|
raise unless session.driver.invalid_element_errors.any? { |et| e.is_a?(et) }
|
||||||
|
|
||||||
%(Obsolete #<Capybara::Node::Element>)
|
%(Obsolete #<Capybara::Node::Element>)
|
||||||
else
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def verify_click_options_support(method)
|
def verify_click_options_support(method)
|
||||||
if base.method(method).arity.zero?
|
raise ArgumentError, "The current driver does not support #{method} options" if base.method(method).arity.zero?
|
||||||
raise ArgumentError, "The current driver does not support #{method} options"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
module Capybara
|
module Capybara
|
||||||
module Node
|
module Node
|
||||||
module Finders
|
module Finders
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Find an {Capybara::Node::Element} based on the given arguments. +find+ will raise an error if the element
|
# Find an {Capybara::Node::Element} based on the given arguments. +find+ will raise an error if the element
|
||||||
|
@ -298,7 +297,7 @@ module Capybara
|
||||||
|
|
||||||
def synced_resolve(query)
|
def synced_resolve(query)
|
||||||
synchronize(query.wait) do
|
synchronize(query.wait) do
|
||||||
if (query.match == :smart or query.match == :prefer_exact)
|
if query.match == :smart or query.match == :prefer_exact
|
||||||
result = query.resolve_for(self, true)
|
result = query.resolve_for(self, true)
|
||||||
result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
|
result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
|
||||||
else
|
else
|
||||||
|
@ -306,11 +305,11 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
if query.match == :one or query.match == :smart and result.size > 1
|
if query.match == :one or query.match == :smart and result.size > 1
|
||||||
raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
|
raise Capybara::Ambiguous, "Ambiguous match, found #{result.size} elements matching #{query.description}"
|
||||||
end
|
|
||||||
if result.empty?
|
|
||||||
raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
raise Capybara::ElementNotFound, "Unable to find #{query.description}" if result.empty?
|
||||||
|
|
||||||
result.first
|
result.first
|
||||||
end.tap(&:allow_reload!)
|
end.tap(&:allow_reload!)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
module Capybara
|
module Capybara
|
||||||
module Node
|
module Node
|
||||||
module Matchers
|
module Matchers
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Checks if a given selector is on the page or a descendant of the current node.
|
# Checks if a given selector is on the page or a descendant of the current node.
|
||||||
|
@ -114,7 +113,7 @@ module Capybara
|
||||||
# @overload assert_all_of_selectors([kind = Capybara.default_selector], *locators, options = {})
|
# @overload assert_all_of_selectors([kind = Capybara.default_selector], *locators, options = {})
|
||||||
#
|
#
|
||||||
def assert_all_of_selectors(*args, wait: session_options.default_max_wait_time, **options, &optional_filter_block)
|
def assert_all_of_selectors(*args, wait: session_options.default_max_wait_time, **options, &optional_filter_block)
|
||||||
selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
|
selector = args.first.is_a?(Symbol) ? args.shift : session_options.default_selector
|
||||||
synchronize(wait) do
|
synchronize(wait) do
|
||||||
args.each do |locator|
|
args.each do |locator|
|
||||||
assert_selector(selector, locator, options, &optional_filter_block)
|
assert_selector(selector, locator, options, &optional_filter_block)
|
||||||
|
@ -138,7 +137,7 @@ module Capybara
|
||||||
# @overload assert_none_of_selectors([kind = Capybara.default_selector], *locators, options = {})
|
# @overload assert_none_of_selectors([kind = Capybara.default_selector], *locators, options = {})
|
||||||
#
|
#
|
||||||
def assert_none_of_selectors(*args, wait: session_options.default_max_wait_time, **options, &optional_filter_block)
|
def assert_none_of_selectors(*args, wait: session_options.default_max_wait_time, **options, &optional_filter_block)
|
||||||
selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
|
selector = args.first.is_a?(Symbol) ? args.shift : session_options.default_selector
|
||||||
synchronize(wait) do
|
synchronize(wait) do
|
||||||
args.each do |locator|
|
args.each do |locator|
|
||||||
assert_no_selector(selector, locator, options, &optional_filter_block)
|
assert_no_selector(selector, locator, options, &optional_filter_block)
|
||||||
|
@ -568,7 +567,6 @@ module Capybara
|
||||||
not_matches_selector?(:css, css, options, &optional_filter_block)
|
not_matches_selector?(:css, css, options, &optional_filter_block)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Asserts that the page or current node has the given text content,
|
# Asserts that the page or current node has the given text content,
|
||||||
# ignoring any HTML tags.
|
# ignoring any HTML tags.
|
||||||
|
@ -658,7 +656,7 @@ module Capybara
|
||||||
alias_method :has_no_content?, :has_no_text?
|
alias_method :has_no_content?, :has_no_text?
|
||||||
|
|
||||||
def ==(other)
|
def ==(other)
|
||||||
self.eql?(other) || (other.respond_to?(:base) && base == other.base)
|
eql?(other) || (other.respond_to?(:base) && base == other.base)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -677,7 +675,7 @@ module Capybara
|
||||||
_set_query_session_options(query_args)
|
_set_query_session_options(query_args)
|
||||||
query = Capybara::Queries::MatchQuery.new(*query_args, &optional_filter_block)
|
query = Capybara::Queries::MatchQuery.new(*query_args, &optional_filter_block)
|
||||||
synchronize(query.wait) do
|
synchronize(query.wait) do
|
||||||
result = query.resolve_for(self.query_scope)
|
result = query.resolve_for(query_scope)
|
||||||
yield result
|
yield result
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
module Capybara
|
module Capybara
|
||||||
module Node
|
module Node
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# A {Capybara::Node::Simple} is a simpler version of {Capybara::Node::Base} which
|
# A {Capybara::Node::Simple} is a simpler version of {Capybara::Node::Base} which
|
||||||
|
@ -85,7 +84,7 @@ module Capybara
|
||||||
option = native.xpath(".//option[@selected='selected']").first || native.xpath(".//option").first
|
option = native.xpath(".//option[@selected='selected']").first || native.xpath(".//option").first
|
||||||
option[:value] || option.content if option
|
option[:value] || option.content if option
|
||||||
end
|
end
|
||||||
elsif tag_name == 'input' && %w(radio checkbox).include?(native[:type])
|
elsif tag_name == 'input' && %w[radio checkbox].include?(native[:type])
|
||||||
native[:value] || 'on'
|
native[:value] || 'on'
|
||||||
else
|
else
|
||||||
native[:value]
|
native[:value]
|
||||||
|
@ -106,8 +105,8 @@ module Capybara
|
||||||
if check_ancestors
|
if check_ancestors
|
||||||
!native.xpath("boolean(./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head'])")
|
!native.xpath("boolean(./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head'])")
|
||||||
else
|
else
|
||||||
#no need for an xpath if only checking the current element
|
# No need for an xpath if only checking the current element
|
||||||
!(native.has_attribute?('hidden') || (native[:style] =~ /display:\s?none/) || %w(script head).include?(tag_name))
|
!(native.has_attribute?('hidden') || (native[:style] =~ /display:\s?none/) || %w[script head].include?(tag_name))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ module Capybara
|
||||||
# Returns false if query does not have any count options specified.
|
# Returns false if query does not have any count options specified.
|
||||||
#
|
#
|
||||||
def expects_none?
|
def expects_none?
|
||||||
if COUNT_KEYS.any? { |k| options.has_key? k }
|
if COUNT_KEYS.any? { |k| options.key? k }
|
||||||
matches_count?(0)
|
matches_count?(0)
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
@ -51,7 +51,7 @@ module Capybara
|
||||||
return (Integer(options[:count]) == count) if options[:count]
|
return (Integer(options[:count]) == count) if options[:count]
|
||||||
return false if options[:maximum] && (Integer(options[:maximum]) < count)
|
return false if options[:maximum] && (Integer(options[:maximum]) < count)
|
||||||
return false if options[:minimum] && (Integer(options[:minimum]) > count)
|
return false if options[:minimum] && (Integer(options[:minimum]) > count)
|
||||||
return false if options[:between] && !(options[:between] === count)
|
return false if options[:between] && !options[:between].include?(count)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ module Capybara
|
||||||
private
|
private
|
||||||
|
|
||||||
def count_message
|
def count_message
|
||||||
message = String.new()
|
message = "".dup
|
||||||
if options[:count]
|
if options[:count]
|
||||||
message << " #{options[:count]} #{Capybara::Helpers.declension('time', 'times', options[:count])}"
|
message << " #{options[:count]} #{Capybara::Helpers.declension('time', 'times', options[:count])}"
|
||||||
elsif options[:between]
|
elsif options[:between]
|
||||||
|
@ -85,7 +85,8 @@ module Capybara
|
||||||
|
|
||||||
def assert_valid_keys
|
def assert_valid_keys
|
||||||
invalid_keys = @options.keys - valid_keys
|
invalid_keys = @options.keys - valid_keys
|
||||||
unless invalid_keys.empty?
|
return if invalid_keys.empty?
|
||||||
|
|
||||||
invalid_names = invalid_keys.map(&:inspect).join(", ")
|
invalid_names = invalid_keys.map(&:inspect).join(", ")
|
||||||
valid_names = valid_keys.map(&:inspect).join(", ")
|
valid_names = valid_keys.map(&:inspect).join(", ")
|
||||||
raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
|
raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
|
||||||
|
@ -93,4 +94,3 @@ module Capybara
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ module Capybara
|
||||||
def valid_keys
|
def valid_keys
|
||||||
%i[wait url ignore_query]
|
%i[wait url ignore_query]
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@ module Capybara
|
||||||
module Queries
|
module Queries
|
||||||
class MatchQuery < Capybara::Queries::SelectorQuery
|
class MatchQuery < Capybara::Queries::SelectorQuery
|
||||||
def visible
|
def visible
|
||||||
if options.has_key?(:visible)
|
if options.key?(:visible)
|
||||||
super
|
super
|
||||||
else
|
else
|
||||||
:all
|
:all
|
||||||
|
|
|
@ -38,7 +38,7 @@ module Capybara
|
||||||
def label; selector.label or selector.name; end
|
def label; selector.label or selector.name; end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
@description = String.new()
|
@description = "".dup
|
||||||
@description << "visible " if visible == :visible
|
@description << "visible " if visible == :visible
|
||||||
@description << "non-visible " if visible == :hidden
|
@description << "non-visible " if visible == :hidden
|
||||||
@description << "#{label} #{locator.inspect}"
|
@description << "#{label} #{locator.inspect}"
|
||||||
|
@ -53,8 +53,8 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def matches_filters?(node)
|
def matches_filters?(node)
|
||||||
return false unless matches_text_filter(node, options[:text]) if options[:text]
|
return false if options[:text] && !matches_text_filter(node, options[:text])
|
||||||
return false unless matches_exact_text_filter(node, exact_text) if exact_text.is_a?(String)
|
return false if exact_text.is_a?(String) && !matches_exact_text_filter(node, exact_text)
|
||||||
|
|
||||||
case visible
|
case visible
|
||||||
when :visible then return false unless node.visible?
|
when :visible then return false unless node.visible?
|
||||||
|
@ -62,7 +62,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
res = node_filters.all? do |name, filter|
|
res = node_filters.all? do |name, filter|
|
||||||
if options.has_key?(name)
|
if options.key?(name)
|
||||||
filter.matches?(node, options[name])
|
filter.matches?(node, options[name])
|
||||||
elsif filter.default?
|
elsif filter.default?
|
||||||
filter.matches?(node, filter.default)
|
filter.matches?(node, filter.default)
|
||||||
|
@ -71,11 +71,13 @@ module Capybara
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if @filter_block
|
||||||
res &&= if node.respond_to?(:session)
|
res &&= if node.respond_to?(:session)
|
||||||
node.session.using_wait_time(0) { @filter_block.call(node) }
|
node.session.using_wait_time(0) { @filter_block.call(node) }
|
||||||
else
|
else
|
||||||
@filter_block.call(node)
|
@filter_block.call(node)
|
||||||
end unless @filter_block.nil?
|
end
|
||||||
|
end
|
||||||
|
|
||||||
res
|
res
|
||||||
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
||||||
|
@ -91,7 +93,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def exact?
|
def exact?
|
||||||
return false if !supports_exact?
|
return false unless supports_exact?
|
||||||
options.fetch(:exact, session_options.exact)
|
options.fetch(:exact, session_options.exact)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -100,13 +102,9 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def xpath(exact = nil)
|
def xpath(exact = nil)
|
||||||
exact = self.exact? if exact.nil?
|
exact = exact? if exact.nil?
|
||||||
expr = apply_expression_filters(@expression)
|
expr = apply_expression_filters(@expression)
|
||||||
expr = if expr.respond_to?(:to_xpath) and exact
|
expr = exact ? expr.to_xpath(:exact) : expr.to_s if expr.respond_to?(:to_xpath)
|
||||||
expr.to_xpath(:exact)
|
|
||||||
else
|
|
||||||
expr.to_s
|
|
||||||
end
|
|
||||||
filtered_xpath(expr)
|
filtered_xpath(expr)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -119,9 +117,9 @@ module Capybara
|
||||||
@resolved_node = node
|
@resolved_node = node
|
||||||
node.synchronize do
|
node.synchronize do
|
||||||
children = if selector.format == :css
|
children = if selector.format == :css
|
||||||
node.find_css(self.css)
|
node.find_css(css)
|
||||||
else
|
else
|
||||||
node.find_xpath(self.xpath(exact))
|
node.find_xpath(xpath(exact))
|
||||||
end.map do |child|
|
end.map do |child|
|
||||||
if node.is_a?(Capybara::Node::Base)
|
if node.is_a?(Capybara::Node::Base)
|
||||||
Capybara::Node::Element.new(node.session, child, node, self)
|
Capybara::Node::Element.new(node.session, child, node, self)
|
||||||
|
@ -145,7 +143,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def node_filters
|
def node_filters
|
||||||
if options.has_key?(:filter_set)
|
if options.key?(:filter_set)
|
||||||
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
|
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
|
||||||
else
|
else
|
||||||
@selector.node_filters
|
@selector.node_filters
|
||||||
|
@ -154,7 +152,7 @@ module Capybara
|
||||||
|
|
||||||
def expression_filters
|
def expression_filters
|
||||||
filters = @selector.expression_filters
|
filters = @selector.expression_filters
|
||||||
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.has_key?(:filter_set)
|
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.key?(:filter_set)
|
||||||
filters
|
filters
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -170,10 +168,10 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def filtered_xpath(expr)
|
def filtered_xpath(expr)
|
||||||
if options.has_key?(:id) || options.has_key?(:class)
|
if options.key?(:id) || options.key?(:class)
|
||||||
expr = "(#{expr})"
|
expr = "(#{expr})"
|
||||||
expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.has_key?(:id) && !custom_keys.include?(:id)
|
expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.key?(:id) && !custom_keys.include?(:id)
|
||||||
if options.has_key?(:class) && !custom_keys.include?(:class)
|
if options.key?(:class) && !custom_keys.include?(:class)
|
||||||
class_xpath = Array(options[:class]).map do |klass|
|
class_xpath = Array(options[:class]).map do |klass|
|
||||||
XPath.attr(:class).contains_word(klass)
|
XPath.attr(:class).contains_word(klass)
|
||||||
end.reduce(:&)
|
end.reduce(:&)
|
||||||
|
@ -184,11 +182,11 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def filtered_css(expr)
|
def filtered_css(expr)
|
||||||
if options.has_key?(:id) || options.has_key?(:class)
|
if options.key?(:id) || options.key?(:class)
|
||||||
css_selectors = expr.split(',').map(&:rstrip)
|
css_selectors = expr.split(',').map(&:rstrip)
|
||||||
expr = css_selectors.map do |sel|
|
expr = css_selectors.map do |sel|
|
||||||
sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if options.has_key?(:id) && !custom_keys.include?(:id)
|
sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if options.key?(:id) && !custom_keys.include?(:id)
|
||||||
sel += Array(options[:class]).map { |k| ".#{Capybara::Selector::CSS.escape(k)}"}.join if options.has_key?(:class) && !custom_keys.include?(:class)
|
sel += Array(options[:class]).map { |k| ".#{Capybara::Selector::CSS.escape(k)}" }.join if options.key?(:class) && !custom_keys.include?(:class)
|
||||||
sel
|
sel
|
||||||
end.join(", ")
|
end.join(", ")
|
||||||
end
|
end
|
||||||
|
@ -197,7 +195,7 @@ module Capybara
|
||||||
|
|
||||||
def apply_expression_filters(expr)
|
def apply_expression_filters(expr)
|
||||||
expression_filters.inject(expr) do |memo, (name, ef)|
|
expression_filters.inject(expr) do |memo, (name, ef)|
|
||||||
if options.has_key?(name)
|
if options.key?(name)
|
||||||
ef.apply_filter(memo, options[name])
|
ef.apply_filter(memo, options[name])
|
||||||
elsif ef.default?
|
elsif ef.default?
|
||||||
ef.apply_filter(memo, ef.default)
|
ef.apply_filter(memo, ef.default)
|
||||||
|
@ -208,10 +206,9 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def warn_exact_usage
|
def warn_exact_usage
|
||||||
if options.has_key?(:exact) && !supports_exact?
|
return unless options.key?(:exact) && !supports_exact?
|
||||||
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
|
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def exact_text
|
def exact_text
|
||||||
options.fetch(:exact_text, session_options.exact_text)
|
options.fetch(:exact_text, session_options.exact_text)
|
||||||
|
|
|
@ -4,9 +4,9 @@ module Capybara
|
||||||
# @api private
|
# @api private
|
||||||
module Queries
|
module Queries
|
||||||
class TextQuery < BaseQuery
|
class TextQuery < BaseQuery
|
||||||
def initialize(type=nil, expected_text, session_options:, **options)
|
def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
|
||||||
@type = type
|
@type = type
|
||||||
@type = Capybara.ignore_hidden_elements or Capybara.visible_text_only ? :visible : :all if @type.nil?
|
@type = Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all if @type.nil?
|
||||||
@expected_text = expected_text
|
@expected_text = expected_text
|
||||||
@options = options
|
@options = options
|
||||||
super(@options)
|
super(@options)
|
||||||
|
@ -47,7 +47,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_message(report_on_invisible)
|
def build_message(report_on_invisible)
|
||||||
message = String.new()
|
message = "".dup
|
||||||
unless (COUNT_KEYS & @options.keys).empty?
|
unless (COUNT_KEYS & @options.keys).empty?
|
||||||
message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
|
message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
|
||||||
end
|
end
|
||||||
|
@ -70,8 +70,7 @@ module Capybara
|
||||||
if invisible_count != @count
|
if invisible_count != @count
|
||||||
details_message << "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
details_message << "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
||||||
end
|
end
|
||||||
rescue
|
rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
|
||||||
# An error getting the non-visible text (if element goes out of scope) should not affect the response
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -29,24 +29,25 @@ class Capybara::RackTest::Browser
|
||||||
end
|
end
|
||||||
|
|
||||||
def submit(method, path, attributes)
|
def submit(method, path, attributes)
|
||||||
path = request_path if not path or path.empty?
|
path = request_path if path.nil? || path.empty?
|
||||||
process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
|
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow(method, path, **attributes)
|
def follow(method, path, **attributes)
|
||||||
return if path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
|
return if path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
|
||||||
process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
|
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
||||||
process(method, path, attributes, env)
|
process(method, path, attributes, env)
|
||||||
if driver.follow_redirects?
|
|
||||||
|
return unless driver.follow_redirects?
|
||||||
|
|
||||||
driver.redirect_limit.times do
|
driver.redirect_limit.times do
|
||||||
process(:get, last_response["Location"], {}, env) if last_response.redirect?
|
process(:get, last_response["Location"], {}, env) if last_response.redirect?
|
||||||
end
|
end
|
||||||
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
|
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def process(method, path, attributes = {}, env = {})
|
def process(method, path, attributes = {}, env = {})
|
||||||
new_uri = URI.parse(path)
|
new_uri = URI.parse(path)
|
||||||
|
@ -56,7 +57,7 @@ class Capybara::RackTest::Browser
|
||||||
else
|
else
|
||||||
new_uri.path = request_path if path.start_with?("?")
|
new_uri.path = request_path if path.start_with?("?")
|
||||||
new_uri.path = "/" if new_uri.path.empty?
|
new_uri.path = "/" if new_uri.path.empty?
|
||||||
new_uri.path = request_path.sub(%r(/[^/]*$), '/') + new_uri.path unless new_uri.path.start_with?('/')
|
new_uri.path = request_path.sub(%r{/[^/]*$}, '/') + new_uri.path unless new_uri.path.start_with?('/')
|
||||||
end
|
end
|
||||||
new_uri.scheme ||= @current_scheme
|
new_uri.scheme ||= @current_scheme
|
||||||
new_uri.host ||= @current_host
|
new_uri.host ||= @current_host
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
class Capybara::RackTest::CSSHandlers < BasicObject
|
class Capybara::RackTest::CSSHandlers < BasicObject
|
||||||
include ::Kernel
|
include ::Kernel
|
||||||
|
|
||||||
def disabled list
|
def disabled(list)
|
||||||
list.find_all { |node| node.has_attribute? 'disabled' }
|
list.find_all { |node| node.has_attribute? 'disabled' }
|
||||||
end
|
end
|
||||||
def enabled list
|
|
||||||
|
def enabled(list)
|
||||||
list.find_all { |node| !node.has_attribute? 'disabled' }
|
list.find_all { |node| !node.has_attribute? 'disabled' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -82,12 +82,12 @@ private
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_input_param(field, params)
|
def add_input_param(field, params)
|
||||||
if %w(radio checkbox).include? field['type']
|
if %w[radio checkbox].include? field['type']
|
||||||
if field['checked']
|
if field['checked']
|
||||||
node=Capybara::RackTest::Node.new(self.driver, field)
|
node = Capybara::RackTest::Node.new(driver, field)
|
||||||
merge_param!(params, field['name'].to_s, node.value.to_s)
|
merge_param!(params, field['name'].to_s, node.value.to_s)
|
||||||
end
|
end
|
||||||
elsif %w(submit image).include? field['type']
|
elsif %w[submit image].include? field['type']
|
||||||
# TO DO identify the click button here (in document order, rather
|
# TO DO identify the click button here (in document order, rather
|
||||||
# than leaving until the end of the params)
|
# than leaving until the end of the params)
|
||||||
elsif field['type'] == 'file'
|
elsif field['type'] == 'file'
|
||||||
|
|
|
@ -20,8 +20,8 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
||||||
def set(value)
|
def set(value)
|
||||||
return if disabled? || readonly?
|
return if disabled? || readonly?
|
||||||
|
|
||||||
if (Array === value) && !multiple?
|
if value.is_a?(Array) && !multiple?
|
||||||
raise TypeError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
raise TypeError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
||||||
end
|
end
|
||||||
|
|
||||||
if radio?
|
if radio?
|
||||||
|
@ -55,13 +55,13 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
||||||
method = self["data-method"] if driver.options[:respect_data_method]
|
method = self["data-method"] if driver.options[:respect_data_method]
|
||||||
method ||= :get
|
method ||= :get
|
||||||
driver.follow(method, self[:href].to_s)
|
driver.follow(method, self[:href].to_s)
|
||||||
elsif (tag_name == 'input' and %w(submit image).include?(type)) or
|
elsif (tag_name == 'input' and %w[submit image].include?(type)) or
|
||||||
((tag_name == 'button') and type.nil? or type == "submit")
|
(tag_name == 'button' and [nil, "submit"].include?(type))
|
||||||
associated_form = form
|
associated_form = form
|
||||||
Capybara::RackTest::Form.new(driver, associated_form).submit(self) if associated_form
|
Capybara::RackTest::Form.new(driver, associated_form).submit(self) if associated_form
|
||||||
elsif (tag_name == 'input' and %w(checkbox radio).include?(type))
|
elsif tag_name == 'input' and %w[checkbox radio].include?(type)
|
||||||
set(!checked?)
|
set(!checked?)
|
||||||
elsif (tag_name == 'label')
|
elsif tag_name == 'label'
|
||||||
labelled_control = if native[:for]
|
labelled_control = if native[:for]
|
||||||
find_xpath("//input[@id='#{native[:for]}']").first
|
find_xpath("//input[@id='#{native[:for]}']").first
|
||||||
else
|
else
|
||||||
|
@ -91,7 +91,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
||||||
end
|
end
|
||||||
|
|
||||||
def disabled?
|
def disabled?
|
||||||
if %w(option optgroup).include? tag_name
|
if %w[option optgroup].include? tag_name
|
||||||
string_node.disabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
string_node.disabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
||||||
else
|
else
|
||||||
string_node.disabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
string_node.disabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
||||||
|
@ -153,13 +153,13 @@ private
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_radio(_value)
|
def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
|
||||||
other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name) == self[:name]] }.to_s
|
other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name) == self[:name]] }.to_s
|
||||||
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
|
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
|
||||||
native['checked'] = 'checked'
|
native['checked'] = 'checked'
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_checkbox(value)
|
def set_checkbox(value) # rubocop:disable Naming/AccessorMethodName
|
||||||
if value && !native['checked']
|
if value && !native['checked']
|
||||||
native['checked'] = 'checked'
|
native['checked'] = 'checked'
|
||||||
elsif !value && native['checked']
|
elsif !value && native['checked']
|
||||||
|
@ -167,13 +167,13 @@ private
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_input(value)
|
def set_input(value) # rubocop:disable Naming/AccessorMethodName
|
||||||
if text_or_password? && attribute_is_not_blank?(:maxlength)
|
if text_or_password? && attribute_is_not_blank?(:maxlength)
|
||||||
# Browser behavior for maxlength="0" is inconsistent, so we stick with
|
# Browser behavior for maxlength="0" is inconsistent, so we stick with
|
||||||
# Firefox, allowing no input
|
# Firefox, allowing no input
|
||||||
value = value.to_s[0...self[:maxlength].to_i]
|
value = value.to_s[0...self[:maxlength].to_i]
|
||||||
end
|
end
|
||||||
if Array === value #Assert multiple attribute is present
|
if value.is_a?(Array) # Assert multiple attribute is present
|
||||||
value.each do |v|
|
value.each do |v|
|
||||||
new_native = native.clone
|
new_native = native.clone
|
||||||
new_native.remove_attribute('value')
|
new_native.remove_attribute('value')
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
require 'forwardable'
|
require 'forwardable'
|
||||||
|
|
||||||
module Capybara
|
module Capybara
|
||||||
|
|
||||||
##
|
##
|
||||||
# A {Capybara::Result} represents a collection of {Capybara::Node::Element} on the page. It is possible to interact with this
|
# A {Capybara::Result} represents a collection of {Capybara::Node::Element} on the page. It is possible to interact with this
|
||||||
# collection similar to an Array because it implements Enumerable and offers the following Array methods through delegation:
|
# collection similar to an Array because it implements Enumerable and offers the following Array methods through delegation:
|
||||||
|
@ -100,7 +99,7 @@ module Capybara
|
||||||
break if @result_cache.size > max
|
break if @result_cache.size > max
|
||||||
@result_cache << @results_enum.next
|
@result_cache << @results_enum.next
|
||||||
end
|
end
|
||||||
return 0 if (@query.options[:between] === @result_cache.size)
|
return 0 if @query.options[:between].include?(@result_cache.size)
|
||||||
return -1 if @result_cache.size < min
|
return -1 if @result_cache.size < min
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,9 +7,9 @@ require 'capybara/rspec/features'
|
||||||
require 'capybara/rspec/matcher_proxies'
|
require 'capybara/rspec/matcher_proxies'
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
config.include Capybara::DSL, :type => :feature
|
config.include Capybara::DSL, type: :feature
|
||||||
config.include Capybara::RSpecMatchers, :type => :feature
|
config.include Capybara::RSpecMatchers, type: :feature
|
||||||
config.include Capybara::RSpecMatchers, :type => :view
|
config.include Capybara::RSpecMatchers, type: :view
|
||||||
|
|
||||||
# The before and after blocks must run instantaneously, because Capybara
|
# The before and after blocks must run instantaneously, because Capybara
|
||||||
# might not actually be used in all examples where it's included.
|
# might not actually be used in all examples where it's included.
|
||||||
|
@ -28,4 +28,3 @@ RSpec.configure do |config|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ module Capybara
|
||||||
Capybara::RSpecMatchers::Compound::Or.new(self, matcher)
|
Capybara::RSpecMatchers::Compound::Or.new(self, matcher)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
class CapybaraEvaluator
|
class CapybaraEvaluator
|
||||||
def initialize(actual, matcher1, matcher2)
|
def initialize(actual, matcher1, matcher2)
|
||||||
@actual = actual
|
@actual = actual
|
||||||
|
@ -34,16 +33,15 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
class And < ::RSpec::Matchers::BuiltIn::Compound::And
|
class And < ::RSpec::Matchers::BuiltIn::Compound::And
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def match(_expected, actual)
|
def match(_expected, actual)
|
||||||
@evaluator = CapybaraEvaluator.new(actual, matcher1, matcher2)
|
@evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
|
||||||
syncer = sync_element(actual)
|
syncer = sync_element(actual)
|
||||||
begin
|
begin
|
||||||
syncer.synchronize do
|
syncer.synchronize do
|
||||||
@evaluator.reset
|
@evaluator.reset
|
||||||
raise ::Capybara::ElementNotFound unless [matcher1_matches?, matcher2_matches?].all?
|
raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].all?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
|
@ -63,16 +61,15 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
class Or < ::RSpec::Matchers::BuiltIn::Compound::Or
|
class Or < ::RSpec::Matchers::BuiltIn::Compound::Or
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def match(_expected, actual)
|
def match(_expected, actual)
|
||||||
@evaluator = CapybaraEvaluator.new(actual, matcher1, matcher2)
|
@evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
|
||||||
syncer = sync_element(actual)
|
syncer = sync_element(actual)
|
||||||
begin
|
begin
|
||||||
syncer.synchronize do
|
syncer.synchronize do
|
||||||
@evaluator.reset
|
@evaluator.reset
|
||||||
raise ::Capybara::ElementNotFound unless [matcher1_matches?, matcher2_matches?].any?
|
raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].any?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
require 'capybara/selector/selector'
|
require 'capybara/selector/selector'
|
||||||
Capybara::Selector::FilterSet.add(:_field) do
|
Capybara::Selector::FilterSet.add(:_field) do
|
||||||
filter(:checked, :boolean) { |node, value| not(value ^ node.checked?) }
|
filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
|
||||||
filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
|
filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
|
||||||
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
||||||
filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
|
filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
|
||||||
|
|
||||||
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name) == val] }
|
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name) == val] }
|
||||||
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder) == val] }
|
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder) == val] }
|
||||||
|
|
||||||
describe do |checked: nil, unchecked: nil, disabled: nil, multiple: nil, **_options|
|
describe do |checked: nil, unchecked: nil, disabled: nil, multiple: nil, **_options|
|
||||||
desc, states = String.new, []
|
desc, states = "".dup, []
|
||||||
states << 'checked' if checked || (unchecked == false)
|
states << 'checked' if checked || (unchecked == false)
|
||||||
states << 'not checked' if unchecked || (checked == false)
|
states << 'not checked' if unchecked || (checked == false)
|
||||||
states << 'disabled' if disabled == true
|
states << 'disabled' if disabled == true
|
||||||
|
@ -80,7 +80,7 @@ Capybara.add_selector(:field) do
|
||||||
|
|
||||||
expression_filter(:type) do |expr, type|
|
expression_filter(:type) do |expr, type|
|
||||||
type = type.to_s
|
type = type.to_s
|
||||||
if ['textarea', 'select'].include?(type)
|
if %w[textarea select].include?(type)
|
||||||
expr.self(type.to_sym)
|
expr.self(type.to_sym)
|
||||||
else
|
else
|
||||||
expr[XPath.attr(:type) == type]
|
expr[XPath.attr(:type) == type]
|
||||||
|
@ -89,15 +89,15 @@ Capybara.add_selector(:field) do
|
||||||
|
|
||||||
filter_set(:_field) # checked/unchecked/disabled/multiple/name/placeholder
|
filter_set(:_field) # checked/unchecked/disabled/multiple/name/placeholder
|
||||||
|
|
||||||
filter(:readonly, :boolean) { |node, value| not(value ^ node.readonly?) }
|
filter(:readonly, :boolean) { |node, value| !(value ^ node.readonly?) }
|
||||||
filter(:with) do |node, with|
|
filter(:with) do |node, with|
|
||||||
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
||||||
end
|
end
|
||||||
describe do |type: nil, **options|
|
describe do |type: nil, **options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
(expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.has_key?(ef) }
|
(expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.key?(ef) }
|
||||||
desc << " of type #{type.inspect}" if type
|
desc << " of type #{type.inspect}" if type
|
||||||
desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
|
desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
||||||
desc
|
desc
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -167,7 +167,7 @@ Capybara.add_selector(:link) do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe do |**options|
|
describe do |**options|
|
||||||
desc = String.new()
|
desc = "".dup
|
||||||
desc << " with href #{options[:href].inspect}" if options[:href]
|
desc << " with href #{options[:href].inspect}" if options[:href]
|
||||||
desc << " with no href attribute" if options.fetch(:href, true).nil?
|
desc << " with no href attribute" if options.fetch(:href, true).nil?
|
||||||
end
|
end
|
||||||
|
@ -211,10 +211,10 @@ Capybara.add_selector(:button) do
|
||||||
res_xpath
|
res_xpath
|
||||||
end
|
end
|
||||||
|
|
||||||
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
||||||
|
|
||||||
describe do |disabled: nil, **options|
|
describe do |disabled: nil, **options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << " that is disabled" if disabled == true
|
desc << " that is disabled" if disabled == true
|
||||||
desc << describe_all_expression_filters(options)
|
desc << describe_all_expression_filters(options)
|
||||||
desc
|
desc
|
||||||
|
@ -231,7 +231,7 @@ Capybara.add_selector(:link_or_button) do
|
||||||
self.class.all.values_at(:link, :button).map { |selector| selector.xpath.call(locator, options) }.reduce(:union)
|
self.class.all.values_at(:link, :button).map { |selector| selector.xpath.call(locator, options) }.reduce(:union)
|
||||||
end
|
end
|
||||||
|
|
||||||
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or !(value ^ node.disabled?) }
|
||||||
|
|
||||||
describe { |disabled: nil, **_options| " that is disabled" if disabled == true }
|
describe { |disabled: nil, **_options| " that is disabled" if disabled == true }
|
||||||
end
|
end
|
||||||
|
@ -274,9 +274,9 @@ Capybara.add_selector(:fillable_field) do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe do |options|
|
describe do |options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << describe_all_expression_filters(options)
|
desc << describe_all_expression_filters(options)
|
||||||
desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
|
desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
||||||
desc
|
desc
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -307,7 +307,7 @@ Capybara.add_selector(:radio_button) do
|
||||||
filter(:option) { |node, value| node.value == value.to_s }
|
filter(:option) { |node, value| node.value == value.to_s }
|
||||||
|
|
||||||
describe do |option: nil, **options|
|
describe do |option: nil, **options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << " with value #{option.inspect}" if option
|
desc << " with value #{option.inspect}" if option
|
||||||
desc << describe_all_expression_filters(options)
|
desc << describe_all_expression_filters(options)
|
||||||
desc
|
desc
|
||||||
|
@ -328,7 +328,6 @@ end
|
||||||
# @filter [String] :option Match the value
|
# @filter [String] :option Match the value
|
||||||
#
|
#
|
||||||
Capybara.add_selector(:checkbox) do
|
Capybara.add_selector(:checkbox) do
|
||||||
|
|
||||||
xpath do |locator, **options|
|
xpath do |locator, **options|
|
||||||
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'checkbox']
|
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'checkbox']
|
||||||
locate_field(xpath, locator, options)
|
locate_field(xpath, locator, options)
|
||||||
|
@ -339,7 +338,7 @@ Capybara.add_selector(:checkbox) do
|
||||||
filter(:option) { |node, value| node.value == value.to_s }
|
filter(:option) { |node, value| node.value == value.to_s }
|
||||||
|
|
||||||
describe do |option: nil, **options|
|
describe do |option: nil, **options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << " with value #{option.inspect}" if option
|
desc << " with value #{option.inspect}" if option
|
||||||
desc << describe_all_expression_filters(options)
|
desc << describe_all_expression_filters(options)
|
||||||
desc
|
desc
|
||||||
|
@ -374,7 +373,7 @@ Capybara.add_selector(:select) do
|
||||||
|
|
||||||
filter(:options) do |node, options|
|
filter(:options) do |node, options|
|
||||||
actual = if node.visible?
|
actual = if node.visible?
|
||||||
node.all(:xpath, './/option', wait: false).map { |option| option.text }
|
node.all(:xpath, './/option', wait: false).map(&:text)
|
||||||
else
|
else
|
||||||
node.all(:xpath, './/option', visible: false, wait: false).map { |option| option.text(:all) }
|
node.all(:xpath, './/option', visible: false, wait: false).map { |option| option.text(:all) }
|
||||||
end
|
end
|
||||||
|
@ -383,9 +382,7 @@ Capybara.add_selector(:select) do
|
||||||
|
|
||||||
filter(:with_options) do |node, options|
|
filter(:with_options) do |node, options|
|
||||||
finder_settings = { minimum: 0 }
|
finder_settings = { minimum: 0 }
|
||||||
if !node.visible?
|
finder_settings[:visible] = false unless node.visible?
|
||||||
finder_settings[:visible] = false
|
|
||||||
end
|
|
||||||
options.all? { |option| node.first(:option, option, finder_settings) }
|
options.all? { |option| node.first(:option, option, finder_settings) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -400,7 +397,7 @@ Capybara.add_selector(:select) do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe do |options: nil, with_options: nil, selected: nil, with_selected: nil, **opts|
|
describe do |options: nil, with_options: nil, selected: nil, with_selected: nil, **opts|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << " with options #{options.inspect}" if options
|
desc << " with options #{options.inspect}" if options
|
||||||
desc << " with at least options #{with_options.inspect}" if with_options
|
desc << " with at least options #{with_options.inspect}" if with_options
|
||||||
desc << " with #{selected.inspect} selected" if selected
|
desc << " with #{selected.inspect} selected" if selected
|
||||||
|
@ -425,13 +422,13 @@ Capybara.add_selector(:option) do
|
||||||
xpath
|
xpath
|
||||||
end
|
end
|
||||||
|
|
||||||
filter(:disabled, :boolean) { |node, value| not(value ^ node.disabled?) }
|
filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
||||||
filter(:selected, :boolean) { |node, value| not(value ^ node.selected?) }
|
filter(:selected, :boolean) { |node, value| !(value ^ node.selected?) }
|
||||||
|
|
||||||
describe do |**options|
|
describe do |**options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << " that is#{' not' unless options[:disabled]} disabled" if options.has_key?(:disabled)
|
desc << " that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
|
||||||
desc << " that is#{' not' unless options[:selected]} selected" if options.has_key?(:selected)
|
desc << " that is#{' not' unless options[:selected]} selected" if options.key?(:selected)
|
||||||
desc
|
desc
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -457,7 +454,7 @@ Capybara.add_selector(:file_field) do
|
||||||
filter_set(:_field, %i[disabled multiple name])
|
filter_set(:_field, %i[disabled multiple name])
|
||||||
|
|
||||||
describe do |**options|
|
describe do |**options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << describe_all_expression_filters(options)
|
desc << describe_all_expression_filters(options)
|
||||||
desc
|
desc
|
||||||
end
|
end
|
||||||
|
@ -475,9 +472,9 @@ Capybara.add_selector(:label) do
|
||||||
xpath(:for) do |locator, options|
|
xpath(:for) do |locator, options|
|
||||||
xpath = XPath.descendant(:label)
|
xpath = XPath.descendant(:label)
|
||||||
xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)] unless locator.nil?
|
xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)] unless locator.nil?
|
||||||
if options.has_key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
if options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
||||||
with_attr = XPath.attr(:for).equals(options[:for].to_s)
|
with_attr = XPath.attr(:for).equals(options[:for].to_s)
|
||||||
labelable_elements = %i(button input keygen meter output progress select textarea)
|
labelable_elements = %i[button input keygen meter output progress select textarea]
|
||||||
wrapped = !XPath.attr(:for) &
|
wrapped = !XPath.attr(:for) &
|
||||||
XPath.descendant(*labelable_elements)[XPath.attr(:id) == options[:for].to_s]
|
XPath.descendant(*labelable_elements)[XPath.attr(:id) == options[:for].to_s]
|
||||||
xpath = xpath[with_attr | wrapped]
|
xpath = xpath[with_attr | wrapped]
|
||||||
|
@ -499,7 +496,7 @@ Capybara.add_selector(:label) do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe do |**options|
|
describe do |**options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << " for #{options[:for]}" if options[:for]
|
desc << " for #{options[:for]}" if options[:for]
|
||||||
desc
|
desc
|
||||||
end
|
end
|
||||||
|
@ -523,7 +520,7 @@ Capybara.add_selector(:table) do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe do |caption: nil, **_options|
|
describe do |caption: nil, **_options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << " with caption #{caption}" if caption
|
desc << " with caption #{caption}" if caption
|
||||||
desc
|
desc
|
||||||
end
|
end
|
||||||
|
@ -547,7 +544,7 @@ Capybara.add_selector(:frame) do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe do |name: nil, **_options|
|
describe do |name: nil, **_options|
|
||||||
desc = String.new
|
desc = "".dup
|
||||||
desc << " with name #{name}" if name
|
desc << " with name #{name}" if name
|
||||||
desc
|
desc
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,20 +2,16 @@ module Capybara
|
||||||
class Selector
|
class Selector
|
||||||
class CSS
|
class CSS
|
||||||
def self.escape(str)
|
def self.escape(str)
|
||||||
out = String.new("")
|
out = "".dup
|
||||||
value = str.dup
|
value = str.dup
|
||||||
out << value.slice!(0...1) if value =~ /^[-_]/
|
out << value.slice!(0...1) if value =~ /^[-_]/
|
||||||
out << if value[0] =~ NMSTART
|
out << (value[0] =~ NMSTART ? value.slice!(0...1) : escape_char(value.slice!(0...1)))
|
||||||
value.slice!(0...1)
|
|
||||||
else
|
|
||||||
escape_char(value.slice!(0...1))
|
|
||||||
end
|
|
||||||
out << value.gsub(/[^a-zA-Z0-9_-]/) { |c| escape_char c }
|
out << value.gsub(/[^a-zA-Z0-9_-]/) { |c| escape_char c }
|
||||||
out
|
out
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.escape_char(c)
|
def self.escape_char(c)
|
||||||
return "\\%06x" % c.ord() unless c =~ %r{[ -/:-~]}
|
return format("\\%06x", c.ord) unless c =~ %r{[ -/:-~]}
|
||||||
"\\#{c}"
|
"\\#{c}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ module Capybara
|
||||||
def description(**options)
|
def description(**options)
|
||||||
options_with_defaults = options.dup
|
options_with_defaults = options.dup
|
||||||
filters.each do |name, filter|
|
filters.each do |name, filter|
|
||||||
options_with_defaults[name] = filter.default if filter.default? && !options_with_defaults.has_key?(name)
|
options_with_defaults[name] = filter.default if filter.default? && !options_with_defaults.key?(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
@descriptions.map do |desc|
|
@descriptions.map do |desc|
|
||||||
|
@ -49,7 +49,6 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
def all
|
def all
|
||||||
@filter_sets ||= {}
|
@filter_sets ||= {}
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def default?
|
def default?
|
||||||
@options.has_key?(:default)
|
@options.key?(:default)
|
||||||
end
|
end
|
||||||
|
|
||||||
def default
|
def default
|
||||||
|
@ -20,13 +20,13 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip?(value)
|
def skip?(value)
|
||||||
@options.has_key?(:skip_if) && value == @options[:skip_if]
|
@options.key?(:skip_if) && value == @options[:skip_if]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def valid_value?(value)
|
def valid_value?(value)
|
||||||
!@options.has_key?(:valid_values) || Array(@options[:valid_values]).include?(value)
|
!@options.key?(:valid_values) || Array(@options[:valid_values]).include?(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Capybara
|
||||||
def apply_filter(expr, value)
|
def apply_filter(expr, value)
|
||||||
return expr if skip?(value)
|
return expr if skip?(value)
|
||||||
|
|
||||||
if !valid_value?(value)
|
unless valid_value?(value)
|
||||||
msg = "Invalid value #{value.inspect} passed to expression filter #{@name} - "
|
msg = "Invalid value #{value.inspect} passed to expression filter #{@name} - "
|
||||||
if default?
|
if default?
|
||||||
warn msg + "defaulting to #{default}"
|
warn msg + "defaulting to #{default}"
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Capybara
|
||||||
def matches?(node, value)
|
def matches?(node, value)
|
||||||
return true if skip?(value)
|
return true if skip?(value)
|
||||||
|
|
||||||
if !valid_value?(value)
|
unless valid_value?(value)
|
||||||
msg = "Invalid value #{value.inspect} passed to filter #{@name} - "
|
msg = "Invalid value #{value.inspect} passed to filter #{@name} - "
|
||||||
if default?
|
if default?
|
||||||
warn msg + "defaulting to #{default}"
|
warn msg + "defaulting to #{default}"
|
||||||
|
|
|
@ -21,7 +21,6 @@ end
|
||||||
|
|
||||||
module Capybara
|
module Capybara
|
||||||
class Selector
|
class Selector
|
||||||
|
|
||||||
attr_reader :name, :format
|
attr_reader :name, :format
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
@ -202,7 +201,7 @@ module Capybara
|
||||||
f_set.descriptions.each { |desc| @filter_set.describe(&desc) }
|
f_set.descriptions.each { |desc| @filter_set.describe(&desc) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def describe &block
|
def describe(&block)
|
||||||
@filter_set.describe(&block)
|
@filter_set.describe(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -235,14 +234,14 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def locate_field(xpath, locator, enable_aria_label: false, **_options)
|
def locate_field(xpath, locator, enable_aria_label: false, **_options)
|
||||||
locate_xpath = xpath #need to save original xpath for the label wrap
|
locate_xpath = xpath # Need to save original xpath for the label wrap
|
||||||
if locator
|
if locator
|
||||||
locator = locator.to_s
|
locator = locator.to_s
|
||||||
attr_matchers = [XPath.attr(:id).equals(locator),
|
attr_matchers = [XPath.attr(:id).equals(locator),
|
||||||
XPath.attr(:name).equals(locator),
|
XPath.attr(:name).equals(locator),
|
||||||
XPath.attr(:placeholder).equals(locator),
|
XPath.attr(:placeholder).equals(locator),
|
||||||
XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for))].reduce(:|)
|
XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for))].reduce(:|)
|
||||||
attr_matchers = attr_matchers | XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
attr_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
||||||
|
|
||||||
locate_xpath = locate_xpath[attr_matchers]
|
locate_xpath = locate_xpath[attr_matchers]
|
||||||
locate_xpath = locate_xpath.union(XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath))
|
locate_xpath = locate_xpath.union(XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath))
|
||||||
|
@ -253,7 +252,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def describe_all_expression_filters(**opts)
|
def describe_all_expression_filters(**opts)
|
||||||
expression_filters.map { |ef| " with #{ef} #{opts[ef]}" if opts.has_key?(ef) }.join
|
expression_filters.map { |ef| " with #{ef} #{opts[ef]}" if opts.key?(ef) }.join
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_by_attr(attribute, value)
|
def find_by_attr(attribute, value)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "uri"
|
require "uri"
|
||||||
|
require "English"
|
||||||
|
|
||||||
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
|
|
||||||
DEFAULT_OPTIONS = {
|
DEFAULT_OPTIONS = {
|
||||||
:browser => :firefox,
|
browser: :firefox,
|
||||||
clear_local_storage: false,
|
clear_local_storage: false,
|
||||||
clear_session_storage: false
|
clear_session_storage: false
|
||||||
}.freeze
|
}.freeze
|
||||||
|
@ -17,7 +17,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
unless @browser
|
unless @browser
|
||||||
if firefox?
|
if firefox?
|
||||||
options[:desired_capabilities] ||= {}
|
options[:desired_capabilities] ||= {}
|
||||||
options[:desired_capabilities].merge!({ unexpectedAlertBehaviour: "ignore" })
|
options[:desired_capabilities][:unexpectedAlertBehaviour] = "ignore"
|
||||||
end
|
end
|
||||||
|
|
||||||
@processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
@processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
||||||
|
@ -28,7 +28,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
main = Process.pid
|
main = Process.pid
|
||||||
at_exit do
|
at_exit do
|
||||||
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
# Store the exit status of the test run since it goes away after calling the at_exit proc...
|
||||||
@exit_status = $!.status if $!.is_a?(SystemExit)
|
@exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit)
|
||||||
quit if Process.pid == main
|
quit if Process.pid == main
|
||||||
exit @exit_status if @exit_status # Force exit with stored status
|
exit @exit_status if @exit_status # Force exit with stored status
|
||||||
end
|
end
|
||||||
|
@ -109,11 +109,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
|
|
||||||
def reset!
|
def reset!
|
||||||
# Use instance variable directly so we avoid starting the browser just to reset the session
|
# Use instance variable directly so we avoid starting the browser just to reset the session
|
||||||
if @browser
|
return unless @browser
|
||||||
|
|
||||||
navigated = false
|
navigated = false
|
||||||
start_time = Capybara::Helpers.monotonic_time
|
start_time = Capybara::Helpers.monotonic_time
|
||||||
begin
|
begin
|
||||||
if !navigated
|
unless navigated
|
||||||
# Only trigger a navigation if we haven't done it already, otherwise it
|
# Only trigger a navigation if we haven't done it already, otherwise it
|
||||||
# can trigger an endless series of unload modals
|
# can trigger an endless series of unload modals
|
||||||
begin
|
begin
|
||||||
|
@ -129,8 +130,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
navigated = true
|
navigated = true
|
||||||
|
|
||||||
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
||||||
until find_xpath("/html/body/*").empty? do
|
until find_xpath("/html/body/*").empty?
|
||||||
raise Capybara::ExpectationNotMet.new('Timed out waiting for Selenium session reset') if (Capybara::Helpers.monotonic_time - start_time) >= 10
|
raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if (Capybara::Helpers.monotonic_time - start_time) >= 10
|
||||||
sleep 0.05
|
sleep 0.05
|
||||||
end
|
end
|
||||||
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
||||||
|
@ -147,7 +148,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def switch_to_frame(frame)
|
def switch_to_frame(frame)
|
||||||
case frame
|
case frame
|
||||||
|
@ -269,7 +269,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
||||||
::Selenium::WebDriver::Error::ElementClickInterceptedError,
|
::Selenium::WebDriver::Error::ElementClickInterceptedError,
|
||||||
::Selenium::WebDriver::Error::InvalidElementStateError,
|
::Selenium::WebDriver::Error::InvalidElementStateError,
|
||||||
::Selenium::WebDriver::Error::ElementNotSelectableError,
|
::Selenium::WebDriver::Error::ElementNotSelectableError
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -303,7 +303,6 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# @api private
|
# @api private
|
||||||
|
@ -393,7 +392,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
# rubocop:enable Metrics/MethodLength
|
# rubocop:enable Metrics/MethodLength
|
||||||
|
|
||||||
def within_given_window(handle)
|
def within_given_window(handle)
|
||||||
original_handle = self.current_window_handle
|
original_handle = current_window_handle
|
||||||
if handle == original_handle
|
if handle == original_handle
|
||||||
yield
|
yield
|
||||||
else
|
else
|
||||||
|
@ -418,7 +417,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
alert.text.match(regexp) ? alert : nil
|
alert.text.match(regexp) ? alert : nil
|
||||||
end
|
end
|
||||||
rescue Selenium::WebDriver::Error::TimeOutError
|
rescue Selenium::WebDriver::Error::TimeOutError
|
||||||
raise Capybara::ModalNotFound.new("Unable to find modal dialog#{" with #{text}" if text}")
|
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -438,7 +437,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
if alert_text.match(regexp)
|
if alert_text.match(regexp)
|
||||||
alert_text
|
alert_text
|
||||||
else
|
else
|
||||||
raise Capybara::ModalNotFound.new("Unable to find modal dialog#{" with #{text}" if text}")
|
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
||||||
end
|
end
|
||||||
elsif called.nil?
|
elsif called.nil?
|
||||||
# page changed so modal_handler data has gone away
|
# page changed so modal_handler data has gone away
|
||||||
|
@ -449,7 +448,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue Selenium::WebDriver::Error::TimeOutError
|
rescue Selenium::WebDriver::Error::TimeOutError
|
||||||
raise Capybara::ModalNotFound.new("Unable to find modal dialog#{" with #{options[:text]}" if options[:text]}")
|
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{options[:text]}" if options[:text]}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -475,13 +474,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_selenium
|
def load_selenium
|
||||||
begin
|
|
||||||
require 'selenium-webdriver'
|
require 'selenium-webdriver'
|
||||||
# Fix for selenium-webdriver 3.4.0 which misnamed these
|
# Fix for selenium-webdriver 3.4.0 which misnamed these
|
||||||
if !defined?(::Selenium::WebDriver::Error::ElementNotInteractableError)
|
unless defined?(::Selenium::WebDriver::Error::ElementNotInteractableError)
|
||||||
::Selenium::WebDriver::Error.const_set('ElementNotInteractableError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
::Selenium::WebDriver::Error.const_set('ElementNotInteractableError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
||||||
end
|
end
|
||||||
if !defined?(::Selenium::WebDriver::Error::ElementClickInterceptedError)
|
unless defined?(::Selenium::WebDriver::Error::ElementClickInterceptedError)
|
||||||
::Selenium::WebDriver::Error.const_set('ElementClickInterceptedError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
::Selenium::WebDriver::Error.const_set('ElementClickInterceptedError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
||||||
end
|
end
|
||||||
rescue LoadError => e
|
rescue LoadError => e
|
||||||
|
@ -492,4 +490,3 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Capybara::Selenium::Node < Capybara::Driver::Node
|
class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
|
|
||||||
def visible_text
|
def visible_text
|
||||||
# Selenium doesn't normalize Unicode whitespace.
|
# Selenium doesn't normalize Unicode whitespace.
|
||||||
Capybara::Helpers.normalize_whitespace(native.text)
|
Capybara::Helpers.normalize_whitespace(native.text)
|
||||||
|
@ -41,8 +40,8 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
tag_name = self.tag_name
|
tag_name = self.tag_name
|
||||||
type = self[:type]
|
type = self[:type]
|
||||||
|
|
||||||
if (Array === value) && !multiple?
|
if value.is_a?(Array) && !multiple?
|
||||||
raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
||||||
end
|
end
|
||||||
|
|
||||||
case tag_name
|
case tag_name
|
||||||
|
@ -60,9 +59,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
when 'textarea'
|
when 'textarea'
|
||||||
set_text(value, options)
|
set_text(value, options)
|
||||||
else
|
else
|
||||||
if content_editable?
|
set_content_editable(value) if content_editable?
|
||||||
set_content_editable(value)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -71,7 +68,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
end
|
end
|
||||||
|
|
||||||
def unselect_option
|
def unselect_option
|
||||||
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." if !select_node.multiple?
|
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." unless select_node.multiple?
|
||||||
native.click if selected?
|
native.click if selected?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -94,7 +91,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
e.message =~ /Other element would receive the click/
|
e.message =~ /Other element would receive the click/
|
||||||
begin
|
begin
|
||||||
driver.execute_script("arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'})", self)
|
driver.execute_script("arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'})", self)
|
||||||
rescue
|
rescue # Swallow error if scrollIntoView with options isn't supported
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
raise e
|
raise e
|
||||||
|
@ -158,7 +155,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
def disabled?
|
def disabled?
|
||||||
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
||||||
if driver.marionette?
|
if driver.marionette?
|
||||||
if %w(option optgroup).include? tag_name
|
if %w[option optgroup].include? tag_name
|
||||||
!native.enabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
!native.enabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
||||||
else
|
else
|
||||||
!native.enabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
!native.enabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
||||||
|
@ -219,6 +216,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# a reference to the select node if this is an option node
|
# a reference to the select node if this is an option node
|
||||||
def select_node
|
def select_node
|
||||||
find_xpath('./ancestor::select[1]').first
|
find_xpath('./ancestor::select[1]').first
|
||||||
|
@ -258,7 +256,7 @@ private
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_file(value)
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
||||||
path_names = value.to_s.empty? ? [] : value
|
path_names = value.to_s.empty? ? [] : value
|
||||||
if driver.chrome?
|
if driver.chrome?
|
||||||
native.send_keys(Array(path_names).join("\n"))
|
native.send_keys(Array(path_names).join("\n"))
|
||||||
|
@ -267,8 +265,8 @@ private
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_content_editable(value)
|
def set_content_editable(value) # rubocop:disable Naming/AccessorMethodName
|
||||||
#ensure we are focused on the element
|
# Ensure we are focused on the element
|
||||||
click
|
click
|
||||||
|
|
||||||
script = <<-JS
|
script = <<-JS
|
||||||
|
|
|
@ -4,7 +4,6 @@ require 'capybara/session/matchers'
|
||||||
require 'addressable/uri'
|
require 'addressable/uri'
|
||||||
|
|
||||||
module Capybara
|
module Capybara
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# The Session class represents a single user's interaction with the system. The Session can use
|
# The Session class represents a single user's interaction with the system. The Session can use
|
||||||
|
@ -94,8 +93,8 @@ module Capybara
|
||||||
|
|
||||||
def driver
|
def driver
|
||||||
@driver ||= begin
|
@driver ||= begin
|
||||||
unless Capybara.drivers.has_key?(mode)
|
unless Capybara.drivers.key?(mode)
|
||||||
other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
|
other_drivers = Capybara.drivers.keys.map(&:inspect)
|
||||||
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
||||||
end
|
end
|
||||||
driver = Capybara.drivers[mode].call(app)
|
driver = Capybara.drivers[mode].call(app)
|
||||||
|
@ -197,7 +196,7 @@ module Capybara
|
||||||
# Addressable doesn't support opaque URIs - we want nil here
|
# Addressable doesn't support opaque URIs - we want nil here
|
||||||
return nil if uri.scheme == "about"
|
return nil if uri.scheme == "about"
|
||||||
path = uri.path
|
path = uri.path
|
||||||
path if path and not path.empty?
|
path if path && !path.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -335,7 +334,7 @@ module Capybara
|
||||||
# @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
|
# @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
|
||||||
#
|
#
|
||||||
def within(*args)
|
def within(*args)
|
||||||
new_scope = if args.first.is_a?(Capybara::Node::Base) then args.first else find(*args) end
|
new_scope = args.first.is_a?(Capybara::Node::Base) ? args.first : find(*args)
|
||||||
begin
|
begin
|
||||||
scopes.push(new_scope)
|
scopes.push(new_scope)
|
||||||
yield
|
yield
|
||||||
|
@ -390,15 +389,19 @@ module Capybara
|
||||||
driver.switch_to_frame(frame)
|
driver.switch_to_frame(frame)
|
||||||
scopes.push(:frame)
|
scopes.push(:frame)
|
||||||
when :parent
|
when :parent
|
||||||
|
if scopes.last != :frame
|
||||||
raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
|
raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
|
||||||
"`within` block." if scopes.last() != :frame
|
"`within` block."
|
||||||
|
end
|
||||||
scopes.pop
|
scopes.pop
|
||||||
driver.switch_to_frame(:parent)
|
driver.switch_to_frame(:parent)
|
||||||
when :top
|
when :top
|
||||||
idx = scopes.index(:frame)
|
idx = scopes.index(:frame)
|
||||||
if idx
|
if idx
|
||||||
|
if scopes.slice(idx..-1).any? { |scope| ![:frame, nil].include?(scope) }
|
||||||
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
|
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
|
||||||
"`within` block." if scopes.slice(idx..-1).any? {|scope| ![:frame, nil].include?(scope)}
|
"`within` block."
|
||||||
|
end
|
||||||
scopes.slice!(idx..-1)
|
scopes.slice!(idx..-1)
|
||||||
driver.switch_to_frame(:top)
|
driver.switch_to_frame(:top)
|
||||||
end
|
end
|
||||||
|
@ -477,11 +480,9 @@ module Capybara
|
||||||
#
|
#
|
||||||
def switch_to_window(window = nil, **options, &window_locator)
|
def switch_to_window(window = nil, **options, &window_locator)
|
||||||
block_given = block_given?
|
block_given = block_given?
|
||||||
if window && block_given
|
raise ArgumentError, "`switch_to_window` can take either a block or a window, not both" if window && block_given
|
||||||
raise ArgumentError, "`switch_to_window` can take either a block or a window, not both"
|
raise ArgumentError, "`switch_to_window`: either window or block should be provided" if !window && !block_given
|
||||||
elsif !window && !block_given
|
unless scopes.last.nil?
|
||||||
raise ArgumentError, "`switch_to_window`: either window or block should be provided"
|
|
||||||
elsif !scopes.last.nil?
|
|
||||||
raise Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from "\
|
raise Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from "\
|
||||||
"`within` or `within_frame` blocks."
|
"`within` or `within_frame` blocks."
|
||||||
end
|
end
|
||||||
|
@ -826,6 +827,7 @@ module Capybara
|
||||||
Capybara::ReadOnlySessionConfig.new(Capybara.session_options)
|
Capybara::ReadOnlySessionConfig.new(Capybara.session_options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
@@instance_created = false
|
@@instance_created = false
|
||||||
|
@ -845,14 +847,12 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def open_file(path)
|
def open_file(path)
|
||||||
begin
|
|
||||||
require "launchy"
|
require "launchy"
|
||||||
Launchy.open(path)
|
Launchy.open(path)
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
warn "File saved to #{path}."
|
warn "File saved to #{path}."
|
||||||
warn "Please install the launchy gem to open the file automatically."
|
warn "Please install the launchy gem to open the file automatically."
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def prepare_path(path, extension)
|
def prepare_path(path, extension)
|
||||||
path = File.expand_path(path || default_fn(extension), config.save_path)
|
path = File.expand_path(path || default_fn(extension), config.save_path)
|
||||||
|
@ -912,9 +912,7 @@ module Capybara
|
||||||
begin
|
begin
|
||||||
driver.window_handles.each do |handle|
|
driver.window_handles.each do |handle|
|
||||||
driver.switch_to_window handle
|
driver.switch_to_window handle
|
||||||
if yield
|
return Window.new(self, handle) if yield
|
||||||
return Window.new(self, handle)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
rescue => e
|
rescue => e
|
||||||
driver.switch_to_window(original_window_handle)
|
driver.switch_to_window(original_window_handle)
|
||||||
|
@ -926,6 +924,5 @@ module Capybara
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,13 +68,13 @@ module Capybara
|
||||||
|
|
||||||
remove_method :app_host=
|
remove_method :app_host=
|
||||||
def app_host=(url)
|
def app_host=(url)
|
||||||
raise ArgumentError.new("Capybara.app_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::DEFAULT_PARSER.make_regexp)
|
raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com)" unless url.nil? || (url =~ URI::DEFAULT_PARSER.make_regexp)
|
||||||
@app_host = url
|
@app_host = url
|
||||||
end
|
end
|
||||||
|
|
||||||
remove_method :default_host=
|
remove_method :default_host=
|
||||||
def default_host=(url)
|
def default_host=(url)
|
||||||
raise ArgumentError.new("Capybara.default_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::DEFAULT_PARSER.make_regexp)
|
raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com)" unless url.nil? || (url =~ URI::DEFAULT_PARSER.make_regexp)
|
||||||
@default_host = url
|
@default_host = url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def eql?(other)
|
def eql?(other)
|
||||||
other.kind_of?(self.class) && @session == other.session && @handle == other.handle
|
other.is_a?(self.class) && @session == other.session && @handle == other.handle
|
||||||
end
|
end
|
||||||
alias_method :==, :eql?
|
alias_method :==, :eql?
|
||||||
|
|
||||||
|
@ -130,9 +130,7 @@ module Capybara
|
||||||
end
|
end
|
||||||
|
|
||||||
def raise_unless_current(what)
|
def raise_unless_current(what)
|
||||||
unless current?
|
raise Capybara::WindowError, "#{what} not current window is not possible." unless current?
|
||||||
raise Capybara::WindowError, "#{what} not current window is not possible."
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'selenium-webdriver'
|
require 'selenium-webdriver'
|
||||||
require 'shared_selenium_session'
|
require 'shared_selenium_session'
|
||||||
|
require 'rspec/shared_spec_matchers'
|
||||||
|
|
||||||
CHROME_DRIVER = if ENV['HEADLESS'] then :selenium_chrome_headless else :selenium_chrome end
|
CHROME_DRIVER = if ENV['HEADLESS'] then :selenium_chrome_headless else :selenium_chrome end
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ CHROME_DRIVER = if ENV['HEADLESS'] then :selenium_chrome_headless else :selenium
|
||||||
Capybara.register_driver :selenium_chrome_clear_storage do |app|
|
Capybara.register_driver :selenium_chrome_clear_storage do |app|
|
||||||
chrome_options = {
|
chrome_options = {
|
||||||
browser: :chrome,
|
browser: :chrome,
|
||||||
options: ::Selenium::WebDriver::Chrome::Options.new()
|
options: ::Selenium::WebDriver::Chrome::Options.new
|
||||||
}
|
}
|
||||||
chrome_options[:options].args << 'headless' if ENV['HEADLESS']
|
chrome_options[:options].args << 'headless' if ENV['HEADLESS']
|
||||||
Capybara::Selenium::Driver.new(app, chrome_options.merge(clear_local_storage: true, clear_session_storage: true))
|
Capybara::Selenium::Driver.new(app, chrome_options.merge(clear_local_storage: true, clear_session_storage: true))
|
||||||
|
@ -31,6 +32,7 @@ Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_DRIVER.to_s, capybar
|
||||||
RSpec.describe "Capybara::Session with chrome" do
|
RSpec.describe "Capybara::Session with chrome" do
|
||||||
include Capybara::SpecHelper
|
include Capybara::SpecHelper
|
||||||
include_examples "Capybara::Session", TestSessions::Chrome, CHROME_DRIVER
|
include_examples "Capybara::Session", TestSessions::Chrome, CHROME_DRIVER
|
||||||
|
include_examples Capybara::RSpecMatchers, TestSessions::Chrome, CHROME_DRIVER
|
||||||
|
|
||||||
context "storage" do
|
context "storage" do
|
||||||
describe "#reset!" do
|
describe "#reset!" do
|
||||||
|
|
Loading…
Reference in New Issue