From 81bfc06990af231175c1203ef6b9ffc1e960077d Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 12 Sep 2022 14:42:15 -0400 Subject: [PATCH] Add tests --- app/soapbox/components/dropdown_menu.tsx | 7 +- .../__tests__/chat-message-list.test.tsx | 136 ++++++++++++++++++ .../__tests__/chat-pane-header.test.tsx | 83 +++++++++++ .../components/__tests__/chat-search.test.tsx | 65 +++++++++ .../chats/components/__tests__/chat.test.tsx | 68 +++++++++ .../components/chat-message-list-intro.tsx | 1 + .../chats/components/chat-message-list.tsx | 10 +- .../chats/components/chat-pane-header.tsx | 6 +- .../features/chats/components/chat-search.tsx | 3 + .../features/chats/components/chat.tsx | 19 ++- .../components/placeholder-chat-message.tsx | 1 + 11 files changed, 387 insertions(+), 12 deletions(-) create mode 100644 app/soapbox/features/chats/components/__tests__/chat-message-list.test.tsx create mode 100644 app/soapbox/features/chats/components/__tests__/chat-pane-header.test.tsx create mode 100644 app/soapbox/features/chats/components/__tests__/chat-search.test.tsx create mode 100644 app/soapbox/features/chats/components/__tests__/chat.test.tsx diff --git a/app/soapbox/components/dropdown_menu.tsx b/app/soapbox/components/dropdown_menu.tsx index bb20c8622..a0543ce7e 100644 --- a/app/soapbox/components/dropdown_menu.tsx +++ b/app/soapbox/components/dropdown_menu.tsx @@ -216,7 +216,12 @@ class DropdownMenu extends React.PureComponent +
    {items.map((option, i) => this.renderItem(option, i))} diff --git a/app/soapbox/features/chats/components/__tests__/chat-message-list.test.tsx b/app/soapbox/features/chats/components/__tests__/chat-message-list.test.tsx new file mode 100644 index 000000000..2e41505ab --- /dev/null +++ b/app/soapbox/features/chats/components/__tests__/chat-message-list.test.tsx @@ -0,0 +1,136 @@ +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { ChatContext } from 'soapbox/contexts/chat-context'; +import { IAccount } from 'soapbox/queries/accounts'; + +import { __stub } from '../../../../api'; +import { queryClient, render, rootState, screen, waitFor } from '../../../../jest/test-helpers'; +import { IChat, IChatMessage } from '../../../../queries/chats'; +import ChatMessageList from '../chat-message-list'; + +const chat: IChat = { + id: '14', + unread: 5, + created_by_account: '2', + account: { + id: '1', + avatar: 'url', + acct: 'username', + } as IAccount, + last_message: null, + accepted: true, +} as IChat; + +const chatMessages: IChatMessage[] = [ + { + account_id: '1', + chat_id: '14', + content: 'this is the first chat', + created_at: new Date('2022-09-09T16:02:26.186Z'), + id: '1', + unread: false, + pending: false, + }, + { + account_id: '2', + chat_id: '14', + content: 'this is the second chat', + created_at: new Date('2022-09-09T16:04:26.186Z'), + id: '2', + unread: true, + pending: false, + }, +]; + +// Mock scrollIntoView function. +window.HTMLElement.prototype.scrollIntoView = function() { }; +Object.assign(navigator, { + clipboard: { + writeText: () => {}, + }, +}); + +const store = rootState.set('me', '1'); + +const renderComponentWithChatContext = () => render( + + + , + undefined, + store, +); + +beforeEach(() => { + queryClient.clear(); +}); + +describe('', () => { + describe('when the query is loading', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet(`/api/v1/pleroma/chats/${chat.id}/messages`).reply(200, chatMessages, { + link: null, + }); + }); + }); + + it('displays the skeleton loader', async() => { + renderComponentWithChatContext(); + + expect(screen.queryAllByTestId('placeholder-chat-message')).toHaveLength(5); + + await waitFor(() => { + expect(screen.getByTestId('chat-message-list-intro')).toBeInTheDocument(); + expect(screen.queryAllByTestId('placeholder-chat-message')).toHaveLength(0); + }); + }); + }); + + describe('when the query is finished loading', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet(`/api/v1/pleroma/chats/${chat.id}/messages`).reply(200, chatMessages, { + link: null, + }); + }); + }); + + it('displays the intro', async() => { + renderComponentWithChatContext(); + + expect(screen.queryAllByTestId('chat-message-list-intro')).toHaveLength(0); + + await waitFor(() => { + expect(screen.getByTestId('chat-message-list-intro')).toBeInTheDocument(); + }); + }); + + it('displays the messages', async() => { + renderComponentWithChatContext(); + + expect(screen.queryAllByTestId('chat-message')).toHaveLength(0); + + await waitFor(() => { + expect(screen.queryAllByTestId('chat-message')).toHaveLength(chatMessages.length); + expect(screen.queryAllByTestId('chat-message')[0]).toHaveTextContent(chatMessages[0].content); + }); + }); + + it('displays the correct menu options depending on the owner of the message', async() => { + renderComponentWithChatContext(); + + await waitFor(() => { + expect(screen.queryAllByTestId('chat-message-menu')).toHaveLength(2); + }); + + await userEvent.click(screen.queryAllByTestId('chat-message-menu')[0].querySelector('button') as any); + expect(screen.getByTestId('dropdown-menu')).toHaveTextContent('Delete'); + expect(screen.getByTestId('dropdown-menu')).toHaveTextContent('Copy'); + + await userEvent.click(screen.queryAllByTestId('chat-message-menu')[1].querySelector('button') as any); + expect(screen.getByTestId('dropdown-menu')).not.toHaveTextContent('Delete'); + expect(screen.getByTestId('dropdown-menu')).toHaveTextContent('Copy'); + }); + }); +}); diff --git a/app/soapbox/features/chats/components/__tests__/chat-pane-header.test.tsx b/app/soapbox/features/chats/components/__tests__/chat-pane-header.test.tsx new file mode 100644 index 000000000..32e5641ce --- /dev/null +++ b/app/soapbox/features/chats/components/__tests__/chat-pane-header.test.tsx @@ -0,0 +1,83 @@ +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { render, screen } from '../../../../jest/test-helpers'; +import ChatPaneHeader from '../chat-pane-header'; + +describe('', () => { + it('handles the onToggle prop', async() => { + const mockFn = jest.fn(); + render(); + + await userEvent.click(screen.getByTestId('icon-button')); + expect(mockFn).toHaveBeenCalled(); + }); + + describe('the "title" prop', () => { + describe('when it is a string', () => { + it('renders the title', () => { + const title = 'Messages'; + render(); + + expect(screen.getByTestId('title')).toHaveTextContent(title); + }); + }); + + describe('when it is a node', () => { + it('renders the title', () => { + const title = ( +

    hello world

    + ); + render(); + + expect(screen.getByTestId('title')).toHaveTextContent('hello world'); + }); + }); + }); + + describe('the "unreadCount" prop', () => { + describe('when present', () => { + it('renders the unread count', () => { + const count = 14; + render(); + + expect(screen.getByTestId('unread-count')).toHaveTextContent(String(count)); + }); + }); + + describe('when 0', () => { + it('does not render the unread count', () => { + const count = 0; + render(); + + expect(screen.queryAllByTestId('unread-count')).toHaveLength(0); + }); + }); + + describe('when unprovided', () => { + it('does not render the unread count', () => { + render(); + + expect(screen.queryAllByTestId('unread-count')).toHaveLength(0); + }); + }); + }); + + describe('secondaryAction prop', () => { + it('handles the secondaryAction callback', async() => { + const mockFn = jest.fn(); + render( + , + ); + + await userEvent.click(screen.queryAllByTestId('icon-button')[0]); + expect(mockFn).toBeCalled(); + }); + }); +}); diff --git a/app/soapbox/features/chats/components/__tests__/chat-search.test.tsx b/app/soapbox/features/chats/components/__tests__/chat-search.test.tsx new file mode 100644 index 000000000..4693e6e87 --- /dev/null +++ b/app/soapbox/features/chats/components/__tests__/chat-search.test.tsx @@ -0,0 +1,65 @@ +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { __stub } from 'soapbox/api'; +import { ChatProvider } from 'soapbox/contexts/chat-context'; + +import { render, screen, waitFor } from '../../../../jest/test-helpers'; +import ChatSearch from '../chat-search'; + +const renderComponent = () => render( + + + , +); + +describe('', () => { + it('renders correctly', () => { + renderComponent(); + + expect(screen.getByTestId('pane-header')).toHaveTextContent('Messages'); + }); + + describe('when the pane is closed', () => { + it('does not render the search input', () => { + renderComponent(); + expect(screen.queryAllByTestId('search')).toHaveLength(0); + }); + }); + + describe('when the pane is open', () => { + beforeEach(async() => { + renderComponent(); + await userEvent.click(screen.getByTestId('icon-button')); + }); + + it('renders the search input', () => { + expect(screen.getByTestId('search')).toBeInTheDocument(); + }); + + describe('when searching', () => { + beforeEach(() => { + __stub((mock) => { + mock.onGet('/api/v1/accounts/search').reply(200, [{ + id: '1', + avatar: 'url', + verified: false, + display_name: 'steve', + acct: 'sjobs', + }]); + }); + }); + + it('renders accounts', async() => { + renderComponent(); + + const user = userEvent.setup(); + await user.type(screen.getByTestId('search'), 'ste'); + + await waitFor(() => { + expect(screen.queryAllByTestId('account')).toHaveLength(1); + }); + }); + }); + }); +}); diff --git a/app/soapbox/features/chats/components/__tests__/chat.test.tsx b/app/soapbox/features/chats/components/__tests__/chat.test.tsx new file mode 100644 index 000000000..9a968d514 --- /dev/null +++ b/app/soapbox/features/chats/components/__tests__/chat.test.tsx @@ -0,0 +1,68 @@ +import React from 'react'; + +import { IChat } from 'soapbox/queries/chats'; + +import { render, screen } from '../../../../jest/test-helpers'; +import Chat from '../chat'; + +const chat: any = { + id: '1', + unread: 5, + created_by_account: '2', + last_message: { + account_id: '2', + chat_id: '1', + content: 'hello world', + created_at: '2022-09-09T16:02:26.186Z', + discarded_at: null, + id: '12332423234', + unread: true, + }, + created_at: new Date('2022-09-09T16:02:26.186Z'), + updated_at: new Date('2022-09-09T16:02:26.186Z'), + accepted: true, + discarded_at: null, + account: { + acct: 'username', + display_name: 'johnnie', + }, +}; + +describe('', () => { + it('renders correctly', () => { + render(); + + expect(screen.getByTestId('chat')).toBeInTheDocument(); + expect(screen.getByTestId('chat')).toHaveTextContent(chat.account.display_name); + }); + + describe('last message content', () => { + it('renders the last message', () => { + render(); + + expect(screen.getByTestId('chat-last-message')).toBeInTheDocument(); + }); + + it('does not render the last message', () => { + const changedChat = { ...chat, last_message: null }; + render(); + + expect(screen.queryAllByTestId('chat-last-message')).toHaveLength(0); + }); + + describe('unread', () => { + it('renders the unread dot', () => { + render(); + + expect(screen.getByTestId('chat-unread-indicator')).toBeInTheDocument(); + }); + + it('does not render the unread dot', () => { + const changedChat = { ...chat, last_message: { ...chat.last_message, unread: false } }; + render(); + + expect(screen.queryAllByTestId('chat-unread-indicator')).toHaveLength(0); + }); + }); + }); +}); diff --git a/app/soapbox/features/chats/components/chat-message-list-intro.tsx b/app/soapbox/features/chats/components/chat-message-list-intro.tsx index dd5f4a479..61d8ab7dd 100644 --- a/app/soapbox/features/chats/components/chat-message-list-intro.tsx +++ b/app/soapbox/features/chats/components/chat-message-list-intro.tsx @@ -37,6 +37,7 @@ const ChatMessageListIntro = () => { return ( = ({ chat, autosize }) => { const formattedChatMessages = chatMessages || []; const me = useAppSelector((state) => state.me); - const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account, 'blocked_by'])); + const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account.id, 'blocked_by'])); const node = useRef(null); const messagesEnd = useRef(null); @@ -240,7 +240,7 @@ const ChatMessageList: React.FC = ({ chat, autosize }) => { } return ( -
    +
    = ({ chat, autosize }) => { > {maybeRenderMedia(chatMessage)} -
    +
    = ({ chat, autosize }) => { return ( - + <> You are blocked by diff --git a/app/soapbox/features/chats/components/chat-pane-header.tsx b/app/soapbox/features/chats/components/chat-pane-header.tsx index a6330d5f6..5b2e17299 100644 --- a/app/soapbox/features/chats/components/chat-pane-header.tsx +++ b/app/soapbox/features/chats/components/chat-pane-header.tsx @@ -21,6 +21,7 @@ const ChatPaneHeader = (props: IChatPaneHeader) => { secondaryActionIcon, title, unreadCount, + ...rest } = props; const ButtonComp = isToggleable ? 'button' : 'div'; @@ -30,9 +31,10 @@ const ChatPaneHeader = (props: IChatPaneHeader) => { } return ( - + {typeof title === 'string' ? ( @@ -43,7 +45,7 @@ const ChatPaneHeader = (props: IChatPaneHeader) => { {(typeof unreadCount !== 'undefined' && unreadCount > 0) && ( - + ({unreadCount}) diff --git a/app/soapbox/features/chats/components/chat-search.tsx b/app/soapbox/features/chats/components/chat-search.tsx index 1f56a0ca2..2d91d9745 100644 --- a/app/soapbox/features/chats/components/chat-search.tsx +++ b/app/soapbox/features/chats/components/chat-search.tsx @@ -50,6 +50,7 @@ const ChatSearch = () => { return (