File: reporter_spec.rb

package info (click to toggle)
ruby-rspec 3.12.0c0e1m1s0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 6,752 kB
  • sloc: ruby: 69,818; sh: 1,861; makefile: 99
file content (333 lines) | stat: -rw-r--r-- 11,424 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
module RSpec::Core
  RSpec.describe Reporter do
    include FormatterSupport

    let(:config)   { Configuration.new }
    let(:world)    { World.new(config) }
    let(:reporter) { Reporter.new config }
    let(:start_time) { Time.now }
    let(:example) { super() }

    describe "finish" do
      let(:formatter) { double("formatter") }

      %w[start_dump dump_pending dump_failures dump_summary close].map(&:to_sym).each do |message|
        it "sends #{message} to the formatter(s) that respond to message" do
          reporter.register_listener formatter, message
          expect(formatter.as_null_object).to receive(message)
          reporter.finish
        end

        it "doesnt notify formatters about messages they dont implement" do
          expect { reporter.finish }.to_not raise_error
        end
      end

      it "dumps the failure summary after the profile and deprecation summary so failures don't scroll off the screen and get missed" do
        config.profile_examples = 10
        formatter = instance_double("RSpec::Core::Formatter::ProgressFormatter")
        reporter.register_listener(formatter, :dump_summary, :dump_profile, :deprecation_summary)

        expect(formatter).to receive(:deprecation_summary).ordered
        expect(formatter).to receive(:dump_profile).ordered
        expect(formatter).to receive(:dump_summary).ordered

        reporter.finish
      end

      it "allows the profiler to be used without being manually setup" do
        config.profile_examples = true
        expect {
          reporter.finish
        }.to_not raise_error
      end
    end

    describe 'start' do
      before { config.start_time = start_time }

      it 'notifies the formatter of start with example count' do
        formatter = double("formatter")
        reporter.register_listener formatter, :start

        expect(formatter).to receive(:start) do |notification|
          expect(notification.count).to eq 3
          expect(notification.load_time).to eq 5
        end

        reporter.start 3, (start_time + 5)
      end

      it 'notifies the formatter of the seed used before notifing of start' do
        formatter = double("formatter")
        reporter.register_listener formatter, :seed
        reporter.register_listener formatter, :start
        expect(formatter).to receive(:seed).ordered.with(
          an_object_having_attributes(:seed => config.seed, :seed_used? => config.seed_used?)
        )
        expect(formatter).to receive(:start).ordered
        reporter.start 1
      end
    end

    context "given one formatter" do
      it "passes messages to that formatter" do
        formatter = double("formatter", :example_started => nil)
        reporter.register_listener formatter, :example_started
        example = new_example

        expect(formatter).to receive(:example_started) do |notification|
          expect(notification.example).to eq example
        end

        reporter.example_started(example)
      end

      it "passes messages to the formatter in the correct order" do
        order = []

        formatter = double("formatter")
        allow(formatter).to receive(:example_group_started)  { |n| order << "Started: #{n.group.description}" }
        allow(formatter).to receive(:example_started)        { |n| order << "Started Example" }
        allow(formatter).to receive(:example_finished)       { |n| order << "Finished Example" }
        allow(formatter).to receive(:example_passed)         { |n| order << "Passed" }
        allow(formatter).to receive(:example_pending)        { |n| order << "Pending" }
        allow(formatter).to receive(:example_failed)         { |n| order << "Failed" }
        allow(formatter).to receive(:example_group_finished) { |n| order << "Finished: #{n.group.description}" }

        reporter.register_listener formatter, :example_group_started, :example_group_finished,
                                              :example_started, :example_finished,
                                              :example_passed, :example_failed, :example_pending

        group = RSpec.describe("root")
        group.describe("context 1") do
          example("passing example") {}
          example("pending example", :skip => true) { }
        end
        group.describe("context 2") do
          example("failed example") { fail }
        end

        group.run(reporter)

        expect(order).to eq([
           "Started: root",
           "Started: context 1",
           "Started Example",
           "Finished Example",
           "Passed",
           "Started Example",
           "Finished Example",
           "Pending",
           "Finished: context 1",
           "Started: context 2",
           "Started Example",
           "Finished Example",
           "Failed",
           "Finished: context 2",
           "Finished: root"
        ])
      end
    end

    context "given an example group with no examples" do
      it "does not pass example_group_started or example_group_finished to formatter" do
        formatter = double("formatter")
        expect(formatter).not_to receive(:example_group_started)
        expect(formatter).not_to receive(:example_group_finished)

        reporter.register_listener formatter, :example_group_started, :example_group_finished

        group = RSpec.describe("root")

        group.run(reporter)
      end
    end

    context "given multiple formatters" do
      it "passes messages to all formatters" do
        formatters = (1..2).map { double("formatter", :example_started => nil) }
        example = new_example

        formatters.each do |formatter|
          expect(formatter).to receive(:example_started) do |notification|
            expect(notification.example).to eq example
          end
          reporter.register_listener formatter, :example_started
        end

        reporter.example_started(example)
      end
    end

    describe "#exit_early" do
      it "returns the passed exit code" do
        expect(reporter.exit_early(42)).to eq(42)
      end

      it "sends a complete cycle of notifications" do
        formatter = double("formatter")
        %w[seed start start_dump dump_pending dump_failures dump_summary seed close].map(&:to_sym).each do |message|
          reporter.register_listener formatter, message
          expect(formatter).to receive(message).ordered
        end
        reporter.exit_early(42)
      end
    end

    describe "#report" do
      it "supports one arg (count)" do
        reporter.report(1) {}
      end

      it "yields itself" do
        yielded = nil
        reporter.report(3) { |r| yielded = r }
        expect(yielded).to eq(reporter)
      end
    end

    describe "#register_listener" do
      let(:listener) { double("listener", :start => nil) }

      before { reporter.register_listener listener, :start }

      it 'will register the listener to specified notifications' do
        expect(reporter.registered_listeners :start).to eq [listener]
      end

      it 'will match string notification names' do
        reporter.register_listener listener, "stop"
        expect(reporter.registered_listeners :stop).to eq [listener]
      end

      it 'will send notifications when a subscribed event is triggered' do
        expect(listener).to receive(:start) do |notification|
          expect(notification.count).to eq 42
        end
        reporter.start 42
      end

      it 'will ignore duplicated listeners' do
        reporter.register_listener listener, :start
        expect(listener).to receive(:start).once
        reporter.start 42
      end
    end

    describe "#publish" do
      let(:listener) { double("listener", :custom => nil) }
      before do
        reporter.register_listener listener, :custom, :start
      end

      it 'will send custom events to registered listeners' do
        expect(listener).to receive(:custom).with(RSpec::Core::Notifications::NullNotification)
        reporter.publish :custom
      end

      it 'will raise when encountering RSpec standard events' do
        expect { reporter.publish :start }.to raise_error(
          StandardError,
          a_string_including("not internal RSpec ones")
        )
      end

      it 'will ignore event names sent as strings' do
        expect(listener).not_to receive(:custom)
        reporter.publish "custom"
      end

      it 'will provide a custom notification object based on the options hash' do
        expect(listener).to receive(:custom).with(
          an_object_having_attributes(:my_data => :value)
        )
        reporter.publish :custom, :my_data => :value
      end
    end

    describe "#abort_with" do
      before { allow(reporter).to receive(:exit!) }

      it "publishes the message and notifies :close" do
        listener = double("Listener")
        reporter.register_listener(listener, :message, :close)
        stream   = StringIO.new

        allow(listener).to receive(:message) { |n| stream << n.message }
        allow(listener).to receive(:close) { stream.close }

        reporter.register_listener(listener)
        reporter.abort_with("Booom!", 1)

        expect(stream).to have_attributes(:string => "Booom!").and be_closed
      end

      it "exits with the provided exit code" do
        reporter.abort_with("msg", 13)
        expect(reporter).to have_received(:exit!).with(13)
      end
    end

    describe "timing" do
      before do
        config.start_time = start_time
      end

      it "uses RSpec::Core::Time as to not be affected by changes to time in examples" do
        formatter = double(:formatter)
        reporter.register_listener formatter, :dump_summary
        reporter.start 1
        allow(Time).to receive_messages(:now => ::Time.utc(2012, 10, 1))

        duration = nil
        allow(formatter).to receive(:dump_summary) do |notification|
          duration = notification.duration
        end

        reporter.finish
        expect(duration).to be < 0.2
      end

      it "captures the load time so it can report it later" do
        formatter = instance_double("ProgressFormatter")
        reporter.register_listener formatter, :dump_summary
        reporter.start 3, (start_time + 5)

        expect(formatter).to receive(:dump_summary) do |notification|
          expect(notification.load_time).to eq(5)
        end

        reporter.finish
      end
    end

    describe "#notify_non_example_exception" do
      it "sends a `message` notification that contains the formatted exception details" do
        formatter_out = StringIO.new
        formatter = Formatters::ProgressFormatter.new(formatter_out)
        reporter.register_listener formatter, :message

        line = __LINE__ + 1
        exception = 1 / 0 rescue $!
        reporter.notify_non_example_exception(exception, "NonExample Context")

        expect(formatter_out.string).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp)
          |
          |NonExample Context
          |Failure/Error: exception = 1 / 0 rescue $!
          |
          |ZeroDivisionError:
          |  divided by 0
          |# #{Metadata.relative_path(__FILE__)}:#{line}
        EOS
      end

      it "records the fact that a non example failure has occurred" do
        expect {
          reporter.notify_non_example_exception(Exception.new, "NonExample Context")
        }.to change(world, :non_example_failure).from(a_falsey_value).to(true)
      end
    end
  end
end