Files
NetAlertX/test/ui/test_ui_login.py

254 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Login Page UI Tests
Tests login functionality and deep link support after login
"""
import sys
import os
import time
import pytest
from selenium.webdriver.common.by import By
# Add test directory to path
sys.path.insert(0, os.path.dirname(__file__))
from .test_helpers import BASE_URL, wait_for_page_load # noqa: E402
def get_login_password():
"""Get login password from config file or environment
Returns the plaintext password that should be used for login.
For test/dev environments, tries common test passwords and defaults.
Returns None if password cannot be determined (will skip test).
"""
# Try environment variable first (for testing)
if os.getenv("LOGIN_PASSWORD"):
return os.getenv("LOGIN_PASSWORD")
# SHA256 hash of "password" - the default test password (from index.php)
DEFAULT_PASSWORD_HASH = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'
# Try common config file locations
config_paths = [
"/data/config/app.conf",
"/app/back/app.conf",
os.path.expanduser("~/.netalertx/app.conf")
]
for config_path in config_paths:
try:
if os.path.exists(config_path):
print(f"📋 Reading config from: {config_path}")
with open(config_path, 'r') as f:
for line in f:
# Only look for SETPWD_password lines (not other config like API keys)
if 'SETPWD_password' in line and '=' in line:
# Extract the value between quotes
value = line.split('=', 1)[1].strip()
# Remove quotes
value = value.strip('"').strip("'")
print(f"✓ Found password config: {value[:32]}...")
# If it's the default, use the default password
if value == DEFAULT_PASSWORD_HASH:
print(" Using default password: '123456'")
return "123456"
# If it's plaintext and looks reasonable
elif len(value) < 100 and not value.startswith('{') and value.isalnum():
print(f" Using plaintext password: '{value}'")
return value
# For other hashes, can't determine plaintext
break # Found SETPWD_password, stop looking
except (FileNotFoundError, IOError, PermissionError) as e:
print(f"⚠ Error reading {config_path}: {e}")
continue
# If we couldn't determine the password from config, try default password
print(" Password not determinable from config, trying default passwords...")
# For now, return first test password to try
# Tests will skip if login fails
return None
def require_login_page(driver):
"""Skip the test if the login form is not present (web protection disabled)."""
fields = driver.find_elements(By.NAME, "loginpassword")
if not fields:
pytest.skip(
"Web protection is disabled (SETPWD_enable_password != true); "
"login page is not shown on this instance"
)
def perform_login(driver, password=None):
"""Helper function to perform login with optional password fallback
Args:
driver: Selenium WebDriver
password: Password to try. If None, will try default test password
"""
if password is None:
password = "123456" # Default test password
require_login_page(driver)
password_input = driver.find_element(By.NAME, "loginpassword")
password_input.send_keys(password)
submit_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
submit_button.click()
# Wait for page to respond to form submission
# This might either redirect or show login error
time.sleep(1)
wait_for_page_load(driver, timeout=5)
def test_login_page_loads(driver):
"""Test: Login page loads successfully"""
driver.get(f"{BASE_URL}/index.php")
wait_for_page_load(driver)
# Skip if web protection is disabled (page redirected away from login form)
require_login_page(driver)
password_field = driver.find_element(By.NAME, "loginpassword")
assert password_field, "Password field should be present"
submit_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
assert submit_button, "Submit button should be present"
def test_login_redirects_to_devices(driver):
"""Test: Successful login redirects to devices page"""
import pytest
password = get_login_password()
# Use password if found, otherwise helper will use default "password"
driver.get(f"{BASE_URL}/index.php")
wait_for_page_load(driver)
perform_login(driver, password)
# Wait for redirect to complete (server-side redirect is usually instant)
time.sleep(1)
# Should be redirected to devices page
if '/devices.php' not in driver.current_url:
pytest.skip(f"Login failed or not configured. URL: {driver.current_url}")
assert '/devices.php' in driver.current_url, \
f"Expected redirect to devices.php, got {driver.current_url}"
def test_login_with_deep_link_preserves_hash(driver):
"""Test: Login with deep link (?next=...) preserves the URL fragment hash
When a user logs in from a deep link URL (e.g., ?next=base64(devices.php%23device-123)),
they should be redirected to the target page with the hash fragment intact.
"""
import base64
import pytest
password = get_login_password()
# Create a deep link to devices.php#device-123
deep_link_path = "/devices.php#device-123"
encoded_path = base64.b64encode(deep_link_path.encode()).decode()
# Navigate to login with deep link
driver.get(f"{BASE_URL}/index.php?next={encoded_path}")
wait_for_page_load(driver)
perform_login(driver, password)
# Wait for redirect to complete (server-side redirect + potential JS handling)
time.sleep(2)
# Check that we're on the right page with the hash preserved
current_url = driver.current_url
print(f"URL after login with deep link: {current_url}")
if '/devices.php' not in current_url:
pytest.skip(f"Login failed or redirect not configured. URL: {current_url}")
# Verify the hash fragment is preserved
assert '#device-123' in current_url, f"Expected #device-123 hash in URL, got {current_url}"
def test_login_with_deep_link_to_network_page(driver):
"""Test: Login with deep link to network.php page preserves hash
User can login with a deep link to the network page (e.g., network.php#settings-panel),
and should be redirected to that page with the hash fragment intact.
"""
import base64
import pytest
password = get_login_password()
# Create a deep link to network.php#settings-panel
deep_link_path = "/network.php#settings-panel"
encoded_path = base64.b64encode(deep_link_path.encode()).decode()
# Navigate to login with deep link
driver.get(f"{BASE_URL}/index.php?next={encoded_path}")
wait_for_page_load(driver)
perform_login(driver, password)
# Wait for redirect to complete
time.sleep(2)
# Check that we're on the right page with the hash preserved
current_url = driver.current_url
print(f"URL after login with network.php deep link: {current_url}")
if '/network.php' not in current_url:
pytest.skip(f"Login failed or redirect not configured. URL: {current_url}")
# Verify the hash fragment is preserved
assert '#settings-panel' in current_url, f"Expected #settings-panel hash in URL, got {current_url}"
def test_login_without_next_parameter(driver):
"""Test: Login without ?next parameter defaults to devices.php"""
import pytest
password = get_login_password()
driver.get(f"{BASE_URL}/index.php")
wait_for_page_load(driver)
perform_login(driver, password)
# Wait for redirect to complete
time.sleep(1)
# Should redirect to default devices page
current_url = driver.current_url
if '/devices.php' not in current_url:
pytest.skip(f"Login failed or not configured. URL: {current_url}")
assert '/devices.php' in current_url, f"Expected default redirect to devices.php, got {current_url}"
def test_url_hash_hidden_input_present(driver):
"""Test: URL fragment hash field is present in login form
The hidden url_hash input field is used to capture and preserve
URL hash fragments during form submission and redirect.
"""
driver.get(f"{BASE_URL}/index.php")
wait_for_page_load(driver)
# Skip if web protection is disabled (login form not shown)
require_login_page(driver)
# Verify the hidden input field exists
url_hash_input = driver.find_element(By.ID, "url_hash")
assert url_hash_input, "Hidden url_hash input field should be present"
assert url_hash_input.get_attribute("type") == "hidden", "url_hash should be a hidden input field"