Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit 9d49bcb

Browse files
committed
Merge pull request #1869 from rspec/allow_publishing_custom_events_via_reporter
Allow the reporter to send custom events to registered formatters
2 parents 9d12a30 + 4e906eb commit 9d49bcb

File tree

4 files changed

+85
-0
lines changed

4 files changed

+85
-0
lines changed

lib/rspec/core/notifications.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,5 +599,19 @@ def self.from_hash(data)
599599
# currently require no information, but we may wish to extend in future.
600600
class NullNotification
601601
end
602+
603+
# `CustomNotification` is used when sending custom events to formatters /
604+
# other registered listeners, it creates attributes based on supplied hash
605+
# of options.
606+
class CustomNotification < Struct
607+
# @param options [Hash] A hash of method / value pairs to create on this notification
608+
# @return [CustomNotification]
609+
#
610+
# Build a custom notification based on the supplied option key / values.
611+
def self.for(options={})
612+
return NullNotification if options.keys.empty?
613+
new(*options.keys).new(*options.values)
614+
end
615+
end
602616
end
603617
end

lib/rspec/core/reporter.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1+
require 'set'
12
module RSpec::Core
23
# A reporter will send notifications to listeners, usually formatters for the
34
# spec suite run.
45
class Reporter
6+
# @private
7+
RSPEC_NOTIFICATIONS = Set.new(
8+
[
9+
:close, :deprecation, :deprecation_summary, :dump_failures, :dump_pending,
10+
:dump_profile, :dump_summary, :example_failed, :example_group_finished,
11+
:example_group_started, :example_passed, :example_pending, :example_started,
12+
:message, :seed, :start, :start_dump, :stop
13+
])
14+
515
def initialize(configuration)
616
@configuration = configuration
717
@listeners = Hash.new { |h, k| h[k] = Set.new }
@@ -79,6 +89,19 @@ def message(message)
7989
notify :message, Notifications::MessageNotification.new(message)
8090
end
8191

92+
# @param event [Symbol] Name of the custom event to trigger on formatters
93+
# @param options [Hash] Hash of arguments to provide via `CustomNotification`
94+
#
95+
# Publish a custom event to supporting registered formatters.
96+
# @see RSpec::Core::Notifications::CustomNotification
97+
def publish(event, options={})
98+
if RSPEC_NOTIFICATIONS.include? event
99+
raise "RSpec::Core::Reporter#publish is intended for sending custom " \
100+
"events not internal RSpec ones, please rename your custom event."
101+
end
102+
notify event, Notifications::CustomNotification.for(options)
103+
end
104+
82105
# @private
83106
def example_group_started(group)
84107
notify :example_group_started, Notifications::GroupNotification.new(group) unless group.descendant_filtered_examples.empty?

spec/rspec/core/reporter_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,37 @@ module RSpec::Core
172172
end
173173
end
174174

175+
describe "#publish" do
176+
let(:listener) { double("listener", :custom => nil) }
177+
before do
178+
reporter.register_listener listener, :custom, :start
179+
end
180+
181+
it 'will send custom events to registered listeners' do
182+
expect(listener).to receive(:custom).with(RSpec::Core::Notifications::NullNotification)
183+
reporter.publish :custom
184+
end
185+
186+
it 'will raise when encountering RSpec standard events' do
187+
expect { reporter.publish :start }.to raise_error(
188+
StandardError,
189+
a_string_including("not internal RSpec ones")
190+
)
191+
end
192+
193+
it 'will ignore event names sent as strings' do
194+
expect(listener).not_to receive(:custom)
195+
reporter.publish "custom"
196+
end
197+
198+
it 'will provide a custom notification object based on the options hash' do
199+
expect(listener).to receive(:custom).with(
200+
an_object_having_attributes(:my_data => :value)
201+
)
202+
reporter.publish :custom, :my_data => :value
203+
end
204+
end
205+
175206
describe "timing" do
176207
before do
177208
config.start_time = start_time

spec/support/formatter_support.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,20 @@ def profile_notification(duration, examples, number)
275275
end
276276

277277
end
278+
279+
if RSpec::Support::RubyFeatures.module_prepends_supported?
280+
module RSpec::Core
281+
class Reporter
282+
module EnforceRSpecNotificationsListComplete
283+
def notify(event, *args)
284+
return super if caller_locations(1, 1).first.label =~ /publish/
285+
return super if RSPEC_NOTIFICATIONS.include?(event)
286+
287+
raise "#{event.inspect} must be added to `RSPEC_NOTIFICATIONS`"
288+
end
289+
end
290+
291+
prepend EnforceRSpecNotificationsListComplete
292+
end
293+
end
294+
end

0 commit comments

Comments
 (0)