Skip to main content
Glama
test_server.py7.77 kB
import asyncio import tempfile import pytest from pathlib import Path from unittest.mock import AsyncMock, patch, MagicMock from webshot_mcp.server import take_screenshot, list_tools, call_tool, server class TestWebshotMCP: """webshot-mcp 服务器测试""" @pytest.mark.asyncio async def test_list_tools(self): """测试工具列表""" tools = await list_tools() assert len(tools) == 1 assert tools[0].name == "webshot" assert "url" in tools[0].inputSchema["properties"] assert "output" in tools[0].inputSchema["properties"] @pytest.mark.asyncio async def test_call_tool_invalid_name(self): """测试调用无效工具名""" with pytest.raises(ValueError, match="未知工具"): await call_tool("invalid_tool", {}) @pytest.mark.asyncio async def test_take_screenshot_invalid_url(self): """测试无效 URL""" with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: with pytest.raises(ValueError, match="URL 必须以 http:// 或 https:// 开头"): await take_screenshot("invalid-url", tmp.name) @pytest.mark.asyncio async def test_take_screenshot_invalid_format(self): """测试无效格式""" with tempfile.NamedTemporaryFile(suffix=".gif", delete=False) as tmp: with pytest.raises(ValueError, match="格式必须是 png、jpeg 或 webp"): await take_screenshot("https://example.com", tmp.name, format="gif") @pytest.mark.asyncio async def test_take_screenshot_invalid_quality(self): """测试无效质量值""" with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: with pytest.raises(ValueError, match="质量必须在 0-100 之间"): await take_screenshot("https://example.com", tmp.name, quality=150) @pytest.mark.asyncio @patch('webshot_mcp.server.async_playwright') async def test_take_screenshot_success(self, mock_playwright): """测试成功截图""" # 模拟 playwright mock_browser = AsyncMock() mock_page = AsyncMock() mock_context = AsyncMock() mock_context.__aenter__ = AsyncMock(return_value=mock_context) mock_context.__aexit__ = AsyncMock(return_value=None) mock_context.chromium.launch = AsyncMock(return_value=mock_browser) mock_browser.new_page = AsyncMock(return_value=mock_page) mock_browser.new_context = AsyncMock() mock_browser.close = AsyncMock() mock_page.goto = AsyncMock() mock_page.screenshot = AsyncMock() mock_page.set_default_timeout = MagicMock() mock_page.set_default_navigation_timeout = MagicMock() mock_page.wait_for_load_state = AsyncMock() mock_playwright.return_value = mock_context with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: result = await take_screenshot("https://example.com", tmp.name, dpi_scale=1) assert result["status"] == "success" assert "截图已成功保存至" in result["message"] # 验证调用 mock_page.goto.assert_called_once_with("https://example.com", wait_until="domcontentloaded", timeout=30000) mock_page.screenshot.assert_called_once() @pytest.mark.asyncio @patch('webshot_mcp.server.async_playwright') async def test_take_screenshot_full_page(self, mock_playwright): """测试全页面截图""" # 模拟 playwright mock_browser = AsyncMock() mock_page = AsyncMock() mock_context = AsyncMock() mock_context.__aenter__ = AsyncMock(return_value=mock_context) mock_context.__aexit__ = AsyncMock(return_value=None) mock_context.chromium.launch = AsyncMock(return_value=mock_browser) mock_browser.new_page = AsyncMock(return_value=mock_page) mock_browser.new_context = AsyncMock() mock_browser.close = AsyncMock() mock_page.goto = AsyncMock() mock_page.screenshot = AsyncMock() mock_page.set_default_timeout = MagicMock() mock_page.set_default_navigation_timeout = MagicMock() mock_page.wait_for_load_state = AsyncMock() mock_playwright.return_value = mock_context with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: await take_screenshot("https://example.com", tmp.name, height=0) # 验证全页面截图参数 call_args = mock_page.screenshot.call_args assert call_args[1]["full_page"] is True @pytest.mark.asyncio @patch('webshot_mcp.server.async_playwright') async def test_take_screenshot_mobile_device(self, mock_playwright): """测试移动设备截图""" # 模拟 playwright mock_browser = AsyncMock() mock_page = AsyncMock() mock_context = AsyncMock() mock_context.__aenter__ = AsyncMock(return_value=mock_context) mock_context.__aexit__ = AsyncMock(return_value=None) mock_context.chromium.launch = AsyncMock(return_value=mock_browser) mock_browser.new_page = AsyncMock(return_value=mock_page) mock_browser.new_context = AsyncMock() mock_browser.close = AsyncMock() # 模拟 context 返回 page mock_context_obj = AsyncMock() mock_context_obj.new_page = AsyncMock(return_value=mock_page) mock_browser.new_context.return_value = mock_context_obj mock_page.goto = AsyncMock() mock_page.screenshot = AsyncMock() mock_page.set_default_timeout = MagicMock() mock_page.set_default_navigation_timeout = MagicMock() mock_page.wait_for_load_state = AsyncMock() # 模拟 devices mock_playwright.return_value = mock_context mock_context.devices = {"iPhone 13": {"viewport": {"width": 390, "height": 664}}} with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: await take_screenshot("https://example.com", tmp.name, device="mobile", dpi_scale=1) # 验证使用了移动设备配置 # 检查是否创建了 context(使用内置设备时) assert mock_browser.new_context.called or mock_browser.new_page.called def test_server_instance(self): """测试服务器实例""" assert server.name == "webshot-mcp" @pytest.mark.asyncio async def test_call_tool_success(self): """测试成功调用工具""" with patch('webshot_mcp.server.take_screenshot') as mock_screenshot: mock_screenshot.return_value = {"status": "success", "message": "截图成功"} result = await call_tool("webshot", { "url": "https://example.com", "output": "/tmp/test.png" }) assert len(result) == 1 assert result[0].text == "截图成功" @pytest.mark.asyncio async def test_call_tool_error(self): """测试工具调用错误""" with patch('webshot_mcp.server.take_screenshot') as mock_screenshot: mock_screenshot.side_effect = Exception("测试错误") result = await call_tool("webshot", { "url": "https://example.com", "output": "/tmp/test.png" }) assert len(result) == 1 assert "截图失败" in result[0].text assert "测试错误" in result[0].text if __name__ == "__main__": pytest.main([__file__])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bingal/webshot-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server