Proxy all/within to the correct version based on parameters passed

This commit is contained in:
Thomas Walpole 2017-03-28 10:41:49 -07:00
parent d900978c54
commit 6356719043
11 changed files with 143 additions and 6 deletions

View File

@ -2,6 +2,11 @@
Release date: Unreleased
### Added
* Proxy methods when using RSpec for `all`/`within` that call either the Capybara::DSL or RSpec matchers
depending on arguments passed
### Fixed
* Element#inspect doesn't raise an error on obsolete elements

View File

@ -241,6 +241,10 @@ RSpec.describe "todos/show.html.erb", type: :view do
end
```
**Note: When you require 'capybara/rspec' proxy methods are installed to work around name collisions between Capybara::DSL methods
`all`/`within` and the identically named built-in RSpec matchers. If you opt not to require 'capybara/rspec' (because you wanted to customize the RSpec
configurationm etc.), you can install the proxy methods by requiring 'capybara/rspec/matcher_proxies' after requiring RSpec and 'capybara/dsl'**
## <a name="using-capybara-with-testunit"></a>Using Capybara with Test::Unit
* If you are using `Test::Unit`, define a base class for your Capybara tests

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'capybara/dsl'
require 'capybara/rspec/matchers'
require 'capybara/rspec/matcher_proxies'
World(Capybara::DSL)
World(Capybara::RSpecMatchers)

View File

@ -1,8 +1,9 @@
# frozen_string_literal: true
require 'capybara/dsl'
require 'rspec/core'
require 'capybara/dsl'
require 'capybara/rspec/matchers'
require 'capybara/rspec/features'
require 'capybara/rspec/matcher_proxies'
RSpec.configure do |config|
config.include Capybara::DSL, :type => :feature
@ -22,6 +23,7 @@ RSpec.configure do |config|
Capybara.use_default_driver
end
end
config.before do
if self.class.include?(Capybara::DSL)
example = fetch_current_example.call(self)

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
module Capybara
module RSpecMatcherProxies
def all(*args)
if defined?(::RSpec::Matchers::BuiltIn::All) && args.first.respond_to?(:matches?)
::RSpec::Matchers::BuiltIn::All.new(*args)
else
find_all(*args)
end
end
def within(*args)
if block_given?
within_element(*args, &Proc.new)
else
be_within(*args)
end
end
end
module DSL
def self.included(base)
warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
if defined?(::RSpec::Matchers) && base.include?(::RSpec::Matchers)
base.send(:include, ::Capybara::RSpecMatcherProxies)
end
super
end
end
end
if defined?(::RSpec::Matchers)
module ::RSpec::Matchers
def self.included(base)
base.send(:include, ::Capybara::RSpecMatcherProxies) if base.include?(::Capybara::DSL)
super
end
end
end

View File

@ -267,4 +267,4 @@ module Capybara
BecomeClosed.new(options)
end
end
end
end

View File

@ -131,9 +131,7 @@ Capybara::SpecHelper.spec '#has_no_selector?' do
end
it "should accept a filter block" do
if !defined?(::RSpec::Expectations::Version) || (Gem::Version.new(RSpec::Expectations::Version::STRING) < Gem::Version.new('3.0'))
skip "RSpec < 3 doesn't pass the block along to the matcher for the Builtin::Has matcher"
end
skip "RSpec < 3 doesn't pass the block along to the matcher for the Builtin::Has matcher" if rspec2?
expect(@session).to have_no_selector(:css, "a#foo") { |el| el[:id] != "foo" }
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
Capybara::SpecHelper.spec "node", :focus_ do
Capybara::SpecHelper.spec "node" do
before do
@session.visit('/with_html')
end

View File

@ -5,4 +5,8 @@ RSpec.describe "capybara/rspec", type: :view do
it "allows matchers to be used on strings" do
expect(%{<h1>Test header</h1>}).to have_css("h1", text: "Test header")
end
it "doesn't include RSpecMatcherProxies" do
expect(self.class.ancestors).not_to include(Capybara::RSpecMatcherProxies)
end
end

View File

@ -7,6 +7,10 @@ RSpec.describe 'capybara/rspec', :type => :feature do
expect(page.body).to include('Another World')
end
it "should include RSpec matcher proxies" do
expect(self.class.ancestors).to include Capybara::RSpecMatcherProxies
end
context "resetting session" do
it "sets a cookie in one example..." do
visit('/set_cookie')
@ -36,6 +40,79 @@ RSpec.describe 'capybara/rspec', :type => :feature do
it "switches to the given driver when giving it as metadata", :driver => :culerity do
expect(Capybara.current_driver).to eq(:culerity)
end
context "#all" do
it "allows access to the Capybara finder" do
visit('/with_html')
expect(all(:css, 'h2.head').size).to eq(5)
end
it "allows access to the RSpec matcher" do
skip "RSpec < 3 doesn't have an `all` matcher" if rspec2?
visit('/with_html')
expect(["test1", "test2"]).to all(be_a(String))
end
end
context "#within" do
it "allows access to the Capybara scoper" do
visit('/with_html')
expect do
within(:css, "#does_not_exist") { click_link "Go to simple" }
end.to raise_error(Capybara::ElementNotFound)
end
it "allows access to the RSpec matcher" do
skip "RSpec version doesn't have a 'within' matcher" unless ::RSpec::Matchers.instance_methods.include?(:within)
visit('/with_html')
# This reads terribly, but must call #within
expect(find(:css, 'span.number').text.to_i).to within(1).of(41)
end
end
end
RSpec.describe 'capybara/rspec', :type => :other do
context "when RSpec::Matchers is included after Capybara::DSL" do
before do
class DSL_MatchersTest
include Capybara::DSL
include RSpec::Matchers
end
@test_class_instance = DSL_MatchersTest.new
end
context "#all" do
it "allows access to the Capybara finder" do
@test_class_instance.visit('/with_html')
expect(@test_class_instance.all(:css, 'h2.head').size).to eq(5)
end
it "allows access to the RSpec matcher" do
skip "RSpec < 3 doesn't have an `all` matcher" if rspec2?
@test_class_instance.visit('/with_html')
expect(["test1", "test2"]).to @test_class_instance.all(be_a(String))
end
end
context "#within" do
it "allows access to the Capybara scoper" do
@test_class_instance.visit('/with_html')
expect do
@test_class_instance.within(:css, "#does_not_exist") { @test_class_instance.click_link "Go to simple" }
end.to raise_error(Capybara::ElementNotFound)
end
it "allows access to the RSpec matcher" do
skip "RSpec version doesn't have a 'within' matcher" unless ::RSpec::Matchers.instance_methods.include?(:within)
@test_class_instance.visit('/with_html')
# This reads terribly, but must call #within
expect(@test_class_instance.find(:css, 'span.number').text.to_i).to @test_class_instance.within(1).of(41)
end
end
end
end
RSpec.describe 'capybara/rspec', :type => :other do

View File

@ -8,3 +8,8 @@ RSpec.configure do |config|
config.filter_run_including focus_: true unless ENV['TRAVIS']
config.run_all_when_everything_filtered = true
end
def rspec2?
!defined?(::RSpec::Expectations::Version) || (Gem::Version.new(RSpec::Expectations::Version::STRING) < Gem::Version.new('3.0'))
end