r_spec-clone 1.0.0.rc1

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.
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ # Clone namespace.
5
+ #
6
+ # @api private
7
+ module Clone
8
+ end
9
+ end
10
+
11
+ require_relative File.join("..", "r_spec")
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Clone
5
+ # Send log messages to the console.
6
+ #
7
+ # @api private
8
+ module Console
9
+ # @param report [::Expresenter::Pass] Passed expectation result presenter.
10
+ #
11
+ # @see https://github.com/fixrb/expresenter
12
+ #
13
+ # @return [nil] Add a colored message to `$stdout`.
14
+ def self.passed_spec(report)
15
+ puts report.colored_string
16
+ end
17
+
18
+ # @param report [::Expresenter::Fail] Failed expectation result presenter.
19
+ #
20
+ # @see https://github.com/fixrb/expresenter
21
+ #
22
+ # @raise [SystemExit] Terminate execution immediately with colored message.
23
+ def self.failed_spec(report)
24
+ abort report.colored_string
25
+ end
26
+
27
+ # The Ruby source filename and line number containing this method or nil if
28
+ # this method was not defined in Ruby (i.e. native).
29
+ #
30
+ # @param filename [String, nil] The Ruby source filename.
31
+ # @param line [Integer, nil] The Ruby source line number.
32
+ #
33
+ # @return [String] The Ruby source filename and line number associated with
34
+ # the evaluated spec.
35
+ def self.source(filename, line)
36
+ puts [filename, line].compact.join(":")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,350 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aw"
4
+
5
+ require_relative "console"
6
+ require_relative "error"
7
+ require_relative "expectation_helper"
8
+
9
+ module RSpec
10
+ module Clone
11
+ # Abstract class for handling the domain-specific language.
12
+ class Dsl
13
+ BEFORE_METHOD = :initialize
14
+ AFTER_METHOD = :terminate
15
+
16
+ private_constant :BEFORE_METHOD, :AFTER_METHOD
17
+
18
+ # Executes the given block before each spec in the current context runs.
19
+ #
20
+ # @example
21
+ # require "r_spec/clone"
22
+ #
23
+ # RSpec.describe Integer do
24
+ # before do
25
+ # @value = 123
26
+ # end
27
+ #
28
+ # it { expect(@value).to be 123 }
29
+ #
30
+ # describe "nested" do
31
+ # before do
32
+ # @value -= 81
33
+ # end
34
+ #
35
+ # it { expect(@value).to be 42 }
36
+ # end
37
+ #
38
+ # it { expect(@value).to be 123 }
39
+ # end
40
+ #
41
+ # # Output to the console
42
+ # # Success: expected to be 123.
43
+ # # Success: expected to be 42.
44
+ # # Success: expected to be 123.
45
+ #
46
+ # @param block [Proc] The content to execute at the class initialization.
47
+ def self.before(&block)
48
+ define_method(BEFORE_METHOD) do
49
+ super()
50
+ instance_eval(&block)
51
+ end
52
+
53
+ private BEFORE_METHOD
54
+ end
55
+
56
+ # Executes the given block after each spec in the current context runs.
57
+ #
58
+ # @example
59
+ # require "r_spec/clone"
60
+ #
61
+ # RSpec.describe Integer do
62
+ # after do
63
+ # puts "That is the answer to everything."
64
+ # end
65
+ #
66
+ # it { expect(42).to be 42 }
67
+ # end
68
+ #
69
+ # # Output to the console
70
+ # # Success: expected to be 42.
71
+ # # That is the answer to everything.
72
+ #
73
+ # @param block [Proc] The content to execute at the class initialization.
74
+ def self.after(&block)
75
+ define_method(AFTER_METHOD) do
76
+ instance_exec(&block)
77
+ super()
78
+ end
79
+
80
+ private AFTER_METHOD
81
+ end
82
+
83
+ # Sets a user-defined property.
84
+ #
85
+ # @example
86
+ # require "r_spec/clone"
87
+ #
88
+ # RSpec.describe "Name stories" do
89
+ # let(:name) { "Bob" }
90
+ #
91
+ # it { expect(name).to eq "Bob" }
92
+ #
93
+ # context "with last name" do
94
+ # let(:name) { "#{super()} Smith" }
95
+ #
96
+ # it { expect(name).to eq "Bob Smith" }
97
+ # end
98
+ # end
99
+ #
100
+ # # Output to the console
101
+ # # Success: expected to eq "Bob".
102
+ # # Success: expected to eq "Bob Smith".
103
+ #
104
+ # @param name [String, Symbol] The name of the property.
105
+ # @param block [Proc] The content of the method to define.
106
+ #
107
+ # @return [Symbol] A private method that define the block content.
108
+ def self.let(name, *args, **kwargs, &block)
109
+ raise Error::ReservedMethod if [BEFORE_METHOD, AFTER_METHOD].include?(name.to_sym)
110
+
111
+ private define_method(name, *args, **kwargs, &block)
112
+ end
113
+
114
+ # Sets a user-defined property named {#subject}.
115
+ #
116
+ # @example
117
+ # require "r_spec/clone"
118
+ #
119
+ # RSpec.describe Array do
120
+ # subject { [1, 2, 3] }
121
+ #
122
+ # it "has the prescribed elements" do
123
+ # expect(subject).to eq([1, 2, 3])
124
+ # end
125
+ # end
126
+ #
127
+ # # Output to the console
128
+ # # Success: expected to eq [1, 2, 3].
129
+ #
130
+ # @param block [Proc] The subject to set.
131
+ # @return [Symbol] A {#subject} method that define the block content.
132
+ def self.subject(&block)
133
+ let(__method__, &block)
134
+ end
135
+
136
+ # Defines an example group that describes a unit to be tested.
137
+ #
138
+ # @example
139
+ # require "r_spec/clone"
140
+ #
141
+ # RSpec.describe String do
142
+ # describe "+" do
143
+ # it("concats") { expect("foo" + "bar").to eq "foobar" }
144
+ # end
145
+ # end
146
+ #
147
+ # # Output to the console
148
+ # # Success: expected to eq "foobar".
149
+ #
150
+ # @param const [Module, String] A module to include in block context.
151
+ # @param block [Proc] The block to define the specs.
152
+ def self.describe(const, &block)
153
+ desc = ::Class.new(self)
154
+ desc.let(:described_class) { const } if const.is_a?(::Module)
155
+ desc.instance_eval(&block)
156
+ end
157
+
158
+ # Defines an example group that establishes a specific context, like _empty
159
+ # array_ versus _array with elements_.
160
+ #
161
+ # Unlike {.describe}, the block is evaluated in isolation in order to scope
162
+ # possible side effects inside its context.
163
+ #
164
+ # @example
165
+ # require "r_spec/clone"
166
+ #
167
+ # RSpec.describe "web resource" do
168
+ # context "when resource is not found" do
169
+ # pending "responds with 404"
170
+ # end
171
+ #
172
+ # context "when resource is found" do
173
+ # pending "responds with 200"
174
+ # end
175
+ # end
176
+ #
177
+ # # Output to the console
178
+ # # Warning: responds with 404.
179
+ # # Warning: responds with 200.
180
+ #
181
+ # @param _description [String] A description that usually begins with
182
+ # "when", "with" or "without".
183
+ # @param block [Proc] The block to define the specs.
184
+ def self.context(_description, &block)
185
+ desc = ::Class.new(self)
186
+ ::Aw.fork! { desc.instance_eval(&block) }
187
+ end
188
+
189
+ # Defines a concrete test case.
190
+ #
191
+ # The test is performed by the block supplied to `&block`.
192
+ #
193
+ # @example The integer after 41
194
+ # require "r_spec/clone"
195
+ #
196
+ # RSpec.describe Integer do
197
+ # it { expect(41.next).to be 42 }
198
+ # end
199
+ #
200
+ # # Output to the console
201
+ # # Success: expected to be 42.
202
+ #
203
+ # @example A division by zero
204
+ # require "r_spec/clone"
205
+ #
206
+ # RSpec.describe Integer do
207
+ # subject { 41 }
208
+ #
209
+ # it { is_expected.to be_an_instance_of described_class }
210
+ #
211
+ # it "raises an error" do
212
+ # expect { subject / 0 }.to raise_exception ZeroDivisionError
213
+ # end
214
+ # end
215
+ #
216
+ # # Output to the console
217
+ # # Success: expected 41 to be an instance of Integer.
218
+ # # Success: divided by 0.
219
+ #
220
+ # It can be used inside a {.describe} or {.context} section.
221
+ #
222
+ # @param _name [String, nil] The name of the spec.
223
+ # @param block [Proc] An expectation to evaluate.
224
+ #
225
+ # @raise (see ExpectationTarget::Base#result)
226
+ # @return (see ExpectationTarget::Base#result)
227
+ def self.it(_name = nil, &block)
228
+ raise ::ArgumentError, "Missing example block" unless block
229
+
230
+ example = ::Class.new(self) { include ExpectationHelper::It }.new
231
+ example.instance_eval(&block)
232
+ rescue ::SystemExit
233
+ Console.source(*block.source_location)
234
+
235
+ exit false
236
+ ensure
237
+ example&.send(AFTER_METHOD)
238
+ end
239
+
240
+ # Use the {.its} method to define a single spec that specifies the actual
241
+ # value of an attribute of the subject using
242
+ # {ExpectationHelper::Its#is_expected}.
243
+ #
244
+ # @example The integer after 41
245
+ # require "r_spec/clone"
246
+ #
247
+ # RSpec.describe Integer do
248
+ # subject { 41 }
249
+ #
250
+ # its(:next) { is_expected.to be 42 }
251
+ # end
252
+ #
253
+ # # Output to the console
254
+ # # Success: expected to be 42.
255
+ #
256
+ # @example A division by zero
257
+ # require "r_spec/clone"
258
+ #
259
+ # RSpec.describe Integer do
260
+ # subject { 41 }
261
+ #
262
+ # its(:/, 0) { is_expected.to raise_exception ZeroDivisionError }
263
+ # end
264
+ #
265
+ # # Output to the console
266
+ # # Success: divided by 0.
267
+ #
268
+ # @example A spec without subject
269
+ # require "r_spec/clone"
270
+ #
271
+ # RSpec.describe Integer do
272
+ # its(:boom) { is_expected.to raise_exception RSpec::Error::UndefinedSubject }
273
+ # end
274
+ #
275
+ # # Output to the console
276
+ # # Success: subject not explicitly defined.
277
+ #
278
+ # @param attribute [String, Symbol] The property to call to subject.
279
+ # @param args [Array] An optional list of arguments.
280
+ # @param kwargs [Hash] An optional list of keyword arguments.
281
+ # @param block [Proc] An expectation to evaluate.
282
+ #
283
+ # @raise (see ExpectationTarget::Base#result)
284
+ # @return (see ExpectationTarget::Base#result)
285
+ def self.its(attribute, *args, **kwargs, &block)
286
+ raise ::ArgumentError, "Missing example block" unless block
287
+
288
+ example = ::Class.new(self) do
289
+ include ExpectationHelper::Its
290
+
291
+ define_method(:actual) do
292
+ subject.public_send(attribute, *args, **kwargs)
293
+ end
294
+ end.new
295
+
296
+ example.instance_eval(&block)
297
+ rescue ::SystemExit
298
+ Console.source(*block.source_location)
299
+
300
+ exit false
301
+ ensure
302
+ example&.send(AFTER_METHOD)
303
+ end
304
+
305
+ # Defines a pending test case.
306
+ #
307
+ # `&block` is never evaluated. It can be used to describe behaviour that is
308
+ # not yet implemented.
309
+ #
310
+ # @example
311
+ # require "r_spec/clone"
312
+ #
313
+ # RSpec.describe "an example" do
314
+ # pending "is implemented but waiting" do
315
+ # expect something to be finished
316
+ # end
317
+ #
318
+ # pending "is not yet implemented and waiting"
319
+ # end
320
+ #
321
+ # # Output to the console
322
+ # # Warning: is implemented but waiting.
323
+ # # Warning: is not yet implemented and waiting.
324
+ #
325
+ # @param message [String] The reason why the example is pending.
326
+ #
327
+ # @return [nil] Write a message to STDOUT.
328
+ #
329
+ # @api public
330
+ def self.pending(message)
331
+ Console.passed_spec Error::PendingExpectation.result(message)
332
+ end
333
+
334
+ private
335
+
336
+ def described_class
337
+ raise Error::UndefinedDescribedClass,
338
+ "the first argument to at least one example group must be a module"
339
+ end
340
+
341
+ def subject
342
+ raise Error::UndefinedSubject, "subject not explicitly defined"
343
+ end
344
+
345
+ define_method(AFTER_METHOD) do
346
+ # do nothing by default
347
+ end
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative File.join("error", "pending_expectation")
4
+ require_relative File.join("error", "reserved_method")
5
+ require_relative File.join("error", "undefined_described_class")
6
+ require_relative File.join("error", "undefined_subject")
7
+
8
+ module RSpec
9
+ module Clone
10
+ # Namespace for exceptions.
11
+ #
12
+ # @api private
13
+ module Error
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "expresenter"
4
+
5
+ module RSpec
6
+ module Clone
7
+ module Error
8
+ # Exception for pending expectations.
9
+ #
10
+ # @api private
11
+ class PendingExpectation < ::RuntimeError
12
+ # @param message [String] The not implemented expectation description.
13
+ #
14
+ # @return [nil] Write a pending expectation to STDOUT.
15
+ def self.result(message)
16
+ ::Expresenter.call(true).with(
17
+ actual: new(message),
18
+ error: nil,
19
+ expected: self,
20
+ got: false,
21
+ matcher: :raise_exception,
22
+ negate: true,
23
+ level: :SHOULD
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end