ChatForm.stories.svelte 4.8 KB

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