mobile responsiveness
This commit is contained in:
parent
b93349aa4d
commit
ad664c32ea
4 changed files with 494 additions and 3 deletions
344
tests/test_mobile.py
Normal file
344
tests/test_mobile.py
Normal file
|
@ -0,0 +1,344 @@
|
|||
import pytest
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
|
||||
|
||||
class TestMobileResponsiveness:
|
||||
"""Test mobile responsiveness and navigation functionality"""
|
||||
|
||||
def test_mobile_nav_toggle_button_exists(self, calculator_page):
|
||||
"""Test that mobile navigation toggle button is present"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
nav_toggle = calculator_page.find_element(By.ID, "navToggle")
|
||||
assert nav_toggle.is_displayed()
|
||||
|
||||
# Check it has the correct class
|
||||
assert "nav-toggle" in nav_toggle.get_attribute("class")
|
||||
|
||||
# Check it has the hamburger icon
|
||||
svg = nav_toggle.find_element(By.TAG_NAME, "svg")
|
||||
assert svg.is_displayed()
|
||||
|
||||
def test_mobile_nav_toggle_functionality(self, calculator_page):
|
||||
"""Test that mobile navigation toggle works correctly"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
nav_toggle = calculator_page.find_element(By.ID, "navToggle")
|
||||
sidenav = calculator_page.find_element(By.ID, "nav")
|
||||
|
||||
# Debug: check if element is actually clickable
|
||||
print(f"Nav toggle displayed: {nav_toggle.is_displayed()}")
|
||||
print(f"Nav toggle enabled: {nav_toggle.is_enabled()}")
|
||||
print(f"Nav toggle location: {nav_toggle.location}")
|
||||
print(f"Nav toggle size: {nav_toggle.size}")
|
||||
|
||||
# Initially, sidenav should not have mobile-active class
|
||||
assert "mobile-active" not in sidenav.get_attribute("class")
|
||||
|
||||
# Wait for element to be clickable
|
||||
WebDriverWait(calculator_page, 10).until(
|
||||
EC.element_to_be_clickable((By.ID, "navToggle"))
|
||||
)
|
||||
|
||||
# Click the toggle button using JavaScript if regular click fails
|
||||
try:
|
||||
nav_toggle.click()
|
||||
except Exception as e:
|
||||
print(f"Regular click failed: {e}")
|
||||
calculator_page.execute_script("arguments[0].click();", nav_toggle)
|
||||
|
||||
# Wait for the mobile-active class to be added
|
||||
WebDriverWait(calculator_page, 5).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
||||
|
||||
# Click again to close
|
||||
try:
|
||||
nav_toggle.click()
|
||||
except Exception as e:
|
||||
print(f"Regular click failed on close: {e}")
|
||||
calculator_page.execute_script("arguments[0].click();", nav_toggle)
|
||||
|
||||
# Wait for the mobile-active class to be removed
|
||||
WebDriverWait(calculator_page, 5).until_not(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
||||
|
||||
def test_mobile_nav_closes_on_outside_click(self, calculator_page):
|
||||
"""Test that mobile navigation closes when clicking outside"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
nav_toggle = calculator_page.find_element(By.ID, "navToggle")
|
||||
sidenav = calculator_page.find_element(By.ID, "nav")
|
||||
|
||||
# Open mobile nav using JavaScript
|
||||
calculator_page.execute_script("arguments[0].click();", nav_toggle)
|
||||
|
||||
# Wait for nav to open
|
||||
WebDriverWait(calculator_page, 5).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
||||
|
||||
# Click on the body element (outside nav) using JavaScript
|
||||
calculator_page.execute_script("document.body.click();")
|
||||
|
||||
# Wait for nav to close
|
||||
WebDriverWait(calculator_page, 5).until_not(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
||||
|
||||
def test_mobile_nav_closes_on_nav_link_click(self, calculator_page):
|
||||
"""Test that mobile navigation closes when clicking a navigation link"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
nav_toggle = calculator_page.find_element(By.ID, "navToggle")
|
||||
sidenav = calculator_page.find_element(By.ID, "nav")
|
||||
|
||||
# Open mobile nav using JavaScript
|
||||
calculator_page.execute_script("arguments[0].click();", nav_toggle)
|
||||
|
||||
# Wait for nav to open
|
||||
WebDriverWait(calculator_page, 5).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
||||
|
||||
# Click on a navigation link
|
||||
nav_link = sidenav.find_element(By.CSS_SELECTOR, "a[data-calc='raid']")
|
||||
nav_link.click()
|
||||
|
||||
# Wait for nav to close
|
||||
WebDriverWait(calculator_page, 5).until_not(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
||||
|
||||
def test_mobile_nav_sticky_positioning(self, calculator_page):
|
||||
"""Test that navigation bar stays at top and content doesn't scroll under it"""
|
||||
# Get the navigation bar
|
||||
nav_bar = calculator_page.find_element(By.CLASS_NAME, "bar")
|
||||
|
||||
# Check that it has sticky positioning
|
||||
position = nav_bar.value_of_css_property("position")
|
||||
assert position == "sticky"
|
||||
|
||||
# Check that it has a high z-index
|
||||
z_index = nav_bar.value_of_css_property("z-index")
|
||||
assert int(z_index) >= 10
|
||||
|
||||
# Check that it has a minimum height
|
||||
min_height = nav_bar.value_of_css_property("min-height")
|
||||
assert min_height == "70px"
|
||||
|
||||
def test_mobile_responsive_layout(self, calculator_page):
|
||||
"""Test that layout changes appropriately on mobile"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
# Get the layout container
|
||||
layout = calculator_page.find_element(By.CLASS_NAME, "layout")
|
||||
|
||||
# Check that it has proper grid layout
|
||||
display = layout.value_of_css_property("display")
|
||||
assert display == "grid"
|
||||
|
||||
# Check that it has responsive grid template
|
||||
grid_template = layout.value_of_css_property("grid-template-columns")
|
||||
# Should be responsive - on mobile it will be 1fr, on desktop 240px 1fr
|
||||
# The actual value might be computed differently, so just check it's a valid grid value
|
||||
assert "px" in grid_template or "fr" in grid_template
|
||||
|
||||
def test_mobile_friendly_inputs(self, calculator_page):
|
||||
"""Test that inputs are mobile-friendly"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
# Navigate to a calculator with inputs
|
||||
calculator_page.get("http://localhost:8008/subnet")
|
||||
|
||||
# Wait for calculator to load
|
||||
WebDriverWait(calculator_page, 10).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='ipAddress']"))
|
||||
)
|
||||
|
||||
# Check input styling
|
||||
ip_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='ipAddress']")
|
||||
|
||||
# Check padding (should be 12px on mobile)
|
||||
padding = ip_input.value_of_css_property("padding")
|
||||
assert "12px" in padding
|
||||
|
||||
# Check font size (should be 16px to prevent zoom on iOS)
|
||||
font_size = ip_input.value_of_css_property("font-size")
|
||||
assert "16px" in font_size
|
||||
|
||||
def test_mobile_table_overflow(self, calculator_page):
|
||||
"""Test that tables have horizontal scroll on mobile"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
# Navigate to subnet calculator which has tables
|
||||
calculator_page.get("http://localhost:8008/subnet")
|
||||
|
||||
# Wait for calculator to load
|
||||
WebDriverWait(calculator_page, 10).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='ipAddress']"))
|
||||
)
|
||||
|
||||
# Enter an IP address to generate the table
|
||||
ip_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='ipAddress']")
|
||||
ip_input.clear()
|
||||
ip_input.send_keys("192.168.1.1")
|
||||
|
||||
# Wait for table to appear
|
||||
WebDriverWait(calculator_page, 10).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "table"))
|
||||
)
|
||||
|
||||
# Check that the result container has overflow handling
|
||||
result_container = calculator_page.find_element(By.CLASS_NAME, "result")
|
||||
overflow_x = result_container.value_of_css_property("overflow-x")
|
||||
# Should have auto or scroll overflow on mobile
|
||||
assert overflow_x in ["auto", "scroll"]
|
||||
|
||||
def test_mobile_footer_layout(self, calculator_page):
|
||||
"""Test that footer is mobile-friendly"""
|
||||
footer_content = calculator_page.find_element(By.CLASS_NAME, "footer-content")
|
||||
|
||||
# Check that footer content has proper flexbox layout
|
||||
display = footer_content.value_of_css_property("display")
|
||||
assert display == "flex"
|
||||
|
||||
# Check that source link is properly positioned
|
||||
source_link = calculator_page.find_element(By.CLASS_NAME, "source-link")
|
||||
assert source_link.is_displayed()
|
||||
assert "https://code.disobey.net/whilb/calculator.127local.net" in source_link.get_attribute("href")
|
||||
|
||||
def test_mobile_nav_theme_toggle_buttons(self, calculator_page):
|
||||
"""Test that both nav toggle and theme toggle buttons are accessible"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
nav_toggle = calculator_page.find_element(By.ID, "navToggle")
|
||||
theme_toggle = calculator_page.find_element(By.ID, "themeToggle")
|
||||
|
||||
# Both buttons should be visible
|
||||
assert nav_toggle.is_displayed()
|
||||
assert theme_toggle.is_displayed()
|
||||
|
||||
# Both should be clickable
|
||||
assert nav_toggle.is_enabled()
|
||||
assert theme_toggle.is_enabled()
|
||||
|
||||
# Check button styling
|
||||
for button in [nav_toggle, theme_toggle]:
|
||||
cursor = button.value_of_css_property("cursor")
|
||||
assert cursor == "pointer"
|
||||
|
||||
def test_mobile_nav_accessibility(self, calculator_page):
|
||||
"""Test mobile navigation accessibility features"""
|
||||
nav_toggle = calculator_page.find_element(By.ID, "navToggle")
|
||||
sidenav = calculator_page.find_element(By.ID, "nav")
|
||||
|
||||
# Check aria-label on toggle button
|
||||
aria_label = nav_toggle.get_attribute("aria-label")
|
||||
assert aria_label == "Toggle navigation"
|
||||
|
||||
# Check that sidenav has proper role (should be navigation)
|
||||
role = sidenav.get_attribute("role")
|
||||
# If no explicit role, check that it's semantically correct
|
||||
if not role:
|
||||
# Should contain navigation links
|
||||
nav_links = sidenav.find_elements(By.CSS_SELECTOR, "a[data-calc]")
|
||||
assert len(nav_links) > 0
|
||||
|
||||
def test_mobile_nav_calculator_integration(self, calculator_page):
|
||||
"""Test that mobile navigation works properly with calculator functionality"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
# Navigate to subnet calculator
|
||||
calculator_page.get("http://localhost:8008/subnet")
|
||||
|
||||
# Wait for calculator to load
|
||||
WebDriverWait(calculator_page, 10).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='ipAddress']"))
|
||||
)
|
||||
|
||||
# Open mobile navigation using JavaScript
|
||||
nav_toggle = calculator_page.find_element(By.ID, "navToggle")
|
||||
calculator_page.execute_script("arguments[0].click();", nav_toggle)
|
||||
|
||||
# Wait for nav to open
|
||||
WebDriverWait(calculator_page, 5).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
||||
|
||||
# Navigate to a different calculator via mobile nav
|
||||
nav_link = calculator_page.find_element(By.CSS_SELECTOR, "a[data-calc='currency']")
|
||||
nav_link.click()
|
||||
|
||||
# Wait for nav to close and currency calculator to load
|
||||
WebDriverWait(calculator_page, 10).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='amount']"))
|
||||
)
|
||||
|
||||
# Verify we're on the currency calculator
|
||||
currency_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='amount']")
|
||||
assert currency_input.is_displayed()
|
||||
|
||||
# Verify nav is closed
|
||||
sidenav = calculator_page.find_element(By.ID, "nav")
|
||||
assert "mobile-active" not in sidenav.get_attribute("class")
|
||||
|
||||
def test_mobile_nav_scroll_behavior(self, calculator_page):
|
||||
"""Test that mobile navigation doesn't interfere with page scrolling"""
|
||||
# Set mobile viewport
|
||||
calculator_page.set_window_size(375, 667)
|
||||
|
||||
# Navigate to a calculator with long content
|
||||
calculator_page.get("http://localhost:8008/subnet")
|
||||
|
||||
# Wait for calculator to load
|
||||
WebDriverWait(calculator_page, 10).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='ipAddress']"))
|
||||
)
|
||||
|
||||
# Enter an IP address to generate content
|
||||
ip_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='ipAddress']")
|
||||
ip_input.clear()
|
||||
ip_input.send_keys("10.0.0.1")
|
||||
|
||||
# Wait for results to appear
|
||||
WebDriverWait(calculator_page, 10).until(
|
||||
EC.presence_of_element_located((By.CLASS_NAME, "result"))
|
||||
)
|
||||
|
||||
# Open mobile navigation using JavaScript
|
||||
nav_toggle = calculator_page.find_element(By.ID, "navToggle")
|
||||
calculator_page.execute_script("arguments[0].click();", nav_toggle)
|
||||
|
||||
# Wait for nav to open
|
||||
WebDriverWait(calculator_page, 5).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
||||
|
||||
# Try to scroll the page
|
||||
calculator_page.execute_script("window.scrollTo(0, 100)")
|
||||
|
||||
# Verify navigation is still open and functional
|
||||
sidenav = calculator_page.find_element(By.ID, "nav")
|
||||
assert "mobile-active" in sidenav.get_attribute("class")
|
||||
|
||||
# Close navigation using JavaScript
|
||||
calculator_page.execute_script("arguments[0].click();", nav_toggle)
|
||||
|
||||
# Verify navigation closes
|
||||
WebDriverWait(calculator_page, 5).until_not(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, ".sidenav.mobile-active"))
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue