@brunosps00/dev-workflow 0.0.3 → 0.0.5
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 +42 -42
- package/bin/dev-workflow.js +1 -1
- package/lib/constants.js +42 -40
- package/lib/init.js +40 -10
- package/package.json +1 -1
- package/scaffold/en/commands/{analyze-project.md → dw-analyze-project.md} +69 -40
- package/scaffold/en/commands/{brainstorm.md → dw-brainstorm.md} +31 -4
- package/scaffold/en/commands/{bugfix.md → dw-bugfix.md} +63 -19
- package/scaffold/en/commands/{code-review.md → dw-code-review.md} +38 -15
- package/scaffold/en/commands/{commit.md → dw-commit.md} +25 -0
- package/scaffold/en/commands/{create-prd.md → dw-create-prd.md} +24 -10
- package/scaffold/en/commands/{create-tasks.md → dw-create-tasks.md} +11 -4
- package/scaffold/en/commands/{create-techspec.md → dw-create-techspec.md} +38 -11
- package/scaffold/en/commands/{deep-research.md → dw-deep-research.md} +18 -17
- package/scaffold/en/commands/{fix-qa.md → dw-fix-qa.md} +20 -3
- package/scaffold/en/commands/dw-functional-doc.md +276 -0
- package/scaffold/en/commands/{generate-pr.md → dw-generate-pr.md} +20 -5
- package/scaffold/en/commands/dw-help.md +309 -0
- package/scaffold/en/commands/{refactoring-analysis.md → dw-refactoring-analysis.md} +50 -26
- package/scaffold/en/commands/{review-implementation.md → dw-review-implementation.md} +25 -6
- package/scaffold/en/commands/{run-plan.md → dw-run-plan.md} +21 -6
- package/scaffold/en/commands/{run-qa.md → dw-run-qa.md} +32 -13
- package/scaffold/en/commands/{run-task.md → dw-run-task.md} +17 -7
- package/scaffold/en/references/playwright-patterns.md +136 -0
- package/scaffold/en/references/refactoring-catalog.md +167 -0
- package/scaffold/en/templates/brainstorm-matrix.md +44 -0
- package/scaffold/en/templates/functional-doc/case-matrix.md +5 -0
- package/scaffold/en/templates/functional-doc/e2e-runbook.md +3 -0
- package/scaffold/en/templates/functional-doc/features.md +3 -0
- package/scaffold/en/templates/functional-doc/overview.md +21 -0
- package/scaffold/en/templates/functional-doc/playwright.spec.ts.tpl +19 -0
- package/scaffold/en/templates/pr-bugfix-template.md +28 -0
- package/scaffold/en/templates/qa-test-credentials.md +37 -0
- package/scaffold/en/templates/tasks-template.md +1 -1
- package/scaffold/en/templates/techspec-template.md +1 -1
- package/scaffold/pt-br/commands/{analyze-project.md → dw-analyze-project.md} +91 -41
- package/scaffold/pt-br/commands/{brainstorm.md → dw-brainstorm.md} +32 -5
- package/scaffold/pt-br/commands/{bugfix.md → dw-bugfix.md} +70 -13
- package/scaffold/pt-br/commands/{code-review.md → dw-code-review.md} +78 -15
- package/scaffold/pt-br/commands/{commit.md → dw-commit.md} +45 -1
- package/scaffold/pt-br/commands/{create-prd.md → dw-create-prd.md} +25 -10
- package/scaffold/pt-br/commands/{create-tasks.md → dw-create-tasks.md} +20 -13
- package/scaffold/pt-br/commands/{create-techspec.md → dw-create-techspec.md} +40 -13
- package/scaffold/pt-br/commands/{deep-research.md → dw-deep-research.md} +19 -11
- package/scaffold/pt-br/commands/{fix-qa.md → dw-fix-qa.md} +30 -1
- package/scaffold/pt-br/commands/dw-functional-doc.md +276 -0
- package/scaffold/pt-br/commands/{generate-pr.md → dw-generate-pr.md} +58 -3
- package/scaffold/pt-br/commands/{help.md → dw-help.md} +81 -59
- package/scaffold/pt-br/commands/{refactoring-analysis.md → dw-refactoring-analysis.md} +49 -25
- package/scaffold/pt-br/commands/{review-implementation.md → dw-review-implementation.md} +50 -2
- package/scaffold/pt-br/commands/{run-plan.md → dw-run-plan.md} +98 -10
- package/scaffold/pt-br/commands/{run-qa.md → dw-run-qa.md} +93 -18
- package/scaffold/pt-br/commands/{run-task.md → dw-run-task.md} +32 -7
- package/scaffold/pt-br/references/playwright-patterns.md +133 -0
- package/scaffold/pt-br/references/refactoring-catalog.md +166 -0
- package/scaffold/pt-br/templates/brainstorm-matrix.md +44 -0
- package/scaffold/pt-br/templates/functional-doc/case-matrix.md +5 -0
- package/scaffold/pt-br/templates/functional-doc/e2e-runbook.md +3 -0
- package/scaffold/pt-br/templates/functional-doc/features.md +3 -0
- package/scaffold/pt-br/templates/functional-doc/overview.md +21 -0
- package/scaffold/pt-br/templates/functional-doc/playwright.spec.ts.tpl +19 -0
- package/scaffold/pt-br/templates/pr-bugfix-template.md +28 -0
- package/scaffold/pt-br/templates/qa-test-credentials.md +37 -0
- package/scaffold/pt-br/templates/techspec-template.md +1 -1
- package/scaffold/rules-readme.md +3 -3
- package/scaffold/scripts/functional-doc/generate-dossier.mjs +821 -0
- package/scaffold/scripts/functional-doc/run-playwright-flow.mjs +275 -0
- package/scaffold/en/commands/help.md +0 -289
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
<system_instructions>
|
|
2
2
|
You are an AI assistant specialized in Quality Assurance. Your task is to validate that the implementation meets all requirements defined in the PRD, TechSpec, and Tasks by executing E2E tests, accessibility checks, and visual analysis.
|
|
3
3
|
|
|
4
|
+
## When to Use
|
|
5
|
+
- Use when validating that implementation meets all requirements from PRD, TechSpec, and Tasks through E2E tests, accessibility checks, and visual analysis
|
|
6
|
+
- Do NOT use when only unit/integration tests are needed (use the project's test runner directly)
|
|
7
|
+
- Do NOT use when requirements have not been defined yet (create PRD first)
|
|
8
|
+
|
|
9
|
+
## Pipeline Position
|
|
10
|
+
**Predecessor:** `/dw-run-plan` or `/dw-run-task` | **Successor:** `/dw-fix-qa` (if bugs) or `/dw-code-review`
|
|
11
|
+
|
|
4
12
|
<critical>Use the Playwright MCP to execute all E2E tests</critical>
|
|
5
13
|
<critical>Verify ALL requirements from the PRD and TechSpec before approving</critical>
|
|
6
14
|
<critical>QA is NOT complete until ALL checks pass</critical>
|
|
@@ -20,7 +28,7 @@ When available in the project under `./.agents/skills/`, use these skills as ope
|
|
|
20
28
|
|
|
21
29
|
| Variable | Description | Example |
|
|
22
30
|
|----------|-------------|---------|
|
|
23
|
-
| `{{PRD_PATH}}` | Path to the PRD folder |
|
|
31
|
+
| `{{PRD_PATH}}` | Path to the PRD folder | `.dw/spec/prd-user-onboarding` |
|
|
24
32
|
|
|
25
33
|
## Objectives
|
|
26
34
|
|
|
@@ -37,8 +45,9 @@ When available in the project under `./.agents/skills/`, use these skills as ope
|
|
|
37
45
|
- PRD: `{{PRD_PATH}}/prd.md`
|
|
38
46
|
- TechSpec: `{{PRD_PATH}}/techspec.md`
|
|
39
47
|
- Tasks: `{{PRD_PATH}}/tasks.md`
|
|
40
|
-
- Project Rules:
|
|
41
|
-
- QA Test Credentials:
|
|
48
|
+
- Project Rules: `.dw/rules/`
|
|
49
|
+
- QA Test Credentials: `.dw/templates/qa-test-credentials.md`
|
|
50
|
+
- Playwright Patterns: `.dw/references/playwright-patterns.md`
|
|
42
51
|
- Evidence folder (required): `{{PRD_PATH}}/QA/`
|
|
43
52
|
- Output Report: `{{PRD_PATH}}/QA/qa-report.md`
|
|
44
53
|
- Bugs found: `{{PRD_PATH}}/QA/bugs.md`
|
|
@@ -56,7 +65,7 @@ Identify the projects with a testable frontend via Playwright by checking the pr
|
|
|
56
65
|
| Web frontend | `http://localhost:3000` | (check project config) |
|
|
57
66
|
| Admin frontend | `http://localhost:4000` | (check project config) |
|
|
58
67
|
|
|
59
|
-
Refer to
|
|
68
|
+
Refer to `.dw/rules/` for project-specific URLs and frameworks.
|
|
60
69
|
|
|
61
70
|
## Process Steps
|
|
62
71
|
|
|
@@ -83,13 +92,13 @@ Refer to `ai/rules/` for project-specific URLs and frameworks.
|
|
|
83
92
|
- `{{PRD_PATH}}/QA/screenshots/`
|
|
84
93
|
- `{{PRD_PATH}}/QA/logs/`
|
|
85
94
|
- `{{PRD_PATH}}/QA/scripts/`
|
|
86
|
-
- Read
|
|
95
|
+
- Read `.dw/templates/qa-test-credentials.md` and choose the appropriate user/profile for the scenario
|
|
87
96
|
- Verify the application is running on localhost
|
|
88
97
|
- Use `browser_navigate` from Playwright MCP to access the application
|
|
89
98
|
- Confirm the page loaded correctly with `browser_snapshot`
|
|
90
99
|
- If persistent session, auth import, network inspection beyond MCP, or browser-first reproduction is needed, complement with `agent-browser`
|
|
91
100
|
|
|
92
|
-
###
|
|
101
|
+
### 3. Menu Page Verification (Required -- Execute BEFORE RF tests)
|
|
93
102
|
|
|
94
103
|
<critical>BEFORE testing individual RFs, verify that EACH menu item in the module leads to a FUNCTIONAL and UNIQUE page. This verification is blocking -- if it fails, QA CANNOT be approved.</critical>
|
|
95
104
|
|
|
@@ -117,7 +126,16 @@ For each menu item in the module:
|
|
|
117
126
|
- Capture screenshot with suffix `-STUB-FAIL.png`
|
|
118
127
|
- QA CANNOT have APPROVED status while stub pages exist in the menu
|
|
119
128
|
|
|
120
|
-
|
|
129
|
+
**Menu Verification Decision Flow:**
|
|
130
|
+
```dot
|
|
131
|
+
digraph menu_check {
|
|
132
|
+
"Check Menu Items" -> "All functional?" [shape=diamond];
|
|
133
|
+
"All functional?" -> "Proceed to RF tests" [label="yes"];
|
|
134
|
+
"All functional?" -> "Report STUB BUG\nFAIL QA" [label="no"];
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 4. E2E Tests with Playwright MCP (Required)
|
|
121
139
|
|
|
122
140
|
Use Playwright MCP tools to test each flow:
|
|
123
141
|
|
|
@@ -150,7 +168,7 @@ For each functional requirement from the PRD:
|
|
|
150
168
|
<critical>It is not enough to validate only the happy path. Each requirement must be exercised against its boundary states and most likely regressions</critical>
|
|
151
169
|
<critical>If a requirement cannot be fully validated via E2E, QA must be marked as REJECTED or BLOCKED, never APPROVED</critical>
|
|
152
170
|
|
|
153
|
-
###
|
|
171
|
+
### 4.1. Required Minimum Matrix per Requirement
|
|
154
172
|
|
|
155
173
|
For each RF, QA must explicitly answer:
|
|
156
174
|
|
|
@@ -172,7 +190,7 @@ Examples of edge cases that must be considered whenever relevant:
|
|
|
172
190
|
- re-entrance/repeated actions
|
|
173
191
|
- API failures, loading, and intermediate states
|
|
174
192
|
|
|
175
|
-
###
|
|
193
|
+
### 5. Accessibility Checks (Required)
|
|
176
194
|
|
|
177
195
|
Verify for each screen/component (WCAG 2.2):
|
|
178
196
|
|
|
@@ -188,14 +206,14 @@ Verify for each screen/component (WCAG 2.2):
|
|
|
188
206
|
Use `browser_press_key` to test keyboard navigation.
|
|
189
207
|
Use `browser_snapshot` to verify labels and semantic structure.
|
|
190
208
|
|
|
191
|
-
###
|
|
209
|
+
### 6. Visual Checks (Required)
|
|
192
210
|
|
|
193
211
|
- Capture screenshots of main screens with `browser_take_screenshot` and save to `{{PRD_PATH}}/QA/screenshots/`
|
|
194
212
|
- Check layouts in different states (empty, with data, error, loading)
|
|
195
213
|
- Document visual inconsistencies found
|
|
196
214
|
- Check responsiveness if applicable (different viewports)
|
|
197
215
|
|
|
198
|
-
###
|
|
216
|
+
### 7. Bug Documentation (If issues found)
|
|
199
217
|
|
|
200
218
|
For each bug found, create an entry in `{{PRD_PATH}}/QA/bugs.md`:
|
|
201
219
|
|
|
@@ -214,7 +232,7 @@ For each bug found, create an entry in `{{PRD_PATH}}/QA/bugs.md`:
|
|
|
214
232
|
- **Status:** Open
|
|
215
233
|
```
|
|
216
234
|
|
|
217
|
-
###
|
|
235
|
+
### 8. QA Report (Required)
|
|
218
236
|
|
|
219
237
|
Generate report in `{{PRD_PATH}}/QA/qa-report.md`:
|
|
220
238
|
|
|
@@ -283,7 +301,8 @@ Generate report in `{{PRD_PATH}}/QA/qa-report.md`:
|
|
|
283
301
|
- Check API calls with `browser_network_requests` and save in `QA/logs/network.log`
|
|
284
302
|
- Save executed E2E test scripts in `QA/scripts/` for reuse and audit
|
|
285
303
|
- For projects using shadcn/ui + Tailwind, verify components follow the design system
|
|
286
|
-
- Use
|
|
304
|
+
- Use `.dw/templates/qa-test-credentials.md` as the official source of login credentials for QA
|
|
305
|
+
- See `.dw/references/playwright-patterns.md` for common test patterns
|
|
287
306
|
- Do not mark a requirement as validated based solely on unit tests, integration tests, code inference, or partial execution
|
|
288
307
|
- If the implementation requires historical data or specific state to validate an edge case, prepare that state and execute the case
|
|
289
308
|
- If there is insufficient time or environment to fully cover a requirement, record it explicitly as a blocker and reject QA
|
|
@@ -4,20 +4,30 @@ You are an AI assistant responsible for implementing software development tasks.
|
|
|
4
4
|
<critical>You must not rush to finish the task. Always check the necessary files, verify the tests, and go through a reasoning process to ensure both understanding and correct execution.</critical>
|
|
5
5
|
<critical>THE TASK CANNOT BE CONSIDERED COMPLETE UNTIL ALL TESTS ARE PASSING</critical>
|
|
6
6
|
|
|
7
|
+
## When to Use
|
|
8
|
+
- Use when executing a single task from a PRD's tasks.md with built-in Level 1 validation
|
|
9
|
+
- Do NOT use when you need to execute ALL tasks sequentially (use `/dw-run-plan` instead)
|
|
10
|
+
- Do NOT use when fixing a bug report (use `/dw-bugfix` instead)
|
|
11
|
+
|
|
12
|
+
## Pipeline Position
|
|
13
|
+
**Predecessor:** `/dw-create-tasks` | **Successor:** `/dw-run-task` (next task) or `/dw-review-implementation`
|
|
14
|
+
|
|
7
15
|
## Complementary Skills
|
|
8
16
|
|
|
9
17
|
When available in the project at `./.agents/skills/`, use these skills as specialized support without replacing this command:
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
| Skill | Trigger |
|
|
20
|
+
|-------|---------|
|
|
21
|
+
| `vercel-react-best-practices` | Task touches React rendering, hydration, data fetching, bundle, cache, or performance |
|
|
22
|
+
| `webapp-testing` | Task has interactive frontend needing E2E validation in a real browser |
|
|
23
|
+
| `agent-browser` | UI validation requires persistent session, operational navigation inspection, or complementary visual evidence |
|
|
14
24
|
|
|
15
25
|
## File Locations
|
|
16
26
|
|
|
17
27
|
- PRD: `./spec/prd-[feature-name]/prd.md`
|
|
18
28
|
- Tech Spec: `./spec/prd-[feature-name]/techspec.md`
|
|
19
29
|
- Tasks: `./spec/prd-[feature-name]/tasks.md`
|
|
20
|
-
- Project Rules:
|
|
30
|
+
- Project Rules: `.dw/rules/`
|
|
21
31
|
|
|
22
32
|
## Steps to Execute
|
|
23
33
|
|
|
@@ -35,7 +45,7 @@ When available in the project at `./.agents/skills/`, use these skills as specia
|
|
|
35
45
|
Analyze considering:
|
|
36
46
|
- Main objectives of the task
|
|
37
47
|
- How the task fits into the project context
|
|
38
|
-
- Alignment with project rules and patterns (
|
|
48
|
+
- Alignment with project rules and patterns (`.dw/rules/`)
|
|
39
49
|
- Possible solutions or approaches
|
|
40
50
|
- If React/Next.js is in scope, explicitly incorporate relevant heuristics from `vercel-react-best-practices`
|
|
41
51
|
|
|
@@ -72,7 +82,7 @@ After providing the summary and approach, **begin implementation immediately**:
|
|
|
72
82
|
|
|
73
83
|
**YOU MUST** start the implementation right after the process above.
|
|
74
84
|
|
|
75
|
-
<critical>Use the Context7 MCP to look up documentation for the language, frameworks, and libraries involved in the implementation</critical>
|
|
85
|
+
<critical>Use the Context7 MCP to look up framework/library documentation for the language, frameworks, and libraries involved in the implementation</critical>
|
|
76
86
|
|
|
77
87
|
## Important Notes
|
|
78
88
|
|
|
@@ -105,7 +115,7 @@ pnpm test # or npm test
|
|
|
105
115
|
- [ ] Code compiles without errors
|
|
106
116
|
- [ ] Lint passes
|
|
107
117
|
- [ ] Multi-tenancy respected (if applicable)
|
|
108
|
-
- [ ] Project patterns followed (
|
|
118
|
+
- [ ] Project patterns followed (`.dw/rules/`)
|
|
109
119
|
|
|
110
120
|
### Functional UI Verification (for tasks with frontend)
|
|
111
121
|
<critical>Placeholder/stub pages are NOT acceptable deliverables for user interaction FRs.</critical>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Playwright Test Patterns
|
|
2
|
+
|
|
3
|
+
Reference for `/dw-run-qa` and `/dw-functional-doc`. Common E2E patterns.
|
|
4
|
+
|
|
5
|
+
## 1. Authenticated Navigation
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { test, expect } from "@playwright/test";
|
|
9
|
+
|
|
10
|
+
test("navigate authenticated route", async ({ page }) => {
|
|
11
|
+
// Login
|
|
12
|
+
await page.goto("/login");
|
|
13
|
+
await page.getByLabel("Email").fill("user@test.com");
|
|
14
|
+
await page.getByLabel("Password").fill("password123");
|
|
15
|
+
await page.getByRole("button", { name: "Sign in" }).click();
|
|
16
|
+
|
|
17
|
+
// Wait for redirect
|
|
18
|
+
await page.waitForURL("/dashboard");
|
|
19
|
+
await expect(page.getByRole("heading", { name: /dashboard/i })).toBeVisible();
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 2. Form Submission with Validation
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
test("submit form with validation errors", async ({ page }) => {
|
|
27
|
+
await page.goto("/users/new");
|
|
28
|
+
|
|
29
|
+
// Submit empty → validation errors
|
|
30
|
+
await page.getByRole("button", { name: "Save" }).click();
|
|
31
|
+
await expect(page.getByText("Name is required")).toBeVisible();
|
|
32
|
+
await expect(page.getByText("Email is required")).toBeVisible();
|
|
33
|
+
|
|
34
|
+
// Fill and submit → success
|
|
35
|
+
await page.getByLabel("Name").fill("Jane Doe");
|
|
36
|
+
await page.getByLabel("Email").fill("jane@example.com");
|
|
37
|
+
await page.getByRole("button", { name: "Save" }).click();
|
|
38
|
+
|
|
39
|
+
await expect(page.getByText("User created successfully")).toBeVisible();
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 3. Table with Filtering and Pagination
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
test("filter and paginate table", async ({ page }) => {
|
|
47
|
+
await page.goto("/users");
|
|
48
|
+
|
|
49
|
+
// Verify initial load
|
|
50
|
+
const rows = page.locator("table tbody tr");
|
|
51
|
+
await expect(rows).toHaveCount(10); // default page size
|
|
52
|
+
|
|
53
|
+
// Filter
|
|
54
|
+
await page.getByPlaceholder("Search...").fill("admin");
|
|
55
|
+
await expect(rows).toHaveCount(2); // filtered results
|
|
56
|
+
|
|
57
|
+
// Clear and paginate
|
|
58
|
+
await page.getByPlaceholder("Search...").clear();
|
|
59
|
+
await page.getByRole("button", { name: "Next" }).click();
|
|
60
|
+
await expect(page.getByText("Page 2")).toBeVisible();
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 4. Modal / Dialog Interaction
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
test("open modal, fill form, confirm", async ({ page }) => {
|
|
68
|
+
await page.goto("/projects");
|
|
69
|
+
|
|
70
|
+
// Open modal
|
|
71
|
+
await page.getByRole("button", { name: "New Project" }).click();
|
|
72
|
+
const dialog = page.getByRole("dialog");
|
|
73
|
+
await expect(dialog).toBeVisible();
|
|
74
|
+
|
|
75
|
+
// Fill modal form
|
|
76
|
+
await dialog.getByLabel("Project Name").fill("My Project");
|
|
77
|
+
await dialog.getByRole("button", { name: "Create" }).click();
|
|
78
|
+
|
|
79
|
+
// Modal closes, item appears in list
|
|
80
|
+
await expect(dialog).not.toBeVisible();
|
|
81
|
+
await expect(page.getByText("My Project")).toBeVisible();
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 5. Permission / Access Denied
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
test("restricted user sees access denied", async ({ page }) => {
|
|
89
|
+
// Login as restricted user
|
|
90
|
+
await page.goto("/login");
|
|
91
|
+
await page.getByLabel("Email").fill("restricted@test.com");
|
|
92
|
+
await page.getByLabel("Password").fill("password123");
|
|
93
|
+
await page.getByRole("button", { name: "Sign in" }).click();
|
|
94
|
+
|
|
95
|
+
// Try to access admin route
|
|
96
|
+
await page.goto("/admin/settings");
|
|
97
|
+
|
|
98
|
+
// Verify access denied
|
|
99
|
+
await expect(page.getByText(/access denied|forbidden|not authorized/i)).toBeVisible();
|
|
100
|
+
// OR redirect to dashboard
|
|
101
|
+
await expect(page).toHaveURL(/dashboard/);
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## 6. API Error Handling
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
test("handles API error gracefully", async ({ page }) => {
|
|
109
|
+
// Intercept API to simulate error
|
|
110
|
+
await page.route("**/api/users", (route) =>
|
|
111
|
+
route.fulfill({ status: 500, body: "Internal Server Error" })
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
await page.goto("/users");
|
|
115
|
+
|
|
116
|
+
// Verify error state
|
|
117
|
+
await expect(page.getByText(/error|something went wrong/i)).toBeVisible();
|
|
118
|
+
|
|
119
|
+
// Verify retry button works
|
|
120
|
+
await page.unroute("**/api/users");
|
|
121
|
+
await page.getByRole("button", { name: /retry|try again/i }).click();
|
|
122
|
+
await expect(page.locator("table tbody tr")).toHaveCount(10);
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Evidence Capture Pattern
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
// Use in any test to capture evidence
|
|
130
|
+
await test.step("Capture evidence", async () => {
|
|
131
|
+
await page.screenshot({
|
|
132
|
+
path: `evidence/screenshots/${testInfo.title}-${Date.now()}.png`,
|
|
133
|
+
fullPage: true
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
```
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Refactoring Catalog — Before/After Examples
|
|
2
|
+
|
|
3
|
+
Reference for `/dw-refactoring-analysis`. Based on Fowler's refactoring catalog.
|
|
4
|
+
|
|
5
|
+
## 1. Long Function → Extract Function
|
|
6
|
+
|
|
7
|
+
**Smell:** Function with >15 lines of logic, multiple responsibilities.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// ❌ BEFORE: 30+ lines doing validation, transformation, and persistence
|
|
11
|
+
async function processOrder(order: Order) {
|
|
12
|
+
if (!order.items.length) throw new Error("Empty order");
|
|
13
|
+
if (order.total < 0) throw new Error("Invalid total");
|
|
14
|
+
if (!order.customer) throw new Error("No customer");
|
|
15
|
+
|
|
16
|
+
const discount = order.customer.isPremium
|
|
17
|
+
? order.total * 0.1
|
|
18
|
+
: order.total > 100 ? order.total * 0.05 : 0;
|
|
19
|
+
const tax = (order.total - discount) * 0.15;
|
|
20
|
+
const finalTotal = order.total - discount + tax;
|
|
21
|
+
|
|
22
|
+
order.discount = discount;
|
|
23
|
+
order.tax = tax;
|
|
24
|
+
order.total = finalTotal;
|
|
25
|
+
order.status = "processed";
|
|
26
|
+
|
|
27
|
+
await db.orders.update(order.id, order);
|
|
28
|
+
await emailService.send(order.customer.email, "Order processed", { order });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ✅ AFTER: Each function has one responsibility
|
|
32
|
+
async function processOrder(order: Order) {
|
|
33
|
+
validateOrder(order);
|
|
34
|
+
const pricing = calculatePricing(order);
|
|
35
|
+
const processed = applyPricing(order, pricing);
|
|
36
|
+
await persistOrder(processed);
|
|
37
|
+
await notifyCustomer(processed);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 2. Feature Envy → Move Method
|
|
42
|
+
|
|
43
|
+
**Smell:** Function accesses another object's data more than its own.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// ❌ BEFORE: calculateShipping knows too much about Address
|
|
47
|
+
function calculateShipping(order: Order) {
|
|
48
|
+
const addr = order.address;
|
|
49
|
+
if (addr.country === "US" && addr.state === "CA") return 5.99;
|
|
50
|
+
if (addr.country === "US") return 9.99;
|
|
51
|
+
return 19.99 + (addr.isRemote ? 10 : 0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ✅ AFTER: Address owns its shipping logic
|
|
55
|
+
class Address {
|
|
56
|
+
getShippingCost(): number {
|
|
57
|
+
if (this.country === "US" && this.state === "CA") return 5.99;
|
|
58
|
+
if (this.country === "US") return 9.99;
|
|
59
|
+
return 19.99 + (this.isRemote ? 10 : 0);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 3. Primitive Obsession → Value Object
|
|
65
|
+
|
|
66
|
+
**Smell:** Using raw strings/numbers for domain concepts (emails, money, dates).
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// ❌ BEFORE: email is just a string everywhere
|
|
70
|
+
function sendEmail(to: string, subject: string) {
|
|
71
|
+
if (!to.includes("@")) throw new Error("Invalid email");
|
|
72
|
+
// ...
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ✅ AFTER: Email is a value object with built-in validation
|
|
76
|
+
class Email {
|
|
77
|
+
constructor(private readonly value: string) {
|
|
78
|
+
if (!value.includes("@")) throw new Error("Invalid email");
|
|
79
|
+
}
|
|
80
|
+
toString() { return this.value; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sendEmail(to: Email, subject: string) { /* ... */ }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 4. Duplicated Logic → Extract Shared Utility
|
|
87
|
+
|
|
88
|
+
**Smell:** Same 3+ lines of logic appear in multiple places.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// ❌ BEFORE: date formatting repeated in 4 components
|
|
92
|
+
const formatted = `${date.getFullYear()}-${String(date.getMonth()+1).padStart(2,"0")}-${String(date.getDate()).padStart(2,"0")}`;
|
|
93
|
+
|
|
94
|
+
// ✅ AFTER: single utility
|
|
95
|
+
function formatDate(date: Date): string {
|
|
96
|
+
return date.toISOString().split("T")[0];
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## 5. God Component → Split by Responsibility
|
|
101
|
+
|
|
102
|
+
**Smell:** React component with 200+ lines, multiple useEffects, mixed concerns.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// ❌ BEFORE: UserDashboard handles data fetching, filtering, rendering, modals
|
|
106
|
+
function UserDashboard() {
|
|
107
|
+
// 50 lines of state + effects
|
|
108
|
+
// 30 lines of handlers
|
|
109
|
+
// 120 lines of JSX with inline conditions
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ✅ AFTER: Split into container + presentation + hook
|
|
113
|
+
function useUserDashboard() { /* data fetching + state */ }
|
|
114
|
+
function UserFilters({ filters, onChange }) { /* filter UI */ }
|
|
115
|
+
function UserTable({ users, onSelect }) { /* table UI */ }
|
|
116
|
+
function UserDashboard() {
|
|
117
|
+
const { users, filters, setFilters } = useUserDashboard();
|
|
118
|
+
return (
|
|
119
|
+
<>
|
|
120
|
+
<UserFilters filters={filters} onChange={setFilters} />
|
|
121
|
+
<UserTable users={users} />
|
|
122
|
+
</>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## 6. Complex Conditional → Strategy Pattern / Early Return
|
|
128
|
+
|
|
129
|
+
**Smell:** Nested if/else chains with 4+ branches.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// ❌ BEFORE: nested conditionals
|
|
133
|
+
function getPrice(user: User, product: Product) {
|
|
134
|
+
if (user.type === "premium") {
|
|
135
|
+
if (product.category === "electronics") {
|
|
136
|
+
return product.price * 0.8;
|
|
137
|
+
} else {
|
|
138
|
+
return product.price * 0.9;
|
|
139
|
+
}
|
|
140
|
+
} else if (user.type === "wholesale") {
|
|
141
|
+
return product.price * 0.7;
|
|
142
|
+
} else {
|
|
143
|
+
return product.price;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ✅ AFTER: strategy map + early return
|
|
148
|
+
const DISCOUNT_STRATEGIES: Record<string, (p: Product) => number> = {
|
|
149
|
+
premium: (p) => p.category === "electronics" ? 0.8 : 0.9,
|
|
150
|
+
wholesale: () => 0.7,
|
|
151
|
+
standard: () => 1.0,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
function getPrice(user: User, product: Product) {
|
|
155
|
+
const discount = DISCOUNT_STRATEGIES[user.type] ?? DISCOUNT_STRATEGIES.standard;
|
|
156
|
+
return product.price * discount(product);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Priority Assessment Guide
|
|
161
|
+
|
|
162
|
+
| Severity | Criteria | Action |
|
|
163
|
+
|----------|----------|--------|
|
|
164
|
+
| **P0 - Critical** | Security risk, data corruption, breaking API contract | Fix immediately |
|
|
165
|
+
| **P1 - High** | >3 duplications, god objects, untestable code | Fix in current sprint |
|
|
166
|
+
| **P2 - Medium** | Long functions, primitive obsession, feature envy | Schedule for refactoring |
|
|
167
|
+
| **P3 - Low** | Minor naming issues, small duplications, style | Fix opportunistically |
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Brainstorm: {{TOPIC}}
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
{{CONTEXT_DESCRIPTION}}
|
|
6
|
+
|
|
7
|
+
## Options Matrix
|
|
8
|
+
|
|
9
|
+
| Criteria | Option A: {{NAME_A}} | Option B: {{NAME_B}} | Option C: {{NAME_C}} |
|
|
10
|
+
|----------|----------------------|----------------------|----------------------|
|
|
11
|
+
| **Approach** | | | |
|
|
12
|
+
| **Effort** | Low / Medium / High | Low / Medium / High | Low / Medium / High |
|
|
13
|
+
| **Risk** | Low / Medium / High | Low / Medium / High | Low / Medium / High |
|
|
14
|
+
| **Scalability** | | | |
|
|
15
|
+
| **Maintainability** | | | |
|
|
16
|
+
| **Dependencies** | | | |
|
|
17
|
+
|
|
18
|
+
## Trade-offs
|
|
19
|
+
|
|
20
|
+
### Option A: {{NAME_A}}
|
|
21
|
+
- **Pros:**
|
|
22
|
+
- **Cons:**
|
|
23
|
+
- **Best when:**
|
|
24
|
+
|
|
25
|
+
### Option B: {{NAME_B}}
|
|
26
|
+
- **Pros:**
|
|
27
|
+
- **Cons:**
|
|
28
|
+
- **Best when:**
|
|
29
|
+
|
|
30
|
+
### Option C: {{NAME_C}}
|
|
31
|
+
- **Pros:**
|
|
32
|
+
- **Cons:**
|
|
33
|
+
- **Best when:**
|
|
34
|
+
|
|
35
|
+
## Recommendation
|
|
36
|
+
|
|
37
|
+
**Recommended:** Option {{X}}
|
|
38
|
+
|
|
39
|
+
**Rationale:** {{WHY}}
|
|
40
|
+
|
|
41
|
+
## Next Steps
|
|
42
|
+
|
|
43
|
+
- [ ] Validate with stakeholders
|
|
44
|
+
- [ ] Create PRD: `/dw-create-prd`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Functional Dossier
|
|
2
|
+
|
|
3
|
+
- **Project:** {{projectName}}
|
|
4
|
+
- **Target:** {{target}}
|
|
5
|
+
- **Type:** {{targetType}}
|
|
6
|
+
- **Detected framework:** {{framework}}
|
|
7
|
+
- **Base URL:** {{baseUrl}}
|
|
8
|
+
- **E2E Runner:** {{playwrightStatus}}
|
|
9
|
+
- **Generated at:** {{generatedAt}}
|
|
10
|
+
|
|
11
|
+
## Summary
|
|
12
|
+
|
|
13
|
+
{{summary}}
|
|
14
|
+
|
|
15
|
+
## Analyzed Sources
|
|
16
|
+
|
|
17
|
+
{{sources}}
|
|
18
|
+
|
|
19
|
+
## Blockers
|
|
20
|
+
|
|
21
|
+
{{blockers}}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
const BASE_URL = process.env.BASE_URL ?? "{{baseUrl}}";
|
|
4
|
+
|
|
5
|
+
test("{{testTitle}}", async ({ page }) => {
|
|
6
|
+
const evidence = [] as string[];
|
|
7
|
+
|
|
8
|
+
await test.step("Open target route", async () => {
|
|
9
|
+
await page.goto(`${BASE_URL}{{routePath}}`);
|
|
10
|
+
await expect(page).toHaveURL(new RegExp("{{routeRegex}}"));
|
|
11
|
+
evidence.push("navigation");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
{{testSteps}}
|
|
15
|
+
|
|
16
|
+
await test.step("Record final context", async () => {
|
|
17
|
+
expect(evidence.length).toBeGreaterThan(0);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
- **Bug:** {{ORIGINAL_PROBLEM}}
|
|
4
|
+
- **Root Cause:** {{WHY_IT_HAPPENED}}
|
|
5
|
+
- **Solution:** {{WHAT_WAS_FIXED}}
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
{{CHANGES_LIST}}
|
|
10
|
+
|
|
11
|
+
## Test Evidence
|
|
12
|
+
|
|
13
|
+
- **Before fix:** {{SCREENSHOT_OR_ERROR_BEFORE}}
|
|
14
|
+
- **After fix:** {{SCREENSHOT_OR_RETEST_PASS}}
|
|
15
|
+
- **Related unit tests:** {{TEST_LIST}}
|
|
16
|
+
|
|
17
|
+
## Test Plan
|
|
18
|
+
|
|
19
|
+
- [ ] Unit tests pass
|
|
20
|
+
- [ ] Build succeeds
|
|
21
|
+
- [ ] Lint passes
|
|
22
|
+
- [ ] Manually tested the fix scenario
|
|
23
|
+
- [ ] Verified no regression in related features
|
|
24
|
+
|
|
25
|
+
## Related
|
|
26
|
+
|
|
27
|
+
- Issue: {{ISSUE_LINK}}
|
|
28
|
+
- Bugfix analysis: `.dw/spec/bugfix-{{NAME}}/prd.md` (if applicable)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# QA Test Credentials
|
|
2
|
+
|
|
3
|
+
## Credential Profiles
|
|
4
|
+
|
|
5
|
+
| Profile | Login | Password | Role | Scope | Use When |
|
|
6
|
+
|---------|-------|----------|------|-------|----------|
|
|
7
|
+
| **Admin** | admin@test.com | {{PASSWORD}} | Administrator | Full access | Testing admin flows, user management, settings |
|
|
8
|
+
| **Standard User** | user@test.com | {{PASSWORD}} | User | Standard access | Testing happy paths, main user flows |
|
|
9
|
+
| **Restricted** | restricted@test.com | {{PASSWORD}} | Viewer | Read-only | Testing permission blocks, access denied scenarios |
|
|
10
|
+
| **Multi-tenant A** | tenant-a@test.com | {{PASSWORD}} | User | Tenant A | Testing tenant isolation, data segregation |
|
|
11
|
+
| **Multi-tenant B** | tenant-b@test.com | {{PASSWORD}} | User | Tenant B | Testing cross-tenant access blocks |
|
|
12
|
+
|
|
13
|
+
## Password Fallback Order
|
|
14
|
+
|
|
15
|
+
If the primary password fails, try in order:
|
|
16
|
+
1. `{{PRIMARY_PASSWORD}}`
|
|
17
|
+
2. `{{FALLBACK_1}}`
|
|
18
|
+
3. `{{FALLBACK_2}}`
|
|
19
|
+
|
|
20
|
+
If all fail, mark authentication as **BLOCKED** in the manifest.
|
|
21
|
+
|
|
22
|
+
## Login Method
|
|
23
|
+
|
|
24
|
+
- **URL:** {{LOGIN_URL}}
|
|
25
|
+
- **Auth provider:** {{AUTH_PROVIDER}} (e.g., Keycloak, Auth0, NextAuth)
|
|
26
|
+
- **Login field:** Email / Username / CPF (choose based on provider)
|
|
27
|
+
- **Notes:** {{NOTES}}
|
|
28
|
+
|
|
29
|
+
## Selection Guide
|
|
30
|
+
|
|
31
|
+
| Bug / Flow Type | Recommended Profile | Why |
|
|
32
|
+
|-----------------|--------------------|----|
|
|
33
|
+
| Happy path testing | Standard User | Represents typical usage |
|
|
34
|
+
| Permission denied tests | Restricted | Validates access control |
|
|
35
|
+
| Admin features | Admin | Full access needed |
|
|
36
|
+
| Multi-tenant isolation | Tenant A + Tenant B | Tests data boundaries |
|
|
37
|
+
| Auth/login flow | All profiles | Tests each access level |
|
|
@@ -31,4 +31,4 @@ Each task follows this flow:
|
|
|
31
31
|
1. `/execute-task [N]_task.md` - Implements the task
|
|
32
32
|
2. Unit tests included in the implementation
|
|
33
33
|
3. Commit at the end of each task (no push)
|
|
34
|
-
4. Next task or `/generate-pr [target-branch]` when all tasks are completed
|
|
34
|
+
4. Next task or `/dw-generate-pr [target-branch]` when all tasks are completed
|