mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-02 14:16:01 -05:00
254 lines
9.0 KiB
Python
254 lines
9.0 KiB
Python
#!/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"
|