Skip to content

Commit a66e8ab

Browse files
committed
test(ChatMessage/ChatMessages): add regression tests
1 parent 79a2132 commit a66e8ab

2 files changed

Lines changed: 107 additions & 2 deletions

File tree

test/components/ChatMessage.spec.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest'
22
import { axe } from 'vitest-axe'
33
import { mountSuspended } from '@nuxt/test-utils/runtime'
44
import { renderEach } from '../component-render'
5-
import ChatMessage from '../../src/runtime/components/ChatMessage.vue'
5+
import ChatMessage, { type ChatMessageSlots } from '../../src/runtime/components/ChatMessage.vue'
66
import theme from '#build/ui/chat-message'
77

88
describe('ChatMessage', () => {
@@ -40,4 +40,54 @@ describe('ChatMessage', () => {
4040

4141
expect(await axe(wrapper.element)).toHaveNoViolations()
4242
})
43+
44+
it('forwards id, role, parts, metadata and content to the content slot', async () => {
45+
const captured: Parameters<Exclude<ChatMessageSlots['content'], undefined>>[0][] = []
46+
await mountSuspended(ChatMessage, {
47+
props: { ...props, metadata: { foo: 'bar' }, content: 'fallback' },
48+
slots: {
49+
content: (slotProps) => {
50+
captured.push(slotProps)
51+
return 'x'
52+
}
53+
}
54+
})
55+
56+
expect(captured).toHaveLength(1)
57+
expect(captured[0]).toMatchObject({
58+
id: props.id,
59+
role: 'user',
60+
parts: props.parts,
61+
metadata: { foo: 'bar' },
62+
content: 'fallback'
63+
})
64+
})
65+
66+
it('does not leak ChatMessage-specific props into the content slot', async () => {
67+
const captured: Parameters<Exclude<ChatMessageSlots['content'], undefined>>[0][] = []
68+
await mountSuspended(ChatMessage, {
69+
props: {
70+
...props,
71+
icon: 'i-lucide-user',
72+
avatar: { src: 'https://github.com/benjamincanac.png' },
73+
variant: 'soft' as const,
74+
side: 'right' as const,
75+
actions: [{ icon: 'i-lucide-copy', label: 'Copy' }],
76+
compact: true,
77+
class: 'foo',
78+
ui: {}
79+
},
80+
slots: {
81+
content: (slotProps) => {
82+
captured.push(slotProps)
83+
return 'x'
84+
},
85+
actions: () => 'actions'
86+
}
87+
})
88+
89+
for (const key of ['as', 'icon', 'avatar', 'variant', 'side', 'actions', 'compact', 'class', 'ui']) {
90+
expect(captured[0]).not.toHaveProperty(key)
91+
}
92+
})
4393
})

test/components/ChatMessages.spec.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest'
22
import { axe } from 'vitest-axe'
33
import { mountSuspended } from '@nuxt/test-utils/runtime'
44
import { renderEach } from '../component-render'
5-
import ChatMessages from '../../src/runtime/components/ChatMessages.vue'
5+
import ChatMessages, { type ChatMessagesSlots } from '../../src/runtime/components/ChatMessages.vue'
66

77
describe('ChatMessages', () => {
88
const statuses = ['ready', 'submitted', 'streaming', 'error'] as any
@@ -43,4 +43,59 @@ describe('ChatMessages', () => {
4343

4444
expect(await axe(wrapper.element)).toHaveNoViolations()
4545
})
46+
47+
it('forwards content slot props together with the deprecated `message`', async () => {
48+
const messages = [
49+
{ id: 'm-1', role: 'user' as const, parts: [{ type: 'text' as const, text: 'a' }], metadata: { foo: 'bar' } },
50+
{ id: 'm-2', role: 'assistant' as const, parts: [{ type: 'text' as const, text: 'b' }] }
51+
]
52+
const captured: Parameters<Exclude<ChatMessagesSlots['content'], undefined>>[0][] = []
53+
await mountSuspended(ChatMessages, {
54+
props: { messages },
55+
slots: {
56+
content: (slotProps) => {
57+
captured.push(slotProps)
58+
return 'x'
59+
}
60+
}
61+
})
62+
63+
expect(captured).toHaveLength(2)
64+
expect(captured[0]).toMatchObject({
65+
id: 'm-1',
66+
role: 'user',
67+
parts: messages[0]!.parts,
68+
metadata: { foo: 'bar' },
69+
message: messages[0]
70+
})
71+
expect(captured[1]).toMatchObject({
72+
id: 'm-2',
73+
role: 'assistant',
74+
parts: messages[1]!.parts,
75+
message: messages[1]
76+
})
77+
})
78+
79+
it('forwards `message` to the actions slot', async () => {
80+
const captured: Parameters<Exclude<ChatMessagesSlots['actions'], undefined>>[0][] = []
81+
await mountSuspended(ChatMessages, {
82+
props: {
83+
...props,
84+
user: { actions: [{ icon: 'i-lucide-copy', label: 'Copy' }] },
85+
assistant: { actions: [{ icon: 'i-lucide-copy', label: 'Copy' }] }
86+
},
87+
slots: {
88+
actions: (slotProps) => {
89+
captured.push(slotProps)
90+
return 'x'
91+
}
92+
}
93+
})
94+
95+
expect(captured.length).toBeGreaterThan(0)
96+
for (const p of captured) {
97+
expect(p).toHaveProperty('message')
98+
expect(p).toHaveProperty('actions')
99+
}
100+
})
46101
})

0 commit comments

Comments
 (0)