1<script module lang="ts">
2 import { defineMeta } from '@storybook/addon-svelte-csf';
3 import ChatForm from '$lib/components/app/chat/ChatForm/ChatForm.svelte';
4 import { expect } from 'storybook/test';
5 import { mockServerProps, mockConfigs } from './fixtures/storybook-mocks';
6 import jpgAsset from './fixtures/assets/1.jpg?url';
7 import svgAsset from './fixtures/assets/hf-logo.svg?url';
8 import pdfAsset from './fixtures/assets/example.pdf?raw';
9
10 const { Story } = defineMeta({
11 title: 'Components/ChatScreen/ChatForm',
12 component: ChatForm,
13 parameters: {
14 layout: 'centered'
15 }
16 });
17
18 let fileAttachments = $state([
19 {
20 id: '1',
21 name: '1.jpg',
22 type: 'image/jpeg',
23 size: 44891,
24 preview: jpgAsset,
25 file: new File([''], '1.jpg', { type: 'image/jpeg' })
26 },
27 {
28 id: '2',
29 name: 'hf-logo.svg',
30 type: 'image/svg+xml',
31 size: 1234,
32 preview: svgAsset,
33 file: new File([''], 'hf-logo.svg', { type: 'image/svg+xml' })
34 },
35 {
36 id: '3',
37 name: 'example.pdf',
38 type: 'application/pdf',
39 size: 351048,
40 file: new File([pdfAsset], 'example.pdf', { type: 'application/pdf' })
41 }
42 ]);
43</script>
44
45<Story
46 name="Default"
47 args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
48 play={async ({ canvas, userEvent }) => {
49 mockServerProps(mockConfigs.noModalities);
50
51 const textarea = await canvas.findByRole('textbox');
52 const submitButton = await canvas.findByRole('button', { name: 'Send' });
53
54 // Expect the input to be focused after the component is mounted
55 await expect(textarea).toHaveFocus();
56
57 // Expect the submit button to be disabled
58 await expect(submitButton).toBeDisabled();
59
60 const text = 'What is the meaning of life?';
61
62 await userEvent.clear(textarea);
63 await userEvent.type(textarea, text);
64
65 await expect(textarea).toHaveValue(text);
66
67 const fileInput = document.querySelector('input[type="file"]');
68 await expect(fileInput).not.toHaveAttribute('accept');
69
70 // Open file attachments dropdown
71 const fileUploadButton = canvas.getByText('Attach files');
72 await userEvent.click(fileUploadButton);
73
74 // Check dropdown menu items are disabled (no modalities)
75 const imagesButton = document.querySelector('.images-button');
76 const audioButton = document.querySelector('.audio-button');
77
78 await expect(imagesButton).toHaveAttribute('data-disabled');
79 await expect(audioButton).toHaveAttribute('data-disabled');
80
81 // Close dropdown by pressing Escape
82 await userEvent.keyboard('{Escape}');
83 }}
84/>
85
86<Story name="Loading" args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', isLoading: true }} />
87
88<Story
89 name="VisionModality"
90 args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
91 play={async ({ canvas, userEvent }) => {
92 mockServerProps(mockConfigs.visionOnly);
93
94 // Open file attachments dropdown and verify it works
95 const fileUploadButton = canvas.getByText('Attach files');
96 await userEvent.click(fileUploadButton);
97
98 // Verify dropdown menu items exist
99 const imagesButton = document.querySelector('.images-button');
100 const audioButton = document.querySelector('.audio-button');
101
102 await expect(imagesButton).toBeInTheDocument();
103 await expect(audioButton).toBeInTheDocument();
104
105 // Close dropdown by pressing Escape
106 await userEvent.keyboard('{Escape}');
107
108 console.log('✅ Vision modality: Dropdown menu verified');
109 }}
110/>
111
112<Story
113 name="AudioModality"
114 args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
115 play={async ({ canvas, userEvent }) => {
116 mockServerProps(mockConfigs.audioOnly);
117
118 // Open file attachments dropdown and verify it works
119 const fileUploadButton = canvas.getByText('Attach files');
120 await userEvent.click(fileUploadButton);
121
122 // Verify dropdown menu items exist
123 const imagesButton = document.querySelector('.images-button');
124 const audioButton = document.querySelector('.audio-button');
125
126 await expect(imagesButton).toBeInTheDocument();
127 await expect(audioButton).toBeInTheDocument();
128
129 // Close dropdown by pressing Escape
130 await userEvent.keyboard('{Escape}');
131
132 console.log('✅ Audio modality: Dropdown menu verified');
133 }}
134/>
135
136<Story
137 name="FileAttachments"
138 args={{
139 class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
140 uploadedFiles: fileAttachments
141 }}
142 play={async ({ canvas }) => {
143 mockServerProps(mockConfigs.bothModalities);
144
145 const jpgAttachment = canvas.getByAltText('1.jpg');
146 const svgAttachment = canvas.getByAltText('hf-logo.svg');
147 const pdfFileExtension = canvas.getByText('PDF');
148 const pdfAttachment = canvas.getByText('example.pdf');
149 const pdfSize = canvas.getByText('342.82 KB');
150
151 await expect(jpgAttachment).toBeInTheDocument();
152 await expect(jpgAttachment).toHaveAttribute('src', jpgAsset);
153
154 await expect(svgAttachment).toBeInTheDocument();
155 await expect(svgAttachment).toHaveAttribute('src', svgAsset);
156
157 await expect(pdfFileExtension).toBeInTheDocument();
158 await expect(pdfAttachment).toBeInTheDocument();
159 await expect(pdfSize).toBeInTheDocument();
160 }}
161/>