* feat: baileys provider and placeholder for link device modal * chore: drop qrcode.vue in favor of just img tag * chore: update modal props * feat: setup channel provider connection * chore: update .env.example with Baileys API default configuration * feat: add support for Baileys provider in WhatsApp events processing * chore: rename Baileys API default host variable to DEFAULT_BAILEYS_URL * feat: add setup and disconnect methods for Baileys channel provider in inboxes controller that will be implemented * feat: add CHANNEL_CONNECTION_UPDATE event and include it in broadcast data preparation * refactor: simplify channel retrieval logic in WhatsappEventsJob * refactor: revert CHANNEL_UPDATE_EVENTS constant from ActionCableBroadcastJob * feat: add 'baileys' as a provider option in Whatsapp channel model * feat: add provider_connection field to Whatsapp channel model and migration * refactor: remove unnecessary CHANNEL_CONNECTION_UPDATE event type * feat: implement channel provider connection with baileys API * feat: add inbox association to Whatsapp channel model and update webhook URL handling * feat: enhance Baileys service to handle webhook multiple event types * refactor: simplify webhook verification logic in Baileys service * feat: add setup channel provider call, and refactor some logic * chore: adapt logic to new API * refactor: fix typo * refactor: fix import * refactor: fix typo * chore: add fixme comment about race condition * fix: remove double disconnect call * feat: implement message processing for incoming WhatsApp messages * refactor: streamline message type determination and improve readability * chore: increase cache key granularity provider connection info might be updated multiple times within 1 second, so updates might be lost due to cache key not being updated. changing cache key to milliseconds solves this * feat: add `is-loading` to buttons * feat: update send_message method to use 'to' parameter and improve error handling * refactor: simplify test setup and update API key in specs * chore: add setup and disconnect channel provider specs * test: fix spec after increase cache key granularity * feat: handle reconnecting state on modal * style: centered error text * feat: advanced options on create inbox * feat: handle new reconnecting on backend * refactor: update inbox controller specs and leave a FIXME note * test: add specs for Whatsapp::IncomingMessageBaileysService * feat: add baileys configuration page * feat: link device button when disconnected on conversation * chore: refactor .env.example * feat: add TODO for unimplemented methods in IncomingMessageBaileysService * fix: correct method name and update environment variable references in WhatsappBaileysService * refactor: simplify channel lookup by removing redundant method and handling phone number check directly * chore: add TODO for unimplemented event processing methods in IncomingMessageBaileysService * fix: update environment variable references in WhatsappBaileysService tests * chore(webhook): add pt-BR translations * chore: add pt-br translations * chore: inboxname component margin * refactor: inboxname computed prop * feat: enhance WhatsApp provider connection handling and message processing * test: inbox controller * chore: improve baileys connection and messages handling * test: incoming message service baileys * refactor: update provider config validation and improve test setup for WhatsApp Baileys service * fix: ensure only text messages are sent and update message source ID * fix: create message * fix: only update message on success * test: fix broken specs * chore: raise error on unsupported message content type * feat: hide provider connection data from non-admins * fix: update advanced options * chore: move class definition * fix: issue with send_message not returning id --------- Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
295 lines
11 KiB
JavaScript
295 lines
11 KiB
JavaScript
import { shallowMount } from '@vue/test-utils';
|
|
import inboxMixin from '../inboxMixin';
|
|
|
|
function getComponentConfigForInbox(channelType, additionalConfig = {}) {
|
|
return {
|
|
render() {},
|
|
mixins: [inboxMixin],
|
|
data() {
|
|
return {
|
|
inbox: {
|
|
channel_type: channelType,
|
|
...additionalConfig,
|
|
},
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
function getComponentConfigForChat(chat) {
|
|
return {
|
|
render() {},
|
|
mixins: [inboxMixin],
|
|
data() {
|
|
return {
|
|
chat,
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
describe('inboxMixin', () => {
|
|
it('returns the correct channel type', () => {
|
|
const Component = getComponentConfigForInbox('Channel::WebWidget');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.channelType).toBe('Channel::WebWidget');
|
|
});
|
|
|
|
it('isAPIInbox returns true if channel type is API', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Api');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isAPIInbox).toBe(true);
|
|
});
|
|
|
|
it('isATwitterInbox returns true if channel type is twitter', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwitterProfile');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwitterInbox).toBe(true);
|
|
});
|
|
|
|
it('isAFacebookInbox returns true if channel type is Facebook', () => {
|
|
const Component = getComponentConfigForInbox('Channel::FacebookPage');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isAFacebookInbox).toBe(true);
|
|
});
|
|
|
|
it('isAWebWidgetInbox returns true if channel type is Facebook', () => {
|
|
const Component = getComponentConfigForInbox('Channel::WebWidget');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isAWebWidgetInbox).toBe(true);
|
|
});
|
|
|
|
it('isASmsInbox returns true if channel type is sms', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Sms');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isASmsInbox).toBe(true);
|
|
});
|
|
|
|
it('isASmsInbox returns true if channel type is twilio sms', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
|
medium: 'sms',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isASmsInbox).toBe(true);
|
|
});
|
|
|
|
it('isALineChannel returns true if channel type is Line', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Line');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isALineChannel).toBe(true);
|
|
});
|
|
|
|
it('isATelegramChannel returns true if channel type is Telegram', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Telegram');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATelegramChannel).toBe(true);
|
|
});
|
|
|
|
it('isATwilioChannel returns true if channel type is Twilio', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwilioSms');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwilioChannel).toBe(true);
|
|
});
|
|
|
|
describe('isATwilioSMSChannel', () => {
|
|
it('returns true if channel type is Twilio and medium is SMS', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
|
medium: 'sms',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
|
});
|
|
|
|
it('returns false if channel type is Twilio but medium is not SMS', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
|
medium: 'whatsapp',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
|
});
|
|
|
|
it('returns false if channel type is not Twilio but medium is SMS', () => {
|
|
const Component = getComponentConfigForInbox('Channel::NotTwilio', {
|
|
medium: 'sms',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
|
});
|
|
|
|
it('returns false if neither channel type is Twilio nor medium is SMS', () => {
|
|
const Component = getComponentConfigForInbox('Channel::NotTwilio', {
|
|
medium: 'not_sms',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
|
});
|
|
|
|
it('returns false if channel type is Twilio but medium is empty', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
|
medium: undefined,
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
|
});
|
|
});
|
|
|
|
it('isAnEmailChannel returns true if channel type is email', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Email');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isAnEmailChannel).toBe(true);
|
|
});
|
|
|
|
it('isTwitterInboxTweet returns true if Twitter channel type is tweet', () => {
|
|
const Component = getComponentConfigForChat({
|
|
channel_type: 'Channel::TwitterProfile',
|
|
additional_attributes: {
|
|
type: 'tweet',
|
|
},
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isTwitterInboxTweet).toBe(true);
|
|
});
|
|
|
|
it('twilioBadge returns string sms if channel type is Twilio and medium is sms', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
|
medium: 'sms',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
|
expect(wrapper.vm.twilioBadge).toBe('sms');
|
|
});
|
|
|
|
it('twitterBadge returns string twitter-tweet if Twitter channel type is tweet', () => {
|
|
const Component = getComponentConfigForChat({
|
|
id: 1,
|
|
additional_attributes: {
|
|
type: 'tweet',
|
|
},
|
|
});
|
|
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isTwitterInboxTweet).toBe(true);
|
|
expect(wrapper.vm.twitterBadge).toBe('twitter-tweet');
|
|
});
|
|
|
|
describe('Badges', () => {
|
|
it('inboxBadge returns string Channel::Telegram if isATwilioChannel and isATwitterInbox is false', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Telegram');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isATwilioChannel).toBe(false);
|
|
expect(wrapper.vm.isATwitterInbox).toBe(false);
|
|
expect(wrapper.vm.channelType).toBe('Channel::Telegram');
|
|
});
|
|
|
|
it('inboxBadge returns correct badge for WhatsApp channel', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Whatsapp');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.inboxBadge).toBe('whatsapp');
|
|
});
|
|
|
|
it('inboxBadge returns the twitterBadge when isATwitterInbox is true', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwitterProfile');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.inboxBadge).toBe('twitter-dm');
|
|
});
|
|
|
|
it('inboxBadge returns the facebookBadge when isAFacebookInbox is true', () => {
|
|
const Component = getComponentConfigForInbox('Channel::FacebookPage');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.inboxBadge).toBe('facebook');
|
|
});
|
|
|
|
it('inboxBadge returns the twilioBadge when isATwilioChannel is true', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
|
medium: 'sms',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.inboxBadge).toBe('sms');
|
|
});
|
|
|
|
it('inboxBadge returns "whatsapp" when isAWhatsAppChannel is true', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Whatsapp');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.inboxBadge).toBe('whatsapp');
|
|
});
|
|
|
|
it('inboxBadge returns the channelType when no specific condition is true', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Email');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.inboxBadge).toBe('Channel::Email');
|
|
});
|
|
});
|
|
|
|
describe('#inboxHasFeature', () => {
|
|
it('detects the correct feature', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Telegram');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.inboxHasFeature('replyTo')).toBe(true);
|
|
expect(wrapper.vm.inboxHasFeature('feature-does-not-exist')).toBe(false);
|
|
});
|
|
|
|
it('returns false for feature not included', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Sms');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.inboxHasFeature('replyTo')).toBe(false);
|
|
expect(wrapper.vm.inboxHasFeature('feature-does-not-exist')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('WhatsApp channel', () => {
|
|
it('returns correct whatsAppAPIProvider', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
|
provider: 'whatsapp_cloud',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.whatsAppAPIProvider).toBe('whatsapp_cloud');
|
|
});
|
|
|
|
it('returns empty whatsAppAPIProvider if nothing is present', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
|
provider: undefined,
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.whatsAppAPIProvider).toBe('');
|
|
});
|
|
|
|
it('isAWhatsAppCloudChannel returns true if channel type is WhatsApp and provider is whatsapp_cloud', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
|
provider: 'whatsapp_cloud',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isAWhatsAppCloudChannel).toBe(true);
|
|
});
|
|
|
|
it('is360DialogWhatsAppChannel returns true if channel type is WhatsApp and provider is default', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
|
provider: 'default',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.is360DialogWhatsAppChannel).toBe(true);
|
|
});
|
|
|
|
it('isAWhatsAppBaileysChannel returns true if channel type is WhatsApp and provider is baileys', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Whatsapp', {
|
|
provider: 'baileys',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isAWhatsAppBaileysChannel).toBe(true);
|
|
});
|
|
|
|
it('isAWhatsAppChannel returns true if channel type is WhatsApp', () => {
|
|
const Component = getComponentConfigForInbox('Channel::Whatsapp');
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isAWhatsAppChannel).toBe(true);
|
|
});
|
|
|
|
it('isAWhatsAppChannel returns true if channel type is Twilio and medium is WhatsApp', () => {
|
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
|
medium: 'whatsapp',
|
|
});
|
|
const wrapper = shallowMount(Component);
|
|
expect(wrapper.vm.isAWhatsAppChannel).toBe(true);
|
|
});
|
|
});
|
|
});
|