Make compound rspec matchers not run sequentially
This commit is contained in:
parent
9c3b053676
commit
0a63937e38
|
@ -0,0 +1,95 @@
|
|||
module Capybara
|
||||
module RSpecMatchers
|
||||
module Compound
|
||||
include ::RSpec::Matchers::Composable
|
||||
|
||||
def and(matcher)
|
||||
Capybara::RSpecMatchers::Compound::And.new(self,matcher)
|
||||
end
|
||||
|
||||
def and_then(matcher)
|
||||
::RSpec::Matchers::BuiltIn::Compound::And.new(self, matcher)
|
||||
end
|
||||
|
||||
def or(matcher)
|
||||
Capybara::RSpecMatchers::Compound::Or.new(self, matcher)
|
||||
end
|
||||
|
||||
|
||||
class CapybaraEvaluator
|
||||
def initialize(actual, matcher_1, matcher_2)
|
||||
@actual = actual
|
||||
@matcher_1 = matcher_1
|
||||
@matcher_2 = matcher_2
|
||||
@match_results = Hash.new { |h, matcher| h[matcher] = matcher.matches?(@actual) }
|
||||
end
|
||||
|
||||
def matcher_matches?(matcher)
|
||||
@match_results[matcher]
|
||||
end
|
||||
|
||||
def reset
|
||||
@match_results.clear
|
||||
end
|
||||
end
|
||||
|
||||
class And < ::RSpec::Matchers::BuiltIn::Compound::And
|
||||
|
||||
private
|
||||
|
||||
def match(_expected, actual)
|
||||
@evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
|
||||
syncer = sync_element(actual)
|
||||
begin
|
||||
syncer.synchronize do
|
||||
@evaluator.reset
|
||||
raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].all?
|
||||
true
|
||||
end
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def sync_element(el)
|
||||
if el.respond_to? :synchronize
|
||||
el
|
||||
elsif el.respond_to? :current_scope
|
||||
el.current_scope
|
||||
else
|
||||
Capybara.string(el)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Or < ::RSpec::Matchers::BuiltIn::Compound::Or
|
||||
|
||||
private
|
||||
|
||||
def match(_expected, actual)
|
||||
@evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
|
||||
syncer = sync_element(actual)
|
||||
begin
|
||||
syncer.synchronize do
|
||||
@evaluator.reset
|
||||
raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].any?
|
||||
true
|
||||
end
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def sync_element(el)
|
||||
if el.respond_to? :synchronize
|
||||
el
|
||||
elsif el.respond_to? :current_scope
|
||||
el.current_scope
|
||||
else
|
||||
Capybara.string(el)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,7 +2,10 @@
|
|||
module Capybara
|
||||
module RSpecMatchers
|
||||
class Matcher
|
||||
include ::RSpec::Matchers::Composable if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.0'))
|
||||
if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.0'))
|
||||
require 'capybara/rspec/compound'
|
||||
include ::Capybara::RSpecMatchers::Compound
|
||||
end
|
||||
|
||||
attr_reader :failure_message, :failure_message_when_negated
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
require 'spec_helper'
|
||||
require 'capybara/dsl'
|
||||
require 'capybara/rspec/matchers'
|
||||
require 'benchmark'
|
||||
|
||||
RSpec.shared_examples Capybara::RSpecMatchers do |session, mode|
|
||||
|
||||
|
@ -825,4 +826,100 @@ RSpec.shared_examples Capybara::RSpecMatchers do |session, mode|
|
|||
expect(html).to have_table('nope').or have_table('Lovely table')
|
||||
end if RSpec::Version::STRING.to_f >= 3.0
|
||||
end
|
||||
|
||||
if RSpec::Version::STRING.to_f >= 3.0
|
||||
context "compounding", requires: [:js] do
|
||||
before(:each) do
|
||||
@session = session
|
||||
@session.visit('/with_js')
|
||||
@el = @session.find(:css, '#reload-me')
|
||||
end
|
||||
|
||||
context "#and" do
|
||||
it "should run 'concurrently'" do
|
||||
Capybara.using_wait_time(2) do
|
||||
matcher = have_text('this is not there').and have_text('neither is this')
|
||||
expect(Benchmark.realtime do
|
||||
expect {
|
||||
expect(@el).to matcher
|
||||
}.to raise_error RSpec::Expectations::ExpectationNotMetError
|
||||
end).to be_between(2,3)
|
||||
end
|
||||
end
|
||||
|
||||
it "should run 'concurrently' and retry" do
|
||||
@session.click_link('reload-link')
|
||||
@session.using_wait_time(2) do
|
||||
expect(Benchmark.realtime do
|
||||
expect {
|
||||
expect(@el).to have_text('waiting to be reloaded').and(have_text('has been reloaded'))
|
||||
}.to raise_error RSpec::Expectations::ExpectationNotMetError, /expected to find text "waiting to be reloaded" in "has been reloaded"/
|
||||
end).to be_between(2,3)
|
||||
end
|
||||
end
|
||||
|
||||
it "should ignore :wait options" do
|
||||
@session.using_wait_time(2) do
|
||||
matcher = have_text('this is not there', wait: 5).and have_text('neither is this', wait: 6)
|
||||
expect(Benchmark.realtime do
|
||||
expect {
|
||||
expect(@el).to matcher
|
||||
}.to raise_error RSpec::Expectations::ExpectationNotMetError
|
||||
end).to be_between(2,3)
|
||||
end
|
||||
end
|
||||
|
||||
it "should work on the session" do
|
||||
@session.using_wait_time(2) do
|
||||
@session.click_link('reload-link')
|
||||
expect(@session).to have_selector(:css, 'h1', text: 'FooBar').and have_text('has been reloaded')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#and_then" do
|
||||
it "should run sequentially" do
|
||||
@session.click_link('reload-link')
|
||||
expect(@el).to have_text('waiting to be reloaded').and_then have_text('has been reloaded')
|
||||
end
|
||||
end
|
||||
|
||||
context "#or" do
|
||||
it "should run 'concurrently'" do
|
||||
@session.using_wait_time(3) do
|
||||
expect(Benchmark.realtime do
|
||||
expect(@el).to have_text('has been reloaded').or have_text('waiting to be reloaded')
|
||||
end).to be < 1
|
||||
end
|
||||
end
|
||||
|
||||
it "should retry" do
|
||||
@session.using_wait_time(3) do
|
||||
expect(Benchmark.realtime do
|
||||
expect {
|
||||
expect(@el).to have_text('has been reloaded').or have_text('random stuff')
|
||||
}.to raise_error RSpec::Expectations::ExpectationNotMetError
|
||||
end).to be > 3
|
||||
end
|
||||
end
|
||||
|
||||
it "should ignore :wait options" do
|
||||
@session.using_wait_time(2) do
|
||||
expect(Benchmark.realtime do
|
||||
expect {
|
||||
expect(@el).to have_text('this is not there', wait: 10).or have_text('neither is this', wait: 15)
|
||||
}.to raise_error RSpec::Expectations::ExpectationNotMetError
|
||||
end).to be_between(2,3)
|
||||
end
|
||||
end
|
||||
|
||||
it "should work on the session" do
|
||||
@session.using_wait_time(2) do
|
||||
@session.click_link('reload-link')
|
||||
expect(@session).to have_selector(:css, 'h1', text: 'Not on the page').or have_text('has been reloaded')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue