diff --git a/openhands/io/json.py b/openhands/io/json.py index b781aabb87..0a9df78d8d 100644 --- a/openhands/io/json.py +++ b/openhands/io/json.py @@ -1,5 +1,6 @@ import json from datetime import datetime +from typing import Any from json_repair import repair_json from litellm.types.utils import ModelResponse @@ -32,7 +33,7 @@ class OpenHandsJSONEncoder(json.JSONEncoder): _json_encoder = OpenHandsJSONEncoder() -def dumps(obj, **kwargs): +def dumps(obj, **kwargs) -> str: """Serialize an object to str format""" if not kwargs: return _json_encoder.encode(obj) @@ -47,7 +48,7 @@ def dumps(obj, **kwargs): return json.dumps(obj, **encoder_kwargs) -def loads(json_str, **kwargs): +def loads(json_str: str, **kwargs) -> Any: """Create a JSON object from str""" try: return json.loads(json_str, **kwargs) diff --git a/tests/e2e/test_settings.py b/tests/e2e/test_settings.py index 7c9386135a..81c989a96f 100644 --- a/tests/e2e/test_settings.py +++ b/tests/e2e/test_settings.py @@ -6,10 +6,13 @@ then ensures the GitHub token is set in Settings → Integrations and that the home screen shows the repository selector. """ +import logging import os from playwright.sync_api import Page, expect +logger = logging.getLogger(__name__) + def test_github_token_configuration(page: Page, base_url: str): """ @@ -28,51 +31,51 @@ def test_github_token_configuration(page: Page, base_url: str): base_url = 'http://localhost:12000' # Navigate to the OpenHands application - print(f'Step 1: Navigating to OpenHands application at {base_url}...') + logger.info(f'Step 1: Navigating to OpenHands application at {base_url}...') page.goto(base_url) page.wait_for_load_state('networkidle', timeout=30000) # Take initial screenshot page.screenshot(path='test-results/token_01_initial_load.png') - print('Screenshot saved: token_01_initial_load.png') + logger.info('Screenshot saved: token_01_initial_load.png') # Step 1.5: Handle any initial modals that might appear (LLM API key configuration) try: # Check for AI Provider Configuration modal config_modal = page.locator('text=AI Provider Configuration') if config_modal.is_visible(timeout=5000): - print('AI Provider Configuration modal detected') + logger.info('AI Provider Configuration modal detected') # Fill in the LLM API key if available llm_api_key_input = page.locator('[data-testid="llm-api-key-input"]') if llm_api_key_input.is_visible(timeout=3000): llm_api_key = os.getenv('LLM_API_KEY', 'test-key') llm_api_key_input.fill(llm_api_key) - print(f'Filled LLM API key (length: {len(llm_api_key)})') + logger.info(f'Filled LLM API key (length: {len(llm_api_key)})') # Click the Save button save_button = page.locator('button:has-text("Save")') if save_button.is_visible(timeout=3000): save_button.click() page.wait_for_timeout(2000) - print('Saved LLM API key configuration') + logger.info('Saved LLM API key configuration') # Check for Privacy Preferences modal privacy_modal = page.locator('text=Your Privacy Preferences') if privacy_modal.is_visible(timeout=5000): - print('Privacy Preferences modal detected') + logger.info('Privacy Preferences modal detected') confirm_button = page.locator('button:has-text("Confirm Preferences")') if confirm_button.is_visible(timeout=3000): confirm_button.click() page.wait_for_timeout(2000) - print('Confirmed privacy preferences') + logger.info('Confirmed privacy preferences') except Exception as e: - print(f'Error handling initial modals: {e}') + logger.error(f'Error handling initial modals: {e}') page.screenshot(path='test-results/token_01_5_modal_error.png') - print('Screenshot saved: token_01_5_modal_error.png') + logger.info('Screenshot saved: token_01_5_modal_error.png') # Step 2: Check if GitHub token is already configured or needs to be set - print('Step 2: Checking if GitHub token is configured...') + logger.info('Step 2: Checking if GitHub token is configured...') try: # First, check if we're already on the home screen with repository selection @@ -80,7 +83,7 @@ def test_github_token_configuration(page: Page, base_url: str): connect_to_provider = page.locator('text=Connect to a Repository') if connect_to_provider.is_visible(timeout=3000): - print('Found "Connect to a Repository" section') + logger.info('Found "Connect to a Repository" section') # Check if we need to configure a provider (GitHub token) navigate_to_settings_button = page.locator( @@ -88,7 +91,9 @@ def test_github_token_configuration(page: Page, base_url: str): ) if navigate_to_settings_button.is_visible(timeout=3000): - print('GitHub token not configured. Need to navigate to settings...') + logger.info( + 'GitHub token not configured. Need to navigate to settings...' + ) # Click the Settings button to navigate to the settings page navigate_to_settings_button.click() @@ -96,19 +101,21 @@ def test_github_token_configuration(page: Page, base_url: str): page.wait_for_timeout(3000) # Wait for navigation to complete # We should now be on the /settings/integrations page - print('Navigated to settings page, looking for GitHub token input...') + logger.info( + 'Navigated to settings page, looking for GitHub token input...' + ) # Check if we're on the settings page with the integrations tab settings_screen = page.locator('[data-testid="settings-screen"]') if settings_screen.is_visible(timeout=5000): - print('Settings screen is visible') + logger.info('Settings screen is visible') # Make sure we're on the Integrations tab integrations_tab = page.locator('text=Integrations') if integrations_tab.is_visible(timeout=3000): # Check if we need to click the tab if not page.url.endswith('/settings/integrations'): - print('Clicking Integrations tab...') + logger.info('Clicking Integrations tab...') integrations_tab.click() page.wait_for_load_state('networkidle') page.wait_for_timeout(2000) @@ -118,7 +125,7 @@ def test_github_token_configuration(page: Page, base_url: str): '[data-testid="github-token-input"]' ) if github_token_input.is_visible(timeout=5000): - print('Found GitHub token input field') + logger.info('Found GitHub token input field') # Fill in the GitHub token from environment variable github_token = os.getenv('GITHUB_TOKEN', '') @@ -126,18 +133,18 @@ def test_github_token_configuration(page: Page, base_url: str): # Clear the field first, then fill it github_token_input.clear() github_token_input.fill(github_token) - print( + logger.info( f'Filled GitHub token from environment variable (length: {len(github_token)})' ) # Verify the token was filled filled_value = github_token_input.input_value() if filled_value: - print( + logger.info( f'Token field now contains value of length: {len(filled_value)}' ) else: - print( + logger.warning( 'WARNING: Token field appears to be empty after filling' ) @@ -146,12 +153,12 @@ def test_github_token_configuration(page: Page, base_url: str): if save_button.is_visible(timeout=3000): # Check if button is enabled is_disabled = save_button.is_disabled() - print( + logger.info( f'Save Changes button found, disabled: {is_disabled}' ) if not is_disabled: - print('Clicking Save Changes button...') + logger.info('Clicking Save Changes button...') save_button.click() # Wait for the save operation to complete @@ -164,46 +171,52 @@ def test_github_token_configuration(page: Page, base_url: str): 'document.querySelector(\'[data-testid="submit-button"]\').disabled === true', timeout=10000, ) - print( + logger.info( 'Save operation completed - form is now clean' ) except Exception: - print( + logger.warning( 'Save operation completed (timeout waiting for form clean state)' ) # Navigate back to home page after successful save - print('Navigating back to home page...') + logger.info('Navigating back to home page...') page.goto(base_url) page.wait_for_load_state('networkidle') page.wait_for_timeout( 5000 ) # Wait longer for providers to be updated else: - print( + logger.warning( 'Save Changes button is disabled - form may be invalid' ) else: - print('Save Changes button not found') + logger.warning('Save Changes button not found') else: - print('No GitHub token found in environment variables') + logger.warning( + 'No GitHub token found in environment variables' + ) else: - print('GitHub token input field not found on settings page') + logger.warning( + 'GitHub token input field not found on settings page' + ) # Take a screenshot to see what's on the page page.screenshot(path='test-results/token_02_settings_debug.png') - print('Debug screenshot saved: token_02_settings_debug.png') + logger.info( + 'Debug screenshot saved: token_02_settings_debug.png' + ) else: - print('Settings screen not found') + logger.warning('Settings screen not found') else: # Branch 2: GitHub token is already configured, repository selection is available - print( + logger.info( 'GitHub token is already configured, repository selection is available' ) # Check if we need to update the token by going to settings manually settings_button = page.locator('button:has-text("Settings")') if settings_button.is_visible(timeout=3000): - print( + logger.info( 'Settings button found, clicking to navigate to settings page...' ) settings_button.click() @@ -213,7 +226,7 @@ def test_github_token_configuration(page: Page, base_url: str): # Navigate to the Integrations tab integrations_tab = page.locator('text=Integrations') if integrations_tab.is_visible(timeout=3000): - print('Clicking Integrations tab...') + logger.info('Clicking Integrations tab...') integrations_tab.click() page.wait_for_load_state('networkidle') page.wait_for_timeout(2000) @@ -223,7 +236,7 @@ def test_github_token_configuration(page: Page, base_url: str): '[data-testid="github-token-input"]' ) if github_token_input.is_visible(timeout=5000): - print('Found GitHub token input field') + logger.info('Found GitHub token input field') # Fill in the GitHub token from environment variable github_token = os.getenv('GITHUB_TOKEN', '') @@ -231,7 +244,7 @@ def test_github_token_configuration(page: Page, base_url: str): # Clear the field first, then fill it github_token_input.clear() github_token_input.fill(github_token) - print( + logger.info( f'Filled GitHub token from environment variable (length: {len(github_token)})' ) @@ -243,52 +256,56 @@ def test_github_token_configuration(page: Page, base_url: str): save_button.is_visible(timeout=3000) and not save_button.is_disabled() ): - print('Clicking Save Changes button...') + logger.info('Clicking Save Changes button...') save_button.click() page.wait_for_timeout(3000) # Navigate back to home page - print('Navigating back to home page...') + logger.info('Navigating back to home page...') page.goto(base_url) page.wait_for_load_state('networkidle') page.wait_for_timeout(3000) else: - print( + logger.warning( 'GitHub token input field not found, going back to home page' ) page.goto(base_url) page.wait_for_load_state('networkidle') else: - print('Integrations tab not found, going back to home page') + logger.warning( + 'Integrations tab not found, going back to home page' + ) page.goto(base_url) page.wait_for_load_state('networkidle') else: - print('Settings button not found, continuing with existing token') + logger.info( + 'Settings button not found, continuing with existing token' + ) else: - print('Could not find "Connect to a Repository" section') + logger.warning('Could not find "Connect to a Repository" section') page.screenshot(path='test-results/token_03_after_settings.png') - print('Screenshot saved: token_03_after_settings.png') + logger.info('Screenshot saved: token_03_after_settings.png') except Exception as e: - print(f'Error checking GitHub token configuration: {e}') + logger.error(f'Error checking GitHub token configuration: {e}') page.screenshot(path='test-results/token_04_error.png') - print('Screenshot saved: token_04_error.png') + logger.info('Screenshot saved: token_04_error.png') # Step 3: Verify we're back on the home screen with repository selection available - print('Step 3: Verifying repository selection is available...') + logger.info('Step 3: Verifying repository selection is available...') # Wait for the home screen to load home_screen = page.locator('[data-testid="home-screen"]') expect(home_screen).to_be_visible(timeout=15000) - print('Home screen is visible') + logger.info('Home screen is visible') # Look for the repository dropdown/selector repo_dropdown = page.locator('[data-testid="repo-dropdown"]') expect(repo_dropdown).to_be_visible(timeout=15000) - print('Repository dropdown is visible') + logger.info('Repository dropdown is visible') # Success - we've verified the GitHub token configuration - print('GitHub token configuration verified successfully') + logger.info('GitHub token configuration verified successfully') page.screenshot(path='test-results/token_05_success.png') - print('Screenshot saved: token_05_success.png') + logger.info('Screenshot saved: token_05_success.png')