fix 1.0.0.beta5 → 1.0.0.beta6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f9b6d58547e6b78494ab484447f08c43a2fd233db5ef946c977b0f21fc0311d
4
- data.tar.gz: 0fb204fd7cb1ccf56ddfacfe6737e137c387d5734855e629edae6a5289d2e291
3
+ metadata.gz: 361429a943a5149aeea4da4dd83150360db392275ec4d9d98e76969e60768dce
4
+ data.tar.gz: 6df8aaff424652d4e73cb163cc1dfd973c9a4a03ec2e2d6e351db5f87b0674a8
5
5
  SHA512:
6
- metadata.gz: 67bd36e9816e7f92c0a2534d3d7055a73c5c249182b6b0e7842441c856e0fb6d47e0fb59dbe89b9fbe3124212b98e765cf5f312a14e276017b1dcd29d120002f
7
- data.tar.gz: e58648ee25f43b7d964a2f0782a05724c935b2661dc4c2afa5c71bff51af0baac694ef86a12c911266bb1d4e4ebc50d3f499e05531722d0362be5fc1f1f6bd5e
6
+ metadata.gz: 24a1c58a8e0db0ef62d97881eabc8a4ff0596d82c96f1dee40fc68edffdc17f0206c6d45aa11040e3b11af35038df58ee7bca1f6190330f91b1b384304dcc304
7
+ data.tar.gz: 0a5ff783abf6bce80c8ff4a3e3f2e938c004bf5cd02e8300f56650e6312638e911638ed41c9293047aa757ae73048cd412bae5b6cc031e43bab82a2f4b92e41c
data/README.md CHANGED
@@ -7,6 +7,8 @@
7
7
  [![RuboCop](https://github.com/fixrb/fix/workflows/RuboCop/badge.svg?branch=main)](https://github.com/fixrb/fix/actions?query=workflow%3Arubocop+branch%3Amain)
8
8
  [![License](https://img.shields.io/github/license/fixrb/fix?label=License&logo=github)](https://github.com/fixrb/fix/raw/main/LICENSE.md)
9
9
 
10
+ ⚠️ This project is still in the experimental phase. May be used at your own risk.
11
+
10
12
  ![Fix specing framework for Ruby](https://github.com/fixrb/fix/raw/main/img/fix.jpg)
11
13
 
12
14
  ## Installation
@@ -14,7 +16,7 @@
14
16
  Add this line to your application's Gemfile:
15
17
 
16
18
  ```ruby
17
- gem "fix", ">= 1.0.0.beta5"
19
+ gem "fix", ">= 1.0.0.beta6"
18
20
  ```
19
21
 
20
22
  And then execute:
@@ -58,16 +60,18 @@ And this fixed behavior:
58
60
  relative "fix"
59
61
 
60
62
  Fix :Duck do
63
+ it MUST be_an_instance_of :Duck
64
+
61
65
  on :swims do
62
- it { MUST eql "Swoosh..." }
66
+ it MUST eql "Swoosh..."
63
67
  end
64
68
 
65
69
  on :speaks do
66
- it { MUST raise_exception NoMethodError }
70
+ it MUST raise_exception NoMethodError
67
71
  end
68
72
 
69
73
  on :sings do
70
- it { MAY eql "♪... ♫..." }
74
+ it MAY eql "♪... ♫..."
71
75
  end
72
76
  end
73
77
  ```
@@ -80,7 +84,7 @@ When I run this test:
80
84
  require_relative "app"
81
85
  require_relative "fix"
82
86
 
83
- Fix[:Duck].call(Duck.new)
87
+ Fix[:Duck].test { Duck.new }
84
88
  ```
85
89
 
86
90
  ```sh
@@ -90,15 +94,14 @@ ruby examples/duck/test.rb
90
94
  I should see this output:
91
95
 
92
96
  ```txt
93
- Test #<Duck:0x00007fc5289bcf68> against Duck:
94
- - NoMethodError: undefined method `sings' for #<Duck:0x00007fc5289bcf68>.
95
- - Success: expected to eql "Swoosh...".
96
- - Success: undefined method `speaks' for #<Duck:0x00007fc5289bcf68>.
97
+ NoMethodError: undefined method `sings' for #<Duck:0x00007fc5289bcf68>.
98
+ Success: expected to eql "Swoosh...".
99
+ Success: undefined method `speaks' for #<Duck:0x00007fc5289bcf68>.
97
100
  ```
98
101
 
99
102
  ## Test suite
100
103
 
101
- __Fix__'s specifications are [fixed here](https://github.com/fixrb/fix/blob/main/fix/) and can be tested against __Fix__'s codebase executing [test/](https://github.com/fixrb/fix/blob/main/test/)'s files.
104
+ __Fix__'s specifications will be [fixed here](https://github.com/fixrb/fix/blob/main/fix/) and will be tested against __Fix__'s codebase executing [test/*](https://github.com/fixrb/fix/blob/main/test/)'s files.
102
105
 
103
106
  ## Contact
104
107
 
data/lib/fix.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative File.join("fix", "doc")
3
4
  require_relative File.join("fix", "test")
4
5
 
5
6
  require_relative "kernel"
@@ -18,6 +19,6 @@ module Fix
18
19
  #
19
20
  # @return [::Fix::Dsl] The specification document.
20
21
  def self.[](name)
21
- Test.new(name)
22
+ Test.new(*Doc.const_get(name).const_get(:CONTEXTS))
22
23
  end
23
24
  end
data/lib/fix/console.rb CHANGED
@@ -3,21 +3,13 @@
3
3
  module Fix
4
4
  # Send log messages to the console.
5
5
  module Console
6
- # @param name [String, Symbol] The name of the specification document.
7
- # @param subject [#object_id] The front object to be tested.
8
- #
9
- # @return [nil] Add a message to `$stdout`.
10
- def self.title(name, subject)
11
- puts "Run #{name} specs against #{subject.inspect}:"
12
- end
13
-
14
6
  # @param report [::Expresenter::Pass] Passed expectation result presenter.
15
7
  #
16
8
  # @see https://github.com/fixrb/expresenter
17
9
  #
18
10
  # @return [nil] Add a colored message to `$stdout`.
19
11
  def self.passed_spec(report)
20
- puts "- #{report.colored_string}"
12
+ puts report.colored_string
21
13
  end
22
14
 
23
15
  # @param report [::Expresenter::Fail] Failed expectation result presenter.
@@ -26,7 +18,7 @@ module Fix
26
18
  #
27
19
  # @raise [SystemExit] Terminate execution immediately with colored message.
28
20
  def self.failed_spec(report)
29
- abort "- #{report.colored_string}"
21
+ abort report.colored_string
30
22
  end
31
23
  end
32
24
  end
data/lib/fix/dsl.rb CHANGED
@@ -4,7 +4,7 @@ require "defi"
4
4
  require "matchi/helper"
5
5
  require "spectus"
6
6
 
7
- require_relative "console"
7
+ require_relative "test"
8
8
 
9
9
  module Fix
10
10
  # Abstract class for handling the domain-specific language.
@@ -16,22 +16,10 @@ module Fix
16
16
  # @example
17
17
  # require "fix"
18
18
  #
19
- # Fix.describe "Name stories" do
19
+ # Fix do
20
20
  # let(:name) { "Bob" }
21
- #
22
- # it { expect(name).to eq "Bob" }
23
- #
24
- # context "with last name" do
25
- # let(:name) { "#{super()} Smith" }
26
- #
27
- # it { expect(name).to eq "Bob Smith" }
28
- # end
29
21
  # end
30
22
  #
31
- # # Output to the console
32
- # # Success: expected to eq "Bob".
33
- # # Success: expected to eq "Bob Smith".
34
- #
35
23
  # @param name [String, Symbol] The name of the property.
36
24
  # @param block [Proc] The content of the method to define.
37
25
  #
@@ -39,11 +27,28 @@ module Fix
39
27
  #
40
28
  # @api public
41
29
  def self.let(name, &block)
42
- private_class_method define_singleton_method(name, &block)
30
+ private define_method(name, &block)
43
31
  end
44
32
 
33
+ # Defines an example group with user-defined properties that describes a
34
+ # unit to be tested.
35
+ #
36
+ # @example
37
+ # require "fix"
38
+ #
39
+ # Fix do
40
+ # with :password, "secret" do
41
+ # it MUST equal true
42
+ # end
43
+ # end
44
+ #
45
+ # @param kwargs [Hash] The list of propreties.
46
+ # @param block [Proc] The block to define the specs.
47
+ #
48
+ # @api public
45
49
  def self.with(**kwargs, &block)
46
50
  klass = ::Class.new(self)
51
+ klass.const_get(:CONTEXTS) << klass
47
52
  kwargs.each { |name, value| klass.let(name) { value } }
48
53
  klass.instance_eval(&block)
49
54
  klass
@@ -54,115 +59,106 @@ module Fix
54
59
  # @example
55
60
  # require "fix"
56
61
  #
57
- # Fix.on String do
58
- # on "+" do
59
- # it("concats") { expect("foo" + "bar").to eq "foobar" }
62
+ # Fix do
63
+ # on :+, 2 do
64
+ # it MUST be 42
60
65
  # end
61
66
  # end
62
67
  #
63
- # # Output to the console
64
- # # Success: expected to eq "foobar".
65
- #
66
- # @param const [Module, String] A module to include in block context.
68
+ # @param method_name [String, Symbol] The method to send to the subject.
67
69
  # @param block [Proc] The block to define the specs.
68
70
  #
69
71
  # @api public
70
72
  def self.on(method_name, *args, **kwargs, &block)
71
73
  klass = ::Class.new(self)
72
- klass.const_get(:SPECS) << klass
74
+ klass.const_get(:CONTEXTS) << klass
75
+
73
76
  const_set("Child#{block.object_id}", klass)
74
77
 
75
78
  klass.define_singleton_method(:challenges) do
76
79
  super() + [::Defi.send(method_name, *args, **kwargs)]
77
80
  end
78
81
 
82
+ def klass.initialize
83
+ if subject.raised?
84
+ subject
85
+ else
86
+ challenge = ::Defi.send(method_name, *args, **kwargs)
87
+ challenge.to(subject.call)
88
+ end
89
+ end
90
+
79
91
  klass.instance_eval(&block)
80
92
  klass
81
93
  end
82
94
 
83
- # Defines a concrete test case.
95
+ # Defines a concrete spec definition.
84
96
  #
85
- # The test is performed by the block supplied to `&block`.
86
- #
87
- # @example The integer after 41
88
- # require "fix"
89
- #
90
- # Fix(:AnswerToEverything) { it { MUST be 42 } }.call(42)
91
- #
92
- # # Output to the console
93
- # # Success: expected to be 42.
94
- #
95
- # @example A division by zero
97
+ # @example
96
98
  # require "fix"
97
99
  #
98
- # Fix :Integer do
99
- # it { MUST be_an_instance_of Integer }
100
- #
101
- # on :/, 0 do
102
- # it { MUST raise_exception ZeroDivisionError }
103
- # end
104
- # end
105
- #
106
- # Fix[:Integer].call(41)
107
- #
108
- # # Output to the console
109
- # # Success: expected 41 to be an instance of Integer.
110
- # # Success: divided by 0.
111
- #
112
- # It can be used inside a {.describe} or {.context} section.
113
- #
114
- # @param block [Proc] An expectation to evaluate.
115
- #
116
- # @raise (see ExpectationTarget::Base#result)
117
- # @return (see ExpectationTarget::Base#result)
100
+ # Fix { it MUST equal 42 }
118
101
  #
119
102
  # @api public
120
- def self.it(&block)
121
- define_method("test_#{block.object_id}", &block)
103
+ def self.it(requirement)
104
+ define_method("test_#{requirement.object_id}") { requirement }
122
105
  end
123
106
 
124
- def self.its(method_name, *args, **kwargs, &block)
107
+ # @todo Move this method inside "fix-its" gem.
108
+ def self.its(method_name, requirement)
125
109
  klass = ::Class.new(self)
126
- klass.const_get(:SPECS) << klass
127
- const_set("Child#{block.object_id}", klass)
110
+ klass.const_get(:CONTEXTS) << klass
111
+
112
+ const_set("Child#{requirement.object_id}", klass)
128
113
 
129
114
  klass.define_singleton_method(:challenges) do
130
- super() + [::Defi.send(method_name, *args, **kwargs)]
115
+ super() + [::Defi.send(method_name)]
131
116
  end
132
117
 
133
- klass.it(&block)
118
+ def klass.initialize
119
+ if subject.raised?
120
+ subject
121
+ else
122
+ challenge = ::Defi.send(method_name, *args, **kwargs)
123
+ challenge.to(subject.call)
124
+ end
125
+ end
126
+
127
+ klass.it(requirement)
134
128
  end
135
129
 
136
130
  def self.challenges
137
131
  []
138
132
  end
139
133
 
134
+ def self.test(&subject)
135
+ Test.new(self).test(&subject)
136
+ end
137
+
140
138
  private_class_method :challenges
141
139
 
142
140
  private
143
141
 
144
- def initialize(subject)
145
- @subject = subject
146
- end
142
+ attr_reader :subject
147
143
 
148
- ::Spectus.methods(false).each do |method_name|
149
- define_method(method_name.upcase) do |matcher|
150
- ::Spectus.public_send(method_name, matcher).call do
151
- self.class.send(:challenges).inject(@subject) do |object, challenge|
152
- challenge.to(object).call
153
- end
154
- end
155
- end
144
+ def initialize(&subject)
145
+ @subject = ::Defi::Value.new(&subject)
156
146
  end
157
147
 
158
- def method_missing(method_name, *args, &block)
159
- self.class.send(method_name)
160
- rescue ::NoMethodError
161
- super
148
+ ::Matchi::Matcher.constants.each do |matcher_const|
149
+ next if matcher_const.equal?(:Base)
150
+
151
+ matcher_klass = ::Matchi::Matcher.const_get(matcher_const)
152
+
153
+ define_singleton_method(matcher_klass.to_sym) do |*args|
154
+ matcher_klass.new(*args)
155
+ end
162
156
  end
163
157
 
164
- def respond_to_missing?(method_name, include_private = false)
165
- self.class.respond_to?(method_name) || super
158
+ ::Spectus.methods(false).each do |method_name|
159
+ define_singleton_method(method_name.upcase) do |matcher|
160
+ ::Spectus.public_send(method_name, matcher)
161
+ end
166
162
  end
167
163
  end
168
164
  end
data/lib/fix/test.rb CHANGED
@@ -8,37 +8,44 @@ require_relative "doc"
8
8
  module Fix
9
9
  # Module for testing spec documents.
10
10
  class Test
11
- attr_reader :name
11
+ attr_reader :contexts
12
12
 
13
- def initialize(name)
14
- @name = name
13
+ def initialize(*contexts)
14
+ @contexts = contexts
15
15
  end
16
16
 
17
- def call(subject)
18
- Console.title(name, subject)
19
- requirements(subject).each { |requirement| test(requirement) }
17
+ def test(&block)
18
+ requirements(&block)
20
19
  exit(true)
21
20
  end
22
21
 
23
22
  private
24
23
 
25
- def requirements(subject)
26
- specs.flat_map do |spec|
27
- example = spec.new(subject)
28
- example.public_methods(false).map do |public_method|
29
- example.method(public_method)
24
+ def requirements(&block)
25
+ contexts.flat_map do |context|
26
+ sandbox = context.new
27
+ sandbox.public_methods(false).shuffle.map do |public_method|
28
+ definition = sandbox.public_send(public_method)
29
+
30
+ report = begin
31
+ definition.call do
32
+ front_object = instance_eval(&block)
33
+
34
+ context.send(:challenges).inject(front_object) do |object, challenge|
35
+ challenge.to(object).call
36
+ end
37
+ end
38
+ rescue ::Expresenter::Fail => e
39
+ e
40
+ end
41
+
42
+ if report.passed?
43
+ Console.passed_spec report
44
+ else
45
+ Console.failed_spec report
46
+ end
30
47
  end
31
- end.shuffle
32
- end
33
-
34
- def specs
35
- Doc.const_get(name).const_get(:SPECS)
36
- end
37
-
38
- def test(requirement)
39
- Console.passed_spec requirement.call
40
- rescue ::Expresenter::Fail => e
41
- Console.failed_spec e
48
+ end
42
49
  end
43
50
  end
44
51
  end
data/lib/kernel.rb CHANGED
@@ -19,12 +19,14 @@ module Kernel
19
19
  # @return [Class] The specification document.
20
20
  #
21
21
  # rubocop:disable Naming/MethodName
22
- def Fix(name, &block)
22
+ def Fix(name = nil, &block)
23
23
  klass = ::Class.new(::Fix::Dsl)
24
- klass.const_set(:SPECS, [klass])
24
+ klass.const_set(:CONTEXTS, [klass])
25
25
  klass.instance_eval(&block)
26
- ::Fix::Doc.const_set(name, klass)
27
- ::Fix::Test.new(name)
26
+
27
+ ::Fix::Doc.const_set(name, klass) unless name.nil?
28
+
29
+ ::Fix::Test.new(*klass.const_get(:CONTEXTS))
28
30
  end
29
31
  # rubocop:enable Naming/MethodName
30
32
  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.beta5
4
+ version: 1.0.0.beta6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-14 00:00:00.000000000 Z
11
+ date: 2021-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: defi
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.1.1
33
+ version: 2.2.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.1.1
40
+ version: 2.2.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: spectus
43
43
  requirement: !ruby/object:Gem::Requirement