@linktr.ee/messaging-react 4.4.3-rc-1788164552 → 4.4.3-rc-1788166409

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "4.4.3-rc-1788164552",
3
+ "version": "4.4.3-rc-1788166409",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -50,8 +50,8 @@
50
50
  "chromatic:local": "yarn storybook:build && chromatic"
51
51
  },
52
52
  "dependencies": {
53
- "@linktr.ee/messaging-core": "2.6.0-rc-1788164552",
54
- "@linktr.ee/messaging-taxonomy": "0.11.0-rc-1788164552",
53
+ "@linktr.ee/messaging-core": "2.6.0-rc-1788166409",
54
+ "@linktr.ee/messaging-taxonomy": "0.11.0-rc-1788166409",
55
55
  "@phosphor-icons/react": "^2.1.10"
56
56
  },
57
57
  "devDependencies": {
@@ -67,26 +67,30 @@ describe('MediaPlayer', () => {
67
67
  })
68
68
 
69
69
  it('keeps an audio poster after canPlay but hides a video poster', () => {
70
- const audio = renderWithProviders(
71
- <MediaPlayer source={SOURCE} mimeType="audio/mpeg" poster={POSTER} />
72
- )
73
- const audioElement = audio.container.querySelector(
74
- 'audio'
75
- ) as HTMLAudioElement
76
- expect(audio.container.querySelectorAll('img')).toHaveLength(1)
77
- fireEvent.canPlay(audioElement)
78
- expect(audio.container.querySelectorAll('img')).toHaveLength(1)
79
- audio.unmount()
80
-
81
- const video = renderWithProviders(
82
- <MediaPlayer source={SOURCE} mimeType="video/mp4" poster={POSTER} />
83
- )
84
- const videoElement = video.container.querySelector(
85
- 'video'
86
- ) as HTMLVideoElement
87
- expect(video.container.querySelectorAll('img')).toHaveLength(1)
88
- fireEvent.canPlay(videoElement)
89
- expect(video.container.querySelectorAll('img')).toHaveLength(0)
70
+ {
71
+ const view = renderWithProviders(
72
+ <MediaPlayer source={SOURCE} mimeType="audio/mpeg" poster={POSTER} />
73
+ )
74
+ const audioElement = view.container.querySelector(
75
+ 'audio'
76
+ ) as HTMLAudioElement
77
+ expect(view.container.querySelectorAll('img')).toHaveLength(1)
78
+ fireEvent.canPlay(audioElement)
79
+ expect(view.container.querySelectorAll('img')).toHaveLength(1)
80
+ view.unmount()
81
+ }
82
+
83
+ {
84
+ const view = renderWithProviders(
85
+ <MediaPlayer source={SOURCE} mimeType="video/mp4" poster={POSTER} />
86
+ )
87
+ const videoElement = view.container.querySelector(
88
+ 'video'
89
+ ) as HTMLVideoElement
90
+ expect(view.container.querySelectorAll('img')).toHaveLength(1)
91
+ fireEvent.canPlay(videoElement)
92
+ expect(view.container.querySelectorAll('img')).toHaveLength(0)
93
+ }
90
94
  })
91
95
 
92
96
  it('uses a type icon as the initial placeholder when no poster is provided', async () => {
@@ -156,13 +160,9 @@ describe('MediaPlayer', () => {
156
160
  const root = container.firstElementChild as HTMLElement
157
161
 
158
162
  fireEvent.click(root)
159
- await waitFor(() =>
160
- expect(screen.getByRole('button', { name: 'Pause' })).toBeInTheDocument()
161
- )
163
+ await screen.findByRole('button', { name: 'Pause' })
162
164
  fireEvent.click(root)
163
- await waitFor(() =>
164
- expect(screen.getByRole('button', { name: 'Play' })).toBeInTheDocument()
165
- )
165
+ await screen.findByRole('button', { name: 'Play' })
166
166
  expect(play).toHaveBeenCalledTimes(1)
167
167
 
168
168
  const onContainerClick = vi.fn()
@@ -223,9 +223,7 @@ describe('MediaPlayer', () => {
223
223
  const button = screen.getByRole('button', { name: 'Play' })
224
224
 
225
225
  fireEvent.click(button)
226
- await waitFor(() =>
227
- expect(screen.getByRole('button', { name: 'Pause' })).toBeInTheDocument()
228
- )
226
+ await screen.findByRole('button', { name: 'Pause' })
229
227
  expect(container.firstElementChild).toBeInTheDocument()
230
228
  })
231
229
 
@@ -310,7 +308,7 @@ describe('MediaPlayer', () => {
310
308
  })
311
309
 
312
310
  it('renders the progress-only track without controls when requested', () => {
313
- const { container } = renderWithProviders(
311
+ renderWithProviders(
314
312
  <MediaPlayer
315
313
  source={SOURCE}
316
314
  mimeType="video/mp4"
@@ -323,7 +321,7 @@ describe('MediaPlayer', () => {
323
321
  screen.getByRole('slider', { name: 'Playback position' })
324
322
  ).toBeInTheDocument()
325
323
  expect(screen.queryByRole('button', { name: 'Play' })).toBeNull()
326
- expect(container.querySelectorAll('[role="slider"]')).toHaveLength(1)
324
+ expect(screen.getAllByRole('slider')).toHaveLength(1)
327
325
  })
328
326
 
329
327
  it('uses natural video dimensions after metadata and aspect-video before then', () => {
@@ -147,55 +147,63 @@ describe('AttachmentThumbnail', () => {
147
147
  })
148
148
 
149
149
  it('renders a document icon placeholder without a thumbnail', () => {
150
- const dark = renderWithProviders(
151
- <AttachmentThumbnail
152
- mimeType="application/pdf"
153
- variant="dark"
154
- title="PDF"
155
- />
156
- )
157
- const darkIcon = dark.container.querySelector('svg') as SVGElement
158
- expect(darkIcon).toHaveClass('size-12', 'text-white/20')
159
- expect(dark.container.firstElementChild).toHaveClass('bg-white/10')
160
- dark.unmount()
150
+ {
151
+ const view = renderWithProviders(
152
+ <AttachmentThumbnail
153
+ mimeType="application/pdf"
154
+ variant="dark"
155
+ title="PDF"
156
+ />
157
+ )
158
+ const darkIcon = view.container.querySelector('svg') as SVGElement
159
+ expect(darkIcon).toHaveClass('size-12', 'text-white/20')
160
+ expect(view.container.firstElementChild).toHaveClass('bg-white/10')
161
+ view.unmount()
162
+ }
161
163
 
162
- const light = renderWithProviders(
163
- <AttachmentThumbnail mimeType="application/pdf" variant="light" />
164
- )
165
- const lightIcon = light.container.querySelector('svg') as SVGElement
166
- expect(lightIcon).toHaveClass('size-12', 'text-black/20')
167
- expect(light.container.firstElementChild).toHaveClass('bg-black/5')
164
+ {
165
+ const view = renderWithProviders(
166
+ <AttachmentThumbnail mimeType="application/pdf" variant="light" />
167
+ )
168
+ const lightIcon = view.container.querySelector('svg') as SVGElement
169
+ expect(lightIcon).toHaveClass('size-12', 'text-black/20')
170
+ expect(view.container.firstElementChild).toHaveClass('bg-black/5')
171
+ }
168
172
  })
169
173
 
170
174
  it('renders the poster-only branch with variant-specific shell and alt fallback', () => {
171
- const dark = renderWithProviders(
172
- <AttachmentThumbnail
173
- mimeType="image/jpeg"
174
- thumbnailUrl={LINKTREE_THUMBNAIL}
175
- title="Poster"
176
- variant="dark"
177
- />
178
- )
179
- const darkImage = screen.getByRole('img', { name: 'Poster' })
180
- expect(darkImage).toHaveAttribute('src', OPTIMIZED_THUMBNAIL)
181
- expect(dark.container.firstElementChild).toHaveClass(
182
- 'aspect-video',
183
- 'bg-white/10'
184
- )
185
- dark.unmount()
175
+ {
176
+ const view = renderWithProviders(
177
+ <AttachmentThumbnail
178
+ mimeType="image/jpeg"
179
+ thumbnailUrl={LINKTREE_THUMBNAIL}
180
+ title="Poster"
181
+ variant="dark"
182
+ />
183
+ )
184
+ const darkImage = screen.getByRole('img', { name: 'Poster' })
185
+ expect(darkImage).toHaveAttribute('src', OPTIMIZED_THUMBNAIL)
186
+ expect(view.container.firstElementChild).toHaveClass(
187
+ 'aspect-video',
188
+ 'bg-white/10'
189
+ )
190
+ view.unmount()
191
+ }
186
192
 
187
- const light = renderWithProviders(
188
- <AttachmentThumbnail
189
- mimeType="image/jpeg"
190
- thumbnailUrl="https://external.example/poster.jpg"
191
- variant="light"
192
- />
193
- )
194
- expect(screen.getByRole('img')).toHaveAttribute('alt', '')
195
- expect(light.container.firstElementChild).toHaveClass(
196
- 'aspect-video',
197
- 'bg-black/5'
198
- )
193
+ {
194
+ const view = renderWithProviders(
195
+ <AttachmentThumbnail
196
+ mimeType="image/jpeg"
197
+ thumbnailUrl="https://external.example/poster.jpg"
198
+ variant="light"
199
+ />
200
+ )
201
+ expect(screen.getByRole('img')).toHaveAttribute('alt', '')
202
+ expect(view.container.firstElementChild).toHaveClass(
203
+ 'aspect-video',
204
+ 'bg-black/5'
205
+ )
206
+ }
199
207
  })
200
208
 
201
209
  it('renders the empty branch with a light placeholder', () => {
@@ -9,29 +9,33 @@ const thumbnail = <div data-testid="thumbnail">thumbnail</div>
9
9
 
10
10
  describe('AttachmentCard', () => {
11
11
  it('applies the dark and light root surfaces and type icon colors', () => {
12
- const dark = renderWithProviders(
13
- <AttachmentCard
14
- variant="dark"
15
- thumbnail={thumbnail}
16
- mimeType="application/pdf"
17
- title="Dark file"
18
- />
19
- )
20
- expect(screen.getByText('Dark file')).toBeInTheDocument()
21
- expect(dark.container.firstElementChild).toHaveClass('bg-[#1e2330]')
22
- expect(dark.container.querySelector('svg')).toHaveClass('text-white/55')
23
- dark.unmount()
12
+ {
13
+ const view = renderWithProviders(
14
+ <AttachmentCard
15
+ variant="dark"
16
+ thumbnail={thumbnail}
17
+ mimeType="application/pdf"
18
+ title="Dark file"
19
+ />
20
+ )
21
+ expect(screen.getByText('Dark file')).toBeInTheDocument()
22
+ expect(view.container.firstElementChild).toHaveClass('bg-[#1e2330]')
23
+ expect(view.container.querySelector('svg')).toHaveClass('text-white/55')
24
+ view.unmount()
25
+ }
24
26
 
25
- const light = renderWithProviders(
26
- <AttachmentCard
27
- variant="light"
28
- thumbnail={thumbnail}
29
- mimeType="application/pdf"
30
- title="Light file"
31
- />
32
- )
33
- expect(light.container.firstElementChild).toHaveClass('bg-white')
34
- expect(light.container.querySelector('svg')).toHaveClass('text-black/55')
27
+ {
28
+ const view = renderWithProviders(
29
+ <AttachmentCard
30
+ variant="light"
31
+ thumbnail={thumbnail}
32
+ mimeType="application/pdf"
33
+ title="Light file"
34
+ />
35
+ )
36
+ expect(view.container.firstElementChild).toHaveClass('bg-white')
37
+ expect(view.container.querySelector('svg')).toHaveClass('text-black/55')
38
+ }
35
39
  })
36
40
 
37
41
  it('uses the dark placeholder title and dims it when title is absent', () => {
@@ -132,7 +136,7 @@ describe('AttachmentCard', () => {
132
136
  )
133
137
  expect(container.querySelectorAll('.pointer-events-auto')).toHaveLength(2)
134
138
 
135
- const withoutSlots = renderWithProviders(
139
+ const view = renderWithProviders(
136
140
  <AttachmentCard
137
141
  variant="light"
138
142
  thumbnail={thumbnail}
@@ -140,7 +144,7 @@ describe('AttachmentCard', () => {
140
144
  />
141
145
  )
142
146
  expect(
143
- withoutSlots.container.querySelectorAll('.pointer-events-auto')
147
+ view.container.querySelectorAll('.pointer-events-auto')
144
148
  ).toHaveLength(0)
145
149
  })
146
150
 
@@ -42,100 +42,110 @@ describe('Avatar', () => {
42
42
  )
43
43
 
44
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()
45
+ {
46
+ const view = renderWithProviders(<Avatar id="user-1" name="Alice" />)
47
+ expect(view.container.querySelector('svg')).not.toBeInTheDocument()
48
+ }
49
49
 
50
- const starredAvatar = renderWithProviders(
51
- <Avatar id="user-1" name="Alice" starred />
52
- )
53
- expect(starredAvatar.container.querySelector('svg')).toBeInTheDocument()
50
+ {
51
+ const view = renderWithProviders(
52
+ <Avatar id="user-1" name="Alice" starred />
53
+ )
54
+ expect(view.container.querySelector('svg')).toBeInTheDocument()
55
+ }
54
56
  })
55
57
 
56
58
  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
- )
59
+ {
60
+ const view = renderWithProviders(<Avatar id="user-1" name="Alice" />)
61
+ const squircleElements = view.container.querySelectorAll(
62
+ '[style*="border-radius"]'
63
+ )
64
+ expect(squircleElements).toHaveLength(3)
65
+ squircleElements.forEach((element) =>
66
+ expect(element).toHaveStyle({ borderRadius: '1rem' })
67
+ )
68
+ }
67
69
 
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
- )
70
+ {
71
+ const view = renderWithProviders(
72
+ <Avatar id="user-1" name="Alice" shape="circle" />
73
+ )
74
+ const circleElements = view.container.querySelectorAll(
75
+ '[style*="border-radius"]'
76
+ )
77
+ expect(circleElements).toHaveLength(3)
78
+ circleElements.forEach((element) =>
79
+ expect(element).toHaveStyle({ borderRadius: '50%' })
80
+ )
81
+ }
78
82
  })
79
83
 
80
84
  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
- })
85
+ {
86
+ const view = renderWithProviders(
87
+ <Avatar id="user-1" name="Alice" size={40} />
88
+ )
89
+ expect(
90
+ view.container.querySelector('[data-testid="avatar-ring"]')
91
+ ).not.toHaveStyle({
92
+ borderWidth: '2px',
93
+ borderStyle: 'solid',
94
+ })
95
+ }
90
96
 
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
- })
97
+ {
98
+ const view = renderWithProviders(
99
+ <Avatar id="user-1" name="Alice" size={40} dmAgentEnabled />
100
+ )
101
+ expect(
102
+ view.container.querySelector('[data-testid="avatar-ring"]')
103
+ ).toHaveStyle({
104
+ borderWidth: '2px',
105
+ borderStyle: 'solid',
106
+ borderColor: 'var(--AI-Gradient, #7F22FE)',
107
+ boxShadow: 'inset 0 1px 2px 0 rgba(255, 255, 255, 0.5)',
108
+ })
109
+ }
102
110
 
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' })
111
+ {
112
+ const view = renderWithProviders(
113
+ <Avatar id="user-1" name="Alice" size={20} dmAgentEnabled />
114
+ )
115
+ expect(
116
+ view.container.querySelector('[data-testid="avatar-ring"]')
117
+ ).toHaveStyle({ borderWidth: '1px' })
118
+ }
109
119
  })
110
120
 
111
121
  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
- )
122
+ {
123
+ const view = renderWithProviders(<Avatar id="" name="Alice" />)
124
+ expect(screen.queryByRole('img')).not.toBeInTheDocument()
125
+ expect(view.container).toHaveTextContent('🍎')
126
+ expect(view.container.querySelector('.avatar-fallback')).toHaveClass(
127
+ 'avatar-fallback',
128
+ 'flex',
129
+ 'h-full',
130
+ 'w-full',
131
+ 'items-center',
132
+ 'justify-center',
133
+ 'font-semibold',
134
+ 'select-none',
135
+ 'transition-colors'
136
+ )
137
+ }
130
138
 
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
+ const view = renderWithProviders(
141
+ <Avatar id="" name="Alice" image="https://example.com/avatar.png" />
142
+ )
143
+ expect(screen.getByRole('img')).toHaveAttribute(
144
+ 'src',
145
+ 'https://example.com/avatar.png'
146
+ )
147
+ expect(view.container).not.toHaveTextContent('🍎')
148
+ }
139
149
  })
140
150
 
141
151
  it('applies the fallback background color and forwards custom classes', () => {
@@ -141,12 +141,12 @@ describe('CustomChannelPreview', () => {
141
141
  it('selects the channel by click and activation keys only', () => {
142
142
  const channel = createMockChannel([])
143
143
  const onChannelSelect = vi.fn()
144
- const { container } = renderWithProviders(
144
+ renderWithProviders(
145
145
  <ChannelListProvider value={{ onChannelSelect, debug: false }}>
146
146
  <CustomChannelPreview channel={channel} />
147
147
  </ChannelListProvider>
148
148
  )
149
- const row = container.querySelector('[role="button"]') as HTMLElement
149
+ const row = screen.getByRole('button')
150
150
 
151
151
  fireEvent.click(row)
152
152
  fireEvent.keyDown(row, { key: 'Enter' })
@@ -213,11 +213,11 @@ describe('CustomChannelPreview', () => {
213
213
  image: 'https://example.com/alice.png',
214
214
  }
215
215
 
216
- const { container, rerender } = renderWithProviders(
216
+ const { rerender } = renderWithProviders(
217
217
  <CustomChannelPreview {...defaultProps} channel={channel} />
218
218
  )
219
- expect(container.querySelectorAll('img')).toHaveLength(1)
220
- expect(container.querySelector('img')).toHaveAttribute(
219
+ expect(screen.getAllByRole('img', { hidden: true })).toHaveLength(1)
220
+ expect(screen.getByRole('img', { hidden: true })).toHaveAttribute(
221
221
  'src',
222
222
  'https://example.com/alice.png'
223
223
  )
@@ -229,7 +229,7 @@ describe('CustomChannelPreview', () => {
229
229
  rerender(
230
230
  <CustomChannelPreview {...defaultProps} channel={channel} unread={1} />
231
231
  )
232
- expect(container.querySelectorAll('img')).toHaveLength(0)
232
+ expect(screen.queryAllByRole('img', { hidden: true })).toHaveLength(0)
233
233
  })
234
234
 
235
235
  it.each([
@@ -359,13 +359,13 @@ describe('CustomMessageInput', () => {
359
359
  vi.unstubAllGlobals()
360
360
  })
361
361
 
362
- const renderCard = () => {
362
+ const mountCard = () => {
363
363
  const { container } = renderWithProviders(<CustomMessageInput />)
364
364
  return container.querySelector('.central-container') as HTMLElement
365
365
  }
366
366
 
367
367
  it('caps the card against the surface height and scrolls it as one region', () => {
368
- const card = renderCard()
368
+ const card = mountCard()
369
369
 
370
370
  expect(card).toHaveClass(
371
371
  'max-h-[calc(var(--messaging-surface-height,100dvh)*0.5)]',
@@ -384,7 +384,7 @@ describe('CustomMessageInput', () => {
384
384
  })
385
385
 
386
386
  it('lets the textarea grow to fit rather than scrolling its own overflow', () => {
387
- const card = renderCard()
387
+ const card = mountCard()
388
388
 
389
389
  // TextareaComposer defaults an absent maxRows to 1, so this has to be set.
390
390
  expect(screen.getByTestId('textarea-composer')).toHaveAttribute(
@@ -403,7 +403,7 @@ describe('CustomMessageInput', () => {
403
403
  })
404
404
 
405
405
  it('leaves plain-text pastes to the browser but lets file pastes reach Stream', () => {
406
- renderCard()
406
+ mountCard()
407
407
 
408
408
  // Stream's paste handler is dispatched from React's root, above this
409
409
  // card, so a listener further up stands in for it: the event reaching
@@ -428,7 +428,7 @@ describe('CustomMessageInput', () => {
428
428
  })
429
429
 
430
430
  it('detects a pasted file via clipboardData.items even when .files is empty (Safari)', () => {
431
- renderCard()
431
+ mountCard()
432
432
 
433
433
  // Safari never populates clipboardData.files on paste, even for an
434
434
  // image — only .items has it. Checking .files here would silently drop
@@ -448,7 +448,7 @@ describe('CustomMessageInput', () => {
448
448
  })
449
449
 
450
450
  it('scrolls the input back into view when the card shrinks under it', () => {
451
- const card = renderCard()
451
+ const card = mountCard()
452
452
  // jsdom has no layout, so stand in for a card whose cap now cuts the
453
453
  // input off below the fold.
454
454
  Object.defineProperty(card, 'scrollHeight', {
@@ -463,7 +463,7 @@ describe('CustomMessageInput', () => {
463
463
  })
464
464
 
465
465
  it('leaves the scroll position alone when nothing in the composer is focused', () => {
466
- const card = renderCard()
466
+ const card = mountCard()
467
467
  Object.defineProperty(card, 'scrollHeight', {
468
468
  configurable: true,
469
469
  value: 380,
@@ -478,7 +478,7 @@ describe('CustomMessageInput', () => {
478
478
  })
479
479
 
480
480
  it('sticks the send button to the bottom of the scroll, at its resting inset', () => {
481
- renderCard()
481
+ mountCard()
482
482
 
483
483
  // The offset must match the wrapper's own margin: sticky resolves against
484
484
  // the border box, so at bottom-0 the button would lose that inset and sit