Skip to content

Commit

Permalink
test(Reactions): adjust tests to fit new implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
arnautov-anton committed Feb 23, 2023
1 parent b210ae6 commit 9ce1535
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 131 deletions.
2 changes: 2 additions & 0 deletions src/components/Message/__tests__/MessageText.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from '../../../mock-builders';

import { Attachment } from '../../Attachment';
import { defaultReactionOptions } from '../../Reactions';
import { Message } from '../Message';
import { MessageOptions as MessageOptionsMock } from '../MessageOptions';
import { MessageSimple } from '../MessageSimple';
Expand Down Expand Up @@ -84,6 +85,7 @@ async function renderMessageText(customProps, channelConfigOverrides = {}, rende
Emoji: EmojiComponentMock,
// eslint-disable-next-line react/display-name
Message: () => <MessageSimple channelConfig={channelConfig} />,
reactionOptions: defaultReactionOptions,
}}
>
<EmojiProvider value={{ emojiConfig: emojiDataMock }}>
Expand Down
9 changes: 4 additions & 5 deletions src/components/Reactions/ReactionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,13 @@ const UnMemoizedReactionsList = <
>
<ButtonWithTooltip
aria-label={`Reactions: ${reactionType}`}
data-testid={`reactions-list-button-${reactionType}`}
title={aggregatedUserNamesByType[reactionType].join(', ')}
type='button'
>
{
<span className='str-chat__message-reaction-emoji'>
<ReactionOption.Component />
</span>
}
<span className='str-chat__message-reaction-emoji'>
<ReactionOption.Component />
</span>
&nbsp;
<span
className='str-chat__message-reaction-count'
Expand Down
4 changes: 2 additions & 2 deletions src/components/Reactions/SimpleReactionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const UnMemoizedSimpleReactionsList = <
data-testid='simple-reaction-list'
onMouseLeave={() => setTooltipReactionType(undefined)}
>
{latestReactionTypes.map((reactionType, i) => {
{latestReactionTypes.map((reactionType) => {
const ReactionOption = getEmojiByReactionType(reactionType);
const isOwnReaction = iHaveReactedWithReaction(reactionType);
const tooltipVisible = tooltipReactionType === reactionType;
Expand All @@ -121,7 +121,7 @@ const UnMemoizedSimpleReactionsList = <
className={clsx('str-chat__simple-reactions-list-item', {
'str-chat__message-reaction-own': isOwnReaction,
})}
key={`${reactionType}-${i}`}
key={reactionType}
onClick={(event) => handleReaction(reactionType, event)}
onKeyUp={(event) => handleReaction(reactionType, event)}
>
Expand Down
67 changes: 33 additions & 34 deletions src/components/Reactions/__tests__/ReactionSelector.test.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import { fireEvent, render, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import EmojiComponentMock from 'emoji-mart/dist-modern/components/emoji/nimble-emoji';
import { toHaveNoViolations } from 'jest-axe';
import { axe } from '../../../../axe-helper';
expect.extend(toHaveNoViolations);

import { ReactionSelector } from '../ReactionSelector';
import { defaultReactionOptions } from '../reactionOptions';
import * as utils from '../utils/utils';

import { Avatar as AvatarMock } from '../../Avatar';
import { defaultMinimalEmojis } from '../../Channel/emojiData';

import { ComponentProvider } from '../../../context/ComponentContext';
import { EmojiProvider } from '../../../context/EmojiContext';
import { MessageProvider } from '../../../context/MessageContext';

import {
emojiComponentMock,
emojiDataMock,
generateReaction,
generateUser,
} from '../../../mock-builders';
import { generateReaction, generateUser } from '../../../mock-builders';

jest.mock('emoji-mart/dist-modern/components/emoji/nimble-emoji', () =>
jest.fn(({ emoji }) => <div data-testid={`emoji-${emoji.id}`} />),
Expand All @@ -45,18 +39,9 @@ const handleReactionMock = jest.fn();

const renderComponent = (props) =>
render(
<ComponentProvider value={{ Avatar: AvatarMock }}>
<ComponentProvider value={{ Avatar: AvatarMock, reactionOptions: defaultReactionOptions }}>
<MessageProvider value={{}}>
<EmojiProvider
value={{
Emoji: emojiComponentMock.Emoji,
emojiConfig: emojiDataMock,
EmojiIndex: emojiComponentMock.EmojiIndex,
EmojiPicker: emojiComponentMock.EmojiPicker,
}}
>
<ReactionSelector handleReaction={handleReactionMock} {...props} />
</EmojiProvider>
<ReactionSelector handleReaction={handleReactionMock} {...props} />
</MessageProvider>
</ComponentProvider>,
);
Expand All @@ -66,28 +51,42 @@ describe('ReactionSelector', () => {
jest.clearAllMocks();
});

it('should render each of default emojis if reactionOptions prop is not specified', async () => {
const { container } = renderComponent();
it('should render each of default emojis from image sprite', async () => {
jest.spyOn(utils, 'getImageDimensions').mockResolvedValue([128, 192]);

defaultMinimalEmojis.forEach((emoji) => {
expect(EmojiComponentMock).toHaveBeenCalledWith(expect.objectContaining({ emoji }), {});
const { container, queryAllByTestId } = renderComponent();

await waitFor(() => {
expect(queryAllByTestId('sprite-image')).toHaveLength(6);
});

const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should render fallbacks of each of the default emojis if sprite did not load', async () => {
jest.spyOn(utils, 'getImageDimensions').mockRejectedValue('Error');
jest.spyOn(console, 'error').mockImplementation(null);

const { container, getByText } = renderComponent();

await waitFor(() => {
expect(getByText('❀️')).toBeInTheDocument();
});

const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should render each of reactionOptions if specified', async () => {
const reactionOptions = [
{ emoji: 'angry', id: 'angry' },
{ emoji: 'banana', id: 'banana' },
{ Component: jest.fn(() => null), type: 'test1' },
{ Component: jest.fn(() => null), type: 'test2' },
];
const { container } = renderComponent({ reactionOptions });

reactionOptions.forEach((emoji) => {
expect(EmojiComponentMock).toHaveBeenCalledWith(
expect.objectContaining({ emoji: expect.objectContaining(emoji) }),
{},
);
reactionOptions.forEach((option) => {
expect(option.Component).toHaveBeenCalledTimes(1);
});
const results = await axe(container);
expect(results).toHaveNoViolations();
Expand Down Expand Up @@ -181,9 +180,9 @@ describe('ReactionSelector', () => {
});

it('should call handleReaction if an emoji is clicked', async () => {
const { container, getByTestId } = renderComponent();
const { container, getByText } = renderComponent();

const emoji = getByTestId('emoji-love');
const emoji = getByText('❀️');

fireEvent.click(emoji);

Expand Down
100 changes: 53 additions & 47 deletions src/components/Reactions/__tests__/ReactionsList.test.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,47 @@
/* eslint-disable react/display-name */
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import EmojiComponentMock from 'emoji-mart/dist-modern/components/emoji/nimble-emoji';
import { toHaveNoViolations } from 'jest-axe';

import { axe } from '../../../../axe-helper';
expect.extend(toHaveNoViolations);

import { ReactionsList } from '../ReactionsList';

import { EmojiProvider } from '../../../context/EmojiContext';
import * as utils from '../utils/utils';
import { MessageProvider } from '../../../context/MessageContext';

import { emojiComponentMock, emojiDataMock, generateReaction } from '../../../mock-builders';
import { generateReaction } from '../../../mock-builders';
import { ComponentProvider } from '../../../context';
import { defaultReactionOptions } from '../reactionOptions';

jest.mock('emoji-mart/dist-modern/components/emoji/nimble-emoji', () =>
jest.fn(({ emoji }) => <div data-testid={`emoji-${emoji.id}`} />),
);
const USER_ID = 'mark';

const renderComponent = ({ reaction_counts = {}, ...props }) => {
const reactions = Object.entries(reaction_counts)
.map(([type, count]) =>
Array(count)
.fill()
.map(() => generateReaction({ type })),
)
.flat();
const generateButtonTitle = (count) =>
Array.from({ length: count }, (_, i) => `${USER_ID}-${i}`).join(', ');

return render(
<MessageProvider value={{}}>
<EmojiProvider
value={{
Emoji: emojiComponentMock.Emoji,
emojiConfig: emojiDataMock,
EmojiIndex: emojiComponentMock.EmojiIndex,
EmojiPicker: emojiComponentMock.EmojiPicker,
}}
>
<ReactionsList reaction_counts={reaction_counts} reactions={reactions} {...props} />
</EmojiProvider>
,
</MessageProvider>,
const renderComponent = ({ reaction_counts = {}, ...props }) => {
const reactions = Object.entries(reaction_counts).flatMap(([type, count]) =>
Array(count)
.fill()
.map((_, i) => generateReaction({ type, user: { id: `${USER_ID}-${i}` } })),
);
};

const expectEmojiToHaveBeenRendered = (id) => {
expect(EmojiComponentMock).toHaveBeenCalledWith(
expect.objectContaining({
emoji: expect.objectContaining({ id }),
}),
{},
return render(
<ComponentProvider value={{ reactionOptions: defaultReactionOptions }}>
<MessageProvider value={{}}>
<ReactionsList reaction_counts={reaction_counts} reactions={reactions} {...props} />,
</MessageProvider>
</ComponentProvider>,
);
};

describe('ReactionsList', () => {
afterEach(jest.clearAllMocks);

// disable warnings (unreachable context)
jest.spyOn(console, 'warn').mockImplementation(null);

it('should render the total reaction count', async () => {
const { container, getByText } = renderComponent({
reaction_counts: {
Expand All @@ -71,14 +58,26 @@ describe('ReactionsList', () => {

it('should render an emoji for each type of reaction', async () => {
const reaction_counts = {
angry: 2,
haha: 2,
love: 5,
};
const { container } = renderComponent({ reaction_counts });
// make sure to render default fallbacks
jest.spyOn(utils, 'getImageDimensions').mockRejectedValue('Error');
jest.spyOn(console, 'error').mockImplementation(null);

const { container, getByTestId } = renderComponent({ reaction_counts });

expect(EmojiComponentMock).toHaveBeenCalledTimes(Object.keys(reaction_counts).length);
const hahaButton = getByTestId('reactions-list-button-haha');
const loveButton = getByTestId('reactions-list-button-love');

expect(hahaButton).toHaveAttribute('title', generateButtonTitle(reaction_counts['haha']));
expect(hahaButton.lastChild).toHaveTextContent(reaction_counts['haha']);
expect(hahaButton.firstChild).toHaveTextContent('πŸ˜‚');

expect(loveButton).toHaveAttribute('title', generateButtonTitle(reaction_counts['love']));
expect(loveButton.lastChild).toHaveTextContent(reaction_counts['love']);
expect(loveButton.firstChild).toHaveTextContent('❀️');

Object.keys(reaction_counts).forEach(expectEmojiToHaveBeenRendered);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Expand All @@ -89,17 +88,24 @@ describe('ReactionsList', () => {
cowboy: 2,
};

const { container } = renderComponent({
const { container, getByTestId } = renderComponent({
reaction_counts,
reactionOptions: [
{ emoji: '🍌', id: 'banana' },
{ emoji: '🀠', id: 'cowboy' },
{ Component: () => <>🍌</>, type: 'banana' },
{ Component: () => <>🀠</>, type: 'cowboy' },
],
});

expect(EmojiComponentMock).toHaveBeenCalledTimes(Object.keys(reaction_counts).length);
const bananaButton = getByTestId('reactions-list-button-banana');
const cowboyButton = getByTestId('reactions-list-button-cowboy');

expect(bananaButton).toHaveAttribute('title', generateButtonTitle(reaction_counts['banana']));
expect(bananaButton.lastChild).toHaveTextContent(reaction_counts['banana']);
expect(bananaButton.firstChild).toHaveTextContent('🍌');
expect(cowboyButton).toHaveAttribute('title', generateButtonTitle(reaction_counts['cowboy']));
expect(cowboyButton.lastChild).toHaveTextContent(reaction_counts['cowboy']);
expect(cowboyButton.firstChild).toHaveTextContent('🀠');

Object.keys(reaction_counts).forEach(expectEmojiToHaveBeenRendered);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Expand All @@ -110,8 +116,8 @@ describe('ReactionsList', () => {
cowboy: 2,
};
const reactionOptions = [
{ emoji: '🍌', id: 'banana' },
{ emoji: '🀠', id: 'cowboy' },
{ Component: () => <>🍌</>, type: 'banana' },
{ Component: () => <>🀠</>, type: 'cowboy' },
];

expect(
Expand Down
Loading

0 comments on commit 9ce1535

Please sign in to comment.