capybara-compose 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +27 -0
- data/README.md +124 -0
- data/lib/capybara/compose.rb +74 -0
- data/lib/capybara/compose/actions.rb +116 -0
- data/lib/capybara/compose/assertions.rb +142 -0
- data/lib/capybara/compose/benchmark_helpers.rb +87 -0
- data/lib/capybara/compose/cucumber.rb +12 -0
- data/lib/capybara/compose/dependency_injection.rb +44 -0
- data/lib/capybara/compose/finders.rb +40 -0
- data/lib/capybara/compose/matchers.rb +79 -0
- data/lib/capybara/compose/rspec.rb +33 -0
- data/lib/capybara/compose/selectors.rb +178 -0
- data/lib/capybara/compose/synchronization.rb +41 -0
- data/lib/capybara/compose/test_helper.rb +171 -0
- data/lib/capybara/compose/to_or_expectation_handler.rb +23 -0
- data/lib/capybara/compose/version.rb +8 -0
- data/lib/generators/test_helper/test_helper_generator.rb +34 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 037e1213be976eef937bb06defb927fc534915273d3f0b01388c78a0f9f69021
|
4
|
+
data.tar.gz: 64cf4560fc3a53a2333c02addb42336d768dbb7d115a41d18a26b584a0205884
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d35e1c57511578085cf4b7484b933d31dbbc16238b5c931d6592e389408d3b4b0d4ceb27bfba61682c2860dc6a9f22b662dacc928993c82a65d0445e0f2b2888
|
7
|
+
data.tar.gz: 9a48802d63b7b580f76ccc37e458587de08d6ae3374c21a46f3f650fa238815cce1bc0f79f9d0d9073735919dda9ea3cb6a093f926d0bb7ccfd6740ce999763e
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
## Capybara Compose 2.0.0 (2021-02-24) ##
|
2
|
+
|
3
|
+
* Rebrand _Capybara Test Helpers_ as _Capybara Compose_, and emphasize how the advantage is in reusing modules shared by the community.
|
4
|
+
|
5
|
+
## Capybara Test Helpers 1.0.4 (2020-11-26) ##
|
6
|
+
|
7
|
+
* Add `have_no` alias for `have_no_selector`.
|
8
|
+
* Add `has_no?` alias for `has_no_selector?`.
|
9
|
+
* Remove internal method `wrap_test_helper`.
|
10
|
+
* Allow to use locator aliases in `assert_` methods as well, for consistency.
|
11
|
+
|
12
|
+
## Capybara Test Helpers 1.0.3 (2020-11-24) ##
|
13
|
+
|
14
|
+
* Add `Capybara::Compose::BenchmarkHelpers`
|
15
|
+
|
16
|
+
## Capybara Test Helpers 1.0.2 (2020-11-23) ##
|
17
|
+
|
18
|
+
* Add `aliases` DSL to define `SELECTORS`.
|
19
|
+
|
20
|
+
## Capybara Test Helpers 1.0.1 (2020-11-19) ##
|
21
|
+
|
22
|
+
* Add `has?` alias for `has_selector?`.
|
23
|
+
* Delegate `have` to the RSpec collection matcher when passing an Integer.
|
24
|
+
|
25
|
+
## Capybara Test Helpers 1.0.0 (2020-11-17) ##
|
26
|
+
|
27
|
+
* Initial Release.
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
[docs]: https://capybara-test-helpers.netlify.app/
|
2
|
+
[guide]: https://capybara-test-helpers.netlify.app/guide/
|
3
|
+
[api]: https://capybara-test-helpers.netlify.app/api/
|
4
|
+
[design patterns]: https://capybara-test-helpers.netlify.app/guide/advanced/design-patterns
|
5
|
+
[installation]: https://capybara-test-helpers.netlify.app/installation
|
6
|
+
[capybara]: https://github.com/teamcapybara/capybara
|
7
|
+
[cucumber]: https://github.com/cucumber/cucumber-ruby
|
8
|
+
[current context]: https://capybara-test-helpers.netlify.app/guide/essentials/current-context
|
9
|
+
[rspec]: https://github.com/rspec/rspec
|
10
|
+
[aliases]: https://capybara-test-helpers.netlify.app/guide/essentials/aliases
|
11
|
+
[assertions]: https://capybara-test-helpers.netlify.app/guide/essentials/assertions
|
12
|
+
[synchronization]: https://capybara-test-helpers.netlify.app/guide/advanced/synchronization
|
13
|
+
|
14
|
+
<h1 align="center">
|
15
|
+
Capybara Test Helpers
|
16
|
+
<p align="center">
|
17
|
+
<a href="https://github.com/ElMassimo/capybara-compose/actions">
|
18
|
+
<img alt="Build Status" src="https://github.com/ElMassimo/capybara-compose/workflows/build/badge.svg"/>
|
19
|
+
</a>
|
20
|
+
<a href="https://codeclimate.com/github/ElMassimo/capybara-compose">
|
21
|
+
<img alt="Maintainability" src="https://codeclimate.com/github/ElMassimo/capybara-compose/badges/gpa.svg"/>
|
22
|
+
</a>
|
23
|
+
<a href="https://codeclimate.com/github/ElMassimo/capybara-compose">
|
24
|
+
<img alt="Test Coverage" src="https://codeclimate.com/github/ElMassimo/capybara-compose/badges/coverage.svg"/>
|
25
|
+
</a>
|
26
|
+
<a href="https://rubygems.org/gems/capybara-compose">
|
27
|
+
<img alt="Gem Version" src="https://img.shields.io/gem/v/capybara-compose.svg?colorB=e9573f"/>
|
28
|
+
</a>
|
29
|
+
<a href="https://github.com/ElMassimo/capybara-compose/blob/main/LICENSE.txt">
|
30
|
+
<img alt="License" src="https://img.shields.io/badge/license-MIT-428F7E.svg"/>
|
31
|
+
</a>
|
32
|
+
</p>
|
33
|
+
</h1>
|
34
|
+
|
35
|
+
[__Capybara Test Helpers__](https://github.com/ElMassimo/capybara-compose) allows you to easily encapsulate logic in your integration tests.
|
36
|
+
|
37
|
+
Write tests that everyone can understand, and leverage your Ruby skills to keep them __easy to read and easy to change__.
|
38
|
+
|
39
|
+
## Features ⚡️
|
40
|
+
|
41
|
+
[Locator Aliases][aliases] work with every [Capybara] method, allowing you to encapsulate CSS selectors and labels, and avoid coupling tests with the implementation.
|
42
|
+
|
43
|
+
The [entire Capybara DSL is available][api], and element results are [wrapped automatically][current context] so that you can chain your own assertions and actions fluently.
|
44
|
+
|
45
|
+
A [powerful syntax for assertions][assertions] and convenient primitives for [synchronization] enable you to write async-aware expectations: say goodbye to flaky tests.
|
46
|
+
|
47
|
+
## Documentation 📖
|
48
|
+
|
49
|
+
Visit the [documentation website][docs] to check out the [guides][guide], searchable [__API reference__][api], and examples.
|
50
|
+
|
51
|
+
## Installation 💿
|
52
|
+
|
53
|
+
Add this line to your application's Gemfile:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
gem 'capybara/compose'
|
57
|
+
```
|
58
|
+
|
59
|
+
To use with [RSpec], add the following to your `spec_helper.rb`:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
require 'capybara/compose/rspec'
|
63
|
+
```
|
64
|
+
|
65
|
+
To use with [Cucumber], add the following to your `support/env.rb`:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require 'capybara/compose/cucumber'
|
69
|
+
```
|
70
|
+
|
71
|
+
Additional installation instructions are available in the [documentation website][installation].
|
72
|
+
|
73
|
+
## Quick Tour 🛣
|
74
|
+
|
75
|
+
Let's say we have a list of cities, and we want to test the _Edit_ functionality using [Capybara].
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
scenario 'editing a city' do
|
79
|
+
visit('/cities')
|
80
|
+
|
81
|
+
within('.cities') {
|
82
|
+
find(:table_row, { 'Name' => 'NYC' }).click_on('Edit')
|
83
|
+
}
|
84
|
+
fill_in 'Name', with: 'New York City'
|
85
|
+
click_on('Update City')
|
86
|
+
|
87
|
+
within('.cities') {
|
88
|
+
expect(page).not_to have_selector(:table_row, { 'Name' => 'NYC' })
|
89
|
+
expect(page).to have_selector(:table_row, { 'Name' => 'New York City' })
|
90
|
+
}
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
Even though it gets the job done, it takes a while to understand what the test is trying to do.
|
95
|
+
|
96
|
+
Without discipline these tests can become __hard to manage__ and require __frequent updating__.
|
97
|
+
|
98
|
+
### Using Test Helpers
|
99
|
+
|
100
|
+
We can avoid the duplication and keep the [focus on the test][design patterns] instead of its
|
101
|
+
implementation by using [__test helpers__][docs].
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
scenario 'editing a city', test_helpers: [:cities] do
|
105
|
+
cities.visit_page
|
106
|
+
|
107
|
+
cities.edit('NYC', with: { name: 'New York City' })
|
108
|
+
|
109
|
+
cities.should_no_longer.have_city('NYC')
|
110
|
+
cities.should_now.have_city('New York City')
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
Learn more about it in the [documentation website][docs].
|
115
|
+
|
116
|
+
## Special Thanks 🙏
|
117
|
+
|
118
|
+
This library wouldn't be the same without early validation from my colleagues, and numerous improvements and bugfixes they contributed to it. Thanks for the support 😃
|
119
|
+
|
120
|
+
- [capybara]: Excellent library to write integration tests in Ruby.
|
121
|
+
|
122
|
+
## License
|
123
|
+
|
124
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara'
|
4
|
+
|
5
|
+
require 'zeitwerk'
|
6
|
+
loader = Zeitwerk::Loader.for_gem
|
7
|
+
loader.tag = 'capybara-compose'
|
8
|
+
loader.ignore(
|
9
|
+
File.expand_path("#{ __dir__ }/compose/rspec.rb"),
|
10
|
+
File.expand_path("#{ __dir__ }/compose/cucumber.rb"),
|
11
|
+
File.expand_path("#{ __dir__ }/compose/minitest.rb"),
|
12
|
+
)
|
13
|
+
loader.push_dir(__dir__, namespace: Capybara)
|
14
|
+
loader.setup
|
15
|
+
|
16
|
+
# Internal: Global configuration for the library.
|
17
|
+
module Capybara::Compose
|
18
|
+
DEFAULT_CONFIGURATION = {
|
19
|
+
helpers_paths: ['test_helpers'].freeze,
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
# Internal: Reserved methods for Capybara::TestHelper.
|
23
|
+
test_helper_methods = [
|
24
|
+
:page,
|
25
|
+
:find_element,
|
26
|
+
:should,
|
27
|
+
:should_not,
|
28
|
+
:not_to,
|
29
|
+
].freeze
|
30
|
+
|
31
|
+
# Internal: Methods that are in the Capybara DSL but are so common that we
|
32
|
+
# don't want to issue a warning if they are used as selectors.
|
33
|
+
SKIPPED_DSL_METHODS = [
|
34
|
+
:title,
|
35
|
+
:body,
|
36
|
+
:html,
|
37
|
+
].freeze
|
38
|
+
|
39
|
+
# Internal: Methods that should not be overiden or used as locator aliases to
|
40
|
+
# avoid confusion while working on test helpers.
|
41
|
+
RESERVED_METHODS = (Capybara::Session::DSL_METHODS - SKIPPED_DSL_METHODS + test_helper_methods).to_set.freeze
|
42
|
+
|
43
|
+
# Internal: Ruby 2.7 swallows keyword arguments, so for methods that take a
|
44
|
+
# Hash as the first argument as well as keyword arguments, we need to manually
|
45
|
+
# detect and move them to args if empty.
|
46
|
+
METHODS_EXPECTING_A_HASH = %i[matches_style? has_style? match_style have_style].to_set.freeze
|
47
|
+
|
48
|
+
# Internal: Struct for configuration.
|
49
|
+
Config = Struct.new(*DEFAULT_CONFIGURATION.keys)
|
50
|
+
|
51
|
+
# Public: Returns the current configuration for the test helpers.
|
52
|
+
def self.config
|
53
|
+
@config ||= Config.new(*DEFAULT_CONFIGURATION.values)
|
54
|
+
yield @config if block_given?
|
55
|
+
@config
|
56
|
+
end
|
57
|
+
|
58
|
+
# Internal: Allows to define methods that are a part of the Capybara DSL, as
|
59
|
+
# well as RSpec matchers.
|
60
|
+
def self.define_helper_method(klass, method_name, wrap: false, assertion: false, target: 'current_context', return_self: assertion, inject_test_helper: true)
|
61
|
+
klass.class_eval <<~HELPER, __FILE__, __LINE__ + 1
|
62
|
+
def #{ method_name }(*args, **kwargs, &filter)
|
63
|
+
#{ 'args.push(kwargs) && (kwargs = {}) if args.empty?' if METHODS_EXPECTING_A_HASH.include?(method_name) }
|
64
|
+
#{ 'kwargs[:test_helper] = self' if inject_test_helper }
|
65
|
+
#{ 'wrap_element ' if wrap }#{ assertion ? "expect(#{ target }).to_or not_to, test_context" : target }.#{ method_name }(*args, **kwargs, &filter)
|
66
|
+
#{ 'self' if return_self }
|
67
|
+
end
|
68
|
+
HELPER
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# NOTE: Simplify migration from Capybara Test Helpers.
|
73
|
+
CapybaraTestHelpers = Capybara::Compose unless defined?(CapybaraTestHelpers)
|
74
|
+
Capybara::TestHelper = Capybara::Compose::TestHelper unless defined?(Capybara::TestHelper)
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Internal: Allows to pass test helpers as arguments to `evaluate_script`.
|
4
|
+
Capybara::Session.prepend(Module.new {
|
5
|
+
private
|
6
|
+
|
7
|
+
# Override: To ensure test helpers are sent to the driver as native elements.
|
8
|
+
def driver_args(args)
|
9
|
+
super(args.map { |arg| arg.is_a?(Capybara::Compose::TestHelper) ? arg.to_capybara_node : arg })
|
10
|
+
end
|
11
|
+
})
|
12
|
+
|
13
|
+
# Internal: Allows to pass a test helper to `scroll_to`.
|
14
|
+
Capybara::Node::Element.prepend(Module.new {
|
15
|
+
# Override: Unwrap capybara test helpers into a node.
|
16
|
+
def scroll_to(pos_or_x_or_el, *args, **kwargs)
|
17
|
+
pos_or_x_or_el = pos_or_x_or_el.to_capybara_node if pos_or_x_or_el.is_a?(Capybara::Compose::TestHelper)
|
18
|
+
super
|
19
|
+
end
|
20
|
+
})
|
21
|
+
|
22
|
+
# Internal: Wraps Capybara actions to enable locator aliases, and to wrap the
|
23
|
+
# result with a test helper so that methods can be chained in a fluent style.
|
24
|
+
module Capybara::Compose::Actions
|
25
|
+
delegate(
|
26
|
+
:==, # Allows to make comparisons more transparent.
|
27
|
+
:===, # Allows to ensure case statements inside Capybara work as expected.
|
28
|
+
to: :to_capybara_node,
|
29
|
+
)
|
30
|
+
|
31
|
+
delegate(
|
32
|
+
:[],
|
33
|
+
:all_text,
|
34
|
+
:checked?,
|
35
|
+
:disabled?,
|
36
|
+
:multiple?,
|
37
|
+
:obscured?,
|
38
|
+
:readonly?,
|
39
|
+
:selected?,
|
40
|
+
:base,
|
41
|
+
:native,
|
42
|
+
:path,
|
43
|
+
:rect,
|
44
|
+
:style,
|
45
|
+
:tag_name,
|
46
|
+
:text,
|
47
|
+
:value,
|
48
|
+
:visible?,
|
49
|
+
:visible_text,
|
50
|
+
to: :to_capybara_node,
|
51
|
+
)
|
52
|
+
|
53
|
+
delegate(
|
54
|
+
:evaluate_script,
|
55
|
+
:evaluate_async_script,
|
56
|
+
to: :current_context,
|
57
|
+
)
|
58
|
+
|
59
|
+
%i[
|
60
|
+
execute_script
|
61
|
+
scroll_to
|
62
|
+
].each do |method_name|
|
63
|
+
Capybara::Compose.define_helper_method(self, method_name, return_self: true, inject_test_helper: false)
|
64
|
+
end
|
65
|
+
|
66
|
+
%i[
|
67
|
+
click
|
68
|
+
double_click
|
69
|
+
drag_to
|
70
|
+
drop
|
71
|
+
flash
|
72
|
+
hover
|
73
|
+
reload
|
74
|
+
right_click
|
75
|
+
select_option
|
76
|
+
send_keys
|
77
|
+
set
|
78
|
+
trigger
|
79
|
+
unselect_option
|
80
|
+
].each do |method_name|
|
81
|
+
Capybara::Compose.define_helper_method(self, method_name, target: :to_capybara_node, return_self: true, inject_test_helper: false)
|
82
|
+
end
|
83
|
+
|
84
|
+
%i[
|
85
|
+
click_on
|
86
|
+
click_link_or_button
|
87
|
+
click_button
|
88
|
+
click_link
|
89
|
+
choose
|
90
|
+
check
|
91
|
+
uncheck
|
92
|
+
fill_in
|
93
|
+
attach_file
|
94
|
+
select
|
95
|
+
unselect
|
96
|
+
].each do |method_name|
|
97
|
+
Capybara::Compose.define_helper_method(self, method_name, wrap: true)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Public: Sets the value for the input, or presses the specified keys, one at a time.
|
101
|
+
def type_in(*text, typing: text.size > 1 || text.first.is_a?(Symbol) || text.first.is_a?(Array), **options)
|
102
|
+
typing ? send_keys(*text) : set(text.first, **options)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Public: Useful to natively give focus to an element.
|
106
|
+
def focus
|
107
|
+
to_capybara_node.execute_script('this.focus()')
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
# Public: Useful to natively blur an element.
|
112
|
+
def blur
|
113
|
+
to_capybara_node.execute_script('this.blur()')
|
114
|
+
self
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# NOTE: Optional dependency to enable `to_or`.
|
4
|
+
begin
|
5
|
+
require 'rspec/expectations'
|
6
|
+
RSpec::Expectations::ExpectationTarget.include(Capybara::Compose::ToOrExpectationHandler)
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
10
|
+
# Internal: Wraps RSpec matchers to allow using them after calling `should` or
|
11
|
+
# `should_not` to perform the assertion.
|
12
|
+
module Capybara::Compose::Assertions
|
13
|
+
# Public: Returns a test helper with a positive assertion state.
|
14
|
+
# Any assertions called after it will execute as `expect(...).to ...`.
|
15
|
+
def should(negated = false)
|
16
|
+
negated = !!negated # Coerce to boolean.
|
17
|
+
return self if negated == @negated
|
18
|
+
|
19
|
+
clone.tap { |test_helper| test_helper.instance_variable_set('@negated', negated) }
|
20
|
+
end
|
21
|
+
[:should_still, :should_now, :and, :and_instead, :and_also, :and_still].each { |should_alias| alias_method should_alias, :should }
|
22
|
+
|
23
|
+
# Public: Returns a test helper with a negative assertion state.
|
24
|
+
# Any assertions called after it will execute as `expect(...).not_to ...`.
|
25
|
+
def should_not
|
26
|
+
@negated ? self : should(true)
|
27
|
+
end
|
28
|
+
[:should_still_not, :should_no_longer, :nor, :and_not].each { |should_alias| alias_method should_alias, :should_not }
|
29
|
+
|
30
|
+
# Public: Makes it more readable when in used in combination with to_or.
|
31
|
+
def not_to
|
32
|
+
raise(ArgumentError, 'You must call `should` or `should_not` before calling this method') if @negated.nil?
|
33
|
+
|
34
|
+
@negated
|
35
|
+
end
|
36
|
+
alias or_should_not not_to
|
37
|
+
|
38
|
+
# Public: Allows to write complex nested assertions.
|
39
|
+
def invert_expectation
|
40
|
+
should(!not_to)
|
41
|
+
end
|
42
|
+
|
43
|
+
%i[
|
44
|
+
have_selector
|
45
|
+
have_no_selector
|
46
|
+
have_css
|
47
|
+
have_no_css
|
48
|
+
have_xpath
|
49
|
+
have_no_xpath
|
50
|
+
have_link
|
51
|
+
have_no_link
|
52
|
+
have_button
|
53
|
+
have_no_button
|
54
|
+
have_field
|
55
|
+
have_no_field
|
56
|
+
have_select
|
57
|
+
have_no_select
|
58
|
+
have_table
|
59
|
+
have_no_table
|
60
|
+
have_checked_field
|
61
|
+
have_no_checked_field
|
62
|
+
have_unchecked_field
|
63
|
+
have_no_unchecked_field
|
64
|
+
have_all_of_selectors
|
65
|
+
have_none_of_selectors
|
66
|
+
have_any_of_selectors
|
67
|
+
have_title
|
68
|
+
have_no_title
|
69
|
+
].each do |method_name|
|
70
|
+
Capybara::Compose.define_helper_method(self, method_name, assertion: true)
|
71
|
+
end
|
72
|
+
|
73
|
+
%i[
|
74
|
+
have_ancestor
|
75
|
+
have_no_ancestor
|
76
|
+
have_sibling
|
77
|
+
have_no_sibling
|
78
|
+
match_selector
|
79
|
+
not_match_selector
|
80
|
+
match_css
|
81
|
+
not_match_css
|
82
|
+
match_xpath
|
83
|
+
not_match_xpath
|
84
|
+
have_text
|
85
|
+
have_no_text
|
86
|
+
have_content
|
87
|
+
have_no_content
|
88
|
+
match_style
|
89
|
+
have_style
|
90
|
+
].each do |method_name|
|
91
|
+
Capybara::Compose.define_helper_method(self, method_name, target: :to_capybara_node, assertion: true)
|
92
|
+
end
|
93
|
+
|
94
|
+
%i[
|
95
|
+
have_current_path
|
96
|
+
have_no_current_path
|
97
|
+
].each do |method_name|
|
98
|
+
Capybara::Compose.define_helper_method(self, method_name, target: :page, assertion: true, inject_test_helper: false)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Public: Allows to call have_selector with a shorter syntax.
|
102
|
+
def have(*args, **kwargs, &filter)
|
103
|
+
if args.first.is_a?(Integer)
|
104
|
+
::RSpec::CollectionMatchers::Have.new(*args, **kwargs)
|
105
|
+
else
|
106
|
+
have_selector(*args, **kwargs, &filter)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
alias have_no have_no_selector
|
111
|
+
|
112
|
+
# Public: Allows to check on any input value asynchronously.
|
113
|
+
def have_value(expected_value, **options)
|
114
|
+
synchronize_expectation(**options) { expect(value).to_or not_to, eq(expected_value) }
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
BE_PREDICATE_REGEX = /^(?:be_(?:an?_)?)(.*)/
|
121
|
+
HAS_REGEX = /^(?:have_)(.*)/
|
122
|
+
DYNAMIC_MATCHER_REGEX = Regexp.union(BE_PREDICATE_REGEX, HAS_REGEX)
|
123
|
+
|
124
|
+
# Internal: Override the method_missing defined in RSpec::Matchers to avoid
|
125
|
+
# accidentally calling a predicate or has matcher instead of an assertion.
|
126
|
+
def method_missing(method, *args, **kwargs, &block)
|
127
|
+
case method.to_s
|
128
|
+
when DYNAMIC_MATCHER_REGEX
|
129
|
+
raise NoMethodError, "undefined method `#{ method }' for #{ inspect }.\nUse `delegate_to_test_context(:#{ method })` in the test helper class if you plan to use it often, or `test_context.#{ method }` as needed in the instance."
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Internal: Override the method_missing defined in RSpec::Matchers to avoid
|
136
|
+
# accidentally calling a predicate or has matcher instead of an assertion.
|
137
|
+
def respond_to_missing?(method, *)
|
138
|
+
return false if method =~ DYNAMIC_MATCHER_REGEX
|
139
|
+
|
140
|
+
super
|
141
|
+
end
|
142
|
+
end
|