Skip to content

Commit cffb7d5

Browse files
Add tests
1 parent c598a70 commit cffb7d5

7 files changed

Lines changed: 453 additions & 114 deletions

File tree

src/components/Reactions/MessageReactions.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { MAX_MESSAGE_REACTIONS_TO_FETCH } from '../Message/hooks';
2121
import type { ReactionGroupResponse, ReactionResponse } from 'stream-chat';
2222
import type { ReactionsComparator, ReactionType } from './types';
2323
import { DialogAnchor, useDialogIsOpen, useDialogOnNearestManager } from '../Dialog';
24-
import { ReactionSelector } from './ReactionSelector';
2524

2625
export type MessageReactionsProps = Partial<
2726
Pick<MessageContextValue, 'handleFetchReactions' | 'reactionDetailsSort'>
@@ -160,6 +159,7 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
160159
aria-pressed={isDialogOpen}
161160
buttonIf={visualStyle === 'clustered'}
162161
className='str-chat__message-reactions__list-button'
162+
data-testid='message-reactions-list-button'
163163
onClick={() => handleReactionButtonClick(null)}
164164
>
165165
<ul className='str-chat__message-reactions__list'>
@@ -168,14 +168,18 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
168168
EmojiComponent && (
169169
<li
170170
className='str-chat__message-reactions__list-item'
171+
data-testid='message-reactions-list-item'
171172
key={reactionType}
172173
>
173174
<FragmentOrButton
174175
buttonIf={visualStyle === 'segmented'}
175176
className='str-chat__message-reactions__list-item-button'
176177
onClick={() => handleReactionButtonClick(reactionType)}
177178
>
178-
<span className='str-chat__message-reactions__list-item-icon'>
179+
<span
180+
className='str-chat__message-reactions__list-item-icon'
181+
data-testid='message-reactions-list-item-icon'
182+
>
179183
<EmojiComponent />
180184
</span>
181185
{visualStyle === 'segmented' && reactionCount > 1 && (
@@ -208,7 +212,10 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
208212
)}
209213
</ul>
210214
{visualStyle === 'clustered' && (
211-
<span className='str-chat__message-reactions__total-count'>
215+
<span
216+
className='str-chat__message-reactions__total-count'
217+
data-testid='message-reactions-total-count'
218+
>
212219
{totalReactionCount}
213220
</span>
214221
)}

src/components/Reactions/MessageReactionsDetail.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,15 @@ export const MessageReactionsDetail: MessageReactionsDetailInterface = ({
118118
</div>
119119
)}
120120
<div className='str-chat__message-reactions-detail__reaction-type-list-container'>
121-
<ul className='str-chat__message-reactions-detail__reaction-type-list'>
121+
<ul
122+
className='str-chat__message-reactions-detail__reaction-type-list'
123+
data-testid='reaction-type-list'
124+
>
122125
<li className='str-chat__message-reactions-detail__reaction-type-list-item'>
123126
<button
127+
aria-label={t('Add reaction')}
124128
className='str-chat__message-reactions-detail__reaction-type-list-item-button'
129+
data-testid='add-reaction-button'
125130
onClick={() => setExtendedReactionListOpen(true)}
126131
>
127132
<span className='str-chat__message-reactions-detail__reaction-type-list-item-icon'>
@@ -151,7 +156,7 @@ export const MessageReactionsDetail: MessageReactionsDetailInterface = ({
151156
</span>
152157
<span
153158
className='str-chat__message-reactions-detail__reaction-type-list-item-count'
154-
data-testclass='message-reactions-item-count'
159+
data-testid='reaction-type-count'
155160
>
156161
{reactionCount}
157162
</span>

src/components/Reactions/ReactionSelector.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ export const ReactionSelector: ReactionSelectorInterface = (props) => {
8080
<div className='str-chat__reaction-selector' data-testid='reaction-selector'>
8181
{!extendedListOpen && (
8282
<>
83-
<ul className='str-chat__reaction-selector-list'>
83+
<ul
84+
className='str-chat__reaction-selector-list'
85+
data-testid='reaction-selector-list'
86+
>
8487
{adjustedQuickReactionOptions.map(
8588
({ Component, name: reactionName, type: reactionType }) => (
8689
<li className='str-chat__reaction-list-selector__item' key={reactionType}>
@@ -109,6 +112,7 @@ export const ReactionSelector: ReactionSelectorInterface = (props) => {
109112
appearance='outline'
110113
circular
111114
className='str-chat__reaction-selector__add-button'
115+
data-testid='reaction-selector-add-button'
112116
onClick={() => setExtendedListOpen(true)}
113117
size='sm'
114118
variant='secondary'
@@ -165,7 +169,10 @@ ReactionSelector.ExtendedList = function ReactionSelectorExtendedList({
165169
}
166170

167171
return (
168-
<div className='str-chat__reaction-selector-extended-list'>
172+
<div
173+
className='str-chat__reaction-selector-extended-list'
174+
data-testid='reaction-selector-extended-list'
175+
>
169176
{Object.entries(reactionOptions.extended).map(
170177
([reactionType, { Component, name: reactionName }]) => (
171178
<button

src/components/Reactions/__tests__/MessageReactions.test.tsx

Lines changed: 46 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
11
import React from 'react';
22
import { render } from '@testing-library/react';
3+
import { fromPartial } from '@total-typescript/shoehorn';
34

45
import { axe } from '../../../../axe-helper';
56

6-
import { MessageReactions } from '../MessageReactions';
7+
import { MessageReactions, type MessageReactionsProps } from '../MessageReactions';
78
import { MessageProvider } from '../../../context/MessageContext';
89

910
import { generateReaction, mockMessageContext } from '../../../mock-builders';
1011
import { DialogManagerProvider, WithComponents } from '../../../context';
11-
import { defaultReactionOptions } from '../reactionOptions';
12+
import { defaultReactionOptions, type ReactionOptions } from '../reactionOptions';
13+
14+
import type { ReactionGroupResponse } from 'stream-chat';
15+
import type { ReactionsComparator } from '../types';
1216

1317
const USER_ID = 'mark';
1418

1519
const renderComponent = ({
1620
reaction_groups = {},
1721
reactionOptions = defaultReactionOptions,
1822
...props
19-
}: any = {}) => {
20-
const reactions = Object.entries(reaction_groups).flatMap(([type, { count }]: any) =>
23+
}: {
24+
reaction_groups?: Record<string, ReactionGroupResponse>;
25+
reactionOptions?: ReactionOptions;
26+
sortReactions?: ReactionsComparator;
27+
} & Partial<Omit<MessageReactionsProps, 'reaction_groups' | 'reactions'>> = {}) => {
28+
const reactions = Object.entries(reaction_groups).flatMap(([type, { count }]) =>
2129
Array.from({ length: count }, (_, i) =>
2230
generateReaction({ type, user: { id: `${USER_ID}-${i}` } }),
2331
),
@@ -50,13 +58,13 @@ describe('MessageReactions', () => {
5058
vi.spyOn(console, 'warn').mockImplementation(null);
5159

5260
it('should render the total reaction count in clustered mode', async () => {
53-
const { container } = renderComponent({
61+
const { container, getByTestId } = renderComponent({
5462
reaction_groups: {
55-
haha: { count: 2 },
56-
love: { count: 5 },
63+
haha: fromPartial<ReactionGroupResponse>({ count: 2 }),
64+
love: fromPartial<ReactionGroupResponse>({ count: 5 }),
5765
},
5866
});
59-
const countEl = container.querySelector('.str-chat__message-reactions__total-count');
67+
const countEl = getByTestId('message-reactions-total-count');
6068
expect(countEl).toBeInTheDocument();
6169
expect(countEl).toHaveTextContent('7');
6270

@@ -73,66 +81,59 @@ describe('MessageReactions', () => {
7381
});
7482

7583
it('should render an emoji for each type of reaction', async () => {
76-
const reaction_groups = {
77-
haha: { count: 2 },
78-
love: { count: 5 },
79-
};
80-
81-
const { container } = renderComponent({ reaction_groups });
84+
const { container, getAllByTestId } = renderComponent({
85+
reaction_groups: {
86+
haha: fromPartial<ReactionGroupResponse>({ count: 2 }),
87+
love: fromPartial<ReactionGroupResponse>({ count: 5 }),
88+
},
89+
});
8290

83-
const listItems = container.querySelectorAll(
84-
'.str-chat__message-reactions__list-item',
85-
);
91+
const listItems = getAllByTestId('message-reactions-list-item');
8692
expect(listItems).toHaveLength(2);
8793

8894
const results = await axe(container);
8995
expect(results).toHaveNoViolations();
9096
});
9197

9298
it('should handle custom reaction options', async () => {
93-
const reaction_groups = {
94-
banana: { count: 1 },
95-
cowboy: { count: 2 },
96-
};
97-
98-
const { container } = renderComponent({
99-
reaction_groups,
99+
const { container, getAllByTestId } = renderComponent({
100+
reaction_groups: {
101+
banana: fromPartial<ReactionGroupResponse>({ count: 1 }),
102+
cowboy: fromPartial<ReactionGroupResponse>({ count: 2 }),
103+
},
100104
reactionOptions: [
101105
{ Component: () => <>🍌</>, type: 'banana' },
102106
{ Component: () => <>🤠</>, type: 'cowboy' },
103107
],
104108
});
105109

106-
const listItems = container.querySelectorAll(
107-
'.str-chat__message-reactions__list-item',
108-
);
110+
const listItems = getAllByTestId('message-reactions-list-item');
109111
expect(listItems).toHaveLength(2);
110112

111113
const results = await axe(container);
112114
expect(results).toHaveNoViolations();
113115
});
114116

115117
it('should order reactions by first reaction timestamp by default', () => {
116-
const { container } = renderComponent({
118+
const { getAllByTestId } = renderComponent({
117119
reaction_groups: {
118-
haha: { count: 2, first_reaction_at: new Date().toISOString() },
119-
like: {
120+
haha: fromPartial<ReactionGroupResponse>({
121+
count: 2,
122+
first_reaction_at: new Date().toISOString(),
123+
}),
124+
like: fromPartial<ReactionGroupResponse>({
120125
count: 8,
121126
first_reaction_at: new Date(Date.now() + 60_000).toISOString(),
122-
},
123-
love: {
127+
}),
128+
love: fromPartial<ReactionGroupResponse>({
124129
count: 5,
125130
first_reaction_at: new Date(Date.now() + 120_000).toISOString(),
126-
},
131+
}),
127132
},
128133
});
129134

130-
const listItems = container.querySelectorAll(
131-
'.str-chat__message-reactions__list-item',
132-
);
133-
const icons = Array.from(listItems).map(
134-
(item) =>
135-
item.querySelector('.str-chat__message-reactions__list-item-icon')?.textContent,
135+
const icons = getAllByTestId('message-reactions-list-item-icon').map(
136+
(el) => el.textContent,
136137
);
137138

138139
// haha (😂) should come first, then like (👍), then love (❤️)
@@ -142,21 +143,17 @@ describe('MessageReactions', () => {
142143
});
143144

144145
it('should use custom comparator if provided', () => {
145-
const { container } = renderComponent({
146+
const { getAllByTestId } = renderComponent({
146147
reaction_groups: {
147-
haha: { count: 2 },
148-
like: { count: 8 },
149-
love: { count: 5 },
148+
haha: fromPartial<ReactionGroupResponse>({ count: 2 }),
149+
like: fromPartial<ReactionGroupResponse>({ count: 8 }),
150+
love: fromPartial<ReactionGroupResponse>({ count: 5 }),
150151
},
151152
sortReactions: (a, b) => b.reactionCount - a.reactionCount,
152153
});
153154

154-
const listItems = container.querySelectorAll(
155-
'.str-chat__message-reactions__list-item',
156-
);
157-
const icons = Array.from(listItems).map(
158-
(item) =>
159-
item.querySelector('.str-chat__message-reactions__list-item-icon')?.textContent,
155+
const icons = getAllByTestId('message-reactions-list-item-icon').map(
156+
(el) => el.textContent,
160157
);
161158

162159
// like (8) > love (5) > haha (2)

0 commit comments

Comments
 (0)