Working version of playwright

This commit is contained in:
2025-08-30 18:51:42 +02:00
parent 3bd503d4d2
commit db56363b1f
10 changed files with 286 additions and 12 deletions

View File

@@ -1,12 +1,96 @@
from io import BytesIO
import subprocess
import pandas as pd
import pytest
import requests
import time
import sys
import os
from pathlib import Path
from components.datagrid.DataGrid import reset_instances
from playwright_config import BROWSER_CONFIG, BASE_URL
USER_EMAIL = "test@mail.com"
USER_ID = "test_user"
APP_PORT = 5002
@pytest.fixture(scope="session")
def app_server():
"""Start the application server for end-to-end tests"""
# Use the same Python executable that's running pytest
python_executable = sys.executable
# Get the absolute path to the src directory
project_root = Path(__file__).parent.parent
src_path = project_root / "src"
# Start the application server
print(f"Starting server on url {BASE_URL}...")
port = BASE_URL.split(':')[-1].split('/')[0] if ':' in BASE_URL else APP_PORT
print(f"Using port {port}")
server_process = subprocess.Popen(
[python_executable, "main.py", "--port", "5002"],
cwd=str(src_path), # Change to src directory where main.py is located
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=os.environ.copy() # Inherit environment variables
)
# Wait for the server to start
max_retries = 10 # Wait up to 30 seconds
for i in range(max_retries):
try:
print(f"Waiting retry {i}/{max_retries}")
response = requests.get(BASE_URL, timeout=1)
if response.status_code in [200, 302, 404]: # Server is responding
print(f"Server started successfully after {i + 1} attempts")
break
except requests.exceptions.RequestException:
time.sleep(1)
else:
# If we get here, the server didn't start in time
print(f"Failed to start after {max_retries} attempts.")
server_process.kill()
stdout, stderr = server_process.communicate()
raise RuntimeError(
f"Server failed to start within {max_retries} seconds.\n"
f"STDOUT: {stdout.decode()}\n"
f"STDERR: {stderr.decode()}"
)
# Yield control to the tests
print('Server started !')
yield server_process
# Cleanup: terminate the server after tests
server_process.terminate()
try:
server_process.wait(timeout=5)
print('Server stopped.')
except subprocess.TimeoutExpired:
server_process.kill()
server_process.wait()
print('Server killed !')
@pytest.fixture(scope="session")
def browser_context_args(browser_context_args):
"""Configure browser context arguments"""
return {
**browser_context_args,
**BROWSER_CONFIG,
"record_video_dir": "test-results/videos/",
"record_har_path": "test-results/har/trace.har",
}
@pytest.fixture(scope="session")
def app_url():
"""Base URL for the application"""
return BASE_URL
@pytest.fixture

View File

@@ -0,0 +1,28 @@
import os
from playwright.sync_api import Playwright
# Playwright configuration
BASE_URL = os.getenv("APP_URL", "http://localhost:5002")
TIMEOUT = 30000
EXPECT_TIMEOUT = 5000
def pytest_configure():
"""Configure pytest for Playwright"""
pass
# Browser configuration
BROWSER_CONFIG = {
#"headless": os.getenv("HEADLESS", "true").lower() == "true",
"viewport": {"width": 1280, "height": 720},
"ignore_https_errors": True,
}
# Test configuration
TEST_CONFIG = {
"base_url": BASE_URL,
"timeout": TIMEOUT,
"expect_timeout": EXPECT_TIMEOUT,
"screenshot": "only-on-failure",
"video": "retain-on-failure",
"trace": "retain-on-failure",
}

View File

@@ -0,0 +1,103 @@
# 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", etc.
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, 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()
@pytest.mark.e2e
def test_page_loads_without_errors(self, 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")