rspec-tap-formatters 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ RSpec.describe RSpec::TAP::Formatters::Flat do
6
+ subject(:formatter) { described_class.new(report_output) }
7
+
8
+ let(:report_output) { StringIO.new }
9
+ let(:report_printer) { RSpec::TAP::Formatters::Printer.new(report_output) }
10
+
11
+ before do
12
+ formatter.instance_variable_set(:@printer, report_printer)
13
+ formatter.instance_variable_set(:@seed, nil)
14
+ formatter.instance_variable_set(:@example_number, 0)
15
+ end
16
+
17
+ describe '#seed' do
18
+ let(:seed) { SecureRandom.random_number(10_000) }
19
+
20
+ context 'with seed used' do
21
+ let(:notification) { OpenStruct.new(seed: seed, seed_used?: true) }
22
+
23
+ it 'updates instance variable' do
24
+ expect { formatter.seed(notification) }
25
+ .to change { formatter.instance_variable_get(:@seed) }
26
+ .from(nil)
27
+ .to(seed)
28
+ end
29
+ end
30
+
31
+ context 'without seed used' do
32
+ let(:notification) { OpenStruct.new(seed: seed, seed_used?: false) }
33
+
34
+ it 'does not update instance variable' do
35
+ expect { formatter.seed(notification) }
36
+ .not_to change { formatter.instance_variable_get(:@seed) }
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#start' do
42
+ let(:count) { 1 + SecureRandom.random_number(5) }
43
+ let(:notification) { OpenStruct.new(count: count) }
44
+
45
+ it 'delegates to printer' do
46
+ allow(report_printer).to receive(:start_output)
47
+
48
+ formatter.start(notification)
49
+
50
+ expect(report_printer).to have_received(:start_output).with(no_args)
51
+ end
52
+ end
53
+
54
+ describe '#start_dump' do
55
+ it 'delegates to printer' do
56
+ allow(report_printer).to receive(:example_progress_dump)
57
+
58
+ formatter.start_dump(OpenStruct.new)
59
+
60
+ expect(report_printer).to have_received(:example_progress_dump)
61
+ .with(no_args)
62
+ end
63
+ end
64
+
65
+ describe '#example_started' do
66
+ let(:example_number) { 1 + SecureRandom.random_number(5) }
67
+
68
+ it 'increments example number by one' do
69
+ expect { formatter.example_started(OpenStruct.new) }
70
+ .to change { formatter.instance_variable_get(:@example_number) }
71
+ .by(1)
72
+ end
73
+ end
74
+
75
+ describe '#example_passed' do
76
+ let(:example_number) { 1 + SecureRandom.random_number(5) }
77
+ let(:example_status) { :success }
78
+ let(:example_status_index) { 1 }
79
+
80
+ let(:description) { 'example-foo' }
81
+ let(:example) { OpenStruct.new(full_description: description) }
82
+ let(:notification) { OpenStruct.new(example: example) }
83
+
84
+ before do
85
+ formatter.instance_variable_set(:@example_number, example_number)
86
+ end
87
+
88
+ it 'delegates progress report to printer' do
89
+ allow(report_printer).to receive(:example_progress_output)
90
+
91
+ formatter.example_passed(notification)
92
+
93
+ expect(report_printer).to have_received(:example_progress_output)
94
+ .with(example_status)
95
+ end
96
+
97
+ it 'delegates status report to printer' do
98
+ allow(report_printer).to receive(:success_output)
99
+
100
+ formatter.example_passed(notification)
101
+
102
+ expect(report_printer).to have_received(:success_output)
103
+ .with(description, example_number, 0)
104
+ end
105
+ end
106
+
107
+ describe '#example_failed' do
108
+ let(:example_number) { 1 + SecureRandom.random_number(5) }
109
+ let(:example_status) { :failure }
110
+ let(:example_status_index) { 2 }
111
+
112
+ let(:description) { 'example-foo' }
113
+ let(:example) { OpenStruct.new(full_description: description) }
114
+ let(:notification) { OpenStruct.new(example: example) }
115
+
116
+ before do
117
+ formatter.instance_variable_set(:@example_number, example_number)
118
+
119
+ allow(report_printer).to receive(:failure_reason_output)
120
+ end
121
+
122
+ it 'delegates progress report to printer' do
123
+ allow(report_printer).to receive(:example_progress_output)
124
+
125
+ formatter.example_failed(notification)
126
+
127
+ expect(report_printer).to have_received(:example_progress_output)
128
+ .with(example_status)
129
+ end
130
+
131
+ it 'delegates status report to printer' do
132
+ allow(report_printer).to receive(:failure_output)
133
+
134
+ formatter.example_failed(notification)
135
+
136
+ expect(report_printer).to have_received(:failure_output)
137
+ .with(description, example_number, 0)
138
+ end
139
+
140
+ it 'delegates reason to printer' do
141
+ formatter.example_failed(notification)
142
+
143
+ expect(report_printer).to have_received(:failure_reason_output)
144
+ .with(notification, 1)
145
+ end
146
+ end
147
+
148
+ describe '#example_pending' do
149
+ let(:example_number) { 1 + SecureRandom.random_number(5) }
150
+ let(:example_status) { :pending }
151
+ let(:example_status_index) { 3 }
152
+
153
+ let(:description) { 'example-foo' }
154
+ let(:example) do
155
+ OpenStruct.new(
156
+ full_description: description,
157
+ execution_result: execution_result
158
+ )
159
+ end
160
+ let(:notification) { OpenStruct.new(example: example) }
161
+
162
+ before do
163
+ formatter.instance_variable_set(:@example_number, example_number)
164
+ end
165
+
166
+ shared_examples_for 'pending example' do
167
+ it 'delegates progress report to printer' do
168
+ allow(report_printer).to receive(:example_progress_output)
169
+
170
+ formatter.example_pending(notification)
171
+
172
+ expect(report_printer).to have_received(:example_progress_output)
173
+ .with(example_status)
174
+ end
175
+
176
+ it 'delegates status report to printer' do
177
+ allow(report_printer).to receive(:pending_output)
178
+
179
+ formatter.example_pending(notification)
180
+
181
+ expect(report_printer).to have_received(:pending_output)
182
+ .with(notification, description, example_number, 0)
183
+ end
184
+ end
185
+
186
+ context 'with pending' do
187
+ let(:pending_message) { "pending-#{SecureRandom.hex}" }
188
+ let(:directive) { "TODO: #{pending_message}" }
189
+ let(:execution_result) do
190
+ OpenStruct.new(
191
+ pending_message: pending_message,
192
+ example_skipped?: false
193
+ )
194
+ end
195
+
196
+ include_examples('pending example')
197
+ end
198
+
199
+ context 'with skipped' do
200
+ let(:pending_message) { "skip-#{SecureRandom.hex}" }
201
+ let(:directive) { "SKIP: #{pending_message}" }
202
+ let(:execution_result) do
203
+ OpenStruct.new(
204
+ pending_message: pending_message,
205
+ example_skipped?: true
206
+ )
207
+ end
208
+
209
+ include_examples('pending example')
210
+ end
211
+ end
212
+
213
+ describe '#message' do
214
+ let(:notification) { OpenStruct.new }
215
+
216
+ it 'delegates to printer' do
217
+ allow(report_printer).to receive(:message_output)
218
+
219
+ formatter.message(notification)
220
+
221
+ expect(report_printer).to have_received(:message_output)
222
+ .with(notification)
223
+ end
224
+ end
225
+
226
+ describe '#dump_failures' do
227
+ let(:notification) { OpenStruct.new }
228
+
229
+ it 'delegates to printer' do
230
+ allow(report_printer).to receive(:store_failed_examples_summary)
231
+
232
+ formatter.dump_failures(notification)
233
+
234
+ expect(report_printer).to have_received(:store_failed_examples_summary)
235
+ .with(notification)
236
+ end
237
+ end
238
+
239
+ describe '#dump_pending' do
240
+ let(:notification) { OpenStruct.new }
241
+
242
+ it 'delegates to printer' do
243
+ allow(report_printer).to receive(:store_pending_examples_summary)
244
+
245
+ formatter.dump_pending(notification)
246
+
247
+ expect(report_printer).to have_received(:store_pending_examples_summary)
248
+ .with(notification)
249
+ end
250
+ end
251
+
252
+ describe '#dump_summary' do
253
+ let(:seed) { 1 + SecureRandom.random_number(10_000) }
254
+ let(:notification) { OpenStruct.new }
255
+
256
+ it 'delegates to printer' do
257
+ allow(report_printer).to receive(:summary_output)
258
+
259
+ formatter.instance_variable_set(:@seed, seed)
260
+ formatter.dump_summary(notification)
261
+
262
+ expect(report_printer).to have_received(:summary_output)
263
+ .with(notification, seed)
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,1075 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ RSpec.shared_context 'when writing to file' do
6
+ before do
7
+ report_printer.instance_variable_set(:@write_to_file, true)
8
+ report_printer.instance_variable_set(:@display_colors, false)
9
+ end
10
+ end
11
+
12
+ RSpec.shared_context 'when not writing to file' do
13
+ before do
14
+ report_printer.instance_variable_set(:@write_to_file, false)
15
+ report_printer.instance_variable_set(:@display_colors, true)
16
+ end
17
+ end
18
+
19
+ RSpec.describe RSpec::TAP::Formatters::Printer do
20
+ subject(:report_printer) { described_class.new(report_output) }
21
+
22
+ let(:report_output) { StringIO.new }
23
+
24
+ before do
25
+ report_printer.instance_variable_set(:@output, report_output)
26
+ report_printer.instance_variable_set(:@write_to_file, false)
27
+ report_printer.instance_variable_set(:@display_colors, true)
28
+ report_printer.instance_variable_set(:@force_colors, false)
29
+ report_printer.instance_variable_set(:@bailed_out, false)
30
+ report_printer.instance_variable_set(:@failed_examples, '')
31
+ report_printer.instance_variable_set(:@pending_examples, '')
32
+ end
33
+
34
+ describe '#start_output' do
35
+ context 'when bailed out' do
36
+ it 'outputs nothing' do
37
+ report_printer.instance_variable_set(:@bailed_out, true)
38
+ report_printer.start_output
39
+
40
+ expect(report_output.string).to be_empty
41
+ end
42
+ end
43
+
44
+ context 'when not bailed out' do
45
+ let(:output_line) { 'TAP version 13' }
46
+
47
+ it 'outputs tap version with tests count' do
48
+ report_printer.start_output
49
+
50
+ expect(report_output.string.chomp).to eq(output_line)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#group_start_output' do
56
+ let(:description) { 'test-or-group-foo' }
57
+ let(:group) { OpenStruct.new(description: description) }
58
+ let(:notification) { OpenStruct.new(group: group) }
59
+
60
+ shared_context 'when root level test' do
61
+ let(:padding) { 0 }
62
+ let(:indentation) { ' ' * padding }
63
+
64
+ before do
65
+ report_printer.group_start_output(notification, padding)
66
+ end
67
+ end
68
+
69
+ shared_context 'when non-root level test' do
70
+ let(:padding) { 1 + SecureRandom.random_number(5) }
71
+ let(:indentation) { ' ' * padding }
72
+
73
+ before do
74
+ report_printer.group_start_output(notification, padding)
75
+ end
76
+ end
77
+
78
+ context 'with colorized outputs' do
79
+ before do
80
+ allow(RSpec::Core::Formatters::ConsoleCodes)
81
+ .to receive(:wrap) do |string, status|
82
+ "<#{status}>#{string}</#{status}>"
83
+ end
84
+
85
+ report_printer.instance_variable_set(:@display_colors, true)
86
+ end
87
+
88
+ context 'when root level test' do
89
+ let(:uncolorized_output_line) do
90
+ "#{indentation}# test: #{description} {"
91
+ end
92
+ let(:output_line) do
93
+ "<detail>#{uncolorized_output_line}</detail>"
94
+ end
95
+
96
+ include_context('when root level test')
97
+
98
+ it 'outputs test information' do
99
+ expect(report_output.string.chomp).to eq(output_line)
100
+ end
101
+
102
+ it 'outputs colorized test information' do
103
+ expect(RSpec::Core::Formatters::ConsoleCodes)
104
+ .to have_received(:wrap)
105
+ .with(a_string_equal_to(uncolorized_output_line), :detail)
106
+ end
107
+ end
108
+
109
+ context 'when non-root level test' do
110
+ let(:uncolorized_output_line) do
111
+ "#{indentation}# group: #{description} {"
112
+ end
113
+ let(:output_line) do
114
+ "<detail>#{uncolorized_output_line}</detail>"
115
+ end
116
+
117
+ include_context('when non-root level test')
118
+
119
+ it 'outputs group information' do
120
+ expect(report_output.string.chomp).to eq(output_line)
121
+ end
122
+
123
+ it 'outputs colorized group information' do
124
+ expect(RSpec::Core::Formatters::ConsoleCodes)
125
+ .to have_received(:wrap)
126
+ .with(a_string_equal_to(uncolorized_output_line), :detail)
127
+ end
128
+ end
129
+ end
130
+
131
+ context 'without colorized outputs' do
132
+ before do
133
+ allow(RSpec::Core::Formatters::ConsoleCodes)
134
+ .to receive(:wrap).with(String, Symbol)
135
+
136
+ report_printer.instance_variable_set(:@display_colors, false)
137
+ end
138
+
139
+ context 'when root level test' do
140
+ let(:output_line) { "#{indentation}# test: #{group.description} {" }
141
+
142
+ include_context('when root level test')
143
+
144
+ it 'outputs test information' do
145
+ expect(report_output.string.chomp).to eq(output_line)
146
+ end
147
+
148
+ it 'outputs non-colorized test information' do
149
+ expect(RSpec::Core::Formatters::ConsoleCodes)
150
+ .not_to have_received(:wrap)
151
+ end
152
+ end
153
+
154
+ context 'when non-root level test' do
155
+ let(:output_line) { "#{indentation}# group: #{group.description} {" }
156
+
157
+ include_context('when non-root level test')
158
+
159
+ it 'outputs group information' do
160
+ expect(report_output.string.chomp).to eq(output_line)
161
+ end
162
+
163
+ it 'outputs non-colorized group information' do
164
+ expect(RSpec::Core::Formatters::ConsoleCodes)
165
+ .not_to have_received(:wrap)
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ describe '#group_finished_output' do
172
+ let(:padding) { 1 + SecureRandom.random_number(5) }
173
+ let(:indentation) { ' ' * padding }
174
+ let(:indentation_one_level_up) { ' ' * (padding - 1) }
175
+
176
+ let(:passed) { 1 + SecureRandom.random_number(5) }
177
+ let(:failed) { 1 + SecureRandom.random_number(5) }
178
+ let(:pending) { 0 }
179
+ let(:tests) { passed + failed + pending }
180
+ let(:test_stats) { [tests, passed, failed, pending] }
181
+
182
+ context 'with colorized outputs' do
183
+ let(:uncolorized_output_line) { "#{indentation_one_level_up}}" }
184
+ let(:output_line) do
185
+ <<-OUTPUT.gsub(/^\s+\|/, '').chomp
186
+ |#{indentation}1..#{tests}
187
+ |#{indentation}# tests: #{tests}, passed: #{passed}, failed: #{failed}
188
+ |<detail>#{uncolorized_output_line}</detail>
189
+ OUTPUT
190
+ end
191
+
192
+ before do
193
+ allow(RSpec::Core::Formatters::ConsoleCodes)
194
+ .to receive(:wrap) do |string, status|
195
+ "<#{status}>#{string}</#{status}>"
196
+ end
197
+
198
+ report_printer.instance_variable_set(:@display_colors, true)
199
+ report_printer.group_finished_output(test_stats, padding)
200
+ end
201
+
202
+ it 'outputs tests summary' do
203
+ expect(report_output.string.chomp).to eq(output_line)
204
+ end
205
+
206
+ it 'outputs colorized tests summary' do
207
+ expect(RSpec::Core::Formatters::ConsoleCodes)
208
+ .to have_received(:wrap)
209
+ .with(a_string_equal_to(uncolorized_output_line), :detail)
210
+ end
211
+ end
212
+
213
+ context 'without colorized outputs' do
214
+ let(:output_line) do
215
+ <<-OUTPUT.gsub(/^\s+\|/, '').chomp
216
+ |#{indentation}1..#{tests}
217
+ |#{indentation}# tests: #{tests}, passed: #{passed}, failed: #{failed}
218
+ |#{indentation_one_level_up}}
219
+ OUTPUT
220
+ end
221
+
222
+ before do
223
+ allow(RSpec::Core::Formatters::ConsoleCodes)
224
+ .to receive(:wrap).with(String, Symbol)
225
+
226
+ report_printer.instance_variable_set(:@display_colors, false)
227
+ report_printer.group_finished_output(test_stats, padding)
228
+ end
229
+
230
+ it 'outputs tests summary' do
231
+ expect(report_output.string.chomp).to eq(output_line)
232
+ end
233
+
234
+ it 'outputs non-colorized tests summary' do
235
+ expect(RSpec::Core::Formatters::ConsoleCodes)
236
+ .not_to have_received(:wrap)
237
+ end
238
+ end
239
+ end
240
+
241
+ describe '#example_progress_output' do
242
+ shared_examples_for 'example progress output' do |char, status|
243
+ context 'when writing to file' do
244
+ include_context('when writing to file')
245
+
246
+ context 'with forced colorized outputs' do
247
+ let(:progress_output) { "<#{status}>#{char}</#{status}>" }
248
+
249
+ before do
250
+ allow(RSpec::Core::Formatters::ConsoleCodes)
251
+ .to receive(:wrap) do |string, color|
252
+ "<#{color}>#{string}</#{color}>"
253
+ end
254
+
255
+ allow(RSpec).to receive(:configuration)
256
+ .with(no_args)
257
+ .and_return(OpenStruct.new(color_enabled?: true))
258
+ end
259
+
260
+ it 'outputs example progress' do
261
+ expect { report_printer.example_progress_output(status) }
262
+ .to output(progress_output).to_stdout
263
+ end
264
+
265
+ it 'outputs colorized example progress' do
266
+ report_printer.example_progress_output(status)
267
+
268
+ expect(RSpec::Core::Formatters::ConsoleCodes)
269
+ .to have_received(:wrap)
270
+ .with(a_string_equal_to(char), status)
271
+ end
272
+
273
+ it 'outputs forced colorized example progress' do
274
+ report_printer.example_progress_output(status)
275
+
276
+ expect(RSpec).to have_received(:configuration).with(no_args)
277
+ end
278
+ end
279
+
280
+ context 'without colorized outputs' do
281
+ let(:progress_output) { char }
282
+
283
+ before do
284
+ allow(RSpec::Core::Formatters::ConsoleCodes)
285
+ .to receive(:wrap).with(String, Symbol)
286
+
287
+ allow(RSpec).to receive(:configuration)
288
+ .with(no_args).and_return(OpenStruct.new(color_enabled?: false))
289
+ end
290
+
291
+ it 'outputs example progress' do
292
+ expect { report_printer.example_progress_output(status) }
293
+ .to output(progress_output).to_stdout
294
+ end
295
+
296
+ it 'outputs non-colorized example progress' do
297
+ report_printer.example_progress_output(status)
298
+
299
+ expect(RSpec::Core::Formatters::ConsoleCodes)
300
+ .not_to have_received(:wrap)
301
+ end
302
+
303
+ it 'outputs non-forced colorized example progress' do
304
+ report_printer.example_progress_output(status)
305
+
306
+ expect(RSpec).to have_received(:configuration).with(no_args)
307
+ end
308
+ end
309
+ end
310
+
311
+ context 'when not writing to file' do
312
+ before do
313
+ allow(RSpec::Core::Formatters::ConsoleCodes)
314
+ .to receive(:wrap).with(String, Symbol)
315
+
316
+ allow(RSpec).to receive(:configuration)
317
+ .with(no_args).and_return(OpenStruct.new)
318
+ end
319
+
320
+ include_context('when not writing to file')
321
+
322
+ it 'does not output example progress' do
323
+ expect { report_printer.example_progress_output(status) }
324
+ .not_to output.to_stdout
325
+ end
326
+
327
+ it 'does not attempt to colorize output' do
328
+ report_printer.example_progress_output(status)
329
+
330
+ expect(RSpec::Core::Formatters::ConsoleCodes)
331
+ .not_to have_received(:wrap)
332
+ end
333
+
334
+ it 'does not attempt to force colorize output' do
335
+ report_printer.example_progress_output(status)
336
+
337
+ expect(RSpec).not_to have_received(:configuration)
338
+ end
339
+ end
340
+ end
341
+
342
+ context 'with success progress' do
343
+ include_examples('example progress output', '.', :success)
344
+ end
345
+
346
+ context 'with failure progress' do
347
+ include_examples('example progress output', 'F', :failure)
348
+ end
349
+
350
+ context 'with success progress' do
351
+ include_examples('example progress output', '*', :pending)
352
+ end
353
+ end
354
+
355
+ describe '#example_progress_dump' do
356
+ context 'when writing to file' do
357
+ include_context('when writing to file')
358
+
359
+ it 'outputs a blank line' do
360
+ expect { report_printer.example_progress_dump }
361
+ .to output("\n").to_stdout
362
+ end
363
+ end
364
+
365
+ context 'without writing to file' do
366
+ include_context('when not writing to file')
367
+
368
+ it 'outputs nothing' do
369
+ expect { report_printer.example_progress_dump }
370
+ .not_to output.to_stdout
371
+ end
372
+ end
373
+ end
374
+
375
+ describe '#success_output' do
376
+ let(:padding) { 3 }
377
+ let(:indentation) { ' ' * padding }
378
+ let(:description) { 'example-foo' }
379
+ let(:example_number) { 1 + SecureRandom.random_number(5) }
380
+
381
+ context 'with colorized outputs' do
382
+ let(:uncolorized_output_line) do
383
+ "#{indentation}ok #{example_number} - #{description}"
384
+ end
385
+ let(:output_line) do
386
+ "<success>#{uncolorized_output_line}</success>"
387
+ end
388
+
389
+ before do
390
+ allow(RSpec::Core::Formatters::ConsoleCodes)
391
+ .to receive(:wrap) do |string, status|
392
+ "<#{status}>#{string}</#{status}>"
393
+ end
394
+
395
+ report_printer.instance_variable_set(:@display_colors, true)
396
+ end
397
+
398
+ it 'outputs example status' do
399
+ report_printer.success_output(description, example_number, padding)
400
+
401
+ expect(report_output.string.chomp).to eq(output_line)
402
+ end
403
+
404
+ it 'outputs colorized status' do
405
+ report_printer.success_output(description, example_number, padding)
406
+
407
+ expect(RSpec::Core::Formatters::ConsoleCodes)
408
+ .to have_received(:wrap)
409
+ .with(a_string_equal_to(uncolorized_output_line), :success)
410
+ end
411
+ end
412
+
413
+ context 'without colorized outputs' do
414
+ let(:output_line) do
415
+ "#{indentation}ok #{example_number} - #{description}"
416
+ end
417
+
418
+ before do
419
+ allow(RSpec::Core::Formatters::ConsoleCodes).to receive(:wrap)
420
+
421
+ report_printer.instance_variable_set(:@display_colors, false)
422
+ end
423
+
424
+ it 'outputs example status' do
425
+ report_printer.success_output(description, example_number, padding)
426
+
427
+ expect(report_output.string.chomp).to eq(output_line)
428
+ end
429
+
430
+ it 'outputs non-colorized example status' do
431
+ report_printer.success_output(description, example_number, padding)
432
+
433
+ expect(RSpec::Core::Formatters::ConsoleCodes)
434
+ .not_to have_received(:wrap)
435
+ end
436
+ end
437
+ end
438
+
439
+ describe '#failure_output' do
440
+ let(:padding) { 3 }
441
+ let(:indentation) { ' ' * padding }
442
+ let(:description) { 'example-foo' }
443
+ let(:example_number) { 1 + SecureRandom.random_number(5) }
444
+
445
+ context 'with colorized outputs' do
446
+ let(:uncolorized_output_line) do
447
+ "#{indentation}not ok #{example_number} - #{description}"
448
+ end
449
+ let(:output_line) do
450
+ "<failure>#{uncolorized_output_line}</failure>"
451
+ end
452
+
453
+ before do
454
+ allow(RSpec::Core::Formatters::ConsoleCodes)
455
+ .to receive(:wrap) do |string, status|
456
+ "<#{status}>#{string}</#{status}>"
457
+ end
458
+
459
+ report_printer.instance_variable_set(:@display_colors, true)
460
+ end
461
+
462
+ it 'outputs example status' do
463
+ report_printer.failure_output(description, example_number, padding)
464
+
465
+ expect(report_output.string.chomp).to eq(output_line)
466
+ end
467
+
468
+ it 'outputs colorized status' do
469
+ report_printer.failure_output(description, example_number, padding)
470
+
471
+ expect(RSpec::Core::Formatters::ConsoleCodes)
472
+ .to have_received(:wrap)
473
+ .with(a_string_equal_to(uncolorized_output_line), :failure)
474
+ end
475
+ end
476
+
477
+ context 'without colorized outputs' do
478
+ let(:output_line) do
479
+ "#{indentation}not ok #{example_number} - #{description}"
480
+ end
481
+
482
+ before do
483
+ allow(RSpec::Core::Formatters::ConsoleCodes).to receive(:wrap)
484
+
485
+ report_printer.instance_variable_set(:@display_colors, false)
486
+ end
487
+
488
+ it 'outputs example status' do
489
+ report_printer.failure_output(description, example_number, padding)
490
+
491
+ expect(report_output.string.chomp).to eq(output_line)
492
+ end
493
+
494
+ it 'outputs non-colorized example status' do
495
+ report_printer.failure_output(description, example_number, padding)
496
+
497
+ expect(RSpec::Core::Formatters::ConsoleCodes)
498
+ .not_to have_received(:wrap)
499
+ end
500
+ end
501
+ end
502
+
503
+ describe '#failure_reason_output' do
504
+ let(:padding) { 4 }
505
+ let(:indentation) { ' ' * padding }
506
+ let(:indentation_one_level_down) { ' ' * (padding + 1) }
507
+ let(:location) do
508
+ "./spec/foo_spec.rb:#{1 + SecureRandom.random_number(5)}"
509
+ end
510
+
511
+ shared_examples_for 'failure reason for non-aggregate failures' do
512
+ context 'without aggregate failures' do
513
+ let(:message_lines) { %w[first_line second_line] }
514
+ let(:formatted_backtrace) { %w[trace_first trace_second trace_third] }
515
+ let(:exception) { RSpec::Expectations::ExpectationNotMetError.new }
516
+ let(:example) do
517
+ OpenStruct.new(
518
+ execution_result: OpenStruct.new(exception: exception),
519
+ metadata: {
520
+ location: location
521
+ }
522
+ )
523
+ end
524
+ let(:notification) do
525
+ OpenStruct.new(
526
+ message_lines: message_lines,
527
+ formatted_backtrace: formatted_backtrace,
528
+ example: example
529
+ )
530
+ end
531
+
532
+ let(:failure_diagnostics) do
533
+ diagnostics = <<-DIAGNOSTICS.gsub(/^\s+\|/, '').chomp
534
+ |---
535
+ |location: "#{location}"
536
+ |error: |-
537
+ | first_line
538
+ | second_line
539
+ |backtrace: |-
540
+ | trace_first
541
+ | trace_second
542
+ | trace_third
543
+ |...
544
+ DIAGNOSTICS
545
+
546
+ diagnostics.lines
547
+ .map { |line| "#{indentation_one_level_down}#{line}" }
548
+ .join
549
+ end
550
+
551
+ it 'outputs failure diagnostics' do
552
+ report_printer.failure_reason_output(notification, padding + 1)
553
+
554
+ expect(report_output.string.chomp).to eq(failure_diagnostics)
555
+ end
556
+ end
557
+ end
558
+
559
+ shared_examples_for 'failure reason output without diagnostics' do
560
+ context 'without diagnostics' do
561
+ let(:example) do
562
+ OpenStruct.new(
563
+ execution_result: OpenStruct.new(
564
+ exception: RSpec::Expectations::ExpectationNotMetError.new
565
+ ),
566
+ metadata: {
567
+ location: location
568
+ }
569
+ )
570
+ end
571
+ let(:notification) do
572
+ OpenStruct.new(
573
+ message_lines: [],
574
+ formatted_backtrace: [],
575
+ example: example
576
+ )
577
+ end
578
+
579
+ it 'outputs nothing' do
580
+ report_printer.failure_reason_output(notification, padding)
581
+
582
+ expect(report_output.string).to be_empty
583
+ end
584
+ end
585
+ end
586
+
587
+ if Gem::Version.new(RSpec::Core::Version::STRING) >=
588
+ Gem::Version.new('3.3.0')
589
+ context 'with RSpec version >= 3.3.0' do
590
+ before do
591
+ stub_const('RSpec::Core::Version::STRING', '3.3.1')
592
+ end
593
+
594
+ context 'with diagnostics' do
595
+ context 'with aggregate failures' do
596
+ let(:exception) do
597
+ RSpec::Expectations::MultipleExpectationsNotMetError.new
598
+ end
599
+ let(:example) do
600
+ OpenStruct.new(
601
+ execution_result: OpenStruct.new(exception: exception),
602
+ metadata: {
603
+ location: location
604
+ }
605
+ )
606
+ end
607
+ let(:notification) { OpenStruct.new(example: example) }
608
+
609
+ let(:failure_diagnostics) do
610
+ diagnostics = <<-DIAGNOSTICS.gsub(/^\s+\|/, '').chomp
611
+ |---
612
+ |location: "#{location}"
613
+ |error: RSpec::Expectations::MultipleExpectationsNotMetError
614
+ |...
615
+ DIAGNOSTICS
616
+
617
+ diagnostics.lines
618
+ .map { |line| "#{indentation_one_level_down}#{line}" }
619
+ .join
620
+ end
621
+
622
+ it 'outputs failure diagnostics' do
623
+ report_printer.failure_reason_output(notification, padding + 1)
624
+
625
+ expect(report_output.string.chomp).to eq(failure_diagnostics)
626
+ end
627
+ end
628
+
629
+ include_examples('failure reason for non-aggregate failures')
630
+ end
631
+
632
+ include_examples('failure reason output without diagnostics')
633
+ end
634
+ end
635
+
636
+ context 'with RSpec version less than 3.3.0' do
637
+ before do
638
+ stub_const('RSpec::Core::Version::STRING', '3.2.9')
639
+ end
640
+
641
+ context 'with diagnostics' do
642
+ include_examples('failure reason for non-aggregate failures')
643
+ end
644
+
645
+ include_examples('failure reason output without diagnostics')
646
+ end
647
+ end
648
+
649
+ describe '#pending_output' do
650
+ let(:padding) { 3 }
651
+ let(:indentation) { ' ' * padding }
652
+ let(:description) { 'example-foo' }
653
+ let(:example_number) { 1 + SecureRandom.random_number(5) }
654
+ let(:pending_message) { "pending-#{SecureRandom.hex}" }
655
+ let(:directive) { "TODO: #{pending_message}" }
656
+ let(:execution_result) do
657
+ OpenStruct.new(
658
+ pending_message: pending_message,
659
+ example_skipped?: false
660
+ )
661
+ end
662
+ let(:example) do
663
+ OpenStruct.new(
664
+ description: description,
665
+ execution_result: execution_result
666
+ )
667
+ end
668
+ let(:notification) { OpenStruct.new(example: example) }
669
+
670
+ context 'with colorized outputs' do
671
+ let(:uncolorized_output_line) do
672
+ "#{indentation}ok #{example_number} - #{description} # #{directive}"
673
+ end
674
+ let(:output_line) do
675
+ "<pending>#{uncolorized_output_line}</pending>"
676
+ end
677
+
678
+ before do
679
+ allow(RSpec::Core::Formatters::ConsoleCodes)
680
+ .to receive(:wrap) do |string, status|
681
+ "<#{status}>#{string}</#{status}>"
682
+ end
683
+
684
+ report_printer.instance_variable_set(:@display_colors, true)
685
+ end
686
+
687
+ it 'outputs example status' do
688
+ report_printer.pending_output(
689
+ notification,
690
+ description,
691
+ example_number,
692
+ padding
693
+ )
694
+
695
+ expect(report_output.string.chomp).to eq(output_line)
696
+ end
697
+
698
+ it 'outputs colorized status' do
699
+ report_printer.pending_output(
700
+ notification,
701
+ description,
702
+ example_number,
703
+ padding
704
+ )
705
+
706
+ expect(RSpec::Core::Formatters::ConsoleCodes)
707
+ .to have_received(:wrap)
708
+ .with(a_string_equal_to(uncolorized_output_line), :pending)
709
+ end
710
+ end
711
+
712
+ context 'without colorized outputs' do
713
+ let(:output_line) do
714
+ "#{indentation}ok #{example_number} - #{description} # #{directive}"
715
+ end
716
+
717
+ before do
718
+ allow(RSpec::Core::Formatters::ConsoleCodes).to receive(:wrap)
719
+
720
+ report_printer.instance_variable_set(:@display_colors, false)
721
+ end
722
+
723
+ it 'outputs example status' do
724
+ report_printer.pending_output(
725
+ notification,
726
+ description,
727
+ example_number,
728
+ padding
729
+ )
730
+
731
+ expect(report_output.string.chomp).to eq(output_line)
732
+ end
733
+
734
+ it 'outputs non-colorized example status' do
735
+ report_printer.pending_output(
736
+ notification,
737
+ description,
738
+ example_number,
739
+ padding
740
+ )
741
+
742
+ expect(RSpec::Core::Formatters::ConsoleCodes)
743
+ .not_to have_received(:wrap)
744
+ end
745
+ end
746
+ end
747
+
748
+ describe '#message_output' do
749
+ context 'when bailed out' do
750
+ it 'outputs nothing' do
751
+ report_printer.instance_variable_set(:@bailed_out, true)
752
+ report_printer.message_output(OpenStruct.new)
753
+
754
+ expect(report_output.string).to be_empty
755
+ end
756
+ end
757
+
758
+ context 'when failure inside example' do
759
+ before do
760
+ allow(RSpec).to receive(:world).with(no_args)
761
+ .and_return(OpenStruct.new(non_example_failure: false))
762
+ end
763
+
764
+ it 'outputs nothing' do
765
+ report_printer.message_output(OpenStruct.new)
766
+
767
+ expect(report_output.string).to be_empty
768
+ end
769
+
770
+ it 'verifies failure type' do
771
+ report_printer.message_output(OpenStruct.new)
772
+
773
+ expect(RSpec).to have_received(:world)
774
+ end
775
+ end
776
+
777
+ context 'when failure outside example' do
778
+ let(:message) do
779
+ <<-MESSAGE.gsub(/^\s+\|/, '').chomp
780
+ |message foo
781
+ |
782
+ |bar message
783
+ |# baz qux message
784
+ |\033[0;31m# colored message quux\033[0m
785
+ MESSAGE
786
+ end
787
+ let(:notification) { OpenStruct.new(message: message) }
788
+
789
+ let(:output_line) do
790
+ <<-OUTPUT.gsub(/^\s+\|/, '').chomp
791
+ |TAP version 13
792
+ |1..0
793
+ |Bail out!
794
+ |# message foo
795
+ |# bar message
796
+ |# baz qux message
797
+ |# colored message quux
798
+ OUTPUT
799
+ end
800
+
801
+ before do
802
+ allow(RSpec).to receive(:world).with(no_args)
803
+ .and_return(OpenStruct.new(non_example_failure: true))
804
+ end
805
+
806
+ it 'outputs message' do
807
+ report_printer.message_output(notification)
808
+
809
+ expect(report_output.string.chomp).to eq(output_line)
810
+ end
811
+
812
+ it 'verifies failure type' do
813
+ report_printer.message_output(notification)
814
+
815
+ expect(RSpec).to have_received(:world)
816
+ end
817
+
818
+ it 'marks execution bailed-out' do
819
+ expect { report_printer.message_output(notification) }
820
+ .to change { report_printer.instance_variable_get(:@bailed_out) }
821
+ .from(false)
822
+ .to(true)
823
+ end
824
+ end
825
+ end
826
+
827
+ describe '#store_failed_examples_summary' do
828
+ context 'with failed examples' do
829
+ let(:failed_examples) do
830
+ <<-FAILURES.gsub(/^\s+\|/, '').chomp
831
+ |Failure:
832
+ |
833
+ | 1) sample spec fails
834
+ | Failure/Error: expect(1).to eq(2)
835
+ |
836
+ | expected: 2
837
+ | got: 1
838
+ |
839
+ | (compared using ==)
840
+ | # ./spec/rspec/string_spec.rb:13
841
+ | 2) sample spec fails twice
842
+ | Got 2 failures:
843
+ |
844
+ | 2.1) Failure/Error: expect(1).to eq(2)
845
+ |
846
+ | expected: 2
847
+ | got: 1
848
+ |
849
+ | (compared using ==)
850
+ | # ./spec/rspec/string_spec.rb:23
851
+ |
852
+ | 2.2) Failure/Error: expect(3).to eq(4)
853
+ |
854
+ | expected: 4
855
+ | got: 3
856
+ |
857
+ | (compared using ==)
858
+ | # ./spec/rspec/string_spec.rb:24
859
+ FAILURES
860
+ end
861
+ let(:notification) do
862
+ OpenStruct.new(
863
+ failure_notifications: %i[failure-foo failure-baz],
864
+ fully_formatted_failed_examples: failed_examples
865
+ )
866
+ end
867
+
868
+ it 'updates failed examples' do
869
+ expect { report_printer.store_failed_examples_summary(notification) }
870
+ .to change {
871
+ report_printer.instance_variable_get(:@failed_examples)
872
+ }
873
+ .from('')
874
+ .to(failed_examples)
875
+ end
876
+ end
877
+
878
+ context 'without failed examples' do
879
+ let(:notification) do
880
+ OpenStruct.new(failure_notifications: [])
881
+ end
882
+
883
+ it 'does not update failed examples' do
884
+ expect { report_printer.store_failed_examples_summary(notification) }
885
+ .not_to change {
886
+ report_printer.instance_variable_get(:@failed_examples)
887
+ }
888
+ end
889
+ end
890
+ end
891
+
892
+ describe '#store_pending_examples_summary' do
893
+ context 'with pending examples' do
894
+ let(:pending_examples) do
895
+ <<-PENDING.gsub(/^\s+\|/, '').chomp
896
+ |Pending: (Failures listed here are expected and do not affect your suite's status)
897
+ |
898
+ | 1) sample spec without implementation succeeds
899
+ | # Not yet implemented
900
+ | # ./spec/rspec/string_spec.rb:8
901
+ |
902
+ | 2) sample spec with unmet expectation also succeeds
903
+ | # No reason given
904
+ | Failure/Error: expect(1).to eq(2)
905
+ |
906
+ | expected: 2
907
+ | got: 1
908
+ |
909
+ | (compared using ==)
910
+ | # ./spec/rspec/string_spec.rb:18
911
+ PENDING
912
+ end
913
+ let(:notification) do
914
+ OpenStruct.new(
915
+ pending_examples: %i[peding-baz pending-qux],
916
+ fully_formatted_pending_examples: pending_examples
917
+ )
918
+ end
919
+
920
+ it 'updates pending examples' do
921
+ expect { report_printer.store_pending_examples_summary(notification) }
922
+ .to change {
923
+ report_printer.instance_variable_get(:@pending_examples)
924
+ }
925
+ .from('')
926
+ .to(pending_examples)
927
+ end
928
+ end
929
+
930
+ context 'without pending examples' do
931
+ let(:notification) do
932
+ OpenStruct.new(pending_examples: [])
933
+ end
934
+
935
+ it 'does not update pending examples' do
936
+ expect { report_printer.store_pending_examples_summary(notification) }
937
+ .not_to change {
938
+ report_printer.instance_variable_get(:@pending_examples)
939
+ }
940
+ end
941
+ end
942
+ end
943
+
944
+ describe '#summary_output' do
945
+ context 'when bailed out' do
946
+ it 'outputs nothing' do
947
+ report_printer.instance_variable_set(:@bailed_out, true)
948
+ report_printer.summary_output(OpenStruct.new, nil)
949
+
950
+ expect(report_output.string).to be_empty
951
+ end
952
+ end
953
+
954
+ context 'when not bailed out' do
955
+ let(:seed) { 1 + SecureRandom.random_number(10_000) }
956
+ let(:tests) { 5 + SecureRandom.random_number(6) }
957
+ let(:examples) { Array.new(tests).map { "example-#{SecureRandom.hex}" } }
958
+ let(:failed) { 2 }
959
+ let(:failed_examples) { examples.sample(2) }
960
+ let(:pending) { 2 }
961
+ let(:pending_examples) { (examples - failed_examples).sample(2) }
962
+ let(:passed) { tests - failed - pending }
963
+ let(:duration) { SecureRandom.random_number.round(6) }
964
+ let(:notification) do
965
+ OpenStruct.new(
966
+ examples: examples,
967
+ failed_examples: failed_examples,
968
+ pending_examples: pending_examples,
969
+ duration: duration
970
+ )
971
+ end
972
+
973
+ let(:failed_examples_summary) do
974
+ <<-FAILURES.gsub(/^\s+\|/, '').chomp
975
+ |Failure:
976
+ |
977
+ | 1) sample spec fails
978
+ | Failure/Error: expect(1).to eq(2)
979
+ |
980
+ | expected: 2
981
+ | got: 1
982
+ |
983
+ | (compared using ==)
984
+ | # ./spec/rspec/string_spec.rb:13
985
+ | 2) sample spec fails twice
986
+ | Got 2 failures:
987
+ |
988
+ | 2.1) Failure/Error: expect(1).to eq(2)
989
+ |
990
+ | expected: 2
991
+ | got: 1
992
+ |
993
+ | (compared using ==)
994
+ | # ./spec/rspec/string_spec.rb:23
995
+ |
996
+ | 2.2) Failure/Error: expect(3).to eq(4)
997
+ |
998
+ | expected: 4
999
+ | got: 3
1000
+ |
1001
+ | (compared using ==)
1002
+ | # ./spec/rspec/string_spec.rb:24
1003
+ FAILURES
1004
+ end
1005
+ let(:pending_examples_summary) do
1006
+ <<-PENDING.gsub(/^\s+\|/, '').chomp
1007
+ |Pending: (Failures listed here are expected and do not affect your suite's status)
1008
+ |
1009
+ | 1) sample spec without implementation succeeds
1010
+ | # Not yet implemented
1011
+ | # ./spec/rspec/string_spec.rb:8
1012
+ |
1013
+ | 2) sample spec with unmet expectation also succeeds
1014
+ | # No reason given
1015
+ | Failure/Error: expect(1).to eq(2)
1016
+ |
1017
+ | expected: 2
1018
+ | got: 1
1019
+ |
1020
+ | (compared using ==)
1021
+ | # ./spec/rspec/string_spec.rb:18
1022
+ PENDING
1023
+ end
1024
+ let(:failed_and_pending_summary) do
1025
+ "#{failed_examples_summary}\n#{pending_examples_summary}"
1026
+ end
1027
+ let(:output_line) do
1028
+ <<-OUTPUT.gsub(/^\s+\|/, '').chomp
1029
+ |1..#{tests}
1030
+ |# tests: #{tests}, passed: #{passed}, failed: #{failed}, pending: #{pending}
1031
+ |# duration: #{duration} seconds
1032
+ |# seed: #{seed}
1033
+ OUTPUT
1034
+ end
1035
+
1036
+ before do
1037
+ report_printer.instance_variable_set(
1038
+ :@failed_examples,
1039
+ failed_examples_summary
1040
+ )
1041
+
1042
+ report_printer.instance_variable_set(
1043
+ :@pending_examples,
1044
+ pending_examples_summary
1045
+ )
1046
+ end
1047
+
1048
+ context 'when writing to file' do
1049
+ include_context('when writing to file')
1050
+
1051
+ it 'outputs tests stats to file' do
1052
+ report_printer.summary_output(notification, seed)
1053
+
1054
+ expect(report_output.string.chomp).to eq(output_line)
1055
+ end
1056
+
1057
+ it 'outputs failed and pending examples to stdout' do
1058
+ expect { report_printer.summary_output(notification, seed) }
1059
+ .to output(failed_and_pending_summary + "\n").to_stdout
1060
+ end
1061
+ end
1062
+
1063
+ context 'when not writing to file' do
1064
+ include_context('when not writing to file')
1065
+
1066
+ it 'outputs tests stats, failed and pending examples' do
1067
+ report_printer.summary_output(notification, seed)
1068
+
1069
+ expect(report_output.string.chomp)
1070
+ .to eq("#{output_line}\n#{failed_and_pending_summary}")
1071
+ end
1072
+ end
1073
+ end
1074
+ end
1075
+ end