Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions web-app/src/components/ui/__tests__/sheet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent>
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<div>Sheet Content</div>
</SheetContent>
</Sheet>
)

const content = document.querySelector('[data-slot="sheet-content"]')
expect(content).toBeInTheDocument()
expect(content).toHaveClass('inset-y-0', 'right-0')
Expand All @@ -67,11 +68,12 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent side="left">
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<div>Sheet Content</div>
</SheetContent>
</Sheet>
)

const content = document.querySelector('[data-slot="sheet-content"]')
expect(content).toHaveClass('inset-y-0', 'left-0')
})
Expand All @@ -81,11 +83,12 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent side="top">
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<div>Sheet Content</div>
</SheetContent>
</Sheet>
)

const content = document.querySelector('[data-slot="sheet-content"]')
expect(content).toHaveClass('inset-x-0', 'top-0')
})
Expand All @@ -95,11 +98,12 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent side="bottom">
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<div>Sheet Content</div>
</SheetContent>
</Sheet>
)

const content = document.querySelector('[data-slot="sheet-content"]')
expect(content).toHaveClass('inset-x-0', 'bottom-0')
})
Expand All @@ -109,13 +113,14 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent>
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<SheetHeader>
<div>Header Content</div>
</SheetHeader>
</SheetContent>
</Sheet>
)

const header = document.querySelector('[data-slot="sheet-header"]')
expect(header).toBeInTheDocument()
expect(header).toHaveClass('flex', 'flex-col', 'gap-1.5', 'p-4')
Expand All @@ -126,13 +131,14 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent>
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<SheetFooter>
<div>Footer Content</div>
</SheetFooter>
</SheetContent>
</Sheet>
)

const footer = document.querySelector('[data-slot="sheet-footer"]')
expect(footer).toBeInTheDocument()
expect(footer).toHaveClass('mt-auto', 'flex', 'flex-col', 'gap-2', 'p-4')
Expand All @@ -143,10 +149,11 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent>
<SheetTitle>Sheet Title</SheetTitle>
<SheetDescription>Test description</SheetDescription>
</SheetContent>
</Sheet>
)

const title = document.querySelector('[data-slot="sheet-title"]')
expect(title).toBeInTheDocument()
expect(title).toHaveTextContent('Sheet Title')
Expand Down Expand Up @@ -174,11 +181,12 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent>
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<div>Content</div>
</SheetContent>
</Sheet>
)

const closeButton = document.querySelector('.absolute.top-4.right-4')
expect(closeButton).toBeInTheDocument()
expect(closeButton).toHaveClass('rounded-xs', 'opacity-70', 'transition-opacity')
Expand All @@ -189,11 +197,12 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent>
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<div>Content</div>
</SheetContent>
</Sheet>
)

const overlay = document.querySelector('[data-slot="sheet-overlay"]')
expect(overlay).toBeInTheDocument()
expect(overlay).toHaveClass('fixed', 'inset-0', 'z-50', 'bg-main-view/50', 'backdrop-blur-xs')
Expand All @@ -204,11 +213,12 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent>
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<SheetClose>Close</SheetClose>
</SheetContent>
</Sheet>
)

const close = document.querySelector('[data-slot="sheet-close"]')
expect(close).toBeInTheDocument()
expect(close).toHaveTextContent('Close')
Expand All @@ -219,11 +229,12 @@ describe('Sheet Components', () => {
<Sheet defaultOpen>
<SheetContent className="custom-sheet">
<SheetTitle>Test Sheet</SheetTitle>
<SheetDescription>Test description</SheetDescription>
<div>Content</div>
</SheetContent>
</Sheet>
)

const content = document.querySelector('[data-slot="sheet-content"]')
expect(content).toHaveClass('custom-sheet')
})
Expand Down
104 changes: 65 additions & 39 deletions web-app/src/containers/__tests__/ChatInput.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,39 +188,39 @@ describe('ChatInput', () => {
mockAppState.tools = []
})

it('renders chat input textarea', () => {
act(() => {
it('renders chat input textarea', async () => {
await act(async () => {
renderWithRouter()
})

const textarea = screen.getByRole('textbox')
expect(textarea).toBeInTheDocument()
expect(textarea).toHaveAttribute('placeholder', 'common:placeholder.chatInput')
})

it('renders send button', () => {
act(() => {
it('renders send button', async () => {
await act(async () => {
renderWithRouter()
})

const sendButton = document.querySelector('[data-test-id="send-message-button"]')
expect(sendButton).toBeInTheDocument()
})

it('disables send button when prompt is empty', () => {
act(() => {
it('disables send button when prompt is empty', async () => {
await act(async () => {
renderWithRouter()
})

const sendButton = document.querySelector('[data-test-id="send-message-button"]')
expect(sendButton).toBeDisabled()
})

it('enables send button when prompt has content', () => {
it('enables send button when prompt has content', async () => {
// Set prompt content
mockPromptState.prompt = 'Hello world'

act(() => {
await act(async () => {
renderWithRouter()
})

Expand All @@ -230,10 +230,14 @@ describe('ChatInput', () => {

it('calls setPrompt when typing in textarea', async () => {
const user = userEvent.setup()
renderWithRouter()
await act(async () => {
renderWithRouter()
})

const textarea = screen.getByRole('textbox')
await user.type(textarea, 'Hello')
await act(async () => {
await user.type(textarea, 'Hello')
})

// setPrompt is called for each character typed
expect(mockPromptState.setPrompt).toHaveBeenCalledTimes(5)
Expand All @@ -246,10 +250,14 @@ describe('ChatInput', () => {
// Set prompt content
mockPromptState.prompt = 'Hello world'

renderWithRouter()
await act(async () => {
renderWithRouter()
})

const sendButton = document.querySelector('[data-test-id="send-message-button"]')
await user.click(sendButton)
await act(async () => {
await user.click(sendButton)
})

// Note: Since useChat now returns the sendMessage function directly, we need to mock it differently
// For now, we'll just check that the button was clicked successfully
Expand All @@ -262,10 +270,14 @@ describe('ChatInput', () => {
// Set prompt content
mockPromptState.prompt = 'Hello world'

renderWithRouter()
await act(async () => {
renderWithRouter()
})

const textarea = screen.getByRole('textbox')
await user.type(textarea, '{Enter}')
await act(async () => {
await user.type(textarea, '{Enter}')
})

// Just verify the textarea exists and Enter was processed
expect(textarea).toBeInTheDocument()
Expand All @@ -277,34 +289,38 @@ describe('ChatInput', () => {
// Set prompt content
mockPromptState.prompt = 'Hello world'

renderWithRouter()
await act(async () => {
renderWithRouter()
})

const textarea = screen.getByRole('textbox')
await user.type(textarea, '{Shift>}{Enter}{/Shift}')
await act(async () => {
await user.type(textarea, '{Shift>}{Enter}{/Shift}')
})

// Just verify the textarea exists
expect(textarea).toBeInTheDocument()
})

it('shows stop button when streaming', () => {
it('shows stop button when streaming', async () => {
// Mock streaming state
mockAppState.streamingContent = { thread_id: 'test-thread' }
act(() => {

await act(async () => {
renderWithRouter()
})

// Stop button should be rendered (as SVG with tabler-icon-player-stop-filled class)
const stopButton = document.querySelector('.tabler-icon-player-stop-filled')
expect(stopButton).toBeInTheDocument()
})


it('shows model selection dropdown', () => {
act(() => {
it('shows model selection dropdown', async () => {
await act(async () => {
renderWithRouter()
})

// Model selection dropdown should be rendered (look for popover trigger)
const modelDropdown = document.querySelector('[data-slot="popover-trigger"]')
expect(modelDropdown).toBeInTheDocument()
Expand All @@ -316,19 +332,25 @@ describe('ChatInput', () => {
// Mock no selected model and prompt with content
mockPromptState.prompt = 'Hello world'

renderWithRouter()
await act(async () => {
renderWithRouter()
})

const sendButton = document.querySelector('[data-test-id="send-message-button"]')
await user.click(sendButton)
await act(async () => {
await user.click(sendButton)
})

// The component should still render without crashing when no model is selected
expect(sendButton).toBeInTheDocument()
})

it('handles file upload', async () => {
const user = userEvent.setup()
renderWithRouter()

await act(async () => {
renderWithRouter()
})

// Wait for async effects to complete (mmproj check)
await waitFor(() => {
// File upload is rendered as hidden input element
Expand All @@ -337,33 +359,37 @@ describe('ChatInput', () => {
})
})

it('disables input when streaming', () => {
it('disables input when streaming', async () => {
// Mock streaming state
mockAppState.streamingContent = { thread_id: 'test-thread' }
act(() => {

await act(async () => {
renderWithRouter()
})

const textarea = screen.getByTestId('chat-input')
expect(textarea).toBeDisabled()
})

it('shows tools dropdown when model supports tools and MCP servers are connected', async () => {
// Mock connected servers
mockGetConnectedServers.mockResolvedValue(['server1'])

renderWithRouter()


await act(async () => {
renderWithRouter()
})

await waitFor(() => {
// Tools dropdown should be rendered (as SVG icon with tabler-icon-tool class)
const toolsIcon = document.querySelector('.tabler-icon-tool')
expect(toolsIcon).toBeInTheDocument()
})
})

it('uses selectedProvider for provider checks', () => {
it('uses selectedProvider for provider checks', async () => {
// This test ensures the component renders without errors when using selectedProvider
expect(() => renderWithRouter()).not.toThrow()
await act(async () => {
expect(() => renderWithRouter()).not.toThrow()
})
})
})
Loading
Loading