diff --git a/webapp/channels/src/components/post/post_component.test.tsx b/webapp/channels/src/components/post/post_component.test.tsx index 1285cf6d66f..88dbad2b073 100644 --- a/webapp/channels/src/components/post/post_component.test.tsx +++ b/webapp/channels/src/components/post/post_component.test.tsx @@ -5,30 +5,39 @@ import React from 'react'; import {DeepPartial} from '@mattermost/types/utilities'; -import {renderWithFullContext, screen} from 'tests/react_testing_utils'; +import mergeObjects from 'packages/mattermost-redux/test/merge_objects'; + +import {renderWithFullContext, screen, userEvent} from 'tests/react_testing_utils'; import {GlobalState} from 'types/store'; +import {getHistory} from 'utils/browser_history'; import {Locations} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; -import PostComponent from './post_component'; -import mergeObjects from 'packages/mattermost-redux/test/merge_objects'; +import PostComponent, {Props} from './post_component'; describe('PostComponent', () => { - const baseProps = { + const currentTeam = TestHelper.getTeamMock(); + const channel = TestHelper.getChannelMock({team_id: currentTeam.id}); + + const baseProps: Props = { center: false, - currentTeam: TestHelper.getTeamMock(), + currentTeam, currentUserId: 'currentUserId', displayName: '', + hasReplies: false, isBot: false, + isCollapsedThreadsEnabled: true, isFlagged: false, isMobileView: false, isPostAcknowledgementsEnabled: false, isPostPriorityEnabled: false, location: Locations.CENTER, - post: TestHelper.getPostMock(), + post: TestHelper.getPostMock({channel_id: channel.id}), recentEmojis: [], + replyCount: 0, + team: currentTeam, actions: { markPostAsUnread: jest.fn(), emitShortcutReactToLastPostFrom: jest.fn(), @@ -56,12 +65,7 @@ describe('PostComponent', () => { }; test('should show reactions in the center channel', () => { - renderWithFullContext( - , - baseState, - ); + renderWithFullContext(, baseState); expect(screen.getByLabelText('reactions')).toBeInTheDocument(); }); @@ -75,56 +79,250 @@ describe('PostComponent', () => { }, }); - const {rerender} = renderWithFullContext( - , - state, - ); + let props: Props = { + ...baseProps, + location: Locations.RHS_ROOT, + }; + const {rerender} = renderWithFullContext(, state); expect(screen.getByLabelText('reactions')).toBeInTheDocument(); - rerender( - , - ); + props = { + ...baseProps, + location: Locations.RHS_COMMENT, + }; + rerender(); expect(screen.getByLabelText('reactions')).toBeInTheDocument(); }); test('should show only show reactions in search results with pinned/saved posts visible', () => { - const {rerender} = renderWithFullContext( - , - baseState, - ); + let props = { + ...baseProps, + location: Locations.SEARCH, + }; + const {rerender} = renderWithFullContext(, baseState); expect(screen.queryByLabelText('reactions')).not.toBeInTheDocument(); - rerender( - , - ); + props = { + ...baseProps, + location: Locations.SEARCH, + isPinnedPosts: true, + }; + rerender(); expect(screen.getByLabelText('reactions')).toBeInTheDocument(); - rerender( - , - ); + props = { + ...baseProps, + location: Locations.SEARCH, + isFlaggedPosts: true, + }; + rerender(); expect(screen.getByLabelText('reactions')).toBeInTheDocument(); }); }); + + describe('thread footer', () => { + test('should never show thread footer for a post that isn\'t part of a thread', () => { + let props: Props = baseProps; + const {rerender} = renderWithFullContext(); + + expect(screen.queryByText(/Follow|Following/)).not.toBeInTheDocument(); + + props = { + ...baseProps, + location: Locations.SEARCH, + }; + rerender(); + + expect(screen.queryByText(/Follow|Following/)).not.toBeInTheDocument(); + }); + + // This probably shouldn't appear in the search results https://mattermost.atlassian.net/browse/MM-53078 + test('should only show thread footer for a root post in the center channel and search results', () => { + const rootPost = TestHelper.getPostMock({ + id: 'rootPost', + channel_id: channel.id, + reply_count: 1, + }); + const state: DeepPartial = { + entities: { + posts: { + posts: { + rootPost, + }, + }, + }, + }; + + let props = { + ...baseProps, + hasReplies: true, + post: rootPost, + replyCount: 1, + }; + const {rerender} = renderWithFullContext(, state); + + expect(screen.queryByText(/Follow|Following/)).toBeInTheDocument(); + + props = { + ...props, + location: Locations.RHS_ROOT, + }; + rerender(); + + expect(screen.queryByText(/Follow|Following/)).not.toBeInTheDocument(); + + props = { + ...props, + location: Locations.SEARCH, + }; + rerender(); + + expect(screen.queryByText(/Follow|Following/)).toBeInTheDocument(); + }); + + test('should never show thread footer for a comment', () => { + let props = { + ...baseProps, + hasReplies: true, + post: { + ...baseProps.post, + root_id: 'some_other_post_id', + }, + }; + const {rerender} = renderWithFullContext(); + + expect(screen.queryByText(/Follow|Following/)).not.toBeInTheDocument(); + + props = { + ...props, + location: Locations.RHS_COMMENT, + }; + rerender(); + + expect(screen.queryByText(/Follow|Following/)).not.toBeInTheDocument(); + + props = { + ...props, + location: Locations.SEARCH, + }; + rerender(); + + expect(screen.queryByText(/Follow|Following/)).not.toBeInTheDocument(); + }); + + test('should not show thread footer with CRT disabled', () => { + const rootPost = TestHelper.getPostMock({ + id: 'rootPost', + channel_id: channel.id, + reply_count: 1, + }); + const state: DeepPartial = { + entities: { + posts: { + posts: { + rootPost, + }, + }, + }, + }; + + let props = { + ...baseProps, + hasReplies: true, + isCollapsedThreadsEnabled: false, + post: rootPost, + replyCount: 1, + }; + const {rerender} = renderWithFullContext(, state); + + expect(screen.queryByText(/Follow|Following/)).not.toBeInTheDocument(); + + props = { + ...props, + location: Locations.SEARCH, + }; + rerender(); + + expect(screen.queryByText(/Follow|Following/)).not.toBeInTheDocument(); + }); + + describe('reply/X replies link', () => { + const rootPost = TestHelper.getPostMock({ + id: 'rootPost', + channel_id: channel.id, + reply_count: 1, + }); + const state: DeepPartial = { + entities: { + posts: { + posts: { + rootPost, + }, + }, + }, + }; + + const propsForRootPost = { + ...baseProps, + hasReplies: true, + post: rootPost, + replyCount: 1, + }; + + test('should select post in RHS when clicked in center channel', () => { + renderWithFullContext(, state); + + userEvent.click(screen.getByText('1 reply')); + + // Yes, this action has a different name than the one you'd expect + expect(propsForRootPost.actions.selectPostFromRightHandSideSearch).toHaveBeenCalledWith(rootPost); + }); + + test('should select post in RHS when clicked in center channel in a DM/GM', () => { + const props = { + ...propsForRootPost, + team: undefined, + }; + renderWithFullContext(, state); + + userEvent.click(screen.getByText('1 reply')); + + // Yes, this action has a different name than the one you'd expect + expect(propsForRootPost.actions.selectPostFromRightHandSideSearch).toHaveBeenCalledWith(rootPost); + expect(getHistory().push).not.toHaveBeenCalled(); + }); + + test('should select post in RHS when clicked in a search result on the current team', () => { + const props = { + ...propsForRootPost, + location: Locations.SEARCH, + }; + renderWithFullContext(, state); + + userEvent.click(screen.getByText('1 reply')); + + expect(propsForRootPost.actions.selectPostFromRightHandSideSearch).toHaveBeenCalledWith(rootPost); + expect(getHistory().push).not.toHaveBeenCalled(); + }); + + test('should jump to post when clicked in a search result on another team', () => { + const props = { + ...propsForRootPost, + location: Locations.SEARCH, + team: TestHelper.getTeamMock({id: 'another_team'}), + }; + renderWithFullContext(, state); + + userEvent.click(screen.getByText('1 reply')); + + expect(propsForRootPost.actions.selectPostFromRightHandSideSearch).not.toHaveBeenCalled(); + expect(getHistory().push).toHaveBeenCalled(); + }); + }); + }); }); diff --git a/webapp/channels/src/components/post/post_component.tsx b/webapp/channels/src/components/post/post_component.tsx index ace78e7bf4f..2ae035d3831 100644 --- a/webapp/channels/src/components/post/post_component.tsx +++ b/webapp/channels/src/components/post/post_component.tsx @@ -82,7 +82,7 @@ export type Props = { channelType?: string; a11yIndex?: number; isBot: boolean; - hasReplies?: boolean; + hasReplies: boolean; isFirstReply?: boolean; previousPostIsComment?: boolean; matches?: string[];