feat: add comprehensive E2E test suite (Playwright + WireGuard + API)
Adds tests/e2e/ with three layers of E2E coverage: - API layer (tests/e2e/api/): unauthenticated access, admin endpoints, peer endpoints, access control enforcement — 24 tests - Playwright UI (tests/e2e/ui/): login flows, admin navigation, peer dashboard/services, role-based ACL, password change — 60+ tests - WireGuard connectivity (tests/e2e/wg/): tunnel up/down, DNS resolution through VPN, service ACL enforcement via iptables, full-tunnel routing Shared helpers: PicAPIClient, WGInterface, playwright_login, cleanup. Makefile targets: test-e2e-api, test-e2e-ui, test-e2e-wg, test-e2e. Adds scripts/reset_admin_password.py for test bootstrap. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
Admin navigation tests.
|
||||
|
||||
Scenario 6: admin can reach every route defined in App.jsx adminNavigation
|
||||
without being redirected to /login.
|
||||
|
||||
Routes under test (from App.jsx adminNavigation):
|
||||
/ Dashboard
|
||||
/peers Peers
|
||||
/network Network Services
|
||||
/wireguard WireGuard
|
||||
/email Email
|
||||
/calendar Calendar
|
||||
/files Files
|
||||
/routing Routing
|
||||
/vault Vault
|
||||
/containers Container Dashboard
|
||||
/cell-network Cell Network
|
||||
/logs Logs
|
||||
/settings Settings
|
||||
/account Account Settings
|
||||
"""
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.ui
|
||||
|
||||
ADMIN_ROUTES = [
|
||||
('/', 'Dashboard'),
|
||||
('/peers', 'Peers'),
|
||||
('/network', 'Network Services'),
|
||||
('/wireguard', 'WireGuard'),
|
||||
('/email', 'Email'),
|
||||
('/calendar', 'Calendar'),
|
||||
('/files', 'Files'),
|
||||
('/routing', 'Routing'),
|
||||
('/vault', 'Vault'),
|
||||
('/containers', 'Containers'),
|
||||
('/cell-network', 'Cell Network'),
|
||||
('/logs', 'Logs'),
|
||||
('/settings', 'Settings'),
|
||||
('/account', 'Account'),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('route,label', ADMIN_ROUTES)
|
||||
def test_admin_can_reach_route(admin_page, webui_base, route, label):
|
||||
"""Admin navigating to each app route should not be sent to /login."""
|
||||
page = admin_page
|
||||
page.goto(f"{webui_base}{route}")
|
||||
page.wait_for_load_state('networkidle')
|
||||
assert '/login' not in page.url, (
|
||||
f"Admin was redirected to /login when navigating to {route} ({label})"
|
||||
)
|
||||
|
||||
|
||||
def test_admin_sidebar_shows_admin_links(admin_page, webui_base):
|
||||
"""The desktop sidebar must show admin-only links: Peers, Settings, WireGuard."""
|
||||
page = admin_page
|
||||
page.goto(f"{webui_base}/")
|
||||
page.wait_for_load_state('networkidle')
|
||||
# These link names come from the adminNavigation array in App.jsx.
|
||||
for link_name in ('Peers', 'Settings', 'WireGuard'):
|
||||
assert page.get_by_role('link', name=link_name).is_visible(), (
|
||||
f"Admin sidebar link '{link_name}' not visible"
|
||||
)
|
||||
|
||||
|
||||
def test_admin_sidebar_does_not_show_my_services(admin_page, webui_base):
|
||||
"""Admin sidebar should NOT contain the peer-only 'My Services' link."""
|
||||
page = admin_page
|
||||
page.goto(f"{webui_base}/")
|
||||
page.wait_for_load_state('networkidle')
|
||||
assert not page.get_by_role('link', name='My Services').is_visible(), (
|
||||
"Admin sidebar should not show the peer-only 'My Services' link"
|
||||
)
|
||||
Reference in New Issue
Block a user