Skip to content

Commit 69c7227

Browse files
authored
Merge pull request #7 from Bandwidth/task/fix-ssml-regex-nested-speak-sentence
fixed ssml for speak sentece when nested in a gather
2 parents 7e13da6 + 8eebebd commit 69c7227

File tree

5 files changed

+375
-7
lines changed

5 files changed

+375
-7
lines changed

.github/actions/validate/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ FROM python:3
33

44
# Copies your code file from your action repository to the filesystem path `/` of the container
55
COPY entrypoint.sh /entrypoint.sh
6+
COPY bxml_tests.py /bxml_tests.py
67

78
#Make entrypoint.sh exacutable
89
RUN chmod +x /entrypoint.sh
Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
"""
2+
bxml_tests.py
3+
4+
Unit tests for BXML
5+
6+
@copyright Bandwidth INC
7+
"""
8+
from bandwidth.voice.bxml.response import Response
9+
from bandwidth.voice.bxml.verbs import *
10+
11+
import unittest
12+
13+
14+
class BxmlTests(unittest.TestCase):
15+
"""
16+
Class for the BXML tests
17+
"""
18+
def test_forward_xml_with_optional_fields(self):
19+
response = Response()
20+
forward = Forward(
21+
to="+10987654321",
22+
from_="+11234567890",
23+
call_timeout=100,
24+
diversion_treatment="propagate",
25+
diversion_reason="away"
26+
)
27+
response.add_verb(forward)
28+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Forward to="+10987654321" callTimeout="100" from="+11234567890" diversionTreatment="propagate" diversionReason="away"/></Response>'
29+
self.assertEqual(response.to_bxml(), expected_bxml)
30+
31+
def test_gather_no_nested(self):
32+
response = Response()
33+
gather = Gather(
34+
gather_url="https://gather.url/nextBXML",
35+
gather_method="POST",
36+
terminating_digits="#",
37+
tag="tag",
38+
max_digits=20,
39+
inter_digit_timeout=50,
40+
username="user",
41+
password="password",
42+
first_digit_timeout=10,
43+
repeat_count=3,
44+
gather_fallback_url="https://test.com",
45+
gather_fallback_method="GET",
46+
fallback_username="fuser",
47+
fallback_password="fpass"
48+
)
49+
response.add_verb(gather)
50+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Gather gatherUrl="https://gather.url/nextBXML" gatherMethod="POST" terminatingDigits="#" tag="tag" maxDigits="20" interDigitTimeout="50" username="user" password="password" firstDigitTimeout="10" repeatCount="3" gatherFallbackUrl="https://test.com" gatherFallbackMethod="GET" fallbackUsername="fuser" fallbackPassword="fpass"/></Response>'
51+
self.assertEqual(response.to_bxml(), expected_bxml)
52+
53+
def test_gather_with_speak_sentence(self):
54+
response = Response()
55+
speak_sentence = SpeakSentence(
56+
sentence="Phrase.",
57+
voice="kate",
58+
locale="en_US",
59+
gender="female"
60+
)
61+
gather = Gather(
62+
gather_url="https://gather.url/nextBXML",
63+
gather_method="POST",
64+
terminating_digits="#",
65+
tag="tag",
66+
max_digits=20,
67+
inter_digit_timeout=50,
68+
username="user",
69+
password="password",
70+
first_digit_timeout=10,
71+
repeat_count=3,
72+
speak_sentence=speak_sentence
73+
)
74+
expected_bxml = expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Gather gatherUrl="https://gather.url/nextBXML" gatherMethod="POST" terminatingDigits="#" tag="tag" maxDigits="20" interDigitTimeout="50" username="user" password="password" firstDigitTimeout="10" repeatCount="3"><SpeakSentence voice="kate" locale="en_US" gender="female">Phrase.</SpeakSentence></Gather></Response>'
75+
response.add_verb(gather)
76+
self.assertEqual(response.to_bxml(), expected_bxml)
77+
78+
def test_gather_play_audio(self):
79+
response = Response()
80+
play_audio_1 = PlayAudio(url="https://audio.url/audio1.wav")
81+
gather = Gather(
82+
gather_url="https://gather.url/nextBXML",
83+
gather_method="POST",
84+
terminating_digits="#",
85+
tag="tag",
86+
max_digits=20,
87+
inter_digit_timeout=50,
88+
username="user",
89+
password="password",
90+
first_digit_timeout=10,
91+
repeat_count=3,
92+
play_audio=play_audio_1
93+
)
94+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Gather gatherUrl="https://gather.url/nextBXML" gatherMethod="POST" terminatingDigits="#" tag="tag" maxDigits="20" interDigitTimeout="50" username="user" password="password" firstDigitTimeout="10" repeatCount="3"><PlayAudio>https://audio.url/audio1.wav</PlayAudio></Gather></Response>'
95+
response.add_verb(gather)
96+
self.assertEqual(response.to_bxml(), expected_bxml)
97+
98+
def test_hangup(self):
99+
response = Response()
100+
hang_up = Hangup()
101+
response.add_verb(hang_up)
102+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Hangup/></Response>'
103+
self.assertEqual(response.to_bxml(), expected_bxml)
104+
105+
def test_pause(self):
106+
response = Response()
107+
pause = Pause(duration=400)
108+
response.add_verb(pause)
109+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Pause duration="400"/></Response>'
110+
self.assertEqual(response.to_bxml(), expected_bxml)
111+
112+
def test_pause_recording(self):
113+
response = Response()
114+
pause_recording = PauseRecording()
115+
response.add_verb(pause_recording)
116+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><PauseRecording/></Response>'
117+
self.assertEqual(response.to_bxml(), expected_bxml)
118+
119+
def test_play_audio(self):
120+
response = Response()
121+
play_audio_1 = PlayAudio(
122+
url="https://audio.url/audio1.wav",
123+
username="user",
124+
password="pass"
125+
)
126+
response.add_verb(play_audio_1)
127+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><PlayAudio username="user" password="pass">https://audio.url/audio1.wav</PlayAudio></Response>'
128+
self.assertEqual(response.to_bxml(), expected_bxml)
129+
130+
def test_record(self):
131+
response = Response()
132+
record = Record(
133+
tag = "tag",
134+
username = "user",
135+
password = "pass",
136+
record_complete_url="https://record.url.server/record",
137+
record_complete_method="POST",
138+
recording_available_url="https://record.url.server/available",
139+
recording_available_method="GET",
140+
terminating_digits="#",
141+
max_duration=90,
142+
file_format="mp3",
143+
transcribe=False,
144+
transcription_available_url="https://transcribe.url.server/available",
145+
transcription_available_method="POST",
146+
silence_timeout=90,
147+
record_complete_fallback_url="https://test.com",
148+
record_complete_fallback_method="GET",
149+
fallback_username="fuser",
150+
fallback_password="fpass"
151+
)
152+
response.add_verb(record)
153+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Record tag="tag" username="user" password="pass" recordCompleteUrl="https://record.url.server/record" recordCompleteMethod="POST" recordingAvailableUrl="https://record.url.server/available" recordingAvailableMethod="GET" terminatingDigits="#" maxDuration="90" fileFormat="mp3" transcribe="false" transcriptionAvailableUrl="https://transcribe.url.server/available" transcriptionAvailableMethod="POST" silenceTimeout="90" recordCompleteFallbackUrl="https://test.com" recordCompleteFallbackMethod="GET" fallbackUsername="fuser" fallbackPassword="fpass"/></Response>'
154+
self.assertEqual(response.to_bxml(), expected_bxml)
155+
156+
def test_redirect(self):
157+
response = Response()
158+
redirect = Redirect(
159+
redirect_url="http://flow.url/newFlow",
160+
redirect_method="POST",
161+
tag="tag",
162+
username="user",
163+
password="pass",
164+
redirect_fallback_url="https://test.com",
165+
redirect_fallback_method="GET",
166+
fallback_username="fuser",
167+
fallback_password="fpass"
168+
)
169+
response.add_verb(redirect)
170+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Redirect redirectUrl="http://flow.url/newFlow" redirectMethod="POST" tag="tag" username="user" password="pass" redirectFallbackUrl="https://test.com" redirectFallbackMethod="GET" fallbackUsername="fuser" fallbackPassword="fpass"/></Response>'
171+
self.assertEqual(response.to_bxml(), expected_bxml)
172+
173+
def test_resume_recording(self):
174+
response = Response()
175+
resume_recording = ResumeRecording()
176+
response.add_verb(resume_recording)
177+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><ResumeRecording/></Response>'
178+
self.assertEqual(response.to_bxml(), expected_bxml)
179+
180+
def test_dtmf(self):
181+
response = Response()
182+
send_dtmf = SendDtmf(
183+
dtmf = "1234",
184+
tone_duration = 200,
185+
tone_interval = 450
186+
)
187+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><SendDtmf toneDuration="200" toneInterval="450">1234</SendDtmf></Response>'
188+
response.add_verb(send_dtmf)
189+
self.assertEqual(response.to_bxml(), expected_bxml)
190+
191+
def test_speak_sentence(self):
192+
response = Response()
193+
speak_sentence = SpeakSentence(
194+
sentence="Phrase.",
195+
voice="kate",
196+
locale="en_US",
197+
gender="female"
198+
)
199+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><SpeakSentence voice="kate" locale="en_US" gender="female">Phrase.</SpeakSentence></Response>'
200+
response.add_verb(speak_sentence)
201+
self.assertEqual(response.to_bxml(), expected_bxml)
202+
203+
def test_speak_sentence_SSML(self):
204+
response = Response()
205+
speak_sentence = SpeakSentence(
206+
sentence='<lang xml:lang="es-MX">Hydrogen</lang> is the most abundant element in the universe.',
207+
voice="kate",
208+
locale="en_US",
209+
gender="female"
210+
)
211+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><SpeakSentence voice="kate" locale="en_US" gender="female"><lang xml:lang="es-MX">Hydrogen</lang> is the most abundant element in the universe.</SpeakSentence></Response>'
212+
response.add_verb(speak_sentence)
213+
self.assertEqual(response.to_bxml(), expected_bxml)
214+
215+
def test_start_recording(self):
216+
response = Response()
217+
record = StartRecording(
218+
tag = "tag",
219+
username = "user",
220+
password = "pass",
221+
recording_available_url="https://record.url.server/available",
222+
recording_available_method="GET",
223+
file_format="mp3",
224+
multi_channel=True,
225+
transcribe=False,
226+
transcription_available_url="https://transcribe.url.server/available",
227+
transcription_available_method="POST"
228+
)
229+
response.add_verb(record)
230+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><StartRecording tag="tag" username="user" password="pass" recordingAvailableUrl="https://record.url.server/available" recordingAvailableMethod="GET" fileFormat="mp3" multiChannel="true" transcribe="false" transcriptionAvailableUrl="https://transcribe.url.server/available" transcriptionAvailableMethod="POST"/></Response>'
231+
self.assertEqual(response.to_bxml(), expected_bxml)
232+
233+
def test_stop_recording(self):
234+
response = Response()
235+
stop_recording = StopRecording()
236+
response.add_verb(stop_recording)
237+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><StopRecording/></Response>'
238+
self.assertEqual(response.to_bxml(), expected_bxml)
239+
240+
def test_transfer(self):
241+
response = Response()
242+
phone1 = PhoneNumber(
243+
number="+11234567891",
244+
transfer_answer_url="https://transfer.com/answer",
245+
transfer_answer_method="POST",
246+
username="user",
247+
password="pass",
248+
tag="tag",
249+
transfer_disconnect_method="POST",
250+
transfer_disconnect_url="https://transfer.com/disconnect",
251+
transfer_answer_fallback_url="https://test.com",
252+
transfer_answer_fallback_method="GET",
253+
fallback_username="fuser",
254+
fallback_password="fpass"
255+
)
256+
phone1_bxml = '<PhoneNumber transferAnswerUrl="https://transfer.com/answer" transferAnswerMethod="POST" username="user" password="pass" tag="tag" transferDisconnectMethod="POST" transferDisconnectUrl="https://transfer.com/disconnect" transferAnswerFallbackUrl="https://test.com" transferAnswerFallbackMethod="GET" fallbackUsername="fuser" fallbackPassword="fpass">+11234567891</PhoneNumber>'
257+
phone2 = PhoneNumber(number="+11234567892")
258+
phone2_bxml = '<PhoneNumber>+11234567892</PhoneNumber>'
259+
transfer = Transfer(
260+
transfer_caller_id="+15555555555",
261+
call_timeout=50,
262+
tag="tag",
263+
transfer_complete_url="https://transcribe.url.server/complete",
264+
transfer_complete_method="POST",
265+
username="user",
266+
password="pass",
267+
diversion_treatment="propagate",
268+
diversion_reason="away",
269+
phone_numbers=[phone1, phone2],
270+
transfer_complete_fallback_url="https://test.com",
271+
transfer_complete_fallback_method="GET",
272+
fallback_username="fusern",
273+
fallback_password="fpassw"
274+
)
275+
response.add_verb(transfer)
276+
expected_bxml = f'<?xml version="1.0" encoding="UTF-8"?><Response><Transfer transferCallerId="+15555555555" callTimeout="50" tag="tag" transferCompleteUrl="https://transcribe.url.server/complete" transferCompleteMethod="POST" username="user" password="pass" diversionTreatment="propagate" diversionReason="away" transferCompleteFallbackUrl="https://test.com" transferCompleteFallbackMethod="GET" fallbackUsername="fusern" fallbackPassword="fpassw">{phone1_bxml}{phone2_bxml}</Transfer></Response>'
277+
self.assertEqual(response.to_bxml(), expected_bxml)
278+
279+
def test_conference(self):
280+
conference = Conference("my-conference", mute=False, hold=True, call_ids_to_coach="c-123,c-345",
281+
conference_event_url="https://test.com", conference_event_method="GET", username="user",
282+
password="pass", tag="tag", conference_event_fallback_url="https://test2.com",
283+
conference_event_fallback_method="POST", fallback_username="fuser", fallback_password="fpass")
284+
285+
response = Response()
286+
response.add_verb(conference)
287+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Conference mute="false" hold="true" callIdsToCoach="c-123,c-345" conferenceEventUrl="https://test.com" conferenceEventMethod="GET" tag="tag" username="user" password="pass" conferenceEventFallbackUrl="https://test2.com" conferenceEventFallbackMethod="POST" fallbackUsername="fuser" fallbackPassword="fpass">my-conference</Conference></Response>'
288+
self.assertEqual(response.to_bxml(), expected_bxml)
289+
290+
def test_conference_coach_array(self):
291+
conference = Conference("my-conference", call_ids_to_coach=["c-123", "c-456"])
292+
293+
response = Response()
294+
response.add_verb(conference)
295+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Conference callIdsToCoach="c-123,c-456">my-conference</Conference></Response>'
296+
self.assertEqual(response.to_bxml(), expected_bxml)
297+
298+
def test_bridge(self):
299+
bridge = Bridge("c-95ac8d6e-1a31c52e-b38f-4198-93c1-51633ec68f8d",
300+
bridge_complete_url="https://test.com",
301+
bridge_complete_method="GET",
302+
bridge_target_complete_url="https://test2.com",
303+
bridge_target_complete_method="POST",
304+
username="user",
305+
password="pass",
306+
tag="custom tag",
307+
bridge_complete_fallback_url="https://test3.com",
308+
bridge_complete_fallback_method="GET",
309+
bridge_target_complete_fallback_url="https://test4.com",
310+
bridge_target_complete_fallback_method="POST",
311+
fallback_username="fuser",
312+
fallback_password="fpass"
313+
)
314+
315+
response = Response()
316+
response.add_verb(bridge)
317+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Bridge bridgeCompleteUrl="https://test.com" bridgeCompleteMethod="GET" bridgeTargetCompleteUrl="https://test2.com" bridgeTargetCompleteMethod="POST" username="user" password="pass" tag="custom tag" bridgeCompleteFallbackUrl="https://test3.com" bridgeCompleteFallbackMethod="GET" bridgeTargetCompleteFallbackUrl="https://test4.com" bridgeTargetCompleteFallbackMethod="POST" fallbackUsername="fuser" fallbackPassword="fpass">c-95ac8d6e-1a31c52e-b38f-4198-93c1-51633ec68f8d</Bridge></Response>'
318+
self.assertEqual(response.to_bxml(), expected_bxml)
319+
320+
def test_ring(self):
321+
ring = Ring(
322+
duration=5
323+
)
324+
325+
response = Response()
326+
response.add_verb(ring)
327+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Ring duration="5"/></Response>'
328+
self.assertEqual(response.to_bxml(), expected_bxml)
329+
330+
def test_start_gather(self):
331+
startGather = StartGather(
332+
dtmfUrl= "https://test.com",
333+
dtmfMethod = "POST",
334+
username = "user",
335+
password = "pass",
336+
tag = "custom tag"
337+
)
338+
339+
response = Response()
340+
response.add_verb(startGather)
341+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><StartGather dtmfUrl="https://test.com" dtmfMethod="POST" username="user" password="pass" tag="custom tag"/></Response>'
342+
self.assertEqual(response.to_bxml(), expected_bxml)
343+
344+
def test_stop_gather(self):
345+
stopGather = StopGather()
346+
347+
response = Response()
348+
response.add_verb(stopGather)
349+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><StopGather/></Response>'
350+
self.assertEqual(response.to_bxml(), expected_bxml)
351+
352+
def test_gather_speak_sentence_ssml(self):
353+
response = Response()
354+
speak_sentence = SpeakSentence(
355+
sentence='Hello. Your number is <say-as interpret-as="telephone">asdf</say-as>, lets play a game. What is 10 + 3. Press the pound key when finished.'
356+
)
357+
gather = Gather(
358+
speak_sentence=speak_sentence
359+
)
360+
expected_bxml = '<?xml version="1.0" encoding="UTF-8"?><Response><Gather><SpeakSentence>Hello. Your number is <say-as interpret-as="telephone">asdf</say-as>, lets play a game. What is 10 + 3. Press the pound key when finished.</SpeakSentence></Gather></Response>'
361+
response.add_verb(gather)
362+
self.assertEqual(response.to_bxml(), expected_bxml)
363+
364+
365+
if __name__ == '__main__':
366+
unittest.main()
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#!/bin/sh
22

3-
pip install twine
4-
pip install wheel
5-
python setup.py sdist bdist_wheel
6-
7-
twine check dist/*
3+
pip install -e .
4+
cp /bxml_tests.py .
5+
python bxml_tests.py

0 commit comments

Comments
 (0)