fix 1.0.0.beta3 → 1.0.0.beta7
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 +4 -4
- data/LICENSE.md +1 -1
- data/README.md +59 -38
- data/lib/fix.rb +12 -15
- data/lib/fix/doc.rb +24 -0
- data/lib/fix/dsl.rb +113 -0
- data/lib/fix/matcher.rb +209 -0
- data/lib/fix/requirement.rb +162 -0
- data/lib/fix/run.rb +55 -0
- data/lib/fix/set.rb +97 -0
- data/lib/kernel.rb +29 -2
- metadata +96 -35
- data/lib/fix/context.rb +0 -147
- data/lib/fix/expectation_result_not_found_error.rb +0 -5
- data/lib/fix/it.rb +0 -31
- data/lib/fix/suspicious_success_error.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81f6a38942f7037e0250876038e1a715b9b905d5fb2186fb1b3dc3884ebee446
|
4
|
+
data.tar.gz: 76f805fc42a2683236ed85efbc888c25fadfaeb2f975177127101e4eb8ae2618
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0ae0f82dd15aff8f14252631c2c8cf134bd6ce78ba814267f53e7b88a5d84ff2c7d14bb753152f51cea595f46a0284ae58d370e6e50f74fa6c6ec7bb7dcace1
|
7
|
+
data.tar.gz: 983e9f29a66982ab2cfef0548b50c064c0b63605acdb0f055da6f35cfd2153f667bf3d58975f6d274b49101d557149336e3a6c465549cfa3658f758903024617
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -1,31 +1,44 @@
|
|
1
1
|
# Fix
|
2
2
|
|
3
|
-
[](https://fixrb.dev/)
|
4
|
+
[](https://github.com/fixrb/fix/releases)
|
5
|
+
[](https://rubydoc.info/github/fixrb/fix/main)
|
6
|
+
[](https://github.com/fixrb/fix/actions?query=workflow%3Aci+branch%3Amain)
|
7
|
+
[](https://github.com/fixrb/fix/actions?query=workflow%3Arubocop+branch%3Amain)
|
8
|
+
[](https://github.com/fixrb/fix/raw/main/LICENSE.md)
|
9
9
|
|
10
|
-
|
10
|
+
⚠️ This project is still in the experimental phase. May be used at your own risk.
|
11
|
+
|
12
|
+

|
13
|
+
|
14
|
+
## Project goals
|
15
|
+
|
16
|
+
* Extract specs from the tests.
|
17
|
+
* Look like English documents.
|
18
|
+
* Be minimalist and easy to hack.
|
19
|
+
* Run tests quickly.
|
11
20
|
|
12
21
|
## Installation
|
13
22
|
|
14
23
|
Add this line to your application's Gemfile:
|
15
24
|
|
16
25
|
```ruby
|
17
|
-
gem
|
26
|
+
gem "fix", ">= 1.0.0.beta7"
|
18
27
|
```
|
19
28
|
|
20
29
|
And then execute:
|
21
30
|
|
22
|
-
|
31
|
+
```sh
|
32
|
+
bundle
|
33
|
+
```
|
23
34
|
|
24
35
|
Or install it yourself as:
|
25
36
|
|
26
|
-
|
37
|
+
```sh
|
38
|
+
gem install fix --pre
|
39
|
+
```
|
27
40
|
|
28
|
-
##
|
41
|
+
## Example
|
29
42
|
|
30
43
|
Given this app:
|
31
44
|
|
@@ -33,59 +46,74 @@ Given this app:
|
|
33
46
|
# examples/duck/app.rb
|
34
47
|
class Duck
|
35
48
|
def walks
|
36
|
-
|
49
|
+
"Klop klop!"
|
37
50
|
end
|
38
51
|
|
39
52
|
def swims
|
40
|
-
|
53
|
+
"Swoosh..."
|
41
54
|
end
|
42
55
|
|
43
56
|
def quacks
|
44
|
-
puts
|
57
|
+
puts "Quaaaaaack!"
|
45
58
|
end
|
46
59
|
end
|
47
60
|
```
|
48
61
|
|
49
|
-
|
62
|
+
And this fixed behavior:
|
50
63
|
|
51
64
|
```ruby
|
52
65
|
# examples/duck/fix.rb
|
53
|
-
require_relative 'app'
|
54
|
-
require_relative '../../lib/fix'
|
55
66
|
|
56
|
-
|
67
|
+
relative "fix"
|
68
|
+
|
69
|
+
Fix :Duck do
|
70
|
+
it SHOULD be_an_instance_of :Duck
|
57
71
|
|
58
|
-
Fix(@bird) do
|
59
72
|
on :swims do
|
60
|
-
it
|
73
|
+
it MUST be_an_instance_of :String
|
74
|
+
it MUST eql "Swoosh..."
|
61
75
|
end
|
62
76
|
|
63
77
|
on :speaks do
|
64
|
-
it
|
78
|
+
it MUST raise_exception NoMethodError
|
65
79
|
end
|
66
80
|
|
67
81
|
on :sings do
|
68
|
-
it
|
82
|
+
it MAY eql "♪... ♫..."
|
69
83
|
end
|
70
84
|
end
|
71
85
|
```
|
72
86
|
|
73
|
-
|
87
|
+
When I run this test:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
# examples/duck/test.rb
|
91
|
+
|
92
|
+
require_relative "app"
|
93
|
+
require_relative "fix"
|
94
|
+
|
95
|
+
Fix[:Duck].test { Duck.new }
|
96
|
+
```
|
74
97
|
|
75
98
|
```sh
|
76
|
-
ruby examples/duck/
|
99
|
+
ruby examples/duck/test.rb
|
77
100
|
```
|
78
101
|
|
102
|
+
I should see this output:
|
103
|
+
|
79
104
|
```txt
|
80
|
-
|
81
|
-
|
82
|
-
|
105
|
+
Success: expected #<Duck:0x00007fc55b3c6388> to be an instance of Duck.
|
106
|
+
Success: expected to eql "Swoosh...".
|
107
|
+
Success: undefined method `speaks' for #<Duck:0x00007fc55b3c46f0>.
|
108
|
+
NoMethodError: undefined method `sings' for #<Duck:0x00007fc558aab6d8>.
|
83
109
|
```
|
84
110
|
|
85
111
|
## Contact
|
86
112
|
|
87
|
-
* Home page: https://fixrb.dev/
|
88
|
-
* Source code: https://github.com/fixrb/fix
|
113
|
+
* Home page: [https://fixrb.dev/](https://fixrb.dev/)
|
114
|
+
* Source code: [https://github.com/fixrb/fix](https://github.com/fixrb/fix)
|
115
|
+
* API Doc: [https://rubydoc.info/gems/fix](https://rubydoc.info/gems/fix)
|
116
|
+
* Twitter: [https://twitter.com/fix\_rb](https://twitter.com/fix\_rb)
|
89
117
|
|
90
118
|
## Versioning
|
91
119
|
|
@@ -93,20 +121,13 @@ __Fix__ follows [Semantic Versioning 2.0](https://semver.org/).
|
|
93
121
|
|
94
122
|
## License
|
95
123
|
|
96
|
-
The gem is available as open source under the terms of the [MIT License](https://
|
124
|
+
The [gem](https://rubygems.org/gems/fix) is available as open source under the terms of the [MIT License](https://github.com/fixrb/fix/raw/main/LICENSE.md).
|
97
125
|
|
98
126
|
***
|
99
127
|
|
100
128
|
<p>
|
101
129
|
This project is sponsored by:<br />
|
102
130
|
<a href="https://sashite.com/"><img
|
103
|
-
src="https://github.com/fixrb/fix/raw/
|
131
|
+
src="https://github.com/fixrb/fix/raw/main/img/sashite.png"
|
104
132
|
alt="Sashite" /></a>
|
105
133
|
</p>
|
106
|
-
|
107
|
-
[travis]: https://travis-ci.org/fixrb/fix
|
108
|
-
[codeclimate]: https://codeclimate.com/github/fixrb/fix
|
109
|
-
[gem]: https://rubygems.org/gems/fix
|
110
|
-
[inchpages]: https://inch-ci.org/github/fixrb/fix
|
111
|
-
[rubydoc]: https://rubydoc.info/gems/fix/frames
|
112
|
-
[gitter]: https://gitter.im/fixrb/fix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
data/lib/fix.rb
CHANGED
@@ -1,25 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("fix", "set")
|
4
|
+
|
5
|
+
require_relative "kernel"
|
6
|
+
|
3
7
|
# Namespace for the Fix framework.
|
4
8
|
#
|
9
|
+
# @api public
|
5
10
|
module Fix
|
6
|
-
#
|
11
|
+
# Test a built specification.
|
7
12
|
#
|
8
|
-
# @example
|
9
|
-
#
|
10
|
-
# it { MUST equal 42 }
|
11
|
-
# end
|
13
|
+
# @example Run _Answer_ specification against `42`.
|
14
|
+
# Fix[:Answer].test(42)
|
12
15
|
#
|
13
|
-
# @param
|
14
|
-
# @param options [Hash] Some options.
|
15
|
-
# @param specs [Proc] The set of specs.
|
16
|
+
# @param name [String, Symbol] The name of the specification document.
|
16
17
|
#
|
17
|
-
# @
|
18
|
-
def self.
|
19
|
-
|
20
|
-
c.instance_eval(&block)
|
18
|
+
# @return [::Fix::Test] The specification document.
|
19
|
+
def self.[](name)
|
20
|
+
::Fix::Set.load(name)
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
|
-
require_relative 'kernel'
|
25
|
-
require_relative File.join('fix', 'context')
|
data/lib/fix/doc.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fix
|
4
|
+
# Module for storing spec documents.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
module Doc
|
8
|
+
# @param name [String, Symbol] The name of the specification document.
|
9
|
+
def self.fetch(name)
|
10
|
+
const_get("#{name}::CONTEXTS")
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param contexts [Array<::Fix::Dsl>] The list of contexts document.
|
14
|
+
def self.specs(*contexts)
|
15
|
+
contexts.flat_map do |context|
|
16
|
+
env = context.new
|
17
|
+
|
18
|
+
env.public_methods(false).map do |public_method|
|
19
|
+
[env] + env.public_send(public_method)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/fix/dsl.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "defi"
|
4
|
+
|
5
|
+
require_relative "matcher"
|
6
|
+
require_relative "requirement"
|
7
|
+
|
8
|
+
module Fix
|
9
|
+
# Abstract class for handling the domain-specific language.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class Dsl
|
13
|
+
extend Matcher
|
14
|
+
extend Requirement
|
15
|
+
|
16
|
+
# Sets a user-defined property.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# require "fix"
|
20
|
+
#
|
21
|
+
# Fix do
|
22
|
+
# let(:name) { "Bob" }
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @param name [String, Symbol] The name of the property.
|
26
|
+
# @param block [Proc] The content of the method to define.
|
27
|
+
#
|
28
|
+
# @return [Symbol] A private method that define the block content.
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def self.let(name, &block)
|
32
|
+
private define_method(name, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Defines an example group with user-defined properties that describes a
|
36
|
+
# unit to be tested.
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# require "fix"
|
40
|
+
#
|
41
|
+
# Fix do
|
42
|
+
# with :password, "secret" do
|
43
|
+
# it MUST be true
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# @param kwargs [Hash] The list of propreties.
|
48
|
+
# @param block [Proc] The block to define the specs.
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def self.with(**kwargs, &block)
|
52
|
+
klass = ::Class.new(self)
|
53
|
+
klass.const_get(:CONTEXTS) << klass
|
54
|
+
kwargs.each { |name, value| klass.let(name) { value } }
|
55
|
+
klass.instance_eval(&block)
|
56
|
+
klass
|
57
|
+
end
|
58
|
+
|
59
|
+
# Defines an example group that describes a unit to be tested.
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# require "fix"
|
63
|
+
#
|
64
|
+
# Fix do
|
65
|
+
# on :+, 2 do
|
66
|
+
# it MUST be 42
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# @param method_name [String, Symbol] The method to send to the subject.
|
71
|
+
# @param block [Proc] The block to define the specs.
|
72
|
+
#
|
73
|
+
# @api public
|
74
|
+
def self.on(method_name, *args, **kwargs, &block)
|
75
|
+
klass = ::Class.new(self)
|
76
|
+
klass.const_get(:CONTEXTS) << klass
|
77
|
+
|
78
|
+
const_set("Child#{block.object_id}", klass)
|
79
|
+
|
80
|
+
klass.define_singleton_method(:challenges) do
|
81
|
+
challenge = ::Defi.send(method_name, *args, **kwargs)
|
82
|
+
super() + [challenge]
|
83
|
+
end
|
84
|
+
|
85
|
+
klass.instance_eval(&block)
|
86
|
+
klass
|
87
|
+
end
|
88
|
+
|
89
|
+
# Defines a concrete spec definition.
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# require "fix"
|
93
|
+
#
|
94
|
+
# Fix { it MUST be 42 }
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
def self.it(requirement)
|
98
|
+
location = caller_locations(1, 1).fetch(0)
|
99
|
+
location = [location.path, location.lineno].join(":")
|
100
|
+
|
101
|
+
define_method("test_#{requirement.object_id}") do
|
102
|
+
[location, requirement, self.class.challenges]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# The list of challenges to be addressed to the object to be tested.
|
107
|
+
#
|
108
|
+
# @return [Array<Defi::Challenge>] A list of challenges.
|
109
|
+
def self.challenges
|
110
|
+
[]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/fix/matcher.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "matchi"
|
4
|
+
|
5
|
+
module Fix
|
6
|
+
# Collection of expectation matchers.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
module Matcher
|
10
|
+
# Equivalence matcher
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# matcher = eq("foo")
|
14
|
+
# matcher.matches? { "foo" } # => true
|
15
|
+
# matcher.matches? { "bar" } # => false
|
16
|
+
#
|
17
|
+
# @param expected [#eql?] An expected equivalent object.
|
18
|
+
#
|
19
|
+
# @return [#matches?] An equivalence matcher.
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def eq(expected)
|
23
|
+
::Matchi::Eq.new(expected)
|
24
|
+
end
|
25
|
+
|
26
|
+
alias eql eq
|
27
|
+
|
28
|
+
# Identity matcher
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# object = "foo"
|
32
|
+
# matcher = be(object)
|
33
|
+
# matcher.matches? { object } # => true
|
34
|
+
# matcher.matches? { "foo" } # => false
|
35
|
+
#
|
36
|
+
# @param expected [#equal?] The expected identical object.
|
37
|
+
#
|
38
|
+
# @return [#matches?] An identity matcher.
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def be(expected)
|
42
|
+
::Matchi::Be.new(expected)
|
43
|
+
end
|
44
|
+
|
45
|
+
alias equal be
|
46
|
+
|
47
|
+
# Comparisons matcher
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# matcher = be_within(1).of(41)
|
51
|
+
# matcher.matches? { 42 } # => true
|
52
|
+
# matcher.matches? { 43 } # => false
|
53
|
+
#
|
54
|
+
# @param delta [Numeric] A numeric value.
|
55
|
+
#
|
56
|
+
# @return [#matches?] A comparison matcher.
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def be_within(delta)
|
60
|
+
::Matchi::BeWithin.new(delta)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Regular expressions matcher
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# matcher = match(/^foo$/)
|
67
|
+
# matcher.matches? { "foo" } # => true
|
68
|
+
# matcher.matches? { "bar" } # => false
|
69
|
+
#
|
70
|
+
# @param expected [#match] A regular expression.
|
71
|
+
#
|
72
|
+
# @return [#matches?] A regular expression matcher.
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
def match(expected)
|
76
|
+
::Matchi::Match.new(expected)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Expecting errors matcher
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# matcher = raise_exception(NameError)
|
83
|
+
# matcher.matches? { Boom } # => true
|
84
|
+
# matcher.matches? { true } # => false
|
85
|
+
#
|
86
|
+
# @param expected [Exception, #to_s] The expected exception name.
|
87
|
+
#
|
88
|
+
# @return [#matches?] An error matcher.
|
89
|
+
#
|
90
|
+
# @api public
|
91
|
+
def raise_exception(expected)
|
92
|
+
::Matchi::RaiseException.new(expected)
|
93
|
+
end
|
94
|
+
|
95
|
+
# True matcher
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# matcher = be_true
|
99
|
+
# matcher.matches? { true } # => true
|
100
|
+
# matcher.matches? { false } # => false
|
101
|
+
# matcher.matches? { nil } # => false
|
102
|
+
# matcher.matches? { 4 } # => false
|
103
|
+
#
|
104
|
+
# @return [#matches?] A `true` matcher.
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
def be_true
|
108
|
+
be(true)
|
109
|
+
end
|
110
|
+
|
111
|
+
# False matcher
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# matcher = be_false
|
115
|
+
# matcher.matches? { false } # => true
|
116
|
+
# matcher.matches? { true } # => false
|
117
|
+
# matcher.matches? { nil } # => false
|
118
|
+
# matcher.matches? { 4 } # => false
|
119
|
+
#
|
120
|
+
# @return [#matches?] A `false` matcher.
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
def be_false
|
124
|
+
be(false)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Nil matcher
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# matcher = be_nil
|
131
|
+
# matcher.matches? { nil } # => true
|
132
|
+
# matcher.matches? { false } # => false
|
133
|
+
# matcher.matches? { true } # => false
|
134
|
+
# matcher.matches? { 4 } # => false
|
135
|
+
#
|
136
|
+
# @return [#matches?] A `nil` matcher.
|
137
|
+
#
|
138
|
+
# @api public
|
139
|
+
def be_nil
|
140
|
+
be(nil)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Type/class matcher
|
144
|
+
#
|
145
|
+
# @example
|
146
|
+
# matcher = be_an_instance_of(String)
|
147
|
+
# matcher.matches? { "foo" } # => true
|
148
|
+
# matcher.matches? { 4 } # => false
|
149
|
+
#
|
150
|
+
# @param expected [Class, #to_s] The expected class name.
|
151
|
+
#
|
152
|
+
# @return [#matches?] A type/class matcher.
|
153
|
+
#
|
154
|
+
# @api public
|
155
|
+
def be_an_instance_of(expected)
|
156
|
+
::Matchi::BeAnInstanceOf.new(expected)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Change matcher
|
160
|
+
#
|
161
|
+
# @example
|
162
|
+
# object = []
|
163
|
+
# matcher = change(object, :length).by(1)
|
164
|
+
# matcher.matches? { object << 1 } # => true
|
165
|
+
#
|
166
|
+
# object = []
|
167
|
+
# matcher = change(object, :length).by_at_least(1)
|
168
|
+
# matcher.matches? { object << 1 } # => true
|
169
|
+
#
|
170
|
+
# object = []
|
171
|
+
# matcher = change(object, :length).by_at_most(1)
|
172
|
+
# matcher.matches? { object << 1 } # => true
|
173
|
+
#
|
174
|
+
# object = "foo"
|
175
|
+
# matcher = change(object, :to_s).from("foo").to("FOO")
|
176
|
+
# matcher.matches? { object.upcase! } # => true
|
177
|
+
#
|
178
|
+
# object = "foo"
|
179
|
+
# matcher = change(object, :to_s).to("FOO")
|
180
|
+
# matcher.matches? { object.upcase! } # => true
|
181
|
+
#
|
182
|
+
# @param object [#object_id] An object.
|
183
|
+
# @param method [Symbol] The name of a method.
|
184
|
+
# @param args [Array] A list of arguments.
|
185
|
+
# @param kwargs [Hash] A list of keyword arguments.
|
186
|
+
#
|
187
|
+
# @return [#matches?] A change matcher.
|
188
|
+
#
|
189
|
+
# @api public
|
190
|
+
def change(object, method, *args, **kwargs, &block)
|
191
|
+
::Matchi::Change.new(object, method, *args, **kwargs, &block)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Satisfy matcher
|
195
|
+
#
|
196
|
+
# @example
|
197
|
+
# matcher = satisfy { |value| value == 42 }
|
198
|
+
# matcher.matches? { 42 } # => true
|
199
|
+
#
|
200
|
+
# @param expected [Proc] A block of code.
|
201
|
+
#
|
202
|
+
# @return [#matches?] A satisfy matcher.
|
203
|
+
#
|
204
|
+
# @api public
|
205
|
+
def satisfy(&expected)
|
206
|
+
::Matchi::Satisfy.new(&expected)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spectus/requirement/optional"
|
4
|
+
require "spectus/requirement/recommended"
|
5
|
+
require "spectus/requirement/required"
|
6
|
+
|
7
|
+
module Fix
|
8
|
+
# Collection of expectation matchers.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
module Requirement
|
12
|
+
# rubocop:disable Naming/MethodName
|
13
|
+
|
14
|
+
# This method mean that the definition is an absolute requirement of the
|
15
|
+
# specification.
|
16
|
+
#
|
17
|
+
# @param matcher [#matches?] The matcher.
|
18
|
+
#
|
19
|
+
# @return [Requirement::Required] An absolute requirement level instance.
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def MUST(matcher)
|
23
|
+
::Spectus::Requirement::Required.new(
|
24
|
+
isolate: false,
|
25
|
+
negate: false,
|
26
|
+
matcher: matcher
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @see MUST
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def MUST!(matcher)
|
34
|
+
::Spectus::Requirement::Required.new(
|
35
|
+
isolate: true,
|
36
|
+
negate: false,
|
37
|
+
matcher: matcher
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
# This method mean that the definition is an absolute prohibition of the specification.
|
42
|
+
#
|
43
|
+
# @param matcher [#matches?] The matcher.
|
44
|
+
#
|
45
|
+
# @return [Requirement::Required] An absolute prohibition level instance.
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def MUST_NOT(matcher)
|
49
|
+
::Spectus::Requirement::Required.new(
|
50
|
+
isolate: false,
|
51
|
+
negate: true,
|
52
|
+
matcher: matcher
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @see MUST_NOT
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def MUST_NOT!(matcher)
|
60
|
+
::Spectus::Requirement::Required.new(
|
61
|
+
isolate: true,
|
62
|
+
negate: true,
|
63
|
+
matcher: matcher
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
# This method mean that there may exist valid reasons in particular
|
68
|
+
# circumstances to ignore a particular item, but the full implications must be
|
69
|
+
# understood and carefully weighed before choosing a different course.
|
70
|
+
#
|
71
|
+
# @param matcher [#matches?] The matcher.
|
72
|
+
#
|
73
|
+
# @return [Requirement::Recommended] A recommended requirement level instance.
|
74
|
+
#
|
75
|
+
# @api public
|
76
|
+
def SHOULD(matcher)
|
77
|
+
::Spectus::Requirement::Recommended.new(
|
78
|
+
isolate: false,
|
79
|
+
negate: false,
|
80
|
+
matcher: matcher
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
# @see SHOULD
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
def SHOULD!(matcher)
|
88
|
+
::Spectus::Requirement::Recommended.new(
|
89
|
+
isolate: true,
|
90
|
+
negate: false,
|
91
|
+
matcher: matcher
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
# This method mean that there may exist valid reasons in particular
|
96
|
+
# circumstances when the particular behavior is acceptable or even useful, but
|
97
|
+
# the full implications should be understood and the case carefully weighed
|
98
|
+
# before implementing any behavior described with this label.
|
99
|
+
#
|
100
|
+
# @param matcher [#matches?] The matcher.
|
101
|
+
#
|
102
|
+
# @return [Requirement::Recommended] A not recommended requirement level
|
103
|
+
# instance.
|
104
|
+
#
|
105
|
+
# @api public
|
106
|
+
def SHOULD_NOT(matcher)
|
107
|
+
::Spectus::Requirement::Recommended.new(
|
108
|
+
isolate: false,
|
109
|
+
negate: true,
|
110
|
+
matcher: matcher
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
# @see SHOULD_NOT
|
115
|
+
#
|
116
|
+
# @api public
|
117
|
+
def SHOULD_NOT!(matcher)
|
118
|
+
::Spectus::Requirement::Recommended.new(
|
119
|
+
isolate: true,
|
120
|
+
negate: true,
|
121
|
+
matcher: matcher
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
# This method mean that an item is truly optional.
|
126
|
+
# One vendor may choose to include the item because a particular marketplace
|
127
|
+
# requires it or because the vendor feels that it enhances the product while
|
128
|
+
# another vendor may omit the same item. An implementation which does not
|
129
|
+
# include a particular option must be prepared to interoperate with another
|
130
|
+
# implementation which does include the option, though perhaps with reduced
|
131
|
+
# functionality. In the same vein an implementation which does include a
|
132
|
+
# particular option must be prepared to interoperate with another
|
133
|
+
# implementation which does not include the option (except, of course, for the
|
134
|
+
# feature the option provides).
|
135
|
+
#
|
136
|
+
# @param matcher [#matches?] The matcher.
|
137
|
+
#
|
138
|
+
# @return [Requirement::Optional] An optional requirement level instance.
|
139
|
+
#
|
140
|
+
# @api public
|
141
|
+
def MAY(matcher)
|
142
|
+
::Spectus::Requirement::Optional.new(
|
143
|
+
isolate: false,
|
144
|
+
negate: false,
|
145
|
+
matcher: matcher
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
# @see MAY
|
150
|
+
#
|
151
|
+
# @api public
|
152
|
+
def MAY!(matcher)
|
153
|
+
::Spectus::Requirement::Optional.new(
|
154
|
+
isolate: true,
|
155
|
+
negate: false,
|
156
|
+
matcher: matcher
|
157
|
+
)
|
158
|
+
end
|
159
|
+
|
160
|
+
# rubocop:enable Naming/MethodName
|
161
|
+
end
|
162
|
+
end
|
data/lib/fix/run.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "expresenter/fail"
|
4
|
+
|
5
|
+
module Fix
|
6
|
+
# Run class.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class Run
|
10
|
+
# @return [::Fix::Dsl] A context instance.
|
11
|
+
attr_reader :environment
|
12
|
+
|
13
|
+
# @return [::Spectus::Requirement::Base] An expectation.
|
14
|
+
attr_reader :requirement
|
15
|
+
|
16
|
+
# @return [Array<::Defi::Challenge>] A list of challenges.
|
17
|
+
attr_reader :challenges
|
18
|
+
|
19
|
+
# @param environment [::Fix::Dsl] A context instance.
|
20
|
+
# @param requirement [::Spectus::Requirement::Base] An expectation.
|
21
|
+
# @param challenges [Array<::Defi::Challenge>] A list of challenges.
|
22
|
+
def initialize(environment, requirement, *challenges)
|
23
|
+
@environment = environment
|
24
|
+
@requirement = requirement
|
25
|
+
@challenges = challenges
|
26
|
+
end
|
27
|
+
|
28
|
+
# Verify if the object checks the condition.
|
29
|
+
#
|
30
|
+
# @param subject [Proc] The block of code to be tested.
|
31
|
+
#
|
32
|
+
# @raise [::Expresenter::Fail] A failed spec exception.
|
33
|
+
# @return [::Expresenter::Pass] A passed spec instance.
|
34
|
+
#
|
35
|
+
# @see https://github.com/fixrb/expresenter
|
36
|
+
def against(&subject)
|
37
|
+
requirement.call { actual_value(&subject) }
|
38
|
+
rescue ::Expresenter::Fail => e
|
39
|
+
e
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# The test's actual value.
|
45
|
+
#
|
46
|
+
# @param subject [Proc] The block of code to be tested.
|
47
|
+
#
|
48
|
+
# @return [#object_id] The actual value to be tested.
|
49
|
+
def actual_value(&subject)
|
50
|
+
challenges.inject(environment.instance_eval(&subject)) do |obj, challenge|
|
51
|
+
challenge.to(obj).call
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/fix/set.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "doc"
|
4
|
+
require_relative "run"
|
5
|
+
|
6
|
+
module Fix
|
7
|
+
# Collection of specifications.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Set
|
11
|
+
# The type of result.
|
12
|
+
#
|
13
|
+
# Passed expectations can be classified as:
|
14
|
+
#
|
15
|
+
# * `success`
|
16
|
+
# * `warning`
|
17
|
+
# * `info`
|
18
|
+
#
|
19
|
+
# Failed expectations can be classified as:
|
20
|
+
#
|
21
|
+
# * `failure`
|
22
|
+
# * `error`
|
23
|
+
LOG_LEVELS = %w[
|
24
|
+
none
|
25
|
+
error
|
26
|
+
failure
|
27
|
+
warning
|
28
|
+
info
|
29
|
+
success
|
30
|
+
].freeze
|
31
|
+
|
32
|
+
# @return [Array] A list of specifications.
|
33
|
+
attr_reader :specs
|
34
|
+
|
35
|
+
# @param name [String, Symbol] The name of the specification document.
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
def self.load(name)
|
39
|
+
new(*Doc.fetch(name))
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param contexts [Array<::Fix::Dsl>] The list of contexts document.
|
43
|
+
def initialize(*contexts)
|
44
|
+
@specs = Doc.specs(*contexts)
|
45
|
+
@passed = true
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param subject [Proc] The block of code to be tested.
|
49
|
+
#
|
50
|
+
# @raise [::SystemExit] The result of the test.
|
51
|
+
#
|
52
|
+
# @api public
|
53
|
+
def test(log_level: 5, &subject)
|
54
|
+
randomize!
|
55
|
+
|
56
|
+
specs.each do |environment, location, requirement, challenges|
|
57
|
+
runner = Run.new(environment, requirement, *challenges)
|
58
|
+
result = runner.against(&subject)
|
59
|
+
|
60
|
+
failed! if result.failed?
|
61
|
+
report!(location, result, log_level: log_level)
|
62
|
+
end
|
63
|
+
|
64
|
+
exit!
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def randomize!
|
70
|
+
specs.shuffle!
|
71
|
+
end
|
72
|
+
|
73
|
+
# @raise [::SystemExit] The result of the test.
|
74
|
+
def exit!
|
75
|
+
::Kernel.exit(passed?)
|
76
|
+
end
|
77
|
+
|
78
|
+
def failed!
|
79
|
+
@passed = false
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Boolean] The test set passed or failed.
|
83
|
+
def passed?
|
84
|
+
@passed
|
85
|
+
end
|
86
|
+
|
87
|
+
def report!(path, result, log_level:)
|
88
|
+
return unless report?(result, log_level: log_level)
|
89
|
+
|
90
|
+
puts "#{path} #{result.colored_string}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def report?(result, log_level:)
|
94
|
+
LOG_LEVELS[1..log_level].any? { |name| result.public_send("#{name}?") }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/kernel.rb
CHANGED
@@ -1,9 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative File.join("fix", "doc")
|
4
|
+
require_relative File.join("fix", "dsl")
|
5
|
+
require_relative File.join("fix", "set")
|
6
|
+
|
7
|
+
# The Kernel module.
|
3
8
|
module Kernel
|
4
9
|
# rubocop:disable Naming/MethodName
|
5
|
-
|
6
|
-
|
10
|
+
|
11
|
+
# Specifications are built with this method.
|
12
|
+
#
|
13
|
+
# @example Require an answer equal to 42.
|
14
|
+
# # The spec
|
15
|
+
# Fix :Answer do
|
16
|
+
# it MUST equal 42
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # A test
|
20
|
+
# Fix[:Answer].test { 42 }
|
21
|
+
#
|
22
|
+
# @param name [String, Symbol] The name of the specification document.
|
23
|
+
# @param block [Proc] The specifications.
|
24
|
+
#
|
25
|
+
# @return [#test] The collection of specifications.
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def Fix(name = nil, &block)
|
29
|
+
klass = ::Class.new(::Fix::Dsl)
|
30
|
+
klass.const_set(:CONTEXTS, [klass])
|
31
|
+
klass.instance_eval(&block)
|
32
|
+
::Fix::Doc.const_set(name, klass) unless name.nil?
|
33
|
+
::Fix::Set.new(*klass.const_get(:CONTEXTS))
|
7
34
|
end
|
8
35
|
# rubocop:enable Naming/MethodName
|
9
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: defi
|
@@ -16,70 +16,84 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.0.
|
19
|
+
version: 2.0.5
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.0.
|
26
|
+
version: 2.0.5
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: matchi
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.2.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: spectus
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
47
|
+
version: 4.0.2
|
34
48
|
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
54
|
+
version: 4.0.2
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- - "
|
59
|
+
- - ">="
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '0'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- - "
|
66
|
+
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rake
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- - "
|
73
|
+
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
75
|
+
version: '0'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- - "
|
80
|
+
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name: rubocop
|
84
|
+
name: rubocop-md
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - ">="
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0
|
89
|
+
version: '0'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rubocop-performance
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,35 +108,77 @@ dependencies:
|
|
94
108
|
- - ">="
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop-thread_safety
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
97
153
|
- !ruby/object:Gem::Dependency
|
98
154
|
name: simplecov
|
99
155
|
requirement: !ruby/object:Gem::Requirement
|
100
156
|
requirements:
|
101
|
-
- - "
|
157
|
+
- - ">="
|
102
158
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0
|
159
|
+
version: '0'
|
104
160
|
type: :development
|
105
161
|
prerelease: false
|
106
162
|
version_requirements: !ruby/object:Gem::Requirement
|
107
163
|
requirements:
|
108
|
-
- - "
|
164
|
+
- - ">="
|
109
165
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0
|
166
|
+
version: '0'
|
111
167
|
- !ruby/object:Gem::Dependency
|
112
168
|
name: yard
|
113
169
|
requirement: !ruby/object:Gem::Requirement
|
114
170
|
requirements:
|
115
|
-
- - "
|
171
|
+
- - ">="
|
116
172
|
- !ruby/object:Gem::Version
|
117
|
-
version: '0
|
173
|
+
version: '0'
|
118
174
|
type: :development
|
119
175
|
prerelease: false
|
120
176
|
version_requirements: !ruby/object:Gem::Requirement
|
121
177
|
requirements:
|
122
|
-
- - "
|
178
|
+
- - ">="
|
123
179
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0
|
125
|
-
description: Specing framework
|
180
|
+
version: '0'
|
181
|
+
description: Specing framework.
|
126
182
|
email: contact@cyril.email
|
127
183
|
executables: []
|
128
184
|
extensions: []
|
@@ -131,16 +187,21 @@ files:
|
|
131
187
|
- LICENSE.md
|
132
188
|
- README.md
|
133
189
|
- lib/fix.rb
|
134
|
-
- lib/fix/
|
135
|
-
- lib/fix/
|
136
|
-
- lib/fix/
|
137
|
-
- lib/fix/
|
190
|
+
- lib/fix/doc.rb
|
191
|
+
- lib/fix/dsl.rb
|
192
|
+
- lib/fix/matcher.rb
|
193
|
+
- lib/fix/requirement.rb
|
194
|
+
- lib/fix/run.rb
|
195
|
+
- lib/fix/set.rb
|
138
196
|
- lib/kernel.rb
|
139
197
|
homepage: https://fixrb.dev/
|
140
198
|
licenses:
|
141
199
|
- MIT
|
142
200
|
metadata:
|
201
|
+
bug_tracker_uri: https://github.com/fixrb/fix/issues
|
202
|
+
documentation_uri: https://rubydoc.info/gems/fix
|
143
203
|
source_code_uri: https://github.com/fixrb/fix
|
204
|
+
wiki_uri: https://github.com/fixrb/fix/wiki
|
144
205
|
post_install_message:
|
145
206
|
rdoc_options: []
|
146
207
|
require_paths:
|
@@ -149,15 +210,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
210
|
requirements:
|
150
211
|
- - ">="
|
151
212
|
- !ruby/object:Gem::Version
|
152
|
-
version: 2.
|
213
|
+
version: 2.7.0
|
153
214
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
215
|
requirements:
|
155
216
|
- - ">"
|
156
217
|
- !ruby/object:Gem::Version
|
157
218
|
version: 1.3.1
|
158
219
|
requirements: []
|
159
|
-
rubygems_version: 3.1.
|
220
|
+
rubygems_version: 3.1.6
|
160
221
|
signing_key:
|
161
222
|
specification_version: 4
|
162
|
-
summary: Specing framework
|
223
|
+
summary: Specing framework.
|
163
224
|
test_files: []
|
data/lib/fix/context.rb
DELETED
@@ -1,147 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'aw'
|
4
|
-
require 'defi'
|
5
|
-
|
6
|
-
module Fix
|
7
|
-
# Wraps the target of challenge.
|
8
|
-
class Context
|
9
|
-
RESERVED_KEYWORDS = %i[
|
10
|
-
alias
|
11
|
-
and
|
12
|
-
begin
|
13
|
-
break
|
14
|
-
case
|
15
|
-
catch
|
16
|
-
class
|
17
|
-
def
|
18
|
-
defined?
|
19
|
-
do
|
20
|
-
else
|
21
|
-
elsif
|
22
|
-
end
|
23
|
-
ensure
|
24
|
-
fail
|
25
|
-
false
|
26
|
-
for
|
27
|
-
if
|
28
|
-
in
|
29
|
-
module
|
30
|
-
next
|
31
|
-
nil
|
32
|
-
not
|
33
|
-
or
|
34
|
-
raise
|
35
|
-
redo
|
36
|
-
rescue
|
37
|
-
retry
|
38
|
-
return
|
39
|
-
self
|
40
|
-
super
|
41
|
-
then
|
42
|
-
throw
|
43
|
-
true
|
44
|
-
undef
|
45
|
-
unless
|
46
|
-
until
|
47
|
-
when
|
48
|
-
while
|
49
|
-
yield
|
50
|
-
].freeze
|
51
|
-
|
52
|
-
attr_reader :callable
|
53
|
-
|
54
|
-
def initialize(subject, challenge, before_hooks_counter = 0, *hooks, **lets)
|
55
|
-
@subject = subject
|
56
|
-
@callable = challenge.to(subject)
|
57
|
-
@before_hooks = hooks[0, before_hooks_counter]
|
58
|
-
@after_hooks = hooks[before_hooks_counter..-1]
|
59
|
-
@lets = lets
|
60
|
-
end
|
61
|
-
|
62
|
-
def before(&block)
|
63
|
-
@before_hooks << block
|
64
|
-
end
|
65
|
-
|
66
|
-
def after(&block)
|
67
|
-
@after_hooks << block
|
68
|
-
end
|
69
|
-
|
70
|
-
def let(name, &block)
|
71
|
-
raise ::TypeError, "expected a Symbol, got #{name.class}" unless name.is_a?(::Symbol)
|
72
|
-
raise ::NameError, "wrong method name `#{name}'" unless name.match(/\A[a-z][a-z0-9_]+[?!]?\z/)
|
73
|
-
raise ::NameError, "reserved keyword name `#{name}'" if RESERVED_KEYWORDS.include?(name)
|
74
|
-
raise ::NameError, "reserved method name `#{name}'" if respond_to?(name, true) && !@lets.key?(name)
|
75
|
-
|
76
|
-
@lets.update(name => block.call)
|
77
|
-
rescue ::SystemExit => e
|
78
|
-
raise SuspiciousSuccessError, "attempt `#{name}' to bypass the tests" if e.success?
|
79
|
-
raise e
|
80
|
-
end
|
81
|
-
|
82
|
-
def let!(name, &block)
|
83
|
-
raise ::TypeError, "expected a Symbol, got #{name.class}" unless name.is_a?(::Symbol)
|
84
|
-
raise ::NameError, "wrong method name `#{name}'" unless name.match(/\A[a-z][a-z0-9_]+[?!]?\z/)
|
85
|
-
raise ::NameError, "reserved keyword name `#{name}'" if RESERVED_KEYWORDS.include?(name)
|
86
|
-
raise ::NameError, "reserved method name `#{name}'" if respond_to?(name, true) && !@lets.key?(name)
|
87
|
-
|
88
|
-
@lets.update(name => ::Aw.fork! { block.call })
|
89
|
-
rescue ::SystemExit => e
|
90
|
-
raise SuspiciousSuccessError, "attempt `#{name}' to bypass the tests" if e.success?
|
91
|
-
raise e
|
92
|
-
end
|
93
|
-
|
94
|
-
# Verify the expectation.
|
95
|
-
#
|
96
|
-
# @param block [Proc] A spec to compare against the computed actual value.
|
97
|
-
#
|
98
|
-
# @return [::Spectus::Result::Pass, ::Spectus::Result::Fail] Pass or fail.
|
99
|
-
def it(_message = nil, &block)
|
100
|
-
print "#{block.source_location.join(':')}: "
|
101
|
-
i = It.new(callable, **@lets)
|
102
|
-
@before_hooks.each { |hook| i.instance_eval(&hook) }
|
103
|
-
result = i.instance_eval(&block)
|
104
|
-
puts result.colored_string
|
105
|
-
rescue ::Spectus::Result::Fail => result
|
106
|
-
abort result.colored_string
|
107
|
-
ensure
|
108
|
-
@after_hooks.each { |hook| i.instance_eval(&hook) }
|
109
|
-
raise ExpectationResultNotFoundError, result.class.inspect unless result.is_a?(::Spectus::Result::Common)
|
110
|
-
end
|
111
|
-
|
112
|
-
def on(name, *args, **options, &block)
|
113
|
-
if callable.raised?
|
114
|
-
actual = callable
|
115
|
-
challenge = ::Defi.send(:call)
|
116
|
-
else
|
117
|
-
actual = callable.object
|
118
|
-
challenge = ::Defi.send(name, *args, **options)
|
119
|
-
end
|
120
|
-
|
121
|
-
o = Context.new(actual, challenge, @before_hooks.length, *@before_hooks + @after_hooks, **@lets)
|
122
|
-
o.instance_eval(&block)
|
123
|
-
end
|
124
|
-
|
125
|
-
def with(_message = nil, **new_lets, &block)
|
126
|
-
actual = callable.object
|
127
|
-
challenge = ::Defi.send(:itself)
|
128
|
-
|
129
|
-
c = Context.new(actual, challenge, @before_hooks.length, *@before_hooks + @after_hooks, **@lets.merge(new_lets))
|
130
|
-
c.instance_eval(&block)
|
131
|
-
end
|
132
|
-
|
133
|
-
private
|
134
|
-
|
135
|
-
def method_missing(name, *args, &block)
|
136
|
-
@lets.fetch(name) { super }
|
137
|
-
end
|
138
|
-
|
139
|
-
def respond_to_missing?(name, include_private = false)
|
140
|
-
@lets.key?(name) || super
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
require_relative 'it'
|
146
|
-
require_relative 'expectation_result_not_found_error'
|
147
|
-
require_relative 'suspicious_success_error'
|
data/lib/fix/it.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spectus/expectation_target'
|
4
|
-
require 'matchi/helper'
|
5
|
-
|
6
|
-
module Fix
|
7
|
-
# Wraps the target of an expectation.
|
8
|
-
class It < ::Spectus::ExpectationTarget
|
9
|
-
include ::Matchi::Helper
|
10
|
-
|
11
|
-
# Create a new expection target
|
12
|
-
#
|
13
|
-
# @param callable [#call] The object to test.
|
14
|
-
def initialize(callable, **lets)
|
15
|
-
raise unless callable.respond_to?(:call)
|
16
|
-
|
17
|
-
@callable = callable
|
18
|
-
@lets = lets
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def method_missing(name, *args, &block)
|
24
|
-
@lets.fetch(name) { super }
|
25
|
-
end
|
26
|
-
|
27
|
-
def respond_to_missing?(name, include_private = false)
|
28
|
-
@lets.key?(name) || super
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|