spec/adhearsion/call_controller/output_spec.rb in adhearsion-2.0.1 vs spec/adhearsion/call_controller/output_spec.rb in adhearsion-2.1.0

- old
+ new

@@ -5,95 +5,174 @@ module Adhearsion class CallController describe Output do include CallControllerTestHelpers - describe "#play_ssml" do - let(:ssml) { RubySpeech::SSML.draw { string "BOO" } } + def expect_ssml_output(ssml) + expect_component_execution Punchblock::Component::Output.new(:ssml => ssml) + end - it 'executes an Output with the correct ssml' do - expect_component_execution Punchblock::Component::Output.new(:ssml => ssml.to_s) - subject.play_ssml ssml + def expect_async_ssml_output(ssml) + expect_message_waiting_for_response Punchblock::Component::Output.new(:ssml => ssml) + end + + describe "#player" do + it "should return a Player component targetted at the current controller" do + player = controller.player + player.should be_a Output::Player + player.controller.should be controller end - describe "if an error is returned" do - before do - pending - subject.should_receive(:execute_component_and_await_completion).once.and_raise(StandardError) - end + it "should return the same player every time" do + controller.player.should be controller.player + end + end - it 'should return false' do - pending - subject.play_ssml(ssml).should be false - end + describe "#async_player" do + it "should return an AsyncPlayer component targetted at the current controller" do + player = controller.async_player + player.should be_a Output::AsyncPlayer + player.controller.should be controller end + + it "should return the same player every time" do + controller.async_player.should be controller.async_player + end end describe "#play_audio" do - let(:audio_file) { "/sounds/boo.wav" } - let(:fallback) { "text for tts" } + let(:audio_file) { "/sounds/boo.wav" } - let(:ssml) do + let :ssml do file = audio_file RubySpeech::SSML.draw { audio :src => file } end - let(:ssml_with_fallback) do + it 'plays the correct ssml' do + expect_ssml_output ssml + subject.play_audio(audio_file).should be true + end + + context "with a fallback" do + let(:fallback) { "text for tts" } + + let :ssml do + file = audio_file + fallback_text = fallback + RubySpeech::SSML.draw do + audio(:src => file) { fallback_text } + end + end + + it 'places the fallback in the SSML doc' do + expect_ssml_output ssml + subject.play_audio(audio_file, :fallback => fallback).should be true + end + end + end + + describe "#play_audio!" do + let(:audio_file) { "/sounds/boo.wav" } + + let :ssml do file = audio_file - fallback_text = fallback - RubySpeech::SSML.draw { - audio(:src => file) { fallback_text } - } + RubySpeech::SSML.draw { audio :src => file } end it 'plays the correct ssml' do - subject.should_receive(:play_ssml).once.with(ssml).and_return true - subject.play_audio(audio_file).should be true + expect_async_ssml_output ssml + subject.play_audio!(audio_file).should be_a Punchblock::Component::Output end - it 'allows for fallback tts' do - subject.should_receive(:play_ssml).once.with(ssml_with_fallback).and_return true - subject.play_audio(audio_file, :fallback => fallback).should be true + context "with a fallback" do + let(:fallback) { "text for tts" } + + let :ssml do + file = audio_file + fallback_text = fallback + RubySpeech::SSML.draw do + audio(:src => file) { fallback_text } + end + end + + it 'places the fallback in the SSML doc' do + expect_async_ssml_output ssml + subject.play_audio!(audio_file, :fallback => fallback).should be_a Punchblock::Component::Output + end end end describe "#play_numeric" do - let :expected_doc do + let :ssml do RubySpeech::SSML.draw do say_as(:interpret_as => 'cardinal') { "123" } end end describe "with a number" do let(:input) { 123 } it 'plays the correct ssml' do - subject.should_receive(:play_ssml).once.with(expected_doc).and_return true + expect_ssml_output ssml subject.play_numeric(input).should be true end end describe "with a string representation of a number" do let(:input) { "123" } it 'plays the correct ssml' do - subject.should_receive(:play_ssml).once.with(expected_doc).and_return true + expect_ssml_output ssml subject.play_numeric(input).should be true end end describe "with something that's not a number" do let(:input) { 'foo' } - it 'returns nil' do - subject.play_numeric(input).should be nil + it 'raises ArgumentError' do + lambda { subject.play_numeric input }.should raise_error(ArgumentError) end end end + describe "#play_numeric!" do + let :ssml do + RubySpeech::SSML.draw do + say_as(:interpret_as => 'cardinal') { "123" } + end + end + + describe "with a number" do + let(:input) { 123 } + + it 'plays the correct ssml' do + expect_async_ssml_output ssml + subject.play_numeric!(input).should be_a Punchblock::Component::Output + end + end + + describe "with a string representation of a number" do + let(:input) { "123" } + + it 'plays the correct ssml' do + expect_async_ssml_output ssml + subject.play_numeric!(input).should be_a Punchblock::Component::Output + end + end + + describe "with something that's not a number" do + let(:input) { 'foo' } + + it 'raises ArgumentError' do + lambda { subject.play_numeric! input }.should raise_error(ArgumentError) + end + end + end + describe "#play_time" do - let :expected_doc do + let :ssml do content = input.to_s opts = expected_say_as_options RubySpeech::SSML.draw do say_as(opts) { content } end @@ -102,32 +181,32 @@ describe "with a time" do let(:input) { Time.parse("12/5/2000") } let(:expected_say_as_options) { {:interpret_as => 'time'} } it 'plays the correct SSML' do - subject.should_receive(:play_ssml).once.with(expected_doc).and_return true + expect_ssml_output ssml subject.play_time(input).should be true end end describe "with a date" do let(:input) { Date.parse('2011-01-23') } let(:expected_say_as_options) { {:interpret_as => 'date'} } it 'plays the correct SSML' do - subject.should_receive(:play_ssml).once.with(expected_doc).and_return true + expect_ssml_output ssml subject.play_time(input).should be true end end describe "with a date and a say_as format" do let(:input) { Date.parse('2011-01-23') } let(:format) { "d-m-y" } let(:expected_say_as_options) { {:interpret_as => 'date', :format => format} } it 'plays the correct SSML' do - subject.should_receive(:play_ssml).once.with(expected_doc).and_return true + expect_ssml_output ssml subject.play_time(input, :format => format).should be true end end describe "with a date and a strftime option" do @@ -135,11 +214,11 @@ let(:base_input) { Date.parse('2011-01-23') } let(:input) { base_input.strftime(strftime) } let(:expected_say_as_options) { {:interpret_as => 'date'} } it 'plays the correct SSML' do - subject.should_receive(:play_ssml).once.with(expected_doc).and_return true + expect_ssml_output ssml subject.play_time(base_input, :strftime => strftime).should be true end end describe "with a date, a format option and a strftime option" do @@ -148,247 +227,390 @@ let(:base_input) { Date.parse('2011-01-23') } let(:input) { base_input.strftime(strftime) } let(:expected_say_as_options) { {:interpret_as => 'date', :format => format} } it 'plays the correct SSML' do - subject.should_receive(:play_ssml).once.with(expected_doc).and_return true + expect_ssml_output ssml subject.play_time(base_input, :format => format, :strftime => strftime).should be true end end describe "with an object other than Time, DateTime, or Date" do let(:input) { "foo" } - it 'returns false' do - subject.play_time(input).should be false + it 'raises ArgumentError' do + lambda { subject.play_time input }.should raise_error(ArgumentError) end end end + describe "#play_time!" do + let :ssml do + content = input.to_s + opts = expected_say_as_options + RubySpeech::SSML.draw do + say_as(opts) { content } + end + end + + describe "with a time" do + let(:input) { Time.parse("12/5/2000") } + let(:expected_say_as_options) { {:interpret_as => 'time'} } + + it 'plays the correct SSML' do + expect_async_ssml_output ssml + subject.play_time!(input).should be_a Punchblock::Component::Output + end + end + + describe "with a date" do + let(:input) { Date.parse('2011-01-23') } + let(:expected_say_as_options) { {:interpret_as => 'date'} } + + it 'plays the correct SSML' do + expect_async_ssml_output ssml + subject.play_time!(input).should be_a Punchblock::Component::Output + end + end + + describe "with a date and a say_as format" do + let(:input) { Date.parse('2011-01-23') } + let(:format) { "d-m-y" } + let(:expected_say_as_options) { {:interpret_as => 'date', :format => format} } + + it 'plays the correct SSML' do + expect_async_ssml_output ssml + subject.play_time!(input, :format => format).should be_a Punchblock::Component::Output + end + end + + describe "with a date and a strftime option" do + let(:strftime) { "%d-%m-%Y" } + let(:base_input) { Date.parse('2011-01-23') } + let(:input) { base_input.strftime(strftime) } + let(:expected_say_as_options) { {:interpret_as => 'date'} } + + it 'plays the correct SSML' do + expect_async_ssml_output ssml + subject.play_time!(base_input, :strftime => strftime).should be_a Punchblock::Component::Output + end + end + + describe "with a date, a format option and a strftime option" do + let(:strftime) { "%d-%m-%Y" } + let(:format) { "d-m-y" } + let(:base_input) { Date.parse('2011-01-23') } + let(:input) { base_input.strftime(strftime) } + let(:expected_say_as_options) { {:interpret_as => 'date', :format => format} } + + it 'plays the correct SSML' do + expect_async_ssml_output ssml + subject.play_time!(base_input, :format => format, :strftime => strftime).should be_a Punchblock::Component::Output + end + end + + describe "with an object other than Time, DateTime, or Date" do + let(:input) { "foo" } + + it 'raises ArgumentError' do + lambda { subject.play_time! input }.should raise_error(ArgumentError) + end + end + end + describe '#play' do describe "with a single string" do - let(:file) { "cents-per-minute" } + let(:audio_file) { "/foo/bar.wav" } + let :ssml do + file = audio_file + RubySpeech::SSML.draw { audio :src => file } + end it 'plays the audio file' do - subject.should_receive(:play_ssml_for).once.with(file).and_return true - subject.play(file).should be true + expect_ssml_output ssml + subject.play(audio_file).should be true end end - describe "with multiple strings" do - let(:args) { ['rock', 'paperz', 'scissors'] } - - it 'plays multiple files' do - args.each do |file| - subject.should_receive(:play_ssml_for).once.with(file).and_return true + describe "with multiple arguments" do + let(:args) { ["/foo/bar.wav", 1, Time.now] } + let :ssml do + file = args[0] + n = args[1].to_s + t = args[2].to_s + RubySpeech::SSML.draw do + audio :src => file + say_as(:interpret_as => 'cardinal') { n } + say_as(:interpret_as => 'time') { t } end + end + it 'plays all arguments in one document' do + expect_ssml_output ssml subject.play(*args).should be true end + end - describe "if an audio file cannot be found" do - before do - pending - subject.should_receive(:play_audio).with(args[0]).and_return(true).ordered - subject.should_receive(:play_audio).with(args[1]).and_return(false).ordered - subject.should_receive(:play_audio).with(args[2]).and_return(true).ordered - end + describe "with a number" do + let(:argument) { 123 } - it 'should return false' do - subject.play(*args).should be false + let(:ssml) do + number = argument.to_s + RubySpeech::SSML.draw do + say_as(:interpret_as => 'cardinal') { number } end end - end - describe "with a number" do it 'plays the number' do - subject.should_receive(:play_ssml_for).with(123).and_return(true) - subject.play(123).should be true + expect_ssml_output ssml + subject.play(argument).should be true end end describe "with a string representation of a number" do + let(:argument) { '123' } + + let(:ssml) do + number = argument + RubySpeech::SSML.draw do + say_as(:interpret_as => 'cardinal') { number } + end + end + it 'plays the number' do - subject.should_receive(:play_ssml_for).with('123').and_return(true) - subject.play('123').should be true + expect_ssml_output ssml + subject.play(argument).should be true end end describe "with a time" do - let(:time) { Time.parse("12/5/2000") } + let(:time) { Time.parse "12/5/2000" } + let(:ssml) do + t = time.to_s + RubySpeech::SSML.draw do + say_as(:interpret_as => 'time') { t } + end + end + it 'plays the time' do - subject.should_receive(:play_ssml_for).with(time).and_return(true) + expect_ssml_output ssml subject.play(time).should be true end end describe "with a date" do - let(:date) { Date.parse('2011-01-23') } + let(:date) { Date.parse '2011-01-23' } + let(:ssml) do + d = date.to_s + RubySpeech::SSML.draw do + say_as(:interpret_as => 'date') { d } + end + end it 'plays the time' do - subject.should_receive(:play_ssml_for).with(date).and_return(true) + expect_ssml_output ssml subject.play(date).should be true end end describe "with an array containing a Date/DateTime/Time object and a hash" do - let(:date) { Date.parse('2011-01-23') } + let(:date) { Date.parse '2011-01-23' } let(:format) { "d-m-y" } let(:strftime) { "%d-%m%Y" } + let :ssml do + d = date.strftime strftime + f = format + RubySpeech::SSML.draw do + say_as(:interpret_as => 'date', :format => f) { d } + end + end + it 'plays the time with the specified format and strftime' do - subject.should_receive(:play_ssml_for).with(date, {:format => format, :strftime => strftime}).and_return(true) - subject.play({:value => date, :format => format, :strftime => strftime}).should be true + expect_ssml_output ssml + subject.play(:value => date, :format => format, :strftime => strftime).should be true end end describe "with an SSML document" do let(:ssml) { RubySpeech::SSML.draw { string "Hello world" } } it "plays the SSML without generating" do - subject.should_receive(:play_ssml).with(ssml).and_return(true) + expect_ssml_output ssml subject.play(ssml).should be true end end end - describe "#play!" do - let(:prompt) { "Press any button." } - let(:second_prompt) { "Or press nothing." } - let(:non_existing) { "http://adhearsion.com/nonexistingfile.mp3" } + describe '#play!' do + describe "with a single string" do + let(:audio_file) { "/foo/bar.wav" } + let :ssml do + file = audio_file + RubySpeech::SSML.draw { audio :src => file } + end - it "calls play a single time" do - subject.should_receive(:play).once.with(prompt).and_return(true) - subject.play!(prompt) + it 'plays the audio file' do + expect_async_ssml_output ssml + subject.play!(audio_file).should be_a Punchblock::Component::Output + end end - it "calls play two times" do - subject.should_receive(:play).once.with(prompt, second_prompt).and_return(true) - subject.play!(prompt, second_prompt) - end + describe "with multiple arguments" do + let(:args) { ["/foo/bar.wav", 1, Time.now] } + let :ssml do + file = args[0] + n = args[1].to_s + t = args[2].to_s + RubySpeech::SSML.draw do + audio :src => file + say_as(:interpret_as => 'cardinal') { n } + say_as(:interpret_as => 'time') { t } + end + end - it "raises an exception if play fails" do - subject.should_receive(:play).once.and_return false - expect { subject.play!(non_existing) }.to raise_error(Output::PlaybackError) + it 'plays all arguments in one document' do + expect_async_ssml_output ssml + subject.play!(*args).should be_a Punchblock::Component::Output + end end - end - describe "#speak" do - it "should be an alias for #say" do - subject.method(:speak).should be == subject.method(:say) - end - end + describe "with a number" do + let(:argument) { 123 } - describe "#say" do - describe "with a RubySpeech document" do - it 'plays the correct SSML' do - ssml = RubySpeech::SSML.draw { string "Hello world" } - expect_component_execution Punchblock::Component::Output.new(:ssml => ssml) - subject.say(ssml).should be_a Punchblock::Component::Output + let(:ssml) do + number = argument.to_s + RubySpeech::SSML.draw do + say_as(:interpret_as => 'cardinal') { number } + end end - end - describe "with a string" do - it 'outputs the correct text' do - str = "Hello world" - ssml = RubySpeech::SSML.draw { string str } - expect_component_execution Punchblock::Component::Output.new(:ssml => ssml) - subject.say(str).should be_a Punchblock::Component::Output + it 'plays the number' do + expect_async_ssml_output ssml + subject.play!(argument).should be_a Punchblock::Component::Output end end - describe "converts the argument to a string" do - it 'calls output with a string' do - argument = 123 - ssml = RubySpeech::SSML.draw { string '123' } - expect_component_execution Punchblock::Component::Output.new(:ssml => ssml) - subject.say(argument).should be_a Punchblock::Component::Output + describe "with a string representation of a number" do + let(:argument) { '123' } + + let(:ssml) do + number = argument + RubySpeech::SSML.draw do + say_as(:interpret_as => 'cardinal') { number } + end end + + it 'plays the number' do + expect_async_ssml_output ssml + subject.play!(argument).should be_a Punchblock::Component::Output + end end - end - describe "#ssml_for" do - let(:prompt) { "Please stand by" } + describe "with a time" do + let(:time) { Time.parse "12/5/2000" } - let(:ssml) do - RubySpeech::SSML.draw do - string 'Please stand by' + let(:ssml) do + t = time.to_s + RubySpeech::SSML.draw do + say_as(:interpret_as => 'time') { t } + end end - end - it 'returns SSML for a text argument' do - subject.ssml_for(prompt).should be == ssml + it 'plays the time' do + expect_async_ssml_output ssml + subject.play!(time).should be_a Punchblock::Component::Output + end end - it 'returns the same SSML passed in if it is SSML' do - subject.ssml_for(ssml) == ssml - end - end + describe "with a date" do + let(:date) { Date.parse '2011-01-23' } + let(:ssml) do + d = date.to_s + RubySpeech::SSML.draw do + say_as(:interpret_as => 'date') { d } + end + end - describe "#detect_type" do - it "detects an HTTP path" do - http_path = "http://adhearsion.com/sounds/hello.mp3" - subject.detect_type(http_path).should be :audio + it 'plays the time' do + expect_async_ssml_output ssml + subject.play!(date).should be_a Punchblock::Component::Output + end end - it "detects a file path" do - file_path = "file:///usr/shared/sounds/hello.mp3" - subject.detect_type(file_path).should be :audio + describe "with an array containing a Date/DateTime/Time object and a hash" do + let(:date) { Date.parse '2011-01-23' } + let(:format) { "d-m-y" } + let(:strftime) { "%d-%m%Y" } - absolute_path = "/usr/shared/sounds/hello.mp3" - subject.detect_type(absolute_path).should be :audio + let :ssml do + d = date.strftime strftime + f = format + RubySpeech::SSML.draw do + say_as(:interpret_as => 'date', :format => f) { d } + end + end - relative_path = "foo/bar" - subject.detect_type(relative_path).should_not be :audio + it 'plays the time with the specified format and strftime' do + expect_async_ssml_output ssml + subject.play!(:value => date, :format => format, :strftime => strftime).should be_a Punchblock::Component::Output + end end - it "detects a Date object" do - today = Date.today - subject.detect_type(today).should be :time - end + describe "with an SSML document" do + let(:ssml) { RubySpeech::SSML.draw { string "Hello world" } } - it "detects a Time object" do - now = Time.now - subject.detect_type(now).should be :time + it "plays the SSML without generating" do + expect_async_ssml_output ssml + subject.play!(ssml).should be_a Punchblock::Component::Output + end end + end - it "detects a DateTime object" do - today = DateTime.now - subject.detect_type(today).should be :time + describe "#interruptible_play" do + let(:output1) { "one two" } + let(:output2) { "three four" } + let(:non_existing) { "http://adhearsion.com/nonexistingfile.mp3" } + + it "plays two outputs in succession" do + subject.should_receive(:stream_file).twice + digit = subject.interruptible_play output1, output2 + digit.should be_nil end - it "detects a Numeric object" do - number = 123 - subject.detect_type(number).should be :numeric + it "stops at the first play when input is received" do + subject.should_receive(:stream_file).once.and_return(2) + digit = subject.interruptible_play output1, output2 + digit.should be == 2 end - it "returns text as a fallback" do - output = "Hello" - subject.detect_type(output).should be :text + it 'raises an exception when output is unsuccessful' do + subject.should_receive(:stream_file).once.and_raise Output::PlaybackError, "Output failed" + expect { subject.interruptible_play non_existing }.to raise_error(Output::PlaybackError) end end describe "#stream_file" do let(:allowed_digits) { '35' } let(:prompt) { "Press 3 or 5 to make something happen." } - let(:ssml) { + let(:ssml) do RubySpeech::SSML.draw do string "Press 3 or 5 to make something happen." end - } + end - let(:grammar) { - RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'acceptdigits' do + let(:grammar) do + RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'acceptdigits' do rule id: 'acceptdigits' do one_of do allowed_digits.each { |d| item { d.to_s } } end end end - } + end let(:output_component) { Punchblock::Component::Output.new :ssml => ssml.to_s } @@ -397,11 +619,11 @@ :grammar => { :value => grammar.to_s } } #test does pass and method works, but not sure if the empty method is a good idea it "plays the correct output" do - def subject.write_and_await_response(input_component) + def controller.write_and_await_response(input_component) # it is actually a no-op here end def expect_component_complete_event complete_event = Punchblock::Event::Complete.new @@ -418,11 +640,11 @@ end it "returns a single digit amongst the allowed when pressed" do flexmock(Punchblock::Event::Complete).new_instances.should_receive(:reason => flexmock(:interpretation => 'dtmf-5', :name => :input)) - def subject.write_and_await_response(input_component) + def controller.write_and_await_response(input_component) input_component.trigger_event_handler Punchblock::Event::Complete.new end def expect_component_complete_event complete_event = Punchblock::Event::Complete.new @@ -433,56 +655,80 @@ end end expect_component_complete_event flexmock(Punchblock::Component::Output).new_instances.should_receive(:stop!) - subject.should_receive(:execute_component_and_await_completion).once.with(output_component) + expect_component_execution output_component subject.stream_file(prompt, allowed_digits).should be == '5' end - end # describe #stream_file + end + describe "#say" do + describe "with a RubySpeech document" do + it 'plays the correct SSML' do + ssml = RubySpeech::SSML.draw { string "Hello world" } + expect_ssml_output ssml + subject.say(ssml).should be_a Punchblock::Component::Output + end + end - describe "#interruptible_play!" do - let(:output1) { "one two" } - let(:output2) { "three four" } - let(:non_existing) { "http://adhearsion.com/nonexistingfile.mp3" } - - it "plays two outputs in succession" do - subject.should_receive(:stream_file).twice - subject.interruptible_play! output1, output2 + describe "with a string" do + it 'outputs the correct text' do + str = "Hello world" + ssml = RubySpeech::SSML.draw { string str } + expect_ssml_output ssml + subject.say(str).should be_a Punchblock::Component::Output + end end - it "stops at the first play when input is received" do - subject.should_receive(:stream_file).once.and_return(2) - subject.interruptible_play! output1, output2 + describe "converts the argument to a string" do + it 'calls output with a string' do + argument = 123 + ssml = RubySpeech::SSML.draw { string '123' } + expect_ssml_output ssml + subject.say(argument).should be_a Punchblock::Component::Output + end end + end - it 'raises an exception when output is unsuccessful' do - subject.should_receive(:stream_file).once.and_raise Output::PlaybackError, "Output failed" - expect { subject.interruptible_play!(non_existing) }.to raise_error(Output::PlaybackError) + describe "#speak" do + it "should be an alias for #say" do + subject.method(:speak).should be == subject.method(:say) end - end # describe interruptible_play! + end - describe "#interruptible_play" do - let(:output1) { "one two" } - let(:output2) { "three four" } - let(:non_existing) { "http://adhearsion.com/nonexistingfile.mp3" } - - it "plays two outputs in succession" do - subject.should_receive(:interruptible_play!).twice - subject.interruptible_play output1, output2 + describe "#say!" do + describe "with a RubySpeech document" do + it 'plays the correct SSML' do + ssml = RubySpeech::SSML.draw { string "Hello world" } + expect_async_ssml_output ssml + subject.say!(ssml).should be_a Punchblock::Component::Output + end end - it "stops at the first play when input is received" do - subject.should_receive(:interruptible_play!).once.and_return(2) - subject.interruptible_play output1, output2 + describe "with a string" do + it 'outputs the correct text' do + str = "Hello world" + ssml = RubySpeech::SSML.draw { string str } + expect_async_ssml_output ssml + subject.say!(str).should be_a Punchblock::Component::Output + end end - it "should not raise an exception when output is unsuccessful" do - subject.should_receive(:stream_file).once.and_raise Output::PlaybackError, "Output failed" - lambda { subject.interruptible_play non_existing }.should_not raise_error(Output::PlaybackError) + describe "converts the argument to a string" do + it 'calls output with a string' do + argument = 123 + ssml = RubySpeech::SSML.draw { string '123' } + expect_async_ssml_output ssml + subject.say!(argument).should be_a Punchblock::Component::Output + end end - end # describe interruptible_play + end + describe "#speak!" do + it "should be an alias for #say!" do + subject.method(:speak!).should be == subject.method(:say!) + end + end end end end