mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-30 23:03:03 -07:00
Refactor login functionality: Remove Remember Me feature and update tests for deep link support
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Login Page UI Tests
|
||||
Tests login functionality, Remember Me, and deep link support
|
||||
Tests login functionality and deep link support after login
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -20,7 +20,7 @@ from .test_helpers import BASE_URL, wait_for_page_load, wait_for_element_by_css
|
||||
|
||||
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).
|
||||
@@ -28,20 +28,20 @@ def get_login_password():
|
||||
# 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'
|
||||
|
||||
|
||||
# List of passwords to try in order
|
||||
passwords_to_try = ["123456", "password", "test", "admin"]
|
||||
|
||||
|
||||
# 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):
|
||||
@@ -55,7 +55,7 @@ def get_login_password():
|
||||
# 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(f" Using default password: '123456'")
|
||||
@@ -69,10 +69,10 @@ def get_login_password():
|
||||
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(f"ℹ Password not determinable from config, trying default passwords...")
|
||||
|
||||
|
||||
# For now, return first test password to try
|
||||
# Tests will skip if login fails
|
||||
return None
|
||||
@@ -80,20 +80,20 @@ def get_login_password():
|
||||
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
@@ -104,11 +104,11 @@ def test_login_page_loads(driver):
|
||||
"""Test: Login page loads successfully"""
|
||||
driver.get(f"{BASE_URL}/index.php")
|
||||
wait_for_page_load(driver)
|
||||
|
||||
|
||||
# Check that login form is present
|
||||
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"
|
||||
|
||||
@@ -118,295 +118,129 @@ def test_login_redirects_to_devices(driver):
|
||||
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"""
|
||||
"""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 JavaScript redirect to complete (up to 5 seconds)
|
||||
for i in range(50):
|
||||
current_url = driver.current_url
|
||||
if '/devices.php' in current_url or '/index.php' not in current_url:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
# 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
|
||||
if '/devices.php' not in current_url:
|
||||
pytest.skip(f"Login failed or not configured. URL: {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_device_tree(driver):
|
||||
"""Test: Login with deep link to network tree page"""
|
||||
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 JavaScript redirect to complete (up to 5 seconds)
|
||||
for i in range(50):
|
||||
current_url = driver.current_url
|
||||
if '/network.php' in current_url or '/index.php' not in current_url:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
# 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
|
||||
if '/network.php' not in current_url:
|
||||
pytest.skip(f"Login failed or not configured. URL: {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_remember_me_checkbox_present(driver):
|
||||
"""Test: Remember Me checkbox is present on login form"""
|
||||
driver.get(f"{BASE_URL}/index.php")
|
||||
wait_for_page_load(driver)
|
||||
|
||||
remember_me_checkbox = driver.find_element(By.NAME, "PWRemember")
|
||||
assert remember_me_checkbox, "Remember Me checkbox should be present"
|
||||
|
||||
|
||||
def test_remember_me_login_creates_cookie(driver):
|
||||
"""Test: Login with Remember Me checkbox creates persistent cookie
|
||||
|
||||
Remember Me now uses a simple cookie-based approach (no API calls).
|
||||
When logged in with Remember Me checked, a NetAlertX_SaveLogin cookie
|
||||
is set with a 7-day expiration. On next page load, the cookie
|
||||
automatically authenticates the user without requiring password re-entry.
|
||||
"""
|
||||
import pytest
|
||||
password = get_login_password()
|
||||
|
||||
driver.get(f"{BASE_URL}/index.php")
|
||||
wait_for_page_load(driver)
|
||||
|
||||
# Use JavaScript to check the checkbox reliably
|
||||
checkbox = driver.find_element(By.NAME, "PWRemember")
|
||||
driver.execute_script("arguments[0].checked = true;", checkbox)
|
||||
driver.execute_script("arguments[0].click();", checkbox) # Trigger any change handlers
|
||||
|
||||
# Verify checkbox is actually checked after clicking
|
||||
time.sleep(0.5)
|
||||
is_checked = checkbox.is_selected()
|
||||
print(f"✓ Checkbox checked via JavaScript: {is_checked}")
|
||||
|
||||
if not is_checked:
|
||||
pytest.skip("Could not check Remember Me checkbox")
|
||||
|
||||
perform_login(driver, password)
|
||||
|
||||
# Wait for redirect
|
||||
time.sleep(2)
|
||||
|
||||
# Main assertion: login should work with Remember Me checked
|
||||
assert '/devices.php' in driver.current_url or '/network.php' in driver.current_url, \
|
||||
f"Login with Remember Me should redirect to app, got {driver.current_url}"
|
||||
|
||||
# Secondary check: verify Remember Me cookie (NetAlertX_SaveLogin) was set
|
||||
cookies = driver.get_cookies()
|
||||
cookie_names = [cookie['name'] for cookie in cookies]
|
||||
|
||||
print(f"Cookies found: {cookie_names}")
|
||||
|
||||
# Check for the Remember Me cookie
|
||||
remember_me_cookie = None
|
||||
for cookie in cookies:
|
||||
if cookie['name'] == 'NetAlertX_SaveLogin':
|
||||
remember_me_cookie = cookie
|
||||
break
|
||||
|
||||
if remember_me_cookie:
|
||||
print(f"✓ Remember Me cookie successfully set: {remember_me_cookie['name']}")
|
||||
print(f" Value (truncated): {remember_me_cookie['value'][:32]}...")
|
||||
print(f" Expires: {remember_me_cookie.get('expiry', 'Not set')}")
|
||||
print(f" HttpOnly: {remember_me_cookie.get('httpOnly', False)}")
|
||||
print(f" Secure: {remember_me_cookie.get('secure', False)}")
|
||||
print(f" SameSite: {remember_me_cookie.get('sameSite', 'Not set')}")
|
||||
else:
|
||||
print("ℹ Remember Me cookie (NetAlertX_SaveLogin) not set in test environment")
|
||||
print(" This is expected if Remember Me checkbox was not properly checked")
|
||||
|
||||
|
||||
def test_remember_me_with_deep_link_preserves_hash(driver):
|
||||
"""Test: Remember Me persistent login preserves URL fragments via cookies
|
||||
|
||||
Remember Me now uses cookies only (no API validation required):
|
||||
1. Login with Remember Me checkbox → NetAlertX_SaveLogin cookie set
|
||||
2. Browser stores cookie persistently (7 days)
|
||||
3. On next page load, cookie presence auto-authenticates user
|
||||
4. Deep link with hash fragment preserved through redirect
|
||||
|
||||
This simulates browser restart by clearing the session cookie (keeping Remember Me cookie).
|
||||
"""
|
||||
import base64
|
||||
import pytest
|
||||
|
||||
password = get_login_password()
|
||||
|
||||
# First, set up a Remember Me session
|
||||
driver.get(f"{BASE_URL}/index.php")
|
||||
wait_for_page_load(driver)
|
||||
|
||||
# Use JavaScript to check the checkbox reliably
|
||||
checkbox = driver.find_element(By.NAME, "PWRemember")
|
||||
driver.execute_script("arguments[0].checked = true;", checkbox)
|
||||
driver.execute_script("arguments[0].click();", checkbox) # Trigger any change handlers
|
||||
|
||||
# Verify checkbox is actually checked
|
||||
time.sleep(0.5)
|
||||
is_checked = checkbox.is_selected()
|
||||
print(f"Checkbox checked for Remember Me test: {is_checked}")
|
||||
|
||||
if not is_checked:
|
||||
pytest.skip("Could not check Remember Me checkbox")
|
||||
|
||||
perform_login(driver, password)
|
||||
|
||||
# Wait and check if login succeeded
|
||||
time.sleep(2)
|
||||
if '/index.php' in driver.current_url and '/devices.php' not in driver.current_url:
|
||||
pytest.skip(f"Initial login failed. Cannot test Remember Me.")
|
||||
|
||||
# Verify Remember Me cookie was set
|
||||
cookies = driver.get_cookies()
|
||||
remember_me_found = False
|
||||
for cookie in cookies:
|
||||
if cookie['name'] == 'NetAlertX_SaveLogin':
|
||||
remember_me_found = True
|
||||
print(f"✓ Remember Me cookie found: {cookie['name']}")
|
||||
break
|
||||
|
||||
if not remember_me_found:
|
||||
pytest.skip("Remember Me cookie was not set during login")
|
||||
|
||||
# Simulate browser restart by clearing session cookies (but keep Remember Me cookie)
|
||||
# Get all cookies, filter out session-related ones, keep Remember Me cookie
|
||||
remember_me_cookie = None
|
||||
for cookie in cookies:
|
||||
if cookie['name'] == 'NetAlertX_SaveLogin':
|
||||
remember_me_cookie = cookie
|
||||
break
|
||||
|
||||
# Clear all cookies
|
||||
driver.delete_all_cookies()
|
||||
|
||||
# Restore Remember Me cookie to simulate browser restart
|
||||
if remember_me_cookie:
|
||||
try:
|
||||
driver.add_cookie({
|
||||
'name': remember_me_cookie['name'],
|
||||
'value': remember_me_cookie['value'],
|
||||
'path': remember_me_cookie.get('path', '/'),
|
||||
'secure': remember_me_cookie.get('secure', False),
|
||||
'domain': remember_me_cookie.get('domain', None),
|
||||
'httpOnly': remember_me_cookie.get('httpOnly', False),
|
||||
'sameSite': remember_me_cookie.get('sameSite', 'Strict')
|
||||
})
|
||||
except Exception as e:
|
||||
pytest.skip(f"Could not restore Remember Me cookie: {e}")
|
||||
|
||||
# Now test deep link with Remember Me cookie (simulated browser restart)
|
||||
deep_link_path = "/devices.php#device-456"
|
||||
encoded_path = base64.b64encode(deep_link_path.encode()).decode()
|
||||
|
||||
driver.get(f"{BASE_URL}/index.php?next={encoded_path}")
|
||||
wait_for_page_load(driver)
|
||||
|
||||
# Wait a moment for Remember Me cookie validation and redirect
|
||||
time.sleep(2)
|
||||
|
||||
# Check current URL - should be on devices with hash
|
||||
current_url = driver.current_url
|
||||
print(f"Current URL after Remember Me auto-login: {current_url}")
|
||||
|
||||
# Verify we're logged in and on the right page
|
||||
assert '/index.php' not in current_url or '/devices.php' in current_url or '/network.php' in current_url, \
|
||||
f"Expected app page after Remember Me auto-login, 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_populated(driver):
|
||||
"""Test: URL fragment hash is populated in hidden url_hash input field"""
|
||||
import base64
|
||||
def test_url_hash_hidden_input_present(driver):
|
||||
"""Test: URL fragment hash field is present in login form
|
||||
|
||||
# Create a deep link
|
||||
deep_link_path = "/devices.php#device-789"
|
||||
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}")
|
||||
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)
|
||||
|
||||
# Wait a bit for JavaScript to execute and populate the hash
|
||||
time.sleep(1)
|
||||
|
||||
# Get the hidden input value - note: this tests JavaScript functionality
|
||||
|
||||
# Verify the hidden input field exists
|
||||
url_hash_input = driver.find_element(By.ID, "url_hash")
|
||||
url_hash_value = url_hash_input.get_attribute("value")
|
||||
|
||||
# The JavaScript should have populated this with window.location.hash
|
||||
# However, since we're navigating to index.php, the hash won't be present at page load
|
||||
# So this test verifies the mechanism exists and would work
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user