expresenter 1.4.1 → 1.5.1

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: 6eb634d0a1d539bbedb8a223d115a4b7ce567d376e67483c98a7a63816799730
4
- data.tar.gz: 6251c7601028185b98b6748c2daca4666b8bcd350c62aa1066534e82d8282f8f
3
+ metadata.gz: 6701ed87c0d50891bb8804abc202aa3a2579d34f658b2d8b59f2b7caec76228a
4
+ data.tar.gz: f9d618cddd0aeacb36667da36816a759ff768aadb3e0f401f893baecf1306a2c
5
5
  SHA512:
6
- metadata.gz: e8c26a86adfdee7cbc76b9308e541a0d5b97c7b6ed1b6eb5836065d587df7f820715f0fadf6c865c36176f4cf6c3ed29d557aa4b2cd30e2367a7d839395a80e1
7
- data.tar.gz: 23d22691711a01ff958fa85276929f5a4c4132859eb44a28fc72577973d40a3535646492760494c6c9562a184599bbff0e36219fdceedae84ff3e315a767e3f0
6
+ metadata.gz: edbc7c35fe288a72bf3160810a5acf87675785e49f6f508ceb60a146ad74aa6072a2a287c24cde38a20ed22db242c7f1f2aeb6202e25f7cbaeacd1f5d39e622c
7
+ data.tar.gz: 2f16a23515e63fa17773da941ecb099a45c04a9973bcc5224d39ba36b41aff28f49d9e148960101bb49b8f0fca52b34c03c17848795bfb2f9ae4500d42c61d4b
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021-2024 Cyril Kato
3
+ Copyright (c) 2021-2025 Cyril Kato
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -2,11 +2,27 @@
2
2
 
3
3
  [![Version](https://img.shields.io/github/v/tag/fixrb/expresenter?label=Version&logo=github)](https://github.com/fixrb/expresenter/tags)
4
4
  [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/fixrb/expresenter/main)
5
- [![Ruby](https://github.com/fixrb/expresenter/workflows/Ruby/badge.svg?branch=main)](https://github.com/fixrb/expresenter/actions?query=workflow%3Aruby+branch%3Amain)
6
- [![RuboCop](https://github.com/fixrb/expresenter/workflows/RuboCop/badge.svg?branch=main)](https://github.com/fixrb/expresenter/actions?query=workflow%3Arubocop+branch%3Amain)
7
5
  [![License](https://img.shields.io/github/license/fixrb/expresenter?label=License&logo=github)](https://github.com/fixrb/expresenter/raw/main/LICENSE.md)
8
6
 
9
- > Expectation result presenter.
7
+ > A Ruby gem for presenting test expectation results with rich formatting and requirement level support. Perfect for test frameworks and assertion libraries that need flexible result reporting with support for MUST/SHOULD/MAY requirement levels.
8
+
9
+ ## Features
10
+
11
+ - Rich test result presentation with:
12
+ - Colored output for different result types (success, warning, info, failure, error)
13
+ - Single-character indicators for compact output (".", "W", "I", "F", "E")
14
+ - Emoji support for visual feedback (✅, ⚠️, 💡, ❌, 💥)
15
+ - ANSI-colored formatted messages with bold titles
16
+ - Support for RFC 2119 requirement levels (MUST/SHOULD/MAY)
17
+ - Comprehensive result classification:
18
+ - Success: Test passed as expected (green)
19
+ - Warning: Non-critical issues, typically for SHOULD/MAY requirements (yellow)
20
+ - Info: Additional information about passing tests (blue)
21
+ - Failure: Test failed but no exception occurred (purple)
22
+ - Error: Unexpected exceptions during test execution (red)
23
+ - Built-in support for negative assertions
24
+ - Detailed error reporting with captured exceptions
25
+ - Clean integration with test frameworks via simple API
10
26
 
11
27
  ## Installation
12
28
 
@@ -30,146 +46,133 @@ gem install expresenter
30
46
 
31
47
  ## Usage
32
48
 
33
- Assuming that an expectation is an assertion that is either `true` or `false`,
34
- qualifying it with `MUST`, `SHOULD` and `MAY`, we can draw up several scenarios:
35
-
36
- | Requirement levels | **MUST** | **SHOULD** | **MAY** |
37
- | ------------------------- | -------- | ---------- | ------- |
38
- | Implemented & Matched | `true` | `true` | `true` |
39
- | Implemented & Not matched | `false` | `true` | `false` |
40
- | Implemented & Exception | `false` | `false` | `false` |
41
- | Not implemented | `false` | `false` | `true` |
42
-
43
- Then,
44
-
45
- * for a `true` assertion, a `Expresenter::Pass` instance can be returned;
46
- * for a `false` assertion, a `Expresenter::Fail` exception can be raised.
47
-
48
- Both class share a same `Common` interface.
49
-
50
- Passed expectations can be classified as:
51
-
52
- * ✅ success
53
- * ⚠️ warning
54
- * 💡 info
55
-
56
- Failed expectations can be classified as:
57
-
58
- * ❌ failure
59
- * 💥 error
60
-
61
- ### Instantiation
62
-
63
- The following parameters are required to instantiate the result:
64
-
65
- * `actual`: Returned value by the challenged subject.
66
- * `definition`: A readable string of the matcher and any expected values.
67
- * `error`: Any possible raised exception.
68
- * `expected`: The expected value.
69
- * `got`: The result of the boolean comparison between the actual value and the expected value through the matcher.
70
- * `negate`: Evaluated to a negative assertion?
71
- * `level`: The requirement level (`:MUST`, `:SHOULD` or `:MAY`).
72
-
73
- #### Examples
74
-
75
- A passed expectation:
49
+ ### Basic Example
76
50
 
77
51
  ```ruby
78
- result = Expresenter.call(true).new(actual: "FOO", definition: 'eq "foo"', error: nil, expected: "foo", got: true, negate: true, level: :MUST)
79
-
80
- result.failed? # => false
81
- result.failure? # => false
82
- result.info? # => false
83
- result.warning? # => false
84
- result.to_sym # => :success
85
- result.char # => "."
86
- result.emoji # => "✅"
87
- result.passed? # => true
88
- result.negate? # => true
89
- result.error? # => false
90
- result.success? # => true
91
- result.inspect # => "Expresenter::Pass(actual: \"FOO\", definition: \"eq \\\"foo\\\"\", error: nil, expected: \"foo\", got: true, negate: true, level: :MUST)"
92
- result.definition # => "eq \"foo\""
93
- result.summary # => "expected \"FOO\" not to eq \"foo\""
94
- result.colored_char # => "\e[32m.\e[0m"
95
- result.colored_string # => "\e[32m\e[1mSuccess\e[22m: expected \"FOO\" not to eq \"foo\".\e[0m"
96
- result.message # => "Success: expected \"FOO\" not to eq \"foo\"."
97
- result.to_s # => "Success: expected \"FOO\" not to eq \"foo\"."
98
- result.titre # => "Success"
52
+ # Create a successful test result
53
+ result = Expresenter.call(true).new(
54
+ actual: "foo",
55
+ definition: 'eq "foo"',
56
+ error: nil,
57
+ got: true,
58
+ negate: false,
59
+ level: :MUST
60
+ )
61
+
62
+ result.passed? # => true
63
+ result.to_sym # => :success
64
+ result.char # => "."
65
+ result.emoji # => ""
66
+ result.to_s # => 'Success: expected "foo" to eq "foo".'
99
67
  ```
100
68
 
101
- A failed expectation:
69
+ ### Handling Different Result Types
102
70
 
103
71
  ```ruby
104
- result = Expresenter.call(false).new(actual: "foo", definition: "eq 42", error: Exception.new("BOOM"), expected: 42, got: true, negate: true, level: :MUST)
105
-
106
- result.failed? # => true
107
- result.failure? # => false
108
- result.info? # => false
109
- result.warning? # => false
110
- result.to_sym # => :error
111
- result.char # => "E"
112
- result.emoji # => "💥"
113
- result.passed? # => false
114
- result.negate? # => true
115
- result.error? # => true
116
- result.success? # => true
117
- result.inspect # => "Expresenter::Fail(actual: \"foo\", definition: \"eq 42\", error: #<Exception: BOOM>, expected: 42, got: true, negate: true, level: :MUST)"
118
- result.definition # => "eq 42"
119
- result.summary # => "BOOM"
120
- result.colored_char # => "\e[31mE\e[0m"
121
- result.colored_string # => "\e[31m\e[1mException\e[22m: BOOM.\e[0m"
122
- result.message # => "Exception: BOOM."
123
- result.to_s # => "Exception: BOOM."
124
- result.titre # => "Exception"
72
+ # Warning example (non-critical requirement)
73
+ warning = Expresenter.call(true).new(
74
+ actual: "foo",
75
+ definition: "be_optimized",
76
+ error: nil,
77
+ got: false, # triggers warning state
78
+ negate: false,
79
+ level: :SHOULD
80
+ )
81
+
82
+ warning.warning? # => true
83
+ warning.char # => "W"
84
+ warning.emoji # => "⚠️"
85
+
86
+ # Failure example with exception
87
+ begin
88
+ Expresenter.call(false).with(
89
+ actual: 42,
90
+ definition: "eq 43",
91
+ error: nil,
92
+ got: false,
93
+ negate: false,
94
+ level: :MUST
95
+ )
96
+ rescue Expresenter::Fail => e
97
+ e.failure? # => true
98
+ e.char # => "F"
99
+ e.emoji # => "❌"
100
+ e.to_s # => "Failure: expected 42 to eq 43."
101
+ end
125
102
  ```
126
103
 
127
- ### Return or Raise
104
+ ### Using Requirement Levels
128
105
 
129
- To return the results which pass, and to raise the results which fail, the `with` method is available.
106
+ Expresenter supports RFC 2119 requirement levels:
130
107
 
131
- In this example, the result passes, the instance is therefore returned:
108
+ - `:MUST` - Critical requirements that must be satisfied
109
+ - `:SHOULD` - Recommended requirements that should be satisfied when possible
110
+ - `:MAY` - Optional requirements that may be satisfied
132
111
 
133
112
  ```ruby
134
- Expresenter.call(true).with(actual: "FOO", definition: 'eq "foo"', error: nil, expected: "foo", got: true, negate: true, level: :MUST) # => Expresenter::Pass(actual: "FOO", definition: "eq \"foo\"", error: nil, expected: "foo", got: true, negate: true, level: :MUST)
113
+ # SHOULD requirement with warning
114
+ result = Expresenter.call(true).new(
115
+ actual: response,
116
+ definition: "have_fast_response_time",
117
+ error: nil,
118
+ got: false,
119
+ negate: false,
120
+ level: :SHOULD
121
+ )
122
+
123
+ result.warning? # => true
135
124
  ```
136
125
 
137
- In this example, the result fails, so the exception is raised:
126
+ ### Working with Negative Assertions
138
127
 
139
128
  ```ruby
140
- Expresenter.call(false).with(actual: "foo", definition: "eq 40", error: Exception.new("BOOM"), expected: 42, got: true, negate: true, level: :MUST)
129
+ # Negative assertion example
130
+ result = Expresenter.call(true).new(
131
+ actual: "foo",
132
+ definition: 'eq "bar"',
133
+ error: nil,
134
+ got: true,
135
+ negate: true, # indicates negative assertion
136
+ level: :MUST
137
+ )
138
+
139
+ result.negate? # => true
140
+ result.to_s # => 'Success: expected "foo" not to eq "bar".'
141
141
  ```
142
142
 
143
- > Traceback (most recent call last):
144
- > 4: from ./bin/console:7:in `<main>'
145
- > 3: from (irb):42
146
- > 2: from (irb):43:in `rescue in irb_binding'
147
- > 1: from /Users/cyril/github/fixrb/expresenter/lib/expresenter/fail.rb:25:in `with'
148
- > Expresenter::Fail (Exception: BOOM.)
143
+ ## Integration
144
+
145
+ Expresenter can be easily integrated into test frameworks and assertion libraries. It provides a simple API for creating and handling test results with rich formatting and requirement levels.
149
146
 
150
- ### More Examples
147
+ Example integration with a test framework:
151
148
 
152
- A full list of unit tests can be viewed (and executed) here:
153
- [./test.rb](https://github.com/fixrb/expresenter/blob/main/test.rb)
149
+ ```ruby
150
+ def assert(actual, matcher, level: :MUST)
151
+ result = matcher.match(actual)
152
+
153
+ Expresenter.call(result.success?).with(
154
+ actual: actual,
155
+ definition: matcher.description,
156
+ error: result.error,
157
+ got: result.matched?,
158
+ negate: false,
159
+ level: level
160
+ )
161
+ end
162
+ ```
154
163
 
155
- ## Contact
164
+ ## Development
156
165
 
157
- * Home page: https://github.com/fixrb/expresenter
158
- * Bugs/issues: https://github.com/fixrb/expresenter/issues
166
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
159
167
 
160
- ## Versioning
168
+ ## Contributing
161
169
 
162
- __Expresenter__ follows [Semantic Versioning 2.0](https://semver.org/).
170
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fixrb/expresenter. This project is intended to be a safe, welcoming space for collaboration.
163
171
 
164
172
  ## License
165
173
 
166
174
  The [gem](https://rubygems.org/gems/expresenter) is available as open source under the terms of the [MIT License](https://github.com/fixrb/expresenter/raw/main/LICENSE.md).
167
175
 
168
- ***
176
+ ## Sponsors
169
177
 
170
- <p>
171
- This project is sponsored by:<br />
172
- <a href="https://sashite.com/"><img
173
- src="https://github.com/fixrb/expresenter/raw/main/img/sashite.png"
174
- alt="Sashité" /></a>
175
- </p>
178
+ This project is sponsored by [Sashité](https://sashite.com/)
@@ -1,110 +1,170 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Expresenter
4
- # Common collection of methods.
4
+ # Common collection of methods shared between Pass and Fail result classes.
5
+ #
6
+ # This module provides the core functionality for presenting test results, including:
7
+ # - Access to test result data (actual value, definition, error state)
8
+ # - Test state queries (passed?, error?, success?)
9
+ # - Result formatting and presentation (summary, colored output)
10
+ # - Support for requirement levels (MUST/SHOULD/MAY)
11
+ #
12
+ # @example Using common methods in a test result
13
+ # result = Expresenter.call(true).new(
14
+ # actual: "foo",
15
+ # definition: 'eq "foo"',
16
+ # error: nil,
17
+ # got: true,
18
+ # negate: false,
19
+ # level: :MUST
20
+ # )
21
+ #
22
+ # result.passed? # => true
23
+ # result.success? # => true
24
+ # result.summary # => 'expected "foo" to eq "foo"'
25
+ # result.to_s # => 'Success: expected "foo" to eq "foo".'
26
+ #
5
27
  module Common
6
- # White space.
28
+ # String constant used for joining message parts.
29
+ #
30
+ # @api private
7
31
  SPACE = " "
8
32
 
9
- # @return [#object_id] Returned value by the challenged subject.
33
+ # The actual value returned by the test subject.
34
+ #
35
+ # @return [#object_id] The value being tested against the expectation.
36
+ # @example
37
+ # result.actual # => "foo"
10
38
  attr_reader :actual
11
39
 
12
- # @return [String] A readable string of the matcher and any expected values.
40
+ # The human-readable description of the expectation.
41
+ #
42
+ # @return [String] Description combining the matcher and expected values.
43
+ # @example
44
+ # result.definition # => 'eq "foo"'
13
45
  attr_reader :definition
14
46
 
15
- # @return [Exception, nil] Any possible raised exception.
47
+ # Any exception that was raised during the test.
48
+ #
49
+ # @return [Exception, nil] The error object if an exception occurred, nil otherwise.
50
+ # @example
51
+ # begin
52
+ # raise StandardError, "Oops"
53
+ # rescue => e
54
+ # result = Expresenter.call(false).new(error: e, ...)
55
+ # result.error # => #<StandardError: Oops>
56
+ # end
16
57
  attr_reader :error
17
58
 
18
- # @return [#object_id] The expected value.
19
- attr_reader :expected
20
-
21
- # @return [#object_id] The result of the boolean comparison between the
22
- # actual value and the expected value through the matcher.
59
+ # The boolean result of comparing the actual value against the expectation.
60
+ #
61
+ # @return [#object_id] Usually true/false, but can be any object that responds to equal?
62
+ # @example
63
+ # result.got # => true
23
64
  attr_reader :got
24
65
 
25
- # @return [:MUST, :SHOULD, :MAY] The requirement level of the expectation.
66
+ # The requirement level of the expectation.
67
+ #
68
+ # @return [:MUST, :SHOULD, :MAY] The RFC 2119 requirement level.
69
+ # @see https://www.ietf.org/rfc/rfc2119.txt
70
+ # @example
71
+ # result.level # => :MUST
26
72
  attr_reader :level
27
73
 
28
- # Did the test pass?
74
+ # Checks if the test passed.
29
75
  #
30
- # @return [Boolean] The spec passed or failed?
76
+ # @return [Boolean] true if the test passed, false otherwise.
77
+ # @example
78
+ # result.passed? # => true
31
79
  def passed?
32
80
  !failed?
33
81
  end
34
82
 
35
- # The value of the negate instance variable.
83
+ # Checks if this is a negative assertion.
36
84
  #
37
- # @return [Boolean] Evaluated to a negative assertion?
85
+ # @return [Boolean] true if this is a negative assertion (using not), false otherwise.
86
+ # @example
87
+ # result = Expresenter.call(true).new(negate: true, ...)
88
+ # result.negate? # => true
38
89
  def negate?
39
90
  @negate
40
91
  end
41
92
 
42
- # The state of error.
93
+ # Checks if an error occurred during the test.
43
94
  #
44
- # @return [Boolean] The test raised an error?
95
+ # @return [Boolean] true if an error was captured, false otherwise.
96
+ # @example
97
+ # begin
98
+ # raise "Oops"
99
+ # rescue => e
100
+ # result = Expresenter.call(false).new(error: e, ...)
101
+ # result.error? # => true
102
+ # end
45
103
  def error?
46
104
  !error.nil?
47
105
  end
48
106
 
49
- # The state of success.
107
+ # Checks if the test was successful.
50
108
  #
51
- # @return [Boolean] The test was a success?
109
+ # @return [Boolean] true if the got value equals true, false otherwise.
110
+ # @example
111
+ # result = Expresenter.call(true).new(got: true, ...)
112
+ # result.success? # => true
52
113
  def success?
53
114
  got.equal?(true)
54
115
  end
55
116
 
56
- # A string containing a human-readable representation of the result.
57
- #
58
- # @return [String] The human-readable representation of the result.
59
- def inspect
60
- "#{self.class}(actual: #{actual.inspect}, " \
61
- "definition: #{definition.inspect}, " \
62
- "error: #{error.inspect}, " \
63
- "expected: #{expected.inspect}, " \
64
- "got: #{got.inspect}, " \
65
- "negate: #{negate?.inspect}, " \
66
- "level: #{level.inspect})"
67
- end
68
-
69
- # The summary of the result.
117
+ # Generates a human-readable summary of the test result.
70
118
  #
71
- # @return [String] A string representing the summary of the result.
119
+ # @return [String] A description of what was expected vs what was received.
120
+ # @example With regular value
121
+ # result.summary # => 'expected "foo" to eq "bar"'
122
+ # @example With error
123
+ # result.summary # => "Unexpected error occurred"
72
124
  def summary
73
125
  if error?
74
126
  error.message
75
127
  elsif actual.is_a?(::Exception)
76
128
  actual.message
77
- elsif actual == expected
78
- ["expected", negation, "to", definition].compact.join(SPACE)
79
129
  else
80
130
  ["expected", actual.inspect, negation, "to", definition].compact.join(SPACE)
81
131
  end
82
132
  end
83
133
 
84
- # Express the result with one colored char.
134
+ # Returns the result indicator with ANSI color codes.
85
135
  #
86
- # @return [String] The colored char that identify the result.
136
+ # @return [String] A colored single character indicating the result type.
137
+ # @example
138
+ # result.colored_char # => "\e[32m.\e[0m"
87
139
  def colored_char
88
140
  color(char)
89
141
  end
90
142
 
91
- # The colored string representation of the result.
143
+ # Returns the full result message with ANSI color codes.
92
144
  #
93
- # @return [String] A string representing the result.
145
+ # @return [String] A colored string with the complete test result.
146
+ # @example
147
+ # result.colored_string # => "\e[32mSuccess: expected 1 to eq 1.\e[0m"
94
148
  def colored_string
95
149
  color(to_bold_s)
96
150
  end
97
151
 
98
- # The representation of the result.
152
+ # Returns the complete result message.
99
153
  #
100
- # @return [String] A string representing the result.
154
+ # @return [String] A string containing the result type and summary.
155
+ # @example
156
+ # result.to_s # => "Success: expected 1 to eq 1."
101
157
  def to_s
102
158
  "#{titre}: #{summary}."
103
159
  end
104
160
 
105
- # Titre for the result.
161
+ # Returns the title for the result type.
106
162
  #
107
- # @return [String] A string representing the titre.
163
+ # @return [String] Either the error class name or capitalized result type.
164
+ # @example Normal result
165
+ # result.titre # => "Success"
166
+ # @example Error result
167
+ # result.titre # => "StandardError"
108
168
  def titre
109
169
  if error?
110
170
  error.class.name
@@ -115,16 +175,18 @@ module Expresenter
115
175
 
116
176
  protected
117
177
 
118
- # The negation, if any.
178
+ # Returns the negation word if this is a negative assertion.
119
179
  #
120
- # @return [String, nil] The negation, or an empty string.
180
+ # @return [String, nil] Returns "not" for negative assertions, nil otherwise.
181
+ # @api private
121
182
  def negation
122
183
  "not" if negate?
123
184
  end
124
185
 
125
- # The representation of the result with the title in bold.
186
+ # Returns the result message with a bold title.
126
187
  #
127
- # @return [String] A string representing the result with the title in bold.
188
+ # @return [String] The result message with ANSI codes for bold title.
189
+ # @api private
128
190
  def to_bold_s
129
191
  "\e[1m#{titre}\e[22m: #{summary}."
130
192
  end
@@ -3,44 +3,109 @@
3
3
  require_relative "common"
4
4
 
5
5
  module Expresenter
6
- # The class that is responsible for reporting that an expectation is false.
6
+ # Class responsible for handling and reporting failed test expectations.
7
+ #
8
+ # The Fail class represents test failures and errors, inheriting from StandardError
9
+ # to support exception handling. It distinguishes between two types of failures:
10
+ # - Regular failures: When an assertion fails but no exception occurred
11
+ # - Errors: When an unexpected exception was raised during the test
12
+ #
13
+ # @example Handling a regular test failure
14
+ # begin
15
+ # Expresenter.call(false).with(
16
+ # actual: 42,
17
+ # definition: 'eq 43',
18
+ # error: nil,
19
+ # got: false,
20
+ # negate: false,
21
+ # level: :MUST
22
+ # )
23
+ # rescue Expresenter::Fail => e
24
+ # e.failure? # => true
25
+ # e.error? # => false
26
+ # e.to_sym # => :failure
27
+ # e.char # => "F"
28
+ # e.emoji # => "❌"
29
+ # e.to_s # => "Failure: expected 42 to eq 43."
30
+ # end
31
+ #
32
+ # @example Handling a test error
33
+ # begin
34
+ # error = StandardError.new("Unexpected error")
35
+ # Expresenter.call(false).with(
36
+ # actual: nil,
37
+ # definition: 'be_valid',
38
+ # error: error,
39
+ # got: false,
40
+ # negate: false,
41
+ # level: :MUST
42
+ # )
43
+ # rescue Expresenter::Fail => e
44
+ # e.failure? # => false
45
+ # e.error? # => true
46
+ # e.to_sym # => :error
47
+ # e.char # => "E"
48
+ # e.emoji # => "💥"
49
+ # e.to_s # => "StandardError: Unexpected error."
50
+ # end
7
51
  class Fail < ::StandardError
8
- # Char representing a failure.
52
+ # Single character indicator for test failures.
53
+ # @api private
9
54
  FAILURE_CHAR = "F"
10
55
 
11
- # Emoji representing a failure.
56
+ # Emoji indicator for test failures.
57
+ # @api private
12
58
  FAILURE_EMOJI = "❌"
13
59
 
14
- # Char representing an error.
60
+ # Single character indicator for test errors.
61
+ # @api private
15
62
  ERROR_CHAR = "E"
16
63
 
17
- # Emoji representing an error.
64
+ # Emoji indicator for test errors.
65
+ # @api private
18
66
  ERROR_EMOJI = "💥"
19
67
 
20
68
  include Common
21
69
 
22
- # @param (see Fail#initialize)
23
- # @raise [Fail] A failed spec exception.
70
+ # Creates and raises a new Fail instance with the given details.
71
+ #
72
+ # @param details [Hash] Test result details (see #initialize for parameters)
73
+ # @raise [Fail] Always raises a Fail exception with the provided details
74
+ # @example
75
+ # Expresenter::Fail.with(
76
+ # actual: 42,
77
+ # definition: 'eq 43',
78
+ # error: nil,
79
+ # got: false,
80
+ # negate: false,
81
+ # level: :MUST
82
+ # ) # raises Expresenter::Fail
24
83
  def self.with(**details)
25
84
  raise new(**details)
26
85
  end
27
86
 
28
- # Initialize method.
87
+ # Initializes a new Fail instance.
88
+ #
89
+ # @param actual [#object_id] The actual value returned by the test
90
+ # @param definition [String] Human-readable description of the expectation
91
+ # @param error [Exception, nil] Any exception that was raised during the test
92
+ # @param got [Boolean, nil] Result of comparing actual vs expected values
93
+ # @param negate [Boolean] Whether this is a negative assertion
94
+ # @param level [:MUST, :SHOULD, :MAY] The requirement level of the test
29
95
  #
30
- # @param actual [#object_id] Returned value by the challenged subject.
31
- # @param definition [String] A readable string of the matcher and any
32
- # expected values.
33
- # @param error [Exception, nil] Any possible raised exception.
34
- # @param expected [#object_id] The expected value.
35
- # @param got [Boolean, nil] The result of the boolean comparison
36
- # between the actual value and the expected value through the matcher.
37
- # @param negate [Boolean] Evaluated to a negative assertion?
38
- # @param level [:MUST, :SHOULD, :MAY] The requirement level.
39
- def initialize(actual:, definition:, error:, expected:, got:, negate:, level:)
96
+ # @example Creating a failure result
97
+ # Fail.new(
98
+ # actual: 42,
99
+ # definition: 'eq 43',
100
+ # error: nil,
101
+ # got: false,
102
+ # negate: false,
103
+ # level: :MUST
104
+ # )
105
+ def initialize(actual:, definition:, error:, got:, negate:, level:)
40
106
  @actual = actual
41
107
  @definition = definition
42
108
  @error = error
43
- @expected = expected
44
109
  @got = got
45
110
  @negate = negate
46
111
  @level = level
@@ -48,37 +113,37 @@ module Expresenter
48
113
  super(to_s)
49
114
  end
50
115
 
51
- # Did the test fail?
116
+ # Always returns true since this class represents failed tests.
52
117
  #
53
- # @return [Boolean] The spec passed or failed?
118
+ # @return [true] Always returns true
54
119
  def failed?
55
120
  true
56
121
  end
57
122
 
58
- # The state of failure.
123
+ # Indicates if this is a regular failure (not an error).
59
124
  #
60
- # @return [Boolean] The test was a failure?
125
+ # @return [Boolean] true if no error was captured, false otherwise
61
126
  def failure?
62
127
  !error?
63
128
  end
64
129
 
65
- # The state of info.
130
+ # Fail results are never informational.
66
131
  #
67
- # @return [Boolean] The test was an info?
132
+ # @return [false] Always returns false
68
133
  def info?
69
134
  false
70
135
  end
71
136
 
72
- # The state of warning.
137
+ # Fail results are never warnings.
73
138
  #
74
- # @return [Boolean] The test was a warning?
139
+ # @return [false] Always returns false
75
140
  def warning?
76
141
  false
77
142
  end
78
143
 
79
- # Identify the state of the result.
144
+ # Returns the symbolic representation of the failure type.
80
145
  #
81
- # @return [Symbol] The identifier of the state.
146
+ # @return [:failure, :error] :failure for regular failures, :error when an exception occurred
82
147
  def to_sym
83
148
  if failure?
84
149
  :failure
@@ -87,9 +152,9 @@ module Expresenter
87
152
  end
88
153
  end
89
154
 
90
- # Express the result with one char.
155
+ # Returns a single character representing the failure type.
91
156
  #
92
- # @return [String] The char that identify the result.
157
+ # @return [String] "F" for failures, "E" for errors
93
158
  def char
94
159
  if failure?
95
160
  FAILURE_CHAR
@@ -98,9 +163,9 @@ module Expresenter
98
163
  end
99
164
  end
100
165
 
101
- # Express the result with one emoji.
166
+ # Returns an emoji representing the failure type.
102
167
  #
103
- # @return [String] The emoji that identify the result.
168
+ # @return [String] "❌" for failures, "💥" for errors
104
169
  def emoji
105
170
  if failure?
106
171
  FAILURE_EMOJI
@@ -111,6 +176,11 @@ module Expresenter
111
176
 
112
177
  protected
113
178
 
179
+ # Applies color formatting to the given string based on failure type.
180
+ #
181
+ # @param str [String] The string to colorize
182
+ # @return [String] ANSI-colored string (purple for failures, red for errors)
183
+ # @api private
114
184
  def color(str)
115
185
  if failure?
116
186
  "\e[35m#{str}\e[0m" # purple
@@ -3,88 +3,143 @@
3
3
  require_relative "common"
4
4
 
5
5
  module Expresenter
6
- # The class that is responsible for reporting that an expectation is true.
6
+ # Class responsible for handling and reporting successful test expectations.
7
+ #
8
+ # The Pass class represents test results that didn't fail, but can be in different states:
9
+ # - Success: Test passed completely as expected
10
+ # - Warning: Test passed but with some concerns (typically for :SHOULD or :MAY requirements)
11
+ # - Info: Test passed but with additional information to note
12
+ #
13
+ # Each state has its own character indicator, emoji, and color for easy visual identification
14
+ # in test output:
15
+ # - Success: "." (green) ✅
16
+ # - Warning: "W" (yellow) ⚠️
17
+ # - Info: "I" (blue) 💡
18
+ #
19
+ # @example Creating a successful test result
20
+ # result = Expresenter::Pass.new(
21
+ # actual: "foo",
22
+ # definition: 'eq "foo"',
23
+ # error: nil,
24
+ # got: true,
25
+ # negate: false,
26
+ # level: :MUST
27
+ # )
28
+ # result.success? # => true
29
+ # result.warning? # => false
30
+ # result.info? # => false
31
+ # result.to_sym # => :success
32
+ # result.char # => "."
33
+ # result.emoji # => "✅"
34
+ # result.to_s # => "Success: expected \"foo\" to eq \"foo\"."
35
+ #
36
+ # @example Creating a warning result
37
+ # result = Expresenter::Pass.new(
38
+ # actual: "foo",
39
+ # definition: 'eq "foo"',
40
+ # error: nil,
41
+ # got: false, # Warning state is triggered when got: false
42
+ # negate: false,
43
+ # level: :SHOULD
44
+ # )
45
+ # result.warning? # => true
46
+ # result.to_sym # => :warning
47
+ # result.char # => "W"
48
+ # result.emoji # => "⚠️"
7
49
  class Pass
8
- # Char representing an info.
50
+ # Single character indicator for informational results.
51
+ # @api private
9
52
  INFO_CHAR = "I"
10
53
 
11
- # Emoji representing an info.
54
+ # Emoji indicator for informational results.
55
+ # @api private
12
56
  INFO_EMOJI = "💡"
13
57
 
14
- # Char representing a success.
58
+ # Single character indicator for successful results.
59
+ # @api private
15
60
  SUCCESS_CHAR = "."
16
61
 
17
- # Emoji representing a success.
62
+ # Emoji indicator for successful results.
63
+ # @api private
18
64
  SUCCESS_EMOJI = "✅"
19
65
 
20
- # Char representing a warning.
66
+ # Single character indicator for warning results.
67
+ # @api private
21
68
  WARNING_CHAR = "W"
22
69
 
23
- # Emoji representing a warning.
70
+ # Emoji indicator for warning results.
71
+ # @api private
24
72
  WARNING_EMOJI = "⚠️"
25
73
 
26
74
  include Common
27
75
 
28
- # @param (see Pass#initialize)
29
- # @return [Pass] A passed spec instance.
76
+ # Creates a new Pass instance with the given details.
77
+ #
78
+ # @param details [Hash] Test result details (see #initialize for parameters)
79
+ # @return [Pass] A new Pass instance
80
+ # @example
81
+ # Expresenter::Pass.with(
82
+ # actual: "foo",
83
+ # definition: 'eq "foo"',
84
+ # error: nil,
85
+ # got: true,
86
+ # negate: false,
87
+ # level: :MUST
88
+ # )
30
89
  def self.with(**details)
31
90
  new(**details)
32
91
  end
33
92
 
34
93
  alias message to_s
35
94
 
36
- # Initialize method.
95
+ # Initializes a new Pass instance.
37
96
  #
38
- # @param actual [#object_id] Returned value by the challenged subject.
39
- # @param definition [String] A readable string of the matcher and any
40
- # expected values.
41
- # @param error [Exception, nil] Any possible raised exception.
42
- # @param expected [#object_id] The expected value.
43
- # @param got [Boolean, nil] The result of the boolean comparison
44
- # between the actual value and the expected value through the matcher.
45
- # @param negate [Boolean] Evaluated to a negative assertion?
46
- # @param level [:MUST, :SHOULD, :MAY] The requirement level.
47
- def initialize(actual:, definition:, error:, expected:, got:, negate:, level:)
97
+ # @param actual [#object_id] The actual value returned by the test
98
+ # @param definition [String] Human-readable description of the expectation
99
+ # @param error [Exception, nil] Any exception that occurred (for info states)
100
+ # @param got [Boolean, nil] Result of comparing actual vs expected values
101
+ # @param negate [Boolean] Whether this is a negative assertion
102
+ # @param level [:MUST, :SHOULD, :MAY] The requirement level of the test
103
+ def initialize(actual:, definition:, error:, got:, negate:, level:)
48
104
  @actual = actual
49
105
  @definition = definition
50
106
  @error = error
51
- @expected = expected
52
107
  @got = got
53
108
  @negate = negate
54
109
  @level = level
55
110
  end
56
111
 
57
- # Did the test fail?
112
+ # Always returns false since this class represents passed tests.
58
113
  #
59
- # @return [Boolean] The spec passed or failed?
114
+ # @return [false] Always returns false
60
115
  def failed?
61
116
  false
62
117
  end
63
118
 
64
- # The state of failure.
119
+ # Pass results are never failures.
65
120
  #
66
- # @return [Boolean] The test was a failure?
121
+ # @return [false] Always returns false
67
122
  def failure?
68
123
  false
69
124
  end
70
125
 
71
- # The state of info.
126
+ # Indicates if this is an informational result.
72
127
  #
73
- # @return [Boolean] The test was an info?
128
+ # @return [Boolean] true if an error was captured but the test still passed
74
129
  def info?
75
130
  !error.nil?
76
131
  end
77
132
 
78
- # The state of warning.
133
+ # Indicates if this is a warning result.
79
134
  #
80
- # @return [Boolean] The test was a warning?
135
+ # @return [Boolean] true if got equals false, indicating a non-critical issue
81
136
  def warning?
82
137
  got.equal?(false)
83
138
  end
84
139
 
85
- # Identify the state of the result.
140
+ # Returns the symbolic representation of the result state.
86
141
  #
87
- # @return [Symbol] The identifier of the state.
142
+ # @return [:success, :warning, :info] The type of pass result
88
143
  def to_sym
89
144
  if success?
90
145
  :success
@@ -95,9 +150,9 @@ module Expresenter
95
150
  end
96
151
  end
97
152
 
98
- # Express the result with one char.
153
+ # Returns a single character representing the result state.
99
154
  #
100
- # @return [String] The char that identify the result.
155
+ # @return [String] "." for success, "W" for warning, "I" for info
101
156
  def char
102
157
  if success?
103
158
  SUCCESS_CHAR
@@ -108,9 +163,9 @@ module Expresenter
108
163
  end
109
164
  end
110
165
 
111
- # Express the result with one emoji.
166
+ # Returns an emoji representing the result state.
112
167
  #
113
- # @return [String] The emoji that identify the result.
168
+ # @return [String] "✅" for success, "⚠️" for warning, "💡" for info
114
169
  def emoji
115
170
  if success?
116
171
  SUCCESS_EMOJI
@@ -123,6 +178,11 @@ module Expresenter
123
178
 
124
179
  protected
125
180
 
181
+ # Applies color formatting to the given string based on result state.
182
+ #
183
+ # @param str [String] The string to colorize
184
+ # @return [String] ANSI-colored string (green for success, yellow for warning, blue for info)
185
+ # @api private
126
186
  def color(str)
127
187
  if success?
128
188
  "\e[32m#{str}\e[0m" # green
data/lib/expresenter.rb CHANGED
@@ -2,15 +2,58 @@
2
2
 
3
3
  # Namespace for the Expresenter library.
4
4
  #
5
- # @example A passed expectation result presenter.
6
- # Expresenter.call(true).with(actual: "FOO", definition: 'eql "foo"', error: nil, expected: "foo", got: true, negate: true, level: :MUST) # => Expresenter::Pass(actual: "FOO", definition: "eql \"foo\"", error: nil, expected: "foo", got: true, negate: true, level: :MUST)
5
+ # The Expresenter library provides a flexible way to present test expectation results with rich
6
+ # formatting and requirement level support. It is designed to work with test frameworks and
7
+ # assertion libraries that need detailed result reporting.
8
+ #
9
+ # Each expectation result can be categorized as:
10
+ # - Success: The test passed as expected
11
+ # - Warning: A non-critical test failure (typically for :SHOULD or :MAY requirements)
12
+ # - Info: Additional information about the test
13
+ # - Failure: A critical test failure
14
+ # - Error: An unexpected error occurred during the test
15
+ #
16
+ # @example Creating a passing expectation result
17
+ # result = Expresenter.call(true).with(
18
+ # actual: "FOO",
19
+ # definition: 'eql "foo"',
20
+ # error: nil,
21
+ # got: true,
22
+ # negate: true,
23
+ # level: :MUST
24
+ # )
25
+ # result.passed? # => true
26
+ # result.to_s # => "Success: expected \"FOO\" not to eql \"foo\"."
27
+ #
28
+ # @example Creating a failing expectation result
29
+ # # This will raise an Expresenter::Fail exception
30
+ # Expresenter.call(false).with(
31
+ # actual: "foo",
32
+ # definition: "eq 42",
33
+ # error: Exception.new("Test failed"),
34
+ # got: false,
35
+ # negate: false,
36
+ # level: :MUST
37
+ # )
38
+ #
7
39
  module Expresenter
8
- # @param is_passed [Boolean] The value of an assertion.
40
+ # Factory method that returns the appropriate result class based on the assertion outcome.
41
+ #
42
+ # @param is_passed [Boolean] The value of the assertion. True indicates a passing test,
43
+ # false indicates a failing test.
44
+ #
45
+ # @return [Class<Pass>, Class<Fail>] Returns the Pass class for passing tests or
46
+ # the Fail class for failing tests. These classes can then be instantiated with
47
+ # detailed test information using #with.
48
+ #
49
+ # @example Getting a Pass class for a successful test
50
+ # result_class = Expresenter.call(true)
51
+ # result_class # => Expresenter::Pass
9
52
  #
10
- # @return [Class<Pass>, Class<Fail>] The class of the result.
53
+ # @example Getting a Fail class for a failed test
54
+ # result_class = Expresenter.call(false)
55
+ # result_class # => Expresenter::Fail
11
56
  #
12
- # @example Get the pass class result.
13
- # call(true) # => Pass
14
57
  def self.call(is_passed)
15
58
  is_passed ? Pass : Fail
16
59
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expresenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-25 00:00:00.000000000 Z
11
+ date: 2024-12-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Expectation result presenter.
14
14
  email: contact@cyril.email
@@ -27,7 +27,7 @@ licenses:
27
27
  - MIT
28
28
  metadata:
29
29
  rubygems_mfa_required: 'true'
30
- post_install_message:
30
+ post_install_message:
31
31
  rdoc_options: []
32
32
  require_paths:
33
33
  - lib
@@ -35,15 +35,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: 3.2.0
38
+ version: 3.1.0
39
39
  required_rubygems_version: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
43
  version: '0'
44
44
  requirements: []
45
- rubygems_version: 3.4.19
46
- signing_key:
45
+ rubygems_version: 3.3.27
46
+ signing_key:
47
47
  specification_version: 4
48
48
  summary: Expectation result presenter.
49
49
  test_files: []