sus 0.8.1 → 0.9.0

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: b15c28cff36f956e3847c3c843f5426efbe05d9ef8f52fbc9df200f7efa1bf12
4
- data.tar.gz: b6fe36ec97a8537b20b73d83d40aab8ebd7e3b60b9554493bf9e1ee2dd01bdf0
3
+ metadata.gz: 34208c06066d1f09d5a32350e26efc277bbd6d322df448bd55cec6070ea15c09
4
+ data.tar.gz: 9969914ac2241c539244e362e876096c5d0c7dbe95e830db021d6dc46965c5c3
5
5
  SHA512:
6
- metadata.gz: d3a1e05b190b0e590c8652356d13bdfe7bc84194b6da6c62d26a7d249d5c600966334f91db47c155db9e906a04832daa3f1d64426744acf830700962d2c3ab49
7
- data.tar.gz: 5325cbc73f4e0dea9808e379008df9ea603a005683d1b342f2950497cc24a751c7b0a4907db1397e92162bc5698b7d8abc528d47229cd09a9e590311b8d86063
6
+ metadata.gz: 8fcf93e09b4e3f60a7ff2f01805bf3a11679f219ad382117b023fc575ae2ada0cb0126151d87922d3b5d2a5c384dc0ae42617332142802b580e09aaa68bb54db
7
+ data.tar.gz: 6c3600065acc4e25a0adf4473fbd0e87ae1901a1238802a4baedf1a45f3c556b6b8d4d46c7b8eb540c87c64d9c82350302c77c669c942fa39c3d713c052d7ac2
checksums.yaml.gz.sig CHANGED
Binary file
data/bin/sus CHANGED
@@ -4,13 +4,20 @@ require_relative '../lib/sus/config'
4
4
  config = Sus::Config.load
5
5
 
6
6
  require_relative '../lib/sus'
7
- filter = Sus::Filter.new
8
- assertions = Sus::Assertions.default(output: Sus::Output::Null.new)
7
+ registry = config.registry
9
8
 
10
- config.prepare(filter)
9
+ if config.verbose?
10
+ output = config.output
11
+ verbose = true
12
+ else
13
+ output = Sus::Output::Null.new
14
+ verbose = false
15
+ end
16
+
17
+ assertions = Sus::Assertions.default(output: output, verbose: verbose)
11
18
 
12
19
  config.before_tests(assertions)
13
- filter.call(assertions)
20
+ registry.call(assertions)
14
21
  config.after_tests(assertions)
15
22
 
16
23
  if assertions.failed.any?
data/bin/sus-parallel CHANGED
@@ -16,10 +16,9 @@ require 'etc'
16
16
  count = Etc.nprocessors
17
17
 
18
18
  loader = Thread.new do
19
- filter = Sus::Filter.new
20
- config.prepare(filter)
19
+ registry = config.registry
21
20
 
22
- filter.each do |child|
21
+ registry.each do |child|
23
22
  guard.synchronize{progress.expand}
24
23
  jobs << child
25
24
  end
@@ -2,13 +2,17 @@
2
2
  require_relative 'output'
3
3
  require_relative 'clock'
4
4
 
5
+ require_relative 'output/backtrace'
6
+
5
7
  module Sus
6
8
  class Assertions
7
9
  def self.default(**options)
8
- self.new(**options, verbose: true)
10
+ self.new(**options)
9
11
  end
10
12
 
11
- def initialize(target: nil, output: Output.buffered, inverted: false, isolated: false, measure: false, verbose: false)
13
+ def initialize(identity: nil, target: nil, output: Output.buffered, inverted: false, isolated: false, measure: false, verbose: false)
14
+ # In theory, the target could carry the identity of the assertion group, but it's not really necessary, so we just handle it explicitly and pass it into any nested assertions.
15
+ @identity = identity
12
16
  @target = target
13
17
  @output = output
14
18
  @inverted = inverted
@@ -50,7 +54,7 @@ module Sus
50
54
  attr :count
51
55
 
52
56
  def inspect
53
- "\#<#{self.class} #{self.object_id} #{@passed.size} passed #{@failed.size} failed #{@deferred.size} deferred>"
57
+ "\#<#{self.class} #{@passed.size} passed #{@failed.size} failed #{@deferred.size} deferred>"
54
58
  end
55
59
 
56
60
  def total
@@ -58,8 +62,6 @@ module Sus
58
62
  end
59
63
 
60
64
  def print(output, verbose: @verbose)
61
- self
62
-
63
65
  if verbose && @target
64
66
  @target.print(output)
65
67
  output.write(": ")
@@ -107,21 +109,19 @@ module Sus
107
109
  end
108
110
 
109
111
  def assert(condition, message = nil)
110
- # sleep 0.5
111
-
112
112
  @count += 1
113
113
 
114
114
  if condition
115
115
  @passed << self
116
116
 
117
- @output.indented do
118
- @output.puts(:indent, :passed, pass_prefix, message || "assertion")
117
+ if @inverted || @verbose
118
+ @output.puts(:indent, :passed, pass_prefix, message || "assertion", Output::Backtrace.first(@identity))
119
119
  end
120
120
  else
121
121
  @failed << self
122
122
 
123
- @output.indented do
124
- @output.puts(:indent, :failed, fail_prefix, message || "assertion")
123
+ if !@inverted || @verbose
124
+ @output.puts(:indent, :failed, fail_prefix, message || "assertion", Output::Backtrace.first(@identity))
125
125
  end
126
126
  end
127
127
  end
@@ -138,37 +138,51 @@ module Sus
138
138
 
139
139
  # This resolves all deferred assertions in order.
140
140
  def resolve!
141
- while block = @deferred.shift
142
- block.call(self)
141
+ @output.indented do
142
+ while block = @deferred.shift
143
+ block.call(self)
144
+ end
143
145
  end
144
146
  end
145
147
 
146
148
  def fail(error)
147
149
  @failed << self
148
150
 
149
- @output.puts(:indent, :failed, fail_prefix, "Unhandled exception ", :value, error.class, ": ", error.message)
150
- error.backtrace.each do |line|
151
- @output.puts(:indent, line)
151
+ lines = error.message.split(/\r?\n/)
152
+
153
+ @output.puts(:indent, :error, fail_prefix, "Unhandled exception ", :value, error.class, ":", :reset, " ", lines.shift)
154
+
155
+ lines.each do |line|
156
+ @output.puts(:indent, "| ", line)
152
157
  end
158
+
159
+ @output.write(Output::Backtrace.for(error, @identity))
153
160
  end
154
161
 
155
- def nested(target, isolated: false, inverted: false, **options)
162
+ def nested(target, identity: @identity, isolated: false, inverted: false, **options)
156
163
  result = nil
157
- output = Output::Buffered.new
158
164
 
159
- output.write(:indent)
160
- target.print(output)
161
- output.puts
165
+ # Isolated assertions need to have buffered output so they can be replayed if they fail:
166
+ if isolated
167
+ output = @output.buffered
168
+ else
169
+ output = @output
170
+ end
171
+
172
+ output.puts(:indent, target)
162
173
 
163
- assertions = self.class.new(target: target, output: output, isolated: isolated, inverted: inverted, **options)
174
+ assertions = self.class.new(identity: identity, target: target, output: output, isolated: isolated, inverted: inverted, verbose: @verbose, **options)
164
175
 
165
- begin
166
- @clock&.start!
167
- result = yield(assertions)
168
- rescue StandardError => error
169
- assertions.fail(error)
170
- ensure
171
- @clock&.stop!
176
+ @clock&.start!
177
+
178
+ output.indented do
179
+ begin
180
+ result = yield(assertions)
181
+ rescue StandardError => error
182
+ assertions.fail(error)
183
+ ensure
184
+ @clock&.stop!
185
+ end
172
186
  end
173
187
 
174
188
  self.add(assertions)
@@ -178,13 +192,15 @@ module Sus
178
192
 
179
193
  def add(assertions)
180
194
  # If the assertions should be an isolated group, make sure any deferred assertions are resolved:
181
- if assertions.isolated
195
+ if assertions.isolated and assertions.deferred?
182
196
  assertions.resolve!
183
197
  end
184
198
 
185
199
  if assertions.deferred?
186
200
  self.defer do
201
+ output.puts(:indent, assertions.target)
187
202
  assertions.resolve!
203
+
188
204
  self.add!(assertions)
189
205
  end
190
206
  else
@@ -204,10 +220,6 @@ module Sus
204
220
  # Otherwise, we append all child assertions into the parent assertions:
205
221
  append!(assertions)
206
222
  end
207
-
208
- @output.indented do
209
- @output.append(assertions.output)
210
- end
211
223
  end
212
224
 
213
225
  def merge!(assertions)
@@ -216,17 +228,17 @@ module Sus
216
228
  if assertions.passed?
217
229
  @passed << assertions
218
230
 
219
- if @verbose
220
- @output.write(:indent, :passed, pass_prefix, :reset)
221
- self.print(@output, verbose: false)
222
- @output.puts
223
- end
231
+ # if @verbose
232
+ # @output.write(:indent, :passed, pass_prefix, :reset)
233
+ # self.print(@output, verbose: false)
234
+ # @output.puts
235
+ # end
224
236
  else
225
237
  @failed << assertions
226
238
 
227
- @output.write(:indent, :failed, fail_prefix, :reset)
228
- self.print(@output, verbose: false)
229
- @output.puts
239
+ # @output.write(:indent, :failed, fail_prefix, :reset)
240
+ # self.print(@output, verbose: false)
241
+ # @output.puts
230
242
  end
231
243
  end
232
244
 
@@ -236,10 +248,11 @@ module Sus
236
248
  @failed.concat(assertions.failed)
237
249
  @deferred.concat(assertions.deferred)
238
250
 
239
- if @verbose
240
- self.print(@output, verbose: false)
241
- @output.puts
242
- end
251
+ # if @verbose
252
+ # @output.write(:indent)
253
+ # self.print(@output, verbose: false)
254
+ # @output.puts
255
+ # end
243
256
  end
244
257
 
245
258
  def pass_prefix
data/lib/sus/base.rb CHANGED
@@ -1,10 +1,16 @@
1
1
 
2
2
  require_relative 'context'
3
+ require_relative 'loader'
3
4
 
4
5
  module Sus
6
+ # The base test case class. We need to be careful about what local state is stored.
5
7
  class Base
6
8
  def initialize(assertions)
7
- @assertions = assertions
9
+ @__assertions__ = assertions
10
+ end
11
+
12
+ def inspect
13
+ "\#<Sus::Base for #{self.class.description.inspect}>"
8
14
  end
9
15
 
10
16
  def before
@@ -22,19 +28,20 @@ module Sus
22
28
  end
23
29
 
24
30
  def assert(...)
25
- @assertions.assert(...)
31
+ @__assertions__.assert(...)
26
32
  end
27
33
 
28
34
  def refute(...)
29
- @assertions.refute(...)
35
+ @__assertions__.refute(...)
30
36
  end
31
37
  end
32
38
 
33
- def self.base(description = "base")
39
+ def self.base(description = nil)
34
40
  base = Class.new(Base)
41
+
35
42
  base.extend(Context)
36
43
  base.description = description
37
-
44
+
38
45
  return base
39
46
  end
40
47
  end
data/lib/sus/be.rb CHANGED
@@ -6,7 +6,9 @@ module Sus
6
6
  end
7
7
 
8
8
  def print(output)
9
- output.write("be ", :be, *@arguments.join(" "))
9
+ operation, *arguments = *@arguments
10
+
11
+ output.write("be ", :be, operation.to_s, :reset, " ", :variable, arguments.map(&:inspect).join, :reset)
10
12
  end
11
13
 
12
14
  def call(assertions, subject)
@@ -56,5 +58,9 @@ module Sus
56
58
  Be
57
59
  end
58
60
  end
61
+
62
+ def be_a(klass)
63
+ Be.new(:is_a?, klass)
64
+ end
59
65
  end
60
66
  end
data/lib/sus/be_within.rb CHANGED
@@ -7,7 +7,7 @@ module Sus
7
7
  end
8
8
 
9
9
  def print(output)
10
- output.write("be within ", :variable, @range)
10
+ output.write("be within ", :variable, @range, :reset)
11
11
  end
12
12
 
13
13
  def call(assertions, subject)
@@ -30,7 +30,7 @@ module Sus
30
30
  end
31
31
 
32
32
  def print(output)
33
- output.write("be within ", :variable, @tolerance)
33
+ output.write("be within ", :variable, @tolerance, :reset)
34
34
  end
35
35
 
36
36
  def call(assertions, subject)
data/lib/sus/config.rb CHANGED
@@ -21,6 +21,8 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  require_relative 'clock'
24
+ require_relative 'registry'
25
+ require_relative 'loader'
24
26
 
25
27
  module Sus
26
28
  class Config
@@ -34,7 +36,7 @@ module Sus
34
36
  end
35
37
  end
36
38
 
37
- def self.load(root: Dir.pwd, argv: ARGV)
39
+ def self.load(root: Dir.pwd, arguments: ARGV)
38
40
  derived = Class.new(self)
39
41
 
40
42
  if path = self.path(root)
@@ -43,15 +45,32 @@ module Sus
43
45
  derived.prepend(config)
44
46
  end
45
47
 
46
- return derived.new(root, argv)
48
+ options = {
49
+ verbose: !!arguments.delete('--verbose')
50
+ }
51
+
52
+ return derived.new(root, arguments, **options)
47
53
  end
48
54
 
49
- def initialize(root, paths)
55
+ def initialize(root, paths, verbose: false)
50
56
  @root = root
51
57
  @paths = paths
58
+ @verbose = verbose
59
+
52
60
  @clock = Clock.new
53
61
  end
54
62
 
63
+ attr :root
64
+ attr :paths
65
+
66
+ def verbose?
67
+ @verbose
68
+ end
69
+
70
+ def partial?
71
+ @paths.any?
72
+ end
73
+
55
74
  def output
56
75
  @output ||= Sus::Output.default
57
76
  end
@@ -62,8 +81,26 @@ module Sus
62
81
  return Dir.glob(DEFAULT_TEST_PATTERN, base: @root)
63
82
  end
64
83
 
65
- def prepare(registry)
84
+ def registry
85
+ @registry ||= self.load_registry
86
+ end
87
+
88
+ def base
89
+ Sus.base
90
+ end
91
+
92
+ def setup_base(base)
93
+ base.extend(Loader)
94
+ base.define_singleton_method(:require_root) {self.root}
95
+ end
96
+
97
+ def load_registry
98
+ registry = Sus::Registry.new
99
+
100
+ self.setup_base(registry.base)
101
+
66
102
  if @paths&.any?
103
+ registry = Sus::Filter.new(registry)
67
104
  @paths.each do |path|
68
105
  registry.load(path)
69
106
  end
@@ -72,6 +109,8 @@ module Sus
72
109
  registry.load(path)
73
110
  end
74
111
  end
112
+
113
+ return registry
75
114
  end
76
115
 
77
116
  def before_tests(assertions)
@@ -81,6 +120,12 @@ module Sus
81
120
  def after_tests(assertions)
82
121
  @clock.stop!
83
122
 
123
+ self.print_summary(assertions)
124
+ end
125
+
126
+ protected
127
+
128
+ def print_summary(assertions)
84
129
  output = self.output
85
130
 
86
131
  assertions.print(output)
@@ -96,12 +141,6 @@ module Sus
96
141
  print_failed_assertions(assertions)
97
142
  end
98
143
 
99
- protected
100
-
101
- def partial?
102
- @paths.any?
103
- end
104
-
105
144
  def print_finished_statistics(assertions)
106
145
  duration = @clock.duration
107
146
  rate = assertions.count / duration
data/lib/sus/expect.rb CHANGED
@@ -42,9 +42,9 @@ module Sus
42
42
  class Base
43
43
  def expect(subject = nil, **options, &block)
44
44
  if block_given?
45
- Expect.new(@assertions, block, **options)
45
+ Expect.new(@__assertions__, block, **options)
46
46
  else
47
- Expect.new(@assertions, subject, **options)
47
+ Expect.new(@__assertions__, subject, **options)
48
48
  end
49
49
  end
50
50
  end
data/lib/sus/file.rb CHANGED
@@ -7,9 +7,7 @@ Sus::TOPLEVEL_CLASS_EVAL = ->(klass, path){klass.class_eval(::File.read(path), p
7
7
  module Sus
8
8
  module File
9
9
  extend Context
10
-
11
- attr_accessor :path
12
-
10
+
13
11
  def self.extended(base)
14
12
  base.children = Hash.new
15
13
  end
@@ -24,6 +22,10 @@ module Sus
24
22
 
25
23
  return base
26
24
  end
25
+
26
+ def print(output)
27
+ output.write("file ", :path, self.identity)
28
+ end
27
29
  end
28
30
 
29
31
  module Context
data/lib/sus/filter.rb CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  module Sus
3
2
  class Filter
4
3
  class Index
@@ -30,7 +29,7 @@ module Sus
30
29
  end
31
30
  end
32
31
 
33
- def initialize(registry: Registry.new)
32
+ def initialize(registry = Registry.new)
34
33
  @registry = registry
35
34
  @index = nil
36
35
  @keys = Array.new
data/lib/sus/it.rb CHANGED
@@ -22,11 +22,11 @@ module Sus
22
22
 
23
23
  def print(output)
24
24
  self.superclass.print(output)
25
- output.write(" it ", :it, self.description, :reset, " ", self.identity.to_s)
25
+ output.write(" it ", :it, self.description, :reset, " ", :identity, self.identity.to_s, :reset)
26
26
  end
27
27
 
28
28
  def call(assertions)
29
- assertions.nested(self, isolated: true, measure: true) do |assertions|
29
+ assertions.nested(self, identity: self.identity, isolated: true, measure: true) do |assertions|
30
30
  instance = self.new(assertions)
31
31
 
32
32
  instance.around do
data/lib/sus/let.rb CHANGED
@@ -4,13 +4,13 @@ require_relative 'context'
4
4
  module Sus
5
5
  module Context
6
6
  def let(name, &block)
7
- ivar = :"@#{name}"
7
+ instance_variable = :"@#{name}"
8
8
 
9
9
  self.define_method(name) do
10
- if value = self.instance_variable_get(ivar)
11
- return value
10
+ if self.instance_variable_defined?(instance_variable)
11
+ return self.instance_variable_get(instance_variable)
12
12
  else
13
- self.instance_variable_set(ivar, self.instance_exec(&block))
13
+ self.instance_variable_set(instance_variable, self.instance_exec(&block))
14
14
  end
15
15
  end
16
16
  end
data/lib/sus/loader.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Sus
2
+ module Loader
3
+ def require_library(path)
4
+ require(::File.join(self.require_root, "lib", path))
5
+ end
6
+
7
+ def require_fixture(path)
8
+ require(::File.join(self.require_root, "fixtures", path))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module Sus
24
+ module Output
25
+ # Print out a backtrace relevant to the given test identity if provided.
26
+ class Backtrace
27
+ def self.first(identity = nil)
28
+ # This implementation could be a little more efficient.
29
+ self.new(caller_locations(1), identity&.path, 1)
30
+ end
31
+
32
+ def self.for(exception, identity = nil)
33
+ self.new(exception.backtrace_locations, identity&.path)
34
+ end
35
+
36
+ def initialize(stack, root = nil, limit = nil)
37
+ @stack = stack
38
+ @root = root
39
+ @limit = limit
40
+ end
41
+
42
+ def filter(root = @root)
43
+ if @root
44
+ stack = @stack.select do |frame|
45
+ frame.path.start_with?(@root)
46
+ end
47
+ else
48
+ stack = @stack
49
+ end
50
+
51
+ if @limit
52
+ stack = stack.take(@limit)
53
+ end
54
+
55
+ return stack
56
+ end
57
+
58
+ def print(output)
59
+ if @limit == 1
60
+ filter.each do |frame|
61
+ output.write " ", :path, frame.path, :line, ":", frame.lineno
62
+ end
63
+ else
64
+ output.indented do
65
+ filter.each do |frame|
66
+ output.puts :indent, :path, frame.path, :line, ":", frame.lineno, :reset, " ", frame.label
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -27,32 +27,55 @@ module Sus
27
27
  # Styled output output.
28
28
  module Output
29
29
  class Buffered
30
- def initialize
31
- @output = Array.new
30
+ def initialize(tee = nil)
31
+ @chunks = Array.new
32
+ @tee = tee
33
+ end
34
+
35
+ attr :chunks
36
+ attr :tee
37
+
38
+ def inspect
39
+ if @tee
40
+ "\#<#{self.class.name} #{@chunks.size} chunks -> #{@tee.class}>"
41
+ else
42
+ "\#<#{self.class.name} #{@chunks.size} chunks>"
43
+ end
44
+ end
45
+
46
+ def buffered
47
+ self.class.new(self)
32
48
  end
33
49
 
34
50
  attr :output
35
51
 
36
52
  def each(&block)
37
- @output.each(&block)
53
+ @chunks.each(&block)
38
54
  end
39
55
 
40
56
  def append(buffer)
41
- @output.concat(buffer.output)
57
+ @chunks.concat(buffer.output)
58
+ @tee&.append(buffer)
42
59
  end
43
60
 
44
61
  def string
45
62
  io = StringIO.new
46
- Text.new(io).append(@output)
63
+ Text.new(io).append(@chunks)
47
64
  return io.string
48
65
  end
49
66
 
67
+ INDENT = [:indent].freeze
68
+
50
69
  def indent
51
- @output << [:indent]
70
+ @chunks << INDENT
71
+ @tee&.indent
52
72
  end
53
73
 
74
+ OUTDENT = [:outdent].freeze
75
+
54
76
  def outdent
55
- @output << [:outdent]
77
+ @chunks << OUTDENT
78
+ @tee&.outdent
56
79
  end
57
80
 
58
81
  def indented
@@ -63,11 +86,13 @@ module Sus
63
86
  end
64
87
 
65
88
  def write(*arguments)
66
- @output << [:write, *arguments]
89
+ @chunks << [:write, *arguments]
90
+ @tee&.write(*arguments)
67
91
  end
68
92
 
69
93
  def puts(*arguments)
70
- @output << [:puts, *arguments]
94
+ @chunks << [:puts, *arguments]
95
+ @tee&.puts(*arguments)
71
96
  end
72
97
  end
73
98
  end
@@ -30,6 +30,12 @@ module Sus
30
30
  def initialize
31
31
  end
32
32
 
33
+ def buffered
34
+ Buffered.new(nil)
35
+ end
36
+
37
+ attr :options
38
+
33
39
  def append(buffer)
34
40
  end
35
41
 
@@ -29,12 +29,17 @@ module Sus
29
29
  class Text
30
30
  def initialize(io)
31
31
  @io = io
32
+
32
33
  @styles = {reset: self.reset}
33
34
 
34
35
  @indent = String.new
35
36
  @styles[:indent] = @indent
36
37
  end
37
38
 
39
+ def buffered
40
+ Buffered.new(self)
41
+ end
42
+
38
43
  def append(buffer)
39
44
  buffer.each do |operation|
40
45
  self.public_send(*operation)
data/lib/sus/output.rb CHANGED
@@ -23,14 +23,19 @@ module Sus
23
23
 
24
24
  output[:context] = output.style(nil, nil, :bold)
25
25
 
26
- output[:describe] = output.style(:cyan, nil, :bold)
26
+ output[:describe] = output.style(:cyan)
27
27
  output[:it] = output.style(:cyan)
28
28
  output[:with] = output.style(:cyan)
29
29
 
30
30
  output[:variable] = output.style(:blue, nil, :bold)
31
31
 
32
- output[:passed] = output.style(:green, nil, :bold)
33
- output[:failed] = output.style(:red, nil, :bold)
32
+ output[:path] = output.style(:yellow)
33
+ output[:line] = output.style(:yellow)
34
+ output[:identity] = output.style(:yellow)
35
+
36
+ output[:passed] = output.style(:green)
37
+ output[:failed] = output.style(:red)
38
+ output[:error] = output.style(:red)
34
39
 
35
40
  return output
36
41
  end
data/lib/sus/receive.rb CHANGED
@@ -9,7 +9,7 @@ module Sus
9
9
  @base = base
10
10
  @method = method
11
11
 
12
- @times = Once.new
12
+ @times = Times.new
13
13
  @arguments = nil
14
14
  @options = nil
15
15
  @block = nil
@@ -35,6 +35,18 @@ module Sus
35
35
  return self
36
36
  end
37
37
 
38
+ def once
39
+ @times = Times.new(Be.new(:==, 1))
40
+ end
41
+
42
+ def twice
43
+ @times = Times.new(Be.new(:==, 2))
44
+ end
45
+
46
+ def with_call_count(predicate)
47
+ @times = Times.new(predicate)
48
+ end
49
+
38
50
  def and_return(*returning)
39
51
  if returning.size == 1
40
52
  @returning = returning.first
@@ -72,8 +84,10 @@ module Sus
72
84
  end
73
85
  end
74
86
 
75
- assertions.defer do
76
- @times.call(assertions, called)
87
+ if @times
88
+ assertions.defer do
89
+ @times.call(assertions, called)
90
+ end
77
91
  end
78
92
  end
79
93
  end
@@ -126,14 +140,20 @@ module Sus
126
140
  end
127
141
  end
128
142
 
129
- class Once
143
+ class Times
144
+ ONCE = Be.new(:==, 1)
145
+
146
+ def initialize(condition = ONCE)
147
+ @condition = condition
148
+ end
149
+
130
150
  def print(output)
131
- output.write("once")
151
+ output.write("with call count ", @condition)
132
152
  end
133
153
 
134
154
  def call(assertions, subject)
135
155
  assertions.nested(self) do |assertions|
136
- Expect.new(assertions, subject).to(Be == 1)
156
+ Expect.new(assertions, subject).to(@condition)
137
157
  end
138
158
  end
139
159
  end
data/lib/sus/registry.rb CHANGED
@@ -16,12 +16,16 @@ require_relative 'let'
16
16
  module Sus
17
17
  class Registry
18
18
  # Create a top level scope with self as the instance:
19
- def initialize(base = Sus.base("test registry"))
19
+ def initialize(base = Sus.base(self))
20
20
  @base = base
21
21
  end
22
22
 
23
23
  attr :base
24
24
 
25
+ def print(output)
26
+ output.write("Test Registry")
27
+ end
28
+
25
29
  def load(path)
26
30
  @base.file(path)
27
31
  end
@@ -64,7 +64,7 @@ module Sus
64
64
  end
65
65
 
66
66
  def print(output)
67
- output.write("respond to ", :variable, @method.to_s)
67
+ output.write("respond to ", :variable, @method.to_s, :reset)
68
68
  end
69
69
 
70
70
  def call(assertions, subject)
data/lib/sus/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sus
4
- VERSION = "0.8.1"
4
+ VERSION = "0.9.0"
5
5
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -37,7 +37,7 @@ cert_chain:
37
37
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
38
38
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
39
39
  -----END CERTIFICATE-----
40
- date: 2022-08-18 00:00:00.000000000 Z
40
+ date: 2022-08-21 00:00:00.000000000 Z
41
41
  dependencies: []
42
42
  description:
43
43
  email:
@@ -67,8 +67,10 @@ files:
67
67
  - lib/sus/it.rb
68
68
  - lib/sus/it_behaves_like.rb
69
69
  - lib/sus/let.rb
70
+ - lib/sus/loader.rb
70
71
  - lib/sus/mock.rb
71
72
  - lib/sus/output.rb
73
+ - lib/sus/output/backtrace.rb
72
74
  - lib/sus/output/bar.rb
73
75
  - lib/sus/output/buffered.rb
74
76
  - lib/sus/output/lines.rb
metadata.gz.sig CHANGED
Binary file