sus 0.13.0 → 0.15.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: beb15dd70f3fd10ea7d5485648b632944450255950ecbbd0c02fbd10aa533933
4
- data.tar.gz: 4cc4f588633c0dee5211f39dec34224f78f44a47c1720c74c4a3fc90df9c7671
3
+ metadata.gz: 68595280910704ed5eb41683985ffaff34df40eb30e72408da93667de742fb0c
4
+ data.tar.gz: fc0ac561eae164316247c381c0a20cb244c61c0af6b68891ea120fad2f7916c4
5
5
  SHA512:
6
- metadata.gz: '0292a0378064908cfff1ca760f09f2bb64f1131835ca7fafae02e1120726af7cd0575a240516392b817133dde4c444e18b0962ac5e381c761c5b98c325696ce9'
7
- data.tar.gz: 54c3cd3c3116562271bc4c1f68172bc9fa43a3a8881bca00033d94ebd26b9a633b39897631a00a9dc90e0fef1b2c82affde754a74f644a95680080379b16d258
6
+ metadata.gz: 1fa0de727817b73859de636f2414dcd08b6001cdd5d837cc9ca72caa3014bdfe08052a0d5c785c91127ee8632e21e64d1eb43817e00baeee2461e698975ab937
7
+ data.tar.gz: 2ce965a251d21411e22f8d5feb8ae5573805c18da2e8061ef47768e60600721ebd0d72cb2d8c9a6e1d6be6989dbddb32f12a8fd5a26d65e64c9cb3b47bc7d4ac
checksums.yaml.gz.sig CHANGED
Binary file
@@ -14,12 +14,15 @@ module Sus
14
14
  self.new(**options)
15
15
  end
16
16
 
17
- def initialize(identity: nil, target: nil, output: Output.buffered, inverted: false, isolated: false, measure: false, verbose: false)
17
+ # @parameter orientation [Boolean] Whether the assertions are positive or negative in general.
18
+ # @parameter inverted [Boolean] Whether the assertions are inverted with respect to the parent.
19
+ def initialize(identity: nil, target: nil, output: Output.buffered, inverted: false, orientation: true, isolated: false, measure: false, verbose: false)
18
20
  # 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.
19
21
  @identity = identity
20
22
  @target = target
21
23
  @output = output
22
24
  @inverted = inverted
25
+ @orientation = orientation
23
26
  @isolated = isolated
24
27
  @verbose = verbose
25
28
 
@@ -39,7 +42,13 @@ module Sus
39
42
  attr :target
40
43
  attr :output
41
44
  attr :level
45
+
46
+ # Whether this aset of assertions is inverted
42
47
  attr :inverted
48
+
49
+ # The absolute orientation of this set of assertions:
50
+ attr :orientation
51
+
43
52
  attr :isolated
44
53
  attr :verbose
45
54
 
@@ -118,18 +127,22 @@ module Sus
118
127
  if condition
119
128
  @passed << self
120
129
 
121
- if @inverted || @verbose
122
- @output.puts(:indent, :passed, pass_prefix, message || "assertion", Output::Backtrace.first(@identity))
130
+ if !@orientation || @verbose
131
+ @output.puts(:indent, *pass_prefix, message || "assertion", Output::Backtrace.first(@identity))
123
132
  end
124
133
  else
125
134
  @failed << self
126
135
 
127
- if !@inverted || @verbose
128
- @output.puts(:indent, :failed, fail_prefix, message || "assertion", Output::Backtrace.first(@identity))
136
+ if @orientation || @verbose
137
+ @output.puts(:indent, *fail_prefix, message || "assertion", Output::Backtrace.first(@identity))
129
138
  end
130
139
  end
131
140
  end
132
141
 
142
+ def skip(reason)
143
+ @output.puts(:indent, :skip, skip_prefix, reason)
144
+ end
145
+
133
146
  def inform(message)
134
147
  @output.puts(:indent, :inform, inform_prefix, message)
135
148
  end
@@ -158,7 +171,7 @@ module Sus
158
171
 
159
172
  lines = error.message.split(/\r?\n/)
160
173
 
161
- @output.puts(:indent, :error, fail_prefix, "Unhandled exception ", :value, error.class, ":", :reset, " ", lines.shift)
174
+ @output.puts(:indent, *fail_prefix, "Unhandled exception ", :value, error.class, ":", :reset, " ", lines.shift)
162
175
 
163
176
  lines.each do |line|
164
177
  @output.puts(:indent, "| ", line)
@@ -177,9 +190,16 @@ module Sus
177
190
  output = @output
178
191
  end
179
192
 
193
+ # Inverting a nested assertions causes the orientation to flip:
194
+ if inverted
195
+ orientation = !@orientation
196
+ else
197
+ orientation = @orientation
198
+ end
199
+
180
200
  output.puts(:indent, target)
181
201
 
182
- assertions = self.class.new(identity: identity, target: target, output: output, isolated: isolated, inverted: inverted, verbose: @verbose, **options)
202
+ assertions = self.class.new(identity: identity, target: target, output: output, isolated: isolated, inverted: inverted, orientation: orientation, verbose: @verbose, **options)
183
203
 
184
204
  output.indented do
185
205
  begin
@@ -264,17 +284,32 @@ module Sus
264
284
  # @output.puts
265
285
  # end
266
286
  end
267
-
287
+
288
+ PASSED_PREFIX = [:passed, "✓ "].freeze
289
+ FAILED_PREFIX = [:failed, "✗ "].freeze
290
+
268
291
  def pass_prefix
269
- "✓ "
292
+ if @orientation
293
+ PASSED_PREFIX
294
+ else
295
+ FAILED_PREFIX
296
+ end
270
297
  end
271
298
 
272
299
  def fail_prefix
273
- "✗ "
300
+ if @orientation
301
+ FAILED_PREFIX
302
+ else
303
+ PASSED_PREFIX
304
+ end
274
305
  end
275
306
 
276
307
  def inform_prefix
277
308
  "ℹ "
278
309
  end
310
+
311
+ def skip_prefix
312
+ "⚠ "
313
+ end
279
314
  end
280
315
  end
data/lib/sus/be.rb CHANGED
@@ -72,5 +72,9 @@ module Sus
72
72
  def be_nil
73
73
  Be::NIL
74
74
  end
75
+
76
+ def be_equal(other)
77
+ Be.new(:equal?, other)
78
+ end
75
79
  end
76
80
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2021-2022, by Samuel Williams.
5
+
6
+ module Sus
7
+ module BeTruthy
8
+ def self.print(output)
9
+ output.write("be truthy")
10
+ end
11
+
12
+ def self.call(assertions, subject)
13
+ assertions.nested(self) do |assertions|
14
+ assertions.assert(subject, self)
15
+ end
16
+ end
17
+ end
18
+
19
+ module BeFalsey
20
+ def self.print(output)
21
+ output.write("be falsey")
22
+ end
23
+
24
+ def self.call(assertions, subject)
25
+ assertions.nested(self) do |assertions|
26
+ assertions.assert(!subject, self)
27
+ end
28
+ end
29
+ end
30
+
31
+ class Base
32
+ def be_truthy
33
+ BeTruthy
34
+ end
35
+
36
+ def be_falsey
37
+ BeFalsey
38
+ end
39
+ end
40
+ end
data/lib/sus/expect.rb CHANGED
@@ -51,9 +51,5 @@ module Sus
51
51
  Expect.new(@__assertions__, subject)
52
52
  end
53
53
  end
54
-
55
- def is_expected
56
- expect(subject)
57
- end
58
54
  end
59
55
  end
data/lib/sus/file.rb CHANGED
@@ -11,7 +11,7 @@ Sus::TOPLEVEL_CLASS_EVAL = ->(klass, path){klass.class_eval(::File.read(path), p
11
11
  module Sus
12
12
  module File
13
13
  extend Context
14
-
14
+
15
15
  def self.extended(base)
16
16
  base.children = Hash.new
17
17
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022, by Samuel Williams.
5
+
6
+ module Sus
7
+ module Have
8
+ class All
9
+ def initialize(predicates)
10
+ @predicates = predicates
11
+ end
12
+
13
+ def print(output)
14
+ first = true
15
+ output.write("have {")
16
+ @predicates.each do |predicate|
17
+ if first
18
+ first = false
19
+ else
20
+ output.write(", ")
21
+ end
22
+
23
+ output.write(predicate)
24
+ end
25
+ output.write("}")
26
+ end
27
+
28
+ def call(assertions, subject)
29
+ assertions.nested(self) do |assertions|
30
+ @predicates.each do |predicate|
31
+ predicate.call(assertions, subject)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022, by Samuel Williams.
5
+
6
+ module Sus
7
+ module Have
8
+ class Any
9
+ def initialize(predicates)
10
+ @predicates = predicates
11
+ end
12
+
13
+ def print(output)
14
+ first = true
15
+ output.write("have any {")
16
+ @predicates.each do |predicate|
17
+ if first
18
+ first = false
19
+ else
20
+ output.write(", ")
21
+ end
22
+
23
+ output.write(predicate)
24
+ end
25
+ output.write("}")
26
+ end
27
+
28
+ def call(assertions, subject)
29
+ assertions.nested(self) do |assertions|
30
+ @predicates.each do |predicate|
31
+ predicate.call(assertions, subject)
32
+ end
33
+
34
+ if assertions.passed.any?
35
+ # We don't care about any failures in this case, as long as one of the values passed:
36
+ assertions.failed.clear
37
+ else
38
+ # Nothing passed, so we failed:
39
+ assertions.assert(false, "could not find any matching value")
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
data/lib/sus/have.rb CHANGED
@@ -3,37 +3,11 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2022, by Samuel Williams.
5
5
 
6
+ require_relative 'have/all'
7
+ require_relative 'have/any'
8
+
6
9
  module Sus
7
10
  module Have
8
- class Composite
9
- def initialize(predicates)
10
- @predicates = predicates
11
- end
12
-
13
- def print(output)
14
- first = true
15
- output.write("have {")
16
- @predicates.each do |predicate|
17
- if first
18
- first = false
19
- else
20
- output.write(", ")
21
- end
22
-
23
- output.write(predicate)
24
- end
25
- output.write("}")
26
- end
27
-
28
- def call(assertions, subject)
29
- assertions.nested(self) do |assertions|
30
- @predicates.each do |predicate|
31
- predicate.call(assertions, subject)
32
- end
33
- end
34
- end
35
- end
36
-
37
11
  class Key
38
12
  def initialize(name, predicate = nil)
39
13
  @name = name
@@ -69,11 +43,29 @@ module Sus
69
43
  end
70
44
  end
71
45
  end
46
+
47
+ class Value
48
+ def initialize(predicate)
49
+ @predicate = predicate
50
+ end
51
+
52
+ def print(output)
53
+ output.write("value ", @predicate, :reset)
54
+ end
55
+
56
+ def call(assertions, subject)
57
+ subject.each_with_index do |value, index|
58
+ assertions.nested("[#{index}] = #{value.inspect}") do |assertions|
59
+ @predicate&.call(assertions, value)
60
+ end
61
+ end
62
+ end
63
+ end
72
64
  end
73
65
 
74
66
  class Base
75
67
  def have(*predicates)
76
- Have::Composite.new(predicates)
68
+ Have::All.new(predicates)
77
69
  end
78
70
 
79
71
  def have_keys(*keys)
@@ -89,7 +81,7 @@ module Sus
89
81
  end
90
82
  end
91
83
 
92
- Have::Composite.new(predicates)
84
+ Have::All.new(predicates)
93
85
  end
94
86
 
95
87
  def have_attributes(**attributes)
@@ -97,7 +89,15 @@ module Sus
97
89
  Have::Attribute.new(key, value)
98
90
  end
99
91
 
100
- Have::Composite.new(predicates)
92
+ Have::All.new(predicates)
93
+ end
94
+
95
+ def have_any(*predicates)
96
+ Have::Any.new(predicates)
97
+ end
98
+
99
+ def have_value(predicate)
100
+ Have::Any.new([Have::Value.new(predicate)])
101
101
  end
102
102
  end
103
103
  end
data/lib/sus/it.rb CHANGED
@@ -39,10 +39,20 @@ module Sus
39
39
  instance = self.new(assertions)
40
40
 
41
41
  instance.around do
42
- instance.call
42
+ handle_skip(instance, assertions)
43
43
  end
44
44
  end
45
45
  end
46
+
47
+ def handle_skip(instance, assertions)
48
+ reason = catch(:skip) do
49
+ return instance.call
50
+ end
51
+
52
+ assertions.skip(reason)
53
+
54
+ return nil
55
+ end
46
56
  end
47
57
 
48
58
  module Context
@@ -50,4 +60,10 @@ module Sus
50
60
  add It.build(self, ...)
51
61
  end
52
62
  end
63
+
64
+ class Base
65
+ def skip(reason)
66
+ throw :skip, reason
67
+ end
68
+ end
53
69
  end
@@ -11,12 +11,18 @@ module Sus
11
11
 
12
12
  attr_accessor :shared
13
13
 
14
- def self.build(parent, shared, unique: false)
14
+ def self.build(parent, shared, unique: false, &block)
15
15
  base = Class.new(parent)
16
16
  base.singleton_class.prepend(ItBehavesLike)
17
17
  base.children = Hash.new
18
18
  base.description = shared.name
19
19
  base.identity = Identity.nested(parent.identity, base.description, unique: unique)
20
+
21
+ # User provided block is evaluated first, so that it can provide default behaviour for the shared context:
22
+ if block_given?
23
+ base.class_exec(&block)
24
+ end
25
+
20
26
  base.class_exec(&shared.block)
21
27
  return base
22
28
  end
@@ -28,8 +34,8 @@ module Sus
28
34
  end
29
35
 
30
36
  module Context
31
- def it_behaves_like(shared, **options)
32
- add ItBehavesLike.build(self, shared, **options)
37
+ def it_behaves_like(shared, **options, &block)
38
+ add ItBehavesLike.build(self, shared, **options, &block)
33
39
  end
34
40
  end
35
41
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022, by Samuel Williams.
4
+ # Copyright, 2021-2022, by Samuel Williams.
5
5
 
6
6
  require_relative 'bar'
7
7
  require_relative 'status'
@@ -14,7 +14,6 @@ module Sus
14
14
  def initialize(state = :free, context = nil)
15
15
  @state = state
16
16
  @context = context
17
- @index = 0
18
17
  end
19
18
 
20
19
  INDICATORS = {
@@ -36,8 +35,6 @@ module Sus
36
35
  end
37
36
 
38
37
  def print(output)
39
- message = @message
40
-
41
38
  output.write(
42
39
  @state, self.indicator, " "
43
40
  )
@@ -19,6 +19,8 @@ module Sus
19
19
  @styles[:indent] = @indent
20
20
  end
21
21
 
22
+ attr :styles
23
+
22
24
  def buffered
23
25
  Buffered.new(self)
24
26
  end
data/lib/sus/output.rb CHANGED
@@ -40,6 +40,7 @@ module Sus
40
40
  output[:passed] = output.style(:green)
41
41
  output[:failed] = output.style(:red)
42
42
  output[:error] = output.style(:red)
43
+ output[:skip] = output.style(:blue)
43
44
  # output[:inform] = output.style(nil, nil, :bold)
44
45
 
45
46
  return output
@@ -26,7 +26,7 @@ module Sus
26
26
  rescue @exception_class => exception
27
27
  # Did it have the right message?
28
28
  if @message
29
- assertions.assert(@message === exception.message, "raised with message")
29
+ @message.call(assertions, exception.message)
30
30
  else
31
31
  assertions.assert(true, "raised")
32
32
  end
data/lib/sus/receive.rb CHANGED
@@ -24,31 +24,40 @@ module Sus
24
24
  output.write("receive ", :variable, @method.to_s, :reset, " ")
25
25
  end
26
26
 
27
- def with_arguments(*arguments)
28
- @arguments = WithArguments.new(arguments)
27
+ def with_arguments(predicate)
28
+ @arguments = WithArguments.new(predicate)
29
29
  return self
30
30
  end
31
-
32
- def with_options(*options)
33
- @options = WithOptions.new(options)
31
+
32
+ def with_options(predicate)
33
+ @options = WithOptions.new(predicate)
34
34
  return self
35
35
  end
36
-
37
- def with_block
38
- @block = WithBlock.new
36
+
37
+ def with_block(predicate = Be.new(:!=, nil))
38
+ @block = WithBlock.new(predicate)
39
39
  return self
40
40
  end
41
-
41
+
42
+ def with(*arguments, **options)
43
+ with_arguments(Be.new(:==, arguments)) if arguments.any?
44
+ with_options(Be.new(:==, options)) if options.any?
45
+ return self
46
+ end
47
+
42
48
  def once
43
49
  @times = Times.new(Be.new(:==, 1))
50
+ return self
44
51
  end
45
52
 
46
53
  def twice
47
54
  @times = Times.new(Be.new(:==, 2))
55
+ return self
48
56
  end
49
57
 
50
58
  def with_call_count(predicate)
51
59
  @times = Times.new(predicate)
60
+ return self
52
61
  end
53
62
 
54
63
  def and_return(*returning)
@@ -101,44 +110,49 @@ module Sus
101
110
  end
102
111
 
103
112
  class WithArguments
104
- def initialize(arguments)
105
- @arguments = arguments
113
+ def initialize(predicate)
114
+ @predicate = predicate
106
115
  end
107
116
 
108
117
  def print(output)
109
- output.write("with arguments ", :variable, @arguments.inspect)
118
+ output.write("with arguments ", @predicate)
110
119
  end
111
120
 
112
121
  def call(assertions, subject)
113
122
  assertions.nested(self) do |assertions|
114
- Expect.new(assertions, subject).to(Be == @arguments)
123
+ @predicate.call(assertions, subject)
115
124
  end
116
125
  end
117
126
  end
118
127
 
119
128
  class WithOptions
120
- def initialize(options)
121
- @options = options
129
+ def initialize(predicate)
130
+ @predicate = predicate
122
131
  end
123
132
 
124
133
  def print(output)
125
- output.write("with options ", :variable, @options.inspect)
134
+ output.write("with options ", @predicate)
126
135
  end
127
136
 
128
137
  def call(assertions, subject)
129
138
  assertions.nested(self) do |assertions|
130
- Expect.new(assertions, subject).to(Be.new(:include?, @options))
139
+ @predicate.call(assertions, subject)
131
140
  end
132
141
  end
133
142
  end
134
143
 
135
144
  class WithBlock
145
+ def initialize(predicate)
146
+ @predicate = predicate
147
+ end
148
+
136
149
  def print(output)
137
- output.write("with block")
150
+ output.write("with block", @predicate)
138
151
  end
139
152
 
140
153
  def call(assertions, subject)
141
154
  assertions.nested(self) do |assertions|
155
+
142
156
  Expect.new(assertions, subject).not.to(Be == nil)
143
157
  end
144
158
  end
data/lib/sus/registry.rb CHANGED
@@ -19,6 +19,8 @@ require_relative 'let'
19
19
 
20
20
  module Sus
21
21
  class Registry
22
+ DIRECTORY_GLOB = "**/*.rb"
23
+
22
24
  # Create a top level scope with self as the instance:
23
25
  def initialize(base = Sus.base(self))
24
26
  @base = base
@@ -31,9 +33,21 @@ module Sus
31
33
  end
32
34
 
33
35
  def load(path)
36
+ if ::File.directory?(path)
37
+ load_directory(path)
38
+ else
39
+ load_file(path)
40
+ end
41
+ end
42
+
43
+ private def load_file(path)
34
44
  @base.file(path)
35
45
  end
36
46
 
47
+ private def load_directory(path)
48
+ ::Dir.glob(::File.join(path, DIRECTORY_GLOB), &self.method(:load_file))
49
+ end
50
+
37
51
  def call(assertions = Assertions.default)
38
52
  @base.call(assertions)
39
53
 
data/lib/sus/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2021-2022, by Samuel Williams.
5
5
 
6
6
  module Sus
7
- VERSION = "0.13.0"
7
+ VERSION = "0.15.0"
8
8
  end
data/lib/sus.rb CHANGED
@@ -10,6 +10,7 @@ require_relative 'sus/assertions'
10
10
 
11
11
  require_relative 'sus/expect'
12
12
  require_relative 'sus/be'
13
+ require_relative 'sus/be_truthy'
13
14
  require_relative 'sus/be_within'
14
15
 
15
16
  require_relative 'sus/mock'
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.13.0
4
+ version: 0.15.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-09-02 00:00:00.000000000 Z
40
+ date: 2022-10-30 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: bake-test
@@ -95,6 +95,7 @@ files:
95
95
  - lib/sus/assertions.rb
96
96
  - lib/sus/base.rb
97
97
  - lib/sus/be.rb
98
+ - lib/sus/be_truthy.rb
98
99
  - lib/sus/be_within.rb
99
100
  - lib/sus/clock.rb
100
101
  - lib/sus/config.rb
@@ -105,6 +106,8 @@ files:
105
106
  - lib/sus/filter.rb
106
107
  - lib/sus/fixtures.rb
107
108
  - lib/sus/have.rb
109
+ - lib/sus/have/all.rb
110
+ - lib/sus/have/any.rb
108
111
  - lib/sus/have_duration.rb
109
112
  - lib/sus/identity.rb
110
113
  - lib/sus/include_context.rb
metadata.gz.sig CHANGED
Binary file