ui: Linting & Formatting scripts (#26819)

This commit is contained in:
Aleksander Grygier
2026-08-10 08:38:37 +02:00
committed by GitHub
parent 1e396e72a8
commit 92d1bb0c99
538 changed files with 8806 additions and 6036 deletions
@@ -3,86 +3,86 @@
import ChatMessage from '$lib/components/app/chat/ChatMessages/ChatMessage/ChatMessage.svelte';
const { Story } = defineMeta({
title: 'Components/ChatScreen/ChatMessage',
component: ChatMessage,
parameters: {
layout: 'centered'
}
},
title: 'Components/ChatScreen/ChatMessage'
});
// Mock messages for different scenarios
const userMessage: DatabaseMessage = {
id: '1',
convId: 'conv-1',
type: 'message',
timestamp: Date.now() - 1000 * 60 * 5,
role: 'user',
children: [],
content: 'What is the meaning of life, the universe, and everything?',
convId: 'conv-1',
id: '1',
parent: '',
role: 'user',
thinking: '',
children: []
timestamp: Date.now() - 1000 * 60 * 5,
type: 'message'
};
const assistantMessage: DatabaseMessage = {
id: '2',
convId: 'conv-1',
type: 'message',
timestamp: Date.now() - 1000 * 60 * 3,
role: 'assistant',
children: [],
content:
'The answer to the ultimate question of life, the universe, and everything is **42**.\n\nThis comes from Douglas Adams\' "The Hitchhiker\'s Guide to the Galaxy," where a supercomputer named Deep Thought calculated this answer over 7.5 million years. However, the question itself was never properly formulated, which is why the answer seems meaningless without context.',
convId: 'conv-1',
id: '2',
parent: '1',
role: 'assistant',
thinking: '',
children: []
timestamp: Date.now() - 1000 * 60 * 3,
type: 'message'
};
const assistantWithReasoning: DatabaseMessage = {
id: '3',
convId: 'conv-1',
type: 'message',
timestamp: Date.now() - 1000 * 60 * 2,
role: 'assistant',
children: [],
content: "Here's the concise answer, now that I've thought it through carefully for you.",
convId: 'conv-1',
id: '3',
parent: '1',
role: 'assistant',
thinking:
"Let's consider the user's question step by step:\\n\\n1. Identify the core problem\\n2. Evaluate relevant information\\n3. Formulate a clear answer\\n\\nFollowing this process ensures the final response stays focused and accurate.",
children: []
timestamp: Date.now() - 1000 * 60 * 2,
type: 'message'
};
const rawOutputMessage: DatabaseMessage = {
id: '6',
convId: 'conv-1',
type: 'message',
timestamp: Date.now() - 1000 * 60,
role: 'assistant',
children: [],
content:
'<|channel|>analysis<|message|>User greeted me. Initiating overcomplicated analysis: Is this a trap? No, just a normal hello. Respond calmly, act like a helpful assistant, and do not start explaining quantum physics again. Confidence 0.73. Engaging socially acceptable greeting protocol...<|end|>Hello there! How can I help you today?',
convId: 'conv-1',
id: '6',
parent: '1',
role: 'assistant',
thinking: '',
children: []
timestamp: Date.now() - 1000 * 60,
type: 'message'
};
let processingMessage = $state({
id: '4',
convId: 'conv-1',
type: 'message',
timestamp: 0, // No timestamp = processing
role: 'assistant',
children: [],
content: '',
convId: 'conv-1',
id: '4',
parent: '1',
role: 'assistant',
thinking: '',
children: []
timestamp: 0, // No timestamp = processing
type: 'message'
});
let streamingMessage = $state({
id: '5',
convId: 'conv-1',
type: 'message',
timestamp: 0, // No timestamp = streaming
role: 'assistant',
children: [],
content: '',
convId: 'conv-1',
id: '5',
parent: '1',
role: 'assistant',
thinking: '',
children: []
timestamp: 0, // No timestamp = streaming
type: 'message'
});
</script>
@@ -93,6 +93,7 @@
}}
play={async () => {
const { settingsStore } = await import('$lib/stores/settings.svelte');
settingsStore.updateConfig('showRawOutputSwitch', false);
}}
/>
@@ -105,6 +106,7 @@
}}
play={async () => {
const { settingsStore } = await import('$lib/stores/settings.svelte');
settingsStore.updateConfig('showRawOutputSwitch', false);
}}
/>
@@ -117,6 +119,7 @@
}}
play={async () => {
const { settingsStore } = await import('$lib/stores/settings.svelte');
settingsStore.updateConfig('showRawOutputSwitch', false);
}}
/>
@@ -129,6 +132,7 @@
}}
play={async () => {
const { settingsStore } = await import('$lib/stores/settings.svelte');
settingsStore.updateConfig('showRawOutputSwitch', true);
}}
/>
@@ -141,16 +145,18 @@
asChild
play={async () => {
const { settingsStore } = await import('$lib/stores/settings.svelte');
settingsStore.updateConfig('showRawOutputSwitch', false);
// Phase 1: Stream reasoning content in chunks
let reasoningText =
'I need to think about this carefully. Let me break down the problem:\n\n1. The user is asking for help with something complex\n2. I should provide a thorough and helpful response\n3. I need to consider multiple approaches\n4. The best solution would be to explain step by step\n\nThis approach will ensure clarity and understanding.';
let reasoningChunk = 'I';
let i = 0;
while (i < reasoningText.length) {
const chunkSize = Math.floor(Math.random() * 5) + 3; // Random 3-7 characters
const chunk = reasoningText.slice(i, i + chunkSize);
reasoningChunk += chunk;
// Update the reactive state directly
@@ -164,11 +170,13 @@
"Based on my analysis, here's the solution:\n\n**Step 1:** First, we need to understand the requirements clearly.\n\n**Step 2:** Then we can implement the solution systematically.\n\n**Step 3:** Finally, we test and validate the results.\n\nThis approach ensures we cover all aspects of the problem effectively.";
let contentChunk = '';
i = 0;
while (i < regularText.length) {
const chunkSize = Math.floor(Math.random() * 5) + 3; // Random 3-7 characters
const chunk = regularText.slice(i, i + chunkSize);
contentChunk += chunk;
// Update the reactive state directly
@@ -193,6 +201,7 @@
}}
play={async () => {
const { settingsStore } = await import('$lib/stores/settings.svelte');
settingsStore.updateConfig('showRawOutputSwitch', false);
// Import the chat store to simulate loading state
const { chatStore } = await import('$lib/stores/chat.svelte');
@@ -1,42 +1,42 @@
<script module lang="ts">
import jpgAsset from './fixtures/assets/1.jpg?url';
import pdfAsset from './fixtures/assets/example.pdf?raw';
import svgAsset from './fixtures/assets/hf-logo.svg?url';
import { defineMeta } from '@storybook/addon-svelte-csf';
import ChatScreenForm from '$lib/components/app/chat/ChatScreen/ChatScreenForm.svelte';
import { expect } from 'storybook/test';
import jpgAsset from './fixtures/assets/1.jpg?url';
import svgAsset from './fixtures/assets/hf-logo.svg?url';
import pdfAsset from './fixtures/assets/example.pdf?raw';
const { Story } = defineMeta({
title: 'Components/ChatScreen/ChatScreenForm',
component: ChatScreenForm,
parameters: {
layout: 'centered'
}
},
title: 'Components/ChatScreen/ChatScreenForm'
});
let fileAttachments = $state([
{
file: new File([''], '1.jpg', { type: 'image/jpeg' }),
id: '1',
name: '1.jpg',
type: 'image/jpeg',
size: 44891,
preview: jpgAsset,
file: new File([''], '1.jpg', { type: 'image/jpeg' })
size: 44891,
type: 'image/jpeg'
},
{
file: new File([''], 'hf-logo.svg', { type: 'image/svg+xml' }),
id: '2',
name: 'hf-logo.svg',
type: 'image/svg+xml',
size: 1234,
preview: svgAsset,
file: new File([''], 'hf-logo.svg', { type: 'image/svg+xml' })
size: 1234,
type: 'image/svg+xml'
},
{
file: new File([pdfAsset], 'example.pdf', { type: 'application/pdf' }),
id: '3',
name: 'example.pdf',
type: 'application/pdf',
size: 351048,
file: new File([pdfAsset], 'example.pdf', { type: 'application/pdf' })
type: 'application/pdf'
}
]);
</script>
@@ -62,6 +62,7 @@
await expect(textarea).toHaveValue(text);
const fileInput = document.querySelector('input[type="file"]');
await expect(fileInput).not.toHaveAttribute('accept');
}}
/>
@@ -1,59 +1,60 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import { expect } from 'storybook/test';
import { MarkdownContent } from '$lib/components/app';
import { AI_TUTORIAL_MD } from './fixtures/ai-tutorial.js';
import { API_DOCS_MD } from './fixtures/api-docs.js';
import { BLOG_POST_MD } from './fixtures/blog-post.js';
import { DATA_ANALYSIS_MD } from './fixtures/data-analysis.js';
import { README_MD } from './fixtures/readme.js';
import { MATH_FORMULAS_MD } from './fixtures/math-formulas.js';
import { EMPTY_MD } from './fixtures/empty.js';
import { MATH_FORMULAS_MD } from './fixtures/math-formulas.js';
import { README_MD } from './fixtures/readme.js';
import { defineMeta } from '@storybook/addon-svelte-csf';
import { MarkdownContent } from '$lib/components/app';
import { expect } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/MarkdownContent',
component: MarkdownContent,
parameters: {
layout: 'centered'
}
},
title: 'Components/MarkdownContent'
});
</script>
<Story name="Empty" args={{ content: EMPTY_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }} />
<Story name="Empty" args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', content: EMPTY_MD }} />
<Story
name="AI Tutorial"
args={{ content: AI_TUTORIAL_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', content: AI_TUTORIAL_MD }}
/>
<Story
name="API Documentation"
args={{ content: API_DOCS_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', content: API_DOCS_MD }}
/>
<Story
name="Technical Blog"
args={{ content: BLOG_POST_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', content: BLOG_POST_MD }}
/>
<Story
name="Data Analysis"
args={{ content: DATA_ANALYSIS_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', content: DATA_ANALYSIS_MD }}
/>
<Story
name="README file"
args={{ content: README_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', content: README_MD }}
/>
<Story
name="Math Formulas"
args={{ content: MATH_FORMULAS_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', content: MATH_FORMULAS_MD }}
/>
<Story
name="URL Links"
args={{
class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
content: `# URL Links Test
Here are some example URLs that should open in new tabs:
@@ -65,11 +66,11 @@ Here are some example URLs that should open in new tabs:
You can also test inline links like https://example.com or https://docs.python.org.
All links should have \`target="_blank"\` and \`rel="noopener noreferrer"\` attributes for security.`,
class: 'max-w-[56rem] w-[calc(100vw-2rem)]'
All links should have \`target="_blank"\` and \`rel="noopener noreferrer"\` attributes for security.`
}}
play={async (context) => {
const { canvasElement } = context;
// Wait for component to render
await new Promise((resolve) => setTimeout(resolve, 100));
@@ -97,22 +98,26 @@ All links should have \`target="_blank"\` and \`rel="noopener noreferrer"\` attr
const hugginFaceLink = linkList.find(
(link) => link.getAttribute('href') === 'https://huggingface.co'
);
expect(hugginFaceLink).toBeTruthy();
expect(hugginFaceLink?.textContent).toBe('Hugging Face Homepage');
const githubLink = linkList.find(
(link) => link.getAttribute('href') === 'https://github.com/ggml-org/llama.cpp'
);
expect(githubLink).toBeTruthy();
expect(githubLink?.textContent).toBe('GitHub Repository');
const openaiLink = linkList.find((link) => link.getAttribute('href') === 'https://openai.com');
expect(openaiLink).toBeTruthy();
expect(openaiLink?.textContent).toBe('OpenAI Website');
const googleLink = linkList.find(
(link) => link.getAttribute('href') === 'https://www.google.com'
);
expect(googleLink).toBeTruthy();
expect(googleLink?.textContent).toBe('Google Search');
@@ -120,11 +125,13 @@ All links should have \`target="_blank"\` and \`rel="noopener noreferrer"\` attr
const exampleLink = linkList.find(
(link) => link.getAttribute('href') === 'https://example.com'
);
expect(exampleLink).toBeTruthy();
const pythonDocsLink = linkList.find(
(link) => link.getAttribute('href') === 'https://docs.python.org'
);
expect(pythonDocsLink).toBeTruthy();
console.log(`✅ URL Links test passed - Found ${links.length} links with proper attributes`);
@@ -3,39 +3,39 @@
import ModelsSelectorList from '$lib/components/app/models/ModelsSelectorList.svelte';
import ModelsSelectorOption from '$lib/components/app/models/ModelsSelectorOption.svelte';
import type { GroupedModelOptions, ModelItem } from '$lib/components/app/models/utils';
import { modelsStore } from '$lib/stores/models.svelte';
import { ServerModelStatus } from '$lib/enums';
import { modelsStore } from '$lib/stores/models.svelte';
const { Story } = defineMeta({
title: 'Components/ModelsSelector',
parameters: {
layout: 'centered'
}
},
title: 'Components/ModelsSelector'
});
const mockModel = (id: string, name: string, orgName?: string, tags?: string[]): ModelOption => ({
id,
name,
model: orgName ? `${orgName}/${name}` : name,
capabilities: [],
id,
model: orgName ? `${orgName}/${name}` : name,
name,
parsedId: {
raw: orgName ? `${orgName}/${name}` : name,
orgName: orgName ?? null,
modelName: name,
params: null,
activatedParams: null,
modelName: name,
orgName: orgName ?? null,
params: null,
quantization: null,
raw: orgName ? `${orgName}/${name}` : name,
tags: tags ?? []
},
tags
});
const mockRouterEntry = (modelName: string, status: ServerModelStatus): ApiModelDataEntry => ({
created: Date.now(),
id: modelName,
in_cache: true,
object: 'model',
owned_by: 'llamacpp',
created: Date.now(),
in_cache: true,
path: `/models/${modelName}`,
status: { value: status }
});
@@ -60,57 +60,58 @@
mockModelsStore();
const loadedModels: ModelItem[] = [
{ option: mockModel('llama3.1-8b', 'Llama-3.1-8B-Instruct', 'meta'), flatIndex: 0 },
{ option: mockModel('mistral-7b', 'Mistral-7B-v0.3', 'mistralai'), flatIndex: 1 }
{ flatIndex: 0, option: mockModel('llama3.1-8b', 'Llama-3.1-8B-Instruct', 'meta') },
{ flatIndex: 1, option: mockModel('mistral-7b', 'Mistral-7B-v0.3', 'mistralai') }
];
const favoriteModels: ModelItem[] = [
{ option: mockModel('qwen2.5-7b', 'Qwen2.5-7B-Instruct', 'Qwen'), flatIndex: 2 },
{ option: mockModel('llama3.2-3b', 'Llama-3.2-3B-Instruct', 'meta'), flatIndex: 3 }
{ flatIndex: 2, option: mockModel('qwen2.5-7b', 'Qwen2.5-7B-Instruct', 'Qwen') },
{ flatIndex: 3, option: mockModel('llama3.2-3b', 'Llama-3.2-3B-Instruct', 'meta') }
];
const availableModels: ModelItem[] = [
{
option: mockModel('deepseek-coder-6.7b', 'DeepSeek-Coder-6.7B', 'deepseek', ['coding']),
flatIndex: 4
flatIndex: 4,
option: mockModel('deepseek-coder-6.7b', 'DeepSeek-Coder-6.7B', 'deepseek', ['coding'])
},
{ option: mockModel('gemma-2-9b', 'Gemma-2-9B-IT', 'google'), flatIndex: 5 },
{ option: mockModel('phi-3-mini', 'Phi-3-mini-4k', 'microsoft'), flatIndex: 6 },
{ option: mockModel('codellama-7b', 'CodeLlama-7B', 'codellama', ['coding']), flatIndex: 7 },
{ option: mockModel('neural-chat-7b', 'Neural-Chat-7B-v3-3', 'intel'), flatIndex: 8 }
{ flatIndex: 5, option: mockModel('gemma-2-9b', 'Gemma-2-9B-IT', 'google') },
{ flatIndex: 6, option: mockModel('phi-3-mini', 'Phi-3-mini-4k', 'microsoft') },
{ flatIndex: 7, option: mockModel('codellama-7b', 'CodeLlama-7B', 'codellama', ['coding']) },
{ flatIndex: 8, option: mockModel('neural-chat-7b', 'Neural-Chat-7B-v3-3', 'intel') }
];
const groupedOptions: GroupedModelOptions = {
loaded: loadedModels,
favorites: favoriteModels,
available: [
{
orgName: 'deepseek',
items: [availableModels[0]]
items: [availableModels[0]],
orgName: 'deepseek'
},
{
orgName: 'google',
items: [availableModels[1]]
items: [availableModels[1]],
orgName: 'google'
},
{
orgName: 'microsoft',
items: [availableModels[2]]
items: [availableModels[2]],
orgName: 'microsoft'
},
{
orgName: 'codellama',
items: [availableModels[3]]
items: [availableModels[3]],
orgName: 'codellama'
},
{
orgName: 'intel',
items: [availableModels[4]]
items: [availableModels[4]],
orgName: 'intel'
}
]
],
favorites: favoriteModels,
loaded: loadedModels
};
function handleSelect(modelId: string) {
const opt = [...loadedModels, ...favoriteModels, ...availableModels].find(
(m) => m.option.id === modelId
);
if (opt) {
selectedModel = opt.option.model;
activeId = modelId;
@@ -134,9 +135,9 @@
<div class="w-80 rounded-lg border border-border bg-popover p-2 shadow-md">
<ModelsSelectorList
groups={{
loaded: [loadedModels[0]],
available: [],
favorites: [],
available: []
loaded: [loadedModels[0]]
}}
currentModel={null}
activeId={null}
@@ -150,9 +151,9 @@
<div class="w-80 rounded-lg border border-border bg-popover p-2 shadow-md">
<ModelsSelectorList
groups={{
loaded: [],
available: [],
favorites: favoriteModels,
available: []
loaded: []
}}
currentModel={null}
activeId={null}
@@ -4,11 +4,11 @@
import { expect } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/PwaRefreshAlert',
component: PwaRefreshAlert,
parameters: {
layout: 'centered'
}
},
title: 'Components/PwaRefreshAlert'
});
</script>
@@ -17,12 +17,15 @@
args={{ needRefresh: true, updateServiceWorker: () => console.log('reload') }}
play={async ({ canvas }) => {
const title = canvas.getByText('Update available');
await expect(title).toBeInTheDocument();
const description = canvas.getByText(/A new version is available/);
await expect(description).toBeInTheDocument();
const button = canvas.getByRole('button', { name: 'Reload' });
await expect(button).toBeInTheDocument();
}}
/>
@@ -32,6 +35,7 @@
args={{ needRefresh: false, updateServiceWorker: () => console.log('reload') }}
play={async ({ canvas }) => {
const title = canvas.queryByText('Update available');
await expect(title).not.toBeInTheDocument();
}}
/>
@@ -44,14 +48,17 @@
}}
play={async ({ canvas, userEvent }) => {
const button = canvas.getByRole('button', { name: 'Reload' });
await expect(button).toBeInTheDocument();
await userEvent.click(button);
const title = canvas.queryByText('Update available');
await expect(title).not.toBeInTheDocument();
const reloadBtn = canvas.queryByRole('button', { name: 'Reload' });
await expect(reloadBtn).not.toBeInTheDocument();
}}
/>
@@ -5,11 +5,11 @@
import { screen } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/SidebarNavigation',
component: SidebarNavigation,
parameters: {
layout: 'centered'
}
},
title: 'Components/SidebarNavigation'
});
</script>
@@ -17,34 +17,34 @@
// Mock conversations for the sidebar
const mockConversations: DatabaseConversation[] = [
{
currNode: 'msg-1',
id: 'conv-1',
name: 'Getting Started with AI',
lastModified: Date.now() - 1000 * 60 * 5, // 5 minutes ago
currNode: 'msg-1'
name: 'Getting Started with AI'
},
{
currNode: 'msg-2',
id: 'conv-2',
name: 'Python Programming Help',
lastModified: Date.now() - 1000 * 60 * 60 * 2, // 2 hours ago
currNode: 'msg-2'
name: 'Python Programming Help'
},
{
currNode: 'msg-3',
id: 'conv-3',
name: 'Creative Writing Ideas',
lastModified: Date.now() - 1000 * 60 * 60 * 24, // 1 day ago
currNode: 'msg-3'
name: 'Creative Writing Ideas'
},
{
currNode: 'msg-4',
id: 'conv-4',
name: 'This is a very long conversation title that should be truncated properly when displayed',
lastModified: Date.now() - 1000 * 60 * 60 * 24 * 3, // 3 days ago
currNode: 'msg-4'
name: 'This is a very long conversation title that should be truncated properly when displayed'
},
{
currNode: 'msg-5',
id: 'conv-5',
name: 'Math Problem Solving',
lastModified: Date.now() - 1000 * 60 * 60 * 24 * 7, // 1 week ago
currNode: 'msg-5'
name: 'Math Problem Solving'
}
];
</script>
@@ -81,8 +81,10 @@
// Expand sidebar first, then click Search in the expanded button list
const logoTrigger = screen.getByRole('button', { name: /expand navigation/i });
await userEvent.click(logoTrigger);
const searchTrigger = screen.getByText('Search');
userEvent.click(searchTrigger);
}}
>
@@ -97,6 +99,7 @@
play={async () => {
// Mock empty conversations store
const { conversationsStore } = await import('$lib/stores/conversations.svelte');
conversationsStore.conversations = [];
}}
>
@@ -1,16 +1,16 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import { Copy } from '@lucide/svelte';
import { defineMeta } from '@storybook/addon-svelte-csf';
import ActionIcon from '$lib/components/app/actions/ActionIcon.svelte';
import { expect } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/ActionIcon/Accessibility',
component: ActionIcon,
parameters: {
layout: 'centered'
},
tags: ['!dev']
tags: ['!dev'],
title: 'Components/ActionIcon/Accessibility'
});
</script>
@@ -4,30 +4,30 @@
import { expect } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/ChatMessageStatistics/Accessibility',
component: ChatMessageStatistics,
parameters: {
layout: 'centered'
},
tags: ['!dev']
tags: ['!dev'],
title: 'Components/ChatMessageStatistics/Accessibility'
});
</script>
<Story
name="ViewButtonsSingleTabStop"
args={{
promptTokens: 100,
promptMs: 500,
predictedTokens: 200,
predictedMs: 1000,
agenticTimings: {
turns: 1,
llm: { predicted_ms: 1000, predicted_n: 200, prompt_ms: 500, prompt_n: 100 },
toolCallsCount: 1,
toolsMs: 500,
llm: { predicted_n: 200, predicted_ms: 1000, prompt_n: 100, prompt_ms: 500 }
turns: 1
},
hideSummary: false,
isLive: false
isLive: false,
predictedMs: 1000,
predictedTokens: 200,
promptMs: 500,
promptTokens: 100
}}
play={async ({ canvas, userEvent }) => {
const reading = await canvas.findByRole('button', { name: 'Reading' });
@@ -1,16 +1,16 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import ChatScreenForm from '$lib/components/app/chat/ChatScreen/ChatScreenForm.svelte';
import { expect, screen, waitFor } from 'storybook/test';
import { ATTACHMENT_TOOLTIP_TEXT } from '$lib/constants';
import { expect, screen, waitFor } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/ChatScreen/ChatScreenForm/Accessibility',
component: ChatScreenForm,
parameters: {
layout: 'centered'
},
tags: ['!dev']
tags: ['!dev'],
title: 'Components/ChatScreen/ChatScreenForm/Accessibility'
});
</script>
@@ -19,6 +19,7 @@
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
play={async ({ canvas, userEvent }) => {
const textarea = await canvas.findByRole('textbox');
await userEvent.clear(textarea);
await userEvent.type(textarea, 'What is the meaning of life?');
@@ -4,12 +4,12 @@
import { expect, waitFor } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/HorizontalScrollCarousel/Accessibility',
component: HorizontalScrollCarousel,
parameters: {
layout: 'centered'
},
tags: ['!dev']
tags: ['!dev'],
title: 'Components/HorizontalScrollCarousel/Accessibility'
});
</script>
@@ -4,20 +4,20 @@
import { expect } from 'storybook/test';
const mockForkedConversation: DatabaseConversation = {
id: 'conv-2',
name: 'Forked Conversation',
lastModified: Date.now(),
currNode: 'msg-2',
forkedFromConversationId: 'conv-1'
forkedFromConversationId: 'conv-1',
id: 'conv-2',
lastModified: Date.now(),
name: 'Forked Conversation'
};
const { Story } = defineMeta({
title: 'Components/SidebarNavigationConversationItem/Accessibility',
component: SidebarNavigationConversationItem,
parameters: {
layout: 'centered'
},
tags: ['!dev']
tags: ['!dev'],
title: 'Components/SidebarNavigationConversationItem/Accessibility'
});
</script>
@@ -1,5 +1,5 @@
import { serverStore } from '$lib/stores/server.svelte';
import { modelsStore } from '$lib/stores/models.svelte';
import { serverStore } from '$lib/stores/server.svelte';
/**
* Mock server properties for Storybook testing
@@ -8,16 +8,17 @@ import { modelsStore } from '$lib/stores/models.svelte';
export function mockServerProps(props: Partial<ApiLlamaCppServerProps>): void {
// Reset any pointer-events from previous tests (dropdown cleanup)
const body = document.querySelector('body');
if (body) body.style.pointerEvents = '';
// Directly set the props for testing purposes
(serverStore as unknown as { props: ApiLlamaCppServerProps }).props = {
model_path: props.model_path || 'test-model',
modalities: {
vision: props.modalities?.vision ?? false,
audio: props.modalities?.audio ?? false,
video: props.modalities?.video ?? false
video: props.modalities?.video ?? false,
vision: props.modalities?.vision ?? false
},
model_path: props.model_path || 'test-model',
...props
} as ApiLlamaCppServerProps;
@@ -41,8 +42,8 @@ export function mockServerProps(props: Partial<ApiLlamaCppServerProps>): void {
(modelsStore as any).models = [
{
id: 'test-model',
name: 'Test Model',
model: 'test-model'
model: 'test-model',
name: 'Test Model'
}
];
@@ -56,12 +57,12 @@ export function mockServerProps(props: Partial<ApiLlamaCppServerProps>): void {
*/
export function resetServerStore(): void {
(serverStore as unknown as { props: ApiLlamaCppServerProps }).props = {
model_path: '',
modalities: {
vision: false,
audio: false,
video: false
}
video: false,
vision: false
},
model_path: ''
} as ApiLlamaCppServerProps;
(serverStore as unknown as { error: string }).error = '';
(serverStore as unknown as { loading: boolean }).loading = false;
@@ -71,16 +72,16 @@ export function resetServerStore(): void {
* Common mock configurations for Storybook stories
*/
export const mockConfigs = {
visionOnly: {
modalities: { vision: true, audio: false }
},
audioOnly: {
modalities: { vision: false, audio: true }
modalities: { audio: true, vision: false }
},
bothModalities: {
modalities: { vision: true, audio: true }
modalities: { audio: true, vision: true }
},
noModalities: {
modalities: { vision: false, audio: false, video: false }
modalities: { audio: false, video: false, vision: false }
},
visionOnly: {
modalities: { audio: false, vision: true }
}
} as const;