#!/usr/bin/env expect
# Exit codes:
# 0 - Test passed
# 1 - Test failed
# Set timeout for command execution
set timeout 30
# Colors for output
set GREEN "\033\[0;32m"
set RED "\033\[0;31m"
set YELLOW "\033\[1;33m"
set BLUE "\033\[0;34m"
set NC "\033\[0m"
# Get the gbox binary path
# Default to using the built binary in the parent directory
set gbox_binary "../gbox"
# If the built binary doesn't exist, fall back to system gbox
if {![file exists $gbox_binary]} {
set gbox_binary "gbox"
}
puts "${BLUE}Testing gbox login command...${NC}"
# Function to check and install Python dependencies
proc check_python_deps {} {
global GREEN RED YELLOW BLUE NC venv_path venv_python
puts "${YELLOW}Checking Python dependencies...${NC}"
# Check if python3 is available, try both python3 and python
set python_cmd ""
if {![catch {exec python3 --version} result]} {
set python_cmd "python3"
puts "${GREEN}✓ Python3 found: $result${NC}"
} elseif {![catch {exec python --version} result]} {
set python_cmd "python"
puts "${GREEN}✓ Python found: $result${NC}"
} else {
puts "${RED}✗ Python not found. Please install Python3.${NC}"
return 0
}
# Get current working directory and construct venv path relative to it
set current_dir [pwd]
puts "${BLUE}Current working directory: $current_dir${NC}"
# Check if we're in CI environment (GitHub Actions)
set ci_env [expr {[info exists env::CI] && $env::CI == "true"}]
if {$ci_env} {
puts "${BLUE}Detected CI environment, using system Python${NC}"
set venv_path ""
set venv_python $python_cmd
} else {
# Check if we're in the gbox project root or a subdirectory
if {[string match "*gbox*" $current_dir]} {
# Find the gbox project root by looking for go.mod or other indicators
set venv_path "$current_dir/venv"
# If we're in a subdirectory, try to find the project root
if {![file exists "$current_dir/go.mod"] && ![file exists "$current_dir/README.md"]} {
# We might be in a subdirectory, try parent directories
set parent_dir [file dirname $current_dir]
if {[file exists "$parent_dir/go.mod"] || [file exists "$parent_dir/README.md"]} {
set venv_path "$parent_dir/venv"
puts "${BLUE}Found project root at: $parent_dir${NC}"
}
}
} else {
# Fallback to current directory
set venv_path "$current_dir/venv"
}
puts "${BLUE}Using virtual environment path: $venv_path${NC}"
if {![file exists $venv_path]} {
puts "${YELLOW}Virtual environment not found, creating one...${NC}"
if {[catch {exec $python_cmd -m venv $venv_path} result]} {
puts "${RED}✗ Failed to create virtual environment: $result${NC}"
return 0
}
puts "${GREEN}✓ Virtual environment created${NC}"
} else {
puts "${GREEN}✓ Virtual environment already exists${NC}"
}
set venv_python "$venv_path/bin/python"
}
# Check if playwright is installed
if {[catch {exec $venv_python -c "import playwright; print('playwright installed')"} result]} {
if {$ci_env} {
puts "${YELLOW}Playwright not found in CI environment, this should have been installed by CI setup${NC}"
puts "${RED}✗ Playwright not available in CI environment${NC}"
return 0
} else {
puts "${YELLOW}Playwright not found in virtual environment, installing...${NC}"
# Install playwright in virtual environment
if {[catch {exec $venv_python -m pip install playwright==1.55.0} result]} {
puts "${RED}✗ Failed to install playwright: $result${NC}"
return 0
}
puts "${GREEN}✓ Playwright installed successfully${NC}"
# Install chromium browser
if {[catch {exec $venv_python -m playwright install chromium} result]} {
puts "${RED}✗ Failed to install chromium browser: $result${NC}"
return 0
}
puts "${GREEN}✓ Chromium browser installed successfully${NC}"
# Install system dependencies for CI
if {[catch {exec $venv_python -m playwright install-deps chromium} result]} {
puts "${YELLOW}Warning: Failed to install system dependencies (this may be expected in some environments)${NC}"
} else {
puts "${GREEN}✓ System dependencies installed successfully${NC}"
}
}
} else {
puts "${GREEN}✓ Playwright already installed${NC}"
}
return 1
}
# Test 1: Help command for login
puts "\n${YELLOW}Testing help command for login...${NC}"
puts "${BLUE}Running Test 1: Help command for login${NC}"
if {[catch {
spawn $gbox_binary login --help
expect {
-re "Authenticate with gbox cloud service" {
puts "${GREEN}✓ Found help description${NC}"
exp_continue
}
-re "Examples:" {
puts "${GREEN}✓ Found examples section${NC}"
exp_continue
}
-re "gbox login" {
puts "${GREEN}✓ Found login command example${NC}"
exp_continue
}
timeout {
puts "${RED}✗ Timeout waiting for help output${NC}"
exit 1
}
eof {
puts "${GREEN}✓ Help command completed successfully${NC}"
}
}
catch {close}
} result]} {
puts "${RED}✗ Test failed: $result${NC}"
exit 1
}
# Test 2: Login command - main flow test
puts "\n${YELLOW}Testing login command main flow...${NC}"
puts "${BLUE}Running Test 2: Login command main flow${NC}"
# Set up variables for the login flow
set login_url ""
set device_code ""
puts "${BLUE}=== Test 2 Phase 1: Starting gbox login command ===${NC}"
if {[catch {
spawn $gbox_binary login
expect {
-re "Device code: (\[A-Z0-9\]{4}-\[A-Z0-9\]{4})" {
set device_code $expect_out(1,string)
puts "${GREEN}✓ Extracted device code: $device_code${NC}"
puts "${BLUE}=== Test 2 Phase 2: Device code extracted ===${NC}"
exp_continue
}
-re "Device code: (\[^\\r\\n\]+)" {
set device_code $expect_out(1,string)
puts "${GREEN}✓ Extracted device code (generic): $device_code${NC}"
puts "${BLUE}=== Test 2 Phase 2: Device code extracted (generic) ===${NC}"
exp_continue
}
-re "To authenticate, please visit:" {
puts "${GREEN}✓ Found URL prompt${NC}"
puts "${BLUE}=== Test 2 Phase 3: URL prompt detected ===${NC}"
exp_continue
}
-re "(https://\[^\\r\\n\]+)" {
set login_url $expect_out(1,string)
puts "${GREEN}✓ Extracted login URL: $login_url${NC}"
puts "${BLUE}=== Test 2 Phase 4: Login URL extracted ===${NC}"
# If we have both URL and device code, run playwright automation
if {$login_url != "" && $device_code != ""} {
puts "${GREEN}✓ Login flow initiated successfully (URL and device code extracted)${NC}"
puts "${BLUE}=== Test 2 Phase 5: Starting Playwright automation ===${NC}"
puts "${YELLOW}Starting automated login with Playwright...${NC}"
# Run playwright automation in background without interrupting the login process
puts "${BLUE}=== Test 2 Phase 6: Checking Python dependencies ===${NC}"
if {![check_python_deps]} {
puts "${RED}✗ Failed to setup Python dependencies${NC}"
exit 1
}
puts "${GREEN}✓ Python dependencies check completed${NC}"
# Create temporary Python script for Playwright automation
puts "${BLUE}=== Test 2 Phase 7: Creating Playwright automation script ===${NC}"
set temp_script "/tmp/test_login_playwright.py"
set fp [open $temp_script w]
puts $fp "#!/usr/bin/env python3"
puts $fp "import asyncio"
puts $fp "import sys"
puts $fp "from playwright.async_api import async_playwright"
puts $fp ""
puts $fp "async def test_login_flow():"
puts $fp " async with async_playwright() as p:"
puts $fp " browser = await p.chromium.launch("
puts $fp " headless=True,"
puts $fp " args=\["
puts $fp " '--no-sandbox',"
puts $fp " '--disable-setuid-sandbox',"
puts $fp " '--disable-dev-shm-usage',"
puts $fp " '--disable-accelerated-2d-canvas',"
puts $fp " '--no-first-run',"
puts $fp " '--no-zygote',"
puts $fp " '--disable-gpu'"
puts $fp " \]"
puts $fp " )"
puts $fp " page = await browser.new_page()"
puts $fp " "
puts $fp " try:"
puts $fp " print(f'Navigating to login URL: $login_url')"
puts $fp " await page.goto('$login_url', wait_until='networkidle')"
puts $fp " print('✓ Successfully navigated to login URL')"
puts $fp " "
puts $fp " # Wait for page to load and look for Continue button"
puts $fp " print('Waiting for page to load...')"
puts $fp " await page.wait_for_load_state('networkidle')"
puts $fp " "
puts $fp " # Try multiple selectors for the Continue button"
puts $fp " print('Looking for Continue button...')"
puts $fp " continue_button = None"
puts $fp " selectors = \["
puts $fp " 'input\[type=\"submit\"\]\[value=\"Continue\"\]',"
puts $fp " 'button\[type=\"submit\"\]:has-text(\"Continue\")',"
puts $fp " 'input.btn\[type=\"submit\"\]',"
puts $fp " 'button:has-text(\"Continue\")',"
puts $fp " 'input\[value=\"Continue\"\]'"
puts $fp " \]"
puts $fp " "
puts $fp " for selector in selectors:"
puts $fp " try:"
puts $fp " print(f'Trying selector: {selector}')"
puts $fp " continue_button = await page.wait_for_selector(selector, timeout=5000)"
puts $fp " if continue_button:"
puts $fp " print(f'✓ Found Continue button with selector: {selector}')"
puts $fp " break"
puts $fp " except Exception as e:"
puts $fp " print(f'Selector {selector} failed: {e}')"
puts $fp " continue"
puts $fp " "
puts $fp " if continue_button:"
puts $fp " await continue_button.click()"
puts $fp " print('✓ Successfully clicked Continue button')"
puts $fp " "
puts $fp " # Wait for navigation after clicking Continue"
puts $fp " print('Waiting for page navigation...')"
puts $fp " await page.wait_for_load_state('networkidle')"
puts $fp " "
puts $fp " # Check if we're on Google OAuth page"
puts $fp " current_url = page.url"
puts $fp " print(f'Current URL after Continue: {current_url}')"
puts $fp " "
puts $fp " if 'accounts.google.com' in current_url:"
puts $fp " print('✓ Detected Google OAuth flow - this is expected for GitHub login')"
puts $fp " print('✓ GitHub OAuth flow initiated successfully')"
puts $fp " return True"
puts $fp " "
puts $fp " else:"
puts $fp " print('✗ Continue button not found')"
puts $fp " return False"
puts $fp " "
puts $fp " # Wait for device code input field (fallback for direct device code flow)"
puts $fp " print('Looking for device code input field...')"
puts $fp " device_code_input = await page.wait_for_selector("
puts $fp " 'input\[name=\"user-code-0\"\]\[id=\"user-code-0\"\]', "
puts $fp " timeout=10000"
puts $fp " )"
puts $fp " "
puts $fp " if device_code_input:"
puts $fp " # Enter the device code character by character"
puts $fp " device_code = '$device_code'"
puts $fp " print(f'Entering device code: {device_code}')"
puts $fp " "
puts $fp " # Remove hyphens from device code for input"
puts $fp " clean_code = device_code.replace('-', '')"
puts $fp " print(f'Clean device code: {clean_code}')"
puts $fp " "
puts $fp " # Enter each character in the corresponding input field"
puts $fp " for i in range(len(clean_code)):"
puts $fp " input_selector = f'input\[name=\"user-code-{i}\"\]'"
puts $fp " print(f'Looking for input field {i}: {input_selector}')"
puts $fp " "
puts $fp " input_field = await page.wait_for_selector("
puts $fp " input_selector, "
puts $fp " timeout=5000"
puts $fp " )"
puts $fp " "
puts $fp " if input_field:"
puts $fp " await input_field.clear()"
puts $fp " await input_field.fill(clean_code\[i\])"
puts $fp " print(f'✓ Entered character {i+1}: {clean_code\[i\]}')"
puts $fp " else:"
puts $fp " print(f'✗ Could not find input field for character {i+1}')"
puts $fp " return False"
puts $fp " else:"
puts $fp " print('✗ Device code input field not found')"
puts $fp " return False"
puts $fp " "
puts $fp " # Click the submit button after entering device code"
puts $fp " print('Looking for submit button...')"
puts $fp " submit_button = await page.wait_for_selector("
puts $fp " 'input.btn.btn-sm.btn-primary.m-0\[type=\"submit\"\]', "
puts $fp " timeout=10000"
puts $fp " )"
puts $fp " "
puts $fp " if submit_button:"
puts $fp " await submit_button.click()"
puts $fp " print('✓ Successfully clicked submit button')"
puts $fp " else:"
puts $fp " print('✗ Submit button not found')"
puts $fp " return False"
puts $fp " "
puts $fp " print('✓ Playwright test completed successfully')"
puts $fp " return True"
puts $fp " "
puts $fp " except Exception as e:"
puts $fp " print(f'✗ Playwright test failed: {e}')"
puts $fp " return False"
puts $fp " finally:"
puts $fp " await browser.close()"
puts $fp ""
puts $fp "if __name__ == '__main__':"
puts $fp " result = asyncio.run(test_login_flow())"
puts $fp " sys.exit(0 if result else 1)"
close $fp
puts "${GREEN}✓ Playwright script created at: $temp_script${NC}"
# Make the script executable
puts "${BLUE}=== Test 2 Phase 8: Making script executable ===${NC}"
exec chmod +x $temp_script
puts "${GREEN}✓ Script made executable${NC}"
# Run the playwright test and wait for completion using virtual environment
puts "${BLUE}=== Test 2 Phase 9: Running Playwright automation ===${NC}"
# Use the same venv_python that was determined in check_python_deps
puts "${BLUE}Using Python executable: $venv_python${NC}"
if {[catch {
set playwright_result [exec $venv_python $temp_script]
puts "${GREEN}✓ Playwright test output: $playwright_result${NC}"
set playwright_success 1
} result]} {
puts "${RED}✗ Playwright test failed: $result${NC}"
set playwright_success 0
}
# Clean up the temporary script
puts "${BLUE}=== Test 2 Phase 10: Cleaning up temporary files ===${NC}"
exec rm -f $temp_script
puts "${GREEN}✓ Temporary script cleaned up${NC}"
if {$playwright_success} {
puts "${GREEN}✓ Playwright automation completed successfully${NC}"
puts "${BLUE}=== Test 2 Phase 11: Playwright automation completed ===${NC}"
} else {
puts "${RED}✗ Playwright automation failed${NC}"
puts "${BLUE}=== Test 2 Phase 11: Playwright automation failed ===${NC}"
}
# Continue with the login process instead of breaking
exp_continue
}
exp_continue
}
-re "Available organizations:" {
puts "${GREEN}✓ Found organization selection prompt${NC}"
puts "${BLUE}=== Test 2 Phase 12: Organization selection detected ===${NC}"
exp_continue
}
-re "Select an organization by entering the number:" {
puts "${GREEN}✓ Found organization selection input prompt${NC}"
puts "${BLUE}=== Test 2 Phase 13: Organization selection input prompt ===${NC}"
# Send "1" and Enter to select the first organization
send "1\r"
puts "${GREEN}✓ Sent organization selection: 1${NC}"
puts "${BLUE}=== Test 2 Phase 14: Organization selection sent ===${NC}"
exp_continue
}
-re "Selected organization:" {
puts "${GREEN}✓ Organization selection confirmed${NC}"
puts "${BLUE}=== Test 2 Phase 15: Organization selection confirmed ===${NC}"
exp_continue
}
-re "Created API key:" {
puts "${GREEN}✓ API key created successfully${NC}"
puts "${BLUE}=== Test 2 Phase 16: API key created ===${NC}"
exp_continue
}
-re "Login process successfully" {
puts "${GREEN}✓ Login process completed successfully${NC}"
puts "${BLUE}=== Test 2 Phase 17: Login process completed ===${NC}"
exp_continue
}
-re "Login successful" {
puts "${GREEN}✓ Login completed successfully${NC}"
puts "${BLUE}=== Test 2 Phase 17: Login successful ===${NC}"
exp_continue
}
-re "Already logged in" {
puts "${GREEN}✓ Already logged in (expected in some cases)${NC}"
puts "${BLUE}=== Test 2 Phase 17: Already logged in ===${NC}"
exp_continue
}
timeout {
puts "${BLUE}=== Test 2 Phase 18: Timeout handling ===${NC}"
# If we have both URL and device code, Playwright should have completed
if {$login_url != "" && $device_code != ""} {
puts "${GREEN}✓ Login flow test completed (URL and device code extracted, Playwright automation completed)${NC}"
puts "${BLUE}=== Test 2 Phase 19: Test completed with extracted credentials ===${NC}"
} else {
puts "${RED}✗ Timeout waiting for login flow${NC}"
puts "${BLUE}=== Test 2 Phase 19: Test failed due to timeout ===${NC}"
exit 1
}
}
eof {
puts "${GREEN}✓ Login command completed${NC}"
puts "${BLUE}=== Test 2 Phase 19: Login command completed normally ===${NC}"
}
}
catch {close}
} result]} {
puts "${RED}✗ Test failed: $result${NC}"
exit 1
}
# Test 3: Test login with invalid flags
puts "\n${YELLOW}Testing login with invalid flags...${NC}"
puts "${BLUE}Running Test 3: Login with invalid flags${NC}"
if {[catch {
spawn $gbox_binary login --invalid-flag
expect {
-re "Error:.*unknown flag.*invalid-flag" {
puts "${GREEN}✓ Found expected error message for invalid flag${NC}"
exp_continue
}
timeout {
puts "${RED}✗ Timeout waiting for error message${NC}"
exit 1
}
eof {
puts "${GREEN}✓ Error message displayed correctly${NC}"
}
}
catch {close}
} result]} {
puts "${RED}✗ Test failed: $result${NC}"
exit 1
}
puts "\n${GREEN}All login tests passed!${NC}"
exit 0