expected 1.0.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
- data/MIT-LICENSE +22 -0
- data/README.md +94 -0
- data/lib/expected.rb +13 -0
- data/lib/expected/configuration.rb +25 -0
- data/lib/expected/matchers.rb +13 -0
- data/lib/expected/matchers/have_attr_accessor.rb +88 -0
- data/lib/expected/matchers/have_attr_reader.rb +103 -0
- data/lib/expected/matchers/have_attr_writer.rb +108 -0
- data/lib/expected/matchers/have_constant.rb +143 -0
- data/lib/expected/matchers/inherit_from.rb +67 -0
- data/lib/expected/version.rb +19 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '0954ddfccf5072455413c702650c98e39ccdbd72a1db7806463598e5c6c0e52b'
|
4
|
+
data.tar.gz: c41841fcb0cfcfde9dfff9c1c27445f88c10a168254c965ea681a0e62676ee0c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a6cf5251016d8d3f9b36af9ac429a0e9c8f433ee4c03b687a438e339d84361c913df7b63f6779597b49f0fb675ca4998c9da8a066e1371458ad04c1dff3675b5
|
7
|
+
data.tar.gz: bb7461e7f4e02295962c7b52288e86a710bf72f93f7a6ab4129eb29a8f9b7d27a66c50c95eea4b0d8681d0ed620b95f0237cf02818b5c5fda5de1e30f81f82c7
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright 2019 Taylor Yelverton
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# Expected: RSpec Matchers - Extended [![Gem Version][version-badge]][rubygems] [![Total Downloads][downloads-total]][rubygems] [![Downloads][downloads-badge]][rubygems]
|
2
|
+
|
3
|
+
[version-badge]: https://img.shields.io/gem/v/expected.svg
|
4
|
+
[rubygems]: https://rubygems.org/gems/expected
|
5
|
+
[downloads-total]: https://img.shields.io/gem/dt/expected.svg
|
6
|
+
[downloads-badge]: https://img.shields.io/gem/dtv/expected.svg
|
7
|
+
|
8
|
+
Adds several simple matchers to RSpec
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
### Add to Gemfile
|
15
|
+
```ruby
|
16
|
+
group :test do
|
17
|
+
gem 'expected'
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
### Add to RSpec
|
22
|
+
Add the following to your projects `spec/rails_helper.rb` for Rails apps, or `spec/spec_helper.rb for non-Rails apps.
|
23
|
+
```ruby
|
24
|
+
Expected.configure do |config|
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
### `inherit_from`
|
37
|
+
Used to test inheritance
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# Test if the subject inherits from the supplied Class
|
41
|
+
it { is_expected.to inherit_from(SomeClass) }
|
42
|
+
```
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
### `have_constant`
|
47
|
+
Used to test constants
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# Test if a constant exists
|
51
|
+
it { is_expected.to have_constant(:FOO) }
|
52
|
+
|
53
|
+
# Test if a constant has a specific value
|
54
|
+
it { is_expected.to have_constant(:FOO).with_value("bar") }
|
55
|
+
|
56
|
+
# Test if a constant's value is a specific type
|
57
|
+
it { is_expected.to have_constant(:FOO).of_type(String) }
|
58
|
+
```
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
### `have_attr_reader`
|
63
|
+
Used to test inclusion of `attr_reader :attribute`
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
# Test if the subject has `attr_reader :attribute`
|
67
|
+
it { is_expected.to have_attr_reader(:attribute) }
|
68
|
+
```
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
### `have_attr_writer`
|
73
|
+
Used to test inclusion of `attr_writer :attribute`
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# Test if the subject has `attr_writer :attribute`
|
77
|
+
it { is_expected.to have_attr_writer(:attribute) }
|
78
|
+
```
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
### `have_attr_accessor`
|
83
|
+
Used to test inclusion of `attr_accessor :attribute`
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
# Test if the subject has `attr_accessor :attribute`
|
87
|
+
it { is_expected.to have_attr_accessor(:attribute) }
|
88
|
+
```
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
## License
|
93
|
+
Expected is copyright © 2019-2020 Taylor Yelverton.
|
94
|
+
It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
|
data/lib/expected.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Container Module
|
4
|
+
module Expected
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'active_support/hash_with_indifferent_access'
|
9
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
10
|
+
|
11
|
+
require 'expected/version'
|
12
|
+
require 'expected/configuration'
|
13
|
+
require 'expected/matchers'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
module Expected
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Configure the library
|
8
|
+
# @yield [Configuration]
|
9
|
+
def configure
|
10
|
+
yield configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
# Memoized {Configuration}
|
14
|
+
# @return [Configuration]
|
15
|
+
def configuration
|
16
|
+
@configuration ||= Configuration.new
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
# Configuration class
|
22
|
+
class Configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expected
|
4
|
+
# Matchers container
|
5
|
+
module Matchers
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'expected/matchers/have_constant'
|
10
|
+
require 'expected/matchers/inherit_from'
|
11
|
+
require 'expected/matchers/have_attr_reader'
|
12
|
+
require 'expected/matchers/have_attr_writer'
|
13
|
+
require 'expected/matchers/have_attr_accessor'
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Expected
|
6
|
+
# :nodoc:
|
7
|
+
module Matchers
|
8
|
+
|
9
|
+
# Used to test inclusion of `attr_accessor :attribute` on the subject
|
10
|
+
# @param attribute [String, Symbol]
|
11
|
+
# @return [HaveAttrAccessorMatcher]
|
12
|
+
#
|
13
|
+
# @example Test if the subject has `attr_accessor :attribute`
|
14
|
+
# it { is_expected.to have_attr_accessor(:some_attribute) }
|
15
|
+
#
|
16
|
+
def have_attr_accessor(attribute) # rubocop:disable Naming/PredicateName
|
17
|
+
HaveAttrAccessorMatcher.new(attribute)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Class used by {#have_constant}
|
21
|
+
class HaveAttrAccessorMatcher
|
22
|
+
attr_reader :attribute, :subject, :has_attr_reader, :has_attr_writer
|
23
|
+
|
24
|
+
# @param attribute [String, Symbol] The attribute the {#subject} is expected to have an attr_accessor for
|
25
|
+
def initialize(attribute)
|
26
|
+
unless attribute.is_a?(String) || attribute.is_a?(Symbol)
|
27
|
+
raise 'HaveAttrAccessorMatcher attribute must be a String or Symbol'
|
28
|
+
end
|
29
|
+
@attribute = attribute.to_sym
|
30
|
+
@has_attr_reader = HaveAttrReaderMatcher.new(attribute)
|
31
|
+
@has_attr_writer = HaveAttrWriterMatcher.new(attribute)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Run the test
|
35
|
+
# @param subject The thing to test against
|
36
|
+
# @return [True] If the test passes
|
37
|
+
# @return [False] if the test fails
|
38
|
+
def matches?(subject)
|
39
|
+
self.subject = subject
|
40
|
+
matches_attr_reader? && matches_attr_writer?
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [String]
|
44
|
+
def failure_message
|
45
|
+
"Expected #{expectation} (#{@failure})"
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [String]
|
49
|
+
def failure_message_when_negated
|
50
|
+
"Did not expect #{expectation}"
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [String]
|
54
|
+
def description
|
55
|
+
"have_attr_accessor: `#{attribute}`"
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# The thing to test against
|
61
|
+
# @return [Class, Module]
|
62
|
+
def subject=(subject) # rubocop:disable Style/TrivialAccessors
|
63
|
+
@subject = subject
|
64
|
+
end
|
65
|
+
|
66
|
+
def matches_attr_reader?
|
67
|
+
ret = has_attr_reader.matches?(subject)
|
68
|
+
msg = has_attr_reader.instance_variable_get(:@failure)
|
69
|
+
@failure = msg if msg
|
70
|
+
ret
|
71
|
+
end
|
72
|
+
|
73
|
+
def matches_attr_writer?
|
74
|
+
ret = has_attr_writer.matches?(subject)
|
75
|
+
msg = has_attr_writer.instance_variable_get(:@failure)
|
76
|
+
@failure = msg if msg
|
77
|
+
ret
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return String
|
81
|
+
def expectation
|
82
|
+
"<#{subject}> to have attr_accessor `#{attribute}`"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Expected
|
6
|
+
# :nodoc:
|
7
|
+
module Matchers
|
8
|
+
|
9
|
+
# Used to test inclusion of `attr_reader :attribute` on the subject
|
10
|
+
# @param attribute [String, Symbol]
|
11
|
+
# @return [HaveAttrReaderMatcher]
|
12
|
+
#
|
13
|
+
# @example Test if the subject has `attr_reader :attribute`
|
14
|
+
# it { is_expected.to have_attr_reader(:some_attribute) }
|
15
|
+
#
|
16
|
+
def have_attr_reader(attribute) # rubocop:disable Naming/PredicateName
|
17
|
+
HaveAttrReaderMatcher.new(attribute)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Class used by {#have_constant}
|
21
|
+
class HaveAttrReaderMatcher
|
22
|
+
attr_reader :attribute, :subject
|
23
|
+
|
24
|
+
# @param attribute [String, Symbol] The attribute the {#subject} is expected to have an attr_reader for
|
25
|
+
def initialize(attribute)
|
26
|
+
unless attribute.is_a?(String) || attribute.is_a?(Symbol)
|
27
|
+
raise 'HaveAttrReaderMatcher attribute must be a String or Symbol'
|
28
|
+
end
|
29
|
+
@attribute = attribute.to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
# Run the test
|
33
|
+
# @param subject The thing to test against
|
34
|
+
# @return [True] If the test passes
|
35
|
+
# @return [False] if the test fails
|
36
|
+
def matches?(subject)
|
37
|
+
self.subject = subject
|
38
|
+
method? &&
|
39
|
+
returns_correct_value?
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [String]
|
43
|
+
def failure_message
|
44
|
+
"Expected #{expectation} (#{@failure})"
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [String]
|
48
|
+
def failure_message_when_negated
|
49
|
+
"Did not expect #{expectation}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String]
|
53
|
+
def description
|
54
|
+
"have_attr_reader: `#{attribute}`"
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# The thing to test against
|
60
|
+
# @return [Class, Module]
|
61
|
+
def subject=(subject)
|
62
|
+
@original_subject = subject
|
63
|
+
@subject = subject.instance_of?(Class) ? subject.allocate : subject
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [Symbol]
|
67
|
+
def attribute_ivar
|
68
|
+
@attribute_ivar ||= :"@#{attribute}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def method?
|
72
|
+
if subject.respond_to? attribute
|
73
|
+
true
|
74
|
+
else
|
75
|
+
@failure = "no method `#{attribute}`"
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def returns_correct_value? # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
81
|
+
has_original_val = subject.instance_variable_defined?(attribute_ivar)
|
82
|
+
original_val = subject.instance_variable_get(attribute_ivar)
|
83
|
+
test_val = SecureRandom.hex
|
84
|
+
subject.instance_variable_set(attribute_ivar, test_val)
|
85
|
+
ret = if subject.send(attribute) == test_val
|
86
|
+
true
|
87
|
+
else
|
88
|
+
@failure = "method `#{attribute}` did not return the value of #{attribute_ivar}"
|
89
|
+
false
|
90
|
+
end
|
91
|
+
subject.instance_variable_set(attribute_ivar, original_val) if has_original_val
|
92
|
+
ret
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return String
|
96
|
+
def expectation
|
97
|
+
"<#{@original_subject}> to have attr_reader `#{attribute}`"
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Expected
|
6
|
+
# :nodoc:
|
7
|
+
module Matchers
|
8
|
+
|
9
|
+
# Used to test inclusion of `attr_writer :attribute` on the subject
|
10
|
+
# @param attribute [String, Symbol]
|
11
|
+
# @return [HaveAttrWriterMatcher]
|
12
|
+
#
|
13
|
+
# @example Test if the subject has `attr_writer :attribute`
|
14
|
+
# it { is_expected.to have_attr_writer(:some_attribute) }
|
15
|
+
#
|
16
|
+
def have_attr_writer(attribute) # rubocop:disable Naming/PredicateName
|
17
|
+
HaveAttrWriterMatcher.new(attribute)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Class used by {#have_constant}
|
21
|
+
class HaveAttrWriterMatcher
|
22
|
+
attr_reader :attribute, :subject
|
23
|
+
|
24
|
+
# @param attribute [String, Symbol] The attribute the {#subject} is expected to have an attr_writer for
|
25
|
+
def initialize(attribute)
|
26
|
+
unless attribute.is_a?(String) || attribute.is_a?(Symbol)
|
27
|
+
raise 'HaveAttrWriterMatcher attribute must be a String or Symbol'
|
28
|
+
end
|
29
|
+
@attribute = attribute.to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
# Run the test
|
33
|
+
# @param subject The thing to test against
|
34
|
+
# @return [True] If the test passes
|
35
|
+
# @return [False] if the test fails
|
36
|
+
def matches?(subject)
|
37
|
+
self.subject = subject
|
38
|
+
method? &&
|
39
|
+
sets_correct_value?
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [String]
|
43
|
+
def failure_message
|
44
|
+
"Expected #{expectation} (#{@failure})"
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [String]
|
48
|
+
def failure_message_when_negated
|
49
|
+
"Did not expect #{expectation}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String]
|
53
|
+
def description
|
54
|
+
"have_attr_writer: `#{attribute}`"
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# The thing to test against
|
60
|
+
# @return [Class, Module]
|
61
|
+
def subject=(subject)
|
62
|
+
@original_subject = subject
|
63
|
+
@subject = subject.instance_of?(Class) ? subject.allocate : subject
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [Symbol]
|
67
|
+
def attribute_ivar
|
68
|
+
@attribute_ivar ||= :"@#{attribute}"
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [Symbol]
|
72
|
+
def method_name
|
73
|
+
@method_name ||= :"#{attribute}="
|
74
|
+
end
|
75
|
+
|
76
|
+
def method?
|
77
|
+
if subject.respond_to? method_name
|
78
|
+
true
|
79
|
+
else
|
80
|
+
@failure = "no method `#{method_name}`"
|
81
|
+
false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def sets_correct_value? # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
86
|
+
has_original_val = subject.instance_variable_defined?(attribute_ivar)
|
87
|
+
original_val = subject.instance_variable_get(attribute_ivar)
|
88
|
+
test_val = SecureRandom.hex
|
89
|
+
subject.send(method_name, test_val)
|
90
|
+
ret = if subject.instance_variable_get(attribute_ivar) == test_val
|
91
|
+
true
|
92
|
+
else
|
93
|
+
@failure = "method `#{method_name}` did not set the value of #{attribute_ivar}"
|
94
|
+
false
|
95
|
+
end
|
96
|
+
subject.instance_variable_set(attribute_ivar, original_val) if has_original_val
|
97
|
+
ret
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return String
|
101
|
+
def expectation
|
102
|
+
"<#{@original_subject}> to have attr_writer `#{attribute}`"
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expected
|
4
|
+
# :nodoc:
|
5
|
+
module Matchers
|
6
|
+
|
7
|
+
# Used to test constants
|
8
|
+
# @param name [String, Symbol]
|
9
|
+
# @return [HaveConstantMatcher]
|
10
|
+
#
|
11
|
+
# @example Test if a constant exists
|
12
|
+
# it { is_expected.to have_constant(:FOO) }
|
13
|
+
#
|
14
|
+
# @example Test if a constant has a specific value
|
15
|
+
# it { is_expected.to have_constant(:FOO).with_value("bar") }
|
16
|
+
#
|
17
|
+
# @example Test if a constant's value is a specific type
|
18
|
+
# it { is_expected.to have_constant(:FOO).of_type(String) }
|
19
|
+
#
|
20
|
+
def have_constant(name) # rubocop:disable Naming/PredicateName
|
21
|
+
HaveConstantMatcher.new(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Class used by {#have_constant}
|
25
|
+
class HaveConstantMatcher
|
26
|
+
attr_reader :name, :subject
|
27
|
+
|
28
|
+
# @raise If the provided name is not a {String} or {Symbol}
|
29
|
+
# @param name [String, Symbol] The name of the constant
|
30
|
+
def initialize(name)
|
31
|
+
unless name.is_a?(String) || name.is_a?(Symbol)
|
32
|
+
raise 'HaveConstantMatcher constant name must be a String or Symbol'
|
33
|
+
end
|
34
|
+
@name = name
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sets the expected value of the constant
|
38
|
+
# @param value The expected value of the constant
|
39
|
+
# @return [self]
|
40
|
+
def with_value(value)
|
41
|
+
options[:value] = value
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# Sets the expected type of the constant's value
|
46
|
+
# @param type [Module, Class] The expected type of the constant's value
|
47
|
+
# @return [self]
|
48
|
+
def of_type(type)
|
49
|
+
options[:type] = type
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Run the test
|
54
|
+
# @param subject The thing to test against
|
55
|
+
# @return [True] If the test passes
|
56
|
+
# @return [False] if the test fails
|
57
|
+
def matches?(subject)
|
58
|
+
self.subject = subject
|
59
|
+
constant_exists? &&
|
60
|
+
correct_type? &&
|
61
|
+
correct_value?
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [String]
|
65
|
+
def failure_message
|
66
|
+
"Expected #{expectation} (#{@failure})"
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [String]
|
70
|
+
def failure_message_when_negated
|
71
|
+
"Did not expect #{expectation}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [String]
|
75
|
+
def description
|
76
|
+
description = "have_constant: #{name}"
|
77
|
+
description += " of_type => #{options[:type].inspect}" if options.key? :type
|
78
|
+
description += " with_value => #{options[:value].inspect}" if options.key? :value
|
79
|
+
description
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# @return [Hash]
|
85
|
+
def options
|
86
|
+
@options ||= {}.with_indifferent_access
|
87
|
+
end
|
88
|
+
|
89
|
+
# The thing to test against
|
90
|
+
# @return [Class, Module]
|
91
|
+
def subject=(subject)
|
92
|
+
@subject = subject.instance_of?(Class) || subject.is_a?(Module) ? subject : subject.class
|
93
|
+
end
|
94
|
+
|
95
|
+
# Check if the {#subject} has the constant
|
96
|
+
# @return Boolean
|
97
|
+
def constant_exists?
|
98
|
+
if subject.const_defined? name
|
99
|
+
true
|
100
|
+
else
|
101
|
+
@failure = 'missing constant'
|
102
|
+
false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Check if the constants value is the correct type, specified from {#of_type}
|
107
|
+
# @return Boolean
|
108
|
+
def correct_type?
|
109
|
+
return true unless options.key? :type
|
110
|
+
value = subject.const_get(name)
|
111
|
+
if value.is_a? options[:type]
|
112
|
+
true
|
113
|
+
else
|
114
|
+
@failure = "type was <#{value.class}>"
|
115
|
+
false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Check if the constants value is correct, specified from {#with_value}
|
120
|
+
# @return Boolean
|
121
|
+
def correct_value?
|
122
|
+
return true unless options.key? :value
|
123
|
+
value = subject.const_get(name)
|
124
|
+
if value == options[:value]
|
125
|
+
true
|
126
|
+
else
|
127
|
+
@failure = "value was #{value.inspect}"
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return String
|
133
|
+
def expectation
|
134
|
+
expectation = "<#{subject}> to have a constant named #{name}"
|
135
|
+
expectation += " with a type of <#{options[:type]}>" if options[:type]
|
136
|
+
expectation += " with a value of #{options[:value].inspect}" if options[:value]
|
137
|
+
expectation
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expected
|
4
|
+
# :nodoc:
|
5
|
+
module Matchers
|
6
|
+
|
7
|
+
# Used to test inheritance
|
8
|
+
# @param expected_ancestor [Class]
|
9
|
+
# @return [InheritFromMatcher]
|
10
|
+
#
|
11
|
+
# @example Test if the subject inherits from the supplied Class
|
12
|
+
# it { is_expected.to inherit_from(SomeClass) }
|
13
|
+
#
|
14
|
+
def inherit_from(expected_ancestor)
|
15
|
+
InheritFromMatcher.new(expected_ancestor)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Class used by {#have_constant}
|
19
|
+
class InheritFromMatcher
|
20
|
+
attr_reader :expected_ancestor, :subject
|
21
|
+
|
22
|
+
# @param expected_ancestor [Class] The ancestor the {#subject} is expected to inherit from
|
23
|
+
def initialize(expected_ancestor)
|
24
|
+
@expected_ancestor = expected_ancestor
|
25
|
+
end
|
26
|
+
|
27
|
+
# Run the test
|
28
|
+
# @param subject The thing to test against
|
29
|
+
# @return [True] If the test passes
|
30
|
+
# @return [False] if the test fails
|
31
|
+
def matches?(subject)
|
32
|
+
self.subject = subject
|
33
|
+
self.subject.ancestors.include? expected_ancestor
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [String]
|
37
|
+
def failure_message
|
38
|
+
"Expected #{expectation}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [String]
|
42
|
+
def failure_message_when_negated
|
43
|
+
"Did not expect #{expectation}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String]
|
47
|
+
def description
|
48
|
+
"inherit_from: <#{expected_ancestor.inspect}>"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# The thing to test against
|
54
|
+
# @return [Class, Module]
|
55
|
+
def subject=(subject)
|
56
|
+
@subject = subject.instance_of?(Class) || subject.is_a?(Module) ? subject : subject.class
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return String
|
60
|
+
def expectation
|
61
|
+
"<#{subject.inspect}> to inherit from <#{expected_ancestor.inspect}>"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expected
|
4
|
+
|
5
|
+
# Contains version information
|
6
|
+
module Version
|
7
|
+
MAJOR = 1
|
8
|
+
MINOR = 0
|
9
|
+
PATCH = 0
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
VERSION = [
|
14
|
+
Version::MAJOR,
|
15
|
+
Version::MINOR,
|
16
|
+
Version::PATCH,
|
17
|
+
].join('.').freeze
|
18
|
+
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: expected
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Taylor Yelverton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-03-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.0.0
|
20
|
+
- - "~>"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 5.0.0
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5.0'
|
33
|
+
description: RSpec's missing matchers
|
34
|
+
email: rubygems@yelvert.io
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- MIT-LICENSE
|
40
|
+
- README.md
|
41
|
+
- lib/expected.rb
|
42
|
+
- lib/expected/configuration.rb
|
43
|
+
- lib/expected/matchers.rb
|
44
|
+
- lib/expected/matchers/have_attr_accessor.rb
|
45
|
+
- lib/expected/matchers/have_attr_reader.rb
|
46
|
+
- lib/expected/matchers/have_attr_writer.rb
|
47
|
+
- lib/expected/matchers/have_constant.rb
|
48
|
+
- lib/expected/matchers/inherit_from.rb
|
49
|
+
- lib/expected/version.rb
|
50
|
+
homepage: https://github.com/yelvert/expected
|
51
|
+
licenses:
|
52
|
+
- MIT
|
53
|
+
metadata:
|
54
|
+
bug_tracker_uri: https://github.com/yelvert/expected/issues
|
55
|
+
changelog_uri: https://github.com/yelvert/expected/commits/master
|
56
|
+
documentation_uri: https://github.com/yelvert/expected/wiki
|
57
|
+
homepage_uri: https://github.com/yelvert/expected
|
58
|
+
source_code_uri: https://github.com/yelvert/expected
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 2.4.0
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.0.4
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: RSpec's missing matchers
|
78
|
+
test_files: []
|