diff --git a/config/config2.yaml b/config/config2.yaml index 8e5825b57..22df387cb 100644 --- a/config/config2.yaml +++ b/config/config2.yaml @@ -4,4 +4,4 @@ llm: api_type: "openai" # or azure / ollama / open_llm etc. Check LLMType for more options model: "gpt-4-turbo-preview" # or gpt-3.5-turbo-1106 / gpt-4-1106-preview base_url: "https://api.openai.com/v1" # or forward url / other llm url - api_key: "YOUR_API_KEY" \ No newline at end of file + api_key: "YOUR_API_KEY-hui" \ No newline at end of file diff --git a/tests/metagpt/tools/libs/test_browser.py b/tests/metagpt/tools/libs/test_browser.py index ec0b5c848..dd699e5e9 100644 --- a/tests/metagpt/tools/libs/test_browser.py +++ b/tests/metagpt/tools/libs/test_browser.py @@ -1,90 +1,122 @@ +from unittest.mock import AsyncMock, patch + import pytest +import pytest_asyncio from metagpt.const import TEST_DATA_PATH -from metagpt.tools.libs.browser import Browser, get_scroll_position +from metagpt.tools.libs.browser import Browser TEST_URL = "https://docs.deepwisdom.ai/main/en/guide/get_started/quickstart.html" TEST_SCREENSHOT_PATH = TEST_DATA_PATH / "screenshot.png" -@pytest.fixture(autouse=True) -def llm_mock(rsp_cache, mocker, request): - # An empty fixture to overwrite the global llm_mock fixture - # because in provider folder, we want to test the aask and aask functions for the specific models - pass - - -@pytest.fixture -def browser(): - browser_instance = Browser() - yield browser_instance - - @pytest.mark.asyncio -async def test_open_and_switch_page(browser): - await browser.start() +class TestBrowser: + test_url = "https://juejin.cn/" - await browser.open_new_page("https://baidu.com") - await browser.open_new_page("https://tencent.com") - assert browser.current_page_url == "https://tencent.com" - await browser.switch_page("https://baidu.com") - assert browser.current_page_url == "https://baidu.com" + @pytest_asyncio.fixture(autouse=True) + async def browser_client(self): + """Setup before each test case.""" + print("browser_client") + browser = await self.async_setup() + yield browser + await self.browser.stop() - await browser.close() + async def async_setup(self): + self.browser = Browser(headless=True) + await self.browser.start() + return self.browser + async def async_teardown(self): + """Teardown after each test case.""" + await self.browser.stop() -@pytest.mark.asyncio -async def test_search(browser): - await browser.start() + async def test_start_and_stop(self): + """Test browser start and stop functionality.""" + assert self.browser.playwright is not None + assert self.browser.browser_instance is not None + assert self.browser.browser_ctx is not None + assert self.browser.page is not None - # search all - await browser.open_new_page(TEST_URL) - search_term = "startup example" - search_results = await browser.search_content_all(search_term) - print(search_results) - # expected search result as of 20240410: - # [{'index': 0, 'content': {'text_block': 'Below is a breakdown of the software startup example. If you install MetaGPT with the git clone approach, simply run', 'links': [{'text': 'software startup example', 'href': 'https://github.com/geekan/MetaGPT/blob/main/metagpt/software_company.py'}]}, 'position': {'from_top': 640, 'from_left': 225}, 'element_obj': selector='text=startup example >> nth=0'>}] - first_result = search_results[0]["content"] - assert "software startup example" in first_result["text_block"] - assert first_result["links"] - assert first_result["links"][0]["href"] == "https://github.com/geekan/MetaGPT/blob/main/metagpt/software_company.py" - assert search_results[0]["position"] + await self.async_teardown() - # scroll to search result - await browser.scroll_to_search_result(search_results, index=0) + assert self.browser.playwright is None + assert self.browser.browser_instance is None + assert self.browser.browser_ctx is None - await browser.close() + async def test_goto(self): + """Test navigating to a URL.""" + mock_reporter = AsyncMock() + self.browser.reporter = mock_reporter + result = await self.browser.goto(self.test_url) + assert "SUCCESS" in result + assert self.test_url in self.browser.page.url -# @pytest.mark.asyncio -# async def test_find_links(browser): -# await browser.start() + @patch("metagpt.tools.libs.browser.click_element", new_callable=AsyncMock) + async def test_click(self, mock_click_element): + """Test clicking on an element.""" + self.browser.accessibility_tree = [ + {"nodeId": "1", "backendDOMNodeId": 101, "name": "Button"}, + {"nodeId": "2", "backendDOMNodeId": 102, "name": "Input"}, + ] + self.browser.page = AsyncMock() -# await browser.open_new_page(TEST_URL) -# link_info = await browser.find_links() -# assert link_info + await self.browser.click(1) -# await browser.close() + mock_click_element.assert_called_once() + @patch("metagpt.tools.libs.browser.click_element", new_callable=AsyncMock) + @patch("metagpt.tools.libs.browser.type_text", new_callable=AsyncMock) + async def test_type(self, mock_type_text, mock_click_element): + """Test typing text into an input field.""" + content = "Hello, world!" + self.browser.accessibility_tree = [ + {"nodeId": "1", "backendDOMNodeId": 101, "name": "Button"}, + {"nodeId": "2", "backendDOMNodeId": 102, "name": "Input"}, + ] + self.browser.page = AsyncMock() -@pytest.mark.asyncio -async def test_scroll(browser): - await browser.start() + await self.browser.type(1, content) - await browser.open_new_page(TEST_URL) + mock_click_element.assert_called_once() + mock_type_text.assert_called_once_with(self.browser.page, content) - await browser.scroll_current_page(offset=-500) - assert await get_scroll_position(browser.current_page) == {"x": 0, "y": 0} # no change if you scrol up from top - initial_view = await browser._view() + @patch("metagpt.tools.libs.browser.key_press", new_callable=AsyncMock) + @patch("metagpt.tools.libs.browser.hover_element", new_callable=AsyncMock) + async def test_hover_press(self, mock_hover_element, mock_key_press): + """Test Hover and press key""" + self.browser.accessibility_tree = [ + {"nodeId": "1", "backendDOMNodeId": 101, "name": "Button"}, + {"nodeId": "2", "backendDOMNodeId": 102, "name": "Input"}, + ] + self.browser.page = AsyncMock() - await browser.scroll_current_page(offset=500) # scroll down - assert await get_scroll_position(browser.current_page) == {"x": 0, "y": 500} - scrolled_view = await browser._view() + key_comb = "Enter" + await self.browser.hover(1) + await self.browser.press(key_comb) + mock_hover_element.assert_called_once() + mock_key_press.assert_called_once_with(self.browser.page, key_comb) - assert initial_view != scrolled_view + async def test_scroll(self): + """Scroll the page up or down.""" + await self.browser.scroll("down") + await self.browser.scroll("up") - await browser.scroll_current_page(offset=-200) # scroll up - assert await get_scroll_position(browser.current_page) == {"x": 0, "y": 300} + async def test_go_back_and_forward(self): + await self.browser.go_back() + await self.browser.go_forward() - await browser.close() + async def test_tab_focus(self): + await self.browser.tab_focus(0) + + async def test_close_tab(self): + """Test closing a tab.""" + mock_close = AsyncMock() + self.browser.page = AsyncMock() + self.browser.page.close = mock_close + + await self.browser.close_tab() + + mock_close.assert_called_once()