rspec-expectations 3.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +5 -0
  4. data/.document +5 -0
  5. data/.yardopts +6 -0
  6. data/Changelog.md +1156 -0
  7. data/LICENSE.md +25 -0
  8. data/README.md +305 -0
  9. data/lib/rspec/expectations.rb +82 -0
  10. data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
  11. data/lib/rspec/expectations/configuration.rb +215 -0
  12. data/lib/rspec/expectations/expectation_target.rb +127 -0
  13. data/lib/rspec/expectations/fail_with.rb +39 -0
  14. data/lib/rspec/expectations/failure_aggregator.rb +194 -0
  15. data/lib/rspec/expectations/handler.rb +170 -0
  16. data/lib/rspec/expectations/minitest_integration.rb +58 -0
  17. data/lib/rspec/expectations/syntax.rb +132 -0
  18. data/lib/rspec/expectations/version.rb +8 -0
  19. data/lib/rspec/matchers.rb +1034 -0
  20. data/lib/rspec/matchers/aliased_matcher.rb +116 -0
  21. data/lib/rspec/matchers/built_in.rb +52 -0
  22. data/lib/rspec/matchers/built_in/all.rb +86 -0
  23. data/lib/rspec/matchers/built_in/base_matcher.rb +193 -0
  24. data/lib/rspec/matchers/built_in/be.rb +288 -0
  25. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  26. data/lib/rspec/matchers/built_in/be_instance_of.rb +26 -0
  27. data/lib/rspec/matchers/built_in/be_kind_of.rb +20 -0
  28. data/lib/rspec/matchers/built_in/be_within.rb +72 -0
  29. data/lib/rspec/matchers/built_in/change.rb +428 -0
  30. data/lib/rspec/matchers/built_in/compound.rb +271 -0
  31. data/lib/rspec/matchers/built_in/contain_exactly.rb +302 -0
  32. data/lib/rspec/matchers/built_in/cover.rb +24 -0
  33. data/lib/rspec/matchers/built_in/eq.rb +40 -0
  34. data/lib/rspec/matchers/built_in/eql.rb +34 -0
  35. data/lib/rspec/matchers/built_in/equal.rb +81 -0
  36. data/lib/rspec/matchers/built_in/exist.rb +90 -0
  37. data/lib/rspec/matchers/built_in/has.rb +103 -0
  38. data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
  39. data/lib/rspec/matchers/built_in/include.rb +149 -0
  40. data/lib/rspec/matchers/built_in/match.rb +106 -0
  41. data/lib/rspec/matchers/built_in/operators.rb +128 -0
  42. data/lib/rspec/matchers/built_in/output.rb +200 -0
  43. data/lib/rspec/matchers/built_in/raise_error.rb +230 -0
  44. data/lib/rspec/matchers/built_in/respond_to.rb +165 -0
  45. data/lib/rspec/matchers/built_in/satisfy.rb +60 -0
  46. data/lib/rspec/matchers/built_in/start_or_end_with.rb +94 -0
  47. data/lib/rspec/matchers/built_in/throw_symbol.rb +132 -0
  48. data/lib/rspec/matchers/built_in/yield.rb +432 -0
  49. data/lib/rspec/matchers/composable.rb +171 -0
  50. data/lib/rspec/matchers/dsl.rb +527 -0
  51. data/lib/rspec/matchers/english_phrasing.rb +58 -0
  52. data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +73 -0
  53. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  54. data/lib/rspec/matchers/generated_descriptions.rb +41 -0
  55. data/lib/rspec/matchers/matcher_delegator.rb +35 -0
  56. data/lib/rspec/matchers/matcher_protocol.rb +99 -0
  57. metadata +215 -0
  58. metadata.gz.sig +0 -0
@@ -0,0 +1,165 @@
1
+ RSpec::Support.require_rspec_support "method_signature_verifier"
2
+
3
+ module RSpec
4
+ module Matchers
5
+ module BuiltIn
6
+ # @api private
7
+ # Provides the implementation for `respond_to`.
8
+ # Not intended to be instantiated directly.
9
+ class RespondTo < BaseMatcher
10
+ def initialize(*names)
11
+ @names = names
12
+ @expected_arity = nil
13
+ @expected_keywords = []
14
+ @unlimited_arguments = nil
15
+ @arbitrary_keywords = nil
16
+ end
17
+
18
+ # @api public
19
+ # Specifies the number of expected arguments.
20
+ #
21
+ # @example
22
+ # expect(obj).to respond_to(:message).with(3).arguments
23
+ def with(n)
24
+ @expected_arity = n
25
+ self
26
+ end
27
+
28
+ # @api public
29
+ # Specifies keyword arguments, if any.
30
+ #
31
+ # @example
32
+ # expect(obj).to respond_to(:message).with_keywords(:color, :shape)
33
+ # @example with an expected number of arguments
34
+ # expect(obj).to respond_to(:message).with(3).arguments.and_keywords(:color, :shape)
35
+ def with_keywords(*keywords)
36
+ @expected_keywords = keywords
37
+ self
38
+ end
39
+ alias :and_keywords :with_keywords
40
+
41
+ # @api public
42
+ # Specifies that the method accepts any keyword, i.e. the method has
43
+ # a splatted keyword parameter of the form **kw_args.
44
+ #
45
+ # @example
46
+ # expect(obj).to respond_to(:message).with_any_keywords
47
+ def with_any_keywords
48
+ @arbitrary_keywords = true
49
+ self
50
+ end
51
+ alias :and_any_keywords :with_any_keywords
52
+
53
+ # @api public
54
+ # Specifies that the number of arguments has no upper limit, i.e. the
55
+ # method has a splatted parameter of the form *args.
56
+ #
57
+ # @example
58
+ # expect(obj).to respond_to(:message).with_unlimited_arguments
59
+ def with_unlimited_arguments
60
+ @unlimited_arguments = true
61
+ self
62
+ end
63
+ alias :and_unlimited_arguments :with_unlimited_arguments
64
+
65
+ # @api public
66
+ # No-op. Intended to be used as syntactic sugar when using `with`.
67
+ #
68
+ # @example
69
+ # expect(obj).to respond_to(:message).with(3).arguments
70
+ def argument
71
+ self
72
+ end
73
+ alias :arguments :argument
74
+
75
+ # @private
76
+ def matches?(actual)
77
+ find_failing_method_names(actual, :reject).empty?
78
+ end
79
+
80
+ # @private
81
+ def does_not_match?(actual)
82
+ find_failing_method_names(actual, :select).empty?
83
+ end
84
+
85
+ # @api private
86
+ # @return [String]
87
+ def failure_message
88
+ "expected #{actual_formatted} to respond to #{@failing_method_names.map { |name| description_of(name) }.join(', ')}#{with_arity}"
89
+ end
90
+
91
+ # @api private
92
+ # @return [String]
93
+ def failure_message_when_negated
94
+ failure_message.sub(/to respond to/, 'not to respond to')
95
+ end
96
+
97
+ # @api private
98
+ # @return [String]
99
+ def description
100
+ "respond to #{pp_names}#{with_arity}"
101
+ end
102
+
103
+ private
104
+
105
+ def find_failing_method_names(actual, filter_method)
106
+ @actual = actual
107
+ @failing_method_names = @names.__send__(filter_method) do |name|
108
+ @actual.respond_to?(name) && matches_arity?(actual, name)
109
+ end
110
+ end
111
+
112
+ def matches_arity?(actual, name)
113
+ expectation = Support::MethodSignatureExpectation.new
114
+
115
+ if @expected_arity.is_a?(Range)
116
+ expectation.min_count = @expected_arity.min
117
+ expectation.max_count = @expected_arity.max
118
+ else
119
+ expectation.min_count = @expected_arity
120
+ end
121
+
122
+ expectation.keywords = @expected_keywords
123
+ expectation.expect_unlimited_arguments = @unlimited_arguments
124
+ expectation.expect_arbitrary_keywords = @arbitrary_keywords
125
+
126
+ return true if expectation.empty?
127
+
128
+ signature = Support::MethodSignature.new(Support.method_handle_for(actual, name))
129
+
130
+ Support::StrictSignatureVerifier.new(signature).with_expectation(expectation).valid?
131
+ end
132
+
133
+ def with_arity
134
+ str = ''.dup
135
+ str << " with #{with_arity_string}" if @expected_arity
136
+ str << " #{str.length == 0 ? 'with' : 'and'} #{with_keywords_string}" if @expected_keywords && @expected_keywords.count > 0
137
+ str << " #{str.length == 0 ? 'with' : 'and'} unlimited arguments" if @unlimited_arguments
138
+ str << " #{str.length == 0 ? 'with' : 'and'} any keywords" if @arbitrary_keywords
139
+ str
140
+ end
141
+
142
+ def with_arity_string
143
+ "#{@expected_arity} argument#{@expected_arity == 1 ? '' : 's'}"
144
+ end
145
+
146
+ def with_keywords_string
147
+ kw_str = case @expected_keywords.count
148
+ when 1
149
+ @expected_keywords.first.inspect
150
+ when 2
151
+ @expected_keywords.map(&:inspect).join(' and ')
152
+ else
153
+ "#{@expected_keywords[0...-1].map(&:inspect).join(', ')}, and #{@expected_keywords.last.inspect}"
154
+ end
155
+
156
+ "keyword#{@expected_keywords.count == 1 ? '' : 's'} #{kw_str}"
157
+ end
158
+
159
+ def pp_names
160
+ @names.length == 1 ? "##{@names.first}" : description_of(@names)
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,60 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `satisfy`.
6
+ # Not intended to be instantiated directly.
7
+ class Satisfy < BaseMatcher
8
+ def initialize(description=nil, &block)
9
+ @description = description
10
+ @block = block
11
+ end
12
+
13
+ # @private
14
+ def matches?(actual, &block)
15
+ @block = block if block
16
+ @actual = actual
17
+ @block.call(actual)
18
+ end
19
+
20
+ # @private
21
+ def description
22
+ @description ||= "satisfy #{block_representation}"
23
+ end
24
+
25
+ # @api private
26
+ # @return [String]
27
+ def failure_message
28
+ "expected #{actual_formatted} to #{description}"
29
+ end
30
+
31
+ # @api private
32
+ # @return [String]
33
+ def failure_message_when_negated
34
+ "expected #{actual_formatted} not to #{description}"
35
+ end
36
+
37
+ private
38
+
39
+ if RSpec::Support::RubyFeatures.ripper_supported?
40
+ def block_representation
41
+ if (block_snippet = extract_block_snippet)
42
+ "expression `#{block_snippet}`"
43
+ else
44
+ 'block'
45
+ end
46
+ end
47
+
48
+ def extract_block_snippet
49
+ return nil unless @block
50
+ Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@block, matcher_name)
51
+ end
52
+ else
53
+ def block_representation
54
+ 'block'
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,94 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ # @api private
5
+ # Base class for the `end_with` and `start_with` matchers.
6
+ # Not intended to be instantiated directly.
7
+ class StartOrEndWith < BaseMatcher
8
+ def initialize(*expected)
9
+ @actual_does_not_have_ordered_elements = false
10
+ @expected = expected.length == 1 ? expected.first : expected
11
+ end
12
+
13
+ # @api private
14
+ # @return [String]
15
+ def failure_message
16
+ super.tap do |msg|
17
+ if @actual_does_not_have_ordered_elements
18
+ msg << ", but it does not have ordered elements"
19
+ elsif !actual.respond_to?(:[])
20
+ msg << ", but it cannot be indexed using #[]"
21
+ end
22
+ end
23
+ end
24
+
25
+ # @api private
26
+ # @return [String]
27
+ def description
28
+ return super unless Hash === expected
29
+ english_name = EnglishPhrasing.split_words(self.class.matcher_name)
30
+ description_of_expected = surface_descriptions_in(expected).inspect
31
+ "#{english_name} #{description_of_expected}"
32
+ end
33
+
34
+ private
35
+
36
+ def match(_expected, actual)
37
+ return false unless actual.respond_to?(:[])
38
+
39
+ begin
40
+ return true if subsets_comparable? && subset_matches?
41
+ element_matches?
42
+ rescue ArgumentError
43
+ @actual_does_not_have_ordered_elements = true
44
+ return false
45
+ end
46
+ end
47
+
48
+ def subsets_comparable?
49
+ # Structs support the Enumerable interface but don't really have
50
+ # the semantics of a subset of a larger set...
51
+ return false if Struct === expected
52
+
53
+ expected.respond_to?(:length)
54
+ end
55
+ end
56
+
57
+ # For RSpec 3.1, the base class was named `StartAndEndWith`. For SemVer reasons,
58
+ # we still provide this constant until 4.0.
59
+ # @deprecated Use StartOrEndWith instead.
60
+ # @private
61
+ StartAndEndWith = StartOrEndWith
62
+
63
+ # @api private
64
+ # Provides the implementation for `start_with`.
65
+ # Not intended to be instantiated directly.
66
+ class StartWith < StartOrEndWith
67
+ private
68
+
69
+ def subset_matches?
70
+ values_match?(expected, actual[0, expected.length])
71
+ end
72
+
73
+ def element_matches?
74
+ values_match?(expected, actual[0])
75
+ end
76
+ end
77
+
78
+ # @api private
79
+ # Provides the implementation for `end_with`.
80
+ # Not intended to be instantiated directly.
81
+ class EndWith < StartOrEndWith
82
+ private
83
+
84
+ def subset_matches?
85
+ values_match?(expected, actual[-expected.length, expected.length])
86
+ end
87
+
88
+ def element_matches?
89
+ values_match?(expected, actual[-1])
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,132 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `throw_symbol`.
6
+ # Not intended to be instantiated directly.
7
+ class ThrowSymbol
8
+ include Composable
9
+
10
+ def initialize(expected_symbol=nil, expected_arg=nil)
11
+ @expected_symbol = expected_symbol
12
+ @expected_arg = expected_arg
13
+ @caught_symbol = @caught_arg = nil
14
+ end
15
+
16
+ # rubocop:disable MethodLength
17
+ # @private
18
+ def matches?(given_proc)
19
+ @block = given_proc
20
+ return false unless Proc === given_proc
21
+
22
+ begin
23
+ if @expected_symbol.nil?
24
+ given_proc.call
25
+ else
26
+ @caught_arg = catch :proc_did_not_throw_anything do
27
+ catch @expected_symbol do
28
+ given_proc.call
29
+ throw :proc_did_not_throw_anything, :nothing_thrown
30
+ end
31
+ end
32
+
33
+ if @caught_arg == :nothing_thrown
34
+ @caught_arg = nil
35
+ else
36
+ @caught_symbol = @expected_symbol
37
+ end
38
+ end
39
+
40
+ # Ruby 1.8 uses NameError with `symbol'
41
+ # Ruby 1.9 uses ArgumentError with :symbol
42
+ rescue NameError, ArgumentError => e
43
+ unless (match_data = e.message.match(/uncaught throw (`|\:)([a-zA-Z0-9_]*)(')?/))
44
+ other_exception = e
45
+ raise
46
+ end
47
+ @caught_symbol = match_data.captures[1].to_sym
48
+ rescue => other_exception
49
+ raise
50
+ ensure
51
+ # rubocop:disable EnsureReturn
52
+ unless other_exception
53
+ if @expected_symbol.nil?
54
+ return !!@caught_symbol
55
+ else
56
+ if @expected_arg.nil?
57
+ return @caught_symbol == @expected_symbol
58
+ else
59
+ return (@caught_symbol == @expected_symbol) && values_match?(@expected_arg, @caught_arg)
60
+ end
61
+ end
62
+ end
63
+ # rubocop:enable EnsureReturn
64
+ end
65
+ end
66
+ # rubocop:enable MethodLength
67
+
68
+ def does_not_match?(given_proc)
69
+ !matches?(given_proc) && Proc === given_proc
70
+ end
71
+
72
+ # @api private
73
+ # @return [String]
74
+ def failure_message
75
+ "expected #{expected} to be thrown, #{actual_result}"
76
+ end
77
+
78
+ # @api private
79
+ # @return [String]
80
+ def failure_message_when_negated
81
+ "expected #{expected('no Symbol')}#{' not' if @expected_symbol} to be thrown, #{actual_result}"
82
+ end
83
+
84
+ # @api private
85
+ # @return [String]
86
+ def description
87
+ "throw #{expected}"
88
+ end
89
+
90
+ # @api private
91
+ # Indicates this matcher matches against a block.
92
+ # @return [True]
93
+ def supports_block_expectations?
94
+ true
95
+ end
96
+
97
+ def expects_call_stack_jump?
98
+ true
99
+ end
100
+
101
+ private
102
+
103
+ def actual_result
104
+ return "but was not a block" unless Proc === @block
105
+ "got #{caught}"
106
+ end
107
+
108
+ def expected(symbol_desc='a Symbol')
109
+ throw_description(@expected_symbol || symbol_desc, @expected_arg)
110
+ end
111
+
112
+ def caught
113
+ throw_description(@caught_symbol || 'nothing', @caught_arg)
114
+ end
115
+
116
+ def throw_description(symbol, arg)
117
+ symbol_description = symbol.is_a?(String) ? symbol : description_of(symbol)
118
+
119
+ arg_description = if arg
120
+ " with #{description_of arg}"
121
+ elsif @expected_arg && @caught_symbol == @expected_symbol
122
+ " with no argument"
123
+ else
124
+ ""
125
+ end
126
+
127
+ symbol_description + arg_description
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end