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

Commit

Permalink
Merge pull request #1 from ocelotconsulting/addingTests
Browse files Browse the repository at this point in the history
Adding tests
  • Loading branch information
OMontano authored Apr 28, 2022
2 parents fdbd7c4 + 13e1676 commit dfdfd4c
Show file tree
Hide file tree
Showing 14 changed files with 1,251 additions and 59 deletions.
18 changes: 3 additions & 15 deletions lambda/conversation/billing.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,7 @@ const {
guard,
} = require('robot3')
const { fakePhoneNumber, fakeWebsite } = require('../constants')

const generateRandomBillAmount = () => {
const min = Math.ceil(0)
const max = Math.floor(500)
const decimalMax = Math.floor(99)

const dollars = Math.floor(Math.random() * (max - min + 1)) + min
const cents = Math.floor(Math.random() * (decimalMax - min + 1)) + min

return `$${dollars}.${cents}`
}

// replace this with an api call
const fetchBillForAddress = async () => ({ billAmount: generateRandomBillAmount() })
const { fetchBill } = require('../service/fetchBill')

const stateMap = {
fresh: state(
Expand All @@ -42,7 +29,7 @@ const stateMap = {
guard(({ resuming, conversationAttributes }) => resuming && conversationAttributes.confirmAddress?.correctAddress),
),
),
correctAddress: invoke(fetchBillForAddress,
correctAddress: invoke(fetchBill,
transition('done', 'returnBill',
reduce((ctx, { data: { billAmount }}) => ({ ...ctx, billAmount })),
),
Expand All @@ -69,6 +56,7 @@ const billing = {
}),
intent: 'Billing',
canInterrupt: true,
shouldBeUnique: true,
}

module.exports = { billing }
5 changes: 3 additions & 2 deletions lambda/conversation/estimatedRestoration.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ const estimatedRestoration = {
stateMap,
transitionStates: [ 'confirmAddress', 'pickAnOutage' ],
dialogMap: {
askAboutHomeOrOther: ({ misunderstandingCount }) => dialog(
askAboutHomeOrOther: ({ misunderstandingCount = 0 }) => dialog(
`estimatedRestoration.homeOrOther.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`
),
selectOtherOutage: ({ misunderstandingCount }) => dialog(
selectOtherOutage: ({ misunderstandingCount = 0 }) => dialog(
`estimatedRestoration.otherLocation.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`,
{ options: utils.objectMapToSpeech(outageOptions) }
),
Expand All @@ -107,6 +107,7 @@ const estimatedRestoration = {
intent: 'EstimatedRestoration',
canInterrupt: true,
description: 'how long it will take to restore power',
shouldBeUnique: true,
}

module.exports = { estimatedRestoration }
129 changes: 107 additions & 22 deletions lambda/conversation/reportOutage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,73 @@ const {
const { utils } = require('@ocelot-consulting/ocelot-voice-framework')
const { fakePhoneNumber, fakeWebsite } = require('../constants')

const callOutageApi = async () => {
console.log('outage reported')
const callOutageApi = async ({houseNumber, phoneNumber}) => {
console.log('outage reported', houseNumber, phoneNumber)

return [{
result: 'noOutage'
}, {
result: 'yesOutage',
impact: '300',
areaDescription: 'The pines neighborhood north of Park Street',
workDescription: 'Crews are on the scenen and expect repairs to complete in about an hour.'
}, {
result: 'badCombination'
}][houseNumber % 3]
}

const stateMap = {
fresh: state(
transition('processIntent', 'confirmAddress',
guard(({ conversationAttributes }) => !conversationAttributes.confirmAddress?.confirmedAddress),
transition('processIntent', 'askForHouseNumber')
),
askForHouseNumber: state(
transition('processIntent', 'askForTelephoneNumber',
guard((ctx, { intent }) => intent.name === 'ANumber'),
reduce((ctx, { intent } ) => ({ ...ctx, houseNumber: intent.slots.number.value, misunderstandingCount: 0 }))
),
transition('processIntent', 'incorrectAddress',
guard(({ conversationAttributes }) => !conversationAttributes.confirmAddress?.correctAddress),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
transition('processIntent', 'letYouKnow',
guard(({ conversationAttributes }) => conversationAttributes.confirmAddress?.correctAddress),
transition('processIntent', 'askForHouseNumber',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
confirmAddress: state(
immediate('incorrectAddress',
guard(({ resuming, conversationAttributes }) => resuming && !conversationAttributes.confirmAddress?.correctAddress),
askForTelephoneNumber: state(
transition('processIntent', 'gotAllData',
guard((ctx, { intent }) => intent.name === 'APhoneNumber'),
reduce((ctx, { intent } ) => ({ ...ctx, phoneNumber: intent.slots.phoneNumber.value, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
immediate('letYouKnow',
guard(({ resuming, conversationAttributes }) => resuming && conversationAttributes.confirmAddress?.correctAddress),
transition('processIntent', 'askForTelephoneNumber',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
letYouKnow: state(
transition('processIntent', 'gotAllData',
transition('processIntent', 'thanksForReporting',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'yes'),
reduce(ctx => ({ ...ctx, letThemKnow: true }))
reduce(ctx => ({ ...ctx, letThemKnow: true, misunderstandingCount: 0 }))
),
transition('processIntent', 'gotAllData',
transition('processIntent', 'thanksForReporting',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'no'),
reduce(ctx => ({ ...ctx, letThemKnow: false, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
transition('processIntent', 'letYouKnow',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
letYouKnowWOutageReport: state(
transition('processIntent', 'thanksForReporting',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'yes'),
reduce(ctx => ({ ...ctx, letThemKnow: true, misunderstandingCount: 0 }))
),
transition('processIntent', 'thanksForReporting',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'no'),
reduce(ctx => ({ ...ctx, letThemKnow: false }))
reduce(ctx => ({ ...ctx, letThemKnow: false, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
Expand All @@ -49,37 +84,87 @@ const stateMap = {
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
reportAnOutage: state(
transition('processIntent', 'letYouKnow',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'yes'),
reduce(ctx => ({ ...ctx, reportingOutage: true, misunderstandingCount: 0 }))
),
transition('processIntent', 'haveANiceDay',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'no'),
reduce(ctx => ({ ...ctx, reportingOutage: false, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
transition('processIntent', 'reportAnOutage',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
tryAgain: state(
transition('processIntent', 'askForHouseNumber',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'yes'),
reduce(ctx => ({ ...ctx, houseNumber: 0, phoneNumber: '', misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'no'),
reduce(ctx => ({ ...ctx, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
transition('processIntent', 'tryAgain',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
gotAllData: invoke(callOutageApi,
transition('done', 'thanksForReporting'),
transition('done', 'reportAnOutage',
guard((ctx, {data: { result }}) => result === 'noOutage'),
reduce((ctx, { data }) => ({ ...ctx, attemptCount: ctx.attemptCount + 1 })),),
transition('done', 'letYouKnowWOutageReport',
guard((ctx, {data: { result }}) => result === 'yesOutage'),
reduce((ctx, { data }) => ({ ...ctx, attemptCount: ctx.attemptCount + 1, outageDetails: {...data} })),),
transition('done', 'tryAgain',
guard((ctx, {data: { result }}) => result === 'badCombination'),
reduce((ctx, { data }) => ({ ...ctx, attemptCount: ctx.attemptCount + 1 })),),
transition('error', 'error',
reduce((ctx, { error }) => ({ ...ctx, error })),
)
),
thanksForReporting: state(),
incorrectAddress: state(),
haveANiceDay: state(),
error: state(),
goBack: state(),
};

const reportOutage = {
handle: ({ dialog, sessionAttributes }) => ({
initialState: {
outageDetails: {},
attemptCount: 0,
houseNumber: 0,
phoneNumber: '',
reportAnOutage: '',
letThemKnow: '',
reportingOutage: '',
misunderstandingCount: 0,
error: '',
},
stateMap,
transitionStates: 'confirmAddress',
dialogMap: {
askForHouseNumber: ({ misunderstandingCount }) => dialog(`reportOutage.askForHouseNumber.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`),
askForTelephoneNumber: ({ misunderstandingCount }) => dialog(`reportOutage.askForTelephoneNumber.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`),
letYouKnow: ({ misunderstandingCount }) => dialog(`reportOutage.letYouKnow.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`),
letYouKnowWOutageReport: ({ misunderstandingCount, outageDetails }) => dialog(`reportOutage.letYouKnowWOutageReport.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`, {...outageDetails}),
reportAnOutage: ({ misunderstandingCount }) => dialog(`reportOutage.reportAnOutage.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`),
tryAgain: ({ misunderstandingCount, houseNumber, phoneNumber }) => dialog(`reportOutage.tryAgain.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`, {houseNumber, phoneNumber}),
thanksForReporting: ({ letThemKnow }) => dialog(`reportOutage.reply.${letThemKnow ? 'withContact' : 'noContact'}`),
incorrectAddress: () => dialog('reportOutage.wrongAddress', { website: fakeWebsite, phoneNumber: fakePhoneNumber }),
haveANiceDay: () => dialog(`reportOutage.reply.haveANiceDay`),
error: () => dialog('home.error'),
},
overrideResume: sessionAttributes.previousPoppedConversation === 'confirmAddress',
}),
intent: 'ReportOutage',
canInterrupt: true,
shouldBeUnique: true,
}

module.exports = { reportOutage }
52 changes: 49 additions & 3 deletions lambda/dialog/ReportOutageDialog.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
const reportOutage = {
wrongAddress: [
`Sorry, we can't report an outage for you because we don't have the right address for your account. Please call <say-as interpret-as="telephone">{{phoneNumber}}</say-as> or visit {{website}} to report the outage`,
],
askForHouseNumber: {
confirm:[
`What is the house number for the house that you would like to report an outage at?`
],
misheard: [
`Please just say a number for the address you're trying to report an outage at.`
]
},
askForTelephoneNumber: {
confirm:[
`What is the telephone number for service at this address?`
],
misheard: [
`Please say the telephone number for this address as a ten digit number.`
]
},
tryAgain: {
confirm: [
`I'm sorry but house number {{houseNumber}} and phone number {{phoneNumber}} don't match our records. Would you like to try saying the numbers again?`
],
misheard: [
`Was that a yes or a no.`,
`I didn't catch that. You can respond yes or no.`,
`I didn't catch that. Would you like to try saying the numbers again?`
]
},
reportAnOutage: {
confirm: [
`There are no reported outages in your area. Would you like to report an outage at your address?`
],
misheard: [
`Was that a yes or a no.`,
`I didn't catch that. You can respond yes or no.`,
`I didn't catch that. Would you like to report an outage at your address?`
]
},
letYouKnow: {
confirm: [
`Would you like a notification when the power is restored?`,
Expand All @@ -14,12 +47,25 @@ const reportOutage = {
`I didn't catch that. Would you like us to notify you when the power is restored?`
]
},
letYouKnowWOutageReport: {
confirm: [
`There is currently an outage in your area affecting {{impact}} customers in {{areaDescription}}. {{workDescription}} Would you like a notification when the power is restored?`
],
misheard: [
`Was that a yes or a no.`,
`I didn't catch that. You can respond yes or no.`,
`I didn't catch that. Would you like us to notify you when the power is restored?`
]
},
reply: {
withContact: [
`Great! We received your outage report, and we'll let you know when power is restored. Thank you.`
],
noContact: [
`Great! We received your outage report. Thank you.`
],
haveANiceDay: [
`Thank you. Come back if there is a problem.`
]
}
}
Expand Down
32 changes: 16 additions & 16 deletions lambda/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lambda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"author": "Ocelot Consulting",
"license": "ISC",
"dependencies": {
"@ocelot-consulting/ocelot-voice-framework": "^1.1.74",
"@ocelot-consulting/ocelot-voice-framework": "^1.1.84",
"robot3": "^0.2.21"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit dfdfd4c

Please sign in to comment.