expresenter 1.4.1 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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: []