capybara-compose 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|