@linktr.ee/messaging-react 4.4.3 → 4.4.4-rc-1788181757
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/AttachmentCard-BbsjzdLJ.cjs +2 -0
- package/dist/AttachmentCard-BbsjzdLJ.cjs.map +1 -0
- package/dist/{AttachmentCard-BqwWgqvi.js → AttachmentCard-DSekwIOA.js} +35 -20
- package/dist/AttachmentCard-DSekwIOA.js.map +1 -0
- package/dist/{Card-BIbqWagz.cjs → Card-BS6Fza2a.cjs} +2 -2
- package/dist/{Card-BIbqWagz.cjs.map → Card-BS6Fza2a.cjs.map} +1 -1
- package/dist/{Card-f51K6V7Y.js → Card-BhwBkgxI.js} +2 -2
- package/dist/{Card-f51K6V7Y.js.map → Card-BhwBkgxI.js.map} +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +36 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/AttachmentCard/MediaPlayer.test.tsx +62 -85
- package/src/components/AttachmentCard/MediaPlayer.tsx +14 -2
- package/src/components/AttachmentCard/Thumbnail.test.tsx +37 -46
- package/src/components/AttachmentCard/index.test.tsx +18 -46
- package/src/components/AttachmentCard/index.tsx +8 -2
- package/src/components/AttachmentCard/utils/icons.ts +11 -1
- package/src/components/Avatar/Avatar.test.tsx +37 -64
- package/src/components/Avatar/index.tsx +8 -1
- package/src/components/ChannelList/CustomChannelPreview.test.tsx +19 -19
- package/src/components/ChannelList/CustomChannelPreview.tsx +8 -2
- package/src/components/ChannelView.outbound-resolution.integration.test.tsx +25 -30
- package/src/components/ChannelView.test.tsx +17 -31
- package/src/components/ChannelView.tsx +1 -0
- package/src/components/CustomLinkPreviewList/CustomLinkPreviewList.test.tsx +8 -5
- package/src/components/CustomLinkPreviewList/index.tsx +4 -1
- package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +26 -21
- package/src/components/CustomMessage/StreamAttachmentMessage.tsx +2 -0
- package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +43 -56
- package/src/components/CustomMessageInput/index.tsx +25 -5
- package/src/components/CustomSystemMessage/CustomSystemMessage.test.tsx +17 -23
- package/src/components/FaqList/FaqList.test.tsx +5 -4
- package/src/components/FaqList/index.tsx +6 -1
- package/src/components/LinkAttachment/LinkAttachment.test.tsx +70 -81
- package/src/components/LinkAttachment/components/_shared/BubbleTail.test.tsx +1 -0
- package/src/components/LinkAttachment/components/_shared/CardBody.test.tsx +20 -61
- package/src/components/LinkAttachment/components/_shared/CardBody.tsx +10 -2
- package/src/components/LinkAttachment/components/_shared/CardCta.test.tsx +1 -21
- package/src/components/LinkAttachment/components/_shared/CardShell.test.tsx +11 -37
- package/src/components/LinkAttachment/components/_shared/CardShell.tsx +5 -2
- package/src/components/LinkAttachment/components/_shared/CardThumbnail.test.tsx +65 -72
- package/src/components/LinkAttachment/components/_shared/CardThumbnail.tsx +13 -2
- package/src/components/MessageAttachment/MessageAttachment.test.tsx +25 -8
- package/src/components/MessageAttachment/_shared/CompactDocumentRow.test.tsx +5 -19
- package/src/components/MessageAttachment/_shared/MediaStackGrid.test.tsx +7 -9
- package/src/components/MessageAttachment/_shared/VideoViewer.test.tsx +9 -2
- package/src/components/MessageAttachment/_shared/VideoViewer.tsx +1 -0
- package/src/components/MessageAttachment/_shared/ViewerShell.test.tsx +2 -2
- package/src/components/MessageBubble/MessageBubble.test.tsx +0 -1
- package/src/components/RichCard/RichCard.test.tsx +24 -54
- package/src/components/RichCard/RichCardBody.tsx +4 -1
- package/src/logging/index.test.tsx +1 -0
- package/src/providers/MessagingProvider.test.tsx +37 -41
- package/dist/AttachmentCard-BqwWgqvi.js.map +0 -1
- package/dist/AttachmentCard-DUrgGRsq.cjs +0 -2
- package/dist/AttachmentCard-DUrgGRsq.cjs.map +0 -1
|
@@ -42,111 +42,84 @@ describe('Avatar', () => {
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
it('does not show a star by default but shows one when starred', () => {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
expect(defaultAvatar.container.querySelector('svg')).not.toBeInTheDocument()
|
|
45
|
+
const view = renderWithProviders(<Avatar id="user-1" name="Alice" />)
|
|
46
|
+
expect(screen.queryByTestId('avatar-star')).not.toBeInTheDocument()
|
|
47
|
+
view.unmount()
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
53
|
-
expect(starredAvatar.container.querySelector('svg')).toBeInTheDocument()
|
|
49
|
+
renderWithProviders(<Avatar id="user-1" name="Alice" starred />)
|
|
50
|
+
expect(screen.getByTestId('avatar-star')).toBeInTheDocument()
|
|
54
51
|
})
|
|
55
52
|
|
|
56
53
|
it('uses a squircle by default and a circle when explicitly requested', () => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
// All three layers (frame, ring, clipped inner) have to agree on the
|
|
55
|
+
// radius, or the corners of one show through another.
|
|
56
|
+
const rounded = () =>
|
|
57
|
+
['avatar', 'avatar-ring', 'avatar-inner'].map((testId) =>
|
|
58
|
+
screen.getByTestId(testId)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
const view = renderWithProviders(<Avatar id="user-1" name="Alice" />)
|
|
62
|
+
rounded().forEach((element) =>
|
|
65
63
|
expect(element).toHaveStyle({ borderRadius: '1rem' })
|
|
66
64
|
)
|
|
65
|
+
view.unmount()
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
const circleElements = circleAvatar.container.querySelectorAll(
|
|
72
|
-
'[style*="border-radius"]'
|
|
73
|
-
)
|
|
74
|
-
expect(circleElements).toHaveLength(3)
|
|
75
|
-
circleElements.forEach((element) =>
|
|
67
|
+
renderWithProviders(<Avatar id="user-1" name="Alice" shape="circle" />)
|
|
68
|
+
rounded().forEach((element) =>
|
|
76
69
|
expect(element).toHaveStyle({ borderRadius: '50%' })
|
|
77
70
|
)
|
|
78
71
|
})
|
|
79
72
|
|
|
80
73
|
it('only applies the AI ring when dmAgentEnabled is true', () => {
|
|
81
|
-
const
|
|
74
|
+
const { unmount: unmountPlain } = renderWithProviders(
|
|
82
75
|
<Avatar id="user-1" name="Alice" size={40} />
|
|
83
76
|
)
|
|
84
|
-
expect(
|
|
85
|
-
defaultAvatar.container.querySelector('[data-testid="avatar-ring"]')
|
|
86
|
-
).not.toHaveStyle({
|
|
77
|
+
expect(screen.getByTestId('avatar-ring')).not.toHaveStyle({
|
|
87
78
|
borderWidth: '2px',
|
|
88
79
|
borderStyle: 'solid',
|
|
89
80
|
})
|
|
81
|
+
unmountPlain()
|
|
90
82
|
|
|
91
|
-
const
|
|
83
|
+
const { unmount: unmountEnabled } = renderWithProviders(
|
|
92
84
|
<Avatar id="user-1" name="Alice" size={40} dmAgentEnabled />
|
|
93
85
|
)
|
|
94
|
-
expect(
|
|
95
|
-
enabledAvatar.container.querySelector('[data-testid="avatar-ring"]')
|
|
96
|
-
).toHaveStyle({
|
|
86
|
+
expect(screen.getByTestId('avatar-ring')).toHaveStyle({
|
|
97
87
|
borderWidth: '2px',
|
|
98
88
|
borderStyle: 'solid',
|
|
99
89
|
borderColor: 'var(--AI-Gradient, #7F22FE)',
|
|
100
90
|
boxShadow: 'inset 0 1px 2px 0 rgba(255, 255, 255, 0.5)',
|
|
101
91
|
})
|
|
92
|
+
unmountEnabled()
|
|
102
93
|
|
|
103
|
-
|
|
94
|
+
renderWithProviders(
|
|
104
95
|
<Avatar id="user-1" name="Alice" size={20} dmAgentEnabled />
|
|
105
96
|
)
|
|
106
|
-
expect(
|
|
107
|
-
|
|
108
|
-
|
|
97
|
+
expect(screen.getByTestId('avatar-ring')).toHaveStyle({
|
|
98
|
+
borderWidth: '1px',
|
|
99
|
+
})
|
|
109
100
|
})
|
|
110
101
|
|
|
111
102
|
it('renders an image instead of the emoji fallback when image is provided', () => {
|
|
112
|
-
const
|
|
113
|
-
expect(
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
)
|
|
103
|
+
const view = renderWithProviders(<Avatar id="" name="Alice" />)
|
|
104
|
+
expect(screen.queryByRole('img')).not.toBeInTheDocument()
|
|
105
|
+
expect(screen.getByTestId('avatar-fallback')).toHaveTextContent('🍎')
|
|
106
|
+
view.unmount()
|
|
130
107
|
|
|
131
|
-
|
|
108
|
+
renderWithProviders(
|
|
132
109
|
<Avatar id="" name="Alice" image="https://example.com/avatar.png" />
|
|
133
110
|
)
|
|
134
|
-
expect(
|
|
111
|
+
expect(screen.getByRole('img')).toHaveAttribute(
|
|
135
112
|
'src',
|
|
136
113
|
'https://example.com/avatar.png'
|
|
137
114
|
)
|
|
138
|
-
expect(
|
|
115
|
+
expect(screen.queryByTestId('avatar-fallback')).not.toBeInTheDocument()
|
|
139
116
|
})
|
|
140
117
|
|
|
141
118
|
it('applies the fallback background color and forwards custom classes', () => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
)
|
|
145
|
-
const root = container.firstElementChild
|
|
146
|
-
const ring = container.querySelector('[data-testid="avatar-ring"]')
|
|
119
|
+
renderWithProviders(<Avatar id="" name="Alice" className="custom-avatar" />)
|
|
120
|
+
const root = screen.getByTestId('avatar')
|
|
147
121
|
|
|
148
|
-
expect(root).toHaveClass('
|
|
149
|
-
expect(ring).toHaveClass('h-full', 'w-full', 'bg-transparent')
|
|
122
|
+
expect(root).toHaveClass('custom-avatar')
|
|
150
123
|
expect(root).toHaveStyle({
|
|
151
124
|
backgroundColor: '#FFD1DD',
|
|
152
125
|
width: '40px',
|
|
@@ -50,12 +50,17 @@ export const Avatar = ({
|
|
|
50
50
|
shape === 'circle' ? { borderRadius: '50%' } : { borderRadius: '1rem' }
|
|
51
51
|
|
|
52
52
|
const avatarInner = (
|
|
53
|
-
<div
|
|
53
|
+
<div
|
|
54
|
+
data-testid="avatar-inner"
|
|
55
|
+
className="h-full w-full overflow-hidden"
|
|
56
|
+
style={borderStyle}
|
|
57
|
+
>
|
|
54
58
|
{image ? (
|
|
55
59
|
<img src={image} alt="" className="h-full w-full object-cover" />
|
|
56
60
|
) : (
|
|
57
61
|
<div
|
|
58
62
|
aria-hidden="true"
|
|
63
|
+
data-testid="avatar-fallback"
|
|
59
64
|
className={classNames(
|
|
60
65
|
'avatar-fallback flex h-full w-full items-center justify-center font-semibold select-none transition-colors'
|
|
61
66
|
)}
|
|
@@ -69,6 +74,7 @@ export const Avatar = ({
|
|
|
69
74
|
|
|
70
75
|
return (
|
|
71
76
|
<div
|
|
77
|
+
data-testid="avatar"
|
|
72
78
|
className={classNames('relative flex-shrink-0', className)}
|
|
73
79
|
style={
|
|
74
80
|
{
|
|
@@ -83,6 +89,7 @@ export const Avatar = ({
|
|
|
83
89
|
{starred && (
|
|
84
90
|
<div
|
|
85
91
|
aria-hidden="true"
|
|
92
|
+
data-testid="avatar-star"
|
|
86
93
|
className="absolute -left-1.5 -top-1.5 z-10 flex size-5 items-center justify-center rounded-full bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]"
|
|
87
94
|
>
|
|
88
95
|
<StarIcon className="size-3 text-yellow-600" weight="duotone" />
|
|
@@ -87,7 +87,7 @@ describe('CustomChannelPreview', () => {
|
|
|
87
87
|
defaultProps.onChannelSelect.mockClear()
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
-
it('
|
|
90
|
+
it('drops the hover affordance on the selected row only', () => {
|
|
91
91
|
const channel = createMockChannel([])
|
|
92
92
|
const otherChannel = createMockChannel([])
|
|
93
93
|
otherChannel.id = 'channel-2'
|
|
@@ -105,7 +105,6 @@ describe('CustomChannelPreview', () => {
|
|
|
105
105
|
)
|
|
106
106
|
|
|
107
107
|
let row = screen.getByRole('button')
|
|
108
|
-
expect(row).toHaveClass('bg-black/[0.04]')
|
|
109
108
|
expect(row).not.toHaveClass('hover:bg-black/[0.02]')
|
|
110
109
|
|
|
111
110
|
rerender(
|
|
@@ -121,7 +120,6 @@ describe('CustomChannelPreview', () => {
|
|
|
121
120
|
)
|
|
122
121
|
|
|
123
122
|
row = screen.getByRole('button')
|
|
124
|
-
expect(row).not.toHaveClass('bg-black/[0.04]')
|
|
125
123
|
expect(row).toHaveClass('hover:bg-black/[0.02]')
|
|
126
124
|
|
|
127
125
|
rerender(
|
|
@@ -141,12 +139,12 @@ describe('CustomChannelPreview', () => {
|
|
|
141
139
|
it('selects the channel by click and activation keys only', () => {
|
|
142
140
|
const channel = createMockChannel([])
|
|
143
141
|
const onChannelSelect = vi.fn()
|
|
144
|
-
|
|
142
|
+
renderWithProviders(
|
|
145
143
|
<ChannelListProvider value={{ onChannelSelect, debug: false }}>
|
|
146
144
|
<CustomChannelPreview channel={channel} />
|
|
147
145
|
</ChannelListProvider>
|
|
148
146
|
)
|
|
149
|
-
const row =
|
|
147
|
+
const row = screen.getByRole('button')
|
|
150
148
|
|
|
151
149
|
fireEvent.click(row)
|
|
152
150
|
fireEvent.keyDown(row, { key: 'Enter' })
|
|
@@ -213,11 +211,11 @@ describe('CustomChannelPreview', () => {
|
|
|
213
211
|
image: 'https://example.com/alice.png',
|
|
214
212
|
}
|
|
215
213
|
|
|
216
|
-
const {
|
|
214
|
+
const { rerender } = renderWithProviders(
|
|
217
215
|
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
218
216
|
)
|
|
219
|
-
expect(
|
|
220
|
-
expect(
|
|
217
|
+
expect(screen.getAllByRole('img', { hidden: true })).toHaveLength(1)
|
|
218
|
+
expect(screen.getByRole('img', { hidden: true })).toHaveAttribute(
|
|
221
219
|
'src',
|
|
222
220
|
'https://example.com/alice.png'
|
|
223
221
|
)
|
|
@@ -229,7 +227,7 @@ describe('CustomChannelPreview', () => {
|
|
|
229
227
|
rerender(
|
|
230
228
|
<CustomChannelPreview {...defaultProps} channel={channel} unread={1} />
|
|
231
229
|
)
|
|
232
|
-
expect(
|
|
230
|
+
expect(screen.queryAllByRole('img', { hidden: true })).toHaveLength(0)
|
|
233
231
|
})
|
|
234
232
|
|
|
235
233
|
it.each([
|
|
@@ -239,7 +237,7 @@ describe('CustomChannelPreview', () => {
|
|
|
239
237
|
[100, '99+'],
|
|
240
238
|
])('renders the unread badge for unread=%s', (unread, expectedBadge) => {
|
|
241
239
|
const channel = createMockChannel([])
|
|
242
|
-
|
|
240
|
+
renderWithProviders(
|
|
243
241
|
<CustomChannelPreview
|
|
244
242
|
{...defaultProps}
|
|
245
243
|
channel={channel}
|
|
@@ -248,10 +246,12 @@ describe('CustomChannelPreview', () => {
|
|
|
248
246
|
)
|
|
249
247
|
|
|
250
248
|
if (expectedBadge) {
|
|
251
|
-
expect(
|
|
249
|
+
expect(
|
|
250
|
+
screen.getByTestId('channel-preview-unread-badge')
|
|
251
|
+
).toHaveTextContent(expectedBadge)
|
|
252
252
|
} else {
|
|
253
253
|
expect(
|
|
254
|
-
|
|
254
|
+
screen.queryByTestId('channel-preview-unread-badge')
|
|
255
255
|
).not.toBeInTheDocument()
|
|
256
256
|
}
|
|
257
257
|
})
|
|
@@ -267,21 +267,21 @@ describe('CustomChannelPreview', () => {
|
|
|
267
267
|
},
|
|
268
268
|
])
|
|
269
269
|
|
|
270
|
-
const {
|
|
270
|
+
const { rerender } = renderWithProviders(
|
|
271
271
|
<CustomChannelPreview {...defaultProps} channel={channel} />
|
|
272
272
|
)
|
|
273
|
-
const timestamp = () =>
|
|
274
|
-
container.querySelector('h3')?.parentElement?.querySelector('span')
|
|
275
273
|
|
|
276
|
-
expect(timestamp
|
|
277
|
-
|
|
274
|
+
expect(screen.getByTestId('channel-preview-timestamp')).toHaveTextContent(
|
|
275
|
+
'Just now'
|
|
276
|
+
)
|
|
278
277
|
|
|
279
278
|
Object.assign(channel.state.messages[0], { created_at: undefined })
|
|
280
279
|
rerender(
|
|
281
280
|
<CustomChannelPreview {...defaultProps} channel={channel} unread={1} />
|
|
282
281
|
)
|
|
283
|
-
expect(
|
|
284
|
-
|
|
282
|
+
expect(
|
|
283
|
+
screen.queryByTestId('channel-preview-timestamp')
|
|
284
|
+
).not.toBeInTheDocument()
|
|
285
285
|
})
|
|
286
286
|
|
|
287
287
|
it.each([
|
|
@@ -192,7 +192,10 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
|
|
|
192
192
|
{participantName}
|
|
193
193
|
</h3>
|
|
194
194
|
{lastMessageTime && (
|
|
195
|
-
<span
|
|
195
|
+
<span
|
|
196
|
+
data-testid="channel-preview-timestamp"
|
|
197
|
+
className="text-xs text-[#717070] flex-shrink-0"
|
|
198
|
+
>
|
|
196
199
|
{lastMessageTime}
|
|
197
200
|
</span>
|
|
198
201
|
)}
|
|
@@ -204,7 +207,10 @@ const CustomChannelPreview = React.memo<ChannelPreviewUIComponentProps>(
|
|
|
204
207
|
{messagePreview}
|
|
205
208
|
</p>
|
|
206
209
|
{unreadCount > 0 && (
|
|
207
|
-
<span
|
|
210
|
+
<span
|
|
211
|
+
data-testid="channel-preview-unread-badge"
|
|
212
|
+
className="bg-[#7f22fe] text-white text-[10px] rounded-full h-4 flex items-center justify-center p-1 min-w-4 text-center flex-shrink-0"
|
|
213
|
+
>
|
|
208
214
|
{unreadCount > 99 ? '99+' : unreadCount}
|
|
209
215
|
</span>
|
|
210
216
|
)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { render, screen, waitFor } from '@testing-library/react'
|
|
2
2
|
import React, { useEffect } from 'react'
|
|
3
3
|
import { Channel as StreamChannel, StreamChat } from 'stream-chat'
|
|
4
4
|
import { Channel, Chat } from 'stream-chat-react'
|
|
@@ -75,42 +75,37 @@ describe('ChannelView outbound resolution', () => {
|
|
|
75
75
|
<RowProbe>{messageNode}</RowProbe>
|
|
76
76
|
)
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</Chat>
|
|
90
|
-
).rerender
|
|
91
|
-
})
|
|
78
|
+
const { rerender } = render(
|
|
79
|
+
<Chat client={client}>
|
|
80
|
+
<Channel channel={channel as StreamChannel}>
|
|
81
|
+
<ChannelView
|
|
82
|
+
channel={channel as StreamChannel}
|
|
83
|
+
resolveOutbound={firstResolver}
|
|
84
|
+
renderMessage={renderMessage}
|
|
85
|
+
/>
|
|
86
|
+
</Channel>
|
|
87
|
+
</Chat>
|
|
88
|
+
)
|
|
92
89
|
|
|
90
|
+
await waitFor(() => expect(mounts).toHaveBeenCalledOnce())
|
|
93
91
|
expect(
|
|
94
92
|
screen.queryByTestId('sent-message-outbound-status')
|
|
95
93
|
).not.toBeInTheDocument()
|
|
96
|
-
expect(mounts).toHaveBeenCalledOnce()
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
)
|
|
110
|
-
})
|
|
95
|
+
rerender(
|
|
96
|
+
<Chat client={client}>
|
|
97
|
+
<Channel channel={channel as StreamChannel}>
|
|
98
|
+
<ChannelView
|
|
99
|
+
channel={channel as StreamChannel}
|
|
100
|
+
resolveOutbound={secondResolver}
|
|
101
|
+
renderMessage={renderMessage}
|
|
102
|
+
/>
|
|
103
|
+
</Channel>
|
|
104
|
+
</Chat>
|
|
105
|
+
)
|
|
111
106
|
|
|
112
107
|
expect(
|
|
113
|
-
screen.
|
|
108
|
+
await screen.findByTestId('sent-message-outbound-status')
|
|
114
109
|
).toHaveTextContent('July 9th Class absentee')
|
|
115
110
|
expect(mounts).toHaveBeenCalledOnce()
|
|
116
111
|
expect(unmounts).not.toHaveBeenCalled()
|
|
@@ -3,7 +3,7 @@ import React from 'react'
|
|
|
3
3
|
import type { Channel } from 'stream-chat'
|
|
4
4
|
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
|
5
5
|
|
|
6
|
-
import { renderWithProviders, screen } from '../test/utils'
|
|
6
|
+
import { renderWithProviders, screen, within } from '../test/utils'
|
|
7
7
|
|
|
8
8
|
import { ChannelView } from './ChannelView'
|
|
9
9
|
|
|
@@ -222,26 +222,22 @@ describe('ChannelView', () => {
|
|
|
222
222
|
})
|
|
223
223
|
|
|
224
224
|
it('widens the content column in the official (read-only) channel via the width var', () => {
|
|
225
|
-
|
|
225
|
+
renderWithProviders(
|
|
226
226
|
<ChannelView
|
|
227
227
|
channel={createChannel({ channelType: OFFICIAL_CHANNEL_TYPE })}
|
|
228
228
|
/>
|
|
229
229
|
)
|
|
230
230
|
|
|
231
|
-
const root =
|
|
232
|
-
'.messaging-channel-view'
|
|
233
|
-
) as HTMLElement
|
|
231
|
+
const root = screen.getByTestId('channel-view')
|
|
234
232
|
expect(root.style.getPropertyValue('--lt-msg-card-width')).toBe('368px')
|
|
235
233
|
})
|
|
236
234
|
|
|
237
235
|
it('leaves the card-width var unset in a regular channel (default widths)', () => {
|
|
238
|
-
|
|
236
|
+
renderWithProviders(
|
|
239
237
|
<ChannelView channel={createChannel({ channelType: 'messaging' })} />
|
|
240
238
|
)
|
|
241
239
|
|
|
242
|
-
const root =
|
|
243
|
-
'.messaging-channel-view'
|
|
244
|
-
) as HTMLElement
|
|
240
|
+
const root = screen.getByTestId('channel-view')
|
|
245
241
|
expect(root.style.getPropertyValue('--lt-msg-card-width')).toBe('')
|
|
246
242
|
})
|
|
247
243
|
|
|
@@ -266,9 +262,7 @@ describe('ChannelView', () => {
|
|
|
266
262
|
).not.toBeInTheDocument()
|
|
267
263
|
expect(screen.getAllByText('Linker').length).toBeGreaterThanOrEqual(1)
|
|
268
264
|
|
|
269
|
-
|
|
270
|
-
expect(avatars.length).toBeGreaterThanOrEqual(1)
|
|
271
|
-
expect(avatars[0].closest('button')).toBeNull()
|
|
265
|
+
expect(screen.getAllByTestId('avatar').length).toBeGreaterThanOrEqual(1)
|
|
272
266
|
expect(screen.getByTestId('channel-actions-menu')).toBeInTheDocument()
|
|
273
267
|
})
|
|
274
268
|
|
|
@@ -284,16 +278,10 @@ describe('ChannelView', () => {
|
|
|
284
278
|
const button = screen.getByRole('button', {
|
|
285
279
|
name: /view details for linker/i,
|
|
286
280
|
})
|
|
287
|
-
expect(button).toHaveClass(
|
|
288
|
-
'appearance-none',
|
|
289
|
-
'border-0',
|
|
290
|
-
'bg-transparent',
|
|
291
|
-
'flex-col'
|
|
292
|
-
)
|
|
293
281
|
|
|
294
282
|
const avatars = screen.getAllByTestId('avatar')
|
|
295
283
|
expect(avatars.length).toBeGreaterThanOrEqual(1)
|
|
296
|
-
expect(avatars[0]
|
|
284
|
+
expect(button).toContainElement(avatars[0])
|
|
297
285
|
expect(screen.getByTestId('channel-actions-menu')).toBeInTheDocument()
|
|
298
286
|
|
|
299
287
|
button.click()
|
|
@@ -346,8 +334,8 @@ describe('ChannelView', () => {
|
|
|
346
334
|
)
|
|
347
335
|
|
|
348
336
|
expect(
|
|
349
|
-
screen.
|
|
350
|
-
).
|
|
337
|
+
within(screen.getByTestId('message-list')).queryByText('Linker')
|
|
338
|
+
).not.toBeInTheDocument()
|
|
351
339
|
})
|
|
352
340
|
|
|
353
341
|
it('renders the actions menu by default', () => {
|
|
@@ -641,14 +629,14 @@ describe('ChannelView', () => {
|
|
|
641
629
|
|
|
642
630
|
it('fires onMessageLinkClick with the href and undefined message when the link is not inside a message wrapper', () => {
|
|
643
631
|
const handler = vi.fn()
|
|
644
|
-
|
|
632
|
+
renderWithProviders(
|
|
645
633
|
<ChannelView channel={createChannel()} onMessageLinkClick={handler} />
|
|
646
634
|
)
|
|
647
635
|
|
|
648
636
|
const anchor = document.createElement('a')
|
|
649
637
|
anchor.href = 'https://example.com/clicked'
|
|
650
638
|
anchor.addEventListener('click', (e) => e.preventDefault())
|
|
651
|
-
|
|
639
|
+
screen.getByTestId('channel-view').appendChild(anchor)
|
|
652
640
|
anchor.click()
|
|
653
641
|
|
|
654
642
|
expect(handler).toHaveBeenCalledOnce()
|
|
@@ -670,7 +658,7 @@ describe('ChannelView', () => {
|
|
|
670
658
|
message,
|
|
671
659
|
]
|
|
672
660
|
|
|
673
|
-
|
|
661
|
+
renderWithProviders(
|
|
674
662
|
<ChannelView channel={channel} onMessageLinkClick={handler} />
|
|
675
663
|
)
|
|
676
664
|
|
|
@@ -680,7 +668,7 @@ describe('ChannelView', () => {
|
|
|
680
668
|
anchor.href = 'https://example.com/clicked'
|
|
681
669
|
anchor.addEventListener('click', (e) => e.preventDefault())
|
|
682
670
|
wrapper.appendChild(anchor)
|
|
683
|
-
|
|
671
|
+
screen.getByTestId('channel-view').appendChild(wrapper)
|
|
684
672
|
anchor.click()
|
|
685
673
|
|
|
686
674
|
expect(handler).toHaveBeenCalledOnce()
|
|
@@ -689,7 +677,7 @@ describe('ChannelView', () => {
|
|
|
689
677
|
|
|
690
678
|
it('passes undefined when the wrapper data-message-id does not match any message in state', () => {
|
|
691
679
|
const handler = vi.fn()
|
|
692
|
-
|
|
680
|
+
renderWithProviders(
|
|
693
681
|
<ChannelView channel={createChannel()} onMessageLinkClick={handler} />
|
|
694
682
|
)
|
|
695
683
|
|
|
@@ -699,7 +687,7 @@ describe('ChannelView', () => {
|
|
|
699
687
|
anchor.href = 'https://example.com/clicked'
|
|
700
688
|
anchor.addEventListener('click', (e) => e.preventDefault())
|
|
701
689
|
wrapper.appendChild(anchor)
|
|
702
|
-
|
|
690
|
+
screen.getByTestId('channel-view').appendChild(wrapper)
|
|
703
691
|
anchor.click()
|
|
704
692
|
|
|
705
693
|
expect(handler).toHaveBeenCalledOnce()
|
|
@@ -710,14 +698,12 @@ describe('ChannelView', () => {
|
|
|
710
698
|
})
|
|
711
699
|
|
|
712
700
|
it('does not throw when a link is clicked without onMessageLinkClick set', () => {
|
|
713
|
-
|
|
714
|
-
<ChannelView channel={createChannel()} />
|
|
715
|
-
)
|
|
701
|
+
renderWithProviders(<ChannelView channel={createChannel()} />)
|
|
716
702
|
|
|
717
703
|
const anchor = document.createElement('a')
|
|
718
704
|
anchor.href = 'https://example.com'
|
|
719
705
|
anchor.addEventListener('click', (e) => e.preventDefault())
|
|
720
|
-
|
|
706
|
+
screen.getByTestId('channel-view').appendChild(anchor)
|
|
721
707
|
expect(() => anchor.click()).not.toThrow()
|
|
722
708
|
})
|
|
723
709
|
})
|
|
@@ -36,13 +36,16 @@ describe('CustomLinkPreviewList', () => {
|
|
|
36
36
|
// alone dropped the hero for the whole ~1.5s debounce + scrape window, so
|
|
37
37
|
// the skeleton was two bare text bars that then snapped open to the 250px
|
|
38
38
|
// featured box (MES-1349).
|
|
39
|
-
|
|
39
|
+
stage({ status: LinkPreviewStatus.LOADING })
|
|
40
40
|
|
|
41
41
|
const shell = screen.getByLabelText(LOADING_LABEL)
|
|
42
42
|
expect(shell).toHaveAttribute('aria-busy', 'true')
|
|
43
|
+
// eslint-disable-next-line local/no-style-only-assertion -- MES-1349: the reserved 250px is the regression itself, and jsdom has no layout to observe the snap-open.
|
|
43
44
|
expect(shell.className).toContain('h-[250px]')
|
|
44
|
-
//
|
|
45
|
-
|
|
45
|
+
// The hero placeholder is the regression guard; the two chin bars stand
|
|
46
|
+
// in for the title and URL either way.
|
|
47
|
+
expect(screen.getByTestId('card-thumbnail-skeleton')).toBeInTheDocument()
|
|
48
|
+
expect(screen.getAllByTestId('card-body-skeleton-bar')).toHaveLength(2)
|
|
46
49
|
})
|
|
47
50
|
|
|
48
51
|
it('keeps the featured hero once a preview loads with an image', () => {
|
|
@@ -68,13 +71,13 @@ describe('CustomLinkPreviewList', () => {
|
|
|
68
71
|
// The inverted layout contract — see this component's doc comment. While
|
|
69
72
|
// this list scrolled and shrank on its own, a staged preview was cropped to
|
|
70
73
|
// keep the textarea above the keyboard instead of scrolling with it.
|
|
71
|
-
|
|
74
|
+
stage({
|
|
72
75
|
status: LinkPreviewStatus.LOADED,
|
|
73
76
|
title: 'Example story',
|
|
74
77
|
image_url: 'https://cdn.example.com/story.jpg',
|
|
75
78
|
})
|
|
76
79
|
|
|
77
|
-
const list =
|
|
80
|
+
const list = screen.getByTestId('link-preview-list')
|
|
78
81
|
expect(list).not.toHaveClass('overflow-y-auto')
|
|
79
82
|
expect(list).not.toHaveClass('min-h-0')
|
|
80
83
|
})
|
|
@@ -95,7 +95,10 @@ export const CustomLinkPreviewList = () => {
|
|
|
95
95
|
// scrolls sideways instead of spilling; at 320px the dismiss button's right
|
|
96
96
|
// edge lands ~6px past the fold, reachable by scrolling. That residue
|
|
97
97
|
// belongs to whoever owns the mobile layout contract, not to this component.
|
|
98
|
-
<div
|
|
98
|
+
<div
|
|
99
|
+
data-testid="link-preview-list"
|
|
100
|
+
className="messaging-link-preview-list flex flex-col items-stretch w-full gap-2"
|
|
101
|
+
>
|
|
99
102
|
{stateLinkPreviews.map((linkPreview) => {
|
|
100
103
|
// A LOADING preview has no `image_url` yet, so keying the layout off
|
|
101
104
|
// the image alone rendered the skeleton as two bare text bars and then
|