rspec-tap-formatters 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE.md +23 -0
- data/README.md +127 -0
- data/lib/rspec/tap/formatters.rb +14 -0
- data/lib/rspec/tap/formatters/compact.rb +168 -0
- data/lib/rspec/tap/formatters/core_ext/hash.rb +52 -0
- data/lib/rspec/tap/formatters/core_ext/string.rb +48 -0
- data/lib/rspec/tap/formatters/default.rb +169 -0
- data/lib/rspec/tap/formatters/flat.rb +139 -0
- data/lib/rspec/tap/formatters/flat_compact.rb +138 -0
- data/lib/rspec/tap/formatters/printer.rb +445 -0
- data/lib/rspec/tap/formatters/test_stats.rb +50 -0
- data/lib/rspec/tap/formatters/version.rb +16 -0
- data/spec/rspec/tap/formatters/compact_spec.rb +399 -0
- data/spec/rspec/tap/formatters/default_spec.rb +407 -0
- data/spec/rspec/tap/formatters/flat_compact_spec.rb +257 -0
- data/spec/rspec/tap/formatters/flat_spec.rb +266 -0
- data/spec/rspec/tap/formatters/printer_spec.rb +1075 -0
- data/spec/rspec/tap/formatters/test_stats_spec.rb +92 -0
- metadata +138 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/core/formatters/base_formatter'
|
4
|
+
require_relative 'printer'
|
5
|
+
require_relative 'test_stats'
|
6
|
+
|
7
|
+
module RSpec
|
8
|
+
module TAP
|
9
|
+
module Formatters
|
10
|
+
# Default TAP formatter
|
11
|
+
class Default < RSpec::Core::Formatters::BaseFormatter
|
12
|
+
# List of subscribed notifications
|
13
|
+
NOTIFICATIONS = %i[
|
14
|
+
seed
|
15
|
+
start
|
16
|
+
start_dump
|
17
|
+
example_group_started
|
18
|
+
example_group_finished
|
19
|
+
example_started
|
20
|
+
example_passed
|
21
|
+
example_failed
|
22
|
+
example_pending
|
23
|
+
message
|
24
|
+
dump_failures
|
25
|
+
dump_pending
|
26
|
+
dump_summary
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
RSpec::Core::Formatters.register(self, *NOTIFICATIONS)
|
30
|
+
|
31
|
+
# Constructor
|
32
|
+
#
|
33
|
+
# @param output [StringIO, File] output stream
|
34
|
+
def initialize(output)
|
35
|
+
super
|
36
|
+
|
37
|
+
@printer = Printer.new(output)
|
38
|
+
@test_stats = TestStats.new
|
39
|
+
@seed = nil
|
40
|
+
@level = 0
|
41
|
+
@example_number = 0
|
42
|
+
end
|
43
|
+
|
44
|
+
# Seed notification
|
45
|
+
#
|
46
|
+
# @param notification [SeedNotification]
|
47
|
+
def seed(notification)
|
48
|
+
@seed = notification.seed if notification.seed_used?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Start notification
|
52
|
+
#
|
53
|
+
# @param notification [StartNotification]
|
54
|
+
def start(notification)
|
55
|
+
super
|
56
|
+
|
57
|
+
@printer.start_output
|
58
|
+
end
|
59
|
+
|
60
|
+
# Execution finished notification
|
61
|
+
#
|
62
|
+
# @param _notification [NullNotification]
|
63
|
+
def start_dump(_notification)
|
64
|
+
@printer.example_progress_dump
|
65
|
+
end
|
66
|
+
|
67
|
+
# Example group start notification
|
68
|
+
#
|
69
|
+
# @param notification [GroupNotification]
|
70
|
+
def example_group_started(notification)
|
71
|
+
@printer.group_start_output(notification, @level)
|
72
|
+
|
73
|
+
@level += 1
|
74
|
+
@example_number = 0
|
75
|
+
end
|
76
|
+
|
77
|
+
# Example group finish notification
|
78
|
+
#
|
79
|
+
# @param notification [GroupNotification]
|
80
|
+
def example_group_finished(notification)
|
81
|
+
@printer.group_finished_output(
|
82
|
+
@test_stats.data[notification.group.metadata[:line_number]],
|
83
|
+
@level
|
84
|
+
)
|
85
|
+
|
86
|
+
@level -= 1 if @level.positive?
|
87
|
+
@test_stats = TestStats.new if @level.zero?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Example start notification
|
91
|
+
#
|
92
|
+
# @param _notification [ExampleNotification]
|
93
|
+
def example_started(_notification)
|
94
|
+
@example_number += 1
|
95
|
+
end
|
96
|
+
|
97
|
+
# Passing example notification
|
98
|
+
#
|
99
|
+
# @param notification [ExampleNotification]
|
100
|
+
def example_passed(notification)
|
101
|
+
@test_stats.populate(notification, 1)
|
102
|
+
@printer.example_progress_output(:success)
|
103
|
+
@printer.success_output(
|
104
|
+
notification.example.description.strip,
|
105
|
+
@example_number,
|
106
|
+
@level
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Failing example notification
|
111
|
+
#
|
112
|
+
# @param notification [FailedExampleNotification]
|
113
|
+
def example_failed(notification)
|
114
|
+
@test_stats.populate(notification, 2)
|
115
|
+
@printer.example_progress_output(:failure)
|
116
|
+
@printer.failure_output(
|
117
|
+
notification.example.description.strip,
|
118
|
+
@example_number,
|
119
|
+
@level
|
120
|
+
)
|
121
|
+
@printer.failure_reason_output(notification, @level + 1)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Pending example notification
|
125
|
+
#
|
126
|
+
# @param notification [PendingExampleFailedAsExpectedNotification
|
127
|
+
# , SkippedExampleException]
|
128
|
+
def example_pending(notification)
|
129
|
+
@test_stats.populate(notification, 3)
|
130
|
+
@printer.example_progress_output(:pending)
|
131
|
+
@printer.pending_output(
|
132
|
+
notification,
|
133
|
+
notification.example.description.strip,
|
134
|
+
@example_number,
|
135
|
+
@level
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Failure outside of example notification
|
140
|
+
#
|
141
|
+
# @param notification [MessageNotification]
|
142
|
+
def message(notification)
|
143
|
+
@printer.message_output(notification)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Failure examples notification
|
147
|
+
#
|
148
|
+
# @param notification [ExamplesNotification]
|
149
|
+
def dump_failures(notification)
|
150
|
+
@printer.store_failed_examples_summary(notification)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Pending examples notification
|
154
|
+
#
|
155
|
+
# @param notification [ExamplesNotification]
|
156
|
+
def dump_pending(notification)
|
157
|
+
@printer.store_pending_examples_summary(notification)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Examples summary notification
|
161
|
+
#
|
162
|
+
# @param notification [SummaryNotification]
|
163
|
+
def dump_summary(notification)
|
164
|
+
@printer.summary_output(notification, @seed)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/core/formatters/base_formatter'
|
4
|
+
require_relative 'printer'
|
5
|
+
require_relative 'test_stats'
|
6
|
+
|
7
|
+
module RSpec
|
8
|
+
module TAP
|
9
|
+
module Formatters
|
10
|
+
# Flat TAP formatter
|
11
|
+
class Flat < RSpec::Core::Formatters::BaseFormatter
|
12
|
+
# List of subscribed notifications
|
13
|
+
NOTIFICATIONS = %i[
|
14
|
+
seed
|
15
|
+
start
|
16
|
+
start_dump
|
17
|
+
example_started
|
18
|
+
example_passed
|
19
|
+
example_failed
|
20
|
+
example_pending
|
21
|
+
message
|
22
|
+
dump_failures
|
23
|
+
dump_pending
|
24
|
+
dump_summary
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
RSpec::Core::Formatters.register(self, *NOTIFICATIONS)
|
28
|
+
|
29
|
+
# Constructor
|
30
|
+
#
|
31
|
+
# @param output [StringIO, File] output stream
|
32
|
+
def initialize(output)
|
33
|
+
super
|
34
|
+
|
35
|
+
@printer = Printer.new(output)
|
36
|
+
@seed = nil
|
37
|
+
@example_number = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
# Seed notification
|
41
|
+
#
|
42
|
+
# @param notification [SeedNotification]
|
43
|
+
def seed(notification)
|
44
|
+
@seed = notification.seed if notification.seed_used?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Start notification
|
48
|
+
#
|
49
|
+
# @param notification [StartNotification]
|
50
|
+
def start(notification)
|
51
|
+
super
|
52
|
+
|
53
|
+
@printer.start_output
|
54
|
+
end
|
55
|
+
|
56
|
+
# Execution finished notification
|
57
|
+
#
|
58
|
+
# @param _notification [NullNotification]
|
59
|
+
def start_dump(_notification)
|
60
|
+
@printer.example_progress_dump
|
61
|
+
end
|
62
|
+
|
63
|
+
# Example start notification
|
64
|
+
#
|
65
|
+
# @param _notification [ExampleNotification]
|
66
|
+
def example_started(_notification)
|
67
|
+
@example_number += 1
|
68
|
+
end
|
69
|
+
|
70
|
+
# Passing example notification
|
71
|
+
#
|
72
|
+
# @param notification [ExampleNotification]
|
73
|
+
def example_passed(notification)
|
74
|
+
@printer.example_progress_output(:success)
|
75
|
+
@printer.success_output(
|
76
|
+
notification.example.full_description.strip,
|
77
|
+
@example_number,
|
78
|
+
0
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Failing example notification
|
83
|
+
#
|
84
|
+
# @param notification [FailedExampleNotification]
|
85
|
+
def example_failed(notification)
|
86
|
+
@printer.example_progress_output(:failure)
|
87
|
+
@printer.failure_output(
|
88
|
+
notification.example.full_description.strip,
|
89
|
+
@example_number,
|
90
|
+
0
|
91
|
+
)
|
92
|
+
@printer.failure_reason_output(notification, 1)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Pending example notification
|
96
|
+
#
|
97
|
+
# @param notification [PendingExampleFailedAsExpectedNotification
|
98
|
+
# , SkippedExampleException]
|
99
|
+
def example_pending(notification)
|
100
|
+
@printer.example_progress_output(:pending)
|
101
|
+
@printer.pending_output(
|
102
|
+
notification,
|
103
|
+
notification.example.full_description.strip,
|
104
|
+
@example_number,
|
105
|
+
0
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Failure outside of example notification
|
110
|
+
#
|
111
|
+
# @param notification [MessageNotification]
|
112
|
+
def message(notification)
|
113
|
+
@printer.message_output(notification)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Failure examples notification
|
117
|
+
#
|
118
|
+
# @param notification [ExamplesNotification]
|
119
|
+
def dump_failures(notification)
|
120
|
+
@printer.store_failed_examples_summary(notification)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Pending examples notification
|
124
|
+
#
|
125
|
+
# @param notification [ExamplesNotification]
|
126
|
+
def dump_pending(notification)
|
127
|
+
@printer.store_pending_examples_summary(notification)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Examples summary notification
|
131
|
+
#
|
132
|
+
# @param notification [SummaryNotification]
|
133
|
+
def dump_summary(notification)
|
134
|
+
@printer.summary_output(notification, @seed)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/core/formatters/base_formatter'
|
4
|
+
require_relative 'printer'
|
5
|
+
require_relative 'test_stats'
|
6
|
+
|
7
|
+
module RSpec
|
8
|
+
module TAP
|
9
|
+
module Formatters
|
10
|
+
# Flat compact TAP formatter
|
11
|
+
class FlatCompact < RSpec::Core::Formatters::BaseFormatter
|
12
|
+
# List of subscribed notifications
|
13
|
+
NOTIFICATIONS = %i[
|
14
|
+
seed
|
15
|
+
start
|
16
|
+
start_dump
|
17
|
+
example_started
|
18
|
+
example_passed
|
19
|
+
example_failed
|
20
|
+
example_pending
|
21
|
+
message
|
22
|
+
dump_failures
|
23
|
+
dump_pending
|
24
|
+
dump_summary
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
RSpec::Core::Formatters.register(self, *NOTIFICATIONS)
|
28
|
+
|
29
|
+
# Constructor
|
30
|
+
#
|
31
|
+
# @param output [StringIO, File] output stream
|
32
|
+
def initialize(output)
|
33
|
+
super
|
34
|
+
|
35
|
+
@printer = Printer.new(output)
|
36
|
+
@seed = nil
|
37
|
+
@example_number = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
# Seed notification
|
41
|
+
#
|
42
|
+
# @param notification [SeedNotification]
|
43
|
+
def seed(notification)
|
44
|
+
@seed = notification.seed if notification.seed_used?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Start notification
|
48
|
+
#
|
49
|
+
# @param notification [StartNotification]
|
50
|
+
def start(notification)
|
51
|
+
super
|
52
|
+
|
53
|
+
@printer.start_output
|
54
|
+
end
|
55
|
+
|
56
|
+
# Execution finished notification
|
57
|
+
#
|
58
|
+
# @param _notification [NullNotification]
|
59
|
+
def start_dump(_notification)
|
60
|
+
@printer.example_progress_dump
|
61
|
+
end
|
62
|
+
|
63
|
+
# Example start notification
|
64
|
+
#
|
65
|
+
# @param _notification [ExampleNotification]
|
66
|
+
def example_started(_notification)
|
67
|
+
@example_number += 1
|
68
|
+
end
|
69
|
+
|
70
|
+
# Passing example notification
|
71
|
+
#
|
72
|
+
# @param notification [ExampleNotification]
|
73
|
+
def example_passed(notification)
|
74
|
+
@printer.example_progress_output(:success)
|
75
|
+
@printer.success_output(
|
76
|
+
notification.example.full_description.strip,
|
77
|
+
@example_number,
|
78
|
+
0
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Failing example notification
|
83
|
+
#
|
84
|
+
# @param notification [FailedExampleNotification]
|
85
|
+
def example_failed(notification)
|
86
|
+
@printer.example_progress_output(:failure)
|
87
|
+
@printer.failure_output(
|
88
|
+
notification.example.full_description.strip,
|
89
|
+
@example_number,
|
90
|
+
0
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Pending example notification
|
95
|
+
#
|
96
|
+
# @param notification [PendingExampleFailedAsExpectedNotification
|
97
|
+
# , SkippedExampleException]
|
98
|
+
def example_pending(notification)
|
99
|
+
@printer.example_progress_output(:pending)
|
100
|
+
@printer.pending_output(
|
101
|
+
notification,
|
102
|
+
notification.example.full_description.strip,
|
103
|
+
@example_number,
|
104
|
+
0
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Failure outside of example notification
|
109
|
+
#
|
110
|
+
# @param notification [MessageNotification]
|
111
|
+
def message(notification)
|
112
|
+
@printer.message_output(notification)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Failure examples notification
|
116
|
+
#
|
117
|
+
# @param notification [ExamplesNotification]
|
118
|
+
def dump_failures(notification)
|
119
|
+
@printer.store_failed_examples_summary(notification)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Pending examples notification
|
123
|
+
#
|
124
|
+
# @param notification [ExamplesNotification]
|
125
|
+
def dump_pending(notification)
|
126
|
+
@printer.store_pending_examples_summary(notification)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Examples summary notification
|
130
|
+
#
|
131
|
+
# @param notification [SummaryNotification]
|
132
|
+
def dump_summary(notification)
|
133
|
+
@printer.summary_output(notification, @seed)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,445 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/core/formatters/console_codes'
|
4
|
+
require_relative 'core_ext/hash'
|
5
|
+
require_relative 'core_ext/string'
|
6
|
+
|
7
|
+
module RSpec
|
8
|
+
module TAP
|
9
|
+
module Formatters
|
10
|
+
# TAP report printer
|
11
|
+
class Printer
|
12
|
+
# Example status progress report characters
|
13
|
+
EXAMPLE_PROGRESS = {
|
14
|
+
success: '.',
|
15
|
+
failure: 'F',
|
16
|
+
pending: '*'
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
# Constructor
|
20
|
+
#
|
21
|
+
# @param output [StringIO, File] output stream
|
22
|
+
def initialize(output)
|
23
|
+
@output = output
|
24
|
+
@write_to_file = output.is_a?(File)
|
25
|
+
@display_colors = !@write_to_file
|
26
|
+
@force_colors = false
|
27
|
+
|
28
|
+
@bailed_out = false
|
29
|
+
|
30
|
+
@failed_examples = ''
|
31
|
+
@pending_examples = ''
|
32
|
+
end
|
33
|
+
|
34
|
+
# Handler for formatter +start+ notification.
|
35
|
+
def start_output
|
36
|
+
return if @bailed_out
|
37
|
+
|
38
|
+
@output.puts('TAP version 13')
|
39
|
+
end
|
40
|
+
|
41
|
+
# Handler for formatter +example_group_started+ notification.
|
42
|
+
#
|
43
|
+
# @param notification [ExampleNotification] example notification
|
44
|
+
# @param padding [Integer] indentation width
|
45
|
+
def group_start_output(notification, padding)
|
46
|
+
description = notification.group.description.strip
|
47
|
+
|
48
|
+
line =
|
49
|
+
if padding.zero?
|
50
|
+
"#{indentation(padding)}# test: #{description} {"
|
51
|
+
else
|
52
|
+
"#{indentation(padding)}# group: #{description} {"
|
53
|
+
end
|
54
|
+
|
55
|
+
@output.puts(colored_line(line, :detail))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handler for formatter +example_group_finished+ notification.
|
59
|
+
#
|
60
|
+
# @param test_stats [Array<Integer>] stats for the example group
|
61
|
+
# @param padding [Integer] indentation width
|
62
|
+
#
|
63
|
+
# @see stats_output
|
64
|
+
def group_finished_output(test_stats, padding)
|
65
|
+
@output.puts("#{indentation(padding)}1..#{test_stats[0]}")
|
66
|
+
stats_output(test_stats, padding)
|
67
|
+
@output.puts(colored_line("#{indentation(padding - 1)}}", :detail))
|
68
|
+
end
|
69
|
+
|
70
|
+
# Prints example progress when writing to a file.
|
71
|
+
# +.+ for passing, +F+ for failing, and `*` for pending example.
|
72
|
+
#
|
73
|
+
# @param status [Symbol] example status
|
74
|
+
def example_progress_output(status)
|
75
|
+
return unless @write_to_file
|
76
|
+
|
77
|
+
@force_colors = RSpec.configuration.color_enabled?
|
78
|
+
|
79
|
+
$stdout.print(colored_line(EXAMPLE_PROGRESS[status], status))
|
80
|
+
|
81
|
+
@force_colors = false
|
82
|
+
end
|
83
|
+
|
84
|
+
# Handler for formatter +start_dump+ notification.
|
85
|
+
def example_progress_dump
|
86
|
+
$stdout.puts if @write_to_file
|
87
|
+
end
|
88
|
+
|
89
|
+
# Handler for formatter +example_passed+ notification.
|
90
|
+
#
|
91
|
+
# @param description [String] example description
|
92
|
+
# @param example_number [Integer] example number
|
93
|
+
# @param padding [Integer] indentation width
|
94
|
+
def success_output(description, example_number, padding)
|
95
|
+
line = "ok #{example_number} - #{description}"
|
96
|
+
line = colored_line("#{indentation(padding)}#{line}", :success)
|
97
|
+
|
98
|
+
@output.puts(line)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Handler for formatter +example_failed+ notification.
|
102
|
+
#
|
103
|
+
# @param description [String] example description
|
104
|
+
# @param example_number [Integer] example number
|
105
|
+
# @param padding [Integer] indentation width
|
106
|
+
def failure_output(description, example_number, padding)
|
107
|
+
line = "not ok #{example_number} - #{description}"
|
108
|
+
line = colored_line("#{indentation(padding)}#{line}", :failure)
|
109
|
+
|
110
|
+
@output.puts(line)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Prints failure reason YAML block
|
114
|
+
# The aggregate failures are not reported for RSpec version
|
115
|
+
# before +3.3.0+.
|
116
|
+
#
|
117
|
+
# @param notification [ExampleNotification] example notification
|
118
|
+
# @param padding [Integer] indentation width
|
119
|
+
def failure_reason_output(notification, padding)
|
120
|
+
rspec_version = Gem::Version.new(RSpec::Core::Version::STRING)
|
121
|
+
reason =
|
122
|
+
if rspec_version >= Gem::Version.new('3.3.0')
|
123
|
+
failure_reason_for_and_post_3_3_0(notification)
|
124
|
+
else
|
125
|
+
failure_reason_pre_3_3_0(notification)
|
126
|
+
end
|
127
|
+
|
128
|
+
return if reason.empty?
|
129
|
+
|
130
|
+
failure_diagnostics_output(
|
131
|
+
{
|
132
|
+
location: notification.example.metadata[:location]
|
133
|
+
}.merge(reason).stringify_keys,
|
134
|
+
padding
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Handler for formatter +example_pending+ notification.
|
139
|
+
#
|
140
|
+
# @param description [String] example description
|
141
|
+
# @param example_number [Integer] example number
|
142
|
+
# @param padding [Integer] indentation width
|
143
|
+
def pending_output(notification, description, example_number, padding)
|
144
|
+
directive = pending_example_directive(notification)
|
145
|
+
line = "ok #{example_number} - #{description} # #{directive}"
|
146
|
+
line = colored_line("#{indentation(padding)}#{line}", :pending)
|
147
|
+
|
148
|
+
@output.puts(line)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Handler for formatter +message+ notification.
|
152
|
+
#
|
153
|
+
# @param notification [ExampleNotification] example notification
|
154
|
+
def message_output(notification)
|
155
|
+
return if @bailed_out
|
156
|
+
return unless RSpec.world.non_example_failure
|
157
|
+
|
158
|
+
bailed_out_message_output(notification)
|
159
|
+
|
160
|
+
@bailed_out = true
|
161
|
+
end
|
162
|
+
|
163
|
+
# Handler for formatter +dump_failures+ notification.
|
164
|
+
#
|
165
|
+
# @param notification [ExampleNotification] example notification
|
166
|
+
def store_failed_examples_summary(notification)
|
167
|
+
return if notification.failure_notifications.empty?
|
168
|
+
|
169
|
+
@failed_examples = notification.fully_formatted_failed_examples
|
170
|
+
end
|
171
|
+
|
172
|
+
# Handler for formatter +dump_pending+ notification.
|
173
|
+
#
|
174
|
+
# @param notification [ExampleNotification] example notification
|
175
|
+
def store_pending_examples_summary(notification)
|
176
|
+
return if notification.pending_examples.empty?
|
177
|
+
|
178
|
+
@pending_examples = notification.fully_formatted_pending_examples
|
179
|
+
end
|
180
|
+
|
181
|
+
# Handler for formatter +dump_summary+ notification.
|
182
|
+
#
|
183
|
+
# @param notification [ExampleNotification] example notification
|
184
|
+
# @param seed [Integer] used seed
|
185
|
+
def summary_output(notification, seed)
|
186
|
+
return if @bailed_out
|
187
|
+
|
188
|
+
@output.puts("1..#{notification.examples.size}")
|
189
|
+
|
190
|
+
return if notification.examples.size.zero?
|
191
|
+
|
192
|
+
execution_stats_output(notification)
|
193
|
+
|
194
|
+
@output.puts("# seed: #{seed}") if seed
|
195
|
+
|
196
|
+
dump_failed_examples_summary if @failed_examples.present?
|
197
|
+
dump_pending_examples_summary if @pending_examples.present?
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
# Provides failure reason for RSpec version before +3.3.0+.
|
203
|
+
#
|
204
|
+
# @param notification [ExampleNotification] example notification
|
205
|
+
# @return [Hash<Symbol, String>] +error+ and +backtrace+ for
|
206
|
+
# the YAML block
|
207
|
+
def failure_reason_pre_3_3_0(notification)
|
208
|
+
failure_error_and_backtrace(notification)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Provides failure reason for RSpec version after +3.3.0+.
|
212
|
+
#
|
213
|
+
# @param notification [ExampleNotification] example notification
|
214
|
+
# @return [Hash<Symbol, String>] +error+ and +backtrace+
|
215
|
+
# for the YAML block
|
216
|
+
def failure_reason_for_and_post_3_3_0(notification)
|
217
|
+
case notification.example.execution_result.exception
|
218
|
+
when RSpec::Expectations::MultipleExpectationsNotMetError
|
219
|
+
multiple_failures_error_and_backtrace(notification)
|
220
|
+
else
|
221
|
+
failure_error_and_backtrace(notification)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Provides failure error and backtrace.
|
226
|
+
#
|
227
|
+
# @param notification [ExampleNotification] example notification
|
228
|
+
# @return [Hash<Symbol, String>] +error+ and +backtrace+
|
229
|
+
# for the YAML block
|
230
|
+
def failure_error_and_backtrace(notification)
|
231
|
+
{
|
232
|
+
error: failure_error(notification),
|
233
|
+
backtrace: failure_backtrace(notification)
|
234
|
+
}.compact
|
235
|
+
end
|
236
|
+
|
237
|
+
# Provides failure error.
|
238
|
+
#
|
239
|
+
# @param notification [ExampleNotification] example notification
|
240
|
+
# @return [String] failure error
|
241
|
+
def failure_error(notification)
|
242
|
+
message_lines = notification.message_lines
|
243
|
+
|
244
|
+
return if message_lines.empty?
|
245
|
+
|
246
|
+
uncolorize_lines(message_lines)
|
247
|
+
.reject(&:blank?)
|
248
|
+
.join("\n")
|
249
|
+
end
|
250
|
+
|
251
|
+
# Provides failure backtrace.
|
252
|
+
#
|
253
|
+
# @param notification [ExampleNotification] example notification
|
254
|
+
# @return [String] failure backtrace
|
255
|
+
def failure_backtrace(notification)
|
256
|
+
formatted_backtrace = notification.formatted_backtrace
|
257
|
+
|
258
|
+
return if formatted_backtrace.empty?
|
259
|
+
|
260
|
+
uncolorize_lines(formatted_backtrace)
|
261
|
+
.reject(&:blank?)
|
262
|
+
.first(10)
|
263
|
+
.join("\n")
|
264
|
+
end
|
265
|
+
|
266
|
+
# Provides aggregate failure error.
|
267
|
+
#
|
268
|
+
# @param notification [ExampleNotification] example notification
|
269
|
+
# @return [Hash<Symbol, String>] +error+ for the YAML block
|
270
|
+
def multiple_failures_error_and_backtrace(notification)
|
271
|
+
{
|
272
|
+
error: multiple_failures_error(notification)
|
273
|
+
}.compact
|
274
|
+
end
|
275
|
+
|
276
|
+
# Provides aggregate failure error.
|
277
|
+
#
|
278
|
+
# @param notification [ExampleNotification] example notification
|
279
|
+
# @return [String] aggregate failure error
|
280
|
+
def multiple_failures_error(notification)
|
281
|
+
message = notification.example.execution_result.exception.message
|
282
|
+
|
283
|
+
return if message.blank?
|
284
|
+
|
285
|
+
uncolorize_lines(message.split("\n"))
|
286
|
+
.reject(&:blank?)
|
287
|
+
.join("\n")
|
288
|
+
end
|
289
|
+
|
290
|
+
# Prints failure error YAML block.
|
291
|
+
#
|
292
|
+
# @param reason [Hash<String, String>] failure reason hash
|
293
|
+
# for +location+, +error+, and +backtrace+
|
294
|
+
# @param padding [Integer] indentation width
|
295
|
+
def failure_diagnostics_output(reason, padding)
|
296
|
+
Psych.dump(reason).lines.each do |line|
|
297
|
+
@output.print("#{indentation(padding)}#{line}")
|
298
|
+
end
|
299
|
+
|
300
|
+
@output.puts("#{indentation(padding)}...")
|
301
|
+
end
|
302
|
+
|
303
|
+
# Finds the directive for pending example.
|
304
|
+
#
|
305
|
+
# @param notification [ExampleNotification] example notification
|
306
|
+
# @return [String] directive +SKIP+ or +TODO+
|
307
|
+
def pending_example_directive(notification)
|
308
|
+
execution_result = notification.example.execution_result
|
309
|
+
could_be_skipped = execution_result.respond_to?(:example_skipped?)
|
310
|
+
|
311
|
+
if could_be_skipped && execution_result.example_skipped?
|
312
|
+
"SKIP: #{execution_result.pending_message}"
|
313
|
+
else
|
314
|
+
"TODO: #{execution_result.pending_message}"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
# Prints failure reason outside of example.
|
319
|
+
# It is the only bail out scenario.
|
320
|
+
#
|
321
|
+
# @param notification [ExampleNotification] example notification
|
322
|
+
def bailed_out_message_output(notification)
|
323
|
+
bailed_out_report_output
|
324
|
+
|
325
|
+
uncolorize_lines(notification.message.split("\n")).each do |line|
|
326
|
+
next if line.blank?
|
327
|
+
|
328
|
+
if line.start_with?('#')
|
329
|
+
@output.puts("# #{line.chars.drop(1).join.strip}")
|
330
|
+
else
|
331
|
+
@output.puts("# #{line}")
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
# Prints required TAP lines for bailed out scenario.
|
337
|
+
def bailed_out_report_output
|
338
|
+
@output.puts('TAP version 13')
|
339
|
+
@output.puts('1..0')
|
340
|
+
@output.puts('Bail out!')
|
341
|
+
end
|
342
|
+
|
343
|
+
# Prints example stats and duration for the entire execution.
|
344
|
+
#
|
345
|
+
# @param notification [ExampleNotification] example notification
|
346
|
+
def execution_stats_output(notification)
|
347
|
+
test_stats = [
|
348
|
+
notification.examples.size,
|
349
|
+
0,
|
350
|
+
notification.failed_examples.size,
|
351
|
+
notification.pending_examples.size
|
352
|
+
]
|
353
|
+
test_stats[1] = test_stats[0] - test_stats.drop(1).reduce(:+)
|
354
|
+
|
355
|
+
stats_output(test_stats, 0)
|
356
|
+
|
357
|
+
@output.puts("# duration: #{notification.duration} seconds")
|
358
|
+
end
|
359
|
+
|
360
|
+
# Prints example stats.
|
361
|
+
#
|
362
|
+
# @param test_stats [Array<Integer>] stats for the example group
|
363
|
+
# @param padding [Integer] indentation width
|
364
|
+
def stats_output(test_stats, padding)
|
365
|
+
stats = %i[tests passed failed pending]
|
366
|
+
.zip(test_stats)
|
367
|
+
.to_h
|
368
|
+
.reject { |_, value| value.zero? }
|
369
|
+
.map { |key, value| "#{key}: #{value}" }
|
370
|
+
.join(', ')
|
371
|
+
|
372
|
+
@output.puts("#{indentation(padding)}# #{stats}")
|
373
|
+
end
|
374
|
+
|
375
|
+
# Prints failed examples list.
|
376
|
+
# It is not included in the TAP report.
|
377
|
+
def dump_failed_examples_summary
|
378
|
+
if @write_to_file
|
379
|
+
$stdout.puts(@failed_examples)
|
380
|
+
else
|
381
|
+
@output.puts(@failed_examples)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
# Prints pending examples list.
|
386
|
+
# It is not included in the TAP report.
|
387
|
+
def dump_pending_examples_summary
|
388
|
+
if @write_to_file
|
389
|
+
$stdout.puts(@pending_examples)
|
390
|
+
else
|
391
|
+
@output.puts(@pending_examples)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
# Converts string to colored string.
|
396
|
+
#
|
397
|
+
# @param line [String] line to print
|
398
|
+
# @param status [Symbol] status for color
|
399
|
+
# (:detail, :success, :failure, and :pending)
|
400
|
+
# @return [String] colored string
|
401
|
+
def colored_line(line, status)
|
402
|
+
if @force_colors || @display_colors
|
403
|
+
RSpec::Core::Formatters::ConsoleCodes.wrap(line, status)
|
404
|
+
else
|
405
|
+
line
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# Converts array of colored strings to uncolored string
|
410
|
+
#
|
411
|
+
# @param lines [Array<String>] lines to uncolor
|
412
|
+
# @return [Array<String>] uncolored lines
|
413
|
+
def uncolorize_lines(lines)
|
414
|
+
lines.map { |line| uncolorize_line(line) }
|
415
|
+
end
|
416
|
+
|
417
|
+
# Converts string to uncolored line.
|
418
|
+
# It strips ANSI escape sequences +\033[XXXm+.
|
419
|
+
#
|
420
|
+
# @example
|
421
|
+
# uncolorize_line('\033[0;31mcolored line\033[0m')
|
422
|
+
# #=> 'colored line'
|
423
|
+
#
|
424
|
+
# @param line [String] line to print
|
425
|
+
# @return [String] uncolored line
|
426
|
+
def uncolorize_line(line)
|
427
|
+
return line if line.blank?
|
428
|
+
|
429
|
+
line.gsub(/\e\[(\d+)(;\d+)*m/, '')
|
430
|
+
end
|
431
|
+
|
432
|
+
# Computes the indentation width by padding.
|
433
|
+
# The default indentation width is +2+.
|
434
|
+
#
|
435
|
+
# @param padding [Integer] indentation width
|
436
|
+
# @return [String] string of whitespaces
|
437
|
+
def indentation(padding)
|
438
|
+
return unless padding.positive?
|
439
|
+
|
440
|
+
' ' * padding
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|