@quidgest/chatbot 0.5.1 → 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.
Files changed (70) hide show
  1. package/README.md +1 -2
  2. package/dist/components/ChatBot/types.d.ts +3 -1
  3. package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +3 -3
  4. package/dist/components/ChatBotInput/__tests__/ChatBotInput.spec.d.ts +1 -0
  5. package/dist/components/ChatBotInput/index.d.ts +2 -2
  6. package/dist/components/ChatBotInput/types.d.ts +4 -4
  7. package/dist/components/ChatBotMessage/__tests__/ChatBotMessage.spec.d.ts +1 -0
  8. package/dist/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.d.ts +1 -0
  9. package/dist/components/ChatBotMessage/types.d.ts +3 -2
  10. package/dist/components/ChatToolBar/__tests__/ChatToolBar.spec.d.ts +1 -0
  11. package/dist/components/FieldPreview/__tests__/FieldPreview.spec.d.ts +1 -0
  12. package/dist/components/MarkdownRender/__tests__/MarkdownRender.spec.d.ts +1 -0
  13. package/dist/components/PulseDots/__tests__/PulseDots.spec.d.ts +1 -0
  14. package/dist/composables/__tests__/useChatMessages.spec.d.ts +1 -0
  15. package/dist/composables/__tests__/useSSE.spec.d.ts +1 -0
  16. package/dist/composables/useChatMessages.d.ts +2 -1
  17. package/dist/composables/useSSE.d.ts +1 -2
  18. package/dist/composables/useTexts.d.ts +2 -0
  19. package/dist/index.js +16 -16
  20. package/dist/index.mjs +2924 -1770
  21. package/dist/style.css +1 -1
  22. package/dist/test/setup.d.ts +1 -0
  23. package/dist/utils/__tests__/parseFieldValue.spec.d.ts +1 -0
  24. package/package.json +27 -5
  25. package/src/assets/styles/preview-file.scss +70 -0
  26. package/src/assets/styles/styles.scss +190 -222
  27. package/src/components/ChatBot/ChatBot.vue +345 -368
  28. package/src/components/ChatBot/types.ts +35 -33
  29. package/src/components/ChatBotInput/ChatBotInput.vue +188 -190
  30. package/src/components/ChatBotInput/__tests__/ChatBotInput.spec.ts +279 -0
  31. package/src/components/ChatBotInput/__tests__/__snapshots__/ChatBotInput.spec.ts.snap +25 -0
  32. package/src/components/ChatBotInput/index.ts +2 -2
  33. package/src/components/ChatBotInput/types.ts +25 -25
  34. package/src/components/ChatBotMessage/ChatBotMessage.vue +159 -134
  35. package/src/components/ChatBotMessage/ChatBotMessageButtons.vue +179 -164
  36. package/src/components/ChatBotMessage/__tests__/ChatBotMessage.spec.ts +256 -0
  37. package/src/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.ts +199 -0
  38. package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessage.spec.ts.snap +35 -0
  39. package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessageButtons.spec.ts.snap +25 -0
  40. package/src/components/ChatBotMessage/types.ts +54 -53
  41. package/src/components/ChatToolBar/ChatToolBar.vue +68 -64
  42. package/src/components/ChatToolBar/__tests__/ChatToolBar.spec.ts +118 -0
  43. package/src/components/ChatToolBar/__tests__/__snapshots__/ChatToolBar.spec.ts.snap +11 -0
  44. package/src/components/ChatToolBar/types.ts +12 -12
  45. package/src/components/FieldPreview/FieldPreview.vue +56 -58
  46. package/src/components/FieldPreview/__tests__/FieldPreview.spec.ts +72 -0
  47. package/src/components/FieldPreview/__tests__/__snapshots__/FieldPreview.spec.ts.snap +25 -0
  48. package/src/components/FieldPreview/field-preview.scss +26 -26
  49. package/src/components/FieldPreview/types.ts +5 -5
  50. package/src/components/MarkdownRender/MarkdownRender.vue +15 -15
  51. package/src/components/MarkdownRender/__tests__/MarkdownRender.spec.ts +68 -0
  52. package/src/components/MarkdownRender/__tests__/__snapshots__/MarkdownRender.spec.ts.snap +8 -0
  53. package/src/components/MarkdownRender/markdown-render.scss +19 -20
  54. package/src/components/MarkdownRender/types.ts +3 -3
  55. package/src/components/PulseDots/PulseDots.vue +17 -17
  56. package/src/components/PulseDots/__tests__/PulseDots.spec.ts +35 -0
  57. package/src/components/PulseDots/__tests__/__snapshots__/PulseDots.spec.ts.snap +7 -0
  58. package/src/components/PulseDots/__tests__/__snapshots__/pulse-dots.spec.ts.snap +7 -0
  59. package/src/components/PulseDots/pulse-dots.scss +24 -23
  60. package/src/composables/__tests__/useChatMessages.spec.ts +51 -0
  61. package/src/composables/__tests__/useSSE.spec.ts +132 -0
  62. package/src/composables/useChatApi.ts +128 -134
  63. package/src/composables/useChatMessages.ts +46 -48
  64. package/src/composables/useSSE.ts +75 -76
  65. package/src/composables/useTexts.ts +30 -30
  66. package/src/test/setup.ts +36 -0
  67. package/src/utils/__tests__/parseFieldValue.spec.ts +27 -0
  68. package/src/utils/parseFieldValue.ts +12 -0
  69. package/src/utils/helper.ts +0 -12
  70. /package/dist/utils/{helper.d.ts → parseFieldValue.d.ts} +0 -0
@@ -1,18 +1,18 @@
1
1
  import { AvailableAgents } from '../ChatBot/types'
2
2
 
3
3
  export type ChatToolBarProps = {
4
- /**
5
- * If true, the toolbar will be disabled and not clickable.
6
- */
7
- disabled?: boolean
4
+ /**
5
+ * If true, the toolbar will be disabled and not clickable.
6
+ */
7
+ disabled?: boolean
8
8
 
9
- /**
10
- * Availabe Agents to select from.
11
- */
12
- availableAgents?: AvailableAgents[]
9
+ /**
10
+ * Availabe Agents to select from.
11
+ */
12
+ availableAgents?: AvailableAgents[]
13
13
 
14
- /**
15
- * The currently selected agent.
16
- */
17
- selectedAgentKey?: string
14
+ /**
15
+ * The currently selected agent.
16
+ */
17
+ selectedAgentKey?: string
18
18
  }
@@ -1,78 +1,76 @@
1
1
  <template>
2
- <div class="q-field-preview">
3
- <div class="q-field-preview__toolbar">
4
- <span>
5
- Suggestions for field: <b>{{ props.name }}</b>
6
- </span>
7
- </div>
8
- <div class="q-field-preview__content">
9
- <component
10
- :is="previewComponent"
11
- v-bind="previewComponentProps" />
12
- </div>
13
- <div class="q-field-preview__footer">
14
- <q-button-group borderless>
15
- <!-- <q-button
2
+ <div class="q-field-preview">
3
+ <div class="q-field-preview__toolbar">
4
+ <span>
5
+ {{ texts.suggestionsForField }} <b>{{ props.name }}</b>
6
+ </span>
7
+ </div>
8
+ <div class="q-field-preview__content">
9
+ <component
10
+ :is="previewComponent"
11
+ v-bind="previewComponentProps" />
12
+ </div>
13
+ <div class="q-field-preview__footer">
14
+ <q-button-group borderless>
15
+ <!-- <q-button
16
16
  :title="texts.regenerateResponse"
17
17
  :disabled="props.disabled"
18
18
  borderless
19
19
  @click="console.log('Regenerate response')">
20
20
  <q-icon icon="reset" />
21
21
  </q-button> -->
22
- <q-button
23
- :label="texts.apply"
24
- :disabled="blockApplyButton"
25
- :readonly="blockApplyButton"
26
- @click="emitApply">
27
- <q-icon icon="apply" />
28
- </q-button>
29
- </q-button-group>
30
- </div>
31
- </div>
22
+ <q-button
23
+ data-testid="apply-button"
24
+ :label="texts.apply"
25
+ :disabled="blockApplyButton"
26
+ :readonly="blockApplyButton"
27
+ @click="emitApply">
28
+ <q-icon icon="apply" />
29
+ </q-button>
30
+ </q-button-group>
31
+ </div>
32
+ </div>
32
33
  </template>
33
34
 
34
35
  <script setup lang="ts">
35
- import { QButton, QIcon, QButtonGroup } from '@quidgest/ui/components'
36
- import MarkdownRender from '../MarkdownRender/MarkdownRender.vue'
37
- import { FieldPreviewProps } from './types'
38
- import { useTexts } from '@/composables/useTexts'
39
- import { computed, ref } from 'vue'
40
- import { parseFieldValue } from '@/utils/helper'
36
+ import { QButton, QIcon, QButtonGroup } from '@quidgest/ui/components'
37
+ import { MarkdownRender } from '@/components/MarkdownRender'
38
+ import { FieldPreviewProps } from './types'
39
+ import { useTexts } from '@/composables/useTexts'
40
+ import { computed, ref } from 'vue'
41
+ import { parseFieldValue } from '@/utils/parseFieldValue'
41
42
 
42
- const texts = useTexts()
43
- const props = defineProps<FieldPreviewProps>()
44
- const blockApply = ref(props.applied)
45
- const blockApplyButton = computed(() => {
46
- return props.disabled || blockApply.value
47
- })
43
+ const props = defineProps<FieldPreviewProps>()
44
+ const emit = defineEmits<{
45
+ (e: 'apply', text: unknown): void
46
+ }>()
47
+ const texts = useTexts()
48
+ const blockApply = ref(props.applied)
49
+ const blockApplyButton = computed(() => {
50
+ return props.disabled || blockApply.value
51
+ })
48
52
 
49
- const previewComponent = computed(() => {
50
- if (props.type === 'text' || props.type === 'multiline_text')
51
- return MarkdownRender
53
+ const previewComponent = computed(() => {
54
+ if (props.type === 'text' || props.type === 'multiline_text') return MarkdownRender
52
55
 
53
- return 'div'
54
- })
56
+ return 'div'
57
+ })
55
58
 
56
- const previewComponentProps = computed(() => {
57
- const componentProps: Record<string, unknown> = {}
59
+ const previewComponentProps = computed(() => {
60
+ const componentProps: Record<string, unknown> = {}
58
61
 
59
- if (previewComponent.value === MarkdownRender)
60
- componentProps.source = props.text
61
- else componentProps.innerHTML = props.text
62
+ if (previewComponent.value === MarkdownRender) componentProps.source = props.text
63
+ else componentProps.innerHTML = props.text
62
64
 
63
- return componentProps
64
- })
65
+ return componentProps
66
+ })
65
67
 
66
- const emits = defineEmits<{
67
- (e: 'apply', text: unknown): void
68
- }>()
68
+ function emitApply() {
69
+ if (blockApply.value) return
70
+ blockApply.value = true
69
71
 
70
- function emitApply() {
71
- if (blockApply.value) return
72
- blockApply.value = true
72
+ const text = parseFieldValue(props.type, props.text)
73
73
 
74
- const text = parseFieldValue(props.type, props.text)
75
-
76
- emits('apply', text)
77
- }
74
+ emit('apply', text)
75
+ }
78
76
  </script>
@@ -0,0 +1,72 @@
1
+ // Componets
2
+ import { FieldPreview } from '..'
3
+
4
+ // Utils
5
+ import { mount } from '@vue/test-utils'
6
+ import { describe, it, expect } from 'vitest'
7
+
8
+ // Types
9
+ import type { FieldPreviewProps } from '..'
10
+
11
+ describe('FieldPreview', () => {
12
+ const props: FieldPreviewProps = {
13
+ name: 'Test Field',
14
+ text: 'Hello, World!',
15
+ type: 'text',
16
+ disabled: false,
17
+ applied: false
18
+ }
19
+
20
+ it('renders correctly with default props', () => {
21
+ const wrapper = mount(FieldPreview, {
22
+ props: { ...props }
23
+ })
24
+ expect(wrapper.html()).toMatchSnapshot()
25
+ })
26
+
27
+ it('renders the correct componentd for text fields', async () => {
28
+ const wrapper = mount(FieldPreview, {
29
+ props: { ...props }
30
+ })
31
+
32
+ expect(wrapper.find('.markdown-renderer').exists()).toBe(true)
33
+ })
34
+
35
+ it('renders the correct componentd for multiline_text fields', async () => {
36
+ const wrapper = mount(FieldPreview, {
37
+ props: { ...props, type: 'multiline_text' }
38
+ })
39
+ expect(wrapper.find('.markdown-renderer').exists()).toBe(true)
40
+ })
41
+
42
+ it('renders the correct default component for unknown field types', async () => {
43
+ const wrapper = mount(FieldPreview, {
44
+ props: { ...props, type: 'boolean' }
45
+ })
46
+
47
+ const textElement = wrapper.find('.q-field-preview__content div')
48
+ expect(textElement.exists()).toBe(true)
49
+ })
50
+
51
+ it('emits the right event on apply buttons is clicked', async () => {
52
+ const wrapper = mount(FieldPreview, {
53
+ props: { ...props }
54
+ })
55
+
56
+ const applyButton = wrapper.find('[data-testid="apply-button"]')
57
+ await applyButton.trigger('click')
58
+
59
+ expect(wrapper.emitted()['apply']).toBeTruthy()
60
+ })
61
+
62
+ it('does not emit the apply event when the button is disabled', async () => {
63
+ const wrapper = mount(FieldPreview, {
64
+ props: { ...props, applied: true }
65
+ })
66
+
67
+ const applyButton = wrapper.find('[data-testid="apply-button"]')
68
+ await applyButton.trigger('click')
69
+
70
+ expect(wrapper.emitted()['apply']).toBeFalsy()
71
+ })
72
+ })
@@ -0,0 +1,25 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`FieldPreview > renders correctly with default props 1`] = `
4
+ "<div class="q-field-preview">
5
+ <div class="q-field-preview__toolbar"><span>Suggestions for field: <b>Test Field</b></span></div>
6
+ <div class="q-field-preview__content">
7
+ <div class="markdown-renderer">
8
+ <p>Hello, World!</p>
9
+ </div>
10
+ </div>
11
+ <div class="q-field-preview__footer">
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">
20
+ <!--v-if--><span class="q-button__content"><span data-test="apply"></span> Apply</span>
21
+ </button>
22
+ </div>
23
+ </div>
24
+ </div>"
25
+ `;
@@ -1,34 +1,34 @@
1
1
  .q-field-preview {
2
- position: relative;
3
- display: flex;
4
- flex-direction: column;
5
- margin: 1rem 0.25rem;
2
+ position: relative;
3
+ display: flex;
4
+ flex-direction: column;
5
+ margin: 1rem 0.25rem;
6
6
 
7
- &__toolbar {
8
- z-index: 1;
9
- display: flex;
10
- flex-direction: row;
11
- align-items: center;
12
- justify-content: space-between;
13
- padding: 0.1rem 0.2rem;
14
- }
7
+ &__toolbar {
8
+ z-index: 1;
9
+ display: flex;
10
+ flex-direction: row;
11
+ align-items: center;
12
+ justify-content: space-between;
13
+ padding: 0.1rem 0.2rem;
14
+ }
15
15
 
16
- &__content {
17
- position: relative;
18
- background-color: #f5f5f5;
19
- border: 1px solid #e0e0e0;
20
- border-radius: 6px;
21
- padding: 0.75rem 1rem;
22
- font-size: 0.875rem;
23
- }
16
+ &__content {
17
+ position: relative;
18
+ background-color: #f5f5f5;
19
+ border: 1px solid #e0e0e0;
20
+ border-radius: 6px;
21
+ padding: 0.75rem 1rem;
22
+ font-size: 0.875rem;
23
+ }
24
24
 
25
- &__footer {
26
- display: flex;
27
- flex-direction: row;
28
- margin-top: 0.25rem;
29
- }
25
+ &__footer {
26
+ display: flex;
27
+ flex-direction: row;
28
+ margin-top: 0.25rem;
29
+ }
30
30
  }
31
31
 
32
32
  .q-field-preview:first-child {
33
- margin: 0 1rem 0.25rem 0.25rem;
33
+ margin: 0 1rem 0.25rem 0.25rem;
34
34
  }
@@ -1,7 +1,7 @@
1
1
  export type FieldPreviewProps = {
2
- name: string
3
- text: string
4
- type: string
5
- disabled?: boolean
6
- applied?: boolean
2
+ name: string
3
+ text: string
4
+ type: string
5
+ disabled?: boolean
6
+ applied?: boolean
7
7
  }
@@ -1,25 +1,25 @@
1
1
  <template>
2
- <div
3
- class="markdown-renderer"
4
- v-html="renderedContent"></div>
2
+ <div
3
+ class="markdown-renderer"
4
+ v-html="renderedContent"></div>
5
5
  </template>
6
6
 
7
7
  <script setup lang="ts">
8
- import { ref, computed } from 'vue'
9
- import type { MarkdownRenderProps } from './types'
8
+ import { ref, computed } from 'vue'
9
+ import type { MarkdownRenderProps } from './types'
10
10
 
11
- import MarkdownIt from 'markdown-it'
11
+ import MarkdownIt from 'markdown-it'
12
12
 
13
- const props = defineProps<MarkdownRenderProps>()
13
+ const props = defineProps<MarkdownRenderProps>()
14
14
 
15
- const md = ref(
16
- new MarkdownIt({
17
- html: true,
18
- ...(props.options ?? {})
19
- })
20
- )
15
+ const md = ref(
16
+ new MarkdownIt({
17
+ html: true,
18
+ ...(props.options ?? {})
19
+ })
20
+ )
21
21
 
22
- if (props.plugins) props.plugins.forEach((plugin) => md.value.use(plugin))
22
+ if (props.plugins) props.plugins.forEach((plugin) => md.value.use(plugin))
23
23
 
24
- const renderedContent = computed(() => md.value.render(props.source))
24
+ const renderedContent = computed(() => md.value.render(props.source))
25
25
  </script>
@@ -0,0 +1,68 @@
1
+ // Components
2
+ import { MarkdownRender } from '..'
3
+
4
+ // Utils
5
+ import { mount } from '@vue/test-utils'
6
+ import { describe, it, expect, vi } from 'vitest'
7
+
8
+ // Types
9
+ import type MarkdownIt from 'markdown-it'
10
+
11
+ // Mock a simple markdown-it plugin
12
+ const mockPlugin = vi.fn((md: MarkdownIt) => {
13
+ md.core.ruler.push('test:rule', (state) => {
14
+ state.tokens.push({
15
+ type: 'test_token',
16
+ content: 'test',
17
+ tag: '',
18
+ attrs: null,
19
+ map: null,
20
+ nesting: 0,
21
+ level: 0,
22
+ children: null,
23
+ markup: '',
24
+ info: '',
25
+ meta: undefined,
26
+ block: false,
27
+ hidden: false,
28
+ attrIndex: function () {
29
+ throw new Error()
30
+ },
31
+ attrPush: function () {
32
+ throw new Error()
33
+ },
34
+ attrSet: function () {
35
+ throw new Error()
36
+ },
37
+ attrGet: function (): string | null {
38
+ throw new Error()
39
+ },
40
+ attrJoin: function () {
41
+ throw new Error()
42
+ }
43
+ })
44
+ })
45
+ })
46
+
47
+ describe('MarkdownRender', () => {
48
+ it('renders correctly with default props', () => {
49
+ const wrapper = mount(MarkdownRender, {
50
+ props: {
51
+ source: '# Hello World\nThis is a **markdown** test.'
52
+ }
53
+ })
54
+ expect(wrapper.html()).toMatchSnapshot()
55
+ })
56
+
57
+ it('renders correctly with custom markdown-it plugins', () => {
58
+ const wrapper = mount(MarkdownRender, {
59
+ props: {
60
+ source: '',
61
+ plugins: [mockPlugin]
62
+ }
63
+ })
64
+
65
+ expect(mockPlugin).toHaveBeenCalled()
66
+ expect(wrapper.props('plugins')).toHaveLength(1)
67
+ })
68
+ })
@@ -0,0 +1,8 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`MarkdownRender > renders correctly with default props 1`] = `
4
+ "<div class="markdown-renderer">
5
+ <h1>Hello World</h1>
6
+ <p>This is a <strong>markdown</strong> test.</p>
7
+ </div>"
8
+ `;
@@ -1,24 +1,23 @@
1
1
  .markdown-renderer {
2
- pre,
3
- code {
4
- white-space: pre-wrap;
5
- word-break: break-word;
6
- overflow-wrap: break-word;
7
- overflow-x: auto;
8
- }
2
+ pre,
3
+ code {
4
+ white-space: pre-wrap;
5
+ overflow-wrap: anywhere;
6
+ overflow-x: auto;
7
+ }
9
8
 
10
- pre {
11
- position: relative;
12
- background-color: #f5f5f5;
13
- border: 1px solid #e0e0e0;
14
- border-radius: 6px;
15
- padding: 0.75rem 1rem;
16
- font-size: 0.875rem;
17
- }
9
+ pre {
10
+ position: relative;
11
+ background-color: #f5f5f5;
12
+ border: 1px solid #e0e0e0;
13
+ border-radius: 6px;
14
+ padding: 0.75rem 1rem;
15
+ font-size: 0.875rem;
16
+ }
18
17
 
19
- code {
20
- padding: 0.2rem 0.4rem;
21
- border-radius: 4px;
22
- font-size: 0.875rem;
23
- }
18
+ code {
19
+ padding: 0.2rem 0.4rem;
20
+ border-radius: 4px;
21
+ font-size: 0.875rem;
22
+ }
24
23
  }
@@ -1,7 +1,7 @@
1
1
  import type { Options, PluginSimple } from 'markdown-it'
2
2
 
3
3
  export interface MarkdownRenderProps {
4
- source: string
5
- options?: Options
6
- plugins?: PluginSimple[]
4
+ source: string
5
+ options?: Options
6
+ plugins?: PluginSimple[]
7
7
  }
@@ -1,24 +1,24 @@
1
1
  <template>
2
- <div class="pulsing-dots">
3
- <span class="generating-text">
4
- {{ texts.generatingResponse }}
5
- </span>
6
- <div class="dots-container">
7
- <span
8
- v-for="(_, index) in dots"
9
- :key="index"
10
- class="dot"
11
- :style="{ animationDelay: index * 0.2 + 's' }">
12
- &bull;
13
- </span>
14
- </div>
15
- </div>
2
+ <div class="pulsing-dots">
3
+ <span class="generating-text">
4
+ {{ texts.generatingResponse }}
5
+ </span>
6
+ <div class="dots-container">
7
+ <span
8
+ v-for="(_, index) in dots"
9
+ :key="index"
10
+ class="dot"
11
+ :style="{ animationDelay: index * 0.2 + 's' }">
12
+ &bull;
13
+ </span>
14
+ </div>
15
+ </div>
16
16
  </template>
17
17
 
18
18
  <script setup lang="ts">
19
- import { useTexts } from '@/composables/useTexts'
19
+ import { useTexts } from '@/composables/useTexts'
20
20
 
21
- const dots = [1, 2, 3]
21
+ const dots = [1, 2, 3]
22
22
 
23
- const texts = useTexts()
23
+ const texts = useTexts()
24
24
  </script>
@@ -0,0 +1,35 @@
1
+ // Components
2
+ import PulseDots from '../PulseDots.vue'
3
+
4
+ // Utils
5
+ import { mount } from '@vue/test-utils'
6
+ import { describe, it, expect } from 'vitest'
7
+ import { useTexts } from '@/composables/useTexts'
8
+
9
+ describe('PulseDots', () => {
10
+ it('renders correctly', () => {
11
+ const wrapper = mount(PulseDots)
12
+ expect(wrapper.html()).toMatchSnapshot()
13
+ })
14
+
15
+ it('renders the correct number of dots', () => {
16
+ const wrapper = mount(PulseDots)
17
+ const dots = wrapper.findAll('.dot')
18
+ expect(dots.length).toBe(3)
19
+ })
20
+
21
+ it('uses the correct generating text', () => {
22
+ const { generatingResponse } = useTexts()
23
+ const wrapper = mount(PulseDots)
24
+ expect(wrapper.text()).toContain(generatingResponse)
25
+ })
26
+
27
+ it('applies correct animation delays to dots', () => {
28
+ const wrapper = mount(PulseDots)
29
+ const dots = wrapper.findAll('.dot')
30
+
31
+ dots.forEach((dot, index) => {
32
+ expect(dot.attributes('style')).toContain(`animation-delay: ${index * 0.2}s`)
33
+ })
34
+ })
35
+ })
@@ -0,0 +1,7 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`PulseDots > renders correctly 1`] = `
4
+ "<div class="pulsing-dots"><span class="generating-text">Generating</span>
5
+ <div class="dots-container"><span class="dot" style="animation-delay: 0s;"> • </span><span class="dot" style="animation-delay: 0.2s;"> • </span><span class="dot" style="animation-delay: 0.4s;"> • </span></div>
6
+ </div>"
7
+ `;
@@ -0,0 +1,7 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`PulseDots > renders correctly 1`] = `
4
+ "<div class="pulsing-dots"><span class="generating-text">Generating</span>
5
+ <div class="dots-container"><span class="dot" style="animation-delay: 0s;"> • </span><span class="dot" style="animation-delay: 0.2s;"> • </span><span class="dot" style="animation-delay: 0.4s;"> • </span></div>
6
+ </div>"
7
+ `;