[Enhancement] Fetch previous messages in the conversation (#355)
* Fetch previous messages in the conversation * Add specs for conversation store * Fix codeclimate issues * Exclude specs folder * Exclude globally * Fix path in exclude patterns * Add endPoints spec * Add snapshots for Spinner * Add specs for actions
This commit is contained in:
parent
1005b9e227
commit
2b41e91768
17 changed files with 406 additions and 84 deletions
|
@ -11,3 +11,16 @@ plugins:
|
||||||
enabled: true
|
enabled: true
|
||||||
brakeman:
|
brakeman:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
exclude_patterns:
|
||||||
|
- "spec/"
|
||||||
|
- "**/specs/"
|
||||||
|
- "db/*"
|
||||||
|
- "bin/**/*"
|
||||||
|
- "db/**/*"
|
||||||
|
- "config/**/*"
|
||||||
|
- "public/**/*"
|
||||||
|
- "vendor/**/*"
|
||||||
|
- "node_modules/**/*"
|
||||||
|
- "lib/tasks/auto_annotate_models.rake"
|
||||||
|
- "app/test-matchers.js"
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Api::V1::Widget::MessagesController < ActionController::Base
|
||||||
account_id: conversation.account_id,
|
account_id: conversation.account_id,
|
||||||
inbox_id: conversation.inbox_id,
|
inbox_id: conversation.inbox_id,
|
||||||
message_type: :incoming,
|
message_type: :incoming,
|
||||||
content: permitted_params[:content]
|
content: permitted_params[:message][:content]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -65,7 +65,8 @@ class Api::V1::Widget::MessagesController < ActionController::Base
|
||||||
|
|
||||||
def message_finder_params
|
def message_finder_params
|
||||||
{
|
{
|
||||||
filter_internal_messages: true
|
filter_internal_messages: true,
|
||||||
|
before: permitted_params[:before]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ class Api::V1::Widget::MessagesController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params
|
||||||
params.fetch(:message).permit(:content)
|
params.permit(:before, message: [:content])
|
||||||
end
|
end
|
||||||
|
|
||||||
def secret_key
|
def secret_key
|
||||||
|
|
|
@ -149,7 +149,9 @@
|
||||||
|
|
||||||
@mixin color-spinner() {
|
@mixin color-spinner() {
|
||||||
@keyframes spinner {
|
@keyframes spinner {
|
||||||
to {transform: rotate(360deg);}
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
|
|
67
app/javascript/shared/components/Spinner.vue
Normal file
67
app/javascript/shared/components/Spinner.vue
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<span class="spinner small"></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '~widget/assets/scss/variables';
|
||||||
|
|
||||||
|
@mixin color-spinner() {
|
||||||
|
@keyframes spinner {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: $space-medium;
|
||||||
|
height: $space-medium;
|
||||||
|
margin-top: -$space-one;
|
||||||
|
margin-left: -$space-one;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.7);
|
||||||
|
border-top-color: lighten($color-woot, 10%);
|
||||||
|
animation: spinner 0.9s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
@include color-spinner();
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: $space-medium;
|
||||||
|
height: $space-medium;
|
||||||
|
padding: $zero $space-medium;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
&.message {
|
||||||
|
padding: $space-normal;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: $space-slab;
|
||||||
|
background: $color-white;
|
||||||
|
border-radius: $space-large;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
margin-top: -$space-slab;
|
||||||
|
margin-left: -$space-slab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
width: $space-normal;
|
||||||
|
height: $space-normal;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
width: $space-normal;
|
||||||
|
height: $space-normal;
|
||||||
|
margin-top: -$space-small;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
10
app/javascript/shared/components/specs/Spinner.spec.js
Normal file
10
app/javascript/shared/components/specs/Spinner.spec.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import Spinner from '../Spinner';
|
||||||
|
|
||||||
|
describe('Spinner', () => {
|
||||||
|
test('matches snapshot', () => {
|
||||||
|
const wrapper = mount(Spinner);
|
||||||
|
expect(wrapper.isVueInstance()).toBeTruthy();
|
||||||
|
expect(wrapper.element).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Spinner matches snapshot 1`] = `
|
||||||
|
<span
|
||||||
|
class="spinner small"
|
||||||
|
/>
|
||||||
|
`;
|
|
@ -7,9 +7,9 @@ const sendMessageAPI = async content => {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getConversationAPI = async conversationId => {
|
const getConversationAPI = async ({ before }) => {
|
||||||
const urlData = endPoints.getConversation(conversationId);
|
const urlData = endPoints.getConversation({ before });
|
||||||
const result = await API.get(urlData.url);
|
const result = await API.get(urlData.url, { params: urlData.params });
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,9 @@ const sendMessage = content => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const getConversation = () => ({
|
const getConversation = ({ before }) => ({
|
||||||
url: `/api/v1/widget/messages${window.location.search}`,
|
url: `/api/v1/widget/messages${window.location.search}`,
|
||||||
|
params: { before },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
25
app/javascript/widget/api/specs/endPoints.spec.js
Normal file
25
app/javascript/widget/api/specs/endPoints.spec.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import endPoints from '../endPoints';
|
||||||
|
|
||||||
|
describe('#sendMessage', () => {
|
||||||
|
it('returns correct payload', () => {
|
||||||
|
expect(endPoints.sendMessage('hello')).toEqual({
|
||||||
|
url: `/api/v1/widget/messages`,
|
||||||
|
params: {
|
||||||
|
message: {
|
||||||
|
content: 'hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getConversation', () => {
|
||||||
|
it('returns correct payload', () => {
|
||||||
|
expect(endPoints.getConversation({ before: 123 })).toEqual({
|
||||||
|
url: `/api/v1/widget/messages`,
|
||||||
|
params: {
|
||||||
|
before: 123,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -13,7 +13,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Spinner from 'widget/components/Spinner.vue';
|
import Spinner from 'shared/components/Spinner.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="conversation--container">
|
<div class="conversation--container">
|
||||||
<div class="conversation-wrap">
|
<div class="conversation-wrap">
|
||||||
|
<div v-if="isFetchingList" class="message--loader">
|
||||||
|
<spinner></spinner>
|
||||||
|
</div>
|
||||||
<ChatMessage
|
<ChatMessage
|
||||||
v-for="message in messages"
|
v-for="message in messages"
|
||||||
:key="message.id"
|
:key="message.id"
|
||||||
|
@ -14,29 +17,71 @@
|
||||||
<script>
|
<script>
|
||||||
import Branding from 'widget/components/Branding.vue';
|
import Branding from 'widget/components/Branding.vue';
|
||||||
import ChatMessage from 'widget/components/ChatMessage.vue';
|
import ChatMessage from 'widget/components/ChatMessage.vue';
|
||||||
|
import Spinner from 'shared/components/Spinner.vue';
|
||||||
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ConversationWrap',
|
name: 'ConversationWrap',
|
||||||
components: {
|
components: {
|
||||||
Branding,
|
Branding,
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
|
Spinner,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
messages: Object,
|
messages: Object,
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
previousScrollHeight: 0,
|
||||||
|
previousConversationSize: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
earliestMessage: 'conversation/getEarliestMessage',
|
||||||
|
allMessagesLoaded: 'conversation/getAllMessagesLoaded',
|
||||||
|
isFetchingList: 'conversation/getIsFetchingList',
|
||||||
|
conversationSize: 'conversation/getConversationSize',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
allMessagesLoaded() {
|
||||||
|
this.previousScrollHeight = 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.$el.addEventListener('scroll', this.handleScroll);
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
this.scrollToBottom();
|
if (this.previousConversationSize !== this.conversationSize) {
|
||||||
|
this.previousConversationSize = this.conversationSize;
|
||||||
|
this.scrollToBottom();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
this.$el.removeEventListener('scroll', this.handleScroll);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
...mapActions('conversation', ['fetchOldConversations']),
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
const container = this.$el;
|
const container = this.$el;
|
||||||
container.scrollTop =
|
container.scrollTop = container.scrollHeight - this.previousScrollHeight;
|
||||||
container.scrollHeight < this.minScrollHeight
|
this.previousScrollHeight = 0;
|
||||||
? this.minScrollHeight
|
},
|
||||||
: container.scrollHeight;
|
handleScroll() {
|
||||||
|
if (
|
||||||
|
this.isFetchingList ||
|
||||||
|
this.allMessagesLoaded ||
|
||||||
|
!this.conversationSize
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$el.scrollTop < 100) {
|
||||||
|
this.fetchOldConversations({ before: this.earliestMessage.id });
|
||||||
|
this.previousScrollHeight = this.$el.scrollHeight;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -56,4 +101,8 @@ export default {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: $space-large $space-small $zero $space-small;
|
padding: $space-large $space-small $zero $space-small;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message--loader {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
<template>
|
|
||||||
<span class="spinner" :class="size"></span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const SIZES = ['small', 'medium', 'large'];
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
size: {
|
|
||||||
validator: value => SIZES.includes(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped lang="scss">
|
|
||||||
@import '~widget/assets/scss/variables.scss';
|
|
||||||
|
|
||||||
.spinner {
|
|
||||||
@keyframes spinner {
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
animation: spinner 0.7s linear infinite;
|
|
||||||
border-radius: 50%;
|
|
||||||
border-top-color: lighten($color-woot, 10%);
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.7);
|
|
||||||
box-sizing: border-box;
|
|
||||||
content: '';
|
|
||||||
height: $space-medium;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -$space-slab;
|
|
||||||
margin-top: -$space-slab;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
width: $space-medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.small:before {
|
|
||||||
border-width: 1px;
|
|
||||||
height: $space-slab;
|
|
||||||
margin-left: -$space-slab/2;
|
|
||||||
margin-top: -$space-slab/2;
|
|
||||||
width: $space-slab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -21,42 +21,54 @@ export const findUndeliveredMessage = (messageInbox, { content }) =>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const DEFAULT_CONVERSATION = 'default';
|
export const DEFAULT_CONVERSATION = 'default';
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
conversations: {},
|
conversations: {},
|
||||||
|
uiFlags: {
|
||||||
|
allMessagesLoaded: false,
|
||||||
|
isFetchingList: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const getters = {
|
export const getters = {
|
||||||
getConversation: _state => _state.conversations,
|
getConversation: _state => _state.conversations,
|
||||||
getConversationSize: _state => Object.keys(_state.conversations).length,
|
getConversationSize: _state => Object.keys(_state.conversations).length,
|
||||||
|
getAllMessagesLoaded: _state => _state.uiFlags.allMessagesLoaded,
|
||||||
|
getIsFetchingList: _state => _state.uiFlags.isFetchingList,
|
||||||
|
getEarliestMessage: _state => {
|
||||||
|
const conversation = Object.values(_state.conversations);
|
||||||
|
if (conversation.length) {
|
||||||
|
return conversation[0];
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const actions = {
|
export const actions = {
|
||||||
sendMessage: async ({ commit }, params) => {
|
sendMessage: async ({ commit }, params) => {
|
||||||
const { content } = params;
|
const { content } = params;
|
||||||
commit('pushMessageToConversations', createTemporaryMessage(content));
|
commit('pushMessageToConversation', createTemporaryMessage(content));
|
||||||
await sendMessageAPI(content);
|
await sendMessageAPI(content);
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchOldConversations: async ({ commit }) => {
|
fetchOldConversations: async ({ commit }, { before } = {}) => {
|
||||||
try {
|
try {
|
||||||
const { data } = await getConversationAPI();
|
commit('setConversationListLoading', true);
|
||||||
commit('initMessagesInConversation', data);
|
const { data } = await getConversationAPI({ before });
|
||||||
|
commit('setMessagesInConversation', data);
|
||||||
|
commit('setConversationListLoading', false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Handle error
|
commit('setConversationListLoading', false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addMessage({ commit }, data) {
|
addMessage({ commit }, data) {
|
||||||
commit('pushMessageToConversations', data);
|
commit('pushMessageToConversation', data);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const mutations = {
|
export const mutations = {
|
||||||
initInboxInConversations($state, lastConversation) {
|
pushMessageToConversation($state, message) {
|
||||||
Vue.set($state.conversations, lastConversation, {});
|
|
||||||
},
|
|
||||||
|
|
||||||
pushMessageToConversations($state, message) {
|
|
||||||
const { id, status, message_type: type } = message;
|
const { id, status, message_type: type } = message;
|
||||||
const messagesInbox = $state.conversations;
|
const messagesInbox = $state.conversations;
|
||||||
const isMessageIncoming = type === MESSAGE_TYPE.INCOMING;
|
const isMessageIncoming = type === MESSAGE_TYPE.INCOMING;
|
||||||
|
@ -71,7 +83,6 @@ const mutations = {
|
||||||
messagesInbox,
|
messagesInbox,
|
||||||
message
|
message
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!messageInConversation) {
|
if (!messageInConversation) {
|
||||||
Vue.set(messagesInbox, id, message);
|
Vue.set(messagesInbox, id, message);
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,12 +91,17 @@ const mutations = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initMessagesInConversation(_state, payload) {
|
setConversationListLoading($state, status) {
|
||||||
|
$state.uiFlags.isFetchingList = status;
|
||||||
|
},
|
||||||
|
|
||||||
|
setMessagesInConversation($state, payload) {
|
||||||
if (!payload.length) {
|
if (!payload.length) {
|
||||||
|
$state.uiFlags.allMessagesLoaded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.map(message => Vue.set(_state.conversations, message.id, message));
|
payload.map(message => Vue.set($state.conversations, message.id, message));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { actions } from '../../conversation';
|
||||||
|
import getUuid from '../../../../helpers/uuid';
|
||||||
|
|
||||||
|
jest.mock('../../../../helpers/uuid');
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
|
||||||
|
describe('#actions', () => {
|
||||||
|
describe('#addMessage', () => {
|
||||||
|
it('sends correct mutations', () => {
|
||||||
|
actions.addMessage({ commit }, { id: 1 });
|
||||||
|
expect(commit).toBeCalledWith('pushMessageToConversation', { id: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#sendMessage', () => {
|
||||||
|
it('sends correct mutations', () => {
|
||||||
|
const mockDate = new Date(1466424490000);
|
||||||
|
getUuid.mockImplementationOnce(() => '1111');
|
||||||
|
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
|
||||||
|
actions.sendMessage({ commit }, { content: 'hello' });
|
||||||
|
spy.mockRestore();
|
||||||
|
expect(commit).toBeCalledWith('pushMessageToConversation', {
|
||||||
|
id: '1111',
|
||||||
|
content: 'hello',
|
||||||
|
status: 'in_progress',
|
||||||
|
created_at: 1466424490000,
|
||||||
|
message_type: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { getters } from '../../conversation';
|
||||||
|
|
||||||
|
describe('#getters', () => {
|
||||||
|
it('getConversation', () => {
|
||||||
|
const state = {
|
||||||
|
conversations: {
|
||||||
|
1: {
|
||||||
|
content: 'hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(getters.getConversation(state)).toEqual({
|
||||||
|
1: {
|
||||||
|
content: 'hello',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getConversationSize', () => {
|
||||||
|
const state = {
|
||||||
|
conversations: {
|
||||||
|
1: {
|
||||||
|
content: 'hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(getters.getConversationSize(state)).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getEarliestMessage', () => {
|
||||||
|
const state = {
|
||||||
|
conversations: {
|
||||||
|
1: {
|
||||||
|
content: 'hello',
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
content: 'hello1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(getters.getEarliestMessage(state)).toEqual({
|
||||||
|
content: 'hello',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uiFlags', () => {
|
||||||
|
const state = {
|
||||||
|
uiFlags: {
|
||||||
|
allMessagesLoaded: false,
|
||||||
|
isFetchingList: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(getters.getAllMessagesLoaded(state)).toEqual(false);
|
||||||
|
expect(getters.getIsFetchingList(state)).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { mutations } from '../../conversation';
|
||||||
|
|
||||||
|
const temporaryMessagePayload = {
|
||||||
|
content: 'hello',
|
||||||
|
id: 1,
|
||||||
|
message_type: 0,
|
||||||
|
status: 'in_progress',
|
||||||
|
};
|
||||||
|
|
||||||
|
const incomingMessagePayload = {
|
||||||
|
content: 'hello',
|
||||||
|
id: 1,
|
||||||
|
message_type: 0,
|
||||||
|
status: 'sent',
|
||||||
|
};
|
||||||
|
|
||||||
|
const outgoingMessagePayload = {
|
||||||
|
content: 'hello',
|
||||||
|
id: 1,
|
||||||
|
message_type: 1,
|
||||||
|
status: 'sent',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('#mutations', () => {
|
||||||
|
describe('#pushMessageToConversation', () => {
|
||||||
|
it('add message to conversation if outgoing', () => {
|
||||||
|
const state = { conversations: {} };
|
||||||
|
mutations.pushMessageToConversation(state, outgoingMessagePayload);
|
||||||
|
expect(state.conversations).toEqual({
|
||||||
|
1: outgoingMessagePayload,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add message to conversation if message in undelivered', () => {
|
||||||
|
const state = { conversations: {} };
|
||||||
|
mutations.pushMessageToConversation(state, temporaryMessagePayload);
|
||||||
|
expect(state.conversations).toEqual({
|
||||||
|
1: temporaryMessagePayload,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaces temporary message in conversation with actual message', () => {
|
||||||
|
const state = {
|
||||||
|
conversations: {
|
||||||
|
rand_id_123: {
|
||||||
|
content: 'hello',
|
||||||
|
id: 'rand_id_123',
|
||||||
|
message_type: 0,
|
||||||
|
status: 'in_progress',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
mutations.pushMessageToConversation(state, incomingMessagePayload);
|
||||||
|
expect(state.conversations).toEqual({
|
||||||
|
1: incomingMessagePayload,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds message in conversation if it is a new message', () => {
|
||||||
|
const state = { conversations: {} };
|
||||||
|
mutations.pushMessageToConversation(state, incomingMessagePayload);
|
||||||
|
expect(state.conversations).toEqual({
|
||||||
|
1: incomingMessagePayload,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#setConversationListLoading', () => {
|
||||||
|
it('set status correctly', () => {
|
||||||
|
const state = { uiFlags: { isFetchingList: false } };
|
||||||
|
mutations.setConversationListLoading(state, true);
|
||||||
|
expect(state.uiFlags.isFetchingList).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#setMessagesInConversation', () => {
|
||||||
|
it('sets allMessagesLoaded flag if payload is empty', () => {
|
||||||
|
const state = { uiFlags: { allMessagesLoaded: false } };
|
||||||
|
mutations.setMessagesInConversation(state, []);
|
||||||
|
expect(state.uiFlags.allMessagesLoaded).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets messages if payload is not empty', () => {
|
||||||
|
const state = {
|
||||||
|
uiFlags: { allMessagesLoaded: false },
|
||||||
|
conversations: {},
|
||||||
|
};
|
||||||
|
mutations.setMessagesInConversation(state, [{ id: 1, content: 'hello' }]);
|
||||||
|
expect(state.conversations).toEqual({
|
||||||
|
1: { id: 1, content: 'hello' },
|
||||||
|
});
|
||||||
|
expect(state.uiFlags.allMessagesLoaded).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
findUndeliveredMessage,
|
findUndeliveredMessage,
|
||||||
createTemporaryMessage,
|
createTemporaryMessage,
|
||||||
} from '../conversation';
|
} from '../../conversation';
|
||||||
|
|
||||||
describe('#findUndeliveredMessage', () => {
|
describe('#findUndeliveredMessage', () => {
|
||||||
it('returns message objects if exist', () => {
|
it('returns message objects if exist', () => {
|
Loading…
Add table
Add a link
Reference in a new issue