rspec-tap-formatters 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|