Implemented role-based access control, Updated e2e authentication tests and Playwright setup.
This commit is contained in:
255
tests/e2e/test_e2e_authentication.py
Normal file
255
tests/e2e/test_e2e_authentication.py
Normal file
@@ -0,0 +1,255 @@
|
||||
# tests/test_e2e_authentication.py
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
from playwright_config import BASE_URL
|
||||
|
||||
|
||||
class TestAuthentication:
|
||||
"""Tests for authentication and login functionality"""
|
||||
|
||||
@pytest.mark.e2e
|
||||
@pytest.mark.smoke
|
||||
def test_unauthenticated_user_redirected_to_login(self, app_server, page: Page):
|
||||
"""Test that when not logged in, the default page is the login page"""
|
||||
# Navigate to the root URL
|
||||
page.goto(BASE_URL)
|
||||
|
||||
# Wait for the page to fully load
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
# Check that we're on the login page
|
||||
# Option 1: Check URL contains login-related path
|
||||
expect(page).to_have_url(f"{BASE_URL}/authlogin/login")
|
||||
|
||||
# Option 2: Check for login form elements
|
||||
# Look for typical login form elements
|
||||
login_indicators = [
|
||||
"input[type='email']",
|
||||
"input[type='password']",
|
||||
"input[name*='username']",
|
||||
"input[name*='email']",
|
||||
"input[name*='password']",
|
||||
"button[type='submit']",
|
||||
"form"
|
||||
]
|
||||
|
||||
# At least one login indicator should be present
|
||||
login_form_found = False
|
||||
for selector in login_indicators:
|
||||
if page.locator(selector).count() > 0:
|
||||
login_form_found = True
|
||||
break
|
||||
|
||||
assert login_form_found, "No login form elements found on the page"
|
||||
|
||||
# Option 3: Check for login-related text content
|
||||
page_content = page.content().lower()
|
||||
login_keywords = ["login", "sign in", "authenticate", "username", "password", "email"]
|
||||
|
||||
has_login_content = any(keyword in page_content for keyword in login_keywords)
|
||||
assert has_login_content, "Page does not contain login-related content"
|
||||
|
||||
# Option 4: Ensure we're not on a protected page
|
||||
# Check that we don't see protected content like "dashboard", "logout", "profile", "settings"]
|
||||
protected_content = ["dashboard", "logout", "profile", "settings"]
|
||||
page_text = page.locator("body").inner_text().lower()
|
||||
|
||||
for protected_word in protected_content:
|
||||
assert protected_word not in page_text, f"Found protected content '{protected_word}' when not logged in"
|
||||
|
||||
@pytest.mark.e2e
|
||||
@pytest.mark.regression
|
||||
def test_login_page_has_required_elements(self, app_server, page: Page):
|
||||
"""Test that the login page contains all required elements"""
|
||||
page.goto(BASE_URL)
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
# Check for essential login form elements
|
||||
expect(page.locator("form")).to_be_visible()
|
||||
|
||||
# Check for input fields (at least email/username and password)
|
||||
username_input = page.locator("input[type='email'], input[name*='username'], input[name*='email']")
|
||||
expect(username_input.first).to_be_visible()
|
||||
|
||||
password_input = page.locator("input[type='password'], input[name*='password']")
|
||||
expect(password_input.first).to_be_visible()
|
||||
|
||||
# Check for submit button
|
||||
submit_button = page.locator("button[type='submit'], input[type='submit']")
|
||||
expect(submit_button.first).to_be_visible()
|
||||
|
||||
def test_page_loads_without_errors(self, app_server, page: Page):
|
||||
"""Test that the login page loads without console errors"""
|
||||
console_errors = []
|
||||
|
||||
def handle_console(msg):
|
||||
if msg.type == "error":
|
||||
console_errors.append(msg.text)
|
||||
|
||||
page.on("console", handle_console)
|
||||
page.goto(BASE_URL)
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
# Check for console errors
|
||||
assert len(console_errors) == 0, f"Console errors found: {console_errors}"
|
||||
|
||||
# Check that the page actually loaded (not a 404 or 500 error)
|
||||
expect(page.locator("body")).to_be_visible()
|
||||
|
||||
# Check that the page has a title
|
||||
expect(page).to_have_title("My Managing Tools")
|
||||
|
||||
# New test to validate database isolation
|
||||
@pytest.mark.e2e
|
||||
@pytest.mark.smoke
|
||||
def test_test_database_isolation(self, app_server, test_database, test_users):
|
||||
"""Test that application uses isolated test database"""
|
||||
import os
|
||||
|
||||
# Verify test environment variables are configured
|
||||
assert os.environ.get("DB_PATH") == test_database
|
||||
assert os.environ.get("ADMIN_EMAIL") == test_users["admin"]["email"]
|
||||
assert os.environ.get("ADMIN_PASSWORD") == test_users["admin"]["password"]
|
||||
|
||||
# Verify temporary database file exists
|
||||
assert os.path.exists(test_database), f"Test database file not found: {test_database}"
|
||||
|
||||
# Verify path contains 'test_mmt_' to confirm isolation
|
||||
assert "test_mmt_" in test_database, "Database path should contain test prefix"
|
||||
|
||||
print(f"✅ Test database isolation confirmed: {test_database}")
|
||||
|
||||
# =============================================================================
|
||||
# PRIORITY 1 TESTS - CRITICAL AUTHENTICATION FLOWS
|
||||
# =============================================================================
|
||||
|
||||
@pytest.mark.e2e
|
||||
@pytest.mark.priority1
|
||||
@pytest.mark.smoke
|
||||
def test_successful_login_with_valid_credentials(self, app_server, test_users, page: Page):
|
||||
"""
|
||||
Priority 1 Test: Validate complete successful login flow with valid credentials
|
||||
|
||||
This test ensures that:
|
||||
1. User can access the login page
|
||||
2. User can enter valid credentials
|
||||
3. Form submission works correctly
|
||||
4. User is redirected to home page after successful authentication
|
||||
5. User session is properly established
|
||||
"""
|
||||
admin_user = test_users["admin"]
|
||||
|
||||
# Step 1: Navigate to login page
|
||||
page.goto(BASE_URL)
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
# Verify we're on the login page
|
||||
expect(page).to_have_url(f"{BASE_URL}/authlogin/login")
|
||||
|
||||
# Step 2: Locate form elements
|
||||
email_input = page.locator("input[type='email'], input[name='email']")
|
||||
password_input = page.locator("input[type='password'], input[name='password']")
|
||||
submit_button = page.locator("button[type='submit']")
|
||||
|
||||
# Verify all required elements are present and visible
|
||||
expect(email_input).to_be_visible()
|
||||
expect(password_input).to_be_visible()
|
||||
expect(submit_button).to_be_visible()
|
||||
|
||||
# Step 3: Fill in valid credentials
|
||||
email_input.fill(admin_user["email"])
|
||||
password_input.fill(admin_user["password"])
|
||||
|
||||
# Verify credentials were entered correctly
|
||||
expect(email_input).to_have_value(admin_user["email"])
|
||||
expect(password_input).to_have_value(admin_user["password"])
|
||||
|
||||
# Step 4: Submit the login form
|
||||
# Use click with wait for navigation to handle the redirect
|
||||
with page.expect_navigation(wait_until="networkidle"):
|
||||
submit_button.click()
|
||||
|
||||
# DEBUGGING BLOCK - Add this
|
||||
print(f"🔍 Current URL: {page.url}")
|
||||
print(f"🔍 Page title: {page.title()}")
|
||||
|
||||
# Take screenshot
|
||||
page.screenshot(path="debug_after_login.png")
|
||||
|
||||
# Save full HTML for inspection
|
||||
with open("debug_page.html", "w", encoding="utf-8") as f:
|
||||
f.write(page.content())
|
||||
|
||||
# Check specific content that's causing the failure
|
||||
page_content = page.content().lower()
|
||||
login_keywords = ["sign in", "login", "authenticate"]
|
||||
|
||||
print("🔍 Checking for login keywords:")
|
||||
for keyword in login_keywords:
|
||||
if keyword in page_content:
|
||||
print(f" ❌ Found '{keyword}' in page content")
|
||||
# Find exactly where this keyword appears
|
||||
occurrences = page.get_by_text(keyword, exact=False)
|
||||
count = occurrences.count()
|
||||
print(f" Found in {count} elements:")
|
||||
for i in range(min(count, 3)): # Show first 3 occurrences
|
||||
element = occurrences.nth(i)
|
||||
try:
|
||||
print(f" - Element {i}: '{element.inner_text()[:50]}...' (visible: {element.is_visible()})")
|
||||
except:
|
||||
print(f" - Element {i}: Could not get text")
|
||||
else:
|
||||
print(f" ✅ '{keyword}' NOT found")
|
||||
|
||||
# Your original assertion (will still fail, but now you have debug info)
|
||||
has_login_content = any(keyword in page_content for keyword in login_keywords)
|
||||
assert not has_login_content, "Login content still visible after successful authentication"
|
||||
|
||||
page.pause()
|
||||
|
||||
# Step 5: Verify successful authentication and redirect
|
||||
# Should be redirected to the home page
|
||||
expect(page).to_have_url(BASE_URL + "/")
|
||||
|
||||
# Step 6: Verify we're actually authenticated (not redirected back to login)
|
||||
# The page should load without being redirected back to login
|
||||
page.wait_for_load_state("networkidle")
|
||||
current_url = page.url
|
||||
assert not current_url.endswith("/authlogin/login"), f"User was redirected back to login page: {current_url}"
|
||||
|
||||
# Step 7: Verify authenticated content is present
|
||||
# Check that we don't see login-related content anymore
|
||||
page_content = page.content().lower()
|
||||
login_keywords = ["sign in", "login", "authenticate"]
|
||||
|
||||
# Should not see login form elements on authenticated page
|
||||
has_login_content = any(keyword in page_content for keyword in login_keywords)
|
||||
assert not has_login_content, "Login content still visible after successful authentication"
|
||||
|
||||
# Step 8: Verify no error messages are displayed
|
||||
# Look for common error message containers
|
||||
error_selectors = [
|
||||
".error",
|
||||
".alert-error",
|
||||
".bg-error",
|
||||
"[class*='error']",
|
||||
".text-red",
|
||||
"[class*='text-red']"
|
||||
]
|
||||
|
||||
for error_selector in error_selectors:
|
||||
error_elements = page.locator(error_selector)
|
||||
if error_elements.count() > 0:
|
||||
# If error elements exist, they should not be visible or should be empty
|
||||
for i in range(error_elements.count()):
|
||||
element = error_elements.nth(i)
|
||||
if element.is_visible():
|
||||
element_text = element.inner_text().strip()
|
||||
assert not element_text, f"Error message found after successful login: {element_text}"
|
||||
|
||||
# Step 9: Verify the page has expected title
|
||||
expect(page).to_have_title("My Managing Tools")
|
||||
|
||||
print(f"✅ Successful login test completed - User {admin_user['email']} authenticated successfully")
|
||||
Reference in New Issue
Block a user