- Add tests/ directory to .gitignore - Document database connectivity with pyodbc and config - Add project structure and file organization guidelines - Update installation steps to use requirements.txt - Document new dependencies: pandas, openpyxl, pyodbc
5.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is a Python automation project using Playwright to interact with a Chinese ERP system (YonBIP/用友). The scripts automate login, navigation, form filling, and data submission workflows in a nested iframe environment.
Environment Setup
# Activate the virtual environment (Windows)
.venv\Scripts\activate
# Install all dependencies
pip install -r requirements.txt
# Install Playwright browsers
playwright install chromium
Running Scripts
# Run the main test script
python tests/test_playwright.py
# Run the record script
python record.py
# Run database query test
python tests/test_db_query.py
Key Dependencies
- playwright==1.57.0: Browser automation framework
- pyodbc: SQL Server database connectivity
- pandas: Data processing and Excel file handling
- openpyxl: Excel file operations
- Uses synchronous API (
playwright.sync_api)
Project Structure
playwrite/
├── config/ # Configuration files (database, etc.)
├── db/ # Database connection components
├── data/ # Data files (excluded from git)
├── docs/ # Documentation (e.g., DEPENDENCIES.md)
├── tests/ # Test scripts (excluded from git)
├── requirements.txt # Python dependencies
├── .gitignore # Git ignore rules
└── CLAUDE.md # This file
File Organization Rules
IMPORTANT: When creating test scripts, always place them in the tests/ folder:
- Test scripts should be prefixed with
test_ - All files in
tests/are excluded from git tracking - Examples:
tests/test_playwright.py,tests/test_db_query.py
Configuration Management:
- Place configuration files in
config/folder - Database credentials and settings go in
config/database_config.py
Data Storage:
- Use
data/folder for temporary data files - This folder is excluded from git tracking
Architecture
Nested Iframe Structure
The target application uses deeply nested iframes that require careful handling:
page (browser context)
└── #forwardFrame (main iframe)
└── #mainiframe (inner iframe, contains actual application UI)
Critical Pattern: When accessing nested iframes, always wait for the inner iframe to be visible before extracting its content frame:
outer_frame = page.locator("#forwardFrame").content_frame
inner_frame_locator = outer_frame.locator("#mainiframe")
inner_frame_locator.wait_for(state="visible", timeout=15000)
inner_frame = inner_frame_locator.content_frame
Loading State Management
The application uses loading overlays ("加载中") that must be detected and waited for:
# Detect and wait for loading to complete
loading_locator = frame.locator("div").filter(has_text="加载中").nth(1)
try:
loading_locator.wait_for(state="visible", timeout=3000)
loading_locator.wait_for(state="hidden", timeout=0) # Infinite wait
except TimeoutError:
# Loading completed quickly or never appeared
pass
Page Navigation Pattern
New windows/popups are handled using expect_popup():
with page.expect_popup() as popup_info:
some_button.click()
new_page = popup_info.value
Database Connection
The project uses pyodbc to connect to SQL Server for data queries:
from db.connection import get_connection
# Using context manager
with get_connection() as db:
results = db.execute_query("SELECT * FROM table")
# Connection automatically closed
# Using the query helper
from db.connection import query_production_orders
results = query_production_orders(['ID1', 'ID2', 'ID3'])
Database configuration is stored in config/database_config.py:
SQL_SERVER_CONFIG = {
'driver': 'ODBC Driver 18 for SQL Server',
'server': '192.168.110.114',
'database': 'CompanyDB',
'username': 'peng',
'password': 'Cqbld123456.',
'TrustServerCertificate': 'yes'
}
Common Utilities
get_input_by_label(frame, label_text)
Locates input fields by their associated label text. Searches upward through parent containers to find the input:
input_box = get_input_by_label(frame, "生产部门")
if input_box:
current_value = input_box.input_value()
input_box.fill("new value")
click_button_until_disappear(frame, button_name, max_attempts, max_duration)
Continuously clicks a button until it disappears from the DOM. Includes logic to auto-fill missing data before each click.
Development Notes
- Browser launch:
chromium.launch(headless=False)for debugging - HTTPS errors: Ignored with
ignore_https_errors=Truedue to self-signed certificates on target system - Locators: Uses role-based locators (
get_by_role()) where possible, falling back to CSS selectors and text filtering - Text matching: Uses regular expressions for matching text with patterns like
re.compile(r"^生产部门$") - Date format: "YYYY-MM-DD" (e.g., "2025-12-28")
- Language: Application is in Chinese; comments and print statements use Chinese for clarity
- Debugging: Uses
pdb.set_trace()for interactive debugging sessions
Target Application Details
- URL:
https://68.11.34.30:8082/(internal network) - System: YonBIP (用友) ERP system
- Login: Requires username and password; may show "force login" confirmation dialog
- Key workflows:
- "补货安排" (Replenishment Arrangement)
- "生产订单" (Production Orders)
- Batch data submission with "保存提交" (Save & Submit) button