UNPKG

@yoctol/kurator

Version:

1,620 lines (1,485 loc) 42.9 kB
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( '真人小編已離開,現在換機器人小編上場!', {} ); }); }); });