Skip to content

Commit

Permalink
fix(@aws-amplify/interactions): fix interactions v3 bugs and refactor…
Browse files Browse the repository at this point in the history
… type (aws-amplify#6381)

* fix(@aws-amplify/interactions): fix interactions v3 bugs and refactor type

Co-authored-by: Jordan Ranz <[email protected]>
  • Loading branch information
wlee221 and jordanranz authored Jul 22, 2020
1 parent cf880a0 commit 8c6fb4a
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,11 @@
if (!audioSupported) {
throw new Error(UNSUPPORTED);
}
recorder = audioRecorder.createRecorder(silenceDetectionConfig);
recorder.record(onSilence, visualizer);
const context = audioRecorder.audioContext();
context.resume().then(() => {
recorder = audioRecorder.createRecorder(silenceDetectionConfig);
recorder.record(onSilence, visualizer);
});
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ function ChatBotInputs(props) {
const handleMicButton = props.handleMicButton;
const micText = props.micText;
const submit = props.submit;
let placeholder;

if (voiceEnabled && textEnabled) {
// @ts-ignore
Expand Down
1 change: 0 additions & 1 deletion packages/aws-amplify-react/src/Interactions/ChatBot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ export class ChatBot extends React.Component<IChatBotProps, IChatBotState> {
messageType: 'voice',
},
};

const response = await Interactions.send(
this.props.botName,
interactionsMessage
Expand Down
7 changes: 5 additions & 2 deletions packages/aws-amplify-react/src/Interactions/aws-lex-audio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,11 @@
if (!audioSupported) {
throw new Error(UNSUPPORTED);
}
recorder = audioRecorder.createRecorder(silenceDetectionConfig);
recorder.record(onSilence, visualizer);
const context = audioRecorder.audioContext();
context.resume().then(() => {
recorder = audioRecorder.createRecorder(silenceDetectionConfig);
recorder.record(onSilence, visualizer);
});
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,11 @@
if (!audioSupported) {
throw new Error(UNSUPPORTED);
}
recorder = audioRecorder.createRecorder(silenceDetectionConfig);
recorder.record(onSilence, visualizer);
const context = audioRecorder.audioContext();
context.resume().then(() => {
recorder = audioRecorder.createRecorder(silenceDetectionConfig);
recorder.record(onSilence, visualizer);
});
};

/**
Expand Down
172 changes: 127 additions & 45 deletions packages/interactions/__tests__/Interactions-unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ import {
PostContentCommand,
PostTextCommand,
} from '@aws-sdk/client-lex-runtime-service';
import { Readable } from 'stream';

// mock stream response
const createReadable = () => {
const stream = new Readable({
read() {},
});
stream.push('test');
stream.push(null);
return stream;
};
const typedStream = new Uint8Array([116, 101, 115, 116]); // 'test' in Uint8Array

LexRuntimeServiceClient.prototype.send = jest.fn((command, callback) => {
if (command instanceof PostTextCommand) {
Expand Down Expand Up @@ -36,12 +48,14 @@ LexRuntimeServiceClient.prototype.send = jest.fn((command, callback) => {
m1: 'voice:hi',
m2: 'voice:done',
},
audioStream: createReadable(),
};
return Promise.resolve(result);
} else {
const result = {
message: 'voice:echo:' + command.input.inputStream,
dialogState: 'ElicitSlot',
audioStream: createReadable(),
};
return Promise.resolve(result);
}
Expand All @@ -54,12 +68,14 @@ LexRuntimeServiceClient.prototype.send = jest.fn((command, callback) => {
m1: 'hi',
m2: 'done',
},
audioStream: createReadable(),
};
return Promise.resolve(result);
} else {
const result = {
message: 'echo:' + command.input.inputStream,
dialogState: 'ElicitSlot',
audioStream: createReadable(),
};
return Promise.resolve(result);
}
Expand Down Expand Up @@ -351,6 +367,7 @@ describe('Interactions', () => {
expect(responseVoice).toEqual({
dialogState: 'ElicitSlot',
message: 'voice:echo:voice:hi',
audioStream: typedStream,
});

const responseText = await interactions.send(
Expand All @@ -360,6 +377,7 @@ describe('Interactions', () => {
expect(responseText).toEqual({
dialogState: 'ElicitSlot',
message: 'echo:hi',
audioStream: typedStream,
});
});

Expand Down Expand Up @@ -424,6 +442,7 @@ describe('Interactions', () => {
expect(responseVoice).toEqual({
dialogState: 'ElicitSlot',
message: 'voice:echo:voice:hi',
audioStream: typedStream,
});

const responseText = await interactions.send(
Expand All @@ -433,11 +452,13 @@ describe('Interactions', () => {
expect(responseText).toEqual({
dialogState: 'ElicitSlot',
message: 'echo:hi',
audioStream: typedStream,
});
});

describe('Sending messages to bot', () => {
test('Interactions configuration and send message to existing bot and call onComplete from Interaction.onComplete', async () => {
jest.useFakeTimers();
test('onComplete callback from `Interactions.onComplete` called with text', async () => {
const curCredSpyOn = jest
.spyOn(Credentials, 'get')
.mockImplementation(() => Promise.resolve({ identityId: '1234' }));
Expand Down Expand Up @@ -466,7 +487,6 @@ describe('Interactions', () => {
interactions.onComplete('BookTripMOBILEHUB', onCompleteCallback);
await interactions.send('BookTripMOBILEHUB', 'hi');
const response = await interactions.send('BookTripMOBILEHUB', 'done');
jest.runAllTimers();
expect(response).toEqual({
dialogState: 'ReadyForFulfillment',
message: 'echo:done',
Expand All @@ -476,56 +496,87 @@ describe('Interactions', () => {
},
});

const interactionsMessageVoice = {
content: 'voice:done',
options: {
messageType: 'voice',
},
};

const interactionsMessageText = {
content: 'done',
options: {
messageType: 'text',
},
};

const voiceResponse = await interactions.send(
const textResponse = await interactions.send(
'BookTripMOBILEHUB',
interactionsMessageVoice
interactionsMessageText
);
expect(voiceResponse).toEqual({
expect(textResponse).toEqual({
dialogState: 'ReadyForFulfillment',
message: 'voice:echo:voice:done',
message: 'echo:done',
slots: {
m1: 'voice:hi',
m2: 'voice:done',
m1: 'hi',
m2: 'done',
},
audioStream: typedStream,
});
jest.runAllTimers();
});

const textResponse = await interactions.send(
test('onComplete callback from `Interactions.onComplete` called with voice', async () => {
const curCredSpyOn = jest
.spyOn(Credentials, 'get')
.mockImplementation(() => Promise.resolve({ identityId: '1234' }));

function onCompleteCallback(err, confirmation) {
expect(confirmation).toEqual({
slots: { m1: 'voice:hi', m2: 'voice:done' },
});
}

const configuration = {
Interactions: {
bots: {
BookTripMOBILEHUB: {
name: 'BookTripMOBILEHUB',
alias: '$LATEST',
region: 'us-east-1',
},
},
},
};

const interactions = new Interactions({});
const config = interactions.configure(configuration);
interactions.onComplete('BookTripMOBILEHUB', onCompleteCallback);

const interactionsMessageVoice = {
content: 'voice:done',
options: {
messageType: 'voice',
},
};

const voiceResponse = await interactions.send(
'BookTripMOBILEHUB',
interactionsMessageText
interactionsMessageVoice
);
expect(textResponse).toEqual({
expect(voiceResponse).toEqual({
dialogState: 'ReadyForFulfillment',
message: 'echo:done',
message: 'voice:echo:voice:done',
slots: {
m1: 'hi',
m2: 'done',
m1: 'voice:hi',
m2: 'voice:done',
},
audioStream: typedStream,
});
jest.runAllTimers();
});

test('Interactions configuration and send message to existing bot and call onComplete from configure onComplete', async () => {
test('onComplete callback from configure being called with text', async () => {
const curCredSpyOn = jest
.spyOn(Credentials, 'get')
.mockImplementation(() => Promise.resolve({ identityId: '1234' }));

function onCompleteCallback(err, confirmation) {
expect(confirmation).toEqual({ slots: { m1: 'hi', m2: 'done' } });
}

const configuration = {
Interactions: {
bots: {
Expand All @@ -540,14 +591,12 @@ describe('Interactions', () => {
};

const interactions = new Interactions({});

const config = interactions.configure(configuration);

expect(config).toEqual(configuration.Interactions);

await interactions.send('BookTripMOBILEHUB', 'hi');
const response = await interactions.send('BookTripMOBILEHUB', 'done');
jest.runAllTimers();
expect(response).toEqual({
dialogState: 'ReadyForFulfillment',
message: 'echo:done',
Expand All @@ -556,46 +605,77 @@ describe('Interactions', () => {
m2: 'done',
},
});

const interactionsMessageVoice = {
content: 'voice:done',
options: {
messageType: 'voice',
},
};

const interactionsMessageText = {
content: 'done',
options: {
messageType: 'text',
},
};

const voiceResponse = await interactions.send(
const textResponse = await interactions.send(
'BookTripMOBILEHUB',
interactionsMessageVoice
interactionsMessageText
);
expect(voiceResponse).toEqual({
expect(textResponse).toEqual({
dialogState: 'ReadyForFulfillment',
message: 'voice:echo:voice:done',
message: 'echo:done',
slots: {
m1: 'voice:hi',
m2: 'voice:done',
m1: 'hi',
m2: 'done',
},
audioStream: typedStream,
});
jest.runAllTimers();
});

const textResponse = await interactions.send(
test('onComplete callback from configure being called with voice', async () => {
const curCredSpyOn = jest
.spyOn(Credentials, 'get')
.mockImplementation(() => Promise.resolve({ identityId: '1234' }));

function onCompleteCallback(err, confirmation) {
expect(confirmation).toEqual({
slots: { m1: 'voice:hi', m2: 'voice:done' },
});
}
const configuration = {
Interactions: {
bots: {
BookTripMOBILEHUB: {
name: 'BookTripMOBILEHUB',
alias: '$LATEST',
region: 'us-east-1',
onComplete: onCompleteCallback,
},
},
},
};

const interactions = new Interactions({});
const config = interactions.configure(configuration);

expect(config).toEqual(configuration.Interactions);

const interactionsMessageVoice = {
content: 'voice:done',
options: {
messageType: 'voice',
},
};
const voiceResponse = await interactions.send(
'BookTripMOBILEHUB',
interactionsMessageText
interactionsMessageVoice
);
expect(textResponse).toEqual({
expect(voiceResponse).toEqual({
dialogState: 'ReadyForFulfillment',
message: 'echo:done',
message: 'voice:echo:voice:done',
slots: {
m1: 'hi',
m2: 'done',
m1: 'voice:hi',
m2: 'voice:done',
},
audioStream: typedStream,
});
jest.runAllTimers();
});

test('aws-exports configuration and send message to not existing bot', async () => {
Expand Down Expand Up @@ -836,6 +916,7 @@ describe('Interactions', () => {
expect(responseVoice).toEqual({
dialogState: 'ElicitSlot',
message: 'voice:echo:voice:hi',
audioStream: typedStream,
});

const responseText = await interactions.send(
Expand All @@ -845,6 +926,7 @@ describe('Interactions', () => {
expect(responseText).toEqual({
dialogState: 'ElicitSlot',
message: 'echo:hi',
audioStream: typedStream,
});
});
});
Expand Down
Loading

0 comments on commit 8c6fb4a

Please sign in to comment.