Files
wehub-resource-sync b7f52be4c9
CI / Run CI (push) Has been cancelled
CI / check-backend (push) Has been cancelled
CI / check-frontend (push) Has been cancelled
CI / tests (push) Has been cancelled
CI / e2e-tests (push) Has been cancelled
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:48:47 +08:00

304 lines
13 KiB
Python

import pytest
from chainlit.element import File, Image, Text
from chainlit.sidebar import ElementSidebar
@pytest.mark.asyncio
class TestElementSidebar:
"""Test suite for ElementSidebar class."""
async def test_set_title(self, mock_chainlit_context):
"""Test ElementSidebar.set_title() method."""
async with mock_chainlit_context as ctx:
await ElementSidebar.set_title("My Sidebar Title")
ctx.emitter.emit.assert_called_once_with(
"set_sidebar_title", "My Sidebar Title"
)
async def test_set_title_with_empty_string(self, mock_chainlit_context):
"""Test ElementSidebar.set_title() with empty string."""
async with mock_chainlit_context as ctx:
await ElementSidebar.set_title("")
ctx.emitter.emit.assert_called_once_with("set_sidebar_title", "")
async def test_set_title_with_special_characters(self, mock_chainlit_context):
"""Test ElementSidebar.set_title() with special characters."""
async with mock_chainlit_context as ctx:
title = "Title with 特殊字符 & symbols! 🎉"
await ElementSidebar.set_title(title)
ctx.emitter.emit.assert_called_once_with("set_sidebar_title", title)
async def test_set_elements_with_single_element(self, mock_chainlit_context):
"""Test ElementSidebar.set_elements() with a single element."""
async with mock_chainlit_context as ctx:
element = File(name="test.txt", url="https://example.com/test.txt")
await ElementSidebar.set_elements([element])
# Verify element.send() was called
ctx.emitter.send_element.assert_called_once()
# Verify emit was called with correct structure
ctx.emitter.emit.assert_called_once()
call_args = ctx.emitter.emit.call_args
assert call_args[0][0] == "set_sidebar_elements"
assert "elements" in call_args[0][1]
assert "key" in call_args[0][1]
assert len(call_args[0][1]["elements"]) == 1
assert call_args[0][1]["key"] is None
async def test_set_elements_with_multiple_elements(self, mock_chainlit_context):
"""Test ElementSidebar.set_elements() with multiple elements."""
async with mock_chainlit_context as ctx:
elements = [
File(name="file1.txt", url="https://example.com/file1.txt"),
Image(name="image1.png", url="https://example.com/image1.png"),
Text(name="text1", content="Some text content"),
]
await ElementSidebar.set_elements(elements)
# Verify all elements were sent (3 send_element calls)
assert ctx.emitter.send_element.call_count == 3
# Verify emit was called with all elements
ctx.emitter.emit.assert_called_once()
call_args = ctx.emitter.emit.call_args
assert call_args[0][0] == "set_sidebar_elements"
assert len(call_args[0][1]["elements"]) == 3
async def test_set_elements_with_empty_list(self, mock_chainlit_context):
"""Test ElementSidebar.set_elements() with empty list (closes sidebar)."""
async with mock_chainlit_context as ctx:
await ElementSidebar.set_elements([])
# No elements to send
ctx.emitter.send_element.assert_not_called()
# Emit should still be called with empty elements
ctx.emitter.emit.assert_called_once()
call_args = ctx.emitter.emit.call_args
assert call_args[0][0] == "set_sidebar_elements"
assert call_args[0][1]["elements"] == []
assert call_args[0][1]["key"] is None
async def test_set_elements_with_key(self, mock_chainlit_context):
"""Test ElementSidebar.set_elements() with a key."""
async with mock_chainlit_context as ctx:
element = File(name="test.txt", url="https://example.com/test.txt")
key = "my_sidebar_key"
await ElementSidebar.set_elements([element], key=key)
# Verify emit was called with the key
ctx.emitter.emit.assert_called_once()
call_args = ctx.emitter.emit.call_args
assert call_args[0][1]["key"] == key
async def test_set_elements_with_for_id(self, mock_chainlit_context):
"""Test ElementSidebar.set_elements() with elements that have for_id."""
async with mock_chainlit_context as ctx:
element = File(
name="test.txt",
url="https://example.com/test.txt",
for_id="message_123",
)
await ElementSidebar.set_elements([element])
# Element should be sent with its for_id
ctx.emitter.send_element.assert_called_once()
# Verify emit was called
ctx.emitter.emit.assert_called_once()
async def test_set_elements_without_for_id(self, mock_chainlit_context):
"""Test ElementSidebar.set_elements() with elements without for_id."""
async with mock_chainlit_context as ctx:
element = File(name="test.txt", url="https://example.com/test.txt")
await ElementSidebar.set_elements([element])
# Element should be sent with empty string for_id
ctx.emitter.send_element.assert_called_once()
# Verify emit was called
ctx.emitter.emit.assert_called_once()
async def test_set_elements_persist_false(self, mock_chainlit_context):
"""Test that set_elements() sends elements with persist=False."""
async with mock_chainlit_context as ctx:
# Mock persist_file to provide chainlit_key
ctx.session.persist_file.return_value = {"id": "test_key"}
element = File(name="test.txt", content=b"test content")
await ElementSidebar.set_elements([element])
# persist_file is still called to get chainlit_key, even with persist=False
# The persist=False affects data layer persistence, not file upload
ctx.session.persist_file.assert_called_once()
# Verify element was sent
ctx.emitter.send_element.assert_called_once()
async def test_set_elements_serialization(self, mock_chainlit_context):
"""Test that elements are properly serialized in set_elements()."""
async with mock_chainlit_context as ctx:
file_elem = File(name="file.txt", url="https://example.com/file.txt")
image_elem = Image(
name="image.png", url="https://example.com/image.png", size="large"
)
await ElementSidebar.set_elements([file_elem, image_elem])
# Verify emit was called with serialized elements
call_args = ctx.emitter.emit.call_args
elements_data = call_args[0][1]["elements"]
assert len(elements_data) == 2
assert elements_data[0]["name"] == "file.txt"
assert elements_data[0]["type"] == "file"
assert elements_data[1]["name"] == "image.png"
assert elements_data[1]["type"] == "image"
assert elements_data[1]["size"] == "large"
@pytest.mark.asyncio
class TestElementSidebarEdgeCases:
"""Test suite for ElementSidebar edge cases."""
async def test_set_title_multiple_times(self, mock_chainlit_context):
"""Test calling set_title() multiple times."""
async with mock_chainlit_context as ctx:
await ElementSidebar.set_title("First Title")
await ElementSidebar.set_title("Second Title")
await ElementSidebar.set_title("Third Title")
assert ctx.emitter.emit.call_count == 3
# Verify last call had the third title
last_call = ctx.emitter.emit.call_args
assert last_call[0][1] == "Third Title"
async def test_set_elements_multiple_times(self, mock_chainlit_context):
"""Test calling set_elements() multiple times."""
async with mock_chainlit_context as ctx:
element1 = File(name="file1.txt", url="https://example.com/file1.txt")
element2 = File(name="file2.txt", url="https://example.com/file2.txt")
await ElementSidebar.set_elements([element1])
await ElementSidebar.set_elements([element2])
# Should have sent both elements
assert ctx.emitter.send_element.call_count == 2
# Should have emitted twice
assert ctx.emitter.emit.call_count == 2
async def test_set_elements_with_same_key_twice(self, mock_chainlit_context):
"""Test calling set_elements() with the same key twice."""
async with mock_chainlit_context as ctx:
element1 = File(name="file1.txt", url="https://example.com/file1.txt")
element2 = File(name="file2.txt", url="https://example.com/file2.txt")
await ElementSidebar.set_elements([element1], key="same_key")
await ElementSidebar.set_elements([element2], key="same_key")
# Both should be sent (server doesn't prevent this)
assert ctx.emitter.send_element.call_count == 2
assert ctx.emitter.emit.call_count == 2
async def test_set_elements_with_different_keys(self, mock_chainlit_context):
"""Test calling set_elements() with different keys."""
async with mock_chainlit_context as ctx:
element1 = File(name="file1.txt", url="https://example.com/file1.txt")
element2 = File(name="file2.txt", url="https://example.com/file2.txt")
await ElementSidebar.set_elements([element1], key="key1")
await ElementSidebar.set_elements([element2], key="key2")
assert ctx.emitter.emit.call_count == 2
# Verify different keys were used
calls = ctx.emitter.emit.call_args_list
assert calls[0][0][1]["key"] == "key1"
assert calls[1][0][1]["key"] == "key2"
async def test_set_elements_with_large_number_of_elements(
self, mock_chainlit_context
):
"""Test set_elements() with many elements."""
async with mock_chainlit_context as ctx:
# Create 50 elements
elements = [
File(name=f"file{i}.txt", url=f"https://example.com/file{i}.txt")
for i in range(50)
]
await ElementSidebar.set_elements(elements)
# All 50 elements should be sent
assert ctx.emitter.send_element.call_count == 50
# Verify emit was called with all 50 elements
call_args = ctx.emitter.emit.call_args
assert len(call_args[0][1]["elements"]) == 50
async def test_set_title_and_set_elements_together(self, mock_chainlit_context):
"""Test using set_title() and set_elements() together."""
async with mock_chainlit_context as ctx:
await ElementSidebar.set_title("My Documents")
elements = [
File(name="doc1.pdf", url="https://example.com/doc1.pdf"),
File(name="doc2.pdf", url="https://example.com/doc2.pdf"),
]
await ElementSidebar.set_elements(elements)
# Verify both methods were called
assert ctx.emitter.emit.call_count == 2
# Verify the calls were correct
calls = ctx.emitter.emit.call_args_list
assert calls[0][0][0] == "set_sidebar_title"
assert calls[0][0][1] == "My Documents"
assert calls[1][0][0] == "set_sidebar_elements"
async def test_set_elements_with_mixed_element_types(self, mock_chainlit_context):
"""Test set_elements() with various element types."""
async with mock_chainlit_context as ctx:
elements = [
File(name="document.pdf", url="https://example.com/doc.pdf"),
Image(
name="photo.jpg", url="https://example.com/photo.jpg", size="medium"
),
Text(name="notes", content="Some important notes"),
]
await ElementSidebar.set_elements(elements)
# Verify all different types were sent
assert ctx.emitter.send_element.call_count == 3
# Verify serialization includes type information
call_args = ctx.emitter.emit.call_args
elements_data = call_args[0][1]["elements"]
assert elements_data[0]["type"] == "file"
assert elements_data[1]["type"] == "image"
assert elements_data[2]["type"] == "text"
async def test_set_title_with_long_string(self, mock_chainlit_context):
"""Test set_title() with a very long title."""
async with mock_chainlit_context as ctx:
long_title = "A" * 1000 # 1000 character title
await ElementSidebar.set_title(long_title)
ctx.emitter.emit.assert_called_once_with("set_sidebar_title", long_title)