""" Tests for browser session file upload functionality using real HTML. Tests cover common real-world file upload patterns: - Standard form file uploads - Hidden file inputs triggered by buttons - Multiple file upload fields - Complex nested structures """ import pytest from pytest_httpserver import HTTPServer from browser_use.browser.profile import BrowserProfile from browser_use.browser.session import BrowserSession class TestBrowserSessionFileUploads: """Tests for file upload element finding functionality with real HTML.""" @pytest.fixture async def browser_session(self): """Create a BrowserSession instance for testing.""" session = BrowserSession(browser_profile=BrowserProfile(headless=True, user_data_dir=None, keep_alive=True)) yield session await session.kill() @pytest.fixture def test_server(self, httpserver: HTTPServer): """Set up test HTTP server with various file upload scenarios.""" return httpserver async def test_standard_file_upload_patterns(self, browser_session: BrowserSession, test_server: HTTPServer): """Test finding file inputs in standard form layouts and modern UI patterns.""" html = """

File Upload Test Page

No file selected

Drop files here or click to upload

""" test_server.expect_request('/upload').respond_with_data(html, content_type='text/html') await browser_session.start() page = await browser_session.get_current_page() await page.goto(test_server.url_for('/upload')) # Wait for page to load await page.wait_for_load_state('networkidle') # Get browser state to populate selector map await browser_session.get_state_summary(cache_clickable_elements_hashes=False) # Get the selector map after page load selector_map = await browser_session.get_selector_map() # The selector map contains clickable elements, so let's test by looking for elements directly # Test 1: Find file input when it's the clicked element itself all_file_inputs = [] for idx, elem in selector_map.items(): if elem.tag_name == 'input' and elem.attributes.get('type') == 'file': all_file_inputs.append((idx, elem)) # Should find at least one file input in the map assert len(all_file_inputs) > 0, f'No file inputs found in selector map. Map has {len(selector_map)} elements' # Test finding the first file input first_file_idx, first_file_elem = all_file_inputs[0] file_input = await browser_session.find_file_upload_element_by_index(first_file_idx) assert file_input is not None assert file_input == first_file_elem # Test 2: Find hidden file input from button - look for any button button_indices = [] for idx, elem in selector_map.items(): if elem.tag_name == 'button': button_indices.append(idx) # Try finding file input from each button found_from_button = False for button_idx in button_indices: file_input = await browser_session.find_file_upload_element_by_index(button_idx) if file_input is not None: found_from_button = True break assert found_from_button, 'Could not find any file input from any button' # Test 3: Find file input from parent containers div_indices = [] for idx, elem in selector_map.items(): if elem.tag_name == 'div': div_indices.append(idx) # Try finding file input from divs found_from_div = False for div_idx in div_indices[:10]: # Test first 10 divs file_input = await browser_session.find_file_upload_element_by_index(div_idx) if file_input is not None: found_from_div = True break assert found_from_div, 'Could not find any file input from any div container' async def test_dom_traversal_functionality(self, browser_session: BrowserSession, test_server: HTTPServer): """Test that the file upload finder correctly traverses the DOM.""" html = """
""" test_server.expect_request('/traversal').respond_with_data(html, content_type='text/html') await browser_session.start() page = await browser_session.get_current_page() await page.goto(test_server.url_for('/traversal')) await page.wait_for_load_state('networkidle') # Get browser state to populate selector map await browser_session.get_state_summary(cache_clickable_elements_hashes=False) selector_map = await browser_session.get_selector_map() # Test that buttons can find nearby file inputs button_count = 0 buttons_that_found_file = 0 for idx, elem in selector_map.items(): if elem.tag_name == 'button': button_count += 1 file_input = await browser_session.find_file_upload_element_by_index(idx) if file_input is not None: buttons_that_found_file += 1 # Verify it's actually a file input assert file_input.tag_name == 'input' assert file_input.attributes.get('type', '').lower() == 'file' # Most buttons should be able to find a file input through DOM traversal assert button_count > 0, 'No buttons found in selector map' assert buttons_that_found_file > 0, 'No buttons could find file inputs' assert buttons_that_found_file >= button_count - 1, 'Most buttons should find file inputs' async def test_traversal_limits(self, browser_session: BrowserSession, test_server: HTTPServer): """Test that traversal limits (max_height and max_descendant_depth) work correctly.""" html = """
""" test_server.expect_request('/limits').respond_with_data(html, content_type='text/html') await browser_session.start() page = await browser_session.get_current_page() await page.goto(test_server.url_for('/limits')) await page.wait_for_load_state('networkidle') # Get browser state to populate selector map await browser_session.get_state_summary(cache_clickable_elements_hashes=False) selector_map = await browser_session.get_selector_map() # Test 1: Button with deep nesting test_button_idx = None for idx, elem in selector_map.items(): if elem.tag_name == 'button' and elem.attributes.get('class') == 'test-button': test_button_idx = idx break if test_button_idx is not None: # With default limits, should find the deeply nested file file_input = await browser_session.find_file_upload_element_by_index(test_button_idx) assert file_input is not None # With very limited traversal, might not find it file_input_limited = await browser_session.find_file_upload_element_by_index( test_button_idx, max_height=0, max_descendant_depth=1 ) # Could be None if file is too deep # Test 2: Direct sibling should be found easily direct_button_idx = None for idx, elem in selector_map.items(): if elem.tag_name == 'button' and elem.attributes.get('class') == 'direct-button': direct_button_idx = idx break if direct_button_idx is not None: file_input = await browser_session.find_file_upload_element_by_index(direct_button_idx, max_height=1) assert file_input is not None assert file_input.attributes.get('id') == 'direct-file' # Test 3: Element that is itself a file input self_file_idx = None for idx, elem in selector_map.items(): if elem.tag_name == 'input' and elem.attributes.get('id') == 'self-file': self_file_idx = idx break if self_file_idx is not None: file_input = await browser_session.find_file_upload_element_by_index(self_file_idx) assert file_input is not None assert file_input.attributes.get('id') == 'self-file' # Test 4: Invalid index returns None invalid_index = max(selector_map.keys()) + 100 if selector_map else 999 file_input = await browser_session.find_file_upload_element_by_index(invalid_index) assert file_input is None