Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
164b4df679 | ||
|
|
2014a82ff8 | ||
|
|
f60ab0eb1d | ||
|
|
dc6757df3d | ||
|
|
cfeddcbec2 | ||
|
|
3aa79dffef | ||
|
|
758b7bf061 | ||
|
|
93d126af7b | ||
|
|
3c6c57ca0e | ||
|
|
b952608eeb | ||
|
|
eb6f1e4031 | ||
|
|
94909255a8 | ||
|
|
91788255f2 | ||
|
|
515b9ba0b5 | ||
|
|
e5c5ebc398 | ||
|
|
21212d9bc3 | ||
|
|
0af9e974bd | ||
|
|
b3141de50b | ||
|
|
1da6962467 |
@@ -124,6 +124,7 @@
|
|||||||
:message="data"
|
:message="data"
|
||||||
@open="openContextMenu"
|
@open="openContextMenu"
|
||||||
@close="closeContextMenu"
|
@close="closeContextMenu"
|
||||||
|
@replyTo="handleReplyTo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -149,6 +150,8 @@ import { MESSAGE_TYPE, MESSAGE_STATUS } from 'shared/constants/messages';
|
|||||||
import { generateBotMessageContent } from './helpers/botMessageContentHelper';
|
import { generateBotMessageContent } from './helpers/botMessageContentHelper';
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||||
|
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||||
|
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -188,6 +191,10 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
inboxSupportsReplyTo: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -269,6 +276,7 @@ export default {
|
|||||||
copy: this.hasText,
|
copy: this.hasText,
|
||||||
delete: this.hasText || this.hasAttachments,
|
delete: this.hasText || this.hasAttachments,
|
||||||
cannedResponse: this.isOutgoing && this.hasText,
|
cannedResponse: this.isOutgoing && this.hasText,
|
||||||
|
replyTo: !this.data.private && this.inboxSupportsReplyTo,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
contentAttributes() {
|
contentAttributes() {
|
||||||
@@ -494,6 +502,13 @@ export default {
|
|||||||
this.showContextMenu = false;
|
this.showContextMenu = false;
|
||||||
this.contextMenuPosition = { x: null, y: null };
|
this.contextMenuPosition = { x: null, y: null };
|
||||||
},
|
},
|
||||||
|
handleReplyTo() {
|
||||||
|
const replyStorageKey = LOCAL_STORAGE_KEYS.MESSAGE_REPLY_TO;
|
||||||
|
const { conversation_id: conversationId, id: replyTo } = this.data;
|
||||||
|
|
||||||
|
LocalStorage.updateJsonStore(replyStorageKey, conversationId, replyTo);
|
||||||
|
bus.$emit(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE, this.data);
|
||||||
|
},
|
||||||
setupHighlightTimer() {
|
setupHighlightTimer() {
|
||||||
if (Number(this.$route.query.messageId) !== Number(this.data.id)) {
|
if (Number(this.$route.query.messageId) !== Number(this.data.id)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
:is-a-whatsapp-channel="isAWhatsAppChannel"
|
:is-a-whatsapp-channel="isAWhatsAppChannel"
|
||||||
:has-instagram-story="hasInstagramStory"
|
:has-instagram-story="hasInstagramStory"
|
||||||
:is-web-widget-inbox="isAWebWidgetInbox"
|
:is-web-widget-inbox="isAWebWidgetInbox"
|
||||||
|
:inbox-supports-reply-to="inboxSupportsReplyTo"
|
||||||
/>
|
/>
|
||||||
<li v-show="unreadMessageCount != 0" class="unread--toast">
|
<li v-show="unreadMessageCount != 0" class="unread--toast">
|
||||||
<span>
|
<span>
|
||||||
@@ -110,7 +111,7 @@ import { mapGetters } from 'vuex';
|
|||||||
import conversationMixin, {
|
import conversationMixin, {
|
||||||
filterDuplicateSourceMessages,
|
filterDuplicateSourceMessages,
|
||||||
} from '../../../mixins/conversations';
|
} from '../../../mixins/conversations';
|
||||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
import configMixin from 'shared/mixins/configMixin';
|
||||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||||
import aiMixin from 'dashboard/mixins/aiMixin';
|
import aiMixin from 'dashboard/mixins/aiMixin';
|
||||||
@@ -123,6 +124,7 @@ import { LocalStorage } from 'shared/helpers/localStorage';
|
|||||||
|
|
||||||
// constants
|
// constants
|
||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
|
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||||
import { REPLY_POLICY } from 'shared/constants/links';
|
import { REPLY_POLICY } from 'shared/constants/links';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||||
@@ -164,12 +166,14 @@ export default {
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
accountId: 'getCurrentAccountId',
|
||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
allConversations: 'getAllConversations',
|
allConversations: 'getAllConversations',
|
||||||
inboxesList: 'inboxes/getInboxes',
|
inboxesList: 'inboxes/getInboxes',
|
||||||
listLoadingStatus: 'getAllMessagesLoaded',
|
listLoadingStatus: 'getAllMessagesLoaded',
|
||||||
loadingChatList: 'getChatListLoadingStatus',
|
loadingChatList: 'getChatListLoadingStatus',
|
||||||
appIntegrations: 'integrations/getAppIntegrations',
|
appIntegrations: 'integrations/getAppIntegrations',
|
||||||
|
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
|
||||||
currentAccountId: 'getCurrentAccountId',
|
currentAccountId: 'getCurrentAccountId',
|
||||||
}),
|
}),
|
||||||
isOpen() {
|
isOpen() {
|
||||||
@@ -316,6 +320,15 @@ export default {
|
|||||||
unreadMessageCount() {
|
unreadMessageCount() {
|
||||||
return this.currentChat.unread_count || 0;
|
return this.currentChat.unread_count || 0;
|
||||||
},
|
},
|
||||||
|
inboxSupportsReplyTo() {
|
||||||
|
return (
|
||||||
|
this.inboxHasFeature(INBOX_FEATURES.REPLY_TO) &&
|
||||||
|
this.isFeatureEnabledonAccount(
|
||||||
|
this.accountId,
|
||||||
|
FEATURE_FLAGS.MESSAGE_REPLY_TO
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ export const LOCAL_STORAGE_KEYS = {
|
|||||||
DRAFT_MESSAGES: 'draftMessages',
|
DRAFT_MESSAGES: 'draftMessages',
|
||||||
COLOR_SCHEME: 'color_scheme',
|
COLOR_SCHEME: 'color_scheme',
|
||||||
DISMISSED_LABEL_SUGGESTIONS: 'labelSuggestionsDismissed',
|
DISMISSED_LABEL_SUGGESTIONS: 'labelSuggestionsDismissed',
|
||||||
|
MESSAGE_REPLY_TO: 'messageReplyTo',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,4 +16,5 @@ export const FEATURE_FLAGS = {
|
|||||||
TEAM_MANAGEMENT: 'team_management',
|
TEAM_MANAGEMENT: 'team_management',
|
||||||
VOICE_RECORDER: 'voice_recorder',
|
VOICE_RECORDER: 'voice_recorder',
|
||||||
AUDIT_LOGS: 'audit_logs',
|
AUDIT_LOGS: 'audit_logs',
|
||||||
|
MESSAGE_REPLY_TO: 'message_reply_to',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -194,6 +194,7 @@
|
|||||||
},
|
},
|
||||||
"CONTEXT_MENU": {
|
"CONTEXT_MENU": {
|
||||||
"COPY": "Copy",
|
"COPY": "Copy",
|
||||||
|
"REPLY_TO": "Reply to this message",
|
||||||
"DELETE": "Delete",
|
"DELETE": "Delete",
|
||||||
"CREATE_A_CANNED_RESPONSE": "Add to canned responses",
|
"CREATE_A_CANNED_RESPONSE": "Add to canned responses",
|
||||||
"TRANSLATE": "Translate",
|
"TRANSLATE": "Translate",
|
||||||
|
|||||||
@@ -44,6 +44,15 @@
|
|||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
>
|
>
|
||||||
<div class="menu-container">
|
<div class="menu-container">
|
||||||
|
<menu-item
|
||||||
|
v-if="enabledOptions['replyTo']"
|
||||||
|
:option="{
|
||||||
|
icon: 'arrow-reply',
|
||||||
|
label: $t('CONVERSATION.CONTEXT_MENU.REPLY_TO'),
|
||||||
|
}"
|
||||||
|
variant="icon"
|
||||||
|
@click="handleReplyTo"
|
||||||
|
/>
|
||||||
<menu-item
|
<menu-item
|
||||||
v-if="enabledOptions['copy']"
|
v-if="enabledOptions['copy']"
|
||||||
:option="{
|
:option="{
|
||||||
@@ -204,10 +213,13 @@ export default {
|
|||||||
this.handleClose();
|
this.handleClose();
|
||||||
this.showTranslateModal = true;
|
this.showTranslateModal = true;
|
||||||
},
|
},
|
||||||
|
handleReplyTo() {
|
||||||
|
this.$emit('replyTo', this.message);
|
||||||
|
this.handleClose();
|
||||||
|
},
|
||||||
onCloseTranslateModal() {
|
onCloseTranslateModal() {
|
||||||
this.showTranslateModal = false;
|
this.showTranslateModal = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
openDeleteModal() {
|
openDeleteModal() {
|
||||||
this.handleClose();
|
this.handleClose();
|
||||||
this.showDeleteModal = true;
|
this.showDeleteModal = true;
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ export const BUS_EVENTS = {
|
|||||||
TOGGLE_SIDEMENU: 'TOGGLE_SIDEMENU',
|
TOGGLE_SIDEMENU: 'TOGGLE_SIDEMENU',
|
||||||
ON_MESSAGE_LIST_SCROLL: 'ON_MESSAGE_LIST_SCROLL',
|
ON_MESSAGE_LIST_SCROLL: 'ON_MESSAGE_LIST_SCROLL',
|
||||||
WEBSOCKET_DISCONNECT: 'WEBSOCKET_DISCONNECT',
|
WEBSOCKET_DISCONNECT: 'WEBSOCKET_DISCONNECT',
|
||||||
|
TOGGLE_REPLY_TO_MESSAGE: 'TOGGLE_REPLY_TO_MESSAGE',
|
||||||
SHOW_TOAST: 'newToastMessage',
|
SHOW_TOAST: 'newToastMessage',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const LocalStorage = {
|
|||||||
} else {
|
} else {
|
||||||
window.localStorage.setItem(key, value);
|
window.localStorage.setItem(key, value);
|
||||||
}
|
}
|
||||||
window.localStorage.setItem(key + ':ts', Date.now());
|
window.localStorage.setItem(key + ':ts', Date.now().toString());
|
||||||
},
|
},
|
||||||
|
|
||||||
setFlag(store, accountId, key, expiry = 24 * 60 * 60 * 1000) {
|
setFlag(store, accountId, key, expiry = 24 * 60 * 60 * 1000) {
|
||||||
@@ -46,4 +46,39 @@ export const LocalStorage = {
|
|||||||
window.localStorage.removeItem(key);
|
window.localStorage.removeItem(key);
|
||||||
window.localStorage.removeItem(key + ':ts');
|
window.localStorage.removeItem(key + ':ts');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateJsonStore(storeName, key, value) {
|
||||||
|
try {
|
||||||
|
const storedValue = this.get(storeName) || {};
|
||||||
|
storedValue[key] = value;
|
||||||
|
this.set(storeName, storedValue);
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Error updating JSON store in localStorage', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getFromJsonStore(storeName, key) {
|
||||||
|
try {
|
||||||
|
const storedValue = this.get(storeName) || {};
|
||||||
|
return storedValue[key] || null;
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Error getting value from JSON store in localStorage', e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteFromJsonStore(storeName, key) {
|
||||||
|
try {
|
||||||
|
const storedValue = this.get(storeName);
|
||||||
|
if (storedValue && key in storedValue) {
|
||||||
|
delete storedValue[key];
|
||||||
|
this.set(storeName, storedValue);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Error deleting entry from JSON store in localStorage', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { LocalStorage } from '../localStorage';
|
||||||
|
|
||||||
|
// Mocking localStorage
|
||||||
|
const localStorageMock = (() => {
|
||||||
|
let store = {};
|
||||||
|
return {
|
||||||
|
getItem: key => store[key] || null,
|
||||||
|
setItem: (key, value) => {
|
||||||
|
store[key] = String(value);
|
||||||
|
},
|
||||||
|
removeItem: key => delete store[key],
|
||||||
|
clear: () => {
|
||||||
|
store = {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
Object.defineProperty(window, 'localStorage', {
|
||||||
|
value: localStorageMock,
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LocalStorage utility', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
localStorage.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set and get methods', () => {
|
||||||
|
LocalStorage.set('testKey', { a: 1 });
|
||||||
|
expect(LocalStorage.get('testKey')).toEqual({ a: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('remove method', () => {
|
||||||
|
LocalStorage.set('testKey', 'testValue');
|
||||||
|
LocalStorage.remove('testKey');
|
||||||
|
expect(LocalStorage.get('testKey')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updateJsonStore method', () => {
|
||||||
|
LocalStorage.updateJsonStore('testStore', 'testKey', 'testValue');
|
||||||
|
expect(LocalStorage.get('testStore')).toEqual({ testKey: 'testValue' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getFromJsonStore method', () => {
|
||||||
|
LocalStorage.set('testStore', { testKey: 'testValue' });
|
||||||
|
expect(LocalStorage.getFromJsonStore('testStore', 'testKey')).toBe(
|
||||||
|
'testValue'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deleteFromJsonStore method', () => {
|
||||||
|
LocalStorage.set('testStore', { testKey: 'testValue' });
|
||||||
|
LocalStorage.deleteFromJsonStore('testStore', 'testKey');
|
||||||
|
expect(LocalStorage.getFromJsonStore('testStore', 'testKey')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setFlag and getFlag methods', () => {
|
||||||
|
const store = 'testStore';
|
||||||
|
const accountId = '123';
|
||||||
|
const key = 'flagKey';
|
||||||
|
const expiry = 1000; // 1 second
|
||||||
|
|
||||||
|
// Set flag and verify it's set
|
||||||
|
LocalStorage.setFlag(store, accountId, key, expiry);
|
||||||
|
expect(LocalStorage.getFlag(store, accountId, key)).toBe(true);
|
||||||
|
|
||||||
|
// Wait for expiry and verify flag is not set
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(LocalStorage.getFlag(store, accountId, key)).toBe(false);
|
||||||
|
resolve();
|
||||||
|
}, expiry + 100); // wait a bit more than expiry time to ensure the flag has expired
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clearAll method', () => {
|
||||||
|
LocalStorage.set('testKey1', 'testValue1');
|
||||||
|
LocalStorage.set('testKey2', 'testValue2');
|
||||||
|
LocalStorage.clearAll();
|
||||||
|
expect(LocalStorage.get('testKey1')).toBeNull();
|
||||||
|
expect(LocalStorage.get('testKey2')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set method with non-object value', () => {
|
||||||
|
LocalStorage.set('testKey', 'testValue');
|
||||||
|
expect(LocalStorage.get('testKey')).toBe('testValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set and get methods with null value', () => {
|
||||||
|
LocalStorage.set('testKey', null);
|
||||||
|
expect(LocalStorage.get('testKey')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set and get methods with undefined value', () => {
|
||||||
|
LocalStorage.set('testKey', undefined);
|
||||||
|
expect(LocalStorage.get('testKey')).toBe('undefined');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set and get methods with boolean value', () => {
|
||||||
|
LocalStorage.set('testKey', true);
|
||||||
|
expect(LocalStorage.get('testKey')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('set and get methods with number value', () => {
|
||||||
|
LocalStorage.set('testKey', 42);
|
||||||
|
expect(LocalStorage.get('testKey')).toBe(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -11,6 +11,24 @@ export const INBOX_TYPES = {
|
|||||||
SMS: 'Channel::Sms',
|
SMS: 'Channel::Sms',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const INBOX_FEATURES = {
|
||||||
|
REPLY_TO: 'replyTo',
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is a single source of truth for inbox features
|
||||||
|
// This is used to check if a feature is available for a particular inbox or not
|
||||||
|
export const INBOX_FEATURE_MAP = {
|
||||||
|
[INBOX_FEATURES.REPLY_TO]: [
|
||||||
|
INBOX_TYPES.WEB,
|
||||||
|
INBOX_TYPES.FB,
|
||||||
|
INBOX_TYPES.TWITTER,
|
||||||
|
INBOX_TYPES.WHATSAPP,
|
||||||
|
INBOX_TYPES.LINE,
|
||||||
|
INBOX_TYPES.TELEGRAM,
|
||||||
|
INBOX_TYPES.API,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
channelType() {
|
channelType() {
|
||||||
@@ -102,4 +120,9 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
inboxHasFeature(feature) {
|
||||||
|
return INBOX_FEATURE_MAP[feature]?.includes(this.channelType) ?? false;
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,218 +1,286 @@
|
|||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import inboxMixin from '../inboxMixin';
|
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', () => {
|
describe('inboxMixin', () => {
|
||||||
it('returns the correct channel type', () => {
|
it('returns the correct channel type', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::WebWidget');
|
||||||
render() {},
|
|
||||||
mixins: [inboxMixin],
|
|
||||||
data() {
|
|
||||||
return { inbox: { channel_type: 'Channel::WebWidget' } };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.channelType).toBe('Channel::WebWidget');
|
expect(wrapper.vm.channelType).toBe('Channel::WebWidget');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('isAPIInbox returns true if channel type is API', () => {
|
it('isAPIInbox returns true if channel type is API', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::Api');
|
||||||
render() {},
|
|
||||||
mixins: [inboxMixin],
|
|
||||||
data() {
|
|
||||||
return { inbox: { channel_type: 'Channel::Api' } };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isAPIInbox).toBe(true);
|
expect(wrapper.vm.isAPIInbox).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('isATwitterInbox returns true if channel type is twitter', () => {
|
it('isATwitterInbox returns true if channel type is twitter', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::TwitterProfile');
|
||||||
render() {},
|
|
||||||
mixins: [inboxMixin],
|
|
||||||
data() {
|
|
||||||
return { inbox: { channel_type: 'Channel::TwitterProfile' } };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isATwitterInbox).toBe(true);
|
expect(wrapper.vm.isATwitterInbox).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('isAFacebookInbox returns true if channel type is Facebook', () => {
|
it('isAFacebookInbox returns true if channel type is Facebook', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::FacebookPage');
|
||||||
render() {},
|
|
||||||
mixins: [inboxMixin],
|
|
||||||
data() {
|
|
||||||
return { inbox: { channel_type: 'Channel::FacebookPage' } };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isAFacebookInbox).toBe(true);
|
expect(wrapper.vm.isAFacebookInbox).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('isAWebWidgetInbox returns true if channel type is Facebook', () => {
|
it('isAWebWidgetInbox returns true if channel type is Facebook', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::WebWidget');
|
||||||
render() {},
|
|
||||||
mixins: [inboxMixin],
|
|
||||||
data() {
|
|
||||||
return { inbox: { channel_type: 'Channel::WebWidget' } };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isAWebWidgetInbox).toBe(true);
|
expect(wrapper.vm.isAWebWidgetInbox).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('isASmsInbox returns true if channel type is sms', () => {
|
it('isASmsInbox returns true if channel type is sms', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::Sms');
|
||||||
render() {},
|
|
||||||
mixins: [inboxMixin],
|
|
||||||
data() {
|
|
||||||
return { inbox: { channel_type: 'Channel::Sms' } };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isASmsInbox).toBe(true);
|
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', () => {
|
it('isATwilioChannel returns true if channel type is Twilio', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::TwilioSms');
|
||||||
render() {},
|
|
||||||
mixins: [inboxMixin],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
inbox: {
|
|
||||||
channel_type: 'Channel::TwilioSms',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isATwilioChannel).toBe(true);
|
expect(wrapper.vm.isATwilioChannel).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('isATwilioSMSChannel returns true if channel type is Twilio and medium is SMS', () => {
|
describe('isATwilioSMSChannel', () => {
|
||||||
const Component = {
|
it('returns true if channel type is Twilio and medium is SMS', () => {
|
||||||
render() {},
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||||
mixins: [inboxMixin],
|
medium: 'sms',
|
||||||
data() {
|
});
|
||||||
return {
|
const wrapper = shallowMount(Component);
|
||||||
inbox: {
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
||||||
channel_type: 'Channel::TwilioSms',
|
});
|
||||||
medium: 'sms',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
|
||||||
expect(wrapper.vm.isATwilioChannel).toBe(true);
|
|
||||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
|
||||||
expect(wrapper.vm.isASmsInbox).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('isATwilioWhatsAppChannel returns true if channel type is Twilio and medium is WhatsApp', () => {
|
it('returns false if channel type is Twilio but medium is not SMS', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||||
render() {},
|
medium: 'whatsapp',
|
||||||
mixins: [inboxMixin],
|
});
|
||||||
data() {
|
const wrapper = shallowMount(Component);
|
||||||
return {
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
||||||
inbox: {
|
});
|
||||||
channel_type: 'Channel::TwilioSms',
|
|
||||||
medium: 'whatsapp',
|
it('returns false if channel type is not Twilio but medium is SMS', () => {
|
||||||
},
|
const Component = getComponentConfigForInbox('Channel::NotTwilio', {
|
||||||
};
|
medium: 'sms',
|
||||||
},
|
});
|
||||||
};
|
const wrapper = shallowMount(Component);
|
||||||
const wrapper = shallowMount(Component);
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(false);
|
||||||
expect(wrapper.vm.isATwilioChannel).toBe(true);
|
});
|
||||||
expect(wrapper.vm.isATwilioWhatsAppChannel).toBe(true);
|
|
||||||
|
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', () => {
|
it('isAnEmailChannel returns true if channel type is email', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::Email');
|
||||||
render() {},
|
|
||||||
mixins: [inboxMixin],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
inbox: { channel_type: 'Channel::Email' },
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isAnEmailChannel).toBe(true);
|
expect(wrapper.vm.isAnEmailChannel).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('isTwitterInboxTweet returns true if Twitter channel type is tweet', () => {
|
it('isTwitterInboxTweet returns true if Twitter channel type is tweet', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForChat({
|
||||||
render() {},
|
channel_type: 'Channel::TwitterProfile',
|
||||||
mixins: [inboxMixin],
|
additional_attributes: {
|
||||||
data() {
|
type: 'tweet',
|
||||||
return {
|
|
||||||
chat: {
|
|
||||||
channel_type: 'Channel::TwitterProfile',
|
|
||||||
additional_attributes: {
|
|
||||||
type: 'tweet',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isTwitterInboxTweet).toBe(true);
|
expect(wrapper.vm.isTwitterInboxTweet).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('twilioBadge returns string sms if channel type is Twilio and medium is sms', () => {
|
it('twilioBadge returns string sms if channel type is Twilio and medium is sms', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForInbox('Channel::TwilioSms', {
|
||||||
render() {},
|
medium: 'sms',
|
||||||
mixins: [inboxMixin],
|
});
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
inbox: {
|
|
||||||
channel_type: 'Channel::TwilioSms',
|
|
||||||
medium: 'sms',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
|
||||||
expect(wrapper.vm.twilioBadge).toBe('sms');
|
expect(wrapper.vm.twilioBadge).toBe('sms');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('twitterBadge returns string twitter-tweet if Twitter channel type is tweet', () => {
|
it('twitterBadge returns string twitter-tweet if Twitter channel type is tweet', () => {
|
||||||
const Component = {
|
const Component = getComponentConfigForChat({
|
||||||
render() {},
|
id: 1,
|
||||||
mixins: [inboxMixin],
|
additional_attributes: {
|
||||||
data() {
|
type: 'tweet',
|
||||||
return {
|
|
||||||
chat: {
|
|
||||||
id: 1,
|
|
||||||
additional_attributes: {
|
|
||||||
type: 'tweet',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|
||||||
const wrapper = shallowMount(Component);
|
const wrapper = shallowMount(Component);
|
||||||
expect(wrapper.vm.isTwitterInboxTweet).toBe(true);
|
expect(wrapper.vm.isTwitterInboxTweet).toBe(true);
|
||||||
expect(wrapper.vm.twitterBadge).toBe('twitter-tweet');
|
expect(wrapper.vm.twitterBadge).toBe('twitter-tweet');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inboxBadge returns string Channel::Telegram if isATwilioChannel and isATwitterInbox is false', () => {
|
describe('Badges', () => {
|
||||||
const Component = {
|
it('inboxBadge returns string Channel::Telegram if isATwilioChannel and isATwitterInbox is false', () => {
|
||||||
render() {},
|
const Component = getComponentConfigForInbox('Channel::Telegram');
|
||||||
mixins: [inboxMixin],
|
const wrapper = shallowMount(Component);
|
||||||
data() {
|
expect(wrapper.vm.isATwilioChannel).toBe(false);
|
||||||
return {
|
expect(wrapper.vm.isATwitterInbox).toBe(false);
|
||||||
inbox: {
|
expect(wrapper.vm.channelType).toBe('Channel::Telegram');
|
||||||
channel_type: 'Channel::Telegram',
|
});
|
||||||
},
|
|
||||||
};
|
it('inboxBadge returns correct badge for WhatsApp channel', () => {
|
||||||
},
|
const Component = getComponentConfigForInbox('Channel::Whatsapp');
|
||||||
};
|
const wrapper = shallowMount(Component);
|
||||||
const wrapper = shallowMount(Component);
|
expect(wrapper.vm.inboxBadge).toBe('whatsapp');
|
||||||
expect(wrapper.vm.isATwilioChannel).toBe(false);
|
});
|
||||||
expect(wrapper.vm.isATwitterInbox).toBe(false);
|
|
||||||
expect(wrapper.vm.channelType).toBe('Channel::Telegram');
|
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('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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,3 +57,5 @@
|
|||||||
enabled: false
|
enabled: false
|
||||||
- name: response_bot
|
- name: response_bot
|
||||||
enabled: false
|
enabled: false
|
||||||
|
- name: message_reply_to
|
||||||
|
enabled: false
|
||||||
|
|||||||
Reference in New Issue
Block a user