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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +352 -0
- data/context/index.yaml +9 -0
- data/context/mocking.md +100 -30
- data/context/{shared.md → shared-contexts.md} +29 -2
- data/lib/sus/assertions.rb +91 -18
- data/lib/sus/base.rb +13 -1
- data/lib/sus/be.rb +84 -0
- data/lib/sus/be_truthy.rb +16 -0
- data/lib/sus/be_within.rb +25 -0
- data/lib/sus/clock.rb +21 -0
- data/lib/sus/config.rb +58 -1
- data/lib/sus/context.rb +28 -5
- data/lib/sus/describe.rb +14 -0
- data/lib/sus/expect.rb +23 -0
- data/lib/sus/file.rb +38 -0
- data/lib/sus/filter.rb +21 -0
- data/lib/sus/fixtures/temporary_directory_context.rb +27 -0
- data/lib/sus/fixtures.rb +1 -0
- data/lib/sus/have/all.rb +8 -0
- data/lib/sus/have/any.rb +8 -0
- data/lib/sus/have.rb +42 -0
- data/lib/sus/have_duration.rb +16 -0
- data/lib/sus/identity.rb +44 -1
- data/lib/sus/integrations.rb +1 -0
- data/lib/sus/it.rb +33 -0
- data/lib/sus/it_behaves_like.rb +16 -0
- data/lib/sus/let.rb +3 -0
- data/lib/sus/mock.rb +39 -1
- data/lib/sus/output/backtrace.rb +31 -1
- data/lib/sus/output/bar.rb +17 -0
- data/lib/sus/output/buffered.rb +32 -1
- data/lib/sus/output/lines.rb +10 -0
- data/lib/sus/output/messages.rb +26 -3
- data/lib/sus/output/null.rb +16 -2
- data/lib/sus/output/progress.rb +29 -1
- data/lib/sus/output/status.rb +13 -0
- data/lib/sus/output/structured.rb +14 -1
- data/lib/sus/output/text.rb +33 -1
- data/lib/sus/output/xterm.rb +11 -1
- data/lib/sus/output.rb +9 -0
- data/lib/sus/raise_exception.rb +16 -0
- data/lib/sus/receive.rb +82 -0
- data/lib/sus/registry.rb +20 -1
- data/lib/sus/respond_to.rb +29 -2
- data/lib/sus/shared.rb +16 -0
- data/lib/sus/tree.rb +10 -0
- data/lib/sus/version.rb +2 -1
- data/lib/sus/with.rb +18 -0
- data/readme.md +8 -0
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +0 -0
- data/context/usage.md +0 -380
data/lib/sus/output/status.rb
CHANGED
|
@@ -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
|
data/lib/sus/output/text.rb
CHANGED
|
@@ -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 {#
|
|
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)
|
data/lib/sus/output/xterm.rb
CHANGED
|
@@ -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
|
data/lib/sus/raise_exception.rb
CHANGED
|
@@ -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
|
-
#
|
|
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
|