From 643b94e3dfa2027101853317c774c087f6072776 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 17 Jan 2024 10:59:49 +0100 Subject: [PATCH] tests: unit tests for ModelPlayground --- packages/frontend/package.json | 2 + .../src/pages/ModelPlayground.spec.ts | 146 ++++++++ .../frontend/src/pages/ModelPlayground.svelte | 2 + yarn.lock | 352 +++++++++++++++++- 4 files changed, 494 insertions(+), 8 deletions(-) create mode 100644 packages/frontend/src/pages/ModelPlayground.spec.ts diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 894bb51e5..6b9f0583c 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -23,7 +23,9 @@ "@fortawesome/free-brands-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/free-regular-svg-icons": "^6.5.1", + "jsdom": "^23.2.0", "@sveltejs/vite-plugin-svelte": "3.0.1", + "@testing-library/jest-dom": "^6.2.0", "@testing-library/dom": "^9.3.3", "@testing-library/svelte": "^4.0.5", "@testing-library/user-event": "^14.5.1", diff --git a/packages/frontend/src/pages/ModelPlayground.spec.ts b/packages/frontend/src/pages/ModelPlayground.spec.ts new file mode 100644 index 000000000..35f0db7a7 --- /dev/null +++ b/packages/frontend/src/pages/ModelPlayground.spec.ts @@ -0,0 +1,146 @@ +import '@testing-library/jest-dom/vitest'; +import { vi, test, expect, beforeEach } from 'vitest'; +import { screen, fireEvent, render } from '@testing-library/svelte'; +import ModelPlayground from './ModelPlayground.svelte'; +import type { ModelInfo } from '@shared/models/IModelInfo'; +import userEvent from '@testing-library/user-event'; + +const mocks = vi.hoisted(() => { + return { + startPlaygroundMock: vi.fn(), + askPlaygroundMock: vi.fn(), + playgroundQueriesSubscribeMock: vi.fn(), + playgroundQueriesMock: { + subscribe: (f: (msg: any) => void) => { + f(mocks.playgroundQueriesSubscribeMock()); + return () => {}; + }, + } + } +}); + +vi.mock('../utils/client', async () => { + return { + studioClient: { + startPlayground: mocks.startPlaygroundMock, + askPlayground: mocks.askPlaygroundMock, + askPlaygroundQueries: () => { }, + }, + rpcBrowser: { + subscribe: () => { + return { + unsubscribe: () => { }, + } + } + } + }; +}); + +vi.mock('../stores/playground-queries', async () => { + return { + playgroundQueries: mocks.playgroundQueriesMock, + } +}); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +test('should start playground at init time and call askPlayground when button clicked', async () => { + mocks.playgroundQueriesSubscribeMock.mockReturnValue([]); + render(ModelPlayground, { + model: { + id: "model1", + name: "Model 1", + description: "A description", + hw: "CPU", + registry: "Hugging Face", + popularity: 3, + license: "?", + url: "https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF/resolve/main/llama-2-7b-chat.Q5_K_S.gguf" + } as ModelInfo + }); + await new Promise(resolve => setTimeout(resolve, 200)); + + expect(mocks.startPlaygroundMock).toHaveBeenCalledOnce(); + + const prompt = screen.getByPlaceholderText('Type your prompt here'); + expect(prompt).toBeInTheDocument(); + const user = userEvent.setup(); + user.type(prompt, 'what is it?'); + + const send = screen.getByRole('button', { name: 'Send Request' }); + expect(send).toBeInTheDocument(); + + expect(mocks.askPlaygroundMock).not.toHaveBeenCalled(); + await fireEvent.click(send); + expect(mocks.askPlaygroundMock).toHaveBeenCalledOnce(); +}); + +test('should display query without response', async () => { + mocks.playgroundQueriesSubscribeMock.mockReturnValue([ + { + id: 1, + modelId: 'model1', + prompt: 'what is 1+1?', + } + ]); + render(ModelPlayground, { + model: { + id: "model1", + name: "Model 1", + description: "A description", + hw: "CPU", + registry: "Hugging Face", + popularity: 3, + license: "?", + url: "https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF/resolve/main/llama-2-7b-chat.Q5_K_S.gguf" + } as ModelInfo + }); + await new Promise(resolve => setTimeout(resolve, 200)); + + const prompt = screen.getByPlaceholderText('Type your prompt here'); + expect(prompt).toBeInTheDocument(); + expect(prompt).toHaveValue('what is 1+1?'); + + const response = screen.queryByRole('textbox', { name: 'response' }); + expect(response).not.toBeInTheDocument(); +}); + +test('should display query without response', async () => { + mocks.playgroundQueriesSubscribeMock.mockReturnValue([ + { + id: 1, + modelId: 'model1', + prompt: 'what is 1+1?', + response: { + choices: [ + { + text: "The response is 2" + } + ] + } + } + ]); + render(ModelPlayground, { + model: { + id: "model1", + name: "Model 1", + description: "A description", + hw: "CPU", + registry: "Hugging Face", + popularity: 3, + license: "?", + url: "https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF/resolve/main/llama-2-7b-chat.Q5_K_S.gguf" + } as ModelInfo + }); + await new Promise(resolve => setTimeout(resolve, 200)); + + const prompt = screen.getByPlaceholderText('Type your prompt here'); + expect(prompt).toBeInTheDocument(); + expect(prompt).toHaveValue('what is 1+1?'); + + const response = screen.queryByRole('textbox', { name: 'response' }); + expect(response).toBeInTheDocument(); + expect(response).toHaveValue('The response is 2'); +}); diff --git a/packages/frontend/src/pages/ModelPlayground.svelte b/packages/frontend/src/pages/ModelPlayground.svelte index a2924ef10..ab4811b62 100644 --- a/packages/frontend/src/pages/ModelPlayground.svelte +++ b/packages/frontend/src/pages/ModelPlayground.svelte @@ -68,6 +68,7 @@
Prompt