Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVincent Petry <vincent@nextcloud.com>2021-05-10 19:25:37 +0300
committerVincent Petry <vincent@nextcloud.com>2021-05-11 15:44:43 +0300
commitcd5823a8b531665f259b603a30ef4fd9eafe8421 (patch)
treefd60213aeb6deb419fbf2aeceeec30a125ee91b7 /src
parent732a53bcc7d8dff0e068ed8b1ae23aeba8c547a4 (diff)
Add JS tests for MessagesList.vue
Signed-off-by: Vincent Petry <vincent@nextcloud.com>
Diffstat (limited to 'src')
-rw-r--r--src/components/MessagesList/MessagesList.spec.js474
-rw-r--r--src/components/MessagesList/MessagesList.vue7
-rw-r--r--src/constants.js2
-rw-r--r--src/test-setup.js2
4 files changed, 483 insertions, 2 deletions
diff --git a/src/components/MessagesList/MessagesList.spec.js b/src/components/MessagesList/MessagesList.spec.js
new file mode 100644
index 000000000..d9f0f2fa5
--- /dev/null
+++ b/src/components/MessagesList/MessagesList.spec.js
@@ -0,0 +1,474 @@
+import Vuex from 'vuex'
+import { createLocalVue, shallowMount } from '@vue/test-utils'
+import { cloneDeep } from 'lodash'
+import storeConfig from '../../store/storeConfig'
+import { ATTENDEE } from '../../constants'
+
+import MessagesList from './MessagesList'
+
+describe('MessagesList.vue', () => {
+ const TOKEN = 'XXTOKENXX'
+ let store
+ let localVue
+ let testStoreConfig
+ const messagesListMock = jest.fn()
+ const getVisualLastReadMessageIdMock = jest.fn()
+
+ beforeEach(() => {
+ localVue = createLocalVue()
+ localVue.use(Vuex)
+
+ testStoreConfig = cloneDeep(storeConfig)
+ testStoreConfig.modules.messagesStore.getters.messagesList
+ = jest.fn().mockReturnValue(messagesListMock)
+ testStoreConfig.modules.messagesStore.getters.getVisualLastReadMessageId
+ = jest.fn().mockReturnValue(getVisualLastReadMessageIdMock)
+ store = new Vuex.Store(testStoreConfig)
+
+ // hack to catch date separators
+ const oldTee = global.t
+ global.t = jest.fn().mockImplementation(function(pkg, text, data) {
+ if (data && data.relativeDate) {
+ return data.relativeDate + ', ' + data.absoluteDate
+ }
+ return oldTee.apply(this, arguments)
+ })
+ })
+
+ afterEach(() => {
+ jest.clearAllMocks()
+ })
+
+ describe('message grouping', () => {
+ test('groups consecutive messages by author', () => {
+ getVisualLastReadMessageIdMock.mockReturnValue(110)
+ const messagesGroup1 = [{
+ id: 100,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'hello',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 100,
+ isReplyable: true,
+ }, {
+ id: 110,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'how are you ?',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 200,
+ isReplyable: true,
+ }]
+
+ const messagesGroup2 = [{
+ id: 200,
+ token: TOKEN,
+ actorId: 'bob',
+ actorDisplayName: 'Bob',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'hello!',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 300,
+ isReplyable: true,
+ }, {
+ id: 210,
+ token: TOKEN,
+ actorId: 'bob',
+ actorDisplayName: 'Bob',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'fine... how abouty you ?',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 400,
+ isReplyable: true,
+ }]
+
+ const messagesGroup3 = [{
+ id: 300,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'fine as well, thanks!',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 0, // temporary
+ isReplyable: true,
+ }]
+
+ const allMessages = messagesGroup1.concat(messagesGroup2.concat(messagesGroup3))
+ messagesListMock.mockReturnValue(allMessages)
+
+ const wrapper = shallowMount(MessagesList, {
+ localVue,
+ store,
+ propsData: {
+ token: TOKEN,
+ isChatScrolledToBottom: true,
+ },
+ })
+
+ const groups = wrapper.findAllComponents({ name: 'MessagesGroup' })
+
+ expect(groups.exists()).toBe(true)
+
+ let group = groups.at(0)
+ expect(group.props('messages')).toStrictEqual(messagesGroup1)
+ expect(group.props('previousMessageId')).toBe(0)
+ expect(group.props('nextMessageId')).toBe(200)
+ expect(group.props('lastReadMessageId')).toBe(110)
+ // using attributes to access v-bind props
+ expect(group.attributes('actorid')).toBe('alice')
+ expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS)
+
+ group = groups.at(1)
+ expect(group.props('messages')).toStrictEqual(messagesGroup2)
+ expect(group.props('previousMessageId')).toBe(110)
+ expect(group.props('nextMessageId')).toBe(300)
+ expect(group.props('lastReadMessageId')).toBe(110)
+ expect(group.attributes('actorid')).toBe('bob')
+ expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS)
+
+ group = groups.at(2)
+ expect(group.props('messages')).toStrictEqual(messagesGroup3)
+ expect(group.props('previousMessageId')).toBe(210)
+ expect(group.props('nextMessageId')).toBe(0)
+ expect(group.props('lastReadMessageId')).toBe(110)
+ expect(group.attributes('actorid')).toBe('alice')
+ expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS)
+
+ expect(messagesListMock).toHaveBeenCalledWith(TOKEN)
+ expect(getVisualLastReadMessageIdMock).toHaveBeenCalledWith(TOKEN)
+
+ const placeholder = wrapper.findAllComponents({ name: 'LoadingPlaceholder' })
+ expect(placeholder.exists()).toBe(false)
+ })
+
+ test('displays a date separator between days', () => {
+ const messages = [{
+ id: 100,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'hello',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: new Date('2020-05-09 13:00:00').getTime() / 1000,
+ isReplyable: true,
+ }, {
+ id: 110,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'no one here ?',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: new Date('2020-05-10 13:00:00').getTime() / 1000,
+ isReplyable: true,
+ }, {
+ id: 'temp-120',
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'seems no one is there...',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 0, // temporary, matches current date
+ isReplyable: true,
+ }]
+
+ const mockDate = new Date('2020-05-11 13:00:00')
+ jest.spyOn(global.Date, 'now').mockImplementation(() => mockDate)
+
+ messagesListMock.mockReturnValue(messages)
+
+ const wrapper = shallowMount(MessagesList, {
+ localVue,
+ store,
+ propsData: {
+ token: TOKEN,
+ isChatScrolledToBottom: true,
+ },
+ })
+
+ const groups = wrapper.findAllComponents({ name: 'MessagesGroup' })
+
+ expect(groups.exists()).toBe(true)
+
+ let group = groups.at(0)
+ expect(group.props('messages')).toStrictEqual([messages[0]])
+ // using attributes to access v-bind props
+ expect(group.attributes('actorid')).toBe('alice')
+ expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS)
+ expect(group.attributes('dateseparator')).toBe('2 days ago, 9 May 2020')
+
+ group = groups.at(1)
+ expect(group.props('messages')).toStrictEqual([messages[1]])
+ expect(group.attributes('actorid')).toBe('alice')
+ expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS)
+ expect(group.attributes('dateseparator')).toBe('Yesterday, 10 May 2020')
+
+ group = groups.at(2)
+ expect(group.props('messages')).toStrictEqual([messages[2]])
+ expect(group.attributes('actorid')).toBe('alice')
+ expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS)
+ expect(group.attributes('dateseparator')).toBe('Today, 11 May 2020')
+
+ expect(messagesListMock).toHaveBeenCalledWith(TOKEN)
+ })
+
+ test('groups system messages with each other', () => {
+ const messages = [{
+ id: 100,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'Alice has entered the call',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: 'call_started',
+ timestamp: 100,
+ isReplyable: true,
+ }, {
+ id: 110,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'Alice has exited the call',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: 'call_ended',
+ timestamp: 200,
+ isReplyable: true,
+ }]
+
+ messagesListMock.mockReturnValue(messages)
+
+ const wrapper = shallowMount(MessagesList, {
+ localVue,
+ store,
+ propsData: {
+ token: TOKEN,
+ isChatScrolledToBottom: true,
+ },
+ })
+
+ const groups = wrapper.findAllComponents({ name: 'MessagesGroup' })
+
+ expect(groups.exists()).toBe(true)
+
+ const group = groups.at(0)
+ expect(group.props('messages')).toStrictEqual(messages)
+ // using attributes to access v-bind props
+ expect(group.attributes('actorid')).toBe('alice')
+ expect(group.attributes('actortype')).toBe(ATTENDEE.ACTOR_TYPE.USERS)
+
+ expect(messagesListMock).toHaveBeenCalledWith(TOKEN)
+ })
+
+ function testNotGrouped(messages) {
+ messagesListMock.mockReturnValue(messages)
+
+ const wrapper = shallowMount(MessagesList, {
+ localVue,
+ store,
+ propsData: {
+ token: TOKEN,
+ isChatScrolledToBottom: true,
+ },
+ })
+
+ const groups = wrapper.findAllComponents({ name: 'MessagesGroup' })
+
+ expect(groups.exists()).toBe(true)
+
+ let group = groups.at(0)
+ expect(group.props('messages')).toStrictEqual([messages[0]])
+ group = groups.at(1)
+ expect(group.props('messages')).toStrictEqual([messages[1]])
+ }
+
+ test('does not group system messages with regular messages from the same author', () => {
+ testNotGrouped([{
+ id: 100,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'Alice has entered the call',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: 'call_started',
+ timestamp: 100,
+ isReplyable: true,
+ }, {
+ id: 110,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'hello',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 200,
+ isReplyable: true,
+ }])
+ })
+
+ test('does not group messages of changelog bot', () => {
+ testNotGrouped([{
+ id: 100,
+ token: TOKEN,
+ actorId: ATTENDEE.CHANGELOG_BOT_ID,
+ actorDisplayName: 'Changelog bot',
+ actorType: ATTENDEE.ACTOR_TYPE.BOTS,
+ message: 'Alice has entered the call',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: 'call_started',
+ timestamp: 100,
+ isReplyable: true,
+ }, {
+ id: 110,
+ token: TOKEN,
+ actorId: ATTENDEE.CHANGELOG_BOT_ID,
+ actorDisplayName: 'Changelog bot',
+ actorType: ATTENDEE.ACTOR_TYPE.BOTS,
+ message: 'hello',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 200,
+ isReplyable: true,
+ }])
+ })
+
+ test('does not group messages with different actor types', () => {
+ testNotGrouped([{
+ id: 100,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'Alice has entered the call',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: 'call_started',
+ timestamp: 100,
+ isReplyable: true,
+ }, {
+ id: 110,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.GUESTS,
+ message: 'hello',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 200,
+ isReplyable: true,
+ }])
+ })
+
+ test('renders a placeholder while loading', () => {
+ messagesListMock.mockReturnValue([])
+
+ const wrapper = shallowMount(MessagesList, {
+ localVue,
+ store,
+ propsData: {
+ token: TOKEN,
+ isChatScrolledToBottom: true,
+ },
+ })
+
+ const groups = wrapper.findAllComponents({ name: 'MessagesGroup' })
+ expect(groups.exists()).toBe(false)
+
+ const placeholder = wrapper.findAllComponents({ name: 'LoadingPlaceholder' })
+ expect(placeholder.exists()).toBe(true)
+ })
+
+ test('skips deleted messages when grouping', () => {
+ const messages = [{
+ id: 100,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'hello',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 100,
+ isReplyable: true,
+ }, {
+ id: 110,
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'deleted',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: 'message_deleted',
+ timestamp: 101,
+ isReplyable: true,
+ }, {
+ id: '120',
+ token: TOKEN,
+ actorId: 'alice',
+ actorDisplayName: 'Alice',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ message: 'hello again',
+ messageType: 'comment',
+ messageParameters: {},
+ systemMessage: '',
+ timestamp: 102,
+ isReplyable: true,
+ }]
+
+ messagesListMock.mockReturnValue(messages)
+
+ const wrapper = shallowMount(MessagesList, {
+ localVue,
+ store,
+ propsData: {
+ token: TOKEN,
+ isChatScrolledToBottom: true,
+ },
+ })
+
+ const groups = wrapper.findAllComponents({ name: 'MessagesGroup' })
+
+ expect(groups.exists()).toBe(true)
+
+ const group = groups.at(0)
+ expect(group.props('messages')).toStrictEqual([messages[0], messages[2]])
+
+ expect(messagesListMock).toHaveBeenCalledWith(TOKEN)
+ })
+ })
+})
diff --git a/src/components/MessagesList/MessagesList.vue b/src/components/MessagesList/MessagesList.vue
index f0d7842d1..b4fd542b3 100644
--- a/src/components/MessagesList/MessagesList.vue
+++ b/src/components/MessagesList/MessagesList.vue
@@ -74,6 +74,7 @@ import MessagesGroup from './MessagesGroup/MessagesGroup'
import Axios from '@nextcloud/axios'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import isInLobby from '../../mixins/isInLobby'
+import { ATTENDEE } from '../../constants'
import debounce from 'debounce'
import { EventBus } from '../../services/EventBus'
import LoadingPlaceholder from '../LoadingPlaceholder'
@@ -172,6 +173,7 @@ export default {
* @returns {object}
*/
messages() {
+ // FIXME: remove if unused ?
return this.$store.getters.messages(this.token)
},
/**
@@ -311,8 +313,8 @@ export default {
return false // No previous message
}
- if (message1.actorType === 'bots' // Don't group messages of commands and bots
- && message1.actorId !== 'changelog') { // Apart from the changelog bot
+ if (message1.actorType === ATTENDEE.ACTOR_TYPE_BOTS // Don't group messages of commands and bots
+ && message1.actorId !== ATTENDEE.CHANGELOG_BOT_ID) { // Apart from the changelog bot
return false
}
@@ -573,6 +575,7 @@ export default {
* @param {object} event The deleteMessage event emitted by the Message component.
*/
handleDeleteMessage(event) {
+ // FIXME: unused ?
this.$store.dispatch('deleteMessage', event.message)
},
diff --git a/src/constants.js b/src/constants.js
index 31025531a..5d6980cfc 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -52,8 +52,10 @@ export const ATTENDEE = {
GUESTS: 'guests',
EMAILS: 'emails',
GROUPS: 'groups',
+ BOTS: 'bots',
},
BRIDGE_BOT_ID: 'bridge-bot',
+ CHANGELOG_BOT_ID: 'changelog',
}
export const PARTICIPANT = {
CALL_FLAG: {
diff --git a/src/test-setup.js b/src/test-setup.js
index 9854337dd..1370b5644 100644
--- a/src/test-setup.js
+++ b/src/test-setup.js
@@ -44,6 +44,8 @@ global.OCA = {
Talk: {
},
}
+
+// TODO: use nextcloud-l10n lib once https://github.com/nextcloud/nextcloud-l10n/issues/271 is solved
global.t = jest.fn().mockImplementation((app, text) => text)
global.n = jest.fn().mockImplementation((app, text) => text)