@n8n/chat 0.3.0 → 0.4.0

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 (104) hide show
  1. package/README.md +22 -5
  2. package/chat.bundle.es.js +10756 -0
  3. package/chat.bundle.umd.js +18 -0
  4. package/chat.es.js +6866 -0
  5. package/chat.umd.js +18 -0
  6. package/package.json +62 -2
  7. package/style.css +1 -0
  8. package/types/App.vue.d.ts +8 -0
  9. package/types/__stories__/App.stories.d.ts +17 -0
  10. package/types/__tests__/index.spec.d.ts +1 -0
  11. package/types/__tests__/setup.d.ts +0 -0
  12. package/types/__tests__/utils/create.d.ts +5 -0
  13. package/types/__tests__/utils/fetch.d.ts +4 -0
  14. package/types/__tests__/utils/selectors.d.ts +12 -0
  15. package/types/api/generic.d.ts +6 -0
  16. package/types/api/message.d.ts +3 -0
  17. package/types/components/Button.vue.d.ts +9 -0
  18. package/types/components/Chat.vue.d.ts +2 -0
  19. package/types/components/ChatWindow.vue.d.ts +2 -0
  20. package/types/components/GetStarted.vue.d.ts +2 -0
  21. package/types/components/GetStartedFooter.vue.d.ts +2 -0
  22. package/types/components/Input.vue.d.ts +2 -0
  23. package/types/components/Layout.vue.d.ts +11 -0
  24. package/types/components/Message.vue.d.ts +21 -0
  25. package/types/components/MessageTyping.vue.d.ts +15 -0
  26. package/types/components/MessagesList.vue.d.ts +14 -0
  27. package/types/components/PoweredBy.vue.d.ts +2 -0
  28. package/types/composables/useChat.d.ts +2 -0
  29. package/types/composables/useI18n.d.ts +4 -0
  30. package/types/composables/useOptions.d.ts +4 -0
  31. package/types/constants/defaults.d.ts +3 -0
  32. package/types/constants/localStorage.d.ts +2 -0
  33. package/types/constants/symbols.d.ts +4 -0
  34. package/types/event-buses/chatEventBus.d.ts +1 -0
  35. package/types/index.d.ts +2 -0
  36. package/types/plugins/chat.d.ts +3 -0
  37. package/types/types/chat.d.ts +11 -0
  38. package/types/types/messages.d.ts +6 -0
  39. package/types/types/options.d.ts +24 -0
  40. package/types/types/webhook.d.ts +15 -0
  41. package/types/utils/event-bus.d.ts +8 -0
  42. package/types/utils/mount.d.ts +1 -0
  43. package/.eslintignore +0 -2
  44. package/.eslintrc.cjs +0 -52
  45. package/.np-config.json +0 -5
  46. package/.storybook/main.ts +0 -27
  47. package/.storybook/preview.scss +0 -4
  48. package/.storybook/preview.ts +0 -16
  49. package/.vscode/extensions.json +0 -3
  50. package/env.d.ts +0 -1
  51. package/index.html +0 -13
  52. package/resources/workflow.json +0 -293
  53. package/scripts/pack.js +0 -11
  54. package/scripts/postbuild.js +0 -16
  55. package/src/App.vue +0 -23
  56. package/src/__stories__/App.stories.ts +0 -43
  57. package/src/__tests__/index.spec.ts +0 -223
  58. package/src/__tests__/setup.ts +0 -1
  59. package/src/__tests__/utils/create.ts +0 -16
  60. package/src/__tests__/utils/fetch.ts +0 -18
  61. package/src/__tests__/utils/selectors.ts +0 -53
  62. package/src/api/generic.ts +0 -64
  63. package/src/api/message.ts +0 -31
  64. package/src/components/Button.vue +0 -41
  65. package/src/components/Chat.vue +0 -48
  66. package/src/components/ChatWindow.vue +0 -125
  67. package/src/components/GetStarted.vue +0 -24
  68. package/src/components/GetStartedFooter.vue +0 -20
  69. package/src/components/Input.vue +0 -93
  70. package/src/components/Layout.vue +0 -82
  71. package/src/components/Message.vue +0 -97
  72. package/src/components/MessageTyping.vue +0 -109
  73. package/src/components/MessagesList.vue +0 -37
  74. package/src/components/PoweredBy.vue +0 -17
  75. package/src/composables/useChat.ts +0 -7
  76. package/src/composables/useI18n.ts +0 -16
  77. package/src/composables/useOptions.ts +0 -11
  78. package/src/constants/defaults.ts +0 -25
  79. package/src/constants/localStorage.ts +0 -2
  80. package/src/constants/symbols.ts +0 -8
  81. package/src/event-buses/chatEventBus.ts +0 -3
  82. package/src/index.ts +0 -42
  83. package/src/main.scss +0 -40
  84. package/src/plugins/chat.ts +0 -101
  85. package/src/shims.d.ts +0 -6
  86. package/src/types/chat.ts +0 -12
  87. package/src/types/messages.ts +0 -6
  88. package/src/types/options.ts +0 -23
  89. package/src/types/webhook.ts +0 -17
  90. package/src/utils/event-bus.ts +0 -51
  91. package/src/utils/mount.ts +0 -16
  92. package/tsconfig.json +0 -27
  93. package/vite.config.ts +0 -51
  94. package/vitest.config.ts +0 -20
  95. /package/{public/favicon.ico → favicon.ico} +0 -0
  96. /package/{src/__tests__/utils/index.ts → types/__tests__/utils/index.d.ts} +0 -0
  97. /package/{src/api/index.ts → types/api/index.d.ts} +0 -0
  98. /package/{src/components/index.ts → types/components/index.d.ts} +0 -0
  99. /package/{src/composables/index.ts → types/composables/index.d.ts} +0 -0
  100. /package/{src/constants/index.ts → types/constants/index.d.ts} +0 -0
  101. /package/{src/event-buses/index.ts → types/event-buses/index.d.ts} +0 -0
  102. /package/{src/plugins/index.ts → types/plugins/index.d.ts} +0 -0
  103. /package/{src/types/index.ts → types/types/index.d.ts} +0 -0
  104. /package/{src/utils/index.ts → types/utils/index.d.ts} +0 -0
@@ -1,293 +0,0 @@
1
- {
2
- "name": "AI Webhook Chat",
3
- "nodes": [
4
- {
5
- "parameters": {
6
- "httpMethod": "POST",
7
- "path": "513107b3-6f3a-4a1e-af21-659f0ed14183",
8
- "responseMode": "responseNode",
9
- "options": {
10
- "domainAllowlist": "*.localhost"
11
- }
12
- },
13
- "id": "51ab2689-647d-4cff-9d6f-0ba4df45e904",
14
- "name": "Webhook",
15
- "type": "n8n-nodes-base.webhook",
16
- "typeVersion": 1,
17
- "position": [
18
- 900,
19
- 200
20
- ],
21
- "webhookId": "513107b3-6f3a-4a1e-af21-659f0ed14183"
22
- },
23
- {
24
- "parameters": {
25
- "options": {}
26
- },
27
- "id": "3c7fd563-f610-41fa-b198-7fcf100e2815",
28
- "name": "Chat OpenAI",
29
- "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
30
- "typeVersion": 1,
31
- "position": [
32
- 1720,
33
- 620
34
- ],
35
- "credentials": {
36
- "openAiApi": {
37
- "id": "B5Fiv70Adfg6htxn",
38
- "name": "Alex's OpenAI Account"
39
- }
40
- }
41
- },
42
- {
43
- "parameters": {
44
- "sessionKey": "={{ $json.body.sessionId }}"
45
- },
46
- "id": "ebc23ffa-3bcf-494f-bcb8-51a5fff91885",
47
- "name": "Window Buffer Memory",
48
- "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
49
- "typeVersion": 1,
50
- "position": [
51
- 1920,
52
- 620
53
- ]
54
- },
55
- {
56
- "parameters": {
57
- "simplifyOutput": false
58
- },
59
- "id": "d6721a60-159b-4a93-ac6b-b81e16d9f16f",
60
- "name": "Memory Chat Retriever",
61
- "type": "@n8n/n8n-nodes-langchain.memoryChatRetriever",
62
- "typeVersion": 1,
63
- "position": [
64
- 1780,
65
- -40
66
- ]
67
- },
68
- {
69
- "parameters": {
70
- "sessionKey": "={{ $json.body.sessionId }}"
71
- },
72
- "id": "347edc3a-1dda-4996-b778-dcdc447ecfd8",
73
- "name": "Memory Chat Retriever Window Buffer Memory",
74
- "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
75
- "typeVersion": 1,
76
- "position": [
77
- 1800,
78
- 160
79
- ]
80
- },
81
- {
82
- "parameters": {
83
- "options": {
84
- "responseCode": 200,
85
- "responseHeaders": {
86
- "entries": [
87
- {
88
- "name": "sessionId",
89
- "value": "={{ $json.body.sessionId }}"
90
- },
91
- {
92
- "name": "Access-Control-Allow-Headers",
93
- "value": "*"
94
- }
95
- ]
96
- }
97
- }
98
- },
99
- "id": "d229963e-e2f1-4381-87d2-47043bd6ccc7",
100
- "name": "Respond to Webhook",
101
- "type": "n8n-nodes-base.respondToWebhook",
102
- "typeVersion": 1,
103
- "position": [
104
- 2460,
105
- 220
106
- ]
107
- },
108
- {
109
- "parameters": {
110
- "dataType": "string",
111
- "value1": "={{ $json.body.action }}",
112
- "rules": {
113
- "rules": [
114
- {
115
- "value2": "loadPreviousSession"
116
- },
117
- {
118
- "value2": "sendMessage",
119
- "output": 1
120
- }
121
- ]
122
- }
123
- },
124
- "id": "fc4ad994-5f38-4dce-b1e5-397acc512687",
125
- "name": "Chatbot Action",
126
- "type": "n8n-nodes-base.switch",
127
- "typeVersion": 1,
128
- "position": [
129
- 1320,
130
- 200
131
- ]
132
- },
133
- {
134
- "parameters": {
135
- "jsCode": "const response = { data: [] };\n\nfor (const item of $input.all()) {\n response.data.push(item.json);\n}\n\nreturn {\n json: response,\n pairedItem: 0\n};"
136
- },
137
- "id": "e1a80bdc-411a-42df-88dd-36915b1ae8f4",
138
- "name": "Code",
139
- "type": "n8n-nodes-base.code",
140
- "typeVersion": 2,
141
- "position": [
142
- 2160,
143
- -40
144
- ]
145
- },
146
- {
147
- "parameters": {
148
- "text": "={{ $json.body.message }}",
149
- "options": {}
150
- },
151
- "id": "f28f5c00-c742-41d5-8ddb-f0f59ab111a3",
152
- "name": "Agent",
153
- "type": "@n8n/n8n-nodes-langchain.agent",
154
- "typeVersion": 1,
155
- "position": [
156
- 1780,
157
- 340
158
- ]
159
- },
160
- {
161
- "parameters": {
162
- "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.body = JSON.parse(item.json.body);\n}\n\nreturn $input.all();"
163
- },
164
- "id": "415c071b-18b2-4ac5-8634-e3d939bf36ac",
165
- "name": "Transform request body",
166
- "type": "n8n-nodes-base.code",
167
- "typeVersion": 2,
168
- "position": [
169
- 1120,
170
- 200
171
- ]
172
- }
173
- ],
174
- "pinData": {},
175
- "connections": {
176
- "Webhook": {
177
- "main": [
178
- [
179
- {
180
- "node": "Transform request body",
181
- "type": "main",
182
- "index": 0
183
- }
184
- ]
185
- ]
186
- },
187
- "Memory Chat Retriever": {
188
- "main": [
189
- [
190
- {
191
- "node": "Code",
192
- "type": "main",
193
- "index": 0
194
- }
195
- ]
196
- ]
197
- },
198
- "Memory Chat Retriever Window Buffer Memory": {
199
- "ai_memory": [
200
- [
201
- {
202
- "node": "Memory Chat Retriever",
203
- "type": "ai_memory",
204
- "index": 0
205
- }
206
- ]
207
- ]
208
- },
209
- "Chatbot Action": {
210
- "main": [
211
- [
212
- {
213
- "node": "Memory Chat Retriever",
214
- "type": "main",
215
- "index": 0
216
- }
217
- ],
218
- [
219
- {
220
- "node": "Agent",
221
- "type": "main",
222
- "index": 0
223
- }
224
- ]
225
- ]
226
- },
227
- "Code": {
228
- "main": [
229
- [
230
- {
231
- "node": "Respond to Webhook",
232
- "type": "main",
233
- "index": 0
234
- }
235
- ]
236
- ]
237
- },
238
- "Chat OpenAI": {
239
- "ai_languageModel": [
240
- [
241
- {
242
- "node": "Agent",
243
- "type": "ai_languageModel",
244
- "index": 0
245
- }
246
- ]
247
- ]
248
- },
249
- "Window Buffer Memory": {
250
- "ai_memory": [
251
- [
252
- {
253
- "node": "Agent",
254
- "type": "ai_memory",
255
- "index": 0
256
- }
257
- ]
258
- ]
259
- },
260
- "Agent": {
261
- "main": [
262
- [
263
- {
264
- "node": "Respond to Webhook",
265
- "type": "main",
266
- "index": 0
267
- }
268
- ]
269
- ]
270
- },
271
- "Transform request body": {
272
- "main": [
273
- [
274
- {
275
- "node": "Chatbot Action",
276
- "type": "main",
277
- "index": 0
278
- }
279
- ]
280
- ]
281
- }
282
- },
283
- "active": true,
284
- "settings": {
285
- "executionOrder": "v1"
286
- },
287
- "versionId": "12c145a2-74bf-48b5-a87a-ba707949eaed",
288
- "id": "L3FlJuFOxZcHtoFT",
289
- "meta": {
290
- "instanceId": "374b43d8b8d6299cc777811a4ad220fc688ee2d54a308cfb0de4450a5233ca9e"
291
- },
292
- "tags": []
293
- }
package/scripts/pack.js DELETED
@@ -1,11 +0,0 @@
1
- const path = require('path');
2
- const shelljs = require('shelljs');
3
-
4
- const rootDirPath = path.resolve(__dirname, '..');
5
- const distDirPath = path.resolve(rootDirPath, 'dist');
6
-
7
- shelljs.cd(rootDirPath);
8
- shelljs.exec('npm run build');
9
-
10
- shelljs.cd(distDirPath);
11
- shelljs.exec('npm pack');
@@ -1,16 +0,0 @@
1
- const path = require('path');
2
- const shelljs = require('shelljs');
3
-
4
- const rootDirPath = path.resolve(__dirname, '..');
5
- const n8nRootDirPath = path.resolve(rootDirPath, '..', '..', '..');
6
- const distDirPath = path.resolve(rootDirPath, 'dist');
7
-
8
- const packageJsonFilePath = path.resolve(rootDirPath, 'package.json');
9
- const readmeFilePath = path.resolve(rootDirPath, 'README.md');
10
- const licenseFilePath = path.resolve(n8nRootDirPath, 'LICENSE.md');
11
-
12
- shelljs.cp(packageJsonFilePath, distDirPath);
13
- shelljs.cp(readmeFilePath, distDirPath);
14
- shelljs.cp(licenseFilePath, distDirPath);
15
-
16
- shelljs.mv(path.resolve(distDirPath, 'src'), path.resolve(distDirPath, 'types'));
package/src/App.vue DELETED
@@ -1,23 +0,0 @@
1
- <script lang="ts" setup>
2
- import { Chat, ChatWindow } from '@/components';
3
- import { computed, onMounted } from 'vue';
4
- import hljs from 'highlight.js/lib/core';
5
- import hljsXML from 'highlight.js/lib/languages/xml';
6
- import hljsJavascript from 'highlight.js/lib/languages/javascript';
7
- import { useOptions } from '@/composables';
8
-
9
- defineProps({});
10
-
11
- const { options } = useOptions();
12
-
13
- const isFullscreen = computed<boolean>(() => options.mode === 'fullscreen');
14
-
15
- onMounted(() => {
16
- hljs.registerLanguage('xml', hljsXML);
17
- hljs.registerLanguage('javascript', hljsJavascript);
18
- });
19
- </script>
20
- <template>
21
- <Chat v-if="isFullscreen" class="n8n-chat" />
22
- <ChatWindow v-else class="n8n-chat" />
23
- </template>
@@ -1,43 +0,0 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- import type { StoryObj } from '@storybook/vue3';
3
- import type { ChatOptions } from '@/types';
4
- import { createChat } from '@/index';
5
- import { onMounted } from 'vue';
6
-
7
- const webhookUrl = 'http://localhost:5678/webhook/513107b3-6f3a-4a1e-af21-659f0ed14183';
8
-
9
- const meta = {
10
- title: 'Chat',
11
- render: (args: Partial<ChatOptions>) => ({
12
- setup() {
13
- onMounted(() => {
14
- createChat(args);
15
- });
16
-
17
- return {};
18
- },
19
- template: '<div id="n8n-chat" />',
20
- }),
21
- parameters: {
22
- layout: 'fullscreen',
23
- },
24
- tags: ['autodocs'],
25
- };
26
-
27
- // eslint-disable-next-line import/no-default-export
28
- export default meta;
29
- type Story = StoryObj<typeof meta>;
30
-
31
- export const Fullscreen: Story = {
32
- args: {
33
- webhookUrl,
34
- mode: 'fullscreen',
35
- } satisfies Partial<ChatOptions>,
36
- };
37
-
38
- export const Windowed: Story = {
39
- args: {
40
- webhookUrl,
41
- mode: 'window',
42
- } satisfies Partial<ChatOptions>,
43
- };
@@ -1,223 +0,0 @@
1
- import { fireEvent, waitFor } from '@testing-library/vue';
2
- import {
3
- createFetchResponse,
4
- createGetLatestMessagesResponse,
5
- createSendMessageResponse,
6
- getChatInputSendButton,
7
- getChatInputTextarea,
8
- getChatMessage,
9
- getChatMessageByText,
10
- getChatMessages,
11
- getChatMessageTyping,
12
- getChatWindowToggle,
13
- getChatWindowWrapper,
14
- getChatWrapper,
15
- getGetStartedButton,
16
- getMountingTarget,
17
- } from '@/__tests__/utils';
18
- import { createChat } from '@/index';
19
- import { useChat } from '@/composables';
20
-
21
- describe('createChat()', () => {
22
- let app: ReturnType<typeof createChat>;
23
-
24
- afterEach(() => {
25
- vi.clearAllMocks();
26
-
27
- app.unmount();
28
- });
29
-
30
- describe('mode', () => {
31
- it('should create fullscreen chat app with default options', () => {
32
- const fetchSpy = vi.spyOn(window, 'fetch');
33
- fetchSpy.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse()));
34
-
35
- app = createChat({
36
- mode: 'fullscreen',
37
- });
38
-
39
- expect(getMountingTarget()).toBeVisible();
40
- expect(getChatWrapper()).toBeVisible();
41
- expect(getChatWindowWrapper()).not.toBeInTheDocument();
42
- });
43
-
44
- it('should create window chat app with default options', () => {
45
- const fetchSpy = vi.spyOn(window, 'fetch');
46
- fetchSpy.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse()));
47
-
48
- app = createChat({
49
- mode: 'window',
50
- });
51
-
52
- expect(getMountingTarget()).toBeDefined();
53
- expect(getChatWindowWrapper()).toBeVisible();
54
- expect(getChatWrapper()).not.toBeVisible();
55
- });
56
-
57
- it('should open window chat app using toggle button', async () => {
58
- const fetchSpy = vi.spyOn(window, 'fetch');
59
- fetchSpy.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse()));
60
-
61
- app = createChat();
62
-
63
- expect(getMountingTarget()).toBeVisible();
64
- expect(getChatWindowWrapper()).toBeVisible();
65
-
66
- const trigger = getChatWindowToggle();
67
- await fireEvent.click(trigger as HTMLElement);
68
-
69
- expect(getChatWrapper()).toBeVisible();
70
- });
71
- });
72
-
73
- describe('loadPreviousMessages', () => {
74
- it('should load previous messages on mount', async () => {
75
- const fetchSpy = vi.spyOn(global, 'fetch');
76
- fetchSpy.mockImplementation(createFetchResponse(createGetLatestMessagesResponse()));
77
-
78
- app = createChat({
79
- mode: 'fullscreen',
80
- });
81
-
82
- const getStartedButton = getGetStartedButton();
83
- await fireEvent.click(getStartedButton as HTMLElement);
84
-
85
- expect(fetchSpy.mock.calls[0][1]).toEqual(
86
- expect.objectContaining({
87
- method: 'POST',
88
- headers: {},
89
- body: expect.stringContaining('"action":"loadPreviousSession"') as unknown,
90
- mode: 'cors',
91
- cache: 'no-cache',
92
- }),
93
- );
94
- });
95
- });
96
-
97
- describe('initialMessages', () => {
98
- it.each(['fullscreen', 'window'] as Array<'fullscreen' | 'window'>)(
99
- 'should show initial default messages in %s mode',
100
- async (mode) => {
101
- const fetchSpy = vi.spyOn(window, 'fetch');
102
- fetchSpy.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse()));
103
-
104
- const initialMessages = ['Hello tester!', 'How are you?'];
105
- app = createChat({
106
- mode,
107
- initialMessages,
108
- });
109
-
110
- if (mode === 'window') {
111
- const trigger = getChatWindowToggle();
112
- await fireEvent.click(trigger as HTMLElement);
113
- }
114
-
115
- const getStartedButton = getGetStartedButton();
116
- await fireEvent.click(getStartedButton as HTMLElement);
117
-
118
- expect(getChatMessages().length).toBe(initialMessages.length);
119
- expect(getChatMessageByText(initialMessages[0])).toBeInTheDocument();
120
- expect(getChatMessageByText(initialMessages[1])).toBeInTheDocument();
121
- },
122
- );
123
- });
124
-
125
- describe('sendMessage', () => {
126
- it.each(['window', 'fullscreen'] as Array<'fullscreen' | 'window'>)(
127
- 'should send a message and render a text message in %s mode',
128
- async (mode) => {
129
- const input = 'Hello User World!';
130
- const output = 'Hello Bot World!';
131
-
132
- const fetchSpy = vi.spyOn(window, 'fetch');
133
- fetchSpy
134
- .mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse))
135
- .mockImplementationOnce(createFetchResponse(createSendMessageResponse(output)));
136
-
137
- app = createChat({
138
- mode,
139
- });
140
-
141
- if (mode === 'window') {
142
- const trigger = getChatWindowToggle();
143
- await fireEvent.click(trigger as HTMLElement);
144
- }
145
-
146
- expect(getChatMessageTyping()).not.toBeInTheDocument();
147
-
148
- const getStartedButton = getGetStartedButton();
149
- await fireEvent.click(getStartedButton as HTMLElement);
150
-
151
- expect(getChatMessages().length).toBe(2);
152
-
153
- const textarea = getChatInputTextarea();
154
- const sendButton = getChatInputSendButton();
155
- await fireEvent.update(textarea as HTMLElement, input);
156
- expect(sendButton).not.toBeDisabled();
157
- await fireEvent.click(sendButton as HTMLElement);
158
-
159
- expect(fetchSpy.mock.calls[1][1]).toEqual(
160
- expect.objectContaining({
161
- method: 'POST',
162
- headers: {},
163
- body: expect.stringMatching(/"action":"sendMessage"/) as unknown,
164
- mode: 'cors',
165
- cache: 'no-cache',
166
- }),
167
- );
168
- expect(fetchSpy.mock.calls[1][1]?.body).toContain(`"${input}"`);
169
-
170
- expect(getChatMessages().length).toBe(3);
171
- expect(getChatMessageByText(input)).toBeInTheDocument();
172
- expect(getChatMessageTyping()).toBeVisible();
173
-
174
- await waitFor(() => expect(getChatMessageTyping()).not.toBeInTheDocument());
175
- expect(getChatMessageByText(output)).toBeInTheDocument();
176
- },
177
- );
178
-
179
- it.each(['fullscreen', 'window'] as Array<'fullscreen' | 'window'>)(
180
- 'should send a message and render a code markdown message in %s mode',
181
- async (mode) => {
182
- const input = 'Teach me javascript!';
183
- const output = '# Code\n```js\nconsole.log("Hello World!");\n```';
184
-
185
- const chatStore = useChat();
186
- console.log(chatStore);
187
-
188
- const fetchSpy = vi.spyOn(window, 'fetch');
189
- fetchSpy
190
- .mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse))
191
- .mockImplementationOnce(createFetchResponse(createSendMessageResponse(output)));
192
-
193
- app = createChat({
194
- mode,
195
- });
196
-
197
- if (mode === 'window') {
198
- const trigger = getChatWindowToggle();
199
- await fireEvent.click(trigger as HTMLElement);
200
- }
201
-
202
- const getStartedButton = getGetStartedButton();
203
- await fireEvent.click(getStartedButton as HTMLElement);
204
-
205
- const textarea = getChatInputTextarea();
206
- const sendButton = getChatInputSendButton();
207
- await fireEvent.update(textarea as HTMLElement, input);
208
- await fireEvent.click(sendButton as HTMLElement);
209
-
210
- expect(getChatMessageByText(input)).toBeInTheDocument();
211
- expect(getChatMessages().length).toBe(3);
212
-
213
- await waitFor(() => expect(getChatMessageTyping()).not.toBeInTheDocument());
214
-
215
- const lastMessage = getChatMessage(-1);
216
- expect(lastMessage).toBeInTheDocument();
217
-
218
- expect(lastMessage.querySelector('h1')).toHaveTextContent('Code');
219
- expect(lastMessage.querySelector('code')).toHaveTextContent('console.log("Hello World!");');
220
- },
221
- );
222
- });
223
- });
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';
@@ -1,16 +0,0 @@
1
- import { createChat } from '@/index';
2
-
3
- export function createTestChat(options: Parameters<typeof createChat>[0] = {}): {
4
- unmount: () => void;
5
- container: Element;
6
- } {
7
- const app = createChat(options);
8
-
9
- const container = app._container as Element;
10
- const unmount = () => app.unmount();
11
-
12
- return {
13
- unmount,
14
- container,
15
- };
16
- }
@@ -1,18 +0,0 @@
1
- import type { LoadPreviousSessionResponse, SendMessageResponse } from '@/types';
2
-
3
- export function createFetchResponse<T>(data: T) {
4
- return async () =>
5
- ({
6
- json: async () => new Promise<T>((resolve) => resolve(data)),
7
- }) as Response;
8
- }
9
-
10
- export const createGetLatestMessagesResponse = (
11
- data: LoadPreviousSessionResponse['data'] = [],
12
- ): LoadPreviousSessionResponse => ({ data });
13
-
14
- export const createSendMessageResponse = (
15
- output: SendMessageResponse['output'],
16
- ): SendMessageResponse => ({
17
- output,
18
- });
@@ -1,53 +0,0 @@
1
- import { screen } from '@testing-library/vue';
2
- import { defaultMountingTarget } from '@/constants';
3
-
4
- export function getMountingTarget(target = defaultMountingTarget) {
5
- return document.querySelector(target);
6
- }
7
-
8
- export function getChatWindowWrapper() {
9
- return document.querySelector('.chat-window-wrapper');
10
- }
11
-
12
- export function getChatWindowToggle() {
13
- return document.querySelector('.chat-window-toggle');
14
- }
15
-
16
- export function getChatWrapper() {
17
- return document.querySelector('.chat-wrapper');
18
- }
19
-
20
- export function getChatMessages() {
21
- return document.querySelectorAll('.chat-message:not(.chat-message-typing)');
22
- }
23
-
24
- export function getChatMessage(index: number) {
25
- const messages = getChatMessages();
26
- return index < 0 ? messages[messages.length + index] : messages[index];
27
- }
28
-
29
- export function getChatMessageByText(text: string) {
30
- return screen.queryByText(text, {
31
- selector: '.chat-message:not(.chat-message-typing) .chat-message-markdown p',
32
- });
33
- }
34
-
35
- export function getChatMessageTyping() {
36
- return document.querySelector('.chat-message-typing');
37
- }
38
-
39
- export function getGetStartedButton() {
40
- return document.querySelector('.chat-get-started .chat-button');
41
- }
42
-
43
- export function getChatInput() {
44
- return document.querySelector('.chat-input');
45
- }
46
-
47
- export function getChatInputTextarea() {
48
- return document.querySelector('.chat-input textarea');
49
- }
50
-
51
- export function getChatInputSendButton() {
52
- return document.querySelector('.chat-input .chat-input-send-button');
53
- }