UNPKG

@orfeas126/box-ui-elements

Version:
419 lines (341 loc) 17.2 kB
// @flow import React, { act } from 'react'; import { IntlProvider } from 'react-intl'; import { fireEvent, render, screen } from '@testing-library/react'; import { ContentState, EditorState } from 'draft-js'; import { BaseComment } from '../BaseComment'; import { annotation, annotationPreviousVersion, comment, currentUser, reply1, reply2, replies, TIME_STRING_SEPT_27_2017, TIME_STRING_SEPT_28_2017, } from '../stories/common'; import messages from '../messages'; import localize from '../../../../../../test/support/i18n'; jest.mock('react-intl', () => ({ ...jest.requireActual('react-intl'), FormattedMessage: ({ defaultMessage }: { defaultMessage: string }) => <span>{defaultMessage}</span>, })); const replyCreate = jest.fn(); const onSelect = jest.fn(); const hideReplies = jest.fn(); const showReplies = jest.fn(); const repliesProps = { hasReplies: true, onReplyCreate: replyCreate, replies, onHideReplies: hideReplies, onShowReplies: showReplies, }; // eslint-disable-next-line import/prefer-default-export export const getWrapper = props => render( <IntlProvider locale="en"> <BaseComment id="1" {...comment} approverSelectorContacts={[]} currentUser={currentUser} mentionSelectorContacts={[]} onSelect={onSelect} {...props} /> </IntlProvider>, ); describe('elements/content-sidebar/ActivityFeed/comment/BaseComment', () => { EditorState.moveFocusToEnd = editor => editor; test.each` activityItem ${annotation} ${comment} `(`should render activity item with replies`, ({ activityItem }) => { getWrapper({ ...activityItem, ...repliesProps }); expect(screen.getByText(activityItem.tagged_message)).toBeInTheDocument(); expect(screen.getByText(activityItem.created_by.name)).toBeInTheDocument(); expect(screen.getByText('Sep 27, 2017')).toBeInTheDocument(); expect(screen.getByText(reply1.tagged_message)).toBeInTheDocument(); expect(screen.getByText(reply1.created_by.name)).toBeInTheDocument(); expect(screen.getByText('Sep 28, 2017')).toBeInTheDocument(); expect(screen.getByText(reply2.tagged_message)).toBeInTheDocument(); expect(screen.getByText(reply2.created_by.name)).toBeInTheDocument(); expect(screen.getByText('Sep 29, 2017')).toBeInTheDocument(); }); test('should render annotation badge when annotationActivityLink prop is defined', () => { getWrapper({ ...annotation, ...repliesProps }); expect(screen.getByText(localize(messages.inlineCommentAnnotationIconTitle.id))).toBeInTheDocument(); }); test.each` annotationType | linkText ${annotation} | ${'Page 1'} ${annotationPreviousVersion} | ${'Version 1'} `(`should properly render AnnotationActivityLink`, ({ annotationType, linkText }) => { getWrapper({ ...annotationType, ...repliesProps }); expect(screen.getByText(linkText)).toBeInTheDocument(); }); test('should correctly render comment when translation is enabled', () => { const translations = { translationEnabled: true, onTranslate: jest.fn(), }; getWrapper({ translations }); expect(screen.getByText(comment.tagged_message)).toBeInTheDocument(); expect(screen.getByText(comment.created_by.name)).toBeInTheDocument(); expect(screen.getByText('Sep 27, 2017')).toBeInTheDocument(); }); test('should render commenter as a link', async () => { const getUserProfileUrl = jest.fn().mockResolvedValue('https://www.test.com/'); await act(async () => { getWrapper({ getUserProfileUrl }); }); await expect(screen.getByText(comment.created_by.name)).toBeInTheDocument(); await expect(screen.getByRole('link')).toHaveAttribute('href', 'https://www.test.com/'); }); test.each` permissions | onCommentEdit | showDelete | showEdit | showResolve ${{ can_delete: true, can_edit: true, can_resolve: true }} | ${jest.fn()} | ${true} | ${true} | ${true} ${{ can_delete: true, can_edit: false, can_resolve: true }} | ${jest.fn()} | ${true} | ${false} | ${true} ${{ can_delete: false, can_edit: true, can_resolve: true }} | ${jest.fn()} | ${false} | ${true} | ${true} ${{ can_delete: true, can_edit: false, can_resolve: true }} | ${undefined} | ${true} | ${false} | ${true} ${{ can_delete: false, can_edit: true, can_resolve: true }} | ${undefined} | ${false} | ${true} | ${true} ${{ can_delete: true, can_edit: true, can_resolve: true }} | ${undefined} | ${true} | ${true} | ${true} ${{ can_delete: true, can_edit: true, can_resolve: false }} | ${jest.fn()} | ${true} | ${true} | ${false} ${{ can_delete: true, can_edit: false, can_resolve: false }} | ${jest.fn()} | ${true} | ${false} | ${false} ${{ can_delete: false, can_edit: true, can_resolve: false }} | ${jest.fn()} | ${false} | ${true} | ${false} ${{ can_delete: true, can_edit: false, can_resolve: false }} | ${undefined} | ${true} | ${false} | ${false} ${{ can_delete: false, can_edit: true, can_resolve: false }} | ${undefined} | ${false} | ${true} | ${false} ${{ can_delete: true, can_edit: true, can_resolve: false }} | ${undefined} | ${true} | ${true} | ${false} `( `show menu for a comment with permissions $permissions and onCommentEdit ($onCommentEdit), should showDelete: $showDelete, showEdit: $showEdit, showResolve: $showResolve`, ({ permissions, onCommentEdit, showDelete, showEdit, showResolve }) => { getWrapper({ onCommentEdit, permissions }); const menuItem = screen.queryByTestId('comment-actions-menu'); expect(menuItem).toBeInTheDocument(); fireEvent.click(menuItem); showDelete ? expect(screen.getByTestId('delete-comment')).toBeInTheDocument() : expect(screen.queryByTestId('delete-comment')).not.toBeInTheDocument(); showEdit ? expect(screen.getByTestId('edit-comment')).toBeInTheDocument() : expect(screen.queryByTestId('edit-comment')).not.toBeInTheDocument(); showResolve ? expect(screen.getByTestId('resolve-comment')).toBeInTheDocument() : expect(screen.queryByTestId('resolve-comment')).not.toBeInTheDocument(); }, ); test.each` permissions | onEdit ${{ can_delete: false, can_edit: false, show_resolve: false }} | ${undefined} ${{ can_delete: false, can_edit: false, show_resolve: false }} | ${jest.fn()} ${{ can_delete: false, can_edit: false, show_resolve: true }} | ${undefined} ${{ can_delete: false, can_edit: false, show_resolve: true }} | ${jest.fn()} `( `show not show menu for a comment with permissions $permissions and onEdit ($onEdit)`, ({ permissions, onEdit }) => { getWrapper({ onEdit, permissions }); const menuItem = screen.queryByTestId('comment-actions-menu'); expect(menuItem).not.toBeInTheDocument(); expect(screen.queryByTestId('delete-comment')).not.toBeInTheDocument(); expect(screen.queryByTestId('edit-comment')).not.toBeInTheDocument(); }, ); test('should render unresolve menu option', () => { getWrapper({ status: 'resolved' }); const menuItem = screen.queryByTestId('comment-actions-menu'); expect(menuItem).toBeInTheDocument(); fireEvent.click(menuItem); expect(screen.getByTestId('unresolve-comment')).toBeInTheDocument(); expect(screen.queryByTestId('resolve-comment')).not.toBeInTheDocument(); }); test('should not show actions menu when comment is pending', () => { getWrapper({ isPending: true }); const menuItem = screen.queryByTestId('comment-actions-menu'); expect(menuItem).not.toBeInTheDocument(); }); test.each` activityItem | activityItemType ${comment} | ${'comment'} ${annotation} | ${'annotation'} `( 'should allow user to edit if they have edit permissions on the $activityItemType and edit handler is defined', async ({ activityItem, activityItemType }) => { const mockOnCommentEdit = jest.fn(); const mockOnAnnotationEdit = jest.fn(); const isComment = activityItemType === 'comment'; const isAnnotation = activityItemType === 'annotation'; getWrapper({ ...activityItem, onCommentEdit: isComment ? mockOnCommentEdit : undefined, onAnnotationEdit: isAnnotation ? mockOnAnnotationEdit : undefined, }); const menuItem = screen.queryByTestId('comment-actions-menu'); fireEvent.click(menuItem); expect(screen.getByTestId('edit-comment')).toBeInTheDocument(); fireEvent.click(screen.getByTestId('edit-comment')); expect(screen.queryByTestId('edit-comment')).not.toBeInTheDocument(); expect(screen.getByTestId('bcs-CommentForm-body')).toBeInTheDocument(); await fireEvent.click(screen.getByRole('button', { name: 'Post' })); if (isComment) { expect(mockOnCommentEdit).toBeCalledWith({ hasMention: false, id: '1', permissions: { can_delete: true, can_edit: true, can_resolve: true, can_reply: true }, text: comment.tagged_message, }); expect(mockOnAnnotationEdit).not.toBeCalled(); } if (isAnnotation) { expect(mockOnAnnotationEdit).toBeCalledWith({ id: '1', permissions: { can_delete: true, can_edit: true, can_resolve: true, can_reply: true }, text: comment.tagged_message, }); expect(mockOnCommentEdit).not.toBeCalled(); } }, ); test.each` status | menuItemTestId | expectedNewStatus ${'open'} | ${'resolve-comment'} | ${'resolved'} ${'resolved'} | ${'unresolve-comment'} | ${'open'} `( `should allow user to resolve / unresolve if they have resolve permissions, edit handler is defined and given status is $status`, ({ status, menuItemTestId, expectedNewStatus }) => { const mockOnStatusChange = jest.fn(); getWrapper({ permissions: { can_resolve: true, can_edit: false, can_delete: false }, type: 'task', status, onStatusChange: mockOnStatusChange, }); const menuItem = screen.queryByTestId('comment-actions-menu'); fireEvent.click(menuItem); expect(screen.getByTestId(menuItemTestId)).toBeInTheDocument(); fireEvent.click(screen.getByTestId(menuItemTestId)); expect(screen.queryByTestId(menuItemTestId)).not.toBeInTheDocument(); expect(mockOnStatusChange).toBeCalledWith({ id: '1', permissions: { can_delete: false, can_edit: false, can_resolve: true, }, status: expectedNewStatus, }); }, ); test('should render an error when one is defined', () => { getWrapper({ error: { title: { defaultMessage: 'Test Error' }, message: { defaultMessage: 'An error has occurred.' } }, }); expect(screen.getByText(comment.tagged_message)).toBeInTheDocument(); expect(screen.getByText(comment.created_by.name)).toBeInTheDocument(); expect(screen.getByText('Sep 27, 2017')).toBeInTheDocument(); expect(screen.getByText('Test Error')).toBeInTheDocument(); expect(screen.getByText('An error has occurred.')).toBeInTheDocument(); }); test('should render an error cta when an action is defined', () => { const onAction = jest.fn(); getWrapper({ error: { title: { defaultMessage: 'Test Error' }, message: { defaultMessage: 'An error has occurred.' }, action: { text: 'Action Button', onAction, }, }, }); expect(screen.getByText(comment.tagged_message)).toBeInTheDocument(); expect(screen.getByText('Test Error')).toBeInTheDocument(); expect(screen.getByText('An error has occurred.')).toBeInTheDocument(); expect(screen.getByText('Action Button')).toBeInTheDocument(); fireEvent.click(screen.getByText('Action Button')); expect(onAction).toHaveBeenCalledTimes(1); }); test('should show edited in ActivityMessage', () => { getWrapper({ modified_at: TIME_STRING_SEPT_28_2017, status: 'open' }); expect(screen.getByText('\\ (edited)')).toBeInTheDocument(); }); test.each` modified_at | status ${undefined} | ${'open'} ${undefined} | ${'resolved'} ${TIME_STRING_SEPT_27_2017} | ${'resolved'} `( `given modified_at = $modified_at and status = $status, edited should not be shown in ActivityMessage`, ({ modified_at, status }) => { getWrapper({ modified_at, status }); expect(screen.queryByText('\\ (edited)')).not.toBeInTheDocument(); }, ); test.each` index ${0} ${1} ${2} `(`should call onSelect when More Options is clicked for comment/reply #$index`, ({ index }) => { getWrapper({ ...repliesProps }); expect(screen.getByText(comment.tagged_message)).toBeInTheDocument(); fireEvent.click(screen.getByText(comment.tagged_message)); expect(onSelect).not.toBeCalled(); expect(screen.getAllByTestId('comment-actions-menu').length).toBe(3); fireEvent.click(screen.getAllByTestId('comment-actions-menu')[index]); expect(onSelect).toBeCalledTimes(1); expect(onSelect).toBeCalledWith(true); }); test('should call onReplyCreate when reply is created', () => { // Mock DraftJS editor and intercept onChange since DraftJS doesn't have a value setter const draftjs = require('draft-js'); draftjs.Editor = jest.fn(props => { const modifiedOnchange = e => { const text = e.target.value; const content = ContentState.createFromText(text); props.onChange(EditorState.createWithContent(content)); }; return <input className="editor" onChange={e => modifiedOnchange(e)} />; }); getWrapper({ ...repliesProps }); const replyButton = screen.getByRole('button', { name: 'Reply' }); expect(replyButton).toBeVisible(); fireEvent.click(replyButton); expect(onSelect).toBeCalledTimes(1); expect(onSelect).toBeCalledWith(true); fireEvent.change(screen.getByRole('textbox'), { target: { value: 'Batman' } }); fireEvent.click(screen.getByText('Post')); expect(replyCreate).toBeCalledTimes(1); expect(replyCreate).toBeCalledWith('Batman'); }); test('should show Hide Replies and call onHideReplies when clicked', () => { getWrapper({ ...repliesProps, repliesTotalCount: 2 }); expect(screen.getByText('Hide replies')).toBeVisible(); fireEvent.click(screen.getByText('Hide replies')); expect(hideReplies).toBeCalledTimes(1); expect(hideReplies).toBeCalledWith([reply2]); expect(showReplies).not.toBeCalled(); }); test('should show Show Replies and call onShowReplies when clicked', () => { const totalCount = 5; getWrapper({ ...repliesProps, repliesTotalCount: totalCount }); // react-intl mocking problem with variables expect(screen.getByText(/See/i)).toBeVisible(); expect(screen.queryByText('Hide replies')).not.toBeInTheDocument(); fireEvent.click(screen.getByText(/See/i)); expect(showReplies).toBeCalledTimes(1); expect(hideReplies).not.toBeCalled(); }); test('should focus on the edit CommentForm when it is opened', () => { const mockFocusFunc = jest.fn(); EditorState.moveFocusToEnd = mockFocusFunc; getWrapper({ canEdit: true }); const menuItem = screen.getByTestId('comment-actions-menu'); fireEvent.click(menuItem); const editButton = screen.getByTestId('edit-comment'); fireEvent.click(editButton); expect(mockFocusFunc).toHaveBeenCalled(); }); });