claude-mpm 4.15.6__py3-none-any.whl → 4.21.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +272 -23
- claude_mpm/agents/PM_INSTRUCTIONS.md +49 -0
- claude_mpm/agents/agent_loader.py +4 -4
- claude_mpm/agents/templates/engineer.json +5 -1
- claude_mpm/agents/templates/php-engineer.json +10 -4
- claude_mpm/agents/templates/python_engineer.json +8 -3
- claude_mpm/agents/templates/rust_engineer.json +12 -7
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
- claude_mpm/cli/commands/mpm_init/core.py +525 -0
- claude_mpm/cli/commands/mpm_init/display.py +341 -0
- claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
- claude_mpm/cli/commands/mpm_init/modes.py +397 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
- claude_mpm/cli/commands/mpm_init_cli.py +396 -0
- claude_mpm/cli/commands/mpm_init_handler.py +67 -1
- claude_mpm/cli/commands/skills.py +488 -0
- claude_mpm/cli/executor.py +2 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +42 -0
- claude_mpm/cli/parsers/skills_parser.py +137 -0
- claude_mpm/cli/startup.py +57 -0
- claude_mpm/commands/mpm-auto-configure.md +52 -0
- claude_mpm/commands/mpm-help.md +6 -0
- claude_mpm/commands/mpm-init.md +112 -6
- claude_mpm/commands/mpm-resume.md +372 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +2 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/constants.py +12 -0
- claude_mpm/core/config.py +42 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/interfaces.py +56 -1
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/hooks/__init__.py +8 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/session_resume_hook.py +121 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/recommender.py +47 -0
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +87 -0
- claude_mpm/services/cli/session_pause_manager.py +504 -0
- claude_mpm/services/cli/session_resume_helper.py +372 -0
- claude_mpm/services/core/base.py +26 -11
- claude_mpm/services/core/interfaces.py +56 -1
- claude_mpm/services/core/models/agent_config.py +3 -0
- claude_mpm/services/core/models/process.py +4 -0
- claude_mpm/services/core/path_resolver.py +1 -1
- claude_mpm/services/diagnostics/models.py +21 -0
- claude_mpm/services/event_bus/relay.py +23 -7
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +2 -0
- claude_mpm/services/mcp_config_manager.py +7 -131
- claude_mpm/services/mcp_gateway/auto_configure.py +31 -25
- claude_mpm/services/mcp_gateway/core/process_pool.py +19 -10
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +26 -21
- claude_mpm/services/memory/failure_tracker.py +19 -4
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/unified/deployment_strategies/local.py +1 -1
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/agent_skills_injector.py +324 -0
- claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +97 -9
- claude_mpm/skills/skills_registry.py +348 -0
- claude_mpm/skills/skills_service.py +739 -0
- claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
- claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
- claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
- claude_mpm/tools/code_tree_analyzer/core.py +380 -0
- claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
- claude_mpm/tools/code_tree_analyzer/events.py +168 -0
- claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
- claude_mpm/tools/code_tree_analyzer/models.py +39 -0
- claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
- claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/METADATA +211 -33
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/RECORD +206 -64
- claude_mpm/agents/INSTRUCTIONS_OLD_DEPRECATED.md +0 -602
- claude_mpm/cli/commands/mpm_init.py +0 -2008
- claude_mpm/tools/code_tree_analyzer.py +0 -1825
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from playwright.sync_api import sync_playwright
|
|
2
|
+
|
|
3
|
+
# Example: Discovering buttons and other elements on a page
|
|
4
|
+
|
|
5
|
+
with sync_playwright() as p:
|
|
6
|
+
browser = p.chromium.launch(headless=True)
|
|
7
|
+
page = browser.new_page()
|
|
8
|
+
|
|
9
|
+
# Navigate to page and wait for it to fully load
|
|
10
|
+
page.goto("http://localhost:5173")
|
|
11
|
+
page.wait_for_load_state("networkidle")
|
|
12
|
+
|
|
13
|
+
# Discover all buttons on the page
|
|
14
|
+
buttons = page.locator("button").all()
|
|
15
|
+
print(f"Found {len(buttons)} buttons:")
|
|
16
|
+
for i, button in enumerate(buttons):
|
|
17
|
+
text = button.inner_text() if button.is_visible() else "[hidden]"
|
|
18
|
+
print(f" [{i}] {text}")
|
|
19
|
+
|
|
20
|
+
# Discover links
|
|
21
|
+
links = page.locator("a[href]").all()
|
|
22
|
+
print(f"\nFound {len(links)} links:")
|
|
23
|
+
for link in links[:5]: # Show first 5
|
|
24
|
+
text = link.inner_text().strip()
|
|
25
|
+
href = link.get_attribute("href")
|
|
26
|
+
print(f" - {text} -> {href}")
|
|
27
|
+
|
|
28
|
+
# Discover input fields
|
|
29
|
+
inputs = page.locator("input, textarea, select").all()
|
|
30
|
+
print(f"\nFound {len(inputs)} input fields:")
|
|
31
|
+
for input_elem in inputs:
|
|
32
|
+
name = (
|
|
33
|
+
input_elem.get_attribute("name")
|
|
34
|
+
or input_elem.get_attribute("id")
|
|
35
|
+
or "[unnamed]"
|
|
36
|
+
)
|
|
37
|
+
input_type = input_elem.get_attribute("type") or "text"
|
|
38
|
+
print(f" - {name} ({input_type})")
|
|
39
|
+
|
|
40
|
+
# Take screenshot for visual reference
|
|
41
|
+
page.screenshot(path="/tmp/page_discovery.png", full_page=True)
|
|
42
|
+
print("\nScreenshot saved to /tmp/page_discovery.png")
|
|
43
|
+
|
|
44
|
+
browser.close()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from playwright.sync_api import sync_playwright
|
|
4
|
+
|
|
5
|
+
# Example: Automating interaction with static HTML files using file:// URLs
|
|
6
|
+
|
|
7
|
+
html_file_path = os.path.abspath("path/to/your/file.html")
|
|
8
|
+
file_url = f"file://{html_file_path}"
|
|
9
|
+
|
|
10
|
+
with sync_playwright() as p:
|
|
11
|
+
browser = p.chromium.launch(headless=True)
|
|
12
|
+
page = browser.new_page(viewport={"width": 1920, "height": 1080})
|
|
13
|
+
|
|
14
|
+
# Navigate to local HTML file
|
|
15
|
+
page.goto(file_url)
|
|
16
|
+
|
|
17
|
+
# Take screenshot
|
|
18
|
+
page.screenshot(path="/mnt/user-data/outputs/static_page.png", full_page=True)
|
|
19
|
+
|
|
20
|
+
# Interact with elements
|
|
21
|
+
page.click("text=Click Me")
|
|
22
|
+
page.fill("#name", "John Doe")
|
|
23
|
+
page.fill("#email", "john@example.com")
|
|
24
|
+
|
|
25
|
+
# Submit form
|
|
26
|
+
page.click('button[type="submit"]')
|
|
27
|
+
page.wait_for_timeout(500)
|
|
28
|
+
|
|
29
|
+
# Take final screenshot
|
|
30
|
+
page.screenshot(path="/mnt/user-data/outputs/after_submit.png", full_page=True)
|
|
31
|
+
|
|
32
|
+
browser.close()
|
|
33
|
+
|
|
34
|
+
print("Static HTML automation completed!")
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
# Playwright Patterns Reference
|
|
2
|
+
|
|
3
|
+
Complete guide to Playwright automation patterns, selectors, and best practices.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Selectors](#selectors)
|
|
8
|
+
- [Wait Strategies](#wait-strategies)
|
|
9
|
+
- [Element Interactions](#element-interactions)
|
|
10
|
+
- [Assertions](#assertions)
|
|
11
|
+
- [Test Organization](#test-organization)
|
|
12
|
+
- [Network Interception](#network-interception)
|
|
13
|
+
- [Screenshots and Videos](#screenshots-and-videos)
|
|
14
|
+
- [Debugging](#debugging)
|
|
15
|
+
- [Parallel Execution](#parallel-execution)
|
|
16
|
+
|
|
17
|
+
## Selectors
|
|
18
|
+
|
|
19
|
+
### Text Selectors
|
|
20
|
+
Most readable and maintainable approach when text is unique:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
page.click('text=Login')
|
|
24
|
+
page.click('text="Sign Up"') # Exact match
|
|
25
|
+
page.click('text=/log.*in/i') # Regex, case-insensitive
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Role-Based Selectors
|
|
29
|
+
Semantic selectors based on ARIA roles:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
page.click('role=button[name="Submit"]')
|
|
33
|
+
page.fill('role=textbox[name="Email"]', 'user@example.com')
|
|
34
|
+
page.click('role=link[name="Learn more"]')
|
|
35
|
+
page.check('role=checkbox[name="Accept terms"]')
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### CSS Selectors
|
|
39
|
+
Traditional CSS selectors for precise targeting:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
page.click('#submit-button')
|
|
43
|
+
page.fill('.email-input', 'user@example.com')
|
|
44
|
+
page.click('button.primary')
|
|
45
|
+
page.click('nav > ul > li:first-child')
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### XPath Selectors
|
|
49
|
+
For complex DOM navigation:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
page.click('xpath=//button[contains(text(), "Submit")]')
|
|
53
|
+
page.click('xpath=//div[@class="modal"]//button[@type="submit"]')
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Data Attributes
|
|
57
|
+
Best practice for test-specific selectors:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
page.click('[data-testid="submit-btn"]')
|
|
61
|
+
page.fill('[data-test="email-input"]', 'test@example.com')
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Chaining Selectors
|
|
65
|
+
Combine selectors for precision:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
page.locator('div.modal').locator('button.submit').click()
|
|
69
|
+
page.locator('role=dialog').locator('text=Confirm').click()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Selector Best Practices
|
|
73
|
+
|
|
74
|
+
**Priority order (most stable to least stable):**
|
|
75
|
+
1. `data-testid` attributes (most stable)
|
|
76
|
+
2. `role=` selectors (semantic, accessible)
|
|
77
|
+
3. `text=` selectors (readable, but text may change)
|
|
78
|
+
4. `id` attributes (stable if not dynamic)
|
|
79
|
+
5. CSS classes (less stable, may change with styling)
|
|
80
|
+
6. XPath (fragile, avoid if possible)
|
|
81
|
+
|
|
82
|
+
## Wait Strategies
|
|
83
|
+
|
|
84
|
+
### Load State Waits
|
|
85
|
+
Essential for dynamic applications:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
# Wait for network to be idle (most common)
|
|
89
|
+
page.goto('http://localhost:3000')
|
|
90
|
+
page.wait_for_load_state('networkidle')
|
|
91
|
+
|
|
92
|
+
# Wait for DOM to be ready
|
|
93
|
+
page.wait_for_load_state('domcontentloaded')
|
|
94
|
+
|
|
95
|
+
# Wait for full load including images
|
|
96
|
+
page.wait_for_load_state('load')
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Element Waits
|
|
100
|
+
Wait for specific elements before interacting:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Wait for element to be visible
|
|
104
|
+
page.wait_for_selector('button.submit', state='visible')
|
|
105
|
+
|
|
106
|
+
# Wait for element to be hidden
|
|
107
|
+
page.wait_for_selector('.loading-spinner', state='hidden')
|
|
108
|
+
|
|
109
|
+
# Wait for element to exist in DOM (may not be visible)
|
|
110
|
+
page.wait_for_selector('.modal', state='attached')
|
|
111
|
+
|
|
112
|
+
# Wait for element to be removed from DOM
|
|
113
|
+
page.wait_for_selector('.error-message', state='detached')
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Timeout Waits
|
|
117
|
+
Fixed time delays (use sparingly):
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# Wait for animations to complete
|
|
121
|
+
page.wait_for_timeout(500)
|
|
122
|
+
|
|
123
|
+
# Wait for delayed content (better to use wait_for_selector)
|
|
124
|
+
page.wait_for_timeout(2000)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Custom Wait Conditions
|
|
128
|
+
Wait for JavaScript conditions:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
# Wait for custom JavaScript condition
|
|
132
|
+
page.wait_for_function('() => document.querySelector(".data").innerText !== "Loading..."')
|
|
133
|
+
|
|
134
|
+
# Wait for variable to be set
|
|
135
|
+
page.wait_for_function('() => window.appReady === true')
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Auto-Waiting
|
|
139
|
+
Playwright automatically waits for elements to be actionable:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# These automatically wait for element to be:
|
|
143
|
+
# - Visible
|
|
144
|
+
# - Stable (not animating)
|
|
145
|
+
# - Enabled (not disabled)
|
|
146
|
+
# - Not obscured by other elements
|
|
147
|
+
page.click('button.submit') # Auto-waits
|
|
148
|
+
page.fill('input.email', 'test@example.com') # Auto-waits
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Element Interactions
|
|
152
|
+
|
|
153
|
+
### Clicking
|
|
154
|
+
```python
|
|
155
|
+
# Basic click
|
|
156
|
+
page.click('button.submit')
|
|
157
|
+
|
|
158
|
+
# Click with options
|
|
159
|
+
page.click('button.submit', button='right') # Right-click
|
|
160
|
+
page.click('button.submit', click_count=2) # Double-click
|
|
161
|
+
page.click('button.submit', modifiers=['Control']) # Ctrl+click
|
|
162
|
+
|
|
163
|
+
# Force click (bypass actionability checks)
|
|
164
|
+
page.click('button.submit', force=True)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Filling Forms
|
|
168
|
+
```python
|
|
169
|
+
# Text inputs
|
|
170
|
+
page.fill('input[name="email"]', 'user@example.com')
|
|
171
|
+
page.type('input[name="search"]', 'query', delay=100) # Type with delay
|
|
172
|
+
|
|
173
|
+
# Clear then fill
|
|
174
|
+
page.fill('input[name="email"]', '')
|
|
175
|
+
page.fill('input[name="email"]', 'new@example.com')
|
|
176
|
+
|
|
177
|
+
# Press keys
|
|
178
|
+
page.press('input[name="search"]', 'Enter')
|
|
179
|
+
page.press('input[name="text"]', 'Control+A')
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Dropdowns and Selects
|
|
183
|
+
```python
|
|
184
|
+
# Select by label
|
|
185
|
+
page.select_option('select[name="country"]', label='United States')
|
|
186
|
+
|
|
187
|
+
# Select by value
|
|
188
|
+
page.select_option('select[name="country"]', value='us')
|
|
189
|
+
|
|
190
|
+
# Select by index
|
|
191
|
+
page.select_option('select[name="country"]', index=2)
|
|
192
|
+
|
|
193
|
+
# Select multiple options
|
|
194
|
+
page.select_option('select[multiple]', ['option1', 'option2'])
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Checkboxes and Radio Buttons
|
|
198
|
+
```python
|
|
199
|
+
# Check a checkbox
|
|
200
|
+
page.check('input[type="checkbox"]')
|
|
201
|
+
|
|
202
|
+
# Uncheck a checkbox
|
|
203
|
+
page.uncheck('input[type="checkbox"]')
|
|
204
|
+
|
|
205
|
+
# Check a radio button
|
|
206
|
+
page.check('input[value="option1"]')
|
|
207
|
+
|
|
208
|
+
# Toggle checkbox
|
|
209
|
+
if page.is_checked('input[type="checkbox"]'):
|
|
210
|
+
page.uncheck('input[type="checkbox"]')
|
|
211
|
+
else:
|
|
212
|
+
page.check('input[type="checkbox"]')
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### File Uploads
|
|
216
|
+
```python
|
|
217
|
+
# Upload single file
|
|
218
|
+
page.set_input_files('input[type="file"]', '/path/to/file.pdf')
|
|
219
|
+
|
|
220
|
+
# Upload multiple files
|
|
221
|
+
page.set_input_files('input[type="file"]', ['/path/to/file1.pdf', '/path/to/file2.pdf'])
|
|
222
|
+
|
|
223
|
+
# Clear file input
|
|
224
|
+
page.set_input_files('input[type="file"]', [])
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Hover and Focus
|
|
228
|
+
```python
|
|
229
|
+
# Hover over element
|
|
230
|
+
page.hover('button.tooltip-trigger')
|
|
231
|
+
|
|
232
|
+
# Focus element
|
|
233
|
+
page.focus('input[name="email"]')
|
|
234
|
+
|
|
235
|
+
# Blur element
|
|
236
|
+
page.evaluate('document.activeElement.blur()')
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Assertions
|
|
240
|
+
|
|
241
|
+
### Element Visibility
|
|
242
|
+
```python
|
|
243
|
+
from playwright.sync_api import expect
|
|
244
|
+
|
|
245
|
+
# Expect element to be visible
|
|
246
|
+
expect(page.locator('button.submit')).to_be_visible()
|
|
247
|
+
|
|
248
|
+
# Expect element to be hidden
|
|
249
|
+
expect(page.locator('.error-message')).to_be_hidden()
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Text Content
|
|
253
|
+
```python
|
|
254
|
+
# Expect exact text
|
|
255
|
+
expect(page.locator('.title')).to_have_text('Welcome')
|
|
256
|
+
|
|
257
|
+
# Expect partial text
|
|
258
|
+
expect(page.locator('.message')).to_contain_text('success')
|
|
259
|
+
|
|
260
|
+
# Expect text matching pattern
|
|
261
|
+
expect(page.locator('.code')).to_have_text(re.compile(r'\d{6}'))
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Element State
|
|
265
|
+
```python
|
|
266
|
+
# Expect element to be enabled/disabled
|
|
267
|
+
expect(page.locator('button.submit')).to_be_enabled()
|
|
268
|
+
expect(page.locator('button.submit')).to_be_disabled()
|
|
269
|
+
|
|
270
|
+
# Expect checkbox to be checked
|
|
271
|
+
expect(page.locator('input[type="checkbox"]')).to_be_checked()
|
|
272
|
+
|
|
273
|
+
# Expect element to be editable
|
|
274
|
+
expect(page.locator('input[name="email"]')).to_be_editable()
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Attributes and Values
|
|
278
|
+
```python
|
|
279
|
+
# Expect attribute value
|
|
280
|
+
expect(page.locator('img')).to_have_attribute('src', '/logo.png')
|
|
281
|
+
|
|
282
|
+
# Expect CSS class
|
|
283
|
+
expect(page.locator('button')).to_have_class('btn-primary')
|
|
284
|
+
|
|
285
|
+
# Expect input value
|
|
286
|
+
expect(page.locator('input[name="email"]')).to_have_value('user@example.com')
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Count and Collections
|
|
290
|
+
```python
|
|
291
|
+
# Expect specific count
|
|
292
|
+
expect(page.locator('li')).to_have_count(5)
|
|
293
|
+
|
|
294
|
+
# Get all elements and assert
|
|
295
|
+
items = page.locator('li').all()
|
|
296
|
+
assert len(items) == 5
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Test Organization
|
|
300
|
+
|
|
301
|
+
### Basic Test Structure
|
|
302
|
+
```python
|
|
303
|
+
from playwright.sync_api import sync_playwright
|
|
304
|
+
|
|
305
|
+
with sync_playwright() as p:
|
|
306
|
+
browser = p.chromium.launch(headless=True)
|
|
307
|
+
page = browser.new_page()
|
|
308
|
+
|
|
309
|
+
# Test logic here
|
|
310
|
+
page.goto('http://localhost:3000')
|
|
311
|
+
page.wait_for_load_state('networkidle')
|
|
312
|
+
|
|
313
|
+
browser.close()
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Using Pytest (Recommended)
|
|
317
|
+
```python
|
|
318
|
+
import pytest
|
|
319
|
+
from playwright.sync_api import sync_playwright
|
|
320
|
+
|
|
321
|
+
@pytest.fixture(scope="session")
|
|
322
|
+
def browser():
|
|
323
|
+
with sync_playwright() as p:
|
|
324
|
+
browser = p.chromium.launch(headless=True)
|
|
325
|
+
yield browser
|
|
326
|
+
browser.close()
|
|
327
|
+
|
|
328
|
+
@pytest.fixture
|
|
329
|
+
def page(browser):
|
|
330
|
+
page = browser.new_page()
|
|
331
|
+
yield page
|
|
332
|
+
page.close()
|
|
333
|
+
|
|
334
|
+
def test_login(page):
|
|
335
|
+
page.goto('http://localhost:3000')
|
|
336
|
+
page.fill('input[name="email"]', 'user@example.com')
|
|
337
|
+
page.fill('input[name="password"]', 'password123')
|
|
338
|
+
page.click('button[type="submit"]')
|
|
339
|
+
expect(page.locator('.welcome-message')).to_be_visible()
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Test Grouping with Describe Blocks
|
|
343
|
+
```python
|
|
344
|
+
class TestAuthentication:
|
|
345
|
+
def test_successful_login(self, page):
|
|
346
|
+
# Test successful login
|
|
347
|
+
pass
|
|
348
|
+
|
|
349
|
+
def test_failed_login(self, page):
|
|
350
|
+
# Test failed login
|
|
351
|
+
pass
|
|
352
|
+
|
|
353
|
+
def test_logout(self, page):
|
|
354
|
+
# Test logout
|
|
355
|
+
pass
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Setup and Teardown
|
|
359
|
+
```python
|
|
360
|
+
@pytest.fixture(autouse=True)
|
|
361
|
+
def setup_and_teardown(page):
|
|
362
|
+
# Setup - runs before each test
|
|
363
|
+
page.goto('http://localhost:3000')
|
|
364
|
+
page.wait_for_load_state('networkidle')
|
|
365
|
+
|
|
366
|
+
yield # Test runs here
|
|
367
|
+
|
|
368
|
+
# Teardown - runs after each test
|
|
369
|
+
page.evaluate('localStorage.clear()')
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Network Interception
|
|
373
|
+
|
|
374
|
+
### Mock API Responses
|
|
375
|
+
```python
|
|
376
|
+
# Intercept and mock API response
|
|
377
|
+
def handle_route(route):
|
|
378
|
+
route.fulfill(
|
|
379
|
+
status=200,
|
|
380
|
+
body='{"success": true, "data": "mocked"}',
|
|
381
|
+
headers={'Content-Type': 'application/json'}
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
page.route('**/api/data', handle_route)
|
|
385
|
+
page.goto('http://localhost:3000')
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Block Resources
|
|
389
|
+
```python
|
|
390
|
+
# Block images and stylesheets for faster tests
|
|
391
|
+
page.route('**/*.{png,jpg,jpeg,gif,svg,css}', lambda route: route.abort())
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Wait for Network Responses
|
|
395
|
+
```python
|
|
396
|
+
# Wait for specific API call
|
|
397
|
+
with page.expect_response('**/api/users') as response_info:
|
|
398
|
+
page.click('button.load-users')
|
|
399
|
+
response = response_info.value
|
|
400
|
+
assert response.status == 200
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Screenshots and Videos
|
|
404
|
+
|
|
405
|
+
### Screenshots
|
|
406
|
+
```python
|
|
407
|
+
# Full page screenshot
|
|
408
|
+
page.screenshot(path='/tmp/screenshot.png', full_page=True)
|
|
409
|
+
|
|
410
|
+
# Element screenshot
|
|
411
|
+
page.locator('.modal').screenshot(path='/tmp/modal.png')
|
|
412
|
+
|
|
413
|
+
# Screenshot with custom dimensions
|
|
414
|
+
page.set_viewport_size({'width': 1920, 'height': 1080})
|
|
415
|
+
page.screenshot(path='/tmp/desktop.png')
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Video Recording
|
|
419
|
+
```python
|
|
420
|
+
browser = p.chromium.launch(headless=True)
|
|
421
|
+
context = browser.new_context(record_video_dir='/tmp/videos/')
|
|
422
|
+
page = context.new_page()
|
|
423
|
+
|
|
424
|
+
# Perform actions...
|
|
425
|
+
|
|
426
|
+
context.close() # Video saved on close
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Debugging
|
|
430
|
+
|
|
431
|
+
### Pause Execution
|
|
432
|
+
```python
|
|
433
|
+
page.pause() # Opens Playwright Inspector
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Console Logs
|
|
437
|
+
```python
|
|
438
|
+
def handle_console(msg):
|
|
439
|
+
print(f"[{msg.type}] {msg.text}")
|
|
440
|
+
|
|
441
|
+
page.on("console", handle_console)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Slow Motion
|
|
445
|
+
```python
|
|
446
|
+
browser = p.chromium.launch(headless=False, slow_mo=1000) # 1 second delay
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Verbose Logging
|
|
450
|
+
```python
|
|
451
|
+
# Set DEBUG environment variable
|
|
452
|
+
# DEBUG=pw:api python test.py
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Parallel Execution
|
|
456
|
+
|
|
457
|
+
### Pytest Parallel
|
|
458
|
+
```bash
|
|
459
|
+
# Install pytest-xdist
|
|
460
|
+
pip install pytest-xdist
|
|
461
|
+
|
|
462
|
+
# Run tests in parallel
|
|
463
|
+
pytest -n auto # Auto-detect CPU cores
|
|
464
|
+
pytest -n 4 # Run with 4 workers
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Browser Context Isolation
|
|
468
|
+
```python
|
|
469
|
+
# Each test gets isolated context (cookies, localStorage, etc.)
|
|
470
|
+
@pytest.fixture
|
|
471
|
+
def context(browser):
|
|
472
|
+
context = browser.new_context()
|
|
473
|
+
yield context
|
|
474
|
+
context.close()
|
|
475
|
+
|
|
476
|
+
@pytest.fixture
|
|
477
|
+
def page(context):
|
|
478
|
+
return context.new_page()
|
|
479
|
+
```
|