rspec-expectations 2.11.3 → 3.11.0
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
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +1026 -21
- data/{License.txt → LICENSE.md} +5 -3
- data/README.md +174 -78
- data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
- data/lib/rspec/expectations/configuration.rb +230 -0
- data/lib/rspec/expectations/expectation_target.rb +130 -55
- data/lib/rspec/expectations/fail_with.rb +17 -33
- data/lib/rspec/expectations/failure_aggregator.rb +212 -0
- data/lib/rspec/expectations/handler.rb +163 -29
- data/lib/rspec/expectations/minitest_integration.rb +58 -0
- data/lib/rspec/expectations/syntax.rb +68 -54
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/expectations.rb +59 -24
- data/lib/rspec/matchers/aliased_matcher.rb +116 -0
- data/lib/rspec/matchers/built_in/all.rb +86 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +150 -20
- data/lib/rspec/matchers/built_in/be.rb +115 -109
- data/lib/rspec/matchers/built_in/be_between.rb +77 -0
- data/lib/rspec/matchers/built_in/be_instance_of.rb +16 -1
- data/lib/rspec/matchers/built_in/be_kind_of.rb +10 -1
- data/lib/rspec/matchers/built_in/be_within.rb +43 -17
- data/lib/rspec/matchers/built_in/change.rb +392 -75
- data/lib/rspec/matchers/built_in/compound.rb +290 -0
- data/lib/rspec/matchers/built_in/contain_exactly.rb +302 -0
- data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
- data/lib/rspec/matchers/built_in/cover.rb +3 -0
- data/lib/rspec/matchers/built_in/eq.rb +26 -8
- data/lib/rspec/matchers/built_in/eql.rb +19 -8
- data/lib/rspec/matchers/built_in/equal.rb +56 -19
- data/lib/rspec/matchers/built_in/exist.rb +74 -10
- data/lib/rspec/matchers/built_in/has.rb +141 -22
- data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
- data/lib/rspec/matchers/built_in/include.rb +175 -20
- data/lib/rspec/matchers/built_in/match.rb +95 -1
- data/lib/rspec/matchers/built_in/operators.rb +128 -0
- data/lib/rspec/matchers/built_in/output.rb +207 -0
- data/lib/rspec/matchers/built_in/raise_error.rb +212 -38
- data/lib/rspec/matchers/built_in/respond_to.rb +155 -29
- data/lib/rspec/matchers/built_in/satisfy.rb +39 -9
- data/lib/rspec/matchers/built_in/start_or_end_with.rb +94 -0
- data/lib/rspec/matchers/built_in/throw_symbol.rb +58 -14
- data/lib/rspec/matchers/built_in/yield.rb +252 -98
- data/lib/rspec/matchers/built_in.rb +47 -33
- data/lib/rspec/matchers/composable.rb +171 -0
- data/lib/rspec/matchers/dsl.rb +530 -10
- data/lib/rspec/matchers/english_phrasing.rb +58 -0
- data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
- data/lib/rspec/matchers/fail_matchers.rb +42 -0
- data/lib/rspec/matchers/generated_descriptions.rb +15 -10
- data/lib/rspec/matchers/matcher_delegator.rb +35 -0
- data/lib/rspec/matchers/matcher_protocol.rb +105 -0
- data/lib/rspec/matchers.rb +604 -252
- data.tar.gz.sig +0 -0
- metadata +178 -278
- metadata.gz.sig +0 -0
- data/features/README.md +0 -49
- data/features/Upgrade.md +0 -53
- data/features/built_in_matchers/README.md +0 -90
- data/features/built_in_matchers/be.feature +0 -173
- data/features/built_in_matchers/be_within.feature +0 -46
- data/features/built_in_matchers/cover.feature +0 -45
- data/features/built_in_matchers/end_with.feature +0 -46
- data/features/built_in_matchers/equality.feature +0 -145
- data/features/built_in_matchers/exist.feature +0 -43
- data/features/built_in_matchers/expect_change.feature +0 -59
- data/features/built_in_matchers/expect_error.feature +0 -138
- data/features/built_in_matchers/have.feature +0 -103
- data/features/built_in_matchers/include.feature +0 -121
- data/features/built_in_matchers/match.feature +0 -50
- data/features/built_in_matchers/operators.feature +0 -221
- data/features/built_in_matchers/predicates.feature +0 -128
- data/features/built_in_matchers/respond_to.feature +0 -78
- data/features/built_in_matchers/satisfy.feature +0 -31
- data/features/built_in_matchers/start_with.feature +0 -46
- data/features/built_in_matchers/throw_symbol.feature +0 -85
- data/features/built_in_matchers/types.feature +0 -114
- data/features/built_in_matchers/yield.feature +0 -146
- data/features/custom_matchers/access_running_example.feature +0 -53
- data/features/custom_matchers/define_diffable_matcher.feature +0 -27
- data/features/custom_matchers/define_matcher.feature +0 -340
- data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
- data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
- data/features/customized_message.feature +0 -22
- data/features/diffing.feature +0 -85
- data/features/implicit_docstrings.feature +0 -52
- data/features/step_definitions/additional_cli_steps.rb +0 -22
- data/features/support/env.rb +0 -5
- data/features/syntax_configuration.feature +0 -68
- data/features/test_frameworks/test_unit.feature +0 -46
- data/lib/rspec/expectations/deprecation.rb +0 -38
- data/lib/rspec/expectations/differ.rb +0 -81
- data/lib/rspec/expectations/errors.rb +0 -9
- data/lib/rspec/expectations/extensions/array.rb +0 -9
- data/lib/rspec/expectations/extensions/object.rb +0 -39
- data/lib/rspec/expectations/extensions.rb +0 -2
- data/lib/rspec/matchers/be_close.rb +0 -9
- data/lib/rspec/matchers/built_in/have.rb +0 -108
- data/lib/rspec/matchers/built_in/match_array.rb +0 -45
- data/lib/rspec/matchers/built_in/start_and_end_with.rb +0 -48
- data/lib/rspec/matchers/compatibility.rb +0 -14
- data/lib/rspec/matchers/configuration.rb +0 -66
- data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
- data/lib/rspec/matchers/matcher.rb +0 -299
- data/lib/rspec/matchers/method_missing.rb +0 -12
- data/lib/rspec/matchers/operator_matcher.rb +0 -84
- data/lib/rspec/matchers/pretty.rb +0 -60
- data/lib/rspec-expectations.rb +0 -1
- data/spec/rspec/expectations/differ_spec.rb +0 -153
- data/spec/rspec/expectations/expectation_target_spec.rb +0 -65
- data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
- data/spec/rspec/expectations/fail_with_spec.rb +0 -70
- data/spec/rspec/expectations/handler_spec.rb +0 -206
- data/spec/rspec/matchers/base_matcher_spec.rb +0 -60
- data/spec/rspec/matchers/be_close_spec.rb +0 -22
- data/spec/rspec/matchers/be_instance_of_spec.rb +0 -40
- data/spec/rspec/matchers/be_kind_of_spec.rb +0 -37
- data/spec/rspec/matchers/be_spec.rb +0 -452
- data/spec/rspec/matchers/be_within_spec.rb +0 -80
- data/spec/rspec/matchers/change_spec.rb +0 -528
- data/spec/rspec/matchers/configuration_spec.rb +0 -202
- data/spec/rspec/matchers/cover_spec.rb +0 -69
- data/spec/rspec/matchers/description_generation_spec.rb +0 -176
- data/spec/rspec/matchers/dsl_spec.rb +0 -57
- data/spec/rspec/matchers/eq_spec.rb +0 -54
- data/spec/rspec/matchers/eql_spec.rb +0 -41
- data/spec/rspec/matchers/equal_spec.rb +0 -60
- data/spec/rspec/matchers/exist_spec.rb +0 -110
- data/spec/rspec/matchers/has_spec.rb +0 -118
- data/spec/rspec/matchers/have_spec.rb +0 -461
- data/spec/rspec/matchers/include_spec.rb +0 -367
- data/spec/rspec/matchers/match_array_spec.rb +0 -124
- data/spec/rspec/matchers/match_spec.rb +0 -61
- data/spec/rspec/matchers/matcher_spec.rb +0 -434
- data/spec/rspec/matchers/matchers_spec.rb +0 -31
- data/spec/rspec/matchers/method_missing_spec.rb +0 -24
- data/spec/rspec/matchers/operator_matcher_spec.rb +0 -221
- data/spec/rspec/matchers/raise_error_spec.rb +0 -344
- data/spec/rspec/matchers/respond_to_spec.rb +0 -295
- data/spec/rspec/matchers/satisfy_spec.rb +0 -44
- data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -182
- data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
- data/spec/rspec/matchers/yield_spec.rb +0 -402
- data/spec/spec_helper.rb +0 -27
- data/spec/support/classes.rb +0 -56
- data/spec/support/in_sub_process.rb +0 -31
- data/spec/support/matchers.rb +0 -22
- data/spec/support/ruby_version.rb +0 -10
- data/spec/support/shared_examples.rb +0 -13
data/lib/rspec/expectations.rb
CHANGED
@@ -1,47 +1,82 @@
|
|
1
|
-
require 'rspec/
|
1
|
+
require 'rspec/support'
|
2
|
+
RSpec::Support.require_rspec_support "caller_filter"
|
3
|
+
RSpec::Support.require_rspec_support "warnings"
|
4
|
+
RSpec::Support.require_rspec_support "object_formatter"
|
5
|
+
|
2
6
|
require 'rspec/matchers'
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
|
8
|
+
RSpec::Support.define_optimized_require_for_rspec(:expectations) { |f| require_relative(f) }
|
9
|
+
|
10
|
+
%w[
|
11
|
+
expectation_target
|
12
|
+
configuration
|
13
|
+
fail_with
|
14
|
+
handler
|
15
|
+
version
|
16
|
+
].each { |file| RSpec::Support.require_rspec_expectations(file) }
|
11
17
|
|
12
18
|
module RSpec
|
13
|
-
# RSpec::Expectations
|
19
|
+
# RSpec::Expectations provides a simple, readable API to express
|
20
|
+
# the expected outcomes in a code example. To express an expected
|
21
|
+
# outcome, wrap an object or block in `expect`, call `to` or `to_not`
|
22
|
+
# (aliased as `not_to`) and pass it a matcher object:
|
14
23
|
#
|
15
|
-
#
|
16
|
-
#
|
24
|
+
# expect(order.total).to eq(Money.new(5.55, :USD))
|
25
|
+
# expect(list).to include(user)
|
26
|
+
# expect(message).not_to match(/foo/)
|
27
|
+
# expect { do_something }.to raise_error
|
17
28
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
29
|
+
# The last form (the block form) is needed to match against ruby constructs
|
30
|
+
# that are not objects, but can only be observed when executing a block
|
31
|
+
# of code. This includes raising errors, throwing symbols, yielding,
|
32
|
+
# and changing values.
|
33
|
+
#
|
34
|
+
# When `expect(...).to` is invoked with a matcher, it turns around
|
35
|
+
# and calls `matcher.matches?(<object wrapped by expect>)`. For example,
|
21
36
|
# in the expression:
|
22
37
|
#
|
23
|
-
# order.total.
|
38
|
+
# expect(order.total).to eq(Money.new(5.55, :USD))
|
24
39
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# `
|
28
|
-
# `eq.
|
40
|
+
# ...`eq(Money.new(5.55, :USD))` returns a matcher object, and it results
|
41
|
+
# in the equivalent of `eq.matches?(order.total)`. If `matches?` returns
|
42
|
+
# `true`, the expectation is met and execution continues. If `false`, then
|
43
|
+
# the spec fails with the message returned by `eq.failure_message`.
|
29
44
|
#
|
30
45
|
# Given the expression:
|
31
46
|
#
|
32
|
-
# order.entries.
|
47
|
+
# expect(order.entries).not_to include(entry)
|
33
48
|
#
|
34
|
-
# the `
|
49
|
+
# ...the `not_to` method (also available as `to_not`) invokes the equivalent of
|
35
50
|
# `include.matches?(order.entries)`, but it interprets `false` as success, and
|
36
51
|
# `true` as a failure, using the message generated by
|
37
|
-
# `
|
52
|
+
# `include.failure_message_when_negated`.
|
38
53
|
#
|
39
54
|
# rspec-expectations ships with a standard set of useful matchers, and writing
|
40
|
-
# your own matchers is quite simple.
|
55
|
+
# your own matchers is quite simple.
|
41
56
|
#
|
42
57
|
# See [RSpec::Matchers](../RSpec/Matchers) for more information about the
|
43
58
|
# built-in matchers that ship with rspec-expectations, and how to write your
|
44
59
|
# own custom matchers.
|
45
60
|
module Expectations
|
61
|
+
# Exception raised when an expectation fails.
|
62
|
+
#
|
63
|
+
# @note We subclass Exception so that in a stub implementation if
|
64
|
+
# the user sets an expectation, it can't be caught in their
|
65
|
+
# code by a bare `rescue`.
|
66
|
+
# @api public
|
67
|
+
class ExpectationNotMetError < Exception
|
68
|
+
end
|
69
|
+
|
70
|
+
# Exception raised from `aggregate_failures` when multiple expectations fail.
|
71
|
+
#
|
72
|
+
# @note The constant is defined here but the extensive logic of this class
|
73
|
+
# is lazily defined when `FailureAggregator` is autoloaded, since we do
|
74
|
+
# not need to waste time defining that functionality unless
|
75
|
+
# `aggregate_failures` is used.
|
76
|
+
class MultipleExpectationsNotMetError < ExpectationNotMetError
|
77
|
+
end
|
78
|
+
|
79
|
+
autoload :BlockSnippetExtractor, "rspec/expectations/block_snippet_extractor"
|
80
|
+
autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
|
46
81
|
end
|
47
82
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
# Decorator that wraps a matcher and overrides `description`
|
4
|
+
# using the provided block in order to support an alias
|
5
|
+
# of a matcher. This is intended for use when composing
|
6
|
+
# matchers, so that you can use an expression like
|
7
|
+
# `include( a_value_within(0.1).of(3) )` rather than
|
8
|
+
# `include( be_within(0.1).of(3) )`, and have the corresponding
|
9
|
+
# description read naturally.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class AliasedMatcher < MatcherDelegator
|
13
|
+
def initialize(base_matcher, description_block)
|
14
|
+
@description_block = description_block
|
15
|
+
super(base_matcher)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Forward messages on to the wrapped matcher.
|
19
|
+
# Since many matchers provide a fluent interface
|
20
|
+
# (e.g. `a_value_within(0.1).of(3)`), we need to wrap
|
21
|
+
# the returned value if it responds to `description`,
|
22
|
+
# so that our override can be applied when it is eventually
|
23
|
+
# used.
|
24
|
+
def method_missing(*)
|
25
|
+
return_val = super
|
26
|
+
return return_val unless RSpec::Matchers.is_a_matcher?(return_val)
|
27
|
+
self.class.new(return_val, @description_block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Provides the description of the aliased matcher. Aliased matchers
|
31
|
+
# are designed to behave identically to the original matcher except
|
32
|
+
# for the description and failure messages. The description is different
|
33
|
+
# to reflect the aliased name.
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
def description
|
37
|
+
@description_block.call(super)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Provides the failure_message of the aliased matcher. Aliased matchers
|
41
|
+
# are designed to behave identically to the original matcher except
|
42
|
+
# for the description and failure messages. The failure_message is different
|
43
|
+
# to reflect the aliased name.
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
def failure_message
|
47
|
+
@description_block.call(super)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Provides the failure_message_when_negated of the aliased matcher. Aliased matchers
|
51
|
+
# are designed to behave identically to the original matcher except
|
52
|
+
# for the description and failure messages. The failure_message_when_negated is different
|
53
|
+
# to reflect the aliased name.
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
def failure_message_when_negated
|
57
|
+
@description_block.call(super)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Decorator used for matchers that have special implementations of
|
62
|
+
# operators like `==` and `===`.
|
63
|
+
# @private
|
64
|
+
class AliasedMatcherWithOperatorSupport < AliasedMatcher
|
65
|
+
# We undef these so that they get delegated via `method_missing`.
|
66
|
+
undef ==
|
67
|
+
undef ===
|
68
|
+
end
|
69
|
+
|
70
|
+
# @private
|
71
|
+
class AliasedNegatedMatcher < AliasedMatcher
|
72
|
+
def matches?(*args, &block)
|
73
|
+
if @base_matcher.respond_to?(:does_not_match?)
|
74
|
+
@base_matcher.does_not_match?(*args, &block)
|
75
|
+
else
|
76
|
+
!super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def does_not_match?(*args, &block)
|
81
|
+
@base_matcher.matches?(*args, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def failure_message
|
85
|
+
optimal_failure_message(__method__, :failure_message_when_negated)
|
86
|
+
end
|
87
|
+
|
88
|
+
def failure_message_when_negated
|
89
|
+
optimal_failure_message(__method__, :failure_message)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
DefaultFailureMessages = BuiltIn::BaseMatcher::DefaultFailureMessages
|
95
|
+
|
96
|
+
# For a matcher that uses the default failure messages, we prefer to
|
97
|
+
# use the override provided by the `description_block`, because it
|
98
|
+
# includes the phrasing that the user has expressed a preference for
|
99
|
+
# by going through the effort of defining a negated matcher.
|
100
|
+
#
|
101
|
+
# However, if the override didn't actually change anything, then we
|
102
|
+
# should return the opposite failure message instead -- the overriden
|
103
|
+
# message is going to be confusing if we return it as-is, as it represents
|
104
|
+
# the non-negated failure message for a negated match (or vice versa).
|
105
|
+
def optimal_failure_message(same, inverted)
|
106
|
+
if DefaultFailureMessages.has_default_failure_messages?(@base_matcher)
|
107
|
+
base_message = @base_matcher.__send__(same)
|
108
|
+
overriden = @description_block.call(base_message)
|
109
|
+
return overriden if overriden != base_message
|
110
|
+
end
|
111
|
+
|
112
|
+
@base_matcher.__send__(inverted)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Matchers
|
3
|
+
module BuiltIn
|
4
|
+
# @api private
|
5
|
+
# Provides the implementation for `all`.
|
6
|
+
# Not intended to be instantiated directly.
|
7
|
+
class All < BaseMatcher
|
8
|
+
# @private
|
9
|
+
attr_reader :matcher, :failed_objects
|
10
|
+
|
11
|
+
def initialize(matcher)
|
12
|
+
@matcher = matcher
|
13
|
+
@failed_objects = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# @private
|
17
|
+
def does_not_match?(_actual)
|
18
|
+
raise NotImplementedError, '`expect().not_to all( matcher )` is not supported.'
|
19
|
+
end
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
# @return [String]
|
23
|
+
def failure_message
|
24
|
+
unless iterable?
|
25
|
+
return "#{improve_hash_formatting(super)}, but was not iterable"
|
26
|
+
end
|
27
|
+
|
28
|
+
all_messages = [improve_hash_formatting(super)]
|
29
|
+
failed_objects.each do |index, matcher_failure_message|
|
30
|
+
all_messages << failure_message_for_item(index, matcher_failure_message)
|
31
|
+
end
|
32
|
+
all_messages.join("\n\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
# @return [String]
|
37
|
+
def description
|
38
|
+
improve_hash_formatting "all #{description_of matcher}"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def match(_expected, _actual)
|
44
|
+
return false unless iterable?
|
45
|
+
|
46
|
+
index_failed_objects
|
47
|
+
failed_objects.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
def index_failed_objects
|
51
|
+
actual.each_with_index do |actual_item, index|
|
52
|
+
cloned_matcher = matcher.clone
|
53
|
+
matches = cloned_matcher.matches?(actual_item)
|
54
|
+
failed_objects[index] = cloned_matcher.failure_message unless matches
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def failure_message_for_item(index, failure_message)
|
59
|
+
failure_message = indent_multiline_message(add_new_line_if_needed(failure_message))
|
60
|
+
indent_multiline_message("object at index #{index} failed to match:#{failure_message}")
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_new_line_if_needed(message)
|
64
|
+
message.start_with?("\n") ? message : "\n#{message}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def indent_multiline_message(message)
|
68
|
+
message = message.sub(/\n+\z/, '')
|
69
|
+
message.lines.map do |line|
|
70
|
+
line =~ /\S/ ? ' ' + line : line
|
71
|
+
end.join
|
72
|
+
end
|
73
|
+
|
74
|
+
def initialize_copy(other)
|
75
|
+
@matcher = @matcher.clone
|
76
|
+
@failed_objects = @failed_objects.clone
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
def iterable?
|
81
|
+
@actual.respond_to?(:each_with_index)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -4,27 +4,46 @@ module RSpec
|
|
4
4
|
# @api private
|
5
5
|
#
|
6
6
|
# Used _internally_ as a base class for matchers that ship with
|
7
|
-
# rspec-expectations.
|
7
|
+
# rspec-expectations and rspec-rails.
|
8
8
|
#
|
9
9
|
# ### Warning:
|
10
10
|
#
|
11
|
-
# This class is for internal use, and subject to change without notice.
|
12
|
-
# strongly recommend that you do not base your custom matchers on this
|
11
|
+
# This class is for internal use, and subject to change without notice.
|
12
|
+
# We strongly recommend that you do not base your custom matchers on this
|
13
13
|
# class. If/when this changes, we will announce it and remove this warning.
|
14
14
|
class BaseMatcher
|
15
|
-
include RSpec::Matchers::
|
15
|
+
include RSpec::Matchers::Composable
|
16
16
|
|
17
|
+
# @api private
|
18
|
+
# Used to detect when no arg is passed to `initialize`.
|
19
|
+
# `nil` cannot be used because it's a valid value to pass.
|
20
|
+
UNDEFINED = Object.new.freeze
|
21
|
+
|
22
|
+
# @private
|
17
23
|
attr_reader :actual, :expected, :rescued_exception
|
18
24
|
|
19
|
-
|
20
|
-
|
25
|
+
# @private
|
26
|
+
attr_writer :matcher_name
|
27
|
+
|
28
|
+
def initialize(expected=UNDEFINED)
|
29
|
+
@expected = expected unless UNDEFINED.equal?(expected)
|
21
30
|
end
|
22
31
|
|
32
|
+
# @api private
|
33
|
+
# Indicates if the match is successful. Delegates to `match`, which
|
34
|
+
# should be defined on a subclass. Takes care of consistently
|
35
|
+
# initializing the `actual` attribute.
|
23
36
|
def matches?(actual)
|
24
37
|
@actual = actual
|
25
38
|
match(expected, actual)
|
26
39
|
end
|
27
40
|
|
41
|
+
# @api private
|
42
|
+
# Used to wrap a block of code that will indicate failure by
|
43
|
+
# raising one of the named exceptions.
|
44
|
+
#
|
45
|
+
# This is used by rspec-rails for some of its matchers that
|
46
|
+
# wrap rails' assertions.
|
28
47
|
def match_unless_raises(*exceptions)
|
29
48
|
exceptions.unshift Exception if exceptions.empty?
|
30
49
|
begin
|
@@ -35,33 +54,144 @@ module RSpec
|
|
35
54
|
end
|
36
55
|
end
|
37
56
|
|
38
|
-
|
39
|
-
|
40
|
-
|
57
|
+
# @api private
|
58
|
+
# Generates a description using {EnglishPhrasing}.
|
59
|
+
# @return [String]
|
60
|
+
def description
|
61
|
+
desc = EnglishPhrasing.split_words(self.class.matcher_name)
|
62
|
+
desc << EnglishPhrasing.list(@expected) if defined?(@expected)
|
63
|
+
desc
|
41
64
|
end
|
42
65
|
|
43
|
-
|
44
|
-
|
45
|
-
|
66
|
+
# @api private
|
67
|
+
# Matchers are not diffable by default. Override this to make your
|
68
|
+
# subclass diffable.
|
69
|
+
def diffable?
|
70
|
+
false
|
46
71
|
end
|
47
72
|
|
48
|
-
|
49
|
-
|
73
|
+
# @api private
|
74
|
+
# Most matchers are value matchers (i.e. meant to work with `expect(value)`)
|
75
|
+
# rather than block matchers (i.e. meant to work with `expect { }`), so
|
76
|
+
# this defaults to false. Block matchers must override this to return true.
|
77
|
+
def supports_block_expectations?
|
78
|
+
false
|
50
79
|
end
|
51
80
|
|
52
|
-
|
81
|
+
# @private
|
82
|
+
def supports_value_expectations?
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
# @api private
|
87
|
+
def expects_call_stack_jump?
|
53
88
|
false
|
54
89
|
end
|
55
90
|
|
56
|
-
|
57
|
-
|
91
|
+
# @private
|
92
|
+
def expected_formatted
|
93
|
+
RSpec::Support::ObjectFormatter.format(@expected)
|
58
94
|
end
|
59
95
|
|
60
|
-
private
|
96
|
+
# @private
|
97
|
+
def actual_formatted
|
98
|
+
RSpec::Support::ObjectFormatter.format(@actual)
|
99
|
+
end
|
61
100
|
|
62
|
-
|
63
|
-
|
101
|
+
# @private
|
102
|
+
def self.matcher_name
|
103
|
+
@matcher_name ||= underscore(name.split('::').last)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @private
|
107
|
+
def matcher_name
|
108
|
+
if defined?(@matcher_name)
|
109
|
+
@matcher_name
|
110
|
+
else
|
111
|
+
self.class.matcher_name
|
112
|
+
end
|
64
113
|
end
|
114
|
+
|
115
|
+
# @private
|
116
|
+
# Borrowed from ActiveSupport.
|
117
|
+
def self.underscore(camel_cased_word)
|
118
|
+
word = camel_cased_word.to_s.dup
|
119
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
120
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
121
|
+
word.tr!('-', '_')
|
122
|
+
word.downcase!
|
123
|
+
word
|
124
|
+
end
|
125
|
+
private_class_method :underscore
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def assert_ivars(*expected_ivars)
|
130
|
+
return unless (expected_ivars - present_ivars).any?
|
131
|
+
ivar_list = EnglishPhrasing.list(expected_ivars)
|
132
|
+
raise "#{self.class.name} needs to supply#{ivar_list}"
|
133
|
+
end
|
134
|
+
|
135
|
+
if RUBY_VERSION.to_f < 1.9
|
136
|
+
# :nocov:
|
137
|
+
def present_ivars
|
138
|
+
instance_variables.map(&:to_sym)
|
139
|
+
end
|
140
|
+
# :nocov:
|
141
|
+
else
|
142
|
+
alias present_ivars instance_variables
|
143
|
+
end
|
144
|
+
|
145
|
+
# @private
|
146
|
+
module HashFormatting
|
147
|
+
# `{ :a => 5, :b => 2 }.inspect` produces:
|
148
|
+
#
|
149
|
+
# {:a=>5, :b=>2}
|
150
|
+
#
|
151
|
+
# ...but it looks much better as:
|
152
|
+
#
|
153
|
+
# {:a => 5, :b => 2}
|
154
|
+
#
|
155
|
+
# This is idempotent and safe to run on a string multiple times.
|
156
|
+
def improve_hash_formatting(inspect_string)
|
157
|
+
inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
|
158
|
+
end
|
159
|
+
module_function :improve_hash_formatting
|
160
|
+
end
|
161
|
+
|
162
|
+
include HashFormatting
|
163
|
+
|
164
|
+
# @api private
|
165
|
+
# Provides default implementations of failure messages, based on the `description`.
|
166
|
+
module DefaultFailureMessages
|
167
|
+
# @api private
|
168
|
+
# Provides a good generic failure message. Based on `description`.
|
169
|
+
# When subclassing, if you are not satisfied with this failure message
|
170
|
+
# you often only need to override `description`.
|
171
|
+
# @return [String]
|
172
|
+
def failure_message
|
173
|
+
"expected #{description_of @actual} to #{description}".dup
|
174
|
+
end
|
175
|
+
|
176
|
+
# @api private
|
177
|
+
# Provides a good generic negative failure message. Based on `description`.
|
178
|
+
# When subclassing, if you are not satisfied with this failure message
|
179
|
+
# you often only need to override `description`.
|
180
|
+
# @return [String]
|
181
|
+
def failure_message_when_negated
|
182
|
+
"expected #{description_of @actual} not to #{description}".dup
|
183
|
+
end
|
184
|
+
|
185
|
+
# @private
|
186
|
+
def self.has_default_failure_messages?(matcher)
|
187
|
+
matcher.method(:failure_message).owner == self &&
|
188
|
+
matcher.method(:failure_message_when_negated).owner == self
|
189
|
+
rescue NameError
|
190
|
+
false
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
include DefaultFailureMessages
|
65
195
|
end
|
66
196
|
end
|
67
197
|
end
|