diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e0811f4 --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +# Playwright 浏览器路径 +PLAYWRIGHT_BROWSERS_PATH=C:\Users\Administrator\AppData\Roaming\erpauto\ms-playwright + +# 用友BIP登录配置 +BIP_URL=https://68.11.34.30:8082/yonbip/resources/uap/rbac/login/main/index.html +BIP_USERNAME=your_username +BIP_PASSWORD=your_password diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0d2473f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +playwright>=1.40.0 +python-dotenv>=1.0.0 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..2c62db7 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,3 @@ +""" +测试模块 +""" diff --git a/tests/test_auth_config.py b/tests/test_auth_config.py new file mode 100644 index 0000000..c053074 --- /dev/null +++ b/tests/test_auth_config.py @@ -0,0 +1,44 @@ +""" +Test if auth module configuration is correct +""" + +from playwright.sync_api import sync_playwright +from dotenv import load_dotenv +from pathlib import Path +import os + +# Load environment variables +PROJECT_ROOT = Path(__file__).resolve().parent.parent +load_dotenv(PROJECT_ROOT / ".env") + +# Configure browser path +os.environ["PLAYWRIGHT_BROWSERS_PATH"] = os.getenv("PLAYWRIGHT_BROWSERS_PATH") + +print("=" * 50) +print("ERP System Configuration Check") +print("=" * 50) + +# Display configuration information +print(f"Browser Path: {os.getenv('PLAYWRIGHT_BROWSERS_PATH')}") +print(f"ERP URL: {os.getenv('ERP_URL')}") +print(f"Username: {os.getenv('ERP_USERNAME')}") +print(f"Password: {'*' * len(os.getenv('ERP_PASSWORD', ''))}") +print(f"Headless Mode: {os.getenv('ERP_HEADLESS')}") +print(f"Ignore HTTPS Errors: {os.getenv('ERP_IGNORE_HTTPS_ERRORS')}") +print(f"Auto Close Browser: {os.getenv('ERP_AUTO_CLOSE_BROWSER')}") + +print("\n" + "=" * 50) +print("Check Playwright Browser") +print("=" * 50) + +with sync_playwright() as p: + chromium_path = p.chromium.executable_path + print(f"Chromium Path: {chromium_path}") + + # Check if browser file exists + if os.path.exists(chromium_path): + print("[OK] Browser file exists") + else: + print("[ERROR] Browser file not found") + +print("\n[OK] Configuration check completed!") diff --git a/tests/test_login.py b/tests/test_login.py new file mode 100644 index 0000000..9b58cca --- /dev/null +++ b/tests/test_login.py @@ -0,0 +1,79 @@ +""" +Test Yonyou BIP system login functionality +""" + +import sys +from pathlib import Path + +# Add project root to Python path +PROJECT_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +from playwright.sync_api import sync_playwright +from utils.auth import login, logout, close_session +import os + +# Load environment variables +from dotenv import load_dotenv +load_dotenv(PROJECT_ROOT / ".env") + +# Configure browser path +os.environ["PLAYWRIGHT_BROWSERS_PATH"] = os.getenv("PLAYWRIGHT_BROWSERS_PATH") + +print("=" * 60) +print("Testing Yonyou BIP Login and Logout") +print("=" * 60) + +# Display configuration information +print(f"\nConfiguration:") +print(f" URL: {os.getenv('ERP_URL')}") +print(f" Username: {os.getenv('ERP_USERNAME')}") +print(f" Headless: {os.getenv('ERP_HEADLESS')}") +print(f" Ignore HTTPS Errors: {os.getenv('ERP_IGNORE_HTTPS_ERRORS')}") + +try: + with sync_playwright() as p: + print("\n[1/5] Starting browser...") + browser, context, page, main_frame = login( + playwright=p, + verbose=True + ) + + print("\n[2/5] Login successful!") + + # Wait a moment to observe the page after login + print("\n[3/5] Waiting 3 seconds to observe the page after login...") + import time + time.sleep(3) + + # Test logout + print("\n[4/5] Testing logout...") + logout(main_frame, verbose=True) + + print("\n[5/5] Logout successful!") + + # Wait a moment to observe the page after logout + print("\nWaiting 2 seconds to observe the page after logout...") + time.sleep(2) + + print("\n[SUCCESS] Login and logout test completed successfully!") + + # Check if auto-close browser + auto_close = os.getenv("ERP_AUTO_CLOSE_BROWSER", "true").lower() in ("true", "1", "yes") + if not auto_close: + print("\n[INFO] Browser will stay open for 10 seconds for manual observation...") + time.sleep(10) + + # Close browser + print("\n[CLEANUP] Closing browser session...") + close_session(browser, context) + +except Exception as e: + print(f"\n[ERROR] Login/logout test failed!") + print(f"Error: {e}") + import traceback + traceback.print_exc() + +print("\n" + "=" * 60) +print("Test completed") +print("=" * 60) diff --git a/utils/auth.py b/utils/auth.py new file mode 100644 index 0000000..74a2bba --- /dev/null +++ b/utils/auth.py @@ -0,0 +1,176 @@ +""" +Authentication module - Responsible for Yonyou BIP system login and logout operations +""" + +import os +from pathlib import Path +from playwright.sync_api import Playwright, Browser, BrowserContext, Page, Frame + +# Load environment variables +from dotenv import load_dotenv + +# Get project root directory +BASE_DIR = Path(__file__).resolve().parent.parent + +# Load .env file +env_path = BASE_DIR / ".env" +load_dotenv(env_path) + +# Configure Playwright browser path +browsers_path = os.getenv("PLAYWRIGHT_BROWSERS_PATH") +if browsers_path: + os.environ["PLAYWRIGHT_BROWSERS_PATH"] = browsers_path + + +def login( + playwright: Playwright, + username: str = None, + password: str = None, + url: str = None, + headless: bool = None, + ignore_https_errors: bool = None, + verbose: bool = True, +) -> tuple[Browser, BrowserContext, Page, Frame]: + """ + Login to Yonyou BIP system + + Args: + playwright: Playwright instance + username: Username (default read from environment variable ERP_USERNAME) + password: Password (default read from environment variable ERP_PASSWORD) + url: Login page URL (default read from environment variable ERP_URL) + headless: Whether to use headless mode (default read from environment variable ERP_HEADLESS) + ignore_https_errors: Whether to ignore HTTPS errors (default read from environment variable ERP_IGNORE_HTTPS_ERRORS) + verbose: Whether to print detailed logs + + Returns: + tuple: (browser, context, page, main_frame) + - browser: Browser instance + - context: Browser context + - page: Page object + - main_frame: Main iframe after login (forwardFrame) + """ + import time + + # Read default configuration from environment variables + username = username or os.getenv("ERP_USERNAME") + password = password or os.getenv("ERP_PASSWORD") + + # URL handling: read from environment variable or parameter + if not url: + url = os.getenv("ERP_URL") + if url and not url.endswith("login/main/index.html"): + url = url.rstrip("/") + "/yonbip/resources/uap/rbac/login/main/index.html" + + if not url: + raise ValueError("URL must be provided either as parameter or through ERP_URL environment variable") + + # headless parameter handling + if headless is None: + headless_str = os.getenv("ERP_HEADLESS", "false").lower() + headless = headless_str in ("true", "1", "yes") + + # ignore_https_errors parameter handling + if ignore_https_errors is None: + ignore_https_errors_str = os.getenv("ERP_IGNORE_HTTPS_ERRORS", "true").lower() + ignore_https_errors = ignore_https_errors_str in ("true", "1", "yes") + + # Launch browser + browser = playwright.chromium.launch(headless=headless) + + # Create context + context = browser.new_context(ignore_https_errors=ignore_https_errors) + + # Create page + page = context.new_page() + + # Navigate to login page + page.goto(url) + + # Extract main iframe (forwardFrame) + main_frame = page.locator("#forwardFrame").content_frame + + # Fill username + main_frame.get_by_role("textbox", name="用户名").fill(username) + + # Fill password + main_frame.get_by_role("textbox", name="密码").fill(password) + + # Click login button + main_frame.get_by_role("button", name="登录").click() + + # Handle force login popup (check if exists) + confirm_btn = main_frame.get_by_role("button", name="确定") + if confirm_btn.count() > 0: + confirm_btn.click() + if verbose: + print("Force login detected") + else: + if verbose: + print("Normal login") + + return browser, context, page, main_frame + + +def logout(main_frame: Frame, verbose: bool = True) -> None: + """ + Execute account logout + + Args: + main_frame: Main iframe object (forwardFrame) + verbose: Whether to print detailed logs + """ + import time + + if verbose: + print("Clicking account menu button...") + + # Element 1: Account menu button (logo icon) + main_frame.get_by_role("img", name="logo").click() + + # Wait for menu to appear + time.sleep(1) + + if verbose: + print("Clicking logout button...") + + # Element 2: "Logout" button + main_frame.get_by_text("退出登录").click() + + # Wait for confirmation dialog to appear + time.sleep(1) + + if verbose: + print("Waiting for logout confirmation dialog...") + + # Element 3: Logout confirmation dialog (check if appears) + try: + confirm_text = main_frame.get_by_text("退出确定要退出当前账号吗?") + if verbose: + print("Found confirmation dialog, clicking confirm button") + + # Element 4: Confirm button + main_frame.get_by_role("button", name="确定(Y)").click() + except: + if verbose: + print("Confirmation dialog not found, may have auto-logged out") + + time.sleep(2) # Wait for logout to complete + + +def close_session(browser: Browser, context: BrowserContext) -> None: + """ + Close browser session + + Args: + browser: Browser instance + context: Browser context + """ + # Check if auto-close browser is enabled + auto_close = os.getenv("ERP_AUTO_CLOSE_BROWSER", "true").lower() in ("true", "1", "yes") + + if auto_close: + context.close() + browser.close() + else: + print("Note: Browser not auto-closed (ERP_AUTO_CLOSE_BROWSER=false)")