Code cleanup and enable line length cop

This commit is contained in:
Thomas Walpole 2018-11-02 11:38:58 -07:00
parent 0685c3902f
commit f6b2f3adf0
27 changed files with 173 additions and 91 deletions

View File

@ -15,11 +15,11 @@ Metrics/LineLength:
Exclude:
- 'spec/**/*'
- 'lib/capybara/spec/**/*'
- 'lib/capybara/selector.rb'
IgnoredPatterns:
- '\s*# '
- '\s*(raise|warn) '
Max: 120
Enabled: false
Metrics/BlockLength:
Exclude:

View File

@ -460,7 +460,8 @@ end
Capybara.register_server :webrick do |app, port, host, **options|
require 'rack/handler/webrick'
Rack::Handler::WEBrick.run(app, { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options))
options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options)
Rack::Handler::WEBrick.run(app, options)
end
Capybara.register_server :puma do |app, port, host, **options|
@ -476,8 +477,8 @@ Capybara.register_server :puma do |app, port, host, **options|
# If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests.
# Therefore construct and run the Server instance ourselves.
# Rack::Handler::Puma.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options))
conf = Rack::Handler::Puma.config(app, { Host: host, Port: port, Threads: '0:4', workers: 0, daemon: false }.merge(options))
options = { Host: host, Port: port, Threads: '0:4', workers: 0, daemon: false }.merge(options)
conf = Rack::Handler::Puma.config(app, options)
events = conf.options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio
events.log 'Capybara starting Puma...'

View File

@ -42,12 +42,12 @@ module Capybara
# @!method 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[text no_text title no_title current_path no_current_path].each do |assertion_name|
class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
def #{assertion_name} *args
def assert_#{assertion_name} *args
self.assertions +=1
subject, args = determine_subject(args)
subject.#{assertion_name}(*args)
subject.assert_#{assertion_name}(*args)
rescue Capybara::ExpectationNotMet => e
raise ::Minitest::Assertion, e.message
end
@ -86,15 +86,14 @@ module Capybara
# @!method assert_style
# see {Capybara::Node::Matchers#assert_style}
%w[assert_selector assert_no_selector
assert_all_of_selectors assert_none_of_selectors assert_any_of_selectors
assert_matches_selector assert_not_matches_selector
assert_style].each do |assertion_name|
%w[selector no_selector style
all_of_selectors none_of_selectors any_of_selectors
matches_selector not_matches_selector].each do |assertion_name|
class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
def #{assertion_name} *args, &optional_filter_block
def assert_#{assertion_name} *args, &optional_filter_block
self.assertions +=1
subject, args = determine_subject(args)
subject.#{assertion_name}(*args, &optional_filter_block)
subject.assert_#{assertion_name}(*args, &optional_filter_block)
rescue Capybara::ExpectationNotMet => e
raise ::Minitest::Assertion, e.message
end

View File

@ -322,7 +322,8 @@ module Capybara
rescue StandardError # rubocop:disable Lint/HandleExceptions swallow extra errors
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)
options[:allow_self] = true if locator.nil?
synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do

View File

@ -100,9 +100,7 @@ module Capybara
#
def assert_selector(*args, &optional_filter_block)
_verify_selector_result(args, optional_filter_block) do |result, query|
unless result.matches_count? && (result.any? || query.expects_none?)
raise Capybara::ExpectationNotMet, result.failure_message
end
raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count? && (result.any? || query.expects_none?)
end
end

View File

@ -104,10 +104,11 @@ module Capybara
return false if (tag_name == 'input') && (native[:type] == 'hidden')
if check_ancestors
!find_xpath("boolean(./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head'])")
!find_xpath(VISIBILITY_XPATH)
# !find_xpath("boolean(./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head'])")
else
# 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.key?('hidden') || (native[:style] =~ /display:\s?none/) || %w[script head].include?(tag_name))
end
end
@ -185,6 +186,14 @@ module Capybara
option[:value] || option.content
end
VISIBILITY_XPATH = XPath.generate do |x|
x.ancestor_or_self[
x.attr(:style)[x.contains('display:none') | x.contains('display: none')] |
x.attr(:hidden) |
x.qname.one_of('script', 'head')
].boolean
end.to_s.freeze
end
end
end

View File

@ -77,17 +77,21 @@ module Capybara
message = +''
count, between, maximum, minimum = options.values_at(:count, :between, :maximum, :minimum)
if count
message << " #{count} #{Capybara::Helpers.declension('time', 'times', count)}"
message << " #{occurrences count}"
elsif between
message << " between #{between.first} and #{between.last} times"
elsif maximum
message << " at most #{maximum} #{Capybara::Helpers.declension('time', 'times', maximum)}"
message << " at most #{occurrences maximum}"
elsif minimum
message << " at least #{minimum} #{Capybara::Helpers.declension('time', 'times', minimum)}"
message << " at least #{occurrences minimum}"
end
message
end
def occurrences(count)
"#{count} #{Capybara::Helpers.declension('time', 'times', count)}"
end
def assert_valid_keys
invalid_keys = @options.keys - valid_keys
return if invalid_keys.empty?

View File

@ -24,7 +24,8 @@ module Capybara
raise ArgumentError, "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
@expression = selector.call(@locator, @options.merge(selector_config: { enable_aria_label: enable_aria_label, test_id: test_id }))
selector_config = { enable_aria_label: enable_aria_label, test_id: test_id }
@expression = selector.call(@locator, @options.merge(selector_config: selector_config))
warn_exact_usage
@ -34,21 +35,23 @@ module Capybara
def name; selector.name; end
def label; selector.label || selector.name; end
def description(applied = false)
def description(only_applied = false)
desc = +''
if !applied || applied_filters
show_for = show_for_stage(only_applied)
if show_for[:any]
desc << 'visible ' if visible == :visible
desc << 'non-visible ' if visible == :hidden
end
desc << "#{label} #{locator.inspect}"
if !applied || applied_filters
if show_for[:any]
desc << " with#{' exact' if exact_text == true} text #{options[:text].inspect}" if options[:text]
desc << " with exact text #{exact_text}" if exact_text.is_a?(String)
end
desc << " with id #{options[:id]}" if options[:id]
desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
desc << selector.description(node_filters: !applied || (applied_filters == :node), **options)
desc << ' that also matches the custom filter block' if @filter_block && (!applied || (applied_filters == :node))
desc << selector.description(node_filters: show_for[:node], **options)
desc << ' that also matches the custom filter block' if @filter_block && show_for[:node]
desc << " within #{@resolved_node.inspect}" if describe_within?
desc
end
@ -60,17 +63,17 @@ module Capybara
def matches_filters?(node)
return true if (@resolved_node&.== node) && options[:allow_self]
@applied_filters ||= :system
applied_filters << :system
return false unless matches_system_filters?(node)
@applied_filters = :node
applied_filters << :node
matches_node_filters?(node) && matches_filter_block?(node)
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
false
end
def visible
case (vis = options.fetch(:visible) { @selector.default_visibility(session_options.ignore_hidden_elements, options) })
case (vis = options.fetch(:visible) { default_visibility })
when true then :visible
when false then :all
else vis
@ -98,7 +101,7 @@ module Capybara
# @api private
def resolve_for(node, exact = nil)
@applied_filters = false
applied_filters.clear
@resolved_node = node
node.synchronize do
children = find_nodes_by_selector_format(node, exact).map(&method(:to_element))
@ -121,8 +124,14 @@ module Capybara
private
def show_for_stage(only_applied)
lambda do |stage = :any|
!only_applied || (stage == :any ? applied_filters.any? : applied_filters.include?(stage))
end
end
def applied_filters
@applied_filters ||= false
@applied_filters ||= []
end
def find_selector(locator)
@ -183,17 +192,21 @@ module Capybara
end
end
def filter_set(name)
::Capybara::Selector::FilterSet.all[name]
end
def node_filters
if options.key?(:filter_set)
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
filter_set(options[:filter_set])
else
@selector.node_filters
end
@selector
end.node_filters
end
def expression_filters
filters = @selector.expression_filters
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.key?(:filter_set)
filters.merge filter_set(options[:filter_set]).expression_filters if options.key?(:filter_set)
filters
end
@ -253,7 +266,7 @@ module Capybara
unapplied_options = options.keys - valid_keys
expression_filters.inject(expression) do |expr, (name, ef)|
if ef.matcher?
unapplied_options.select { |option_name| ef.handles_option?(option_name) }.inject(expr) do |memo, option_name|
unapplied_options.select(&ef.method(:handles_option?)).inject(expr) do |memo, option_name|
unapplied_options.delete(option_name)
ef.apply_filter(memo, option_name, options[option_name])
end
@ -348,6 +361,10 @@ module Capybara
!!node.text(text_visible, normalize_ws: normalize_ws).match(regexp)
end
def default_visibility
@selector.default_visibility(session_options.ignore_hidden_elements, options)
end
def builder
selector.builder
end

View File

@ -5,7 +5,7 @@ module Capybara
module Queries
class StyleQuery < BaseQuery
def initialize(expected_styles, session_options:, **options)
@expected_styles = expected_styles.each_with_object({}) { |(style, value), str_keys| str_keys[style.to_s] = value }
@expected_styles = stringify_keys(expected_styles)
@options = options
@actual_styles = {}
super(@options)
@ -33,6 +33,10 @@ module Capybara
private
def stringify_keys(hsh)
hsh.each_with_object({}) { |(k, v), str_keys| str_keys[k.to_s] = v }
end
def valid_keys
%i[wait]
end

View File

@ -65,7 +65,7 @@ module Capybara
insensitive_count = @actual_text.scan(insensitive_regexp).size
return if insensitive_count == @count
"it was found #{insensitive_count} #{Capybara::Helpers.declension('time', 'times', insensitive_count)} using a case insensitive search"
"it was found #{occurrences insensitive_count} using a case insensitive search"
end
def invisible_message
@ -73,7 +73,7 @@ module Capybara
invisible_count = invisible_text.scan(@search_regexp).size
return if invisible_count == @count
"it was found #{invisible_count} #{Capybara::Helpers.declension('time', 'times', invisible_count)} including non-visible text"
"it was found #{occurrences invisible_count} including non-visible text"
rescue StandardError
# An error getting the non-visible text (if element goes out of scope) should not affect the response
nil

View File

@ -8,7 +8,7 @@ module Capybara
@expected_title = expected_title.is_a?(Regexp) ? expected_title : expected_title.to_s
@options = options
super(@options)
@search_regexp = Capybara::Helpers.to_regexp(@expected_title, all_whitespace: true, exact: options.fetch(:exact, false))
@search_regexp = Helpers.to_regexp(@expected_title, all_whitespace: true, exact: options.fetch(:exact, false))
assert_valid_keys
end

View File

@ -96,9 +96,11 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
return true if string_node.disabled?
if %w[option optgroup].include? tag_name
find_xpath('parent::*[self::optgroup or self::select or self::datalist]')[0].disabled?
find_xpath(OPTION_OWNER_XPATH)[0].disabled?
# find_xpath('parent::*[self::optgroup or self::select or self::datalist]')[0].disabled?
else
!find_xpath('parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]').empty?
!find_xpath(DISABLED_BY_FIELDSET_XPATH).empty?
# !find_xpath('parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]').empty?
end
end
@ -256,4 +258,18 @@ protected
def textarea?
tag_name == 'textarea'
end
OPTION_OWNER_XPATH = XPath.parent(:optgroup, :select, :datalist).to_s.freeze
DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
x.parent(:fieldset)[
XPath.attr(:disabled)
] + x.ancestor[
~x.self(:legend) |
x.preceding_sibling(:legend)
][
x.parent(:fieldset)[
x.attr(:disabled)
]
]
end.to_s.freeze
end

View File

@ -108,7 +108,8 @@ module Capybara
if count.zero?
message << ' but there were no matches'
else
message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: " << full_results.map(&:text).map(&:inspect).join(', ')
message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: " \
<< full_results.map(&:text).map(&:inspect).join(', ')
end
unless rest.empty?
elements = rest.map { |el| el.text rescue '<<ERROR>>' }.map(&:inspect).join(', ') # rubocop:disable Style/RescueModifier

View File

@ -14,10 +14,10 @@ RSpec.configure do |config|
end
RSpec.configure do |config|
config.alias_example_group_to :feature, capybara_feature: true, type: :feature
config.alias_example_group_to :xfeature, capybara_feature: true, type: :feature, skip: 'Temporarily disabled with xfeature'
config.alias_example_group_to :ffeature, capybara_feature: true, type: :feature, focus: true
config.alias_example_group_to :feature, :capybara_feature, type: :feature
config.alias_example_group_to :xfeature, :capybara_feature, type: :feature, skip: 'Temporarily disabled with xfeature'
config.alias_example_group_to :ffeature, :capybara_feature, :focus, type: :feature
config.alias_example_to :scenario
config.alias_example_to :xscenario, skip: 'Temporarily disabled with xscenario'
config.alias_example_to :fscenario, focus: true
config.alias_example_to :fscenario, :focus
end

View File

@ -27,7 +27,9 @@ if RUBY_ENGINE == 'jruby'
def included(base)
warn 'including Capybara::DSL in the global scope is not recommended!' if base == Object
base.send(:include, ::Capybara::RSpecMatcherProxies) if defined?(::RSpec::Matchers) && base.include?(::RSpec::Matchers)
if defined?(::RSpec::Matchers) && base.include?(::RSpec::Matchers)
base.send(:include, ::Capybara::RSpecMatcherProxies)
end
super
end
end

View File

@ -129,7 +129,8 @@ module Capybara
Matchers::HaveStyle.new(styles, options)
end
%w[selector css xpath text title current_path link button field checked_field unchecked_field select table].each do |matcher_type|
%w[selector css xpath text title current_path link button
field checked_field unchecked_field select table].each do |matcher_type|
define_method "have_no_#{matcher_type}" do |*args, &optional_filter_block|
Matchers::NegatedMatcher.new(send("have_#{matcher_type}", *args, &optional_filter_block))
end

View File

@ -278,7 +278,7 @@ Capybara.add_selector(:select) do
expression_filter(:with_options) do |expr, options|
options.inject(expr) do |xpath, option|
xpath[Capybara::Selector.all[:option].call(option)]
xpath[self.class.all[:option].call(option)]
end
end
@ -325,7 +325,7 @@ Capybara.add_selector(:datalist_input) do
expression_filter(:with_options) do |expr, options|
options.inject(expr) do |xpath, option|
xpath[XPath.attr(:list) == XPath.anywhere(:datalist)[Capybara::Selector.all[:datalist_option].call(option)].attr(:id)]
xpath[XPath.attr(:list) == XPath.anywhere(:datalist)[self.class.all[:datalist_option].call(option)].attr(:id)]
end
end

View File

@ -21,11 +21,11 @@ module Capybara
end
S = '\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
H = /[0-9a-fA-F]/
UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/
NONASCII = /[#{S}]/
ESCAPE = /#{UNICODE}|\\[ -~#{S}]/
NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/
H = /[0-9a-fA-F]/.freeze
UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/.freeze
NONASCII = /[#{S}]/.freeze
ESCAPE = /#{UNICODE}|\\[ -~#{S}]/.freeze
NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/.freeze
class Splitter
def split(css)

View File

@ -430,15 +430,19 @@ module Capybara
def describe_all_expression_filters(**opts)
expression_filters.map do |ef_name, ef|
if ef.matcher?
opts.keys.map do |key|
" with #{ef_name}[#{key} => #{opts[key]}]" if ef.handles_option?(key) && !::Capybara::Queries::SelectorQuery::VALID_KEYS.include?(key)
end.join
handled_custom_keys(ef, opts.keys).map { |key| " with #{ef_name}[#{key} => #{opts[key]}]" }.join
elsif opts.key?(ef_name)
" with #{ef_name} #{opts[ef_name]}"
end
end.join
end
def handled_custom_keys(filter, keys)
keys.select do |key|
filter.handles_option?(key) && !::Capybara::Queries::SelectorQuery::VALID_KEYS.include?(key)
end
end
def find_by_attr(attribute, value)
finder_name = "find_by_#{attribute}_attr"
if respond_to?(finder_name, true)

View File

@ -352,11 +352,15 @@ private
when :chrome
extend ChromeDriver
when :firefox
require 'capybara/selenium/patches/pause_duration_fix' if sel_driver.capabilities['moz:geckodriverVersion']&.start_with?('0.22.')
require 'capybara/selenium/patches/pause_duration_fix' if pause_broken?(sel_driver)
extend MarionetteDriver if sel_driver.capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
end
end
def pause_broken?(driver)
driver.capabilities['moz:geckodriverVersion']&.start_with?('0.22.')
end
def setup_exit_handler
main = Process.pid
at_exit do

View File

@ -170,22 +170,9 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
selector = node[:tagName]
if node[:namespaceURI] != default_ns
selector = XPath.child.where((XPath.local_name == selector) & (XPath.namespace_uri == node[:namespaceURI])).to_s
selector
end
if parent
siblings = parent.find_xpath(selector)
selector += case siblings.size
when 0
'[ERROR]' # IE doesn't support full XPath (namespace-uri, etc)
when 1
'' # index not necessary when only one matching element
else
idx = siblings.index(node)
# Element may not be found in the siblings if it has gone away
idx.nil? ? '[ERROR]' : "[#{idx + 1}]"
end
end
selector += sibling_index(parent, node, selector) if parent
result.push selector
end
@ -203,6 +190,20 @@ protected
private
def sibling_index(parent, node, selector)
siblings = parent.find_xpath(selector)
case siblings.size
when 0
'[ERROR]' # IE doesn't support full XPath (namespace-uri, etc)
when 1
'' # index not necessary when only one matching element
else
idx = siblings.index(node)
# Element may not be found in the siblings if it has gone away
idx.nil? ? '[ERROR]' : "[#{idx + 1}]"
end
end
def boolean_attr(val)
val && (val != 'false')
end

View File

@ -9,8 +9,9 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
super
rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
if tag_name == 'tr'
warn 'You are attempting to click a table row which has issues in geckodriver/marionette - see https://github.com/mozilla/geckodriver/issues/1228. ' \
'Your test should probably be clicking on a table cell like a user would. Clicking the first cell in the row instead.'
warn 'You are attempting to click a table row which has issues in geckodriver/marionette - '\
'see https://github.com/mozilla/geckodriver/issues/1228. Your test should probably be '\
'clicking on a table cell like a user would. Clicking the first cell in the row instead.'
return find_css('th:first-child,td:first-child')[0].click(keys, options)
end
raise
@ -26,7 +27,8 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
if %w[option optgroup].include? tag_name
find_xpath('parent::*[self::optgroup or self::select]')[0].disabled?
else
!find_xpath('parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]').empty?
!find_xpath(DISABLED_BY_FIELDSET_XPATH).empty?
# !find_xpath('parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]').empty?
end
end
@ -99,14 +101,27 @@ private
return nil unless local_file
raise ArgumentError, "You may only upload files: #{local_file.inspect}" unless File.file?(local_file)
result = bridge.http.call(:post, "session/#{bridge.session_id}/file", file: Selenium::WebDriver::Zipper.zip_file(local_file))
result['value']
file = ::Selenium::WebDriver::Zipper.zip_file(local_file)
bridge.http.call(:post, "session/#{bridge.session_id}/file", file: file)['value']
end
def browser_version
driver.browser.capabilities[:browser_version].to_f
end
DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
x.parent(:fieldset)[
x.attr(:disabled)
] + x.ancestor[
~x.self(:legned) |
x.preceding_sibling(:legend)
][
x.parent(:fieldset)[
x.attr(:disabled)
]
]
end.to_s.freeze
class ModifierKeysStack
def initialize
@stack = []

View File

@ -6,6 +6,4 @@ module PauseDurationFix
end
end
if defined?(::Selenium::WebDriver::Interactions::Pause)
::Selenium::WebDriver::Interactions::Pause.prepend PauseDurationFix
end
::Selenium::WebDriver::Interactions::Pause.prepend PauseDurationFix

View File

@ -18,7 +18,12 @@ module Capybara
attr_reader :app, :port, :host
def initialize(app, *deprecated_options, port: Capybara.server_port, host: Capybara.server_host, reportable_errors: Capybara.server_errors, extra_middleware: [])
def initialize(app,
*deprecated_options,
port: Capybara.server_port,
host: Capybara.server_host,
reportable_errors: Capybara.server_errors,
extra_middleware: [])
warn 'Positional arguments, other than the application, to Server#new are deprecated, please use keyword arguments' unless deprecated_options.empty?
@app = app
@extra_middleware = extra_middleware

View File

@ -16,7 +16,7 @@ module Capybara
def initialize(app)
@app = app
@disable_markup = format(DISABLE_MARKUP_TEMPLATE, selector: AnimationDisabler.selector_for(Capybara.disable_animation))
@disable_markup = format(DISABLE_MARKUP_TEMPLATE, selector: self.class.selector_for(Capybara.disable_animation))
end
def call(env)

View File

@ -819,7 +819,9 @@ module Capybara
end
def prepare_path(path, extension)
File.expand_path(path || default_fn(extension), config.save_path).tap { |p_path| FileUtils.mkdir_p(File.dirname(p_path)) }
File.expand_path(path || default_fn(extension), config.save_path).tap do |p_path|
FileUtils.mkdir_p(File.dirname(p_path))
end
end
def default_fn(extension)

View File

@ -50,17 +50,17 @@ Capybara::SpecHelper.spec '#has_css?' do
it 'should be able to generate an error message if the scope is a sibling' do
el = @session.find(:css, '#first')
@session.within el.sibling(:css, '#second') do
expect {
expect do
expect(@session).to have_css('a#not_on_page')
}.to raise_error /there were no matches/
end.to raise_error(/there were no matches/)
end
end
it 'should be able to generate an error message if the scope is a sibling from XPath' do
el = @session.find(:css, '#first').find(:xpath, './following-sibling::*[1]') do
expect {
expect do
expect(el).to have_css('a#not_on_page')
}.to raise_error /there were no matches/
end.to raise_error(/there were no matches/)
end
end
end