Spaces:
Sleeping
Sleeping
| """ | |
| Exploration agent tests for intelligent UI bug discovery. | |
| This test module validates the ExplorationAgent's heuristic algorithms | |
| (depth-first search, breadth-first search, random walk) for automated | |
| bug discovery through UI navigation. | |
| Test Coverage: | |
| - DFS exploration (depth-first, deep UI paths first) | |
| - BFS exploration (breadth-first, all links at depth first) | |
| - Random walk exploration (stochastic, edge case discovery) | |
| - Limit enforcement (max_depth, max_actions) | |
| - Bug detection (console errors, broken images) | |
| - Visited URL tracking (infinite loop prevention) | |
| - Reproducibility (random seed produces same results) | |
| """ | |
| import pytest | |
| from playwright.sync_api import Page | |
| from tests.browser_discovery.conftest import ( | |
| authenticated_page, | |
| exploration_agent, | |
| console_monitor, | |
| ) | |
| # ============================================================================ | |
| # DEPTH-FIRST SEARCH (DFS) EXPLORATION TESTS | |
| # ============================================================================ | |
| def test_dfs_explores_dashboard_depth_first(authenticated_page: Page, exploration_agent): | |
| """Test DFS explores dashboard using depth-first navigation. | |
| BROWSER-01: DFS should navigate deep into UI paths first before | |
| exploring siblings (e.g., dashboard → agent → execute → results). | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with DFS algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using DFS (max_depth=3, max_actions=20) | |
| bugs = exploration_agent.explore_dfs(max_depth=3, max_actions=20) | |
| # Get exploration report | |
| report = exploration_agent.get_exploration_report() | |
| # Verify exploration occurred | |
| assert report["actions_taken"] > 0, "DFS should take at least 1 action" | |
| assert report["urls_visited"] > 0, "DFS should visit at least 1 URL" | |
| # Verify no critical bugs found | |
| critical_bugs = [bug for bug in bugs if bug.get("type") == "console_error"] | |
| assert len(critical_bugs) == 0, f"DFS found {len(critical_bugs)} console errors" | |
| def test_dfs_respects_max_depth_limit(authenticated_page: Page, exploration_agent): | |
| """Test DFS respects max_depth limit to prevent infinite exploration. | |
| BROWSER-01: DFS should stop at max_depth to prevent infinite loops | |
| in cyclic navigation structures. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with DFS algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using DFS with max_depth=2 (shallow exploration) | |
| bugs = exploration_agent.explore_dfs(max_depth=2, max_actions=20) | |
| # Get exploration report | |
| report = exploration_agent.get_exploration_report() | |
| # Verify exploration completed without exceeding depth | |
| assert report["actions_taken"] <= 20, "DFS should respect max_actions limit" | |
| assert len(bugs) < 100, "DFS should not generate excessive bugs" | |
| # Verify no infinite loop errors | |
| infinite_loop_bugs = [bug for bug in bugs if "loop" in str(bug).lower()] | |
| assert len(infinite_loop_bugs) == 0, "DFS should track visited URLs to prevent infinite loops" | |
| def test_dfs_respects_max_actions_limit(authenticated_page: Page, exploration_agent): | |
| """Test DFS respects max_actions limit to prevent excessive clicking. | |
| BROWSER-01: DFS should stop after max_actions to prevent long-running | |
| test executions. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with DFS algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using DFS with max_actions=10 (limited actions) | |
| bugs = exploration_agent.explore_dfs(max_depth=3, max_actions=10) | |
| # Get exploration report | |
| report = exploration_agent.get_exploration_report() | |
| # Verify DFS stopped at max_actions | |
| assert report["actions_taken"] <= 10, "DFS should respect max_actions=10 limit" | |
| assert report["urls_visited"] >= 1, "DFS should visit at least starting URL" | |
| def test_dfs_detects_console_errors(authenticated_page: Page, exploration_agent, console_monitor): | |
| """Test DFS captures console errors during exploration. | |
| BROWSER-02: DFS should detect JavaScript console errors during | |
| navigation and report them as bugs. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with DFS algorithm | |
| console_monitor: Console monitor fixture for error detection | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using DFS | |
| bugs = exploration_agent.explore_dfs(max_depth=2, max_actions=15) | |
| # Check console monitor for errors | |
| console_errors = console_monitor.get("error", []) | |
| # Verify console errors were captured | |
| # (Note: Dashboard should have no console errors in healthy state) | |
| if console_errors: | |
| # If console errors exist, DFS should have detected them | |
| console_bugs = [bug for bug in bugs if bug.get("type") == "console_error"] | |
| assert len(console_bugs) > 0, "DFS should capture console errors" | |
| # ============================================================================ | |
| # BREADTH-FIRST SEARCH (BFS) EXPLORATION TESTS | |
| # ============================================================================ | |
| def test_bfs_explores_all_links_breadth_first(authenticated_page: Page, exploration_agent): | |
| """Test BFS explores dashboard using breadth-first navigation. | |
| BROWSER-01: BFS should explore all links at current depth before | |
| going deeper (e.g., explore all dashboard sections before diving | |
| into each). | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with BFS algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using BFS (max_depth=3, max_actions=20) | |
| bugs = exploration_agent.explore_bfs(max_depth=3, max_actions=20) | |
| # Get exploration report | |
| report = exploration_agent.get_exploration_report() | |
| # Verify exploration occurred | |
| assert report["actions_taken"] > 0, "BFS should take at least 1 action" | |
| assert report["urls_visited"] > 0, "BFS should visit at least 1 URL" | |
| # Verify no critical bugs found | |
| critical_bugs = [bug for bug in bugs if bug.get("type") == "console_error"] | |
| assert len(critical_bugs) == 0, f"BFS found {len(critical_bugs)} console errors" | |
| def test_bfs_covers_more_urls_than_dfs(authenticated_page: Page, exploration_agent): | |
| """Test BFS covers more URLs than DFS for same action limit. | |
| BROWSER-01: BFS explores all links at each depth before going deeper, | |
| which should result in broader URL coverage than DFS for the same | |
| number of actions. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with BFS/DFS algorithms | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using BFS | |
| bfs_bugs = exploration_agent.explore_bfs(max_depth=3, max_actions=15) | |
| bfs_report = exploration_agent.get_exploration_report() | |
| bfs_urls = bfs_report["urls_visited"] | |
| # Navigate back to dashboard for DFS test | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using DFS | |
| dfs_bugs = exploration_agent.explore_dfs(max_depth=3, max_actions=15) | |
| dfs_report = exploration_agent.get_exploration_report() | |
| dfs_urls = dfs_report["urls_visited"] | |
| # BFS should cover equal or more URLs than DFS (broader coverage) | |
| assert bfs_urls >= dfs_urls, f"BFS should cover >= URLs than DFS (BFS: {bfs_urls}, DFS: {dfs_urls})" | |
| def test_bfs_respects_depth_limits(authenticated_page: Page, exploration_agent): | |
| """Test BFS respects max_depth limit to prevent infinite exploration. | |
| BROWSER-01: BFS should stop at max_depth to prevent infinite loops | |
| in cyclic navigation structures. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with BFS algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using BFS with max_depth=2 (shallow exploration) | |
| bugs = exploration_agent.explore_bfs(max_depth=2, max_actions=20) | |
| # Get exploration report | |
| report = exploration_agent.get_exploration_report() | |
| # Verify exploration completed without exceeding depth | |
| assert report["actions_taken"] <= 20, "BFS should respect max_actions limit" | |
| assert len(bugs) < 100, "BFS should not generate excessive bugs" | |
| # Verify no infinite loop errors | |
| infinite_loop_bugs = [bug for bug in bugs if "loop" in str(bug).lower()] | |
| assert len(infinite_loop_bugs) == 0, "BFS should track visited URLs to prevent infinite loops" | |
| def test_bfs_handles_navigation_back(authenticated_page: Page, exploration_agent): | |
| """Test BFS can navigate back correctly after exploration. | |
| BROWSER-01: BFS should navigate back to previous pages correctly | |
| to continue breadth-first exploration. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with BFS algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using BFS | |
| bugs = exploration_agent.explore_bfs(max_depth=2, max_actions=10) | |
| # Get exploration report | |
| report = exploration_agent.get_exploration_report() | |
| # Verify exploration completed without navigation errors | |
| nav_errors = [bug for bug in bugs if "navigation" in str(bug).lower() or "go_back" in str(bug).lower()] | |
| assert len(nav_errors) == 0, "BFS should handle navigation back without errors" | |
| # Verify BFS visited multiple URLs | |
| assert report["urls_visited"] >= 1, "BFS should visit at least starting URL" | |
| # ============================================================================ | |
| # RANDOM WALK EXPLORATION TESTS | |
| # ============================================================================ | |
| def test_random_walk_explores_stochastically(authenticated_page: Page, exploration_agent): | |
| """Test random walk explores UI stochastically for edge cases. | |
| BROWSER-01: Random walk should explore stochastically to discover | |
| unexpected state combinations and edge cases that systematic | |
| algorithms (DFS/BFS) miss. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with random walk algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using random walk (max_actions=30) | |
| bugs = exploration_agent.explore_random(max_actions=30) | |
| # Get exploration report | |
| report = exploration_agent.get_exploration_report() | |
| # Verify exploration occurred | |
| assert report["actions_taken"] > 0, "Random walk should take at least 1 action" | |
| assert report["urls_visited"] > 0, "Random walk should visit at least 1 URL" | |
| # Verify no critical bugs found | |
| critical_bugs = [bug for bug in bugs if bug.get("type") == "console_error"] | |
| assert len(critical_bugs) == 0, f"Random walk found {len(critical_bugs)} console errors" | |
| def test_random_walk_with_seed_is_reproducible(authenticated_page: Page, exploration_agent): | |
| """Test random walk with seed produces reproducible exploration paths. | |
| BROWSER-01: Random walk with seed should produce the same exploration | |
| path when run multiple times, enabling reproducible bug discovery. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with random walk algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using random walk with seed=42 | |
| bugs_1 = exploration_agent.explore_random(max_actions=20, seed=42) | |
| report_1 = exploration_agent.get_exploration_report() | |
| # Navigate back to dashboard for second run | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using random walk with same seed | |
| bugs_2 = exploration_agent.explore_random(max_actions=20, seed=42) | |
| report_2 = exploration_agent.get_exploration_report() | |
| # Verify reproducibility (same number of actions and URLs) | |
| assert report_1["actions_taken"] == report_2["actions_taken"], \ | |
| "Random walk with seed should produce same number of actions" | |
| assert report_1["urls_visited"] == report_2["urls_visited"], \ | |
| "Random walk with seed should visit same number of URLs" | |
| def test_random_walk_handles_infinite_loops(authenticated_page: Page, exploration_agent): | |
| """Test random walk handles infinite loops through visited URL tracking. | |
| BROWSER-01: Random walk should track visited URLs to prevent infinite | |
| loops in cyclic navigation structures. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with random walk algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using random walk (max_actions=30) | |
| bugs = exploration_agent.explore_random(max_actions=30) | |
| # Get exploration report | |
| report = exploration_agent.get_exploration_report() | |
| # Verify exploration completed without infinite loops | |
| assert report["actions_taken"] <= 30, "Random walk should respect max_actions limit" | |
| assert len(bugs) < 100, "Random walk should not generate excessive bugs" | |
| # Verify no infinite loop errors | |
| infinite_loop_bugs = [bug for bug in bugs if "loop" in str(bug).lower()] | |
| assert len(infinite_loop_bugs) == 0, "Random walk should track visited URLs to prevent infinite loops" | |
| def test_random_walk_discovers_edge_cases(authenticated_page: Page, exploration_agent): | |
| """Test random walk may discover bugs DFS/BFS miss. | |
| BROWSER-01: Random walk's stochastic exploration should discover | |
| edge cases and unexpected state combinations that systematic | |
| algorithms miss. | |
| Args: | |
| authenticated_page: Authenticated Playwright page fixture | |
| exploration_agent: ExplorationAgent fixture with random walk algorithm | |
| """ | |
| # Navigate to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using DFS first | |
| dfs_bugs = exploration_agent.explore_dfs(max_depth=2, max_actions=15) | |
| dfs_report = exploration_agent.get_exploration_report() | |
| # Navigate back to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using BFS | |
| bfs_bugs = exploration_agent.explore_bfs(max_depth=2, max_actions=15) | |
| bfs_report = exploration_agent.get_exploration_report() | |
| # Navigate back to dashboard | |
| authenticated_page.goto("http://localhost:3001/dashboard") | |
| authenticated_page.wait_for_load_state("domcontentloaded") | |
| # Explore using random walk | |
| random_bugs = exploration_agent.explore_random(max_actions=30) | |
| random_report = exploration_agent.get_exploration_report() | |
| # Random walk should take equal or more actions than DFS/BFS | |
| # (May revisit URLs due to stochastic nature) | |
| assert random_report["actions_taken"] >= 0, "Random walk should take actions" | |
| # All algorithms should find same critical bugs (console errors) | |
| dfs_console_bugs = len([b for b in dfs_bugs if b.get("type") == "console_error"]) | |
| bfs_console_bugs = len([b for b in bfs_bugs if b.get("type") == "console_error"]) | |
| random_console_bugs = len([b for b in random_bugs if b.get("type") == "console_error"]) | |
| # Console errors should be detected by all algorithms | |
| assert dfs_console_bugs == bfs_console_bugs == random_console_bugs, \ | |
| "All algorithms should detect same console errors" | |