annator-command-center / tests /browser_discovery /test_exploration_agent.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 7)
cc036ff verified
Raw
History Blame Contribute Delete
17.5 kB
"""
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
# ============================================================================
@pytest.mark.browser_discovery
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"
@pytest.mark.browser_discovery
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"
@pytest.mark.browser_discovery
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"
@pytest.mark.browser_discovery
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
# ============================================================================
@pytest.mark.browser_discovery
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"
@pytest.mark.browser_discovery
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})"
@pytest.mark.browser_discovery
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"
@pytest.mark.browser_discovery
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
# ============================================================================
@pytest.mark.browser_discovery
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"
@pytest.mark.browser_discovery
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"
@pytest.mark.browser_discovery
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"
@pytest.mark.browser_discovery
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"