sus 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/context/getting-started.md +352 -0
  4. data/context/index.yaml +9 -0
  5. data/context/mocking.md +100 -30
  6. data/context/{shared.md → shared-contexts.md} +29 -2
  7. data/lib/sus/assertions.rb +91 -18
  8. data/lib/sus/base.rb +13 -1
  9. data/lib/sus/be.rb +84 -0
  10. data/lib/sus/be_truthy.rb +16 -0
  11. data/lib/sus/be_within.rb +25 -0
  12. data/lib/sus/clock.rb +21 -0
  13. data/lib/sus/config.rb +58 -1
  14. data/lib/sus/context.rb +28 -5
  15. data/lib/sus/describe.rb +14 -0
  16. data/lib/sus/expect.rb +23 -0
  17. data/lib/sus/file.rb +38 -0
  18. data/lib/sus/filter.rb +21 -0
  19. data/lib/sus/fixtures/temporary_directory_context.rb +27 -0
  20. data/lib/sus/fixtures.rb +1 -0
  21. data/lib/sus/have/all.rb +8 -0
  22. data/lib/sus/have/any.rb +8 -0
  23. data/lib/sus/have.rb +42 -0
  24. data/lib/sus/have_duration.rb +16 -0
  25. data/lib/sus/identity.rb +44 -1
  26. data/lib/sus/integrations.rb +1 -0
  27. data/lib/sus/it.rb +33 -0
  28. data/lib/sus/it_behaves_like.rb +16 -0
  29. data/lib/sus/let.rb +3 -0
  30. data/lib/sus/mock.rb +39 -1
  31. data/lib/sus/output/backtrace.rb +31 -1
  32. data/lib/sus/output/bar.rb +17 -0
  33. data/lib/sus/output/buffered.rb +32 -1
  34. data/lib/sus/output/lines.rb +10 -0
  35. data/lib/sus/output/messages.rb +26 -3
  36. data/lib/sus/output/null.rb +16 -2
  37. data/lib/sus/output/progress.rb +29 -1
  38. data/lib/sus/output/status.rb +13 -0
  39. data/lib/sus/output/structured.rb +14 -1
  40. data/lib/sus/output/text.rb +33 -1
  41. data/lib/sus/output/xterm.rb +11 -1
  42. data/lib/sus/output.rb +9 -0
  43. data/lib/sus/raise_exception.rb +16 -0
  44. data/lib/sus/receive.rb +82 -0
  45. data/lib/sus/registry.rb +20 -1
  46. data/lib/sus/respond_to.rb +29 -2
  47. data/lib/sus/shared.rb +16 -0
  48. data/lib/sus/tree.rb +10 -0
  49. data/lib/sus/version.rb +2 -1
  50. data/lib/sus/with.rb +18 -0
  51. data/readme.md +8 -0
  52. data/releases.md +4 -0
  53. data.tar.gz.sig +0 -0
  54. metadata +3 -3
  55. metadata.gz.sig +0 -0
  56. data/context/usage.md +0 -380
@@ -5,27 +5,38 @@
5
5
 
6
6
  module Sus
7
7
  module Output
8
+ # Represents a status indicator for test execution.
8
9
  class Status
10
+ # Register status styling with an output handler.
11
+ # @parameter output [Output] The output handler to register with.
9
12
  def self.register(output)
10
13
  output[:free] ||= output.style(:blue)
11
14
  output[:busy] ||= output.style(:orange)
12
15
  end
13
16
 
17
+ # Initialize a new Status indicator.
18
+ # @parameter state [Symbol] The state (:free or :busy).
19
+ # @parameter context [Object, nil] Optional context to display.
14
20
  def initialize(state = :free, context = nil)
15
21
  @state = state
16
22
  @context = context
17
23
  end
18
24
 
25
+ # Status indicators for different states.
19
26
  INDICATORS = {
20
27
  busy: ["◑", "◒", "◐", "◓"],
21
28
  free: ["◌"]
22
29
  }
23
30
 
31
+ # Update the status.
32
+ # @parameter state [Symbol] The new state.
33
+ # @parameter context [Object, nil] Optional new context.
24
34
  def update(state, context = nil)
25
35
  @state = state
26
36
  @context = context
27
37
  end
28
38
 
39
+ # @returns [String] The current indicator character (animated for busy state).
29
40
  def indicator
30
41
  if indicators = INDICATORS[@state]
31
42
  return indicators[(Time.now.to_f * 10) % indicators.size]
@@ -34,6 +45,8 @@ module Sus
34
45
  return " "
35
46
  end
36
47
 
48
+ # Print the status to the output.
49
+ # @parameter output [Output] The output handler.
37
50
  def print(output)
38
51
  output.write(
39
52
  @state, self.indicator, " "
@@ -6,22 +6,35 @@
6
6
  require_relative "null"
7
7
 
8
8
  module Sus
9
- # Styled output output.
10
9
  module Output
10
+ # Represents a structured JSON output handler for machine-readable output.
11
11
  class Structured < Null
12
+ # Create a buffered structured output handler.
13
+ # @parameter io [IO] The IO object to write to.
14
+ # @parameter identity [Identity, nil] Optional identity.
15
+ # @returns [Buffered] A new Buffered instance wrapping a Structured handler.
12
16
  def self.buffered(...)
13
17
  Buffered.new(self.new(...))
14
18
  end
15
19
 
20
+ # Initialize a new Structured output handler.
21
+ # @parameter io [IO] The IO object to write to.
22
+ # @parameter identity [Identity, nil] Optional identity.
16
23
  def initialize(io, identity = nil)
17
24
  @io = io
18
25
  @identity = identity
19
26
  end
20
27
 
28
+ # Output a skip message as JSON.
29
+ # @parameter reason [String] The reason for skipping.
30
+ # @parameter identity [Identity, nil] The identity where the skip occurred.
21
31
  def skip(reason, identity)
22
32
  inform(reason.to_s, identity)
23
33
  end
24
34
 
35
+ # Output an informational message as JSON.
36
+ # @parameter message [String, Object] The message to output.
37
+ # @parameter identity [Identity, nil] The identity where the message was generated.
25
38
  def inform(message, identity)
26
39
  unless message.is_a?(String)
27
40
  message = message.inspect
@@ -8,9 +8,12 @@ require_relative "buffered"
8
8
 
9
9
  module Sus
10
10
  module Output
11
+ # Represents a plain text output handler without color support.
11
12
  class Text
12
13
  include Messages
13
14
 
15
+ # Initialize a new Text output handler.
16
+ # @parameter io [IO] The IO object to write to.
14
17
  def initialize(io)
15
18
  @io = io
16
19
 
@@ -20,30 +23,41 @@ module Sus
20
23
  @styles[:indent] = @indent
21
24
  end
22
25
 
26
+ # @attribute [Hash] The style definitions.
23
27
  attr :styles
24
28
 
29
+ # Create a buffered output handler.
30
+ # @returns [Buffered] A new Buffered instance.
25
31
  def buffered
26
32
  Buffered.new(self)
27
33
  end
28
34
 
35
+ # Append and replay chunks from a buffer.
36
+ # @parameter buffer [Buffered] The buffer to append from.
29
37
  def append(buffer)
30
38
  buffer.each do |operation|
31
39
  self.public_send(*operation)
32
40
  end
33
41
  end
34
42
 
43
+ # @attribute [IO] The IO object to write to.
35
44
  attr :io
36
45
 
46
+ # The indentation string.
37
47
  INDENTATION = "\t"
38
48
 
49
+ # Increase indentation level.
39
50
  def indent
40
51
  @indent << INDENTATION
41
52
  end
42
53
 
54
+ # Decrease indentation level.
43
55
  def outdent
44
56
  @indent.slice!(INDENTATION)
45
57
  end
46
58
 
59
+ # Execute a block with increased indentation.
60
+ # @yields {...} The block to execute.
47
61
  def indented
48
62
  self.indent
49
63
  yield
@@ -51,33 +65,49 @@ module Sus
51
65
  self.outdent
52
66
  end
53
67
 
68
+ # @returns [Boolean] Whether the IO is interactive (a TTY).
54
69
  def interactive?
55
70
  @io.tty?
56
71
  end
57
72
 
73
+ # Get a style by key.
74
+ # @parameter key [Symbol] The style key.
75
+ # @returns [String] The style value.
58
76
  def [] key
59
77
  @styles[key]
60
78
  end
61
79
 
80
+ # Set a style by key.
81
+ # @parameter key [Symbol] The style key.
82
+ # @parameter value [String] The style value.
62
83
  def []= key, value
63
84
  @styles[key] = value
64
85
  end
65
86
 
87
+ # @returns [Array(Integer)] The terminal size [height, width] (defaults to [24, 80]).
66
88
  def size
67
89
  [24, 80]
68
90
  end
69
91
 
92
+ # @returns [Integer] The terminal width (defaults to 80).
70
93
  def width
71
94
  size.last
72
95
  end
73
96
 
97
+ # @returns [Boolean] Always returns false, as Text output doesn't support colors.
74
98
  def colors?
75
99
  false
76
100
  end
77
101
 
102
+ # Create a style string (no-op for Text output).
103
+ # @parameter foreground [Symbol, nil] The foreground color.
104
+ # @parameter background [Symbol, nil] The background color.
105
+ # @parameter attributes [Array] Additional style attributes.
106
+ # @returns [String] An empty string.
78
107
  def style(foreground, background = nil, *attributes)
79
108
  end
80
109
 
110
+ # @returns [String] An empty string (no reset needed for plain text).
81
111
  def reset
82
112
  end
83
113
 
@@ -85,6 +115,7 @@ module Sus
85
115
  # When the argument is a symbol, look up the style and inject it into the io stream.
86
116
  # When the argument is a proc/lambda, call it with self as the argument.
87
117
  # When the argument is anything else, write it directly to the io.
118
+ # @parameter arguments [Array] The arguments to write.
88
119
  def write(*arguments)
89
120
  arguments.each do |argument|
90
121
  case argument
@@ -102,7 +133,8 @@ module Sus
102
133
  end
103
134
  end
104
135
 
105
- # Print out the arguments as per {#print}, followed by the reset sequence and a newline.
136
+ # Print out the arguments as per {#write}, followed by the reset sequence and a newline.
137
+ # @parameter arguments [Array] The arguments to write.
106
138
  def puts(*arguments)
107
139
  write(*arguments)
108
140
  @io.puts(self.reset)
@@ -8,9 +8,10 @@ require "io/console"
8
8
  require_relative "text"
9
9
 
10
10
  module Sus
11
- # Styled output output.
12
11
  module Output
12
+ # Represents an XTerm-compatible output handler with color and style support.
13
13
  class XTerm < Text
14
+ # Color codes for ANSI terminal colors.
14
15
  COLORS = {
15
16
  black: 0,
16
17
  red: 1,
@@ -23,6 +24,7 @@ module Sus
23
24
  default: 9,
24
25
  }
25
26
 
27
+ # Style attribute codes for ANSI terminal attributes.
26
28
  ATTRIBUTES = {
27
29
  normal: 0,
28
30
  bold: 1,
@@ -35,14 +37,21 @@ module Sus
35
37
  hidden: 8,
36
38
  }
37
39
 
40
+ # @returns [Boolean] Always returns true, as XTerm output supports colors.
38
41
  def colors?
39
42
  true
40
43
  end
41
44
 
45
+ # @returns [Array(Integer)] The terminal size [height, width].
42
46
  def size
43
47
  @io.winsize
44
48
  end
45
49
 
50
+ # Create an ANSI escape sequence for styling.
51
+ # @parameter foreground [Symbol, nil] The foreground color name.
52
+ # @parameter background [Symbol, nil] The background color name.
53
+ # @parameter attributes [Array] Additional style attributes.
54
+ # @returns [String] An ANSI escape sequence.
46
55
  def style(foreground, background = nil, *attributes)
47
56
  tokens = []
48
57
 
@@ -61,6 +70,7 @@ module Sus
61
70
  return "\e[#{tokens.join(';')}m"
62
71
  end
63
72
 
73
+ # @returns [String] The ANSI reset sequence.
64
74
  def reset
65
75
  "\e[0m"
66
76
  end
data/lib/sus/output.rb CHANGED
@@ -11,7 +11,11 @@ require_relative "output/null"
11
11
  require_relative "output/progress"
12
12
 
13
13
  module Sus
14
+ # Represents output handlers for test results and messages.
14
15
  module Output
16
+ # Create an appropriate output handler for the given IO.
17
+ # @parameter io [IO] The IO object to write to.
18
+ # @returns [XTerm, Text] An XTerm handler if the IO is a TTY, otherwise a Text handler.
15
19
  def self.for(io)
16
20
  if io.isatty
17
21
  XTerm.new(io)
@@ -20,6 +24,9 @@ module Sus
20
24
  end
21
25
  end
22
26
 
27
+ # Create a default output handler with styling configured.
28
+ # @parameter io [IO] The IO object to write to (defaults to $stderr).
29
+ # @returns [XTerm, Text] A configured output handler.
23
30
  def self.default(io = $stderr)
24
31
  output = self.for(io)
25
32
 
@@ -47,6 +54,8 @@ module Sus
47
54
  return output
48
55
  end
49
56
 
57
+ # Create a buffered output handler.
58
+ # @returns [Buffered] A new buffered output handler.
50
59
  def self.buffered
51
60
  Buffered.new
52
61
  end
@@ -4,18 +4,28 @@
4
4
  # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
6
  module Sus
7
+ # Represents a predicate that checks if a block raises an exception.
7
8
  class RaiseException
9
+ # Initialize a new RaiseException predicate.
10
+ # @parameter exception_class [Class] The exception class to expect.
11
+ # @parameter message [String, Regexp, Object | Nil] Optional message matcher.
8
12
  def initialize(exception_class = Exception, message: nil)
9
13
  @exception_class = exception_class
10
14
  @message = message
11
15
  @predicate = nil
12
16
  end
13
17
 
18
+ # Add an additional predicate to check on the exception.
19
+ # @parameter predicate [Object] The predicate to apply to the exception.
20
+ # @returns [RaiseException] Returns self for method chaining.
14
21
  def and(predicate)
15
22
  @predicate = predicate
16
23
  return self
17
24
  end
18
25
 
26
+ # Evaluate this predicate against a subject (block).
27
+ # @parameter assertions [Assertions] The assertions instance to use.
28
+ # @parameter subject [Proc] The block to evaluate.
19
29
  def call(assertions, subject)
20
30
  assertions.nested(self) do |assertions|
21
31
  begin
@@ -36,6 +46,8 @@ module Sus
36
46
  end
37
47
  end
38
48
 
49
+ # Print a representation of this predicate.
50
+ # @parameter output [Output] The output target.
39
51
  def print(output)
40
52
  output.write("raise exception")
41
53
 
@@ -54,6 +66,10 @@ module Sus
54
66
  end
55
67
 
56
68
  class Base
69
+ # Create a predicate that checks if a block raises an exception.
70
+ # @parameter exception_class [Class] The exception class to expect.
71
+ # @parameter message [String, Regexp, Object | Nil] Optional message matcher.
72
+ # @returns [RaiseException] A new RaiseException predicate.
57
73
  def raise_exception(...)
58
74
  RaiseException.new(...)
59
75
  end
data/lib/sus/receive.rb CHANGED
@@ -6,7 +6,12 @@
6
6
  require_relative "respond_to"
7
7
 
8
8
  module Sus
9
+ # Represents an expectation that a method will be called on an object.
9
10
  class Receive
11
+ # Initialize a new Receive expectation.
12
+ # @parameter base [Object] The base object (usually self from Base).
13
+ # @parameter method [Symbol] The method name to expect.
14
+ # @yields {...} Optional block that returns the value to return from the method.
10
15
  def initialize(base, method, &block)
11
16
  @base = base
12
17
  @method = method
@@ -19,46 +24,73 @@ module Sus
19
24
  @returning = block
20
25
  end
21
26
 
27
+ # Print a representation of this expectation.
28
+ # @parameter output [Output] The output target.
22
29
  def print(output)
23
30
  output.write("receive ", :variable, @method.to_s, :reset)
24
31
  end
25
32
 
33
+ # Specify that the method should be called with specific arguments.
34
+ # @parameter predicate [Object] The predicate to match against the arguments.
35
+ # @returns [Receive] Returns self for method chaining.
26
36
  def with_arguments(predicate)
27
37
  @arguments = WithArguments.new(predicate)
28
38
  return self
29
39
  end
30
40
 
41
+ # Specify that the method should be called with specific keyword options.
42
+ # @parameter predicate [Object] The predicate to match against the options.
43
+ # @returns [Receive] Returns self for method chaining.
31
44
  def with_options(predicate)
32
45
  @options = WithOptions.new(predicate)
33
46
  return self
34
47
  end
35
48
 
49
+ # Specify that the method should be called with a block.
50
+ # @parameter predicate [Object] Optional predicate to match against the block.
51
+ # @returns [Receive] Returns self for method chaining.
36
52
  def with_block(predicate = Be.new(:!=, nil))
37
53
  @block = WithBlock.new(predicate)
38
54
  return self
39
55
  end
40
56
 
57
+ # Specify that the method should be called with specific arguments and options.
58
+ # @parameter arguments [Array] The positional arguments to match.
59
+ # @parameter options [Hash] The keyword arguments to match.
60
+ # @returns [Receive] Returns self for method chaining.
41
61
  def with(*arguments, **options)
42
62
  with_arguments(Be.new(:==, arguments)) if arguments.any?
43
63
  with_options(Be.new(:==, options)) if options.any?
44
64
  return self
45
65
  end
46
66
 
67
+ # Specify that the method should be called exactly once.
68
+ # @returns [Receive] Returns self for method chaining.
47
69
  def once
48
70
  @times = Times.new(Be.new(:==, 1))
49
71
  return self
50
72
  end
51
73
 
74
+ # Specify that the method should be called exactly twice.
75
+ # @returns [Receive] Returns self for method chaining.
52
76
  def twice
53
77
  @times = Times.new(Be.new(:==, 2))
54
78
  return self
55
79
  end
56
80
 
81
+ # Specify a predicate to match against the call count.
82
+ # @parameter predicate [Object] The predicate to match against the call count.
83
+ # @returns [Receive] Returns self for method chaining.
57
84
  def with_call_count(predicate)
58
85
  @times = Times.new(predicate)
59
86
  return self
60
87
  end
61
88
 
89
+ # Specify the value to return when the method is called.
90
+ # @parameter returning [Array] Values to return. If one value, returns it directly; if multiple, returns an array.
91
+ # @yields {...} Optional block that computes the return value.
92
+ # @returns [Receive] Returns self for method chaining.
93
+ # @raises [ArgumentError] If both values and a block are provided.
62
94
  def and_return(*returning, &block)
63
95
  if block_given?
64
96
  if returning.any?
@@ -75,6 +107,9 @@ module Sus
75
107
  return self
76
108
  end
77
109
 
110
+ # Specify that the method should raise an exception when called.
111
+ # @parameter exception [Class, String] The exception class or message to raise.
112
+ # @returns [Receive] Returns self for method chaining.
78
113
  def and_raise(...)
79
114
  @returning = proc do
80
115
  raise(...)
@@ -83,6 +118,12 @@ module Sus
83
118
  return self
84
119
  end
85
120
 
121
+ # Validate the method call arguments, options, and block.
122
+ # @parameter mock [Mock] The mock instance.
123
+ # @parameter assertions [Assertions] The assertions instance.
124
+ # @parameter arguments [Array] The positional arguments.
125
+ # @parameter options [Hash] The keyword arguments.
126
+ # @parameter block [Proc, nil] The block argument.
86
127
  def validate(mock, assertions, arguments, options, block)
87
128
  return unless @arguments or @options or @block
88
129
 
@@ -93,6 +134,9 @@ module Sus
93
134
  end
94
135
  end
95
136
 
137
+ # Evaluate this expectation against a subject.
138
+ # @parameter assertions [Assertions] The assertions instance to use.
139
+ # @parameter subject [Object] The object to expect the method call on.
96
140
  def call(assertions, subject)
97
141
  assertions.nested(self) do |assertions|
98
142
  mock = @base.mock(subject)
@@ -123,19 +167,28 @@ module Sus
123
167
  end
124
168
  end
125
169
 
170
+ # @returns [Boolean] Whether the original method should be called.
126
171
  def call_original?
127
172
  @returning.nil?
128
173
  end
129
174
 
175
+ # Represents a constraint on method call arguments.
130
176
  class WithArguments
177
+ # Initialize a new WithArguments constraint.
178
+ # @parameter predicate [Object] The predicate to match against arguments.
131
179
  def initialize(predicate)
132
180
  @predicate = predicate
133
181
  end
134
182
 
183
+ # Print a representation of this constraint.
184
+ # @parameter output [Output] The output target.
135
185
  def print(output)
136
186
  output.write("with arguments ", @predicate)
137
187
  end
138
188
 
189
+ # Evaluate this constraint against arguments.
190
+ # @parameter assertions [Assertions] The assertions instance to use.
191
+ # @parameter subject [Array] The arguments to check.
139
192
  def call(assertions, subject)
140
193
  assertions.nested(self) do |assertions|
141
194
  Expect.new(assertions, subject).to(@predicate)
@@ -143,15 +196,23 @@ module Sus
143
196
  end
144
197
  end
145
198
 
199
+ # Represents a constraint on method call keyword options.
146
200
  class WithOptions
201
+ # Initialize a new WithOptions constraint.
202
+ # @parameter predicate [Object] The predicate to match against options.
147
203
  def initialize(predicate)
148
204
  @predicate = predicate
149
205
  end
150
206
 
207
+ # Print a representation of this constraint.
208
+ # @parameter output [Output] The output target.
151
209
  def print(output)
152
210
  output.write("with options ", @predicate)
153
211
  end
154
212
 
213
+ # Evaluate this constraint against options.
214
+ # @parameter assertions [Assertions] The assertions instance to use.
215
+ # @parameter subject [Hash] The options to check.
155
216
  def call(assertions, subject)
156
217
  assertions.nested(self) do |assertions|
157
218
  Expect.new(assertions, subject).to(@predicate)
@@ -159,15 +220,23 @@ module Sus
159
220
  end
160
221
  end
161
222
 
223
+ # Represents a constraint on method call block argument.
162
224
  class WithBlock
225
+ # Initialize a new WithBlock constraint.
226
+ # @parameter predicate [Object] The predicate to match against the block.
163
227
  def initialize(predicate)
164
228
  @predicate = predicate
165
229
  end
166
230
 
231
+ # Print a representation of this constraint.
232
+ # @parameter output [Output] The output target.
167
233
  def print(output)
168
234
  output.write("with block", @predicate)
169
235
  end
170
236
 
237
+ # Evaluate this constraint against a block.
238
+ # @parameter assertions [Assertions] The assertions instance to use.
239
+ # @parameter subject [Proc, nil] The block to check.
171
240
  def call(assertions, subject)
172
241
  assertions.nested(self) do |assertions|
173
242
 
@@ -176,17 +245,26 @@ module Sus
176
245
  end
177
246
  end
178
247
 
248
+ # Represents a constraint on method call count.
179
249
  class Times
250
+ # A predicate that matches at least one call.
180
251
  AT_LEAST_ONCE = Be.new(:>=, 1)
181
252
 
253
+ # Initialize a new Times constraint.
254
+ # @parameter condition [Object] The predicate to match against the call count.
182
255
  def initialize(condition = AT_LEAST_ONCE)
183
256
  @condition = condition
184
257
  end
185
258
 
259
+ # Print a representation of this constraint.
260
+ # @parameter output [Output] The output target.
186
261
  def print(output)
187
262
  output.write("with call count ", @condition)
188
263
  end
189
264
 
265
+ # Evaluate this constraint against a call count.
266
+ # @parameter assertions [Assertions] The assertions instance to use.
267
+ # @parameter subject [Integer] The call count to check.
190
268
  def call(assertions, subject)
191
269
  assertions.nested(self) do |assertions|
192
270
  Expect.new(assertions, subject).to(@condition)
@@ -196,6 +274,10 @@ module Sus
196
274
  end
197
275
 
198
276
  class Base
277
+ # Create an expectation that a method will be called.
278
+ # @parameter method [Symbol] The method name to expect.
279
+ # @yields {...} Optional block that returns the value to return from the method.
280
+ # @returns [Receive] A new Receive expectation.
199
281
  def receive(method, &block)
200
282
  Receive.new(self, method, &block)
201
283
  end
data/lib/sus/registry.rb CHANGED
@@ -19,25 +19,34 @@ require_relative "include_context"
19
19
  require_relative "let"
20
20
 
21
21
  module Sus
22
+ # Represents a registry of test files and contexts.
22
23
  class Registry
24
+ # The glob pattern used to find Ruby files in directories.
23
25
  DIRECTORY_GLOB = "**/*.rb"
24
26
 
25
- # Create a top level scope with self as the instance:
27
+ # Initialize a new registry.
28
+ # @parameter options [Hash] Options to pass to the base context.
26
29
  def initialize(**options)
27
30
  @base = Sus.base(self, **options)
28
31
  @loaded = {}
29
32
  end
30
33
 
34
+ # @attribute [Class] The base test context class.
31
35
  attr :base
32
36
 
37
+ # Print a representation of this registry.
38
+ # @parameter output [Output] The output target.
33
39
  def print(output)
34
40
  output.write("Test Registry")
35
41
  end
36
42
 
43
+ # @returns [String] A string representation of this registry.
37
44
  def to_s
38
45
  @base&.identity&.to_s || self.class.name
39
46
  end
40
47
 
48
+ # Load a test file or directory.
49
+ # @parameter path [String] The path to load (file or directory).
41
50
  def load(path)
42
51
  if ::File.directory?(path)
43
52
  load_directory(path)
@@ -46,24 +55,34 @@ module Sus
46
55
  end
47
56
  end
48
57
 
58
+ # Load a single test file.
59
+ # @parameter path [String] The path to the test file.
49
60
  private def load_file(path)
50
61
  @loaded[path] ||= @base.file(path)
51
62
  end
52
63
 
64
+ # Load all Ruby files in a directory.
65
+ # @parameter path [String] The directory path.
53
66
  private def load_directory(path)
54
67
  ::Dir.glob(::File.join(path, DIRECTORY_GLOB), &self.method(:load_file))
55
68
  end
56
69
 
70
+ # Execute all tests in the registry.
71
+ # @parameter assertions [Assertions] Optional assertions instance to use.
72
+ # @returns [Assertions] The assertions instance with results.
57
73
  def call(assertions = Assertions.default)
58
74
  @base.call(assertions)
59
75
 
60
76
  return assertions
61
77
  end
62
78
 
79
+ # Iterate over all test cases in the registry.
80
+ # @yields {|test| ...} Each test case.
63
81
  def each(...)
64
82
  @base.each(...)
65
83
  end
66
84
 
85
+ # @returns [Hash] The child contexts and tests.
67
86
  def children
68
87
  @base.children
69
88
  end