diff --git a/src/components/ChatWidget.vue b/src/components/ChatWidget.vue index 7a84816..c6817c4 100644 --- a/src/components/ChatWidget.vue +++ b/src/components/ChatWidget.vue @@ -76,6 +76,8 @@ export default { console.log('Opey Status: ', this.opeyContext.status) } catch (error) { console.error('Error in chat:', error); + // on error, remove the assistant message placeholder, as it will be empty. + this.opeyContext.messages = this.opeyContext.messages.filter(m => m.id !== this.opeyContext.currentAssistantMessage.id); this.opeyContext.status = 'ready'; } }, diff --git a/src/obp/opey-functions.ts b/src/obp/opey-functions.ts index eb07d4a..230a7e2 100644 --- a/src/obp/opey-functions.ts +++ b/src/obp/opey-functions.ts @@ -87,12 +87,16 @@ export async function sendOpeyMessage( if (!stream) { throw new Error('No stream returned from API') } + + if (response.status !== 200) { + throw new Error(`Error sending Opey message: ${response.statusText}`); + } await processOpeyStream(stream, context); } catch (error) { console.error('Error sending Opey message:', error); context.status = 'ready'; - throw error; + throw new Error(`Error sending Opey message: ${error}`); } } \ No newline at end of file diff --git a/src/test/ChatWidget.test.ts b/src/test/ChatWidget.test.ts index 27d1b8f..52f5f47 100644 --- a/src/test/ChatWidget.test.ts +++ b/src/test/ChatWidget.test.ts @@ -1,9 +1,77 @@ import { mount } from '@vue/test-utils'; -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import ChatWidget from '../components/ChatWidget.vue' +import { OpeyStreamContext } from '@/obp/opey-functions'; describe('ChatWidget', () => { - it('should append messages in correct order', async () => { + let mockContext: OpeyStreamContext; + + beforeEach(() => { + mockContext = { + currentAssistantMessage: { + id: '123', + role: 'assistant', + content: '', + }, + messages: [], + status: 'loading', + } + + // create a mock stream + const mockStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(`data: {"type":"token","content":"test"}\n`)); + controller.close(); + }, + }); + + // mock the fetch function + global.fetch = vi.fn(() => + Promise.resolve(new Response(mockStream, { + headers: { 'content-type': 'text/event-stream' }, + status: 200, + })) + ); + }) + afterEach(() => { + vi.clearAllMocks() + }) + it('should call fetch when sending a user message', async () => { + const wrapper = mount(ChatWidget, {}) + + await wrapper.vm.onSubmit() + expect(global.fetch).toHaveBeenCalled() + }) + it('should clear the assistant message placeholder from messages list on error', async () => { + // mock the fetch function with a rejected promise + const wrapper = mount(ChatWidget, {}) + + global.fetch = vi.fn(() => + Promise.reject(new Error('Test error')) + ); + + await wrapper.vm.onSubmit() + expect(wrapper.vm.opeyContext.messages.find(m => m.id === wrapper.vm.opeyContext.currentAssistantMessage.id)).toBeUndefined() + + + }) + it('should trigger onSubmit when enter key is pressed in the input', async () => { + const wrapper = mount(ChatWidget, {}) + // This opens the chat widget + wrapper.vm.chatOpen = true + await wrapper.vm.$nextTick() + + // Get the input element and trigger the keypress enter event + // This will probably fail if the class name of the input element changes + const input = wrapper.get('.user-input-container input') + input.trigger('keypress.enter') + expect(global.fetch).toHaveBeenCalled() + }) + it('displays chat when chatOpen is set to true', async () => { + const wrapper = mount(ChatWidget, {}) + wrapper.vm.chatOpen = true + await wrapper.vm.$nextTick() + expect(wrapper.find('.chat-container').exists()).toBe(true) }) }) \ No newline at end of file diff --git a/src/test/opey-functions.test.ts b/src/test/opey-functions.test.ts index 0e531bf..e69edaa 100644 --- a/src/test/opey-functions.test.ts +++ b/src/test/opey-functions.test.ts @@ -135,7 +135,7 @@ describe('sendOpeyMessage', () => { expect(global.fetch).toHaveBeenCalled() }) - it("should push the 'ready' status to the context", async () => { + it("should push the 'ready' status to the context after success", async () => { await OpeyModule.sendOpeyMessage('test message', '123', false, mockContext) diff --git a/vite.config.mts b/vite.config.mts index b53563c..946dc00 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -45,11 +45,4 @@ export default defineConfig({ }, }, }, - test: { - // enable jest-like global test APIs - globals: true, - // simulate DOM with happy-dom - // (requires installing happy-dom as a peer dependency) - environment: 'happy-dom' - }, }) diff --git a/vitest.config.js b/vitest.config.js index 83ccc58..ad18ac0 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -34,6 +34,10 @@ export default defineConfig({ exclude:[ ...configDefaults.exclude, '**/backend-tests/*' - ] + ], + pool: "vmThreads", + deps: { + inline: ['element-plus'], + } }, }); \ No newline at end of file