Skip to content

Commit

Permalink
Resolve the .ended() promise in case of call failure (#732)
Browse files Browse the repository at this point in the history
* Emit playback.failed event on playback failure and resolve the playback promise

* comments for the e2e changes

* end stated variable moved to top

* resolve the promise in case of prompt failure

* resolve the recording promise when finished

* resolve the promise in case the detection has finished

* resolve the collect promise on finished

* resolve the tap promise and update end state types

* e2e test cases for voice prompt

* e2e test cases for detect function

* e2e test cases for tap function

* e2e test cases for recording function

* sleep function as a util and changeset update
  • Loading branch information
iAmmar7 authored Feb 16, 2023
1 parent 7f529f4 commit 9ad158b
Show file tree
Hide file tree
Showing 19 changed files with 699 additions and 18 deletions.
12 changes: 12 additions & 0 deletions .changeset/seven-beers-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@signalwire/realtime-api': minor
'@signalwire/core': minor
---

Emit `playback.failed` event on playback failure
Resolve the playback `.ended()` promise in case of Playback failure
Resolve the playback `.ended()` promise in case of Prompt failure
Resolve the playback `.ended()` promise in case of Recording failure
Resolve the playback `.ended()` promise in case of Detect failure
Resolve the playback `.ended()` promise in case of Collect failure
Resolve the playback `.ended()` promise in case of Tap failure
6 changes: 6 additions & 0 deletions internal/e2e-realtime-api/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,9 @@ export const sessionStorageMock = () => {
},
}
}

export const sleep = (ms = 3000) => {
return new Promise((r) => {
setTimeout(r, ms)
})
}
8 changes: 1 addition & 7 deletions internal/e2e-realtime-api/src/voice.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import tap from 'tap'
import { Voice } from '@signalwire/realtime-api'
import { createTestRunner } from './utils'

const sleep = (ms = 3000) => {
return new Promise((r) => {
setTimeout(r, ms)
})
}
import { createTestRunner, sleep } from './utils'

const handler = () => {
return new Promise<number>(async (resolve, reject) => {
Expand Down
8 changes: 1 addition & 7 deletions internal/e2e-realtime-api/src/voiceCollect.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import tap from 'tap'
import { Voice } from '@signalwire/realtime-api'
import { createTestRunner } from './utils'

const sleep = (ms = 3000) => {
return new Promise((r) => {
setTimeout(r, ms)
})
}
import { createTestRunner, sleep } from './utils'

const handler = () => {
return new Promise<number>(async (resolve, reject) => {
Expand Down
82 changes: 82 additions & 0 deletions internal/e2e-realtime-api/src/voiceDetect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import tap from 'tap'
import { Voice } from '@signalwire/realtime-api'
import { createTestRunner } from './utils'

const handler = () => {
return new Promise<number>(async (resolve, reject) => {
const client = new Voice.Client({
host: process.env.RELAY_HOST || 'relay.swire.io',
project: process.env.RELAY_PROJECT as string,
token: process.env.RELAY_TOKEN as string,
contexts: [process.env.VOICE_CONTEXT as string],
})

client.on('call.received', async (call) => {
console.log(
'Inbound - Got call',
call.id,
call.from,
call.to,
call.direction
)

try {
const resultAnswer = await call.answer()
tap.ok(resultAnswer.id, 'Inbound - Call answered')
tap.equal(
call.id,
resultAnswer.id,
'Inbound - Call answered gets the same instance'
)

// Send digits 1234 to the caller
const sendDigitResult = await call.sendDigits('1w2w3w4w#')
tap.equal(
call.id,
sendDigitResult.id,
'Inbound - sendDigit returns the same instance'
)

// Callee hangs up a call
await call.hangup()
} catch (error) {
console.error('Inbound - Error', error)
reject(4)
}
})

const call = await client.dialPhone({
to: process.env.VOICE_DIAL_TO_NUMBER as string,
from: process.env.VOICE_DIAL_FROM_NUMBER as string,
timeout: 30,
})
tap.ok(call.id, 'Outbound - Call resolved')

// Start a detect
const detect = await call.detectDigit({ digits: '1234' })

tap.equal(
call.id,
detect.callId,
'Outbound - Detect returns the same instance'
)

const { type } = await detect.ended()

tap.equal(type, 'digit', 'Outbound - Received the digit')

resolve(0)
})
}

async function main() {
const runner = createTestRunner({
name: 'Voice Detect E2E',
testHandler: handler,
executionTime: 60_000,
})

await runner.run()
}

main()
126 changes: 126 additions & 0 deletions internal/e2e-realtime-api/src/voicePlayback.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import tap from 'tap'
import { Voice } from '@signalwire/realtime-api'
import { createTestRunner } from './utils'

const handler = () => {
return new Promise<number>(async (resolve, reject) => {
const client = new Voice.Client({
host: process.env.RELAY_HOST || 'relay.swire.io',
project: process.env.RELAY_PROJECT as string,
token: process.env.RELAY_TOKEN as string,
contexts: [process.env.VOICE_CONTEXT as string],
})

client.on('call.received', async (call) => {
console.log(
'Inbound - Got call',
call.id,
call.from,
call.to,
call.direction
)

try {
const resultAnswer = await call.answer()
tap.ok(resultAnswer.id, 'Inbound - Call answered')
tap.equal(
call.id,
resultAnswer.id,
'Inbound - Call answered gets the same instance'
)

// Play an invalid audio
const handle = await call.playAudio({
url: 'https://cdn.fake.com/default-music/fake.mp3',
})

tap.equal(
call.id,
handle.callId,
'Inbound - playback returns the same instance'
)

const waitForPlaybackFailed = new Promise((resolve) => {
call.on('playback.failed', (playback) => {
tap.equal(playback.state, 'error', 'Inbound - playback has failed')
resolve(true)
})
})
// Wait for the inbound audio to failed
await waitForPlaybackFailed

// Callee hangs up a call
await call.hangup()
} catch (error) {
console.error('Inbound - Error', error)
reject(4)
}
})

const call = await client.dialPhone({
to: process.env.VOICE_DIAL_TO_NUMBER as string,
from: process.env.VOICE_DIAL_FROM_NUMBER as string,
timeout: 30,
})
tap.ok(call.id, 'Outbound - Call resolved')

// Play an audio
const handle = await call.playAudio({
url: 'https://cdn.signalwire.com/default-music/welcome.mp3',
})

tap.equal(
call.id,
handle.callId,
'Outbound - Playback returns the same instance'
)

const waitForPlaybackStarted = new Promise((resolve) => {
call.on('playback.started', (playback) => {
tap.equal(playback.state, 'playing', 'Outbound - Playback has started')
resolve(true)
})
})
// Wait for the outbound audio to start
await waitForPlaybackStarted

const waitForPlaybackEnded = new Promise((resolve) => {
call.on('playback.ended', (playback) => {
tap.equal(
playback.state,
'finished',
'Outbound - Playback has finished'
)
resolve(true)
})
})
// Wait for the outbound audio to end (callee hung up the call or audio ended)
await waitForPlaybackEnded

const waitForParams = ['ended', 'ending', ['ending', 'ended']] as const
const results = await Promise.all(
waitForParams.map((params) => call.waitFor(params as any))
)
waitForParams.forEach((value, i) => {
if (typeof value === 'string') {
tap.ok(results[i], `"${value}": completed successfully.`)
} else {
tap.ok(results[i], `${JSON.stringify(value)}: completed successfully.`)
}
})

resolve(0)
})
}

async function main() {
const runner = createTestRunner({
name: 'Voice Playback E2E',
testHandler: handler,
executionTime: 60_000,
})

await runner.run()
}

main()
105 changes: 105 additions & 0 deletions internal/e2e-realtime-api/src/voicePrompt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import tap from 'tap'
import { Voice } from '@signalwire/realtime-api'
import { createTestRunner } from './utils'

const handler = () => {
return new Promise<number>(async (resolve, reject) => {
const client = new Voice.Client({
host: process.env.RELAY_HOST || 'relay.swire.io',
project: process.env.RELAY_PROJECT as string,
token: process.env.RELAY_TOKEN as string,
contexts: [process.env.VOICE_CONTEXT as string],
})

client.on('call.received', async (call) => {
console.log(
'Inbound - Got call',
call.id,
call.from,
call.to,
call.direction
)

try {
const resultAnswer = await call.answer()
tap.ok(resultAnswer.id, 'Inbound - Call answered')
tap.equal(
call.id,
resultAnswer.id,
'Inbound - Call answered gets the same instance'
)

// Send digits 1234 to the caller
const sendDigitResult = await call.sendDigits('1w2w3w4w#')
tap.equal(
call.id,
sendDigitResult.id,
'Inbound - sendDigit returns the same instance'
)

// Callee hangs up a call
await call.hangup()
} catch (error) {
console.error('Inbound - Error', error)
reject(4)
}
})

const call = await client.dialPhone({
to: process.env.VOICE_DIAL_TO_NUMBER as string,
from: process.env.VOICE_DIAL_FROM_NUMBER as string,
timeout: 30,
})
tap.ok(call.id, 'Outbound - Call resolved')

// Start a prompt
const prompt = await call.prompt({
playlist: new Voice.Playlist({ volume: 1.0 }).add(
Voice.Playlist.TTS({
text: 'Welcome to SignalWire! Please enter your 4 digits PIN',
})
),
digits: {
max: 4,
digitTimeout: 10,
terminators: '#',
},
})

tap.equal(
call.id,
prompt.callId,
'Outbound - Prompt returns the same instance'
)

const { digits } = await prompt.ended()

tap.equal(digits, '1234', 'Outbound - Received the same digit')

const waitForParams = ['ended', 'ending', ['ending', 'ended']] as const
const results = await Promise.all(
waitForParams.map((params) => call.waitFor(params as any))
)
waitForParams.forEach((value, i) => {
if (typeof value === 'string') {
tap.ok(results[i], `"${value}": completed successfully.`)
} else {
tap.ok(results[i], `${JSON.stringify(value)}: completed successfully.`)
}
})

resolve(0)
})
}

async function main() {
const runner = createTestRunner({
name: 'Voice Prompt E2E',
testHandler: handler,
executionTime: 60_000,
})

await runner.run()
}

main()
Loading

0 comments on commit 9ad158b

Please sign in to comment.