@linktr.ee/messaging-react 4.2.0-rc-1787325521 → 4.2.0-rc-1787599724
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Card-BYJyA83X.js → Card-D7UPq5-g.js} +2 -2
- package/dist/{Card-BYJyA83X.js.map → Card-D7UPq5-g.js.map} +1 -1
- package/dist/{Card-BplJtZao.cjs → Card-DcBZqMBt.cjs} +2 -2
- package/dist/{Card-BplJtZao.cjs.map → Card-DcBZqMBt.cjs.map} +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/{index-BUC9vqhJ.js → index-CSkr_CO5.js} +2089 -1952
- package/dist/index-CSkr_CO5.js.map +1 -0
- package/dist/index-CwkY7QwT.cjs +3 -0
- package/dist/index-CwkY7QwT.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +59 -78
- package/dist/index.js +26 -30
- package/dist/testing.cjs +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/package.json +13 -11
- package/src/components/AttachmentCard/utils/icons.ts +7 -6
- package/src/components/Avatar/Avatar.test.tsx +0 -126
- package/src/components/Avatar/getAvatarEmoji.test.ts +0 -39
- package/src/components/ChannelList/CustomChannelPreview.test.tsx +3 -410
- package/src/components/ChannelList/index.test.tsx +1 -132
- package/src/components/ChannelView.stories.tsx +0 -4
- package/src/components/ChannelView.test.tsx +3 -9
- package/src/components/CustomMessage/CustomMessage.stories.tsx +314 -6
- package/src/components/CustomMessage/CustomMessage.test.tsx +2 -56
- package/src/components/CustomMessage/MessageTag.stories.tsx +53 -17
- package/src/components/CustomMessage/MessageTag.test.tsx +40 -7
- package/src/components/CustomMessage/MessageTag.tsx +39 -7
- package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +7 -8
- package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +1 -2
- package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +33 -0
- package/src/components/CustomMessage/StreamAttachmentMessage.tsx +12 -5
- package/src/components/CustomMessage/index.tsx +19 -22
- package/src/components/CustomMessageInput/index.tsx +1 -1
- package/src/components/CustomSystemMessage/index.tsx +2 -3
- package/src/components/LinkAttachment/LinkAttachment.test.tsx +0 -327
- package/src/components/MessageAttachment/Audio/AudioAttachment.stories.tsx +165 -58
- package/src/components/MessageAttachment/Audio/WaveformAudioRow.test.tsx +153 -0
- package/src/components/MessageAttachment/Audio/WaveformAudioRow.tsx +450 -0
- package/src/components/MessageAttachment/Audio/audioRowRequester.test.ts +24 -0
- package/src/components/MessageAttachment/Audio/audioRowRequester.ts +16 -0
- package/src/components/MessageAttachment/Audio/index.tsx +83 -77
- package/src/components/MessageAttachment/MessageAttachment.test.tsx +176 -74
- package/src/components/MessageAttachment/_shared/fileMeta.test.ts +0 -44
- package/src/components/MessageAttachment/_shared/useIsClient.ts +23 -0
- package/src/components/MessageAttachment/index.tsx +25 -9
- package/src/components/MessageAttachment/types.ts +19 -9
- package/src/components/MessagingShell/MessagingShell.test.tsx +0 -20
- package/src/components/MessagingShell/index.tsx +0 -2
- package/src/components/RichCard/RichCard.test.tsx +1 -3
- package/src/index.ts +0 -4
- package/src/providers/MessagingProvider.test.tsx +2 -130
- package/src/providers/MessagingProvider.tsx +15 -26
- package/src/styles.css +210 -57
- package/src/testing/createMockMessagingClient.ts +2 -2
- package/src/types.ts +0 -9
- package/dist/index-BUC9vqhJ.js.map +0 -1
- package/dist/index-BkqyKPJP.cjs +0 -3
- package/dist/index-BkqyKPJP.cjs.map +0 -1
- package/src/components/AttachmentCard/MediaPlayer.test.tsx +0 -346
- package/src/components/AttachmentCard/Thumbnail.test.tsx +0 -215
- package/src/components/AttachmentCard/index.test.tsx +0 -160
- package/src/components/ChannelList/ChannelListContext.test.tsx +0 -87
- package/src/components/CustomMessage/Locked.stories.tsx +0 -108
- package/src/components/CustomMessage/PaidMessage.stories.tsx +0 -42
- package/src/components/CustomMessage/Tip.stories.tsx +0 -48
- package/src/components/CustomMessage/TipMessage/TipMessage.stories.tsx +0 -51
- package/src/components/CustomMessage/TipMessage/TipMessage.test.tsx +0 -65
- package/src/components/CustomMessage/TipMessage/index.tsx +0 -27
- package/src/components/FaqList/FaqList.test.tsx +0 -204
- package/src/components/LinkAttachment/components/_shared/BubbleTail.test.tsx +0 -76
- package/src/components/LinkAttachment/components/_shared/CardBody.test.tsx +0 -248
- package/src/components/LinkAttachment/components/_shared/CardCta.test.tsx +0 -123
- package/src/components/LinkAttachment/components/_shared/CardShell.test.tsx +0 -193
- package/src/components/LinkAttachment/components/_shared/CardThumbnail.test.tsx +0 -242
- package/src/components/LinkAttachment/components/_shared/hex.test.ts +0 -37
- package/src/components/LinkAttachment/components/_shared/normalizeExternalHref.test.ts +0 -72
- package/src/components/LinkAttachment/components/_shared/useChinPalette.test.ts +0 -162
- package/src/components/MessageAttachment/MessageAttachment.behavior.test.tsx +0 -182
- package/src/components/MessageAttachment/_shared/Bubble.test.tsx +0 -111
- package/src/components/MessageAttachment/_shared/CarouselNav.test.tsx +0 -36
- package/src/components/MessageAttachment/_shared/CompactDocumentRow.test.tsx +0 -114
- package/src/components/MessageAttachment/_shared/DismissButton.test.tsx +0 -42
- package/src/components/MessageAttachment/_shared/DownloadAction.test.tsx +0 -168
- package/src/components/MessageAttachment/_shared/ImageViewer.test.tsx +0 -52
- package/src/components/MessageAttachment/_shared/MediaStackGrid.test.tsx +0 -69
- package/src/components/MessageAttachment/_shared/PdfViewer.test.tsx +0 -45
- package/src/components/MessageAttachment/_shared/VideoViewer.test.tsx +0 -55
- package/src/components/MessageAttachment/_shared/ViewerShell.test.tsx +0 -246
- package/src/components/MessageAttachment/_shared/triggerDownload.test.ts +0 -113
- package/src/components/MessageAttachment/_shared/useCarousel.test.tsx +0 -68
- package/src/components/MessageAttachment/_shared/viewerChromeClasses.test.ts +0 -16
- package/src/logging/index.test.tsx +0 -136
- package/src/logging/index.tsx +0 -48
- package/src/stories/customMessageStoryTemplate.tsx +0 -202
|
@@ -27,130 +27,4 @@ describe('Avatar', () => {
|
|
|
27
27
|
it('keeps fixed emoji sizes for larger avatars', () => {
|
|
28
28
|
expect(emojiFontSize(40)).toHaveStyle({ fontSize: '14px' })
|
|
29
29
|
})
|
|
30
|
-
|
|
31
|
-
it.each([
|
|
32
|
-
[32, '14px'],
|
|
33
|
-
[55, '14px'],
|
|
34
|
-
[56, '18px'],
|
|
35
|
-
[119, '18px'],
|
|
36
|
-
[120, '36px'],
|
|
37
|
-
])(
|
|
38
|
-
'uses the exact fixed font size at the %spx boundary',
|
|
39
|
-
(size, fontSize) => {
|
|
40
|
-
expect(emojiFontSize(size)).toHaveStyle({ fontSize })
|
|
41
|
-
}
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
it('does not show a star by default but shows one when starred', () => {
|
|
45
|
-
const defaultAvatar = renderWithProviders(
|
|
46
|
-
<Avatar id="user-1" name="Alice" />
|
|
47
|
-
)
|
|
48
|
-
expect(defaultAvatar.container.querySelector('svg')).not.toBeInTheDocument()
|
|
49
|
-
|
|
50
|
-
const starredAvatar = renderWithProviders(
|
|
51
|
-
<Avatar id="user-1" name="Alice" starred />
|
|
52
|
-
)
|
|
53
|
-
expect(starredAvatar.container.querySelector('svg')).toBeInTheDocument()
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('uses a squircle by default and a circle when explicitly requested', () => {
|
|
57
|
-
const squircleAvatar = renderWithProviders(
|
|
58
|
-
<Avatar id="user-1" name="Alice" />
|
|
59
|
-
)
|
|
60
|
-
const squircleElements = squircleAvatar.container.querySelectorAll(
|
|
61
|
-
'[style*="border-radius"]'
|
|
62
|
-
)
|
|
63
|
-
expect(squircleElements).toHaveLength(3)
|
|
64
|
-
squircleElements.forEach((element) =>
|
|
65
|
-
expect(element).toHaveStyle({ borderRadius: '1rem' })
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
const circleAvatar = renderWithProviders(
|
|
69
|
-
<Avatar id="user-1" name="Alice" shape="circle" />
|
|
70
|
-
)
|
|
71
|
-
const circleElements = circleAvatar.container.querySelectorAll(
|
|
72
|
-
'[style*="border-radius"]'
|
|
73
|
-
)
|
|
74
|
-
expect(circleElements).toHaveLength(3)
|
|
75
|
-
circleElements.forEach((element) =>
|
|
76
|
-
expect(element).toHaveStyle({ borderRadius: '50%' })
|
|
77
|
-
)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('only applies the AI ring when dmAgentEnabled is true', () => {
|
|
81
|
-
const defaultAvatar = renderWithProviders(
|
|
82
|
-
<Avatar id="user-1" name="Alice" size={40} />
|
|
83
|
-
)
|
|
84
|
-
expect(
|
|
85
|
-
defaultAvatar.container.querySelector('[data-testid="avatar-ring"]')
|
|
86
|
-
).not.toHaveStyle({
|
|
87
|
-
borderWidth: '2px',
|
|
88
|
-
borderStyle: 'solid',
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
const enabledAvatar = renderWithProviders(
|
|
92
|
-
<Avatar id="user-1" name="Alice" size={40} dmAgentEnabled />
|
|
93
|
-
)
|
|
94
|
-
expect(
|
|
95
|
-
enabledAvatar.container.querySelector('[data-testid="avatar-ring"]')
|
|
96
|
-
).toHaveStyle({
|
|
97
|
-
borderWidth: '2px',
|
|
98
|
-
borderStyle: 'solid',
|
|
99
|
-
borderColor: 'var(--AI-Gradient, #7F22FE)',
|
|
100
|
-
boxShadow: 'inset 0 1px 2px 0 rgba(255, 255, 255, 0.5)',
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
const smallEnabledAvatar = renderWithProviders(
|
|
104
|
-
<Avatar id="user-1" name="Alice" size={20} dmAgentEnabled />
|
|
105
|
-
)
|
|
106
|
-
expect(
|
|
107
|
-
smallEnabledAvatar.container.querySelector('[data-testid="avatar-ring"]')
|
|
108
|
-
).toHaveStyle({ borderWidth: '1px' })
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('renders an image instead of the emoji fallback when image is provided', () => {
|
|
112
|
-
const fallbackAvatar = renderWithProviders(<Avatar id="" name="Alice" />)
|
|
113
|
-
expect(
|
|
114
|
-
fallbackAvatar.container.querySelector('img')
|
|
115
|
-
).not.toBeInTheDocument()
|
|
116
|
-
expect(fallbackAvatar.container).toHaveTextContent('🍎')
|
|
117
|
-
expect(
|
|
118
|
-
fallbackAvatar.container.querySelector('.avatar-fallback')
|
|
119
|
-
).toHaveClass(
|
|
120
|
-
'avatar-fallback',
|
|
121
|
-
'flex',
|
|
122
|
-
'h-full',
|
|
123
|
-
'w-full',
|
|
124
|
-
'items-center',
|
|
125
|
-
'justify-center',
|
|
126
|
-
'font-semibold',
|
|
127
|
-
'select-none',
|
|
128
|
-
'transition-colors'
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
const imageAvatar = renderWithProviders(
|
|
132
|
-
<Avatar id="" name="Alice" image="https://example.com/avatar.png" />
|
|
133
|
-
)
|
|
134
|
-
expect(imageAvatar.container.querySelector('img')).toHaveAttribute(
|
|
135
|
-
'src',
|
|
136
|
-
'https://example.com/avatar.png'
|
|
137
|
-
)
|
|
138
|
-
expect(imageAvatar.container).not.toHaveTextContent('🍎')
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it('applies the fallback background color and forwards custom classes', () => {
|
|
142
|
-
const { container } = renderWithProviders(
|
|
143
|
-
<Avatar id="" name="Alice" className="custom-avatar" />
|
|
144
|
-
)
|
|
145
|
-
const root = container.firstElementChild
|
|
146
|
-
const ring = container.querySelector('[data-testid="avatar-ring"]')
|
|
147
|
-
|
|
148
|
-
expect(root).toHaveClass('relative', 'flex-shrink-0', 'custom-avatar')
|
|
149
|
-
expect(ring).toHaveClass('h-full', 'w-full', 'bg-transparent')
|
|
150
|
-
expect(root).toHaveStyle({
|
|
151
|
-
backgroundColor: '#FFD1DD',
|
|
152
|
-
width: '40px',
|
|
153
|
-
height: '40px',
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
30
|
})
|
|
@@ -29,43 +29,4 @@ describe('getAvatarBgColor', () => {
|
|
|
29
29
|
expect(color).toBe(emojiBgColors[emoji])
|
|
30
30
|
expect(getAvatarBgColor(id)).toBe(color)
|
|
31
31
|
})
|
|
32
|
-
|
|
33
|
-
it.each([
|
|
34
|
-
['', '🍎', '#FFD1DD'],
|
|
35
|
-
['id-2', '🍌', '#FFF1C9'],
|
|
36
|
-
['id-3', '🍇', '#E6D9FF'],
|
|
37
|
-
['id-4', '🍊', '#D6F4FF'],
|
|
38
|
-
['id-5', '🍓', '#FFE0AE'],
|
|
39
|
-
['id-6', '🥥', '#F7E7D8'],
|
|
40
|
-
['id-7', '🍒', '#DFF4D8'],
|
|
41
|
-
['id-8', '🥭', '#FFD8C2'],
|
|
42
|
-
['id-9', '🍉', '#D6EBFF'],
|
|
43
|
-
['id-10', '🍋', '#CAEEB5'],
|
|
44
|
-
['id-11', '🥝', '#FFE7C7'],
|
|
45
|
-
['id-12', '🫒', '#F6E3B4'],
|
|
46
|
-
['id-13', '🍈', '#D9F7E1'],
|
|
47
|
-
])('returns the exact fruit pair for %j', (id, emoji, bgColor) => {
|
|
48
|
-
expect(getAvatarEmoji(id)).toBe(emoji)
|
|
49
|
-
expect(getAvatarBgColor(id)).toBe(bgColor)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('uses the absolute hash value for ids with a negative raw hash', () => {
|
|
53
|
-
const id = 'id-100'
|
|
54
|
-
|
|
55
|
-
expect(getAvatarEmoji(id)).toBe('🥭')
|
|
56
|
-
expect(getAvatarBgColor(id)).toBe('#FFD8C2')
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('is deterministic and keeps emoji and background color paired', () => {
|
|
60
|
-
const ids = ['', 'id-2', 'id-7', 'id-13', 'id-100']
|
|
61
|
-
|
|
62
|
-
ids.forEach((id) => {
|
|
63
|
-
const emoji = getAvatarEmoji(id)
|
|
64
|
-
const color = getAvatarBgColor(id)
|
|
65
|
-
|
|
66
|
-
expect(getAvatarEmoji(id)).toBe(emoji)
|
|
67
|
-
expect(getAvatarBgColor(id)).toBe(color)
|
|
68
|
-
expect(emojiBgColors[emoji]).toBe(color)
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
32
|
})
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { act, createEvent, fireEvent } from '@testing-library/react'
|
|
1
|
+
import { act, fireEvent } from '@testing-library/react'
|
|
3
2
|
import { Channel, LocalMessage, StreamChat } from 'stream-chat'
|
|
4
|
-
import {
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
5
4
|
|
|
6
5
|
import { renderWithProviders, screen } from '../../test/utils'
|
|
7
6
|
import type { ChannelPreviewProps } from '../../types'
|
|
@@ -14,15 +13,9 @@ const mockUser = {
|
|
|
14
13
|
name: 'Current User',
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
const mockChatContext = vi.hoisted(() => ({
|
|
18
|
-
current: {
|
|
19
|
-
client: { userID: 'current-user' },
|
|
20
|
-
} as { client?: { userID?: string } },
|
|
21
|
-
}))
|
|
22
|
-
|
|
23
16
|
vi.mock('stream-chat-react', async (importOriginal) => ({
|
|
24
17
|
...(await importOriginal<typeof import('stream-chat-react')>()),
|
|
25
|
-
useChatContext: () =>
|
|
18
|
+
useChatContext: () => ({ client: { userID: 'current-user' } }),
|
|
26
19
|
}))
|
|
27
20
|
|
|
28
21
|
type MockChannel = Channel & {
|
|
@@ -80,406 +73,6 @@ describe('CustomChannelPreview', () => {
|
|
|
80
73
|
onChannelSelect: vi.fn(),
|
|
81
74
|
}
|
|
82
75
|
|
|
83
|
-
beforeEach(() => {
|
|
84
|
-
mockChatContext.current = {
|
|
85
|
-
client: { userID: 'current-user' },
|
|
86
|
-
}
|
|
87
|
-
defaultProps.onChannelSelect.mockClear()
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('applies selected and unselected row styling from context', () => {
|
|
91
|
-
const channel = createMockChannel([])
|
|
92
|
-
const otherChannel = createMockChannel([])
|
|
93
|
-
otherChannel.id = 'channel-2'
|
|
94
|
-
|
|
95
|
-
const { rerender } = renderWithProviders(
|
|
96
|
-
<ChannelListProvider
|
|
97
|
-
value={{
|
|
98
|
-
selectedChannel: channel,
|
|
99
|
-
onChannelSelect: defaultProps.onChannelSelect,
|
|
100
|
-
debug: false,
|
|
101
|
-
}}
|
|
102
|
-
>
|
|
103
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
104
|
-
</ChannelListProvider>
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
let row = screen.getByRole('button')
|
|
108
|
-
expect(row).toHaveClass('bg-black/[0.04]')
|
|
109
|
-
expect(row).not.toHaveClass('hover:bg-black/[0.02]')
|
|
110
|
-
|
|
111
|
-
rerender(
|
|
112
|
-
<ChannelListProvider
|
|
113
|
-
value={{
|
|
114
|
-
selectedChannel: otherChannel,
|
|
115
|
-
onChannelSelect: defaultProps.onChannelSelect,
|
|
116
|
-
debug: false,
|
|
117
|
-
}}
|
|
118
|
-
>
|
|
119
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
120
|
-
</ChannelListProvider>
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
row = screen.getByRole('button')
|
|
124
|
-
expect(row).not.toHaveClass('bg-black/[0.04]')
|
|
125
|
-
expect(row).toHaveClass('hover:bg-black/[0.02]')
|
|
126
|
-
|
|
127
|
-
rerender(
|
|
128
|
-
<ChannelListProvider
|
|
129
|
-
value={{
|
|
130
|
-
selectedChannel: otherChannel,
|
|
131
|
-
onChannelSelect: defaultProps.onChannelSelect,
|
|
132
|
-
debug: false,
|
|
133
|
-
}}
|
|
134
|
-
>
|
|
135
|
-
<CustomChannelPreview {...defaultProps} channel={channel} unread={1} />
|
|
136
|
-
</ChannelListProvider>
|
|
137
|
-
)
|
|
138
|
-
expect(screen.getByRole('button')).toHaveClass('hover:bg-black/[0.02]')
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it('selects the channel by click and activation keys only', () => {
|
|
142
|
-
const channel = createMockChannel([])
|
|
143
|
-
const onChannelSelect = vi.fn()
|
|
144
|
-
const { container } = renderWithProviders(
|
|
145
|
-
<ChannelListProvider value={{ onChannelSelect, debug: false }}>
|
|
146
|
-
<CustomChannelPreview channel={channel} />
|
|
147
|
-
</ChannelListProvider>
|
|
148
|
-
)
|
|
149
|
-
const row = container.querySelector('[role="button"]') as HTMLElement
|
|
150
|
-
|
|
151
|
-
fireEvent.click(row)
|
|
152
|
-
fireEvent.keyDown(row, { key: 'Enter' })
|
|
153
|
-
const spaceEvent = createEvent.keyDown(row, { key: ' ' })
|
|
154
|
-
fireEvent(row, spaceEvent)
|
|
155
|
-
|
|
156
|
-
expect(spaceEvent.defaultPrevented).toBe(true)
|
|
157
|
-
expect(onChannelSelect).toHaveBeenCalledTimes(3)
|
|
158
|
-
expect(onChannelSelect).toHaveBeenNthCalledWith(1, channel)
|
|
159
|
-
expect(onChannelSelect).toHaveBeenNthCalledWith(2, channel)
|
|
160
|
-
expect(onChannelSelect).toHaveBeenNthCalledWith(3, channel)
|
|
161
|
-
|
|
162
|
-
fireEvent.keyDown(row, { key: 'a' })
|
|
163
|
-
fireEvent.keyDown(row, { key: 'Enter', repeat: true })
|
|
164
|
-
expect(onChannelSelect).toHaveBeenCalledTimes(3)
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('renders with an unhydrated client and resolves the fallback member', () => {
|
|
168
|
-
mockChatContext.current = { client: undefined }
|
|
169
|
-
const channel = createMockChannel([])
|
|
170
|
-
|
|
171
|
-
expect(() =>
|
|
172
|
-
renderWithProviders(
|
|
173
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
174
|
-
)
|
|
175
|
-
).not.toThrow()
|
|
176
|
-
expect(screen.getByText('Current User')).toBeInTheDocument()
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('resolves the other participant using the current client user id', () => {
|
|
180
|
-
const channel = createMockChannel([])
|
|
181
|
-
|
|
182
|
-
renderWithProviders(
|
|
183
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
184
|
-
)
|
|
185
|
-
expect(screen.getByText('Alice')).toBeInTheDocument()
|
|
186
|
-
|
|
187
|
-
mockChatContext.current = { client: { userID: 'participant-1' } }
|
|
188
|
-
renderWithProviders(
|
|
189
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
190
|
-
)
|
|
191
|
-
expect(screen.getByText('Current User')).toBeInTheDocument()
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it('renders safe fallbacks when channel state is empty', () => {
|
|
195
|
-
const channel = createMockChannel([])
|
|
196
|
-
Object.assign(channel, { state: {} })
|
|
197
|
-
|
|
198
|
-
expect(() =>
|
|
199
|
-
renderWithProviders(
|
|
200
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
201
|
-
)
|
|
202
|
-
).not.toThrow()
|
|
203
|
-
expect(screen.getByText('Unknown member')).toBeInTheDocument()
|
|
204
|
-
expect(screen.getByText('No messages yet')).toBeInTheDocument()
|
|
205
|
-
expect(screen.queryByText('Starred conversation.')).not.toBeInTheDocument()
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('renders a participant image only when one is supplied', () => {
|
|
209
|
-
const channel = createMockChannel([])
|
|
210
|
-
channel.state.members['participant-1'].user = {
|
|
211
|
-
id: 'participant-1',
|
|
212
|
-
name: 'Alice',
|
|
213
|
-
image: 'https://example.com/alice.png',
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const { container, rerender } = renderWithProviders(
|
|
217
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
218
|
-
)
|
|
219
|
-
expect(container.querySelectorAll('img')).toHaveLength(1)
|
|
220
|
-
expect(container.querySelector('img')).toHaveAttribute(
|
|
221
|
-
'src',
|
|
222
|
-
'https://example.com/alice.png'
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
channel.state.members['participant-1'].user = {
|
|
226
|
-
id: 'participant-1',
|
|
227
|
-
name: 'Alice',
|
|
228
|
-
}
|
|
229
|
-
rerender(
|
|
230
|
-
<CustomChannelPreview {...defaultProps} channel={channel} unread={1} />
|
|
231
|
-
)
|
|
232
|
-
expect(container.querySelectorAll('img')).toHaveLength(0)
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it.each([
|
|
236
|
-
[undefined, undefined],
|
|
237
|
-
[3, '3'],
|
|
238
|
-
[99, '99'],
|
|
239
|
-
[100, '99+'],
|
|
240
|
-
])('renders the unread badge for unread=%s', (unread, expectedBadge) => {
|
|
241
|
-
const channel = createMockChannel([])
|
|
242
|
-
const { container } = renderWithProviders(
|
|
243
|
-
<CustomChannelPreview
|
|
244
|
-
{...defaultProps}
|
|
245
|
-
channel={channel}
|
|
246
|
-
unread={unread}
|
|
247
|
-
/>
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
if (expectedBadge) {
|
|
251
|
-
expect(screen.getByText(expectedBadge)).toBeInTheDocument()
|
|
252
|
-
} else {
|
|
253
|
-
expect(
|
|
254
|
-
container.querySelector('span[class*="bg-[#7f22fe]"]')
|
|
255
|
-
).not.toBeInTheDocument()
|
|
256
|
-
}
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
it('renders a timestamp only when the last message has created_at', () => {
|
|
260
|
-
const channel = createMockChannel([
|
|
261
|
-
{
|
|
262
|
-
id: 'msg-1',
|
|
263
|
-
text: 'Recent message',
|
|
264
|
-
type: 'regular',
|
|
265
|
-
created_at: new Date(),
|
|
266
|
-
user: { id: 'participant-1', name: 'Alice' },
|
|
267
|
-
},
|
|
268
|
-
])
|
|
269
|
-
|
|
270
|
-
const { container, rerender } = renderWithProviders(
|
|
271
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
272
|
-
)
|
|
273
|
-
const timestamp = () =>
|
|
274
|
-
container.querySelector('h3')?.parentElement?.querySelector('span')
|
|
275
|
-
|
|
276
|
-
expect(timestamp()).toHaveTextContent('Just now')
|
|
277
|
-
expect(screen.getByText('Just now')).toBeInTheDocument()
|
|
278
|
-
|
|
279
|
-
Object.assign(channel.state.messages[0], { created_at: undefined })
|
|
280
|
-
rerender(
|
|
281
|
-
<CustomChannelPreview {...defaultProps} channel={channel} unread={1} />
|
|
282
|
-
)
|
|
283
|
-
expect(timestamp()).toBeNull()
|
|
284
|
-
expect(screen.queryByText('Just now')).not.toBeInTheDocument()
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
it.each([
|
|
288
|
-
[
|
|
289
|
-
'tip with text',
|
|
290
|
-
{
|
|
291
|
-
metadata: { custom_type: MESSAGE_CUSTOM_TYPE.TIP },
|
|
292
|
-
text: 'A generous tip',
|
|
293
|
-
},
|
|
294
|
-
'A generous tip',
|
|
295
|
-
],
|
|
296
|
-
[
|
|
297
|
-
'tip without text',
|
|
298
|
-
{ metadata: { custom_type: MESSAGE_CUSTOM_TYPE.TIP } },
|
|
299
|
-
'Sent a tip',
|
|
300
|
-
],
|
|
301
|
-
[
|
|
302
|
-
'paid with text',
|
|
303
|
-
{
|
|
304
|
-
metadata: { custom_type: MESSAGE_CUSTOM_TYPE.PAID },
|
|
305
|
-
text: 'Paid message',
|
|
306
|
-
},
|
|
307
|
-
'Paid message',
|
|
308
|
-
],
|
|
309
|
-
[
|
|
310
|
-
'paid without text',
|
|
311
|
-
{ metadata: { custom_type: MESSAGE_CUSTOM_TYPE.PAID } },
|
|
312
|
-
'Sent a message',
|
|
313
|
-
],
|
|
314
|
-
[
|
|
315
|
-
'link attachment',
|
|
316
|
-
{ attachments: [{ og_scrape_url: 'https://example.com' }] },
|
|
317
|
-
'https://example.com',
|
|
318
|
-
],
|
|
319
|
-
[
|
|
320
|
-
'image attachment',
|
|
321
|
-
{ attachments: [{ type: 'image' }] },
|
|
322
|
-
'📷 Sent an image',
|
|
323
|
-
],
|
|
324
|
-
[
|
|
325
|
-
'video attachment',
|
|
326
|
-
{ attachments: [{ type: 'video' }] },
|
|
327
|
-
'🎥 Sent a video',
|
|
328
|
-
],
|
|
329
|
-
['audio attachment', { attachments: [{ type: 'audio' }] }, '🎵 Sent audio'],
|
|
330
|
-
['file attachment', { attachments: [{ type: 'file' }] }, '📎 Sent a file'],
|
|
331
|
-
[
|
|
332
|
-
'unknown attachment',
|
|
333
|
-
{ attachments: [{ type: 'unknown' }] },
|
|
334
|
-
'📎 Sent an attachment',
|
|
335
|
-
],
|
|
336
|
-
])('renders the %s preview text', (_name, message, expectedText) => {
|
|
337
|
-
const channel = createMockChannel([
|
|
338
|
-
{
|
|
339
|
-
id: 'msg-1',
|
|
340
|
-
type: 'regular',
|
|
341
|
-
...message,
|
|
342
|
-
user: { id: 'participant-1', name: 'Alice' },
|
|
343
|
-
},
|
|
344
|
-
])
|
|
345
|
-
|
|
346
|
-
renderWithProviders(
|
|
347
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
348
|
-
)
|
|
349
|
-
expect(screen.getByText(expectedText)).toBeInTheDocument()
|
|
350
|
-
})
|
|
351
|
-
|
|
352
|
-
it('prefixes chatbot previews but not normal message previews', () => {
|
|
353
|
-
const channel = createMockChannel([
|
|
354
|
-
{
|
|
355
|
-
id: 'msg-1',
|
|
356
|
-
text: 'Automated response',
|
|
357
|
-
type: 'regular',
|
|
358
|
-
metadata: { custom_type: MESSAGE_CUSTOM_TYPE.CHATBOT },
|
|
359
|
-
},
|
|
360
|
-
])
|
|
361
|
-
const { rerender } = renderWithProviders(
|
|
362
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
363
|
-
)
|
|
364
|
-
expect(screen.getByText('✨ Automated response')).toBeInTheDocument()
|
|
365
|
-
|
|
366
|
-
channel.state.messages[0].metadata = {}
|
|
367
|
-
channel.state.messages[0].text = 'Human response'
|
|
368
|
-
rerender(
|
|
369
|
-
<CustomChannelPreview {...defaultProps} channel={channel} unread={1} />
|
|
370
|
-
)
|
|
371
|
-
expect(screen.getByText('Human response')).toBeInTheDocument()
|
|
372
|
-
expect(screen.queryByText('✨ Human response')).not.toBeInTheDocument()
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
it('uses renderMessagePreview from context with the resolved default text', () => {
|
|
376
|
-
const channel = createMockChannel([
|
|
377
|
-
{
|
|
378
|
-
id: 'msg-1',
|
|
379
|
-
text: 'Default preview',
|
|
380
|
-
type: 'regular',
|
|
381
|
-
},
|
|
382
|
-
])
|
|
383
|
-
const renderMessagePreview = vi.fn(
|
|
384
|
-
(_message: LocalMessage | undefined, _defaultPreview?: string) =>
|
|
385
|
-
'Custom preview'
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
renderWithProviders(
|
|
389
|
-
<ChannelListProvider
|
|
390
|
-
value={{ onChannelSelect: vi.fn(), debug: false, renderMessagePreview }}
|
|
391
|
-
>
|
|
392
|
-
<CustomChannelPreview channel={channel} />
|
|
393
|
-
</ChannelListProvider>
|
|
394
|
-
)
|
|
395
|
-
|
|
396
|
-
expect(screen.getByText('Custom preview')).toBeInTheDocument()
|
|
397
|
-
expect(renderMessagePreview).toHaveBeenCalledWith(
|
|
398
|
-
channel.state.messages[0],
|
|
399
|
-
'Default preview'
|
|
400
|
-
)
|
|
401
|
-
})
|
|
402
|
-
|
|
403
|
-
it('renders the default preview when renderMessagePreview is not supplied', () => {
|
|
404
|
-
const channel = createMockChannel([
|
|
405
|
-
{
|
|
406
|
-
id: 'msg-1',
|
|
407
|
-
text: 'Default preview',
|
|
408
|
-
type: 'regular',
|
|
409
|
-
},
|
|
410
|
-
])
|
|
411
|
-
|
|
412
|
-
renderWithProviders(
|
|
413
|
-
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
414
|
-
)
|
|
415
|
-
expect(screen.getByText('Default preview')).toBeInTheDocument()
|
|
416
|
-
})
|
|
417
|
-
|
|
418
|
-
it('uses the viewer language translation when available', () => {
|
|
419
|
-
const channel = createMockChannel([
|
|
420
|
-
{
|
|
421
|
-
id: 'msg-1',
|
|
422
|
-
text: 'Raw text',
|
|
423
|
-
type: 'regular',
|
|
424
|
-
i18n: { es_text: 'Texto traducido', language: 'es' },
|
|
425
|
-
},
|
|
426
|
-
])
|
|
427
|
-
const { rerender } = renderWithProviders(
|
|
428
|
-
<ChannelListProvider
|
|
429
|
-
value={{
|
|
430
|
-
onChannelSelect: vi.fn(),
|
|
431
|
-
debug: false,
|
|
432
|
-
viewerLanguage: 'es-MX',
|
|
433
|
-
}}
|
|
434
|
-
>
|
|
435
|
-
<CustomChannelPreview channel={channel} />
|
|
436
|
-
</ChannelListProvider>
|
|
437
|
-
)
|
|
438
|
-
expect(screen.getByText('Texto traducido')).toBeInTheDocument()
|
|
439
|
-
|
|
440
|
-
rerender(
|
|
441
|
-
<ChannelListProvider value={{ onChannelSelect: vi.fn(), debug: false }}>
|
|
442
|
-
<CustomChannelPreview channel={channel} />
|
|
443
|
-
</ChannelListProvider>
|
|
444
|
-
)
|
|
445
|
-
expect(screen.getByText('Raw text')).toBeInTheDocument()
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
it('logs the preview payload only when debug is enabled', () => {
|
|
449
|
-
const channel = createMockChannel([])
|
|
450
|
-
const consoleLog = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
451
|
-
|
|
452
|
-
renderWithProviders(
|
|
453
|
-
<ChannelListProvider value={{ onChannelSelect: vi.fn(), debug: true }}>
|
|
454
|
-
<CustomChannelPreview channel={channel} />
|
|
455
|
-
</ChannelListProvider>
|
|
456
|
-
)
|
|
457
|
-
expect(consoleLog).toHaveBeenCalledWith(
|
|
458
|
-
'📺 [ChannelList] 📋 CHANNEL PREVIEW RENDER',
|
|
459
|
-
expect.objectContaining({
|
|
460
|
-
channelId: channel.id,
|
|
461
|
-
isSelected: false,
|
|
462
|
-
participantName: 'Alice',
|
|
463
|
-
unreadCount: 0,
|
|
464
|
-
hasTimestamp: false,
|
|
465
|
-
})
|
|
466
|
-
)
|
|
467
|
-
|
|
468
|
-
consoleLog.mockClear()
|
|
469
|
-
const { rerender } = renderWithProviders(
|
|
470
|
-
<ChannelListProvider value={{ onChannelSelect: vi.fn(), debug: false }}>
|
|
471
|
-
<CustomChannelPreview channel={channel} />
|
|
472
|
-
</ChannelListProvider>
|
|
473
|
-
)
|
|
474
|
-
expect(consoleLog).not.toHaveBeenCalled()
|
|
475
|
-
rerender(
|
|
476
|
-
<ChannelListProvider value={{ onChannelSelect: vi.fn(), debug: false }}>
|
|
477
|
-
<CustomChannelPreview channel={channel} />
|
|
478
|
-
</ChannelListProvider>
|
|
479
|
-
)
|
|
480
|
-
expect(consoleLog).not.toHaveBeenCalled()
|
|
481
|
-
})
|
|
482
|
-
|
|
483
76
|
it('uses username when participant name is UUID-like', () => {
|
|
484
77
|
const channel = createMockChannel([])
|
|
485
78
|
channel.state.members['participant-1'] = {
|