expresenter 1.5.0 → 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: 0ddc8ef213f1b42ee370c0e8332886cc0c0a158197bda6eaba301f0a0edeae6f
4
- data.tar.gz: bee453e0190c6ce4e6a03955ad586af392ad424d2338196618ddcc608137d7ab
3
+ metadata.gz: 6701ed87c0d50891bb8804abc202aa3a2579d34f658b2d8b59f2b7caec76228a
4
+ data.tar.gz: f9d618cddd0aeacb36667da36816a759ff768aadb3e0f401f893baecf1306a2c
5
5
  SHA512:
6
- metadata.gz: 3589fcd607346650908c5c95f19eafe8a30813ccc869f2dde6c155112b3a9bbd64053d872f2de2c32eecc7ac520348043d9dd103bb9149c948dd76a5ad7856eb
7
- data.tar.gz: bbb3b0eaa9173024a9df1d6806bb243597fcfa1b7d7e7c96e68e70f5532bfa92eaf44d8b58b4cde56810ac6fed993775805677e52f17df474790de5f138b9600
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,20 +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
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.
10
8
 
11
9
  ## Features
12
10
 
13
- - Rich result formatting with colored output
14
- - Support for MUST/SHOULD/MAY requirement levels
15
- - Multiple result classification: success, warning, info, failure, and error
16
- - Emoji support for visual result indication (✅, ⚠️, 💡, ❌, 💥)
17
- - Flexible negation support for negative assertions
18
- - Detailed error reporting with custom messages
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
19
26
 
20
27
  ## Installation
21
28
 
@@ -39,133 +46,128 @@ gem install expresenter
39
46
 
40
47
  ## Usage
41
48
 
42
- Assuming that an expectation is an assertion that is either `true` or `false`,
43
- qualifying it with `MUST`, `SHOULD` and `MAY`, we can draw up several scenarios:
44
-
45
- | Requirement levels | **MUST** | **SHOULD** | **MAY** |
46
- | ------------------------- | -------- | ---------- | ------- |
47
- | Implemented & Matched | `true` | `true` | `true` |
48
- | Implemented & Not matched | `false` | `true` | `false` |
49
- | Implemented & Exception | `false` | `false` | `false` |
50
- | Not implemented | `false` | `false` | `true` |
51
-
52
- Then,
53
-
54
- * for a `true` assertion, a `Expresenter::Pass` instance can be returned;
55
- * for a `false` assertion, a `Expresenter::Fail` exception can be raised.
56
-
57
- Both class share a same `Common` interface.
58
-
59
- Passed expectations can be classified as:
60
-
61
- * ✅ success
62
- * ⚠️ warning
63
- * 💡 info
64
-
65
- Failed expectations can be classified as:
66
-
67
- * ❌ failure
68
- * 💥 error
69
-
70
- ### Instantiation
71
-
72
- The following parameters are required to instantiate the result:
73
-
74
- * `actual`: Returned value by the challenged subject.
75
- * `definition`: A readable string of the matcher and any expected values.
76
- * `error`: Any possible raised exception.
77
- * `got`: The result of the boolean comparison between the actual value and the expected value through the matcher.
78
- * `negate`: Evaluated to a negative assertion?
79
- * `level`: The requirement level (`:MUST`, `:SHOULD` or `:MAY`).
80
-
81
- #### Examples
82
-
83
- A passed expectation:
49
+ ### Basic Example
84
50
 
85
51
  ```ruby
86
- result = Expresenter.call(true).new(actual: "FOO", definition: 'eq "foo"', error: nil, got: true, negate: true, level: :MUST)
87
-
88
- result.failed? # => false
89
- result.failure? # => false
90
- result.info? # => false
91
- result.warning? # => false
92
- result.to_sym # => :success
93
- result.char # => "."
94
- result.emoji # => "✅"
95
- result.passed? # => true
96
- result.negate? # => true
97
- result.error? # => false
98
- result.success? # => true
99
- result.definition # => "eq \"foo\""
100
- result.summary # => "expected \"FOO\" not to eq \"foo\""
101
- result.colored_char # => "\e[32m.\e[0m"
102
- result.colored_string # => "\e[32m\e[1mSuccess\e[22m: expected \"FOO\" not to eq \"foo\".\e[0m"
103
- result.message # => "Success: expected \"FOO\" not to eq \"foo\"."
104
- result.to_s # => "Success: expected \"FOO\" not to eq \"foo\"."
105
- 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".'
106
67
  ```
107
68
 
108
- A failed expectation:
69
+ ### Handling Different Result Types
109
70
 
110
71
  ```ruby
111
- result = Expresenter.call(false).new(actual: "foo", definition: "eq 42", error: Exception.new("BOOM"), got: true, negate: true, level: :MUST)
112
-
113
- result.failed? # => true
114
- result.failure? # => false
115
- result.info? # => false
116
- result.warning? # => false
117
- result.to_sym # => :error
118
- result.char # => "E"
119
- result.emoji # => "💥"
120
- result.passed? # => false
121
- result.negate? # => true
122
- result.error? # => true
123
- result.success? # => true
124
- result.definition # => "eq 42"
125
- result.summary # => "BOOM"
126
- result.colored_char # => "\e[31mE\e[0m"
127
- result.colored_string # => "\e[31m\e[1mException\e[22m: BOOM.\e[0m"
128
- result.message # => "Exception: BOOM."
129
- result.to_s # => "Exception: BOOM."
130
- 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
131
102
  ```
132
103
 
133
- ### Return or Raise
104
+ ### Using Requirement Levels
134
105
 
135
- 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:
136
107
 
137
- 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
138
111
 
139
112
  ```ruby
140
- Expresenter.call(true).with(actual: "FOO", definition: 'eq "foo"', error: nil, got: true, negate: true, level: :MUST) # => Expresenter::Pass(actual: "FOO", definition: "eq \"foo\"", error: nil, 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
141
124
  ```
142
125
 
143
- In this example, the result fails, so the exception is raised:
126
+ ### Working with Negative Assertions
144
127
 
145
128
  ```ruby
146
- Expresenter.call(false).with(actual: "foo", definition: "eq 40", error: Exception.new("BOOM"), 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".'
147
141
  ```
148
142
 
149
- > Traceback (most recent call last):
150
- > 4: from ./bin/console:7:in `<main>'
151
- > 3: from (irb):42
152
- > 2: from (irb):43:in `rescue in irb_binding'
153
- > 1: from /Users/cyril/github/fixrb/expresenter/lib/expresenter/fail.rb:25:in `with'
154
- > 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.
155
146
 
156
- ### More Examples
147
+ Example integration with a test framework:
157
148
 
158
- A full list of unit tests can be viewed (and executed) here:
159
- [./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
+ ```
160
163
 
161
- ## Contact
164
+ ## Development
162
165
 
163
- * Home page: https://github.com/fixrb/expresenter
164
- * 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.
165
167
 
166
- ## Versioning
168
+ ## Contributing
167
169
 
168
- __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.
169
171
 
170
172
  ## License
171
173
 
@@ -1,58 +1,126 @@
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 result of the boolean comparison between the
19
- # 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
20
64
  attr_reader :got
21
65
 
22
- # @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
23
72
  attr_reader :level
24
73
 
25
- # Did the test pass?
74
+ # Checks if the test passed.
26
75
  #
27
- # @return [Boolean] The spec passed or failed?
76
+ # @return [Boolean] true if the test passed, false otherwise.
77
+ # @example
78
+ # result.passed? # => true
28
79
  def passed?
29
80
  !failed?
30
81
  end
31
82
 
32
- # The value of the negate instance variable.
83
+ # Checks if this is a negative assertion.
33
84
  #
34
- # @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
35
89
  def negate?
36
90
  @negate
37
91
  end
38
92
 
39
- # The state of error.
93
+ # Checks if an error occurred during the test.
40
94
  #
41
- # @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
42
103
  def error?
43
104
  !error.nil?
44
105
  end
45
106
 
46
- # The state of success.
107
+ # Checks if the test was successful.
47
108
  #
48
- # @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
49
113
  def success?
50
114
  got.equal?(true)
51
115
  end
52
116
 
53
- # The summary of the result.
117
+ # Generates a human-readable summary of the test result.
54
118
  #
55
- # @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"
56
124
  def summary
57
125
  if error?
58
126
  error.message
@@ -63,30 +131,40 @@ module Expresenter
63
131
  end
64
132
  end
65
133
 
66
- # Express the result with one colored char.
134
+ # Returns the result indicator with ANSI color codes.
67
135
  #
68
- # @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"
69
139
  def colored_char
70
140
  color(char)
71
141
  end
72
142
 
73
- # The colored string representation of the result.
143
+ # Returns the full result message with ANSI color codes.
74
144
  #
75
- # @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"
76
148
  def colored_string
77
149
  color(to_bold_s)
78
150
  end
79
151
 
80
- # The representation of the result.
152
+ # Returns the complete result message.
81
153
  #
82
- # @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."
83
157
  def to_s
84
158
  "#{titre}: #{summary}."
85
159
  end
86
160
 
87
- # Titre for the result.
161
+ # Returns the title for the result type.
88
162
  #
89
- # @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"
90
168
  def titre
91
169
  if error?
92
170
  error.class.name
@@ -97,16 +175,18 @@ module Expresenter
97
175
 
98
176
  protected
99
177
 
100
- # The negation, if any.
178
+ # Returns the negation word if this is a negative assertion.
101
179
  #
102
- # @return [String, nil] The negation, or an empty string.
180
+ # @return [String, nil] Returns "not" for negative assertions, nil otherwise.
181
+ # @api private
103
182
  def negation
104
183
  "not" if negate?
105
184
  end
106
185
 
107
- # The representation of the result with the title in bold.
186
+ # Returns the result message with a bold title.
108
187
  #
109
- # @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
110
190
  def to_bold_s
111
191
  "\e[1m#{titre}\e[22m: #{summary}."
112
192
  end
@@ -3,38 +3,105 @@
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 got [Boolean, nil] The result of the boolean comparison
35
- # between the actual value and the expected value through the matcher.
36
- # @param negate [Boolean] Evaluated to a negative assertion?
37
- # @param level [:MUST, :SHOULD, :MAY] The requirement 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
+ # )
38
105
  def initialize(actual:, definition:, error:, got:, negate:, level:)
39
106
  @actual = actual
40
107
  @definition = definition
@@ -46,37 +113,37 @@ module Expresenter
46
113
  super(to_s)
47
114
  end
48
115
 
49
- # Did the test fail?
116
+ # Always returns true since this class represents failed tests.
50
117
  #
51
- # @return [Boolean] The spec passed or failed?
118
+ # @return [true] Always returns true
52
119
  def failed?
53
120
  true
54
121
  end
55
122
 
56
- # The state of failure.
123
+ # Indicates if this is a regular failure (not an error).
57
124
  #
58
- # @return [Boolean] The test was a failure?
125
+ # @return [Boolean] true if no error was captured, false otherwise
59
126
  def failure?
60
127
  !error?
61
128
  end
62
129
 
63
- # The state of info.
130
+ # Fail results are never informational.
64
131
  #
65
- # @return [Boolean] The test was an info?
132
+ # @return [false] Always returns false
66
133
  def info?
67
134
  false
68
135
  end
69
136
 
70
- # The state of warning.
137
+ # Fail results are never warnings.
71
138
  #
72
- # @return [Boolean] The test was a warning?
139
+ # @return [false] Always returns false
73
140
  def warning?
74
141
  false
75
142
  end
76
143
 
77
- # Identify the state of the result.
144
+ # Returns the symbolic representation of the failure type.
78
145
  #
79
- # @return [Symbol] The identifier of the state.
146
+ # @return [:failure, :error] :failure for regular failures, :error when an exception occurred
80
147
  def to_sym
81
148
  if failure?
82
149
  :failure
@@ -85,9 +152,9 @@ module Expresenter
85
152
  end
86
153
  end
87
154
 
88
- # Express the result with one char.
155
+ # Returns a single character representing the failure type.
89
156
  #
90
- # @return [String] The char that identify the result.
157
+ # @return [String] "F" for failures, "E" for errors
91
158
  def char
92
159
  if failure?
93
160
  FAILURE_CHAR
@@ -96,9 +163,9 @@ module Expresenter
96
163
  end
97
164
  end
98
165
 
99
- # Express the result with one emoji.
166
+ # Returns an emoji representing the failure type.
100
167
  #
101
- # @return [String] The emoji that identify the result.
168
+ # @return [String] "❌" for failures, "💥" for errors
102
169
  def emoji
103
170
  if failure?
104
171
  FAILURE_EMOJI
@@ -109,6 +176,11 @@ module Expresenter
109
176
 
110
177
  protected
111
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
112
184
  def color(str)
113
185
  if failure?
114
186
  "\e[35m#{str}\e[0m" # purple
@@ -3,46 +3,103 @@
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 got [Boolean, nil] The result of the boolean comparison
43
- # between the actual value and the expected value through the matcher.
44
- # @param negate [Boolean] Evaluated to a negative assertion?
45
- # @param level [:MUST, :SHOULD, :MAY] The requirement 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
46
103
  def initialize(actual:, definition:, error:, got:, negate:, level:)
47
104
  @actual = actual
48
105
  @definition = definition
@@ -52,37 +109,37 @@ module Expresenter
52
109
  @level = level
53
110
  end
54
111
 
55
- # Did the test fail?
112
+ # Always returns false since this class represents passed tests.
56
113
  #
57
- # @return [Boolean] The spec passed or failed?
114
+ # @return [false] Always returns false
58
115
  def failed?
59
116
  false
60
117
  end
61
118
 
62
- # The state of failure.
119
+ # Pass results are never failures.
63
120
  #
64
- # @return [Boolean] The test was a failure?
121
+ # @return [false] Always returns false
65
122
  def failure?
66
123
  false
67
124
  end
68
125
 
69
- # The state of info.
126
+ # Indicates if this is an informational result.
70
127
  #
71
- # @return [Boolean] The test was an info?
128
+ # @return [Boolean] true if an error was captured but the test still passed
72
129
  def info?
73
130
  !error.nil?
74
131
  end
75
132
 
76
- # The state of warning.
133
+ # Indicates if this is a warning result.
77
134
  #
78
- # @return [Boolean] The test was a warning?
135
+ # @return [Boolean] true if got equals false, indicating a non-critical issue
79
136
  def warning?
80
137
  got.equal?(false)
81
138
  end
82
139
 
83
- # Identify the state of the result.
140
+ # Returns the symbolic representation of the result state.
84
141
  #
85
- # @return [Symbol] The identifier of the state.
142
+ # @return [:success, :warning, :info] The type of pass result
86
143
  def to_sym
87
144
  if success?
88
145
  :success
@@ -93,9 +150,9 @@ module Expresenter
93
150
  end
94
151
  end
95
152
 
96
- # Express the result with one char.
153
+ # Returns a single character representing the result state.
97
154
  #
98
- # @return [String] The char that identify the result.
155
+ # @return [String] "." for success, "W" for warning, "I" for info
99
156
  def char
100
157
  if success?
101
158
  SUCCESS_CHAR
@@ -106,9 +163,9 @@ module Expresenter
106
163
  end
107
164
  end
108
165
 
109
- # Express the result with one emoji.
166
+ # Returns an emoji representing the result state.
110
167
  #
111
- # @return [String] The emoji that identify the result.
168
+ # @return [String] "✅" for success, "⚠️" for warning, "💡" for info
112
169
  def emoji
113
170
  if success?
114
171
  SUCCESS_EMOJI
@@ -121,6 +178,11 @@ module Expresenter
121
178
 
122
179
  protected
123
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
124
186
  def color(str)
125
187
  if success?
126
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, got: true, negate: true, level: :MUST) # => Expresenter::Pass(actual: "FOO", definition: "eql \"foo\"", error: nil, 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,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expresenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2024-12-29 00:00:00.000000000 Z
11
+ date: 2024-12-31 00:00:00.000000000 Z
11
12
  dependencies: []
12
13
  description: Expectation result presenter.
13
14
  email: contact@cyril.email
@@ -26,6 +27,7 @@ licenses:
26
27
  - MIT
27
28
  metadata:
28
29
  rubygems_mfa_required: 'true'
30
+ post_install_message:
29
31
  rdoc_options: []
30
32
  require_paths:
31
33
  - lib
@@ -33,14 +35,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
33
35
  requirements:
34
36
  - - ">="
35
37
  - !ruby/object:Gem::Version
36
- version: 3.2.0
38
+ version: 3.1.0
37
39
  required_rubygems_version: !ruby/object:Gem::Requirement
38
40
  requirements:
39
41
  - - ">="
40
42
  - !ruby/object:Gem::Version
41
43
  version: '0'
42
44
  requirements: []
43
- rubygems_version: 3.6.2
45
+ rubygems_version: 3.3.27
46
+ signing_key:
44
47
  specification_version: 4
45
48
  summary: Expectation result presenter.
46
49
  test_files: []