@yoctol/kurator
Version:
1,620 lines (1,485 loc) • 42.9 kB
JavaScript
const BottenderTransformer = require('../BottenderTransformer');
function setup({ hasThreadControl } = {}) {
return {
context: {
platform: 'messenger',
sendText: jest.fn(),
sendImage: jest.fn(),
sendButtonTemplate: jest.fn(),
sendGenericTemplate: jest.fn(),
passThreadControlToPageInbox: jest.fn(),
takeThreadControl: jest.fn(),
},
kurator: {
_alreadySwitchToHumanActionId: 'ALREADY_SWITCH_TO_HUMAN_ACITON_ID',
getLocalAction: jest.fn(),
getRemoteAction: jest.fn(),
hasThreadControl: jest.fn().mockReturnValue(hasThreadControl),
},
};
}
describe('#toBottenderComposeAction', () => {
it('should support basic text', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'text',
platform: 'messenger',
descriptor: {
text: 'hello',
},
});
await action(context);
expect(context.sendText).toBeCalledWith('hello', {});
});
it('should support text with web_url button', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'text',
platform: 'messenger',
descriptor: {
text: 'hello',
buttons: [
{
type: 'web_url',
title: 'Yoctol',
payload: 'https://yoctol.com',
webviewSetting: {
extension: true,
heightRatio: 'tall',
},
},
],
},
});
await action(context);
expect(context.sendButtonTemplate).toBeCalledWith(
'hello',
[
{
type: 'web_url',
title: 'Yoctol',
url: 'https://yoctol.com',
messenger_extensions: true,
webview_height_ratio: 'tall',
},
],
{}
);
});
it('should support text with postback button', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'text',
platform: 'messenger',
descriptor: {
text: 'hello',
buttons: [
{
type: 'postback',
title: '登入',
payload: '__行動服務流量查詢結果__',
},
{
type: 'postback',
title: '第一次用,加入會員',
payload: '__行動服務加入會員__',
},
],
},
});
await action(context);
expect(context.sendButtonTemplate).toBeCalledWith(
'hello',
[
{
type: 'postback',
title: '登入',
payload: '__行動服務流量查詢結果__',
},
{
type: 'postback',
title: '第一次用,加入會員',
payload: '__行動服務加入會員__',
},
],
{}
);
});
it('should avoid invalid keys in button', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'text',
platform: 'messenger',
descriptor: {
text: 'hello',
buttons: [
{
type: 'web_url',
title: 'Yoctol',
payload: 'https://yoctol.com',
webviewSetting: {
extension: true,
heightRatio: 'tall',
},
__typename: 'Button',
},
],
},
});
await action(context);
expect(context.sendButtonTemplate).toBeCalledWith(
'hello',
[
{
type: 'web_url',
title: 'Yoctol',
url: 'https://yoctol.com',
messenger_extensions: true,
webview_height_ratio: 'tall',
},
],
{}
);
});
it('should support pure image', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'pure_image',
platform: 'messenger',
descriptor: {
imageUrl: 'https://example.com/image.jpg',
},
});
await action(context);
expect(context.sendImage).toBeCalledWith(
'https://example.com/image.jpg',
{}
);
});
it('should support basic image', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'image',
platform: 'messenger',
descriptor: {
imageUrl: 'https://example.com/image.jpg',
},
});
await action(context);
expect(context.sendImage).toBeCalledWith(
'https://example.com/image.jpg',
{}
);
});
it('should support image with buttons (includes webUrl)', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'image',
platform: 'messenger',
descriptor: {
imageUrl: 'https://example.com/image.jpg',
title: 'Welcome!',
subtitle: 'We have the right hat for everyone.',
webUrl: 'https://petersfancybrownhats.com/view?item=103',
buttons: [
{
type: 'web_url',
title: 'View Website',
payload: 'https://petersfancybrownhats.com',
webviewSetting: {
extension: true,
heightRatio: 'tall',
},
},
{
type: 'postback',
title: 'Start Chatting',
payload: 'DEVELOPER_DEFINED_PAYLOAD',
},
],
},
});
await action(context);
expect(context.sendGenericTemplate).toBeCalledWith(
[
{
title: 'Welcome!',
image_url: 'https://example.com/image.jpg',
subtitle: 'We have the right hat for everyone.',
default_action: {
type: 'web_url',
url: 'https://petersfancybrownhats.com/view?item=103',
messenger_extensions: false,
webview_height_ratio: 'tall',
},
buttons: [
{
type: 'web_url',
title: 'View Website',
url: 'https://petersfancybrownhats.com',
messenger_extensions: true,
webview_height_ratio: 'tall',
},
{
type: 'postback',
title: 'Start Chatting',
payload: 'DEVELOPER_DEFINED_PAYLOAD',
},
],
},
],
{ image_aspect_ratio: 'horizontal' }
);
});
it('should support carousel (includes webUrl)', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'carousel',
platform: 'messenger',
descriptor: {},
actions: [
{
type: 'image',
descriptor: {
title: '小可樂',
webUrl: 'https://yoctol.com',
buttons: [
{
id: 'dlf7nZarC',
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
imageUrl:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004659',
subtitle: '$34',
},
},
{
type: 'image',
descriptor: {
title: '中可樂',
webUrl: 'https://yoctol.com',
buttons: [
{
id: 'lclAuJsmF3',
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
imageUrl:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004658',
subtitle: '$43',
},
},
{
type: 'image',
descriptor: {
title: '大可樂',
webUrl: 'https://yoctol.com',
buttons: [
{
id: '7yR381reSL',
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
imageUrl:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004657',
subtitle: '$51',
},
},
],
});
await action(context);
expect(context.sendGenericTemplate).toBeCalledWith(
[
{
title: '小可樂',
image_url:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004659',
subtitle: '$34',
default_action: {
type: 'web_url',
url: 'https://yoctol.com',
messenger_extensions: false,
webview_height_ratio: 'tall',
},
buttons: [
{
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
},
{
title: '中可樂',
image_url:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004658',
subtitle: '$43',
default_action: {
type: 'web_url',
url: 'https://yoctol.com',
messenger_extensions: false,
webview_height_ratio: 'tall',
},
buttons: [
{
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
},
{
title: '大可樂',
image_url:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004657',
subtitle: '$51',
default_action: {
type: 'web_url',
url: 'https://yoctol.com',
messenger_extensions: false,
webview_height_ratio: 'tall',
},
buttons: [
{
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
},
],
{ image_aspect_ratio: 'horizontal' }
);
});
it('should support image with buttons (without webUrl)', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'image',
platform: 'messenger',
descriptor: {
imageUrl: 'https://example.com/image.jpg',
title: 'Welcome!',
subtitle: 'We have the right hat for everyone.',
buttons: [
{
type: 'web_url',
title: 'View Website',
payload: 'https://petersfancybrownhats.com',
webviewSetting: {
extension: true,
heightRatio: 'tall',
},
},
{
type: 'postback',
title: 'Start Chatting',
payload: 'DEVELOPER_DEFINED_PAYLOAD',
},
],
},
});
await action(context);
expect(context.sendGenericTemplate).toBeCalledWith(
[
{
title: 'Welcome!',
image_url: 'https://example.com/image.jpg',
subtitle: 'We have the right hat for everyone.',
buttons: [
{
type: 'web_url',
title: 'View Website',
url: 'https://petersfancybrownhats.com',
messenger_extensions: true,
webview_height_ratio: 'tall',
},
{
type: 'postback',
title: 'Start Chatting',
payload: 'DEVELOPER_DEFINED_PAYLOAD',
},
],
},
],
{ image_aspect_ratio: 'horizontal' }
);
});
it('should support random', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'weight',
platform: 'messenger',
descriptor: {},
actions: [
{
type: 'text',
platform: 'messenger',
descriptor: {
text: 'hello',
},
},
{
type: 'text',
platform: 'messenger',
descriptor: {
text: 'world',
},
},
],
});
await action(context);
expect(context.sendText).toHaveBeenCalledTimes(1);
expect(context.sendText).toBeCalledWith(
expect.stringMatching(/(hello|world)/),
{}
);
});
it('should support random with image', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'weight',
platform: 'messenger',
descriptor: {},
actions: [
{
type: 'image',
platform: 'messenger',
descriptor: {
imageUrl: 'https://example.com/image1.jpg',
},
},
{
type: 'image',
platform: 'messenger',
descriptor: {
imageUrl: 'https://example.com/image2.jpg',
},
},
],
});
await action(context);
expect(context.sendImage).toHaveBeenCalledTimes(1);
expect(context.sendImage).toBeCalledWith(
expect.stringMatching(/https:\/\/example.com\/image(1|2)\.jpg/),
{}
);
});
it('should support weight', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'weight',
platform: 'messenger',
descriptor: {
probabilities: [0.5, 0.5],
},
actions: [
{
type: 'text',
platform: 'messenger',
descriptor: {
text: 'hello',
},
},
{
type: 'text',
platform: 'messenger',
descriptor: {
text: 'world',
},
},
],
});
await action(context);
expect(context.sendText).toHaveBeenCalledTimes(1);
expect(context.sendText).toBeCalledWith(
expect.stringMatching(/(hello|world)/),
{}
);
});
it('should support weight with image', async () => {
const { context } = setup();
const action = BottenderTransformer.toBottenderComposeAction({
type: 'weight',
platform: 'messenger',
descriptor: {
probabilities: [0.5, 0.5],
},
actions: [
{
type: 'image',
platform: 'messenger',
descriptor: {
imageUrl: 'https://example.com/image1.jpg',
},
},
{
type: 'image',
platform: 'messenger',
descriptor: {
imageUrl: 'https://example.com/image2.jpg',
},
},
],
});
await action(context);
expect(context.sendImage).toHaveBeenCalledTimes(1);
expect(context.sendImage).toBeCalledWith(
expect.stringMatching(/https:\/\/example.com\/image(1|2)\.jpg/),
{}
);
});
});
describe('#createFromActionDefinition', () => {
it('should create series actions', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'world',
},
},
],
});
await action(context);
expect(context.sendText).toHaveBeenCalledTimes(2);
expect(context.sendText.mock.calls[0][0]).toBe('hello');
expect(context.sendText.mock.calls[1][0]).toBe('world');
});
describe('quick replies', () => {
it('should support text + quick replies (text)', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '不跟你說',
payload: '',
},
},
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '跟你說',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendText).toBeCalledWith('hello', {
quick_replies: [
{ content_type: 'text', payload: '', title: '不跟你說' },
{ content_type: 'text', payload: '', title: '跟你說' },
],
});
});
it('should support text + quick replies (location)', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
descriptor: {
type: 'location',
title: '',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendText).toBeCalledWith('hello', {
quick_replies: [{ content_type: 'location' }],
});
});
it('should support text + quick replies (user_phone_number)', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
descriptor: {
type: 'user_phone_number',
title: '',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendText).toBeCalledWith('hello', {
quick_replies: [{ content_type: 'user_phone_number' }],
});
});
it('should support text + quick replies (user_email)', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
descriptor: {
type: 'user_email',
title: '',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendText).toBeCalledWith('hello', {
quick_replies: [{ content_type: 'user_email' }],
});
});
it('should support text + buttons + quick replies', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
buttons: [
{
type: 'postback',
title: '登入',
payload: '__行動服務流量查詢結果__',
},
{
type: 'postback',
title: '第一次用,加入會員',
payload: '__行動服務加入會員__',
},
],
},
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '不跟你說',
payload: '',
},
},
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '跟你說',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendButtonTemplate).toBeCalledWith(
'hello',
[
{
type: 'postback',
title: '登入',
payload: '__行動服務流量查詢結果__',
},
{
type: 'postback',
title: '第一次用,加入會員',
payload: '__行動服務加入會員__',
},
],
{
quick_replies: [
{ content_type: 'text', payload: '', title: '不跟你說' },
{ content_type: 'text', payload: '', title: '跟你說' },
],
}
);
});
it('should support image + quick replies', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'image',
descriptor: {
imageUrl: 'https://example.com/image.jpg',
},
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '不跟你說',
payload: '',
},
},
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '跟你說',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendImage).toBeCalledWith(
'https://example.com/image.jpg',
{
quick_replies: [
{ content_type: 'text', payload: '', title: '不跟你說' },
{ content_type: 'text', payload: '', title: '跟你說' },
],
}
);
});
it('should support image + buttons + quick replies', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'image',
descriptor: {
imageUrl: 'https://example.com/image.jpg',
title: 'Welcome!',
subtitle: 'We have the right hat for everyone.',
webUrl: 'https://petersfancybrownhats.com/view?item=103',
buttons: [
{
type: 'web_url',
title: 'View Website',
payload: 'https://petersfancybrownhats.com',
webviewSetting: {
extension: true,
heightRatio: 'tall',
},
},
{
type: 'postback',
title: 'Start Chatting',
payload: 'DEVELOPER_DEFINED_PAYLOAD',
},
],
},
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '不跟你說',
payload: '',
},
},
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '跟你說',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendGenericTemplate).toBeCalledWith(
[
{
title: 'Welcome!',
image_url: 'https://example.com/image.jpg',
subtitle: 'We have the right hat for everyone.',
default_action: {
type: 'web_url',
url: 'https://petersfancybrownhats.com/view?item=103',
messenger_extensions: false,
webview_height_ratio: 'tall',
},
buttons: [
{
type: 'web_url',
title: 'View Website',
url: 'https://petersfancybrownhats.com',
messenger_extensions: true,
webview_height_ratio: 'tall',
},
{
type: 'postback',
title: 'Start Chatting',
payload: 'DEVELOPER_DEFINED_PAYLOAD',
},
],
},
],
{
quick_replies: [
{ content_type: 'text', payload: '', title: '不跟你說' },
{ content_type: 'text', payload: '', title: '跟你說' },
],
image_aspect_ratio: 'horizontal',
}
);
});
it('should support carousel + quick replies', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'carousel',
descriptor: {},
actions: [
{
type: 'image',
descriptor: {
title: '小可樂',
webUrl: 'https://yoctol.com',
buttons: [
{
id: 'dlf7nZarC',
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
imageUrl:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004659',
subtitle: '$34',
},
},
{
type: 'image',
descriptor: {
title: '中可樂',
webUrl: 'https://yoctol.com',
buttons: [
{
id: 'lclAuJsmF3',
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
imageUrl:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004658',
subtitle: '$43',
},
},
{
type: 'image',
descriptor: {
title: '大可樂',
webUrl: 'https://yoctol.com',
buttons: [
{
id: '7yR381reSL',
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
imageUrl:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004657',
subtitle: '$51',
},
},
],
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '不跟你說',
payload: '',
},
},
{
type: 'quick_reply',
descriptor: {
type: 'text',
title: '跟你說',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendGenericTemplate).toBeCalledWith(
[
{
title: '小可樂',
image_url:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004659',
subtitle: '$34',
default_action: {
type: 'web_url',
url: 'https://yoctol.com',
messenger_extensions: false,
webview_height_ratio: 'tall',
},
buttons: [
{
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
},
{
title: '中可樂',
image_url:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004658',
subtitle: '$43',
default_action: {
type: 'web_url',
url: 'https://yoctol.com',
messenger_extensions: false,
webview_height_ratio: 'tall',
},
buttons: [
{
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
},
{
title: '大可樂',
image_url:
'https://sales.vscinemas.com.tw/CDN/media/entity/get/ItemGraphic/10004657',
subtitle: '$51',
default_action: {
type: 'web_url',
url: 'https://yoctol.com',
messenger_extensions: false,
webview_height_ratio: 'tall',
},
buttons: [
{
type: 'postback',
title: '來一杯',
payload: '__來一杯__',
},
],
},
],
{
quick_replies: [
{ content_type: 'text', payload: '', title: '不跟你說' },
{ content_type: 'text', payload: '', title: '跟你說' },
],
image_aspect_ratio: 'horizontal',
}
);
});
it('should support random + quick replies', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'weight',
descriptor: {},
actions: [
{
type: 'text',
platform: 'messenger',
descriptor: {
text: 'hello',
},
},
{
type: 'text',
platform: 'messenger',
descriptor: {
text: 'world',
},
},
],
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
platform: 'messenger',
descriptor: {
type: 'text',
title: '不跟你說',
payload: '',
},
},
{
type: 'quick_reply',
platform: 'messenger',
descriptor: {
type: 'text',
title: '跟你說',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendText).toHaveBeenCalledTimes(1);
expect(context.sendText).toBeCalledWith(
expect.stringMatching(/(hello|world)/),
{
quick_replies: [
{ content_type: 'text', payload: '', title: '不跟你說' },
{ content_type: 'text', payload: '', title: '跟你說' },
],
}
);
});
it('should support weight + quick replies', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition({
actions: [
{
platform: 'messenger',
type: 'weight',
descriptor: {
probabilities: [0.5, 0.5],
},
actions: [
{
type: 'text',
platform: 'messenger',
descriptor: {
text: 'hello',
},
},
{
type: 'text',
platform: 'messenger',
descriptor: {
text: 'world',
},
},
],
},
{
platform: 'messenger',
type: 'quick_replies',
descriptor: {},
actions: [
{
type: 'quick_reply',
platform: 'messenger',
descriptor: {
type: 'text',
title: '不跟你說',
payload: '',
},
},
{
type: 'quick_reply',
platform: 'messenger',
descriptor: {
type: 'text',
title: '跟你說',
payload: '',
},
},
],
},
],
});
await action(context);
expect(context.sendText).toHaveBeenCalledTimes(1);
expect(context.sendText).toBeCalledWith(
expect.stringMatching(/(hello|world)/),
{
quick_replies: [
{ content_type: 'text', payload: '', title: '不跟你說' },
{ content_type: 'text', payload: '', title: '跟你說' },
],
}
);
});
});
it('should support rpc', async () => {
const { context, kurator } = setup();
kurator.getLocalAction.mockReturnValue(async _context => {
await _context.sendText('world');
});
const action = BottenderTransformer.createFromActionDefinition(
{
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'messenger',
type: 'rpc',
descriptor: {
name: 'world',
},
},
],
},
{
kurator,
}
);
await action(context);
expect(kurator.getLocalAction).toBeCalledWith('world', {
byReference: true,
});
expect(context.sendText).toHaveBeenCalledTimes(2);
expect(context.sendText.mock.calls[0][0]).toBe('hello');
expect(context.sendText.mock.calls[1][0]).toBe('world');
});
it('should support bridge', async () => {
const { context, kurator } = setup();
kurator.getRemoteAction.mockReturnValue(async _context => {
await _context.sendText('world');
});
const action = BottenderTransformer.createFromActionDefinition(
{
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'messenger',
type: 'bridge',
descriptor: {
bridgeActionId: '202468246467646540',
},
},
],
},
{
kurator,
}
);
await action(context);
expect(kurator.getRemoteAction).toBeCalledWith('202468246467646540', {
byReference: true,
});
expect(context.sendText).toHaveBeenCalledTimes(2);
expect(context.sendText.mock.calls[0][0]).toBe('hello');
expect(context.sendText.mock.calls[1][0]).toBe('world');
});
it('should support message options', async () => {
const { context } = setup();
const action = BottenderTransformer.createFromActionDefinition(
{
actions: [
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'messenger',
type: 'text',
descriptor: {
text: 'world',
},
},
],
},
{
tag: 'PAYMENT_UPDATE',
messaging_type: 'MESSAGE_TAG',
persona_id: '<PERSONA_ID>',
}
);
await action(context);
expect(context.sendText).toHaveBeenCalledTimes(2);
expect(context.sendText).toBeCalledWith('hello', {
messaging_type: 'MESSAGE_TAG',
persona_id: '<PERSONA_ID>',
tag: 'PAYMENT_UPDATE',
});
expect(context.sendText).toBeCalledWith('world', {
messaging_type: 'MESSAGE_TAG',
persona_id: '<PERSONA_ID>',
tag: 'PAYMENT_UPDATE',
});
});
it('should fallback to universal action when platfrom action not found', async () => {
const { context } = setup();
context.platfrom = 'A_NEW_PLATFORM';
const action = BottenderTransformer.createFromActionDefinition(
{
actions: [
{
platform: 'universal',
type: 'text',
descriptor: {
text: 'hello',
},
},
{
platform: 'universal',
type: 'text',
descriptor: {
text: 'world',
},
},
],
},
{ customAdapter: require('../adapters/messenger') } // should have a custom adapter, use messenger adapter here for convenience
);
await action(context);
expect(context.sendText).toHaveBeenCalledTimes(2);
expect(context.sendText.mock.calls[0][0]).toBe('hello');
expect(context.sendText.mock.calls[1][0]).toBe('world');
});
describe('switch to human actions', () => {
it('should support SWITCH_TO_HUMAN type of action', async () => {
const { context, kurator } = setup({ hasThreadControl: true });
const action = BottenderTransformer.createFromActionDefinition(
{
kind: 'SWITCH_TO_HUMAN',
actions: [
{
type: 'text',
descriptor: {
text: '現在是真人在回應你喔,請耐心等待!',
buttons: [
{
type: 'postback',
title: '回到機器人模式',
payload: '395936974071928831',
},
],
},
platform: 'messenger',
},
],
},
{
kurator,
}
);
await action(context);
expect(kurator.hasThreadControl).toBeCalled();
expect(context.passThreadControlToPageInbox).toBeCalled();
expect(context.sendButtonTemplate).toBeCalledWith(
'現在是真人在回應你喔,請耐心等待!',
[
{
type: 'postback',
title: '回到機器人模式',
payload: '395936974071928831',
},
],
{}
);
});
it('should call ALREADY_SWITCH_TO_HUMAN when not having thread contorl', async () => {
const { context, kurator } = setup({ hasThreadControl: false });
const action = BottenderTransformer.createFromActionDefinition(
{
kind: 'SWITCH_TO_HUMAN',
actions: [
{
type: 'text',
descriptor: {
text: '現在是真人在回應你喔,請耐心等待!',
buttons: [
{
type: 'postback',
title: '回到機器人模式',
payload: '395936974071928831',
},
],
},
platform: 'messenger',
},
],
},
{
kurator,
}
);
await action(context);
expect(kurator.hasThreadControl).toBeCalled();
expect(context.passThreadControlToPageInbox).not.toBeCalled();
expect(kurator.getRemoteAction).toBeCalledWith(
kurator._alreadySwitchToHumanActionId,
{ byReference: true }
);
});
it('should support SWITCH_TO_BOT type of action', async () => {
const { context, kurator } = setup({ hasThreadControl: false });
const action = BottenderTransformer.createFromActionDefinition(
{
kind: 'SWITCH_TO_BOT',
actions: [
{
type: 'text',
descriptor: {
text: '真人小編已離開,現在換機器人小編上場!',
buttons: [],
},
platform: 'messenger',
},
],
},
{
kurator,
}
);
await action(context);
expect(kurator.hasThreadControl).toBeCalled();
expect(context.takeThreadControl).toBeCalled();
expect(context.sendText).toBeCalledWith(
'真人小編已離開,現在換機器人小編上場!',
{}
);
});
it('should do nothing when trigger SWITCH_TO_BOT but having thread control', async () => {
const { context, kurator } = setup({ hasThreadControl: true });
const action = BottenderTransformer.createFromActionDefinition(
{
kind: 'SWITCH_TO_BOT',
actions: [
{
type: 'text',
descriptor: {
text: '真人小編已離開,現在換機器人小編上場!',
buttons: [],
},
platform: 'messenger',
},
],
},
{
kurator,
}
);
await action(context);
expect(kurator.hasThreadControl).toBeCalled();
expect(context.takeThreadControl).not.toBeCalled();
expect(context.sendText).toBeCalledWith(
'真人小編已離開,現在換機器人小編上場!',
{}
);
});
});
});