qa-skills 3.0.0
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.
- package/README.md +168 -0
- package/bin/cli.js +42 -0
- package/dist/agents/registry.d.ts +5 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +101 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/types.d.ts +9 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/dependencies.d.ts +21 -0
- package/dist/dependencies.d.ts.map +1 -0
- package/dist/dependencies.js +125 -0
- package/dist/dependencies.js.map +1 -0
- package/dist/installer.d.ts +25 -0
- package/dist/installer.d.ts.map +1 -0
- package/dist/installer.js +437 -0
- package/dist/installer.js.map +1 -0
- package/dist/scaffold.d.ts +27 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +182 -0
- package/dist/scaffold.js.map +1 -0
- package/package.json +40 -0
- package/skills/qa-accessibility-test-writer/SKILL.md +127 -0
- package/skills/qa-accessibility-test-writer/references/axe-core-patterns.md +349 -0
- package/skills/qa-accessibility-test-writer/references/best-practices.md +184 -0
- package/skills/qa-accessibility-test-writer/references/wcag-tests.md +331 -0
- package/skills/qa-api-contract-curator/SKILL.md +104 -0
- package/skills/qa-api-contract-curator/references/breaking-changes.md +363 -0
- package/skills/qa-api-contract-curator/references/openapi-structure.md +404 -0
- package/skills/qa-browser-data-collector/SKILL.md +132 -0
- package/skills/qa-browser-data-collector/references/data-collection-checklist.md +91 -0
- package/skills/qa-browser-data-collector/references/playwright-mcp-patterns.md +113 -0
- package/skills/qa-bug-ticket-creator/SKILL.md +148 -0
- package/skills/qa-bug-ticket-creator/references/bug-report-format.md +149 -0
- package/skills/qa-bug-ticket-creator/references/severity-guide.md +81 -0
- package/skills/qa-bug-ticket-creator/templates/bug-ticket-template.md +39 -0
- package/skills/qa-changelog-analyzer/SKILL.md +134 -0
- package/skills/qa-changelog-analyzer/references/git-analysis-patterns.md +138 -0
- package/skills/qa-changelog-analyzer/references/impact-mapping.md +120 -0
- package/skills/qa-clickup-integration/SKILL.md +166 -0
- package/skills/qa-clickup-integration/references/api-patterns.md +102 -0
- package/skills/qa-clickup-integration/references/field-mapping.md +71 -0
- package/skills/qa-codeceptjs-writer/SKILL.md +136 -0
- package/skills/qa-codeceptjs-writer/references/best-practices.md +207 -0
- package/skills/qa-codeceptjs-writer/references/config.md +255 -0
- package/skills/qa-codeceptjs-writer/references/patterns.md +285 -0
- package/skills/qa-coverage-analyzer/SKILL.md +166 -0
- package/skills/qa-coverage-analyzer/references/best-practices.md +142 -0
- package/skills/qa-coverage-analyzer/references/coverage-dimensions.md +155 -0
- package/skills/qa-coverage-analyzer/references/tools.md +204 -0
- package/skills/qa-cypress-writer/SKILL.md +134 -0
- package/skills/qa-cypress-writer/references/assertions.md +121 -0
- package/skills/qa-cypress-writer/references/best-practices.md +82 -0
- package/skills/qa-cypress-writer/references/config.md +121 -0
- package/skills/qa-cypress-writer/references/patterns.md +170 -0
- package/skills/qa-data-factory/SKILL.md +126 -0
- package/skills/qa-data-factory/references/factory-patterns.md +164 -0
- package/skills/qa-data-factory/references/faker-guide.md +131 -0
- package/skills/qa-diagram-generator/SKILL.md +125 -0
- package/skills/qa-diagram-generator/references/c4-model.md +53 -0
- package/skills/qa-diagram-generator/references/charts.md +58 -0
- package/skills/qa-diagram-generator/references/class-diagram.md +85 -0
- package/skills/qa-diagram-generator/references/er-diagram.md +69 -0
- package/skills/qa-diagram-generator/references/flowchart.md +92 -0
- package/skills/qa-diagram-generator/references/from-screenshot.md +45 -0
- package/skills/qa-diagram-generator/references/gantt.md +49 -0
- package/skills/qa-diagram-generator/references/journey.md +50 -0
- package/skills/qa-diagram-generator/references/mindmap.md +75 -0
- package/skills/qa-diagram-generator/references/sequence.md +69 -0
- package/skills/qa-diagram-generator/references/state-diagram.md +56 -0
- package/skills/qa-discovery-interview/SKILL.md +182 -0
- package/skills/qa-discovery-interview/references/completeness-checklist.md +53 -0
- package/skills/qa-discovery-interview/references/conflict-patterns.md +101 -0
- package/skills/qa-discovery-interview/references/qa-categories.md +147 -0
- package/skills/qa-discovery-interview/templates/qa-brief-template.md +168 -0
- package/skills/qa-environment-checker/SKILL.md +142 -0
- package/skills/qa-environment-checker/references/dependency-matrix.md +101 -0
- package/skills/qa-environment-checker/references/health-checks.md +209 -0
- package/skills/qa-environment-checker/templates/env-readiness-template.md +64 -0
- package/skills/qa-flaky-detector/SKILL.md +153 -0
- package/skills/qa-flaky-detector/references/ci-analysis.md +140 -0
- package/skills/qa-flaky-detector/references/flaky-patterns.md +247 -0
- package/skills/qa-github-issues-enhanced/SKILL.md +175 -0
- package/skills/qa-github-issues-enhanced/references/issue-templates.md +425 -0
- package/skills/qa-github-issues-enhanced/references/label-taxonomy.md +130 -0
- package/skills/qa-github-issues-enhanced/references/workflow-patterns.md +188 -0
- package/skills/qa-httpx-writer/SKILL.md +138 -0
- package/skills/qa-httpx-writer/references/assertions.md +195 -0
- package/skills/qa-httpx-writer/references/best-practices.md +140 -0
- package/skills/qa-httpx-writer/references/config.md +212 -0
- package/skills/qa-httpx-writer/references/patterns.md +262 -0
- package/skills/qa-jest-writer/SKILL.md +131 -0
- package/skills/qa-jest-writer/references/assertions.md +125 -0
- package/skills/qa-jest-writer/references/best-practices.md +136 -0
- package/skills/qa-jest-writer/references/config.md +134 -0
- package/skills/qa-jest-writer/references/patterns.md +172 -0
- package/skills/qa-jira-integration/SKILL.md +135 -0
- package/skills/qa-jira-integration/references/api-patterns.md +143 -0
- package/skills/qa-jira-integration/references/field-mapping.md +79 -0
- package/skills/qa-jira-integration/references/xray-integration.md +85 -0
- package/skills/qa-jmeter-writer/SKILL.md +171 -0
- package/skills/qa-jmeter-writer/references/best-practices.md +157 -0
- package/skills/qa-jmeter-writer/references/config.md +204 -0
- package/skills/qa-jmeter-writer/references/patterns.md +242 -0
- package/skills/qa-junit5-writer/SKILL.md +157 -0
- package/skills/qa-junit5-writer/references/assertions.md +118 -0
- package/skills/qa-junit5-writer/references/config.md +97 -0
- package/skills/qa-junit5-writer/references/patterns.md +162 -0
- package/skills/qa-k6-writer/SKILL.md +155 -0
- package/skills/qa-k6-writer/references/best-practices.md +236 -0
- package/skills/qa-k6-writer/references/config.md +219 -0
- package/skills/qa-k6-writer/references/patterns.md +304 -0
- package/skills/qa-linear-integration/SKILL.md +137 -0
- package/skills/qa-linear-integration/references/api-patterns.md +249 -0
- package/skills/qa-linear-integration/references/field-mapping.md +121 -0
- package/skills/qa-locust-writer/SKILL.md +151 -0
- package/skills/qa-locust-writer/references/best-practices.md +126 -0
- package/skills/qa-locust-writer/references/config.md +170 -0
- package/skills/qa-locust-writer/references/patterns.md +235 -0
- package/skills/qa-manual-test-designer/SKILL.md +145 -0
- package/skills/qa-manual-test-designer/references/exploratory-charters.md +138 -0
- package/skills/qa-manual-test-designer/references/personas.md +146 -0
- package/skills/qa-manual-test-designer/templates/exploratory-charter-template.md +47 -0
- package/skills/qa-manual-test-designer/templates/test-case-template.md +31 -0
- package/skills/qa-mobile-test-writer/SKILL.md +144 -0
- package/skills/qa-mobile-test-writer/references/best-practices.md +214 -0
- package/skills/qa-mobile-test-writer/references/config.md +309 -0
- package/skills/qa-mobile-test-writer/references/patterns.md +304 -0
- package/skills/qa-nfr-analyst/SKILL.md +177 -0
- package/skills/qa-nfr-analyst/references/iso-25010-model.md +159 -0
- package/skills/qa-nfr-analyst/references/owasp-wstg-baseline.md +202 -0
- package/skills/qa-nfr-analyst/references/wcag-checklist.md +184 -0
- package/skills/qa-nfr-analyst/templates/owasp-checklist-template.md +89 -0
- package/skills/qa-nfr-analyst/templates/wcag-checklist-template.md +48 -0
- package/skills/qa-orchestrator/SKILL.md +132 -0
- package/skills/qa-orchestrator/references/handoff-chains.md +105 -0
- package/skills/qa-orchestrator/references/pipeline-modes.md +115 -0
- package/skills/qa-orchestrator/references/scheduler-rules.md +84 -0
- package/skills/qa-pact-writer/SKILL.md +133 -0
- package/skills/qa-pact-writer/references/best-practices.md +100 -0
- package/skills/qa-pact-writer/references/config.md +135 -0
- package/skills/qa-pact-writer/references/patterns.md +161 -0
- package/skills/qa-plan-creator/SKILL.md +139 -0
- package/skills/qa-plan-creator/references/introduction-plan.md +43 -0
- package/skills/qa-plan-creator/references/migration-plan.md +44 -0
- package/skills/qa-plan-creator/references/onboarding-plan.md +46 -0
- package/skills/qa-plan-creator/references/performance-plan.md +44 -0
- package/skills/qa-plan-creator/references/regression-plan.md +45 -0
- package/skills/qa-plan-creator/references/release-plan.md +45 -0
- package/skills/qa-plan-creator/references/sprint-plan.md +44 -0
- package/skills/qa-plan-creator/references/test-plan.md +59 -0
- package/skills/qa-plan-creator/references/uat-plan.md +43 -0
- package/skills/qa-plan-creator/templates/checklist-template.md +36 -0
- package/skills/qa-plan-creator/templates/regression-checklist-template.md +49 -0
- package/skills/qa-plan-creator/templates/release-checklist-template.md +46 -0
- package/skills/qa-plan-creator/templates/test-plan-template.md +74 -0
- package/skills/qa-playwright-py-writer/SKILL.md +156 -0
- package/skills/qa-playwright-py-writer/references/best-practices.md +194 -0
- package/skills/qa-playwright-py-writer/references/config.md +195 -0
- package/skills/qa-playwright-py-writer/references/patterns.md +212 -0
- package/skills/qa-playwright-ts-writer/SKILL.md +151 -0
- package/skills/qa-playwright-ts-writer/references/assertions.md +109 -0
- package/skills/qa-playwright-ts-writer/references/best-practices.md +191 -0
- package/skills/qa-playwright-ts-writer/references/config.md +144 -0
- package/skills/qa-playwright-ts-writer/references/patterns.md +171 -0
- package/skills/qa-pytest-writer/SKILL.md +145 -0
- package/skills/qa-pytest-writer/references/assertions.md +149 -0
- package/skills/qa-pytest-writer/references/best-practices.md +97 -0
- package/skills/qa-pytest-writer/references/config.md +176 -0
- package/skills/qa-pytest-writer/references/patterns.md +251 -0
- package/skills/qa-qase-integration/SKILL.md +149 -0
- package/skills/qa-qase-integration/references/api-reference.md +354 -0
- package/skills/qa-qase-integration/references/ci-integration.md +196 -0
- package/skills/qa-qase-integration/references/field-mapping.md +157 -0
- package/skills/qa-requirements-generator/SKILL.md +152 -0
- package/skills/qa-requirements-generator/references/iso-29148-structure.md +153 -0
- package/skills/qa-requirements-generator/references/requirement-patterns.md +278 -0
- package/skills/qa-rest-assured-writer/SKILL.md +137 -0
- package/skills/qa-rest-assured-writer/references/best-practices.md +50 -0
- package/skills/qa-rest-assured-writer/references/config.md +124 -0
- package/skills/qa-rest-assured-writer/references/patterns.md +192 -0
- package/skills/qa-risk-analyzer/SKILL.md +158 -0
- package/skills/qa-risk-analyzer/references/impact-analysis.md +133 -0
- package/skills/qa-risk-analyzer/references/risk-factors.md +123 -0
- package/skills/qa-robot-framework-writer/SKILL.md +147 -0
- package/skills/qa-robot-framework-writer/references/best-practices.md +249 -0
- package/skills/qa-robot-framework-writer/references/config.md +204 -0
- package/skills/qa-robot-framework-writer/references/libraries.md +273 -0
- package/skills/qa-robot-framework-writer/references/patterns.md +216 -0
- package/skills/qa-security-test-writer/SKILL.md +123 -0
- package/skills/qa-security-test-writer/references/best-practices.md +155 -0
- package/skills/qa-security-test-writer/references/owasp-top10.md +331 -0
- package/skills/qa-security-test-writer/references/zap-config.md +258 -0
- package/skills/qa-selenium-java-writer/SKILL.md +143 -0
- package/skills/qa-selenium-java-writer/references/best-practices.md +59 -0
- package/skills/qa-selenium-java-writer/references/config.md +143 -0
- package/skills/qa-selenium-java-writer/references/patterns.md +170 -0
- package/skills/qa-selenium-py-writer/SKILL.md +150 -0
- package/skills/qa-selenium-py-writer/references/best-practices.md +175 -0
- package/skills/qa-selenium-py-writer/references/config.md +224 -0
- package/skills/qa-selenium-py-writer/references/patterns.md +255 -0
- package/skills/qa-shortcut-integration/SKILL.md +143 -0
- package/skills/qa-shortcut-integration/references/api-patterns.md +126 -0
- package/skills/qa-shortcut-integration/references/field-mapping.md +66 -0
- package/skills/qa-spec-auditor/SKILL.md +162 -0
- package/skills/qa-spec-auditor/references/audit-checklist.md +144 -0
- package/skills/qa-spec-auditor/references/drift-patterns.md +207 -0
- package/skills/qa-spec-writer/SKILL.md +143 -0
- package/skills/qa-spec-writer/references/gherkin-guide.md +253 -0
- package/skills/qa-spec-writer/references/specification-patterns.md +274 -0
- package/skills/qa-spring-test-writer/SKILL.md +170 -0
- package/skills/qa-spring-test-writer/references/best-practices.md +57 -0
- package/skills/qa-spring-test-writer/references/config.md +179 -0
- package/skills/qa-spring-test-writer/references/patterns.md +235 -0
- package/skills/qa-supertest-writer/SKILL.md +150 -0
- package/skills/qa-supertest-writer/references/assertions.md +192 -0
- package/skills/qa-supertest-writer/references/best-practices.md +102 -0
- package/skills/qa-supertest-writer/references/config.md +166 -0
- package/skills/qa-supertest-writer/references/patterns.md +242 -0
- package/skills/qa-task-creator/SKILL.md +142 -0
- package/skills/qa-task-creator/references/linking-patterns.md +127 -0
- package/skills/qa-task-creator/references/task-types.md +169 -0
- package/skills/qa-task-creator/templates/task-template.md +24 -0
- package/skills/qa-test-doc-compiler/SKILL.md +114 -0
- package/skills/qa-test-doc-compiler/references/agile-tailoring.md +220 -0
- package/skills/qa-test-doc-compiler/references/iso-29119-3-documents.md +302 -0
- package/skills/qa-test-healer/SKILL.md +101 -0
- package/skills/qa-test-healer/references/diagnosis-patterns.md +142 -0
- package/skills/qa-test-healer/references/fix-strategies.md +177 -0
- package/skills/qa-test-reporter/SKILL.md +130 -0
- package/skills/qa-test-reporter/references/best-practices.md +162 -0
- package/skills/qa-test-reporter/references/iso-29119-reports.md +236 -0
- package/skills/qa-test-reporter/references/report-formats.md +287 -0
- package/skills/qa-test-reviewer/SKILL.md +142 -0
- package/skills/qa-test-reviewer/references/anti-patterns.md +268 -0
- package/skills/qa-test-reviewer/references/review-checklist.md +93 -0
- package/skills/qa-test-strategy/SKILL.md +133 -0
- package/skills/qa-test-strategy/references/entry-exit-criteria.md +176 -0
- package/skills/qa-test-strategy/references/risk-matrix.md +102 -0
- package/skills/qa-test-strategy/references/testing-types.md +143 -0
- package/skills/qa-testcase-from-docs/SKILL.md +161 -0
- package/skills/qa-testcase-from-docs/references/test-case-format.md +196 -0
- package/skills/qa-testcase-from-docs/references/test-design-techniques.md +126 -0
- package/skills/qa-testcase-from-docs/templates/test-case-template.md +31 -0
- package/skills/qa-testcase-from-ui/SKILL.md +109 -0
- package/skills/qa-testcase-from-ui/references/ui-element-patterns.md +126 -0
- package/skills/qa-testcase-from-ui/references/visual-analysis-guide.md +146 -0
- package/skills/qa-testcase-from-ui/templates/test-case-template.md +31 -0
- package/skills/qa-visual-regression-writer/SKILL.md +175 -0
- package/skills/qa-visual-regression-writer/references/best-practices.md +154 -0
- package/skills/qa-visual-regression-writer/references/config.md +220 -0
- package/skills/qa-visual-regression-writer/references/patterns.md +213 -0
- package/skills/qa-vitest-writer/SKILL.md +141 -0
- package/skills/qa-vitest-writer/references/assertions.md +105 -0
- package/skills/qa-vitest-writer/references/best-practices.md +62 -0
- package/skills/qa-vitest-writer/references/config.md +127 -0
- package/skills/qa-vitest-writer/references/patterns.md +141 -0
- package/skills/qa-webdriverio-writer/SKILL.md +145 -0
- package/skills/qa-webdriverio-writer/references/best-practices.md +176 -0
- package/skills/qa-webdriverio-writer/references/config.md +240 -0
- package/skills/qa-webdriverio-writer/references/patterns.md +269 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Playwright Python Patterns
|
|
2
|
+
|
|
3
|
+
Common patterns for E2E testing with Playwright Python (sync and async API).
|
|
4
|
+
|
|
5
|
+
## Sync vs Async API
|
|
6
|
+
|
|
7
|
+
| API | Import | Use Case |
|
|
8
|
+
|-----|--------|----------|
|
|
9
|
+
| **Sync** | `from playwright.sync_api import Page, expect` | Default; simpler, pytest-playwright fixtures |
|
|
10
|
+
| **Async** | `from playwright.async_api import Page, expect` | Async test suites; `playwright_pytest_asyncio = True` in pytest.ini |
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
# Sync (default)
|
|
14
|
+
def test_login(page: Page):
|
|
15
|
+
page.goto("/login")
|
|
16
|
+
page.get_by_label("Email").fill("user@example.com")
|
|
17
|
+
page.get_by_label("Password").fill("secret")
|
|
18
|
+
page.get_by_role("button", name="Sign in").click()
|
|
19
|
+
expect(page).to_have_url(re.compile(r".*dashboard"))
|
|
20
|
+
|
|
21
|
+
# Async (requires playwright_pytest_asyncio)
|
|
22
|
+
@pytest.mark.asyncio
|
|
23
|
+
async def test_login_async(page: Page):
|
|
24
|
+
await page.goto("/login")
|
|
25
|
+
await page.get_by_label("Email").fill("user@example.com")
|
|
26
|
+
await page.get_by_role("button", name="Sign in").click()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Pytest Fixtures (pytest-playwright)
|
|
30
|
+
|
|
31
|
+
| Fixture | Scope | Description |
|
|
32
|
+
|---------|-------|-------------|
|
|
33
|
+
| `playwright` | Session | Playwright instance |
|
|
34
|
+
| `browser_type` | Session | Chromium, Firefox, or WebKit launcher |
|
|
35
|
+
| `browser` | Function | Browser instance |
|
|
36
|
+
| `context` | Function | Browser context (isolated) |
|
|
37
|
+
| `page` | Function | Page (tab) for tests |
|
|
38
|
+
| `browser_context_args` | Function | Override context options |
|
|
39
|
+
| `browser_name` | Session | `chromium`, `firefox`, or `webkit` |
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
def test_with_page(page: Page):
|
|
43
|
+
page.goto("https://example.com")
|
|
44
|
+
assert page.title() == "Example Domain"
|
|
45
|
+
|
|
46
|
+
def test_with_context(context: BrowserContext, page: Page):
|
|
47
|
+
# context and page are fresh per test
|
|
48
|
+
page.goto("/")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Navigation
|
|
52
|
+
|
|
53
|
+
| Pattern | Sync | Async |
|
|
54
|
+
|---------|------|-------|
|
|
55
|
+
| Go to URL | `page.goto("/login")` | `await page.goto("/login")` |
|
|
56
|
+
| Go back | `page.go_back()` | `await page.go_back()` |
|
|
57
|
+
| Reload | `page.reload()` | `await page.reload()` |
|
|
58
|
+
| Wait for URL | `page.wait_for_url("**/dashboard")` | `await page.wait_for_url("**/dashboard")` |
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
def test_navigates_after_login(page: Page):
|
|
62
|
+
page.goto("/login")
|
|
63
|
+
page.get_by_label("Email").fill("user@example.com")
|
|
64
|
+
page.get_by_label("Password").fill("secret")
|
|
65
|
+
page.get_by_role("button", name="Sign in").click()
|
|
66
|
+
page.wait_for_url(re.compile(r".*dashboard"))
|
|
67
|
+
expect(page).to_have_url(re.compile(r".*dashboard"))
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Forms
|
|
71
|
+
|
|
72
|
+
| Pattern | Example |
|
|
73
|
+
|---------|---------|
|
|
74
|
+
| Fill text | `page.get_by_label("Name").fill("John")` |
|
|
75
|
+
| Fill by role | `page.get_by_role("textbox", name="Email").fill("a@b.com")` |
|
|
76
|
+
| Select dropdown | `page.get_by_label("Country").select_option("US")` |
|
|
77
|
+
| Check checkbox | `page.get_by_role("checkbox", name="Subscribe").check()` |
|
|
78
|
+
| Radio | `page.get_by_role("radio", name="Option A").check()` |
|
|
79
|
+
| Submit | `page.get_by_role("button", name="Submit").click()` |
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
def test_registration_form(page: Page):
|
|
83
|
+
page.goto("/register")
|
|
84
|
+
page.get_by_label("Email").fill("new@example.com")
|
|
85
|
+
page.get_by_label("Password").fill("SecurePass123")
|
|
86
|
+
page.get_by_role("checkbox", name="Terms").check()
|
|
87
|
+
page.get_by_role("button", name="Create account").click()
|
|
88
|
+
expect(page.get_by_text("Welcome")).to_be_visible()
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Assertions (expect)
|
|
92
|
+
|
|
93
|
+
| Assertion | Example |
|
|
94
|
+
|-----------|---------|
|
|
95
|
+
| Visible | `expect(locator).to_be_visible()` |
|
|
96
|
+
| Text | `expect(locator).to_have_text("Hello")` |
|
|
97
|
+
| URL | `expect(page).to_have_url(re.compile(r".*dashboard"))` |
|
|
98
|
+
| Count | `expect(locator).to_have_count(3)` |
|
|
99
|
+
| Enabled | `expect(locator).to_be_enabled()` |
|
|
100
|
+
| Checked | `expect(locator).to_be_checked()` |
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from playwright.sync_api import expect
|
|
104
|
+
|
|
105
|
+
def test_homepage(page: Page):
|
|
106
|
+
page.goto("/")
|
|
107
|
+
expect(page.get_by_role("heading", name="Welcome")).to_be_visible()
|
|
108
|
+
expect(page.locator(".item")).to_have_count(5)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Network Mocking (page.route)
|
|
112
|
+
|
|
113
|
+
| Pattern | Example |
|
|
114
|
+
|---------|---------|
|
|
115
|
+
| Mock response | `page.route("**/api/users", lambda route: route.fulfill(status=200, body="[]"))` |
|
|
116
|
+
| Abort request | `page.route("**/analytics", lambda route: route.abort())` |
|
|
117
|
+
| Continue | `page.route("**/api/**", lambda route: route.continue_())` |
|
|
118
|
+
| Unroute | `page.unroute("**/api/users")` |
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
def test_empty_state(page: Page):
|
|
122
|
+
def handle_route(route):
|
|
123
|
+
route.fulfill(
|
|
124
|
+
status=200,
|
|
125
|
+
content_type="application/json",
|
|
126
|
+
body='{"items": []}'
|
|
127
|
+
)
|
|
128
|
+
page.route("**/api/items", handle_route)
|
|
129
|
+
page.goto("/items")
|
|
130
|
+
expect(page.get_by_text("No items found")).to_be_visible()
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## File Upload
|
|
134
|
+
|
|
135
|
+
| Pattern | Example |
|
|
136
|
+
|---------|---------|
|
|
137
|
+
| Single file | `page.get_by_label("Upload").set_input_files("fixtures/doc.pdf")` |
|
|
138
|
+
| Multiple files | `page.get_by_label("Upload").set_input_files(["a.pdf", "b.pdf"])` |
|
|
139
|
+
| Buffer | `page.get_by_label("Upload").set_input_files(FilePayload(name="test.txt", mime_type="text/plain", buffer=b"content"))` |
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from pathlib import Path
|
|
143
|
+
|
|
144
|
+
def test_upload(page: Page):
|
|
145
|
+
page.goto("/upload")
|
|
146
|
+
page.get_by_label("Choose file").set_input_files(Path(__file__).parent / "fixtures" / "sample.pdf")
|
|
147
|
+
page.get_by_role("button", name="Upload").click()
|
|
148
|
+
expect(page.get_by_text("Upload complete")).to_be_visible()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Iframe Handling
|
|
152
|
+
|
|
153
|
+
| Pattern | Example |
|
|
154
|
+
|---------|---------|
|
|
155
|
+
| Frame locator | `frame = page.frame_locator('iframe[name="embed"]')` |
|
|
156
|
+
| Interact in frame | `frame.get_by_role("button").click()` |
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
def test_iframe_content(page: Page):
|
|
160
|
+
page.goto("/embed")
|
|
161
|
+
frame = page.frame_locator("iframe#widget")
|
|
162
|
+
frame.get_by_role("button", name="Submit").click()
|
|
163
|
+
expect(frame.get_by_text("Success")).to_be_visible()
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Multi-Tab / Popup
|
|
167
|
+
|
|
168
|
+
| Pattern | Example |
|
|
169
|
+
|---------|---------|
|
|
170
|
+
| Wait for popup | `with page.expect_popup() as popup_info: page.get_by_role("link").click()` then `popup = popup_info.value` |
|
|
171
|
+
| New page | `new_page = context.new_page()` |
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
def test_opens_external_link(page: Page, context: BrowserContext):
|
|
175
|
+
with page.expect_popup() as popup_info:
|
|
176
|
+
page.get_by_role("link", name="External").click()
|
|
177
|
+
popup = popup_info.value
|
|
178
|
+
popup.wait_for_load_state()
|
|
179
|
+
expect(popup).to_have_url(re.compile(r"external\.com"))
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Page Object Model (POM)
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
# pages/base_page.py
|
|
186
|
+
from playwright.sync_api import Page
|
|
187
|
+
|
|
188
|
+
class BasePage:
|
|
189
|
+
def __init__(self, page: Page, base_url: str):
|
|
190
|
+
self.page = page
|
|
191
|
+
self.base_url = base_url
|
|
192
|
+
|
|
193
|
+
def goto(self, path: str):
|
|
194
|
+
self.page.goto(f"{self.base_url}{path}")
|
|
195
|
+
|
|
196
|
+
# pages/login_page.py
|
|
197
|
+
from playwright.sync_api import Page
|
|
198
|
+
from .base_page import BasePage
|
|
199
|
+
|
|
200
|
+
class LoginPage(BasePage):
|
|
201
|
+
def __init__(self, page: Page, base_url: str):
|
|
202
|
+
super().__init__(page, base_url)
|
|
203
|
+
|
|
204
|
+
def login(self, email: str, password: str):
|
|
205
|
+
self.page.get_by_label("Email").fill(email)
|
|
206
|
+
self.page.get_by_label("Password").fill(password)
|
|
207
|
+
self.page.get_by_role("button", name="Sign in").click()
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def error_message(self):
|
|
211
|
+
return self.page.get_by_role("alert")
|
|
212
|
+
```
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qa-playwright-ts-writer
|
|
3
|
+
description: Generate Playwright E2E, component, and unit tests for TypeScript with auto-wait, multi-browser support, POM pattern, and live browser record mode via Playwright MCP.
|
|
4
|
+
output_dir: tests/e2e
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# QA Playwright TypeScript Writer
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Write Playwright tests (E2E, component, unit) from test case specifications. Transform structured test cases into executable Playwright test files with auto-waiting, multi-browser support, Page Object Model, and optional live browser record mode.
|
|
12
|
+
|
|
13
|
+
## Trigger Phrases
|
|
14
|
+
|
|
15
|
+
- "Write Playwright tests for [feature/flow]"
|
|
16
|
+
- "Generate E2E tests from test cases"
|
|
17
|
+
- "Create Playwright tests with record mode"
|
|
18
|
+
- "Add Playwright component tests for [component]"
|
|
19
|
+
- "Playwright tests for [URL/flow]"
|
|
20
|
+
- "Record browser interactions and generate test code"
|
|
21
|
+
- "POM-based Playwright tests for [page]"
|
|
22
|
+
- "Multi-browser Playwright tests"
|
|
23
|
+
- "Heal my failing Playwright tests"
|
|
24
|
+
|
|
25
|
+
## Three Modes
|
|
26
|
+
|
|
27
|
+
| Mode | When to Use | Behavior |
|
|
28
|
+
|------|-------------|----------|
|
|
29
|
+
| **Record Mode** | User wants live browser capture | Use Playwright MCP (e.g., cursor-ide-browser) → navigate, interact, capture interactions → generate test code from recorded steps |
|
|
30
|
+
| **Generate Mode** | Default; from test case specs | Read test cases (from qa-testcase-from-docs, qa-testcase-from-ui, qa-manual-test-designer) → generate Playwright code |
|
|
31
|
+
| **Heal Mode** | Tests fail after changes | Delegate to **qa-test-healer** to auto-fix broken selectors, assertions, waits; mark unfixable as `test.fixme()` |
|
|
32
|
+
|
|
33
|
+
## Test Types
|
|
34
|
+
|
|
35
|
+
| Type | Scope | Approach |
|
|
36
|
+
|------|-------|----------|
|
|
37
|
+
| **E2E** | Full user flows, page navigation | page.goto, locators, assertions, network interception, multi-browser |
|
|
38
|
+
| **Component** | Isolated component testing | @playwright/experimental-ct-react, ct-vue, ct-svelte; mount, interact, assert |
|
|
39
|
+
| **Unit** | Pure functions, utilities | Use test runner for logic; Playwright optional for DOM utilities |
|
|
40
|
+
|
|
41
|
+
## E2E Testing
|
|
42
|
+
|
|
43
|
+
- **Navigation:** page.goto, page.goBack, page.reload
|
|
44
|
+
- **Interactions:** click, fill, selectOption, check, hover, press
|
|
45
|
+
- **Assertions:** expect(locator).toBeVisible, toHaveText, toHaveURL, etc.
|
|
46
|
+
- **Network:** page.route for API mocking, request/response interception
|
|
47
|
+
- **Multi-browser:** Chromium, Firefox, WebKit via projects in config
|
|
48
|
+
- **Auto-wait:** Playwright auto-waits for elements; avoid page.waitForTimeout
|
|
49
|
+
|
|
50
|
+
See `references/patterns.md` for navigation, forms, auth, file upload, drag-drop, iframes, multi-tab, API mocking, visual comparison.
|
|
51
|
+
|
|
52
|
+
## Component Testing
|
|
53
|
+
|
|
54
|
+
- **React:** `@playwright/experimental-ct-react` — mount components, pass props, assert
|
|
55
|
+
- **Vue:** `@playwright/experimental-ct-vue` — mount, slots, provide/inject
|
|
56
|
+
- **Svelte:** `@playwright/experimental-ct-svelte` — mount, component API
|
|
57
|
+
|
|
58
|
+
Component tests run in isolated browser context; use `test` from `@playwright/experimental-ct-*`.
|
|
59
|
+
|
|
60
|
+
## Page Object Model (POM)
|
|
61
|
+
|
|
62
|
+
- **Base page:** Shared selectors, navigation helpers, common actions
|
|
63
|
+
- **Page-specific:** Extend base; encapsulate page-specific locators and methods
|
|
64
|
+
- **Component objects:** Reusable components (header, modal, form) as classes
|
|
65
|
+
|
|
66
|
+
See `references/best-practices.md` for POM structure.
|
|
67
|
+
|
|
68
|
+
## Key Patterns
|
|
69
|
+
|
|
70
|
+
- **Structure:** test.describe / test / test.step for grouping
|
|
71
|
+
- **Locators:** getByRole > getByTestId > getByText > getByLabel > CSS selector
|
|
72
|
+
- **Assertions:** expect(locator).toBeVisible, toHaveText, toHaveURL, toHaveCount, etc.
|
|
73
|
+
- **Network:** page.route(url, handler) for mocking; page.unroute to clear
|
|
74
|
+
- **Fixtures:** test.extend for custom fixtures (auth, API client)
|
|
75
|
+
- **Steps:** test.step('description', async () => { ... }) for trace grouping
|
|
76
|
+
|
|
77
|
+
See `references/assertions.md` for full assertion reference.
|
|
78
|
+
|
|
79
|
+
## Locator Priority
|
|
80
|
+
|
|
81
|
+
1. **getByRole** — Accessibility-based; most resilient
|
|
82
|
+
2. **getByTestId** — data-testid; explicit, stable
|
|
83
|
+
3. **getByText** — Visible text; use for unique labels
|
|
84
|
+
4. **getByLabel** — Form labels; good for inputs
|
|
85
|
+
5. **CSS selector** — Last resort; brittle for dynamic content
|
|
86
|
+
|
|
87
|
+
## Configuration
|
|
88
|
+
|
|
89
|
+
- **playwright.config.ts** — projects (browsers), retries, workers, reporter, baseURL
|
|
90
|
+
- **Global setup/teardown** — Auth state, DB seeding
|
|
91
|
+
- **Test directory** — e2e/, tests/, or colocated
|
|
92
|
+
|
|
93
|
+
See `references/config.md` for full config guide.
|
|
94
|
+
|
|
95
|
+
## MCP Integration
|
|
96
|
+
|
|
97
|
+
- **Context7 MCP** — Fetch Playwright documentation when needed
|
|
98
|
+
- **Playwright MCP** (cursor-ide-browser, @playwright/mcp) — Record mode: navigate, snapshot, click, type; capture interactions → generate test code
|
|
99
|
+
|
|
100
|
+
## Scope
|
|
101
|
+
|
|
102
|
+
**Can do (autonomous):**
|
|
103
|
+
- Generate Playwright E2E, component, unit tests from test case specs
|
|
104
|
+
- Use Record Mode with Playwright MCP to capture and generate tests
|
|
105
|
+
- Apply POM pattern, stable locators, auto-wait
|
|
106
|
+
- Configure playwright.config.ts (projects, retries, reporter)
|
|
107
|
+
- Use page.route for API mocking
|
|
108
|
+
- Delegate to qa-test-healer when tests fail (Heal Mode)
|
|
109
|
+
- Use Context7 MCP for Playwright docs
|
|
110
|
+
|
|
111
|
+
**Cannot do (requires confirmation):**
|
|
112
|
+
- Change production code structure
|
|
113
|
+
- Add dependencies not in package.json
|
|
114
|
+
- Override project Playwright config without approval
|
|
115
|
+
- Navigate to URLs not provided (Record Mode)
|
|
116
|
+
|
|
117
|
+
**Will not do (out of scope):**
|
|
118
|
+
- Execute tests (user runs `npx playwright test`)
|
|
119
|
+
- Write Jest/Vitest unit tests (use qa-jest-writer)
|
|
120
|
+
- Modify CI/CD pipelines
|
|
121
|
+
- Bypass security or access restricted areas
|
|
122
|
+
|
|
123
|
+
## References
|
|
124
|
+
|
|
125
|
+
- `references/patterns.md` — Navigation, forms, auth, file upload, drag-drop, iframes, multi-tab, API mocking, visual
|
|
126
|
+
- `references/assertions.md` — Playwright assertion reference
|
|
127
|
+
- `references/config.md` — playwright.config.ts, projects, reporter, setup
|
|
128
|
+
- `references/best-practices.md` — POM, locators, flakiness, isolation, debugging
|
|
129
|
+
|
|
130
|
+
## Quality Checklist
|
|
131
|
+
|
|
132
|
+
- [ ] Auto-wait used; no page.waitForTimeout (use expect with timeout or waitFor)
|
|
133
|
+
- [ ] No hardcoded waits; prefer expect auto-retry
|
|
134
|
+
- [ ] POM pattern applied for page-specific logic
|
|
135
|
+
- [ ] Stable locators (getByRole, getByTestId preferred)
|
|
136
|
+
- [ ] Tests independent (no shared state, order-independent)
|
|
137
|
+
- [ ] Proper teardown (fixtures, afterEach if needed)
|
|
138
|
+
- [ ] Traceability to test case IDs where applicable
|
|
139
|
+
- [ ] No hardcoded secrets (use env vars)
|
|
140
|
+
|
|
141
|
+
## Troubleshooting
|
|
142
|
+
|
|
143
|
+
| Symptom | Likely Cause | Fix |
|
|
144
|
+
|---------|--------------|-----|
|
|
145
|
+
| Element not found | Selector too specific, dynamic content | Use getByRole/getByTestId; add data-testid if needed |
|
|
146
|
+
| Timeout | Element not ready, slow network | Increase expect timeout; use waitFor; check for overlays |
|
|
147
|
+
| Flaky tests | Race conditions, shared state | Ensure test isolation; use auto-wait; avoid fixed delays |
|
|
148
|
+
| Record mode empty | MCP not capturing steps | Verify Playwright MCP active; lock browser before actions |
|
|
149
|
+
| Multi-tab fails | Wrong context | Use page.context().pages() or new context for new tab |
|
|
150
|
+
| API mock not applied | Route registered after request | Call page.route before page.goto |
|
|
151
|
+
| Component test fails | Missing mount setup | Check ct config; ensure component imported correctly |
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Playwright Assertion Reference
|
|
2
|
+
|
|
3
|
+
Playwright uses `expect` from `@playwright/test` with auto-retry and rich error messages.
|
|
4
|
+
|
|
5
|
+
## Visibility & State
|
|
6
|
+
|
|
7
|
+
| Assertion | Description | Example |
|
|
8
|
+
|-----------|-------------|---------|
|
|
9
|
+
| `toBeVisible()` | Element is visible | `expect(page.getByRole('heading')).toBeVisible()` |
|
|
10
|
+
| `toBeHidden()` | Element is hidden | `expect(page.getByText('Loading')).toBeHidden()` |
|
|
11
|
+
| `toBeAttached()` | Element is in DOM | `expect(locator).toBeAttached()` |
|
|
12
|
+
| `toBeDetached()` | Element is not in DOM | `expect(locator).toBeDetached()` |
|
|
13
|
+
|
|
14
|
+
## Text
|
|
15
|
+
|
|
16
|
+
| Assertion | Description | Example |
|
|
17
|
+
|-----------|-------------|---------|
|
|
18
|
+
| `toHaveText(text)` | Exact text match | `expect(locator).toHaveText('Submit')` |
|
|
19
|
+
| `toHaveText(regex)` | Regex match | `expect(locator).toHaveText(/Welcome, .+/)` |
|
|
20
|
+
| `toContainText(text)` | Contains text | `expect(locator).toContainText('Error')` |
|
|
21
|
+
| `toHaveValue(value)` | Input value | `expect(input).toHaveValue('typed')` |
|
|
22
|
+
|
|
23
|
+
## URL & Navigation
|
|
24
|
+
|
|
25
|
+
| Assertion | Description | Example |
|
|
26
|
+
|-----------|-------------|---------|
|
|
27
|
+
| `toHaveURL(url)` | Page URL | `expect(page).toHaveURL('/dashboard')` |
|
|
28
|
+
| `toHaveURL(regex)` | URL regex | `expect(page).toHaveURL(/.*\/user\/\d+/)` |
|
|
29
|
+
| `toHaveTitle(title)` | Page title | `expect(page).toHaveTitle('Dashboard')` |
|
|
30
|
+
|
|
31
|
+
## Count & List
|
|
32
|
+
|
|
33
|
+
| Assertion | Description | Example |
|
|
34
|
+
|-----------|-------------|---------|
|
|
35
|
+
| `toHaveCount(n)` | Number of matches | `expect(page.getByRole('listitem')).toHaveCount(5)` |
|
|
36
|
+
| `toHaveLength(n)` | Array length (for locator.all()) | `expect(await locator.all()).toHaveLength(3)` |
|
|
37
|
+
|
|
38
|
+
## Attributes & Properties
|
|
39
|
+
|
|
40
|
+
| Assertion | Description | Example |
|
|
41
|
+
|-----------|-------------|---------|
|
|
42
|
+
| `toHaveAttribute(name, value?)` | Attribute exists/value | `expect(link).toHaveAttribute('href', '/about')` |
|
|
43
|
+
| `toHaveClass(class)` | Has CSS class | `expect(btn).toHaveClass('active')` |
|
|
44
|
+
| `toHaveCSS(name, value)` | CSS property | `expect(el).toHaveCSS('display', 'none')` |
|
|
45
|
+
|
|
46
|
+
## Form Elements
|
|
47
|
+
|
|
48
|
+
| Assertion | Description | Example |
|
|
49
|
+
|-----------|-------------|---------|
|
|
50
|
+
| `toBeChecked()` | Checkbox/radio checked | `expect(checkbox).toBeChecked()` |
|
|
51
|
+
| `toBeEnabled()` | Element enabled | `expect(button).toBeEnabled()` |
|
|
52
|
+
| `toBeDisabled()` | Element disabled | `expect(button).toBeDisabled()` |
|
|
53
|
+
| `toBeEditable()` | Input is editable | `expect(input).toBeEditable()` |
|
|
54
|
+
| `toBeEmpty()` | No text/children | `expect(div).toBeEmpty()` |
|
|
55
|
+
|
|
56
|
+
## Soft Assertions
|
|
57
|
+
|
|
58
|
+
| Pattern | Description | Example |
|
|
59
|
+
|---------|-------------|---------|
|
|
60
|
+
| `expect.soft()` | Continue on failure | `await expect.soft(locator).toBeVisible()` |
|
|
61
|
+
| Collect failures | Run all, report at end | Use multiple `expect.soft` in sequence |
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
test('validates form fields', async ({ page }) => {
|
|
65
|
+
await expect.soft(page.getByLabel('Email')).toBeVisible();
|
|
66
|
+
await expect.soft(page.getByLabel('Password')).toBeVisible();
|
|
67
|
+
await expect.soft(page.getByRole('button')).toBeEnabled();
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Timeout
|
|
72
|
+
|
|
73
|
+
| Pattern | Description | Example |
|
|
74
|
+
|---------|-------------|---------|
|
|
75
|
+
| `{ timeout: ms }` | Override default | `expect(locator).toBeVisible({ timeout: 10000 })` |
|
|
76
|
+
| Config default | playwright.config.ts | `expect: { timeout: 5000 }` |
|
|
77
|
+
|
|
78
|
+
## Negation
|
|
79
|
+
|
|
80
|
+
| Pattern | Example |
|
|
81
|
+
|---------|---------|
|
|
82
|
+
| `not` | `expect(locator).not.toBeVisible()` |
|
|
83
|
+
| `expect(locator).not.toHaveText('Error')` |
|
|
84
|
+
|
|
85
|
+
## Screenshot Assertions
|
|
86
|
+
|
|
87
|
+
| Assertion | Description | Example |
|
|
88
|
+
|-----------|-------------|---------|
|
|
89
|
+
| `toHaveScreenshot(name?)` | Visual regression | `await expect(page).toHaveScreenshot('home.png')` |
|
|
90
|
+
| Options | mask, maxDiffPixels | `await expect(page).toHaveScreenshot({ mask: [locator] })` |
|
|
91
|
+
|
|
92
|
+
## Common Patterns
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Wait for element and assert
|
|
96
|
+
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
97
|
+
|
|
98
|
+
// Assert list length
|
|
99
|
+
await expect(page.getByRole('listitem')).toHaveCount(3);
|
|
100
|
+
|
|
101
|
+
// Assert input value
|
|
102
|
+
await expect(page.getByLabel('Search')).toHaveValue('query');
|
|
103
|
+
|
|
104
|
+
// Assert URL after action
|
|
105
|
+
await expect(page).toHaveURL(/\/users\/\d+/);
|
|
106
|
+
|
|
107
|
+
// Assert disabled state
|
|
108
|
+
await expect(page.getByRole('button', { name: 'Submit' })).toBeDisabled();
|
|
109
|
+
```
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Playwright Best Practices
|
|
2
|
+
|
|
3
|
+
Guidelines for maintainable, stable Playwright tests.
|
|
4
|
+
|
|
5
|
+
## Page Object Model (POM)
|
|
6
|
+
|
|
7
|
+
### Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pages/
|
|
11
|
+
BasePage.ts # Shared navigation, common helpers
|
|
12
|
+
LoginPage.ts # Login-specific locators and methods
|
|
13
|
+
DashboardPage.ts # Dashboard-specific
|
|
14
|
+
components/
|
|
15
|
+
Header.ts # Reusable header component
|
|
16
|
+
Modal.ts # Reusable modal
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Base Page
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// pages/BasePage.ts
|
|
23
|
+
import { Page } from '@playwright/test';
|
|
24
|
+
|
|
25
|
+
export class BasePage {
|
|
26
|
+
constructor(protected page: Page, protected baseURL: string) {}
|
|
27
|
+
|
|
28
|
+
async goto(path: string) {
|
|
29
|
+
await this.page.goto(`${this.baseURL}${path}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getByTestId(id: string) {
|
|
33
|
+
return this.page.getByTestId(id);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Page-Specific Class
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// pages/LoginPage.ts
|
|
42
|
+
import { Page } from '@playwright/test';
|
|
43
|
+
import { BasePage } from './BasePage';
|
|
44
|
+
|
|
45
|
+
export class LoginPage extends BasePage {
|
|
46
|
+
constructor(page: Page, baseURL: string) {
|
|
47
|
+
super(page, baseURL);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async login(email: string, password: string) {
|
|
51
|
+
await this.page.getByLabel('Email').fill(email);
|
|
52
|
+
await this.page.getByLabel('Password').fill(password);
|
|
53
|
+
await this.page.getByRole('button', { name: 'Sign in' }).click();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get errorMessage() {
|
|
57
|
+
return this.page.getByRole('alert');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Usage in Tests
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
test('login flow', async ({ page }) => {
|
|
66
|
+
const loginPage = new LoginPage(page, baseURL);
|
|
67
|
+
await loginPage.goto('/login');
|
|
68
|
+
await loginPage.login('user@example.com', 'secret');
|
|
69
|
+
await expect(loginPage.errorMessage).not.toBeVisible();
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Locator Strategies
|
|
74
|
+
|
|
75
|
+
### Priority Order
|
|
76
|
+
|
|
77
|
+
1. **getByRole** — Best for accessibility; resilient to DOM changes
|
|
78
|
+
2. **getByTestId** — Explicit, stable; requires adding data-testid
|
|
79
|
+
3. **getByText** — For unique visible text
|
|
80
|
+
4. **getByLabel** — For form inputs with labels
|
|
81
|
+
5. **CSS selector** — Last resort; brittle
|
|
82
|
+
|
|
83
|
+
### Examples
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Prefer
|
|
87
|
+
page.getByRole('button', { name: 'Submit' })
|
|
88
|
+
page.getByTestId('submit-btn')
|
|
89
|
+
page.getByLabel('Email address')
|
|
90
|
+
page.getByText('Welcome back')
|
|
91
|
+
|
|
92
|
+
// Avoid when possible
|
|
93
|
+
page.locator('.btn-primary')
|
|
94
|
+
page.locator('#submit')
|
|
95
|
+
page.locator('div > span:nth-child(2)')
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Avoiding Flakiness
|
|
99
|
+
|
|
100
|
+
| Practice | Description |
|
|
101
|
+
|----------|-------------|
|
|
102
|
+
| **Auto-wait** | Playwright auto-waits; avoid `page.waitForTimeout` |
|
|
103
|
+
| **Assert before act** | Use `expect` to wait for readiness before clicking |
|
|
104
|
+
| **Stable selectors** | Prefer role/testid over CSS |
|
|
105
|
+
| **Isolation** | Each test gets fresh context; no shared state |
|
|
106
|
+
| **Deterministic data** | Use fixtures, mocks; avoid time-dependent data |
|
|
107
|
+
| **No fixed delays** | Use `expect` with timeout or `waitFor` instead of `setTimeout` |
|
|
108
|
+
|
|
109
|
+
### Anti-Patterns
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// BAD: Fixed delay
|
|
113
|
+
await page.waitForTimeout(3000);
|
|
114
|
+
|
|
115
|
+
// GOOD: Wait for element
|
|
116
|
+
await expect(page.getByRole('heading')).toBeVisible();
|
|
117
|
+
|
|
118
|
+
// BAD: Brittle CSS
|
|
119
|
+
await page.locator('div.container > div:nth-child(2) button').click();
|
|
120
|
+
|
|
121
|
+
// GOOD: Role or testid
|
|
122
|
+
await page.getByRole('button', { name: 'Save' }).click();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Test Isolation
|
|
126
|
+
|
|
127
|
+
- Each test runs in a new browser context (default)
|
|
128
|
+
- No shared cookies, localStorage, or session
|
|
129
|
+
- Use `storageState` for auth when needed; create fresh per test or reuse via project
|
|
130
|
+
- Clean up test data in `afterEach` if tests create DB records
|
|
131
|
+
|
|
132
|
+
## Parallel Execution
|
|
133
|
+
|
|
134
|
+
- `fullyParallel: true` — Run tests in parallel (default for many projects)
|
|
135
|
+
- `workers` — Number of parallel workers; reduce in CI if flaky
|
|
136
|
+
- Avoid shared resources (files, DB rows) that can conflict
|
|
137
|
+
|
|
138
|
+
## Debugging
|
|
139
|
+
|
|
140
|
+
| Tool | Use |
|
|
141
|
+
|------|-----|
|
|
142
|
+
| `--debug` | Pause and step through |
|
|
143
|
+
| `--headed` | Run with visible browser |
|
|
144
|
+
| `page.pause()` | Breakpoint in code |
|
|
145
|
+
| Trace viewer | `npx playwright show-trace trace.zip` |
|
|
146
|
+
| Screenshot on failure | `screenshot: 'only-on-failure'` |
|
|
147
|
+
| Video on failure | `video: 'retain-on-failure'` |
|
|
148
|
+
|
|
149
|
+
## Fixtures and test.extend
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { test as base } from '@playwright/test';
|
|
153
|
+
|
|
154
|
+
export const test = base.extend<{ authenticatedPage: Page }>({
|
|
155
|
+
authenticatedPage: async ({ page }, use) => {
|
|
156
|
+
// Login logic
|
|
157
|
+
await page.goto('/login');
|
|
158
|
+
await page.getByLabel('Email').fill('test@example.com');
|
|
159
|
+
await page.getByLabel('Password').fill('secret');
|
|
160
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
161
|
+
await page.waitForURL(/dashboard/);
|
|
162
|
+
await use(page);
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('authenticated flow', async ({ authenticatedPage }) => {
|
|
167
|
+
await authenticatedPage.goto('/settings');
|
|
168
|
+
// ...
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## test.step for Grouping
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
test('checkout flow', async ({ page }) => {
|
|
176
|
+
await test.step('add item to cart', async () => {
|
|
177
|
+
await page.goto('/products/1');
|
|
178
|
+
await page.getByRole('button', { name: 'Add to cart' }).click();
|
|
179
|
+
});
|
|
180
|
+
await test.step('proceed to checkout', async () => {
|
|
181
|
+
await page.getByRole('link', { name: 'Cart' }).click();
|
|
182
|
+
await page.getByRole('button', { name: 'Checkout' }).click();
|
|
183
|
+
});
|
|
184
|
+
await test.step('complete payment', async () => {
|
|
185
|
+
await page.getByLabel('Card number').fill('4111111111111111');
|
|
186
|
+
await page.getByRole('button', { name: 'Pay' }).click();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Steps appear in traces and reports for easier debugging.
|