diff --git a/src/components/Message/__tests__/MessageText.test.js b/src/components/Message/__tests__/MessageText.test.js
index 364116232..3907ebd09 100644
--- a/src/components/Message/__tests__/MessageText.test.js
+++ b/src/components/Message/__tests__/MessageText.test.js
@@ -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';
@@ -84,6 +85,7 @@ async function renderMessageText(customProps, channelConfigOverrides = {}, rende
Emoji: EmojiComponentMock,
// eslint-disable-next-line react/display-name
Message: () => ,
+ reactionOptions: defaultReactionOptions,
}}
>
diff --git a/src/components/Reactions/ReactionsList.tsx b/src/components/Reactions/ReactionsList.tsx
index d1275763c..d1c7e5893 100644
--- a/src/components/Reactions/ReactionsList.tsx
+++ b/src/components/Reactions/ReactionsList.tsx
@@ -113,14 +113,13 @@ const UnMemoizedReactionsList = <
>
- {
-
-
-
- }
+
+
+
setTooltipReactionType(undefined)}
>
- {latestReactionTypes.map((reactionType, i) => {
+ {latestReactionTypes.map((reactionType) => {
const ReactionOption = getEmojiByReactionType(reactionType);
const isOwnReaction = iHaveReactedWithReaction(reactionType);
const tooltipVisible = tooltipReactionType === reactionType;
@@ -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)}
>
diff --git a/src/components/Reactions/__tests__/ReactionSelector.test.js b/src/components/Reactions/__tests__/ReactionSelector.test.js
index 16ae03166..1393df48c 100644
--- a/src/components/Reactions/__tests__/ReactionSelector.test.js
+++ b/src/components/Reactions/__tests__/ReactionSelector.test.js
@@ -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 }) => ),
@@ -45,18 +39,9 @@ const handleReactionMock = jest.fn();
const renderComponent = (props) =>
render(
-
+
-
-
-
+
,
);
@@ -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();
@@ -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);
diff --git a/src/components/Reactions/__tests__/ReactionsList.test.js b/src/components/Reactions/__tests__/ReactionsList.test.js
index 3a5c0365c..f51e263a6 100644
--- a/src/components/Reactions/__tests__/ReactionsList.test.js
+++ b/src/components/Reactions/__tests__/ReactionsList.test.js
@@ -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 }) => ),
-);
+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(
-
-
-
-
- ,
- ,
+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(
+
+
+ ,
+
+ ,
);
};
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: {
@@ -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();
});
@@ -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();
});
@@ -110,8 +116,8 @@ describe('ReactionsList', () => {
cowboy: 2,
};
const reactionOptions = [
- { emoji: '🍌', id: 'banana' },
- { emoji: '🤠', id: 'cowboy' },
+ { Component: () => <>🍌>, type: 'banana' },
+ { Component: () => <>🤠>, type: 'cowboy' },
];
expect(
diff --git a/src/components/Reactions/__tests__/SimpleReactionsList.test.js b/src/components/Reactions/__tests__/SimpleReactionsList.test.js
index e8ac915e4..db1815539 100644
--- a/src/components/Reactions/__tests__/SimpleReactionsList.test.js
+++ b/src/components/Reactions/__tests__/SimpleReactionsList.test.js
@@ -1,69 +1,54 @@
import React from 'react';
import { fireEvent, 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 * as utils from '../utils/utils';
import { SimpleReactionsList } from '../SimpleReactionsList';
+import { defaultReactionOptions } from '../reactionOptions';
import { ChatProvider } from '../../../context/ChatContext';
-import { EmojiProvider } from '../../../context/EmojiContext';
import { MessageProvider } from '../../../context/MessageContext';
-import { emojiComponentMock, emojiDataMock, generateReaction } from '../../../mock-builders';
+import { generateReaction } from '../../../mock-builders';
+import { ComponentProvider } from '../../../context';
jest.mock('emoji-mart/dist-modern/components/emoji/nimble-emoji', () =>
jest.fn(({ emoji }) => ),
);
const handleReactionMock = jest.fn();
-const loveEmojiTestId = 'emoji-love';
+// const loveEmojiTestId = 'emoji-love';
const renderComponent = ({ reaction_counts = {}, themeVersion = '1', ...props }) => {
- const reactions = Object.entries(reaction_counts)
- .map(([type, count]) =>
- Array(count)
- .fill()
- .map(() => generateReaction({ type })),
- )
- .flat();
+ const reactions = Object.entries(reaction_counts).flatMap(([type, count]) =>
+ Array(count)
+ .fill()
+ .map((_, i) => generateReaction({ type, user: { id: `${USER_ID}-${i}` } })),
+ );
return {
...render(
-
-
+
+
-
-
+
+
,
),
reactions,
};
};
-const expectEmojiToHaveBeenRendered = (id) => {
- expect(EmojiComponentMock).toHaveBeenCalledWith(
- expect.objectContaining({
- emoji: expect.objectContaining({ id }),
- }),
- {},
- );
-};
+const USER_ID = 'mark';
describe.each(['1', '2'])('SimpleReactionsList v%s', (themeVersion) => {
afterEach(jest.clearAllMocks);
@@ -81,7 +66,7 @@ describe.each(['1', '2'])('SimpleReactionsList v%s', (themeVersion) => {
it('should render the total reaction count', async () => {
const { container, getByText } = renderComponent({
reaction_counts: {
- angry: 2,
+ haha: 2,
love: 5,
},
themeVersion,
@@ -95,14 +80,16 @@ describe.each(['1', '2'])('SimpleReactionsList v%s', (themeVersion) => {
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, themeVersion });
+ // force to render default fallbacks
+ jest.spyOn(utils, 'getImageDimensions').mockRejectedValue('Error');
+ jest.spyOn(console, 'error').mockImplementation(null);
- expect(EmojiComponentMock).toHaveBeenCalledTimes(Object.keys(reaction_counts).length);
+ const { container } = renderComponent({ reaction_counts, themeVersion });
- Object.keys(reaction_counts).forEach(expectEmojiToHaveBeenRendered);
+ expect(container).toMatchSnapshot();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
@@ -122,9 +109,7 @@ describe.each(['1', '2'])('SimpleReactionsList v%s', (themeVersion) => {
themeVersion,
});
- expect(EmojiComponentMock).toHaveBeenCalledTimes(Object.keys(reaction_counts).length);
-
- Object.keys(reaction_counts).forEach(expectEmojiToHaveBeenRendered);
+ expect(container).toMatchSnapshot();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
@@ -134,9 +119,9 @@ describe.each(['1', '2'])('SimpleReactionsList v%s', (themeVersion) => {
love: 1,
};
- const { container, getByTestId } = renderComponent({ reaction_counts, themeVersion });
+ const { container, getByText } = renderComponent({ reaction_counts, themeVersion });
- fireEvent.click(getByTestId(loveEmojiTestId));
+ fireEvent.click(getByText('❤️'));
expect(handleReactionMock).toHaveBeenCalledWith('love', expect.any(Object));
const results = await axe(container);
@@ -148,18 +133,18 @@ describe.each(['1', '2'])('SimpleReactionsList v%s', (themeVersion) => {
love: 3,
};
- const { container, getByTestId, queryByText, reactions } = renderComponent({
+ const { container, getByText, queryByText, reactions } = renderComponent({
reaction_counts,
themeVersion,
});
- fireEvent.mouseEnter(getByTestId(loveEmojiTestId));
+ fireEvent.mouseEnter(getByText('❤️'));
reactions.forEach(({ user }) => {
expect(queryByText(user.name || user.id, { exact: false })).toBeInTheDocument();
});
- fireEvent.mouseLeave(getByTestId(loveEmojiTestId));
+ fireEvent.mouseLeave(getByText('❤️'));
reactions.forEach(({ user }) => {
expect(queryByText(user.id, { exact: false })).not.toBeInTheDocument();
diff --git a/src/components/Reactions/__tests__/__snapshots__/SimpleReactionsList.test.js.snap b/src/components/Reactions/__tests__/__snapshots__/SimpleReactionsList.test.js.snap
new file mode 100644
index 000000000..a01ea96c6
--- /dev/null
+++ b/src/components/Reactions/__tests__/__snapshots__/SimpleReactionsList.test.js.snap
@@ -0,0 +1,75 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SimpleReactionsList v1 should handle custom reaction options 1`] = ``;
+
+exports[`SimpleReactionsList v1 should render an emoji for each type of reaction 1`] = `
+
+
+
+ -
+
+ 😂
+
+
+
+ -
+
+ ❤️
+
+
+
+ -
+ 7
+
+
+
+
+`;
+
+exports[`SimpleReactionsList v2 should handle custom reaction options 1`] = ``;
+
+exports[`SimpleReactionsList v2 should render an emoji for each type of reaction 1`] = `
+
+
+
+ -
+
+ 😂
+
+
+
+ -
+
+ ❤️
+
+
+
+ -
+ 7
+
+
+
+
+`;