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.
@@ -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