rspec-rayo 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.document +5 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +72 -0
  7. data/Rakefile +22 -0
  8. data/lib/rspec-rayo/rayo/call.rb +135 -0
  9. data/lib/rspec-rayo/rayo/driver.rb +110 -0
  10. data/lib/rspec-rayo/rayo/matchers/ask.rb +60 -0
  11. data/lib/rspec-rayo/rayo/matchers/call_control.rb +105 -0
  12. data/lib/rspec-rayo/rayo/matchers/conference.rb +73 -0
  13. data/lib/rspec-rayo/rayo/matchers/dtmf.rb +21 -0
  14. data/lib/rspec-rayo/rayo/matchers/input.rb +60 -0
  15. data/lib/rspec-rayo/rayo/matchers/join.rb +56 -0
  16. data/lib/rspec-rayo/rayo/matchers/output.rb +15 -0
  17. data/lib/rspec-rayo/rayo/matchers/recording.rb +32 -0
  18. data/lib/rspec-rayo/rayo/matchers/say.rb +15 -0
  19. data/lib/rspec-rayo/rayo/matchers/transfer.rb +31 -0
  20. data/lib/rspec-rayo/rayo/matchers.rb +138 -0
  21. data/lib/rspec-rayo/tropo1/driver.rb +51 -0
  22. data/lib/rspec-rayo/version.rb +3 -0
  23. data/lib/rspec-rayo.rb +7 -0
  24. data/rspec-rayo.gemspec +31 -0
  25. data/spec/rspec-rayo/rayo/matchers/ask_spec.rb +43 -0
  26. data/spec/rspec-rayo/rayo/matchers/call_control_spec.rb +83 -0
  27. data/spec/rspec-rayo/rayo/matchers/conference_spec.rb +54 -0
  28. data/spec/rspec-rayo/rayo/matchers/dtmf_spec.rb +16 -0
  29. data/spec/rspec-rayo/rayo/matchers/input_spec.rb +43 -0
  30. data/spec/rspec-rayo/rayo/matchers/join_spec.rb +33 -0
  31. data/spec/rspec-rayo/rayo/matchers/output_spec.rb +23 -0
  32. data/spec/rspec-rayo/rayo/matchers/say_spec.rb +23 -0
  33. data/spec/rspec-rayo/rayo/matchers/transfer_spec.rb +23 -0
  34. data/spec/rspec-rayo/rayo/matchers_spec.rb +30 -0
  35. data/spec/spec_helper.rb +8 -0
  36. metadata +193 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ Gemfile.lock
2
+
3
+ # rcov generated
4
+ coverage
5
+
6
+ # rdoc generated
7
+ rdoc
8
+
9
+ # yard generated
10
+ doc
11
+ .yardoc
12
+
13
+ # bundler
14
+ .bundle
15
+ pkg
16
+
17
+ spec/reports
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --tty
3
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Voxeo Corporation
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ rspec-rayo
2
+ ============
3
+
4
+ This library extends the Rspec testing library for Rayo specific expectations. The library also provides classes for Tropo1 and Rayo drivers.
5
+
6
+ Howto Install
7
+ -------------
8
+
9
+ gem install rspec-rayo
10
+
11
+ Example Driver Setup
12
+ --------------------
13
+
14
+ ap "Starting RayoDriver to manage events over XMPP."
15
+ @rayo = RSpecRayo::RayoDriver.new :username => @config['rayo_server']['jid'],
16
+ :password => @config['rayo_server']['password'],
17
+ :wire_logger => Logger.new(@config['rayo_server']['wire_log']),
18
+ :transport_logger => Logger.new(@config['rayo_server']['transport_log'])
19
+
20
+ ap "Starting Tropo1Driver to host scripts via DRb and launch calls via HTTP."
21
+ @tropo1 = RSpecRayo::Tropo1Driver.new(@config['tropo1']['druby_uri'])
22
+
23
+ Custom Matchers
24
+ ---------------
25
+
26
+ * have_executed_correctly
27
+ * have_dialed_correctly
28
+ * be_a_valid_complete_hangup_event
29
+ * be_a_valid_complete_error_event
30
+ * be_a_valid_complete_stopped_event
31
+
32
+ * be_a_valid_successful_ask_event
33
+ * be_a_valid_ask_noinput_event
34
+ * be_a_valid_ask_nomatch_event
35
+
36
+ * be_a_valid_offer_event
37
+ * be_a_valid_answered_event
38
+ * be_a_valid_hangup_event
39
+ * be_a_valid_ringing_event
40
+ * be_a_valid_redirect_event
41
+ * be_a_valid_reject_event
42
+
43
+ * be_a_valid_conference_offhold_event
44
+ * be_a_valid_speaking_event
45
+ * be_a_valid_finished_speaking_event
46
+ * be_a_valid_conference_complete_terminator_event
47
+
48
+ * be_a_valid_dtmf_event
49
+
50
+ * be_a_valid_successful_input_event
51
+ * be_a_valid_input_noinput_event
52
+ * be_a_valid_input_nomatch_event
53
+
54
+ * be_a_valid_joined_event
55
+ * be_a_valid_unjoined_event
56
+
57
+ * be_a_valid_output_event
58
+
59
+ * be_a_valid_complete_recording_event
60
+ * be_a_valid_stopped_recording_event
61
+
62
+ * be_a_valid_say_event
63
+
64
+ * be_a_valid_transfer_event
65
+ * be_a_valid_transfer_timeout_event
66
+
67
+
68
+ Copyright
69
+ ---------
70
+
71
+ Copyright (c) 2011 Voxeo Corporation. See LICENSE.txt for further details.
72
+
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'rspec/core'
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec) do |spec|
8
+ spec.pattern = FileList['spec/**/*_spec.rb']
9
+ end
10
+
11
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
12
+ spec.pattern = 'spec/**/*_spec.rb'
13
+ spec.rcov = true
14
+ end
15
+
16
+ task :default => :spec
17
+
18
+ require 'ci/reporter/rake/rspec'
19
+ task :hudson => ['ci:setup:rspec', :spec]
20
+
21
+ require 'yard'
22
+ YARD::Rake::YardocTask.new
@@ -0,0 +1,135 @@
1
+ require 'future-resource'
2
+
3
+ module RSpecRayo
4
+ class Call
5
+ attr_accessor :status, :call_id
6
+ attr_reader :queue
7
+
8
+ def initialize(options)
9
+ @call_event = FutureResource.new
10
+ self.call_event = options[:call_event] if options[:call_event]
11
+ @ring_event = FutureResource.new
12
+ @protocol = options[:protocol]
13
+ @queue = options[:queue]
14
+ @read_timeout = options[:read_timeout] || 5
15
+ @write_timeout = options[:write_timeout] || 5
16
+ @status = :offered
17
+ end
18
+
19
+ def accept
20
+ write(Punchblock::Command::Accept.new).tap do |response|
21
+ @status = :accepted if response
22
+ end
23
+ end
24
+
25
+ def answer
26
+ write Punchblock::Command::Answer.new
27
+ end
28
+
29
+ def ask(options = {})
30
+ write Punchblock::Component::Tropo::Ask.new(options)
31
+ end
32
+
33
+ def conference(options = {})
34
+ write Punchblock::Component::Tropo::Conference.new(options)
35
+ end
36
+
37
+ def dial(options = {})
38
+ write Punchblock::Command::Dial.new(options)
39
+ end
40
+
41
+ def hangup
42
+ write(Punchblock::Command::Hangup.new).tap do |response|
43
+ @status = :finished if response
44
+ end
45
+ end
46
+
47
+ def redirect(options = {})
48
+ write Punchblock::Command::Redirect.new(options)
49
+ end
50
+
51
+ def reject(reason = nil)
52
+ write(Punchblock::Command::Reject.new(reason)).tap do |response|
53
+ @status = :finished if response
54
+ end
55
+ end
56
+
57
+ def say(options = {})
58
+ write Punchblock::Component::Tropo::Say.new(options)
59
+ end
60
+
61
+ def transfer(options = {})
62
+ write Punchblock::Component::Tropo::Transfer.new(options)
63
+ end
64
+
65
+ def record(options = {})
66
+ write Punchblock::Component::Record.new(options)
67
+ end
68
+
69
+ def output(options = {})
70
+ write Punchblock::Component::Output.new(options)
71
+ end
72
+
73
+ def input(options = {})
74
+ write Punchblock::Component::Input.new(options)
75
+ end
76
+
77
+ def join(options = {})
78
+ write Punchblock::Command::Join.new(options)
79
+ end
80
+
81
+ def unjoin(options = {})
82
+ write Punchblock::Command::Unjoin.new(options)
83
+ end
84
+
85
+ def mute
86
+ write Punchblock::Command::Mute.new
87
+ end
88
+
89
+ def unmute
90
+ write Punchblock::Command::Unmute.new
91
+ end
92
+
93
+ def dtmf(tones)
94
+ write Punchblock::Command::DTMF.new(:tones => tones)
95
+ end
96
+
97
+ def last_event?(timeout = 2)
98
+ begin
99
+ next_event timeout
100
+ rescue Timeout::Error
101
+ true
102
+ end
103
+ end
104
+
105
+ def next_event(timeout = nil)
106
+ Timeout::timeout(timeout || @read_timeout) { @queue.pop }
107
+ end
108
+
109
+ def call_event
110
+ @call_event.resource @write_timeout
111
+ end
112
+
113
+ def call_event=(other)
114
+ raise ArgumentError, 'Call event must be a Punchblock::Event::Offer' unless other.is_a? Punchblock::Event::Offer
115
+ @call_event.resource = other
116
+ @call_id = other.call_id
117
+ end
118
+
119
+ def ring_event
120
+ @ring_event.resource @write_timeout
121
+ end
122
+
123
+ def ring_event=(other)
124
+ raise ArgumentError, 'Ring event must be a Punchblock::Event::Ringing' unless other.is_a? Punchblock::Event::Ringing
125
+ @ring_event.resource = other
126
+ end
127
+
128
+ private
129
+
130
+ def write(msg)
131
+ response = @protocol.write @call_id, msg
132
+ msg if response
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,110 @@
1
+ module RSpecRayo
2
+ class RayoDriver
3
+ attr_reader :event_queue, :threads
4
+ attr_accessor :calls
5
+
6
+ def initialize(options)
7
+ @calls = {}
8
+ @call_queue = Queue.new
9
+ @queue_timeout = options[:queue_timeout] || 5
10
+ @write_timeout = options[:write_timeout] || 5
11
+ @threads = []
12
+
13
+ initialize_rayo options
14
+ end
15
+
16
+ def get_call
17
+ read_queue @call_queue
18
+ end
19
+
20
+ def cleanup_calls
21
+ @calls.each_pair do |call_id, call|
22
+ call.hangup unless call.status == :finished
23
+ end
24
+ @calls = {}
25
+ end
26
+
27
+ def dial(options)
28
+ Call.new(:protocol => @rayo, :queue => Queue.new, :read_timeout => @queue_timeout, :write_timeout => @write_timeout).tap do |call|
29
+ dial = call.dial options
30
+ call.call_id = dial.call_id
31
+ @calls.merge! call.call_id => call
32
+ end
33
+ end
34
+
35
+ def start_event_dispatcher
36
+ @threads << Thread.new do
37
+ event = nil
38
+
39
+ until event == 'STOP' do
40
+ event = @event_queue.pop
41
+ case event
42
+ when Punchblock::Event::Offer
43
+ call = Call.new :call_event => event,
44
+ :protocol => @rayo,
45
+ :queue => Queue.new,
46
+ :read_timeout => @queue_timeout,
47
+ :write_timeout => @write_timeout
48
+ @calls.merge! event.call_id => call
49
+ @call_queue.push call
50
+ when Punchblock::Event::Ringing
51
+ call = @calls[event.call_id]
52
+ call.ring_event = event if call
53
+ else
54
+ # Temp based on this nil returned on conference: https://github.com/tropo/punchblock/issues/27
55
+ begin
56
+ if event.is_a?(Punchblock::Event::End)
57
+ @calls[event.call_id].status = :finished
58
+ end
59
+ @calls[event.call_id].queue.push event unless event.nil?
60
+ rescue => error
61
+ # Event nil issue to be addressed here: https://github.com/tropo/rspec-rayo/issues/2
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+
69
+ def read_queue(queue)
70
+ Timeout::timeout(@queue_timeout) { queue.pop }
71
+ end
72
+
73
+ def initialize_rayo(options)
74
+ initialize_logging options
75
+
76
+ # Setup our Rayo environment
77
+ @rayo = Punchblock::Connection.new :username => options[:username],
78
+ :password => options[:password],
79
+ :wire_logger => @wire_logger,
80
+ :transport_logger => @transport_logger,
81
+ :auto_reconnect => false,
82
+ :write_timeout => options[:write_timeout]
83
+ @event_queue = @rayo.event_queue
84
+
85
+ start_rayo
86
+ end
87
+
88
+ def initialize_logging(options)
89
+ @wire_logger = options[:wire_logger]
90
+ @wire_logger.level = options[:log_level]
91
+ #@wire_logger.info "Starting up..." if @wire_logger
92
+
93
+ @transport_logger = options[:transport_logger]
94
+ @transport_logger.level = options[:log_level]
95
+ #@transport_logger.info "Starting up..." if @transport_logger
96
+ end
97
+
98
+ def start_rayo
99
+ # Launch the Rayo thread
100
+ @threads << Thread.new do
101
+ begin
102
+ @rayo.run
103
+ rescue => e
104
+ puts "Exception in XMPP thread! #{e.message}"
105
+ puts e.backtrace.join("\t\n")
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,60 @@
1
+ RSpec::Matchers.define :be_a_valid_successful_ask_event do
2
+ chain :with_utterance do |utterance|
3
+ @utterance = utterance
4
+ end
5
+
6
+ chain :with_interpretation do |interpretation|
7
+ @interpretation = interpretation
8
+ end
9
+
10
+ match_for_should do |event|
11
+ basic_validation event, Punchblock::Event::Complete, true do
12
+ match_type event.reason, Punchblock::Component::Tropo::Ask::Complete::Success
13
+ @error = "The utterance was not correct. Expected '#{@utterance}', got '#{event.reason.utterance}'" if @utterance && event.reason.utterance != @utterance
14
+ @error = "The interpretation was not correct. Expected '#{@interpretation}', got '#{event.reason.interpretation}'" if @interpretation && event.reason.interpretation != @interpretation
15
+ end
16
+ end
17
+
18
+ failure_message_for_should do |actual|
19
+ "The ask event was not valid: #{@error}"
20
+ end
21
+
22
+ description do
23
+ "be a valid successful ask event".tap do |d|
24
+ d << " with utterance '#{@utterance}'" if @utterance
25
+ d << " with interpretation '#{@interpretation}'" if @interpretation
26
+ end
27
+ end
28
+ end
29
+
30
+ RSpec::Matchers.define :be_a_valid_ask_noinput_event do
31
+ match_for_should do |event|
32
+ basic_validation event, Punchblock::Event::Complete, true do
33
+ match_type event.reason, Punchblock::Component::Tropo::Ask::Complete::NoInput
34
+ end
35
+ end
36
+
37
+ failure_message_for_should do |actual|
38
+ "The ask event was not valid: #{@error}"
39
+ end
40
+
41
+ description do
42
+ "be a valid noinput ask event"
43
+ end
44
+ end
45
+
46
+ RSpec::Matchers.define :be_a_valid_ask_nomatch_event do
47
+ match_for_should do |event|
48
+ basic_validation event, Punchblock::Event::Complete, true do
49
+ match_type event.reason, Punchblock::Component::Tropo::Ask::Complete::NoMatch
50
+ end
51
+ end
52
+
53
+ failure_message_for_should do |actual|
54
+ "The ask event was not valid: #{@error}"
55
+ end
56
+
57
+ description do
58
+ "be a valid nomatch ask event"
59
+ end
60
+ end
@@ -0,0 +1,105 @@
1
+ RSpec::Matchers.define :be_a_valid_offer_event do
2
+ match_for_should do |event|
3
+ basic_validation event, Punchblock::Event::Offer do
4
+ event.headers_hash.each do |k,v|
5
+ if v.nil?
6
+ @error = "#{k.to_s} is nil - expected a value"
7
+ raise RSpec::Expectations::ExpectationNotMetError
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ failure_message_for_should do |actual|
14
+ "The offer event was not valid: #{@error}"
15
+ end
16
+
17
+ description do
18
+ "be a valid offer event"
19
+ end
20
+ end
21
+
22
+ RSpec::Matchers.define :be_a_valid_answered_event do
23
+ match_for_should do |event|
24
+ basic_validation event, Punchblock::Event::Answered
25
+ end
26
+
27
+ failure_message_for_should do |actual|
28
+ "The answer event was not valid: #{@error}"
29
+ end
30
+
31
+ description do
32
+ "be a valid answered event"
33
+ end
34
+ end
35
+
36
+ RSpec::Matchers.define :be_a_valid_hangup_event do
37
+ match_for_should do |event|
38
+ basic_validation event, Punchblock::Event::End do
39
+ unless event.reason == :hangup
40
+ @error = "got #{event.reason.inspect} - expected :hangup"
41
+ raise RSpec::Expectations::ExpectationNotMetError
42
+ end
43
+ end
44
+ end
45
+
46
+ failure_message_for_should do |actual|
47
+ "The hangup event was not valid: #{@error}"
48
+ end
49
+
50
+ description do
51
+ "be a valid hangup event"
52
+ end
53
+ end
54
+
55
+ RSpec::Matchers.define :be_a_valid_ringing_event do
56
+ match_for_should do |event|
57
+ basic_validation event, Punchblock::Event::Ringing
58
+ end
59
+
60
+ failure_message_for_should do |actual|
61
+ "The ring event was not valid: #{@error}"
62
+ end
63
+
64
+ description do
65
+ "be a valid ringing event"
66
+ end
67
+ end
68
+
69
+ RSpec::Matchers.define :be_a_valid_redirect_event do
70
+ match_for_should do |event|
71
+ basic_validation event, Punchblock::Event::End do
72
+ unless event.reason == :redirect
73
+ @error = "got #{event.reason.inspect} - expected :redirect"
74
+ raise RSpec::Expectations::ExpectationNotMetError
75
+ end
76
+ end
77
+ end
78
+
79
+ failure_message_for_should do |actual|
80
+ "The reject event was not valid: #{@error}"
81
+ end
82
+
83
+ description do
84
+ "be a valid redirect event"
85
+ end
86
+ end
87
+
88
+ RSpec::Matchers.define :be_a_valid_reject_event do
89
+ match_for_should do |event|
90
+ basic_validation event, Punchblock::Event::End do
91
+ unless event.reason == :reject
92
+ @error = "got #{event.reason.inspect} - expected :reject"
93
+ raise RSpec::Expectations::ExpectationNotMetError
94
+ end
95
+ end
96
+ end
97
+
98
+ failure_message_for_should do |actual|
99
+ "The reject event was not valid: #{@error}"
100
+ end
101
+
102
+ description do
103
+ "be a valid reject event"
104
+ end
105
+ end
@@ -0,0 +1,73 @@
1
+ RSpec::Matchers.define :be_a_valid_conference_offhold_event do
2
+ match_for_should do |event|
3
+ basic_validation event, Punchblock::Component::Tropo::Conference::OffHold, true
4
+ end
5
+
6
+ failure_message_for_should do |actual|
7
+ "The ask event was not valid: #{@error}"
8
+ end
9
+
10
+ description do
11
+ "Validate an ask event"
12
+ end
13
+ end
14
+
15
+ RSpec::Matchers.define :be_a_valid_speaking_event do
16
+ chain :for_call_id do |call_id|
17
+ @call_id = call_id
18
+ end
19
+
20
+ match_for_should do |event|
21
+ basic_validation event, Punchblock::Component::Tropo::Conference::Speaking, true do
22
+ @error = "The speaking call ID was not correct. Expected '#{@call_id}', got '#{event.speaking_call_id}'" if @call_id && event.speaking_call_id != @call_id
23
+ end
24
+ end
25
+
26
+ failure_message_for_should do |actual|
27
+ "The speaking event was not valid: #{@error}"
28
+ end
29
+
30
+ description do
31
+ "be a valid speaking event".tap do |d|
32
+ d << " with call ID '#{@call_id}'" if @call_id
33
+ end
34
+ end
35
+ end
36
+
37
+ RSpec::Matchers.define :be_a_valid_finished_speaking_event do
38
+ chain :for_call_id do |call_id|
39
+ @call_id = call_id
40
+ end
41
+
42
+ match_for_should do |event|
43
+ basic_validation event, Punchblock::Component::Tropo::Conference::FinishedSpeaking, true do
44
+ @error = "The finished-speaking call ID was not correct. Expected '#{@call_id}', got '#{event.speaking_call_id}'" if @call_id && event.speaking_call_id != @call_id
45
+ end
46
+ end
47
+
48
+ failure_message_for_should do |actual|
49
+ "The finished speaking event was not valid: #{@error}"
50
+ end
51
+
52
+ description do
53
+ "be a valid finished speaking event".tap do |d|
54
+ d << " with call ID '#{@call_id}'" if @call_id
55
+ end
56
+ end
57
+ end
58
+
59
+ RSpec::Matchers.define :be_a_valid_conference_complete_terminator_event do
60
+ match_for_should do |event|
61
+ basic_validation event, Punchblock::Event::Complete, true do
62
+ match_type event.reason, Punchblock::Component::Tropo::Conference::Complete::Terminator
63
+ end
64
+ end
65
+
66
+ failure_message_for_should do |actual|
67
+ "The conference complete terminator event was not valid: #{@error}"
68
+ end
69
+
70
+ description do
71
+ "be a valid conference complete terminator event"
72
+ end
73
+ end
@@ -0,0 +1,21 @@
1
+ RSpec::Matchers.define :be_a_valid_dtmf_event do
2
+ chain :with_signal do |signal|
3
+ @signal = signal
4
+ end
5
+
6
+ match_for_should do |event|
7
+ basic_validation event, Punchblock::Event::DTMF do
8
+ @error = "The signal was not correct. Expected '#{@signal}', got '#{event.signal}'" if @signal && event.signal != @signal
9
+ end
10
+ end
11
+
12
+ failure_message_for_should do |actual|
13
+ "The DTMF event was not valid: #{@error}"
14
+ end
15
+
16
+ description do
17
+ "be a valid DTMF event".tap do |d|
18
+ d << " with signal '#{@signal}'" if @signal
19
+ end
20
+ end
21
+ end