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
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js')
-rw-r--r--src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js437
1 files changed, 437 insertions, 0 deletions
diff --git a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js
new file mode 100644
index 000000000..78d91b6ec
--- /dev/null
+++ b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js
@@ -0,0 +1,437 @@
+import Vuex, { Store } from 'vuex'
+import { createLocalVue, shallowMount } from '@vue/test-utils'
+import { cloneDeep } from 'lodash'
+import storeConfig from '../../../../../store/storeConfig'
+import { CONVERSATION, PARTICIPANT, ATTENDEE } from '../../../../../constants'
+import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
+import { findActionButton } from '../../../../../test-helpers'
+import MessageButtonsBar from './../MessageButtonsBar/MessageButtonsBar.vue'
+
+describe('MessageButtonsBar.vue', () => {
+ const TOKEN = 'XXTOKENXX'
+ let localVue
+ let testStoreConfig
+ let store
+ let messageProps
+ let conversationProps
+ let getActorTypeMock
+
+ beforeEach(() => {
+ localVue = createLocalVue()
+ localVue.use(Vuex)
+
+ conversationProps = {
+ token: TOKEN,
+ lastCommonReadMessage: 0,
+ type: CONVERSATION.TYPE.GROUP,
+ readOnly: CONVERSATION.STATE.READ_WRITE,
+ }
+
+ testStoreConfig = cloneDeep(storeConfig)
+ testStoreConfig.modules.tokenStore.getters.getToken
+ = jest.fn().mockReturnValue(() => TOKEN)
+ testStoreConfig.modules.conversationsStore.getters.conversation
+ = jest.fn().mockReturnValue((token) => conversationProps)
+ testStoreConfig.modules.actorStore.getters.getActorId
+ = jest.fn().mockReturnValue(() => 'user-id-1')
+ getActorTypeMock = jest.fn().mockReturnValue(() => ATTENDEE.ACTOR_TYPE.USERS)
+ testStoreConfig.modules.actorStore.getters.getActorType = getActorTypeMock
+
+ messageProps = {
+ message: 'test message',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ actorId: 'user-id-1',
+ actorDisplayName: 'user-display-name-1',
+ messageParameters: {},
+ id: 123,
+ isTemporary: false,
+ isFirstMessage: true,
+ isReplyable: true,
+ timestamp: new Date('2020-05-07 09:23:00').getTime() / 1000,
+ token: TOKEN,
+ systemMessage: '',
+ messageType: 'comment',
+ previousMessageId: 100,
+ messageObject: {},
+ messageApiData: {
+ apiDummyData: 1,
+ },
+ participant: {
+ actorId: 'user-id-1',
+ actorType: ATTENDEE.ACTOR_TYPE.USERS,
+ participantType: PARTICIPANT.TYPE.USER,
+ },
+ }
+ })
+
+ afterEach(() => {
+ jest.clearAllMocks()
+ })
+
+ describe('actions', () => {
+
+ beforeEach(() => {
+ store = new Store(testStoreConfig)
+ })
+
+ describe('reply action', () => {
+ test('replies to message', async () => {
+ const replyAction = jest.fn()
+ testStoreConfig.modules.quoteReplyStore.actions.addMessageToBeReplied = replyAction
+ store = new Store(testStoreConfig)
+
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ stubs: {
+ ActionButton,
+ },
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'Reply')
+ expect(actionButton.exists()).toBe(true)
+ expect(actionButton.isVisible()).toBe(true)
+ await actionButton.find('button').trigger('click')
+
+ expect(replyAction).toHaveBeenCalledWith(expect.anything(), {
+ id: 123,
+ actorId: 'user-id-1',
+ actorType: 'users',
+ actorDisplayName: 'user-display-name-1',
+ message: 'test message',
+ messageParameters: {},
+ messageType: 'comment',
+ systemMessage: '',
+ timestamp: new Date('2020-05-07 09:23:00').getTime() / 1000,
+ token: TOKEN,
+ previousMessageId: 100,
+ })
+ })
+
+ test('hides reply button when not replyable', async () => {
+ messageProps.isReplyable = false
+ store = new Store(testStoreConfig)
+
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ stubs: {
+ ActionButton,
+ },
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'Reply')
+ expect(actionButton.isVisible()).toBe(false)
+ })
+ })
+
+ describe('private reply action', () => {
+ test('creates a new conversation when replying to message privately', async () => {
+ const routerPushMock = jest.fn().mockResolvedValue()
+ const createOneToOneConversation = jest.fn()
+ testStoreConfig.modules.conversationsStore.actions.createOneToOneConversation = createOneToOneConversation
+ store = new Store(testStoreConfig)
+
+ messageProps.actorId = 'another-user'
+
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ mocks: {
+ $router: {
+ push: routerPushMock,
+ },
+ },
+ stubs: {
+ ActionButton,
+ },
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'Reply privately')
+ expect(actionButton.exists()).toBe(true)
+
+ createOneToOneConversation.mockResolvedValueOnce({
+ token: 'new-token',
+ })
+
+ await actionButton.find('button').trigger('click')
+
+ expect(createOneToOneConversation).toHaveBeenCalledWith(expect.anything(), 'another-user')
+
+ expect(routerPushMock).toHaveBeenCalledWith({
+ name: 'conversation',
+ params: {
+ token: 'new-token',
+ },
+ })
+ })
+
+ /**
+ * @param {boolean} visible Whether or not the reply-private action is visible
+ */
+ function testPrivateReplyActionVisible(visible) {
+ store = new Store(testStoreConfig)
+
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ stubs: {
+ ActionButton,
+ },
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'Reply privately')
+ expect(actionButton.exists()).toBe(visible)
+ }
+
+ test('hides private reply action for own messages', async () => {
+ // using default message props which have the
+ // actor id set to the current user
+ testPrivateReplyActionVisible(false)
+ })
+
+ test('hides private reply action for one to one conversation type', async () => {
+ messageProps.actorId = 'another-user'
+ conversationProps.type = CONVERSATION.TYPE.ONE_TO_ONE
+ testPrivateReplyActionVisible(false)
+ })
+
+ test('hides private reply action for guest messages', async () => {
+ messageProps.actorId = 'guest-user'
+ messageProps.actorType = ATTENDEE.ACTOR_TYPE.GUESTS
+ testPrivateReplyActionVisible(false)
+ })
+
+ test('hides private reply action when current user is a guest', async () => {
+ messageProps.actorId = 'another-user'
+ getActorTypeMock.mockClear().mockReturnValue(() => ATTENDEE.ACTOR_TYPE.GUESTS)
+ testPrivateReplyActionVisible(false)
+ })
+ })
+
+ describe('delete action', () => {
+ test('emits delete event', async () => {
+ // need to mock the date to be within 6h
+ const mockDate = new Date('2020-05-07 10:00:00')
+ jest.spyOn(global.Date, 'now')
+ .mockImplementation(() => mockDate)
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ stubs: {
+ ActionButton,
+ },
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'Delete')
+ expect(actionButton.exists()).toBe(true)
+
+ await actionButton.find('button').trigger('click')
+
+ expect(wrapper.emitted().delete).toBeTruthy()
+ })
+
+ /**
+ * @param {boolean} visible Whether or not the delete action is visible
+ * @param {Date} mockDate The message date (deletion only works within 6h)
+ * @param {number} participantType The participant type of the user
+ */
+ function testDeleteMessageVisible(visible, mockDate, participantType = PARTICIPANT.TYPE.USER) {
+ store = new Store(testStoreConfig)
+
+ // need to mock the date to be within 6h
+ if (!mockDate) {
+ mockDate = new Date('2020-05-07 10:00:00')
+ }
+
+ jest.spyOn(global.Date, 'now')
+ .mockImplementation(() => mockDate)
+
+ messageProps.participant.participantType = participantType
+
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ stubs: {
+ ActionButton,
+ },
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'Delete')
+ expect(actionButton.exists()).toBe(visible)
+ }
+
+ test('hides delete action when message is older than 6 hours', () => {
+ testDeleteMessageVisible(false, new Date('2020-05-07 15:24:00'))
+ })
+
+ test('hides delete action when the conversation is read-only', () => {
+ conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY
+ testDeleteMessageVisible(false)
+ })
+
+ test('hides delete action for file messages', () => {
+ messageProps.message = '{file}'
+ messageProps.messageParameters.file = {}
+ testDeleteMessageVisible(false)
+ })
+
+ test('hides delete action on other people messages for non-moderators', () => {
+ messageProps.actorId = 'another-user'
+ conversationProps.type = CONVERSATION.TYPE.GROUP
+ testDeleteMessageVisible(false)
+ })
+
+ test('shows delete action on other people messages for moderators', () => {
+ messageProps.actorId = 'another-user'
+ conversationProps.type = CONVERSATION.TYPE.GROUP
+ testDeleteMessageVisible(true, null, PARTICIPANT.TYPE.MODERATOR)
+ })
+
+ test('shows delete action on other people messages for owner', () => {
+ messageProps.actorId = 'another-user'
+ conversationProps.type = CONVERSATION.TYPE.PUBLIC
+ testDeleteMessageVisible(true, null, PARTICIPANT.TYPE.OWNER)
+ })
+
+ test('does not show delete action even for guest moderators', () => {
+ messageProps.actorId = 'another-user'
+ conversationProps.type = CONVERSATION.TYPE.PUBLIC
+ testDeleteMessageVisible(false, null, PARTICIPANT.TYPE.GUEST_MODERATOR)
+ })
+
+ test('does not show delete action on other people messages in one to one conversations', () => {
+ messageProps.actorId = 'another-user'
+ conversationProps.type = CONVERSATION.TYPE.ONE_TO_ONE
+ testDeleteMessageVisible(false)
+ })
+ })
+
+ test('marks message as unread', async () => {
+ const updateLastReadMessageAction = jest.fn().mockResolvedValueOnce()
+ const fetchConversationAction = jest.fn().mockResolvedValueOnce()
+ testStoreConfig.modules.conversationsStore.actions.updateLastReadMessage = updateLastReadMessageAction
+ testStoreConfig.modules.conversationsStore.actions.fetchConversation = fetchConversationAction
+ store = new Store(testStoreConfig)
+
+ messageProps.previousMessageId = 100
+
+ // appears even with more restrictive conditions
+ conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY
+ messageProps.actorId = 'another-user'
+ messageProps.participant = {
+ actorId: 'guest-id-1',
+ actorType: ATTENDEE.ACTOR_TYPE.GUESTS,
+ participantType: PARTICIPANT.TYPE.GUEST,
+ }
+
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ stubs: {
+ ActionButton,
+ },
+
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'Mark as unread')
+ expect(actionButton.exists()).toBe(true)
+
+ await actionButton.find('button').trigger('click')
+ // needs two updates...
+ await wrapper.vm.$nextTick()
+ await wrapper.vm.$nextTick()
+
+ expect(updateLastReadMessageAction).toHaveBeenCalledWith(expect.anything(), {
+ token: TOKEN,
+ id: 100,
+ updateVisually: true,
+ })
+
+ expect(fetchConversationAction).toHaveBeenCalledWith(expect.anything(), {
+ token: TOKEN,
+ })
+ })
+
+ test('copies message link', async () => {
+ const copyTextMock = jest.fn()
+
+ // appears even with more restrictive conditions
+ conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY
+ messageProps.actorId = 'another-user'
+ messageProps.participant = {
+ actorId: 'guest-id-1',
+ actorType: ATTENDEE.ACTOR_TYPE.GUESTS,
+ participantType: PARTICIPANT.TYPE.GUEST,
+ }
+
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ mocks: {
+ $copyText: copyTextMock,
+ },
+ stubs: {
+ ActionButton,
+ },
+
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'Copy message link')
+ expect(actionButton.exists()).toBe(true)
+
+ await actionButton.find('button').trigger('click')
+
+ expect(copyTextMock).toHaveBeenCalledWith('http://localhost/nc-webroot/call/XXTOKENXX#message_123')
+ })
+
+ test('renders clickable custom actions', async () => {
+ const handler = jest.fn()
+ const handler2 = jest.fn()
+ const actionsGetterMock = jest.fn().mockReturnValue([{
+ label: 'first action',
+ icon: 'some-icon',
+ callback: handler,
+ }, {
+ label: 'second action',
+ icon: 'some-icon2',
+ callback: handler2,
+ }])
+ testStoreConfig.modules.messageActionsStore.getters.messageActions = actionsGetterMock
+ testStoreConfig.modules.messagesStore.getters.message = jest.fn(() => () => messageProps)
+ store = new Store(testStoreConfig)
+ const wrapper = shallowMount(MessageButtonsBar, {
+ localVue,
+ store,
+ stubs: {
+ ActionButton,
+ },
+ propsData: messageProps,
+ })
+
+ const actionButton = findActionButton(wrapper, 'first action')
+ expect(actionButton.exists()).toBe(true)
+ await actionButton.find('button').trigger('click')
+
+ expect(handler).toHaveBeenCalledWith({
+ apiDummyData: 1,
+ },)
+
+ const actionButton2 = findActionButton(wrapper, 'second action')
+ expect(actionButton2.exists()).toBe(true)
+ await actionButton2.find('button').trigger('click')
+
+ expect(handler2).toHaveBeenCalledWith({
+ apiDummyData: 1,
+ })
+ })
+ })
+})