@quidgest/chatbot 0.5.3-dev.0 → 0.5.3
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/components/ChatBot/ChatBot.vue.d.ts +0 -2
- package/dist/components/ChatBot/types.d.ts +4 -3
- package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +3 -3
- package/dist/components/ChatBotInput/index.d.ts +2 -2
- package/dist/components/ChatBotInput/types.d.ts +4 -4
- package/dist/components/ChatBotMessage/ChatBotMessage.vue.d.ts +1 -3
- package/dist/components/ChatBotMessage/__tests__/ChatBotMessage.spec.d.ts +1 -0
- package/dist/components/ChatBotMessage/types.d.ts +3 -2
- package/dist/components/FieldPreview/FieldPreview.vue.d.ts +0 -2
- package/dist/composables/useChatApi.d.ts +1 -1
- package/dist/composables/useChatMessages.d.ts +2 -2
- package/dist/composables/useTexts.d.ts +1 -4
- package/dist/index.js +25 -25
- package/dist/index.mjs +2308 -1661
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/assets/styles/preview-file.scss +70 -0
- package/src/assets/styles/styles.scss +9 -33
- package/src/components/ChatBot/ChatBot.vue +70 -71
- package/src/components/ChatBot/types.ts +4 -3
- package/src/components/ChatBotInput/ChatBotInput.vue +81 -74
- package/src/components/ChatBotInput/__tests__/ChatBotInput.spec.ts +29 -42
- package/src/components/ChatBotInput/__tests__/__snapshots__/ChatBotInput.spec.ts.snap +5 -5
- package/src/components/ChatBotInput/index.ts +2 -2
- package/src/components/ChatBotInput/types.ts +4 -4
- package/src/components/ChatBotMessage/ChatBotMessage.vue +34 -8
- package/src/components/ChatBotMessage/__tests__/ChatBotMessage.spec.ts +256 -0
- package/src/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.ts +4 -4
- package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessage.spec.ts.snap +35 -0
- package/src/components/ChatBotMessage/types.ts +4 -3
- package/src/components/ChatToolBar/ChatToolBar.vue +1 -2
- package/src/components/ChatToolBar/__tests__/ChatToolBar.spec.ts +18 -38
- package/src/components/FieldPreview/FieldPreview.vue +9 -31
- package/src/components/FieldPreview/__tests__/__snapshots__/FieldPreview.spec.ts.snap +11 -5
- package/src/components/FieldPreview/field-preview.scss +0 -4
- package/src/components/MarkdownRender/MarkdownRender.vue +0 -1
- package/src/composables/__tests__/useChatMessages.spec.ts +1 -14
- package/src/composables/useChatApi.ts +53 -57
- package/src/composables/useChatMessages.ts +4 -8
- package/src/composables/useTexts.ts +2 -5
- package/src/test/setup.ts +0 -5
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// Components
|
|
2
|
+
import { ChatBotMessage, ChatBotMessageButtons } from '..'
|
|
3
|
+
import PulseDots from '@/components/PulseDots/PulseDots.vue'
|
|
4
|
+
import { FieldPreview } from '@/components/FieldPreview/'
|
|
5
|
+
import { MarkdownRender } from '@/components/MarkdownRender/'
|
|
6
|
+
|
|
7
|
+
// Utils
|
|
8
|
+
import { mount } from '@vue/test-utils'
|
|
9
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
10
|
+
import { parseFieldValue } from '@/utils/parseFieldValue'
|
|
11
|
+
|
|
12
|
+
// Types
|
|
13
|
+
import type { ChatBotMessageProps } from '..'
|
|
14
|
+
|
|
15
|
+
// Composable
|
|
16
|
+
import { useChatMessages } from '@/composables/useChatMessages'
|
|
17
|
+
import { ChatBotFile } from '@/components/ChatBotInput'
|
|
18
|
+
|
|
19
|
+
describe('ChatBotMessage', () => {
|
|
20
|
+
const fixedDate = new Date('2025-01-01T11:47:00')
|
|
21
|
+
const { clearMessages, addChatMessage } = useChatMessages()
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.useFakeTimers()
|
|
25
|
+
vi.setSystemTime(fixedDate)
|
|
26
|
+
|
|
27
|
+
navigator.clipboard.writeText('')
|
|
28
|
+
|
|
29
|
+
clearMessages()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
vi.useRealTimers()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const props: ChatBotMessageProps = {
|
|
37
|
+
sender: 'bot',
|
|
38
|
+
message: 'Hello, this is a test message.',
|
|
39
|
+
date: fixedDate,
|
|
40
|
+
loading: false,
|
|
41
|
+
dateFormat: 'HH:mm',
|
|
42
|
+
apiEndpoint: 'https://api.example.com/chat',
|
|
43
|
+
fields: [],
|
|
44
|
+
userImage: '',
|
|
45
|
+
chatbotImage: '',
|
|
46
|
+
file: undefined
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
it('renders the component with default props', () => {
|
|
50
|
+
const wrapper = mount(ChatBotMessage, { props })
|
|
51
|
+
expect(wrapper.html()).toMatchSnapshot()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('renders the image preview when imagePreviewUrl is provided', () => {
|
|
55
|
+
const file: ChatBotFile = {
|
|
56
|
+
fileData: new File(['dummy content'], 'example.png', { type: 'image/png' }),
|
|
57
|
+
previewUrl: 'tottaly-legit-image.png'
|
|
58
|
+
}
|
|
59
|
+
const wrapper = mount(ChatBotMessage, {
|
|
60
|
+
props: { ...props, file }
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const img = wrapper.find('.q-chatbot__image-preview img')
|
|
64
|
+
expect(img.exists()).toBe(true)
|
|
65
|
+
expect(img.attributes('src')).toBe('tottaly-legit-image.png')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('shows loading state when loading is true', () => {
|
|
69
|
+
const wrapper = mount(ChatBotMessage, {
|
|
70
|
+
props: { ...props, loading: true }
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const pulseDots = wrapper.findComponent(PulseDots)
|
|
74
|
+
expect(pulseDots.exists()).toBe(true)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('renders the field preview component when fields are provided and the sender is bot', () => {
|
|
78
|
+
const wrapper = mount(ChatBotMessage, {
|
|
79
|
+
props: {
|
|
80
|
+
...props,
|
|
81
|
+
sender: 'bot',
|
|
82
|
+
fields: [
|
|
83
|
+
{
|
|
84
|
+
type: 'text',
|
|
85
|
+
text: 'Sample Field',
|
|
86
|
+
name: 'This is a sample field value'
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const fieldPreview = wrapper.findComponent(FieldPreview)
|
|
93
|
+
expect(fieldPreview.exists()).toBe(true)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('renders a normal when no fields are provided and the sender is user', () => {
|
|
97
|
+
const wrapper = mount(ChatBotMessage, {
|
|
98
|
+
props: { ...props, sender: 'user' }
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const fieldPreview = wrapper.findComponent(FieldPreview)
|
|
102
|
+
expect(fieldPreview.exists()).toBe(false)
|
|
103
|
+
|
|
104
|
+
const markdownRender = wrapper.findComponent(MarkdownRender)
|
|
105
|
+
expect(markdownRender.exists()).toBe(false)
|
|
106
|
+
|
|
107
|
+
const messageDiv = wrapper.find('.q-chatbot__user-text')
|
|
108
|
+
expect(messageDiv.exists()).toBe(true)
|
|
109
|
+
expect(messageDiv.text()).toBe(props.message)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('renders markdown-render when no fields are provided', () => {
|
|
113
|
+
const wrapper = mount(ChatBotMessage, { props })
|
|
114
|
+
|
|
115
|
+
const markdownRender = wrapper.findComponent(MarkdownRender)
|
|
116
|
+
expect(markdownRender.exists()).toBe(true)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('adds the text to the clipboard when copy event is emmited', async () => {
|
|
120
|
+
const wrapper = mount(ChatBotMessage, { props })
|
|
121
|
+
|
|
122
|
+
const messageButtons = wrapper.findComponent(ChatBotMessageButtons)
|
|
123
|
+
const copyButton = messageButtons.find('.q-chatbot__copy-button')
|
|
124
|
+
|
|
125
|
+
await copyButton.trigger('click')
|
|
126
|
+
const clipboardText = await navigator.clipboard.readText()
|
|
127
|
+
|
|
128
|
+
expect(navigator.clipboard).toBeDefined()
|
|
129
|
+
expect(clipboardText).toBe(props.message)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('does not add the text to the clipboard if message is empty', async () => {
|
|
133
|
+
const wrapper = mount(ChatBotMessage, { props: { ...props, message: '' } })
|
|
134
|
+
const messageButtons = wrapper.findComponent(ChatBotMessageButtons)
|
|
135
|
+
const copyButton = messageButtons.find('.q-chatbot__copy-button')
|
|
136
|
+
|
|
137
|
+
await copyButton.trigger('click')
|
|
138
|
+
const clipboardText = await navigator.clipboard.readText()
|
|
139
|
+
|
|
140
|
+
expect(navigator.clipboard).toBeDefined()
|
|
141
|
+
expect(clipboardText).toBe('')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('catchs errors when trying to copy to clipboard', async () => {
|
|
145
|
+
const wrapper = mount(ChatBotMessage, { props })
|
|
146
|
+
const messageButtons = wrapper.findComponent(ChatBotMessageButtons)
|
|
147
|
+
const copyButton = messageButtons.find('.q-chatbot__copy-button')
|
|
148
|
+
|
|
149
|
+
vi.spyOn(navigator.clipboard, 'writeText').mockRejectedValueOnce(
|
|
150
|
+
new Error('Clipboard error')
|
|
151
|
+
)
|
|
152
|
+
// Suppress console.error for this test
|
|
153
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
154
|
+
|
|
155
|
+
await copyButton.trigger('click')
|
|
156
|
+
const clipboardText = await navigator.clipboard.readText()
|
|
157
|
+
|
|
158
|
+
expect(navigator.clipboard).toBeDefined()
|
|
159
|
+
expect(clipboardText).toBe('')
|
|
160
|
+
consoleErrorSpy.mockRestore()
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('emits apply-fields event with fields when apply all button is clicked', async () => {
|
|
164
|
+
const testFields = [
|
|
165
|
+
{
|
|
166
|
+
type: 'text',
|
|
167
|
+
text: 'Sample Field',
|
|
168
|
+
name: 'This is a sample field value'
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
type: 'number',
|
|
172
|
+
text: 'Number Field',
|
|
173
|
+
name: '42'
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
addChatMessage('Test message 1', 'bot').fields = testFields
|
|
177
|
+
|
|
178
|
+
const wrapper = mount(ChatBotMessage, {
|
|
179
|
+
props: { ...props, sender: 'bot', fields: testFields }
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
const messageButtons = wrapper.findComponent(ChatBotMessageButtons)
|
|
183
|
+
const applyAllButton = messageButtons.find('.q-chatbot__apply-all-button')
|
|
184
|
+
|
|
185
|
+
expect(applyAllButton.exists()).toBe(true)
|
|
186
|
+
await applyAllButton.trigger('click')
|
|
187
|
+
|
|
188
|
+
const fieldsToApply = testFields.map((field) => {
|
|
189
|
+
return {
|
|
190
|
+
name: field.name,
|
|
191
|
+
text: parseFieldValue(field.type, field.text)
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
expect(wrapper.emitted('apply-fields')).toBeTruthy()
|
|
196
|
+
expect(wrapper.emitted('apply-fields')?.[0]).toEqual([fieldsToApply])
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('emits apply-fieldd when a single field is applied', async () => {
|
|
200
|
+
const testFields = [
|
|
201
|
+
{
|
|
202
|
+
type: 'text',
|
|
203
|
+
text: 'Sample Field',
|
|
204
|
+
name: 'This is a sample field value'
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
addChatMessage('Test message 1', 'bot').fields = testFields
|
|
209
|
+
|
|
210
|
+
const wrapper = mount(ChatBotMessage, {
|
|
211
|
+
props: { ...props, sender: 'bot', fields: testFields }
|
|
212
|
+
})
|
|
213
|
+
const fieldPreview = wrapper.findComponent(FieldPreview)
|
|
214
|
+
expect(fieldPreview.exists()).toBe(true)
|
|
215
|
+
|
|
216
|
+
const applyButton = fieldPreview.find("[data-testid='apply-button']")
|
|
217
|
+
expect(applyButton.exists()).toBe(true)
|
|
218
|
+
|
|
219
|
+
await applyButton.trigger('click')
|
|
220
|
+
|
|
221
|
+
const fieldToApply = [
|
|
222
|
+
{
|
|
223
|
+
name: testFields[0].name,
|
|
224
|
+
text: parseFieldValue(testFields[0].type, testFields[0].text)
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
expect(wrapper.emitted('apply-fields')).toBeTruthy()
|
|
229
|
+
expect(wrapper.emitted('apply-fields')?.[0]).toEqual([fieldToApply])
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('renders a different preview when the file is not an image', async () => {
|
|
233
|
+
const file: ChatBotFile = {
|
|
234
|
+
fileData: new File(['dummy content'], 'example.pdf', { type: 'application/pdf' }),
|
|
235
|
+
previewUrl: ''
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const wrapper = mount(ChatBotMessage, {
|
|
239
|
+
props: { ...props, file }
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
const img = wrapper.find('.q-chatbot__image-preview img')
|
|
243
|
+
expect(img.exists()).toBe(false)
|
|
244
|
+
|
|
245
|
+
const filePreview = wrapper.find('.q-chatbot__file-preview-container')
|
|
246
|
+
expect(filePreview.exists()).toBe(true)
|
|
247
|
+
|
|
248
|
+
const fileName = wrapper.find('.q-chatbot__file-name')
|
|
249
|
+
expect(fileName.exists()).toBe(true)
|
|
250
|
+
expect(fileName.text()).toBe(file.fileData.name)
|
|
251
|
+
|
|
252
|
+
const fileExtension = wrapper.find('.q-chatbot__file-extension')
|
|
253
|
+
expect(fileExtension.exists()).toBe(true)
|
|
254
|
+
expect(fileExtension.text()).toBe('PDF')
|
|
255
|
+
})
|
|
256
|
+
})
|
|
@@ -85,8 +85,8 @@ describe('ChatBotMessageButtons', () => {
|
|
|
85
85
|
const message = addChatMessage('Test message 1', 'bot')
|
|
86
86
|
|
|
87
87
|
message.fields = [
|
|
88
|
-
{ name: 'field1', text: 'value1', type: 'text'
|
|
89
|
-
{ name: 'field2', text: 'value2', type: 'text'
|
|
88
|
+
{ name: 'field1', text: 'value1', type: 'text' },
|
|
89
|
+
{ name: 'field2', text: 'value2', type: 'text' }
|
|
90
90
|
]
|
|
91
91
|
|
|
92
92
|
const wrapper = mount(ChatBotMessageButtons, {
|
|
@@ -104,8 +104,8 @@ describe('ChatBotMessageButtons', () => {
|
|
|
104
104
|
const message = addChatMessage('Test message 1', 'bot')
|
|
105
105
|
|
|
106
106
|
message.fields = [
|
|
107
|
-
{ name: 'field1', text: 'value1', type: 'text'
|
|
108
|
-
{ name: 'field2', text: 'value2', type: 'text'
|
|
107
|
+
{ name: 'field1', text: 'value1', type: 'text' },
|
|
108
|
+
{ name: 'field2', text: 'value2', type: 'text' }
|
|
109
109
|
]
|
|
110
110
|
|
|
111
111
|
const wrapper = mount(ChatBotMessageButtons, {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`ChatBotMessage > renders the component with default props 1`] = `
|
|
4
|
+
"<div class="q-chatbot__message-container"><span data-test="" type="img" alt="Sender Image" class="q-chatbot__profile"></span>
|
|
5
|
+
<div class="q-chatbot__message-wrapper">
|
|
6
|
+
<!--v-if-->
|
|
7
|
+
<div class="q-chatbot__message">
|
|
8
|
+
<div class="markdown-renderer q-chatbot__text">
|
|
9
|
+
<p>Hello, this is a test message.</p>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
<!--teleport start-->
|
|
14
|
+
<transition-stub name="fade" appear="true" persisted="false" css="true">
|
|
15
|
+
<!--v-if-->
|
|
16
|
+
</transition-stub>
|
|
17
|
+
<transition-stub name="fade" appear="true" persisted="false" css="true">
|
|
18
|
+
<!--v-if-->
|
|
19
|
+
</transition-stub>
|
|
20
|
+
<!--teleport end-->
|
|
21
|
+
<!--v-if-->
|
|
22
|
+
<div class="q-chatbot__feedback-buttons">
|
|
23
|
+
<div class="q-button-group" role="group"><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless q-chatbot__good-response-button" title="Good response">
|
|
24
|
+
<!--v-if--><span class="q-button__content"><span data-test="thumb-up"></span> </span>
|
|
25
|
+
</button><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless q-chatbot__bad-response-button" title="Bad response">
|
|
26
|
+
<!--v-if--><span class="q-button__content"><span data-test="thumb-down"></span> </span>
|
|
27
|
+
</button><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless q-chatbot__copy-button" title="Copy response">
|
|
28
|
+
<!--v-if--><span class="q-button__content"><span data-test="copy-content"></span> </span>
|
|
29
|
+
</button>
|
|
30
|
+
<!--v-if-->
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="q-chatbot__sender">11:47</div>
|
|
34
|
+
</div>"
|
|
35
|
+
`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ChatBotMessageSender, FieldData } from '@/components/ChatBot/types'
|
|
1
|
+
import type { ChatBotMessageSender, FieldData } from '@/components/ChatBot/types'
|
|
2
|
+
import type { ChatBotFile } from '../ChatBotInput'
|
|
2
3
|
|
|
3
4
|
export type ChatBotMessageProps = {
|
|
4
5
|
/*
|
|
@@ -27,9 +28,9 @@ export type ChatBotMessageProps = {
|
|
|
27
28
|
dateFormat: string
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
+
* File Preview
|
|
31
32
|
*/
|
|
32
|
-
|
|
33
|
+
file?: ChatBotFile
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Default api endpoint
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
<q-select
|
|
5
5
|
v-if="hasAgents"
|
|
6
6
|
v-model="selectedChat"
|
|
7
|
-
inline
|
|
8
7
|
class="q-chatbot__tools-select-input"
|
|
9
8
|
size="medium"
|
|
10
9
|
:items="availableChats"
|
|
@@ -23,7 +22,7 @@
|
|
|
23
22
|
</template>
|
|
24
23
|
|
|
25
24
|
<script setup lang="ts">
|
|
26
|
-
import { QButton, QIcon } from '@quidgest/ui/components'
|
|
25
|
+
import { QButton, QSelect, QIcon } from '@quidgest/ui/components'
|
|
27
26
|
|
|
28
27
|
import { useTexts } from '@/composables/useTexts'
|
|
29
28
|
import { ChatToolBarProps } from './types'
|
|
@@ -7,7 +7,6 @@ import { mount } from '@vue/test-utils'
|
|
|
7
7
|
|
|
8
8
|
// Types
|
|
9
9
|
import type { ChatToolBarProps } from '../'
|
|
10
|
-
import { nextTick } from 'vue'
|
|
11
10
|
|
|
12
11
|
describe('ChatToolBar', () => {
|
|
13
12
|
const props: ChatToolBarProps = {
|
|
@@ -54,25 +53,24 @@ describe('ChatToolBar', () => {
|
|
|
54
53
|
const wrapper = mount(ChatToolBar, {
|
|
55
54
|
props: {
|
|
56
55
|
...props,
|
|
57
|
-
availableAgents: [
|
|
58
|
-
|
|
56
|
+
availableAgents: [
|
|
57
|
+
{ key: 'agent1', value: 'Agent 1', formId: 'key' },
|
|
58
|
+
{ key: 'agent2', value: 'Agent 2', formId: 'key' }
|
|
59
|
+
],
|
|
60
|
+
selectedAgentKey: 'agent1'
|
|
59
61
|
}
|
|
60
62
|
})
|
|
61
63
|
|
|
62
|
-
const select = wrapper.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const overlay = wrapper.find('.q-overlay__content')
|
|
66
|
-
|
|
67
|
-
const item = overlay.find('.q-list-item')
|
|
68
|
-
await item.trigger('click')
|
|
64
|
+
const select = wrapper.findComponent({ name: 'QSelect' })
|
|
65
|
+
// Simulate selecting a new agent
|
|
66
|
+
await select.vm.$emit('update:modelValue', 'agent2')
|
|
69
67
|
|
|
70
68
|
expect(wrapper.emitted()['change-chat']).toBeTruthy()
|
|
71
|
-
expect(wrapper.emitted()['change-chat'][0]).toEqual([
|
|
69
|
+
expect(wrapper.emitted()['change-chat']?.[0]).toEqual([
|
|
72
70
|
{
|
|
73
|
-
key: '
|
|
71
|
+
key: 'agent2',
|
|
74
72
|
formId: 'key',
|
|
75
|
-
value: 'Agent
|
|
73
|
+
value: 'Agent 2'
|
|
76
74
|
}
|
|
77
75
|
])
|
|
78
76
|
})
|
|
@@ -88,21 +86,9 @@ describe('ChatToolBar', () => {
|
|
|
88
86
|
selectedAgentKey: 'agent1'
|
|
89
87
|
}
|
|
90
88
|
})
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
await select.
|
|
94
|
-
|
|
95
|
-
const overlay = wrapper.find('.q-overlay__content')
|
|
96
|
-
|
|
97
|
-
// Manually add a fake item that does not exist in the options
|
|
98
|
-
const fakeItem = document.createElement('div')
|
|
99
|
-
fakeItem.setAttribute('data-key', 'agent3')
|
|
100
|
-
overlay.element.appendChild(fakeItem)
|
|
101
|
-
|
|
102
|
-
await nextTick()
|
|
103
|
-
const item = overlay.find('[data-key="agent3"]')
|
|
104
|
-
await item.trigger('click')
|
|
105
|
-
|
|
89
|
+
const select = wrapper.findComponent({ name: 'QSelect' })
|
|
90
|
+
// Simulate selecting a new agent
|
|
91
|
+
await select.vm.$emit('update:modelValue', 'agent3')
|
|
106
92
|
expect(wrapper.emitted()['change-chat']).toBeFalsy()
|
|
107
93
|
})
|
|
108
94
|
|
|
@@ -117,18 +103,12 @@ describe('ChatToolBar', () => {
|
|
|
117
103
|
selectedAgentKey: 'agent1'
|
|
118
104
|
}
|
|
119
105
|
})
|
|
120
|
-
const select = wrapper.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const overlay = wrapper.find('.q-overlay__content')
|
|
124
|
-
const items = overlay.findAll('.q-list-item')
|
|
125
|
-
|
|
126
|
-
// Default options is always the last one
|
|
127
|
-
const defaultOption = items[items.length - 1]
|
|
128
|
-
await defaultOption.trigger('click')
|
|
106
|
+
const select = wrapper.findComponent({ name: 'QSelect' })
|
|
107
|
+
// Simulate selecting a new agent
|
|
108
|
+
await select.vm.$emit('update:modelValue', '')
|
|
129
109
|
|
|
130
110
|
expect(wrapper.emitted()['change-chat']).toBeTruthy()
|
|
131
|
-
expect(wrapper.emitted()['change-chat'][0]).toEqual([
|
|
111
|
+
expect(wrapper.emitted()['change-chat']?.[0]).toEqual([
|
|
132
112
|
{
|
|
133
113
|
key: '',
|
|
134
114
|
formId: ''
|
|
@@ -5,24 +5,23 @@
|
|
|
5
5
|
{{ texts.suggestionsForField }} <b>{{ props.name }}</b>
|
|
6
6
|
</span>
|
|
7
7
|
</div>
|
|
8
|
-
<div
|
|
8
|
+
<div class="q-field-preview__content">
|
|
9
9
|
<component
|
|
10
10
|
:is="previewComponent"
|
|
11
11
|
v-bind="previewComponentProps" />
|
|
12
12
|
</div>
|
|
13
13
|
<div class="q-field-preview__footer">
|
|
14
14
|
<q-button-group borderless>
|
|
15
|
+
<!-- <q-button
|
|
16
|
+
:title="texts.regenerateResponse"
|
|
17
|
+
:disabled="props.disabled"
|
|
18
|
+
borderless
|
|
19
|
+
@click="console.log('Regenerate response')">
|
|
20
|
+
<q-icon icon="reset" />
|
|
21
|
+
</q-button> -->
|
|
15
22
|
<q-button
|
|
16
|
-
:title="texts.regenerateResponse"
|
|
17
|
-
:disabled="blockRegenerateButton"
|
|
18
|
-
:readonly="blockRegenerateButton"
|
|
19
|
-
borderless
|
|
20
|
-
@click="regenerate">
|
|
21
|
-
<q-icon icon="reset" />
|
|
22
|
-
</q-button>
|
|
23
|
-
<q-button
|
|
24
|
-
:label="texts.apply"
|
|
25
23
|
data-testid="apply-button"
|
|
24
|
+
:label="texts.apply"
|
|
26
25
|
:disabled="blockApplyButton"
|
|
27
26
|
:readonly="blockApplyButton"
|
|
28
27
|
@click="emitApply">
|
|
@@ -44,19 +43,13 @@
|
|
|
44
43
|
const props = defineProps<FieldPreviewProps>()
|
|
45
44
|
const emit = defineEmits<{
|
|
46
45
|
(e: 'apply', text: unknown): void
|
|
47
|
-
(e: 'regenerate', name: string): void
|
|
48
46
|
}>()
|
|
49
47
|
const texts = useTexts()
|
|
50
48
|
const blockApply = ref(props.applied)
|
|
51
|
-
const blockRegenerate = ref(false)
|
|
52
49
|
const blockApplyButton = computed(() => {
|
|
53
50
|
return props.disabled || blockApply.value
|
|
54
51
|
})
|
|
55
52
|
|
|
56
|
-
const blockRegenerateButton = computed(() => {
|
|
57
|
-
return props.disabled || blockRegenerate.value
|
|
58
|
-
})
|
|
59
|
-
|
|
60
53
|
const previewComponent = computed(() => {
|
|
61
54
|
if (props.type === 'text' || props.type === 'multiline_text') return MarkdownRender
|
|
62
55
|
|
|
@@ -72,14 +65,6 @@
|
|
|
72
65
|
return componentProps
|
|
73
66
|
})
|
|
74
67
|
|
|
75
|
-
const classes = computed(() => {
|
|
76
|
-
const classes = ['q-field-preview__content']
|
|
77
|
-
|
|
78
|
-
if (previewComponent.value !== MarkdownRender) classes.push('preserve-whitespace')
|
|
79
|
-
|
|
80
|
-
return classes
|
|
81
|
-
})
|
|
82
|
-
|
|
83
68
|
function emitApply() {
|
|
84
69
|
if (blockApply.value) return
|
|
85
70
|
blockApply.value = true
|
|
@@ -88,11 +73,4 @@
|
|
|
88
73
|
|
|
89
74
|
emit('apply', text)
|
|
90
75
|
}
|
|
91
|
-
|
|
92
|
-
function regenerate() {
|
|
93
|
-
if (blockRegenerate.value) return
|
|
94
|
-
blockRegenerate.value = true
|
|
95
|
-
|
|
96
|
-
emit('regenerate', props.name)
|
|
97
|
-
}
|
|
98
76
|
</script>
|
|
@@ -2,18 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`FieldPreview > renders correctly with default props 1`] = `
|
|
4
4
|
"<div class="q-field-preview">
|
|
5
|
-
<div class="q-field-preview__toolbar"><span>Suggestions for field <b>Test Field</b></span></div>
|
|
5
|
+
<div class="q-field-preview__toolbar"><span>Suggestions for field: <b>Test Field</b></span></div>
|
|
6
6
|
<div class="q-field-preview__content">
|
|
7
7
|
<div class="markdown-renderer">
|
|
8
8
|
<p>Hello, World!</p>
|
|
9
9
|
</div>
|
|
10
10
|
</div>
|
|
11
11
|
<div class="q-field-preview__footer">
|
|
12
|
-
<div class="q-button-group" role="group"
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
<div class="q-button-group" role="group">
|
|
13
|
+
<!-- <q-button
|
|
14
|
+
:title="texts.regenerateResponse"
|
|
15
|
+
:disabled="props.disabled"
|
|
16
|
+
borderless
|
|
17
|
+
@click="console.log('Regenerate response')">
|
|
18
|
+
<q-icon icon="reset" />
|
|
19
|
+
</q-button> --><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless" data-testid="apply-button">
|
|
15
20
|
<!--v-if--><span class="q-button__content"><span data-test="apply"></span> Apply</span>
|
|
16
|
-
</button
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
17
23
|
</div>
|
|
18
24
|
</div>"
|
|
19
25
|
`;
|
|
@@ -4,8 +4,7 @@ import { useChatMessages } from '../useChatMessages'
|
|
|
4
4
|
// Utils
|
|
5
5
|
import { describe, it, expect, beforeEach } from 'vitest'
|
|
6
6
|
|
|
7
|
-
const { addChatMessage, clearMessages, getMessages, getLastMessage
|
|
8
|
-
useChatMessages()
|
|
7
|
+
const { addChatMessage, clearMessages, getMessages, getLastMessage } = useChatMessages()
|
|
9
8
|
|
|
10
9
|
beforeEach(() => {
|
|
11
10
|
// Reset chat messages before each test
|
|
@@ -49,16 +48,4 @@ describe('useChatMessages', () => {
|
|
|
49
48
|
expect(messages).toEqual([])
|
|
50
49
|
expect(messages.length).toBe(0)
|
|
51
50
|
})
|
|
52
|
-
|
|
53
|
-
it('should delete a message by ID', () => {
|
|
54
|
-
const msg1 = addChatMessage('First message', 'bot')
|
|
55
|
-
const msg2 = addChatMessage('Second message', 'user')
|
|
56
|
-
|
|
57
|
-
expect(getMessages().length).toBe(2)
|
|
58
|
-
deleteMessageById(msg1.id)
|
|
59
|
-
|
|
60
|
-
const messages = getMessages()
|
|
61
|
-
expect(messages.length).toBe(1)
|
|
62
|
-
expect(messages[0].id).toBe(msg2.id)
|
|
63
|
-
})
|
|
64
51
|
})
|