@schalkneethling/toolkit 0.2.0 → 0.4.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.
Files changed (31) hide show
  1. package/README.md +29 -7
  2. package/commands/rpm-advance.md +13 -0
  3. package/commands/rpm-checkpoint.md +13 -0
  4. package/commands/rpm-feedback.md +15 -0
  5. package/commands/rpm-handoff.md +14 -0
  6. package/commands/rpm-review.md +13 -0
  7. package/commands/rpm-start.md +13 -0
  8. package/dist/index.mjs +90 -6
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +2 -1
  11. package/skills/css-coder/SKILL.md +95 -0
  12. package/skills/css-coder/references/patterns.md +224 -0
  13. package/skills/css-tokens/README.md +152 -0
  14. package/skills/css-tokens/SKILL.md +125 -0
  15. package/skills/css-tokens/references/tokens.css +162 -0
  16. package/skills/frontend-security/SKILL.md +134 -0
  17. package/skills/frontend-security/references/csp-configuration.md +191 -0
  18. package/skills/frontend-security/references/csrf-protection.md +327 -0
  19. package/skills/frontend-security/references/dom-security.md +229 -0
  20. package/skills/frontend-security/references/file-upload-security.md +310 -0
  21. package/skills/frontend-security/references/framework-patterns.md +307 -0
  22. package/skills/frontend-security/references/input-validation.md +232 -0
  23. package/skills/frontend-security/references/jwt-security.md +300 -0
  24. package/skills/frontend-security/references/nodejs-npm-security.md +261 -0
  25. package/skills/frontend-security/references/xss-prevention.md +163 -0
  26. package/skills/frontend-testing/SKILL.md +357 -0
  27. package/skills/frontend-testing/references/accessibility-testing.md +368 -0
  28. package/skills/frontend-testing/references/aria-snapshots.md +517 -0
  29. package/skills/frontend-testing/references/locator-strategies.md +295 -0
  30. package/skills/frontend-testing/references/visual-regression.md +466 -0
  31. package/skills/refined-plan-mode/SKILL.md +84 -0
@@ -0,0 +1,295 @@
1
+ # Locator Strategies
2
+
3
+ Locators determine how tests find elements. Good locators are resilient to implementation changes while verifying accessibility.
4
+
5
+ ## Priority Hierarchy
6
+
7
+ Both Playwright and Testing Library recommend this order. Each level provides accessibility verification as a byproduct.
8
+
9
+ ### 1. Accessible Roles (First Choice)
10
+
11
+ Query the accessibility tree. If this fails, the UI likely has accessibility issues.
12
+
13
+ ```javascript
14
+ // Playwright
15
+ page.getByRole("button", { name: /submit/i })
16
+ page.getByRole("textbox", { name: /email/i })
17
+ page.getByRole("heading", { level: 1 })
18
+ page.getByRole("navigation")
19
+ page.getByRole("listitem")
20
+
21
+ // Testing Library
22
+ screen.getByRole("button", { name: /submit/i })
23
+ screen.getByRole("checkbox", { checked: true })
24
+ ```
25
+
26
+ **Why first**: Users and assistive technologies perceive the page through roles. Testing with roles validates both functionality and accessibility.
27
+
28
+ **Name option**: Filters by accessible name (visible text, aria-label, or aria-labelledby).
29
+
30
+ ```javascript
31
+ // Multiple buttons? Filter by name
32
+ page.getByRole("button", { name: /save/i }) // Save button
33
+ page.getByRole("button", { name: /cancel/i }) // Cancel button
34
+ page.getByRole("button", { name: /delete/i }) // Delete button
35
+ ```
36
+
37
+ **Common roles**:
38
+ - `button`, `link`, `textbox`, `checkbox`, `radio`
39
+ - `combobox` (select), `listbox`, `option`
40
+ - `heading`, `navigation`, `main`, `article`
41
+ - `dialog`, `alert`, `alertdialog`
42
+ - `list`, `listitem`, `table`, `row`, `cell`
43
+ - `tab`, `tablist`, `tabpanel`
44
+
45
+ ### 2. Label Text (Forms)
46
+
47
+ How users find form fields. Validates label-input association.
48
+
49
+ ```javascript
50
+ // Playwright
51
+ page.getByLabel("Email address")
52
+ page.getByLabel(/password/i)
53
+
54
+ // Testing Library
55
+ screen.getByLabelText("Email address")
56
+ screen.getByLabelText(/confirm password/i)
57
+ ```
58
+
59
+ **Why second**: Form users navigate by labels. Tests using labels fail if labels are missing or improperly associated—catching real accessibility bugs.
60
+
61
+ ### 3. Placeholder Text (Fallback for Forms)
62
+
63
+ Use only when labels aren't available. Placeholder is not a label substitute.
64
+
65
+ ```javascript
66
+ page.getByPlaceholder("Search products...")
67
+ screen.getByPlaceholderText("Enter your query")
68
+ ```
69
+
70
+ **Why third**: Better than test IDs, but UI should have proper labels.
71
+
72
+ ### 4. Visible Text (Non-Interactive Content)
73
+
74
+ For elements identified by their content.
75
+
76
+ ```javascript
77
+ // Playwright
78
+ page.getByText("Welcome back, Sarah")
79
+ page.getByText(/order confirmed/i)
80
+
81
+ // Testing Library
82
+ screen.getByText("No results found")
83
+ screen.getByText(/loading/i)
84
+ ```
85
+
86
+ **Why fourth**: Useful for assertions on content, but doesn't verify semantic structure.
87
+
88
+ ### 5. Alt Text (Images)
89
+
90
+ For images with meaningful alt text.
91
+
92
+ ```javascript
93
+ page.getByAltText("Company logo")
94
+ screen.getByAltText(/product photo/i)
95
+ ```
96
+
97
+ ### 6. Title Attribute
98
+
99
+ Rarely used. Most elements shouldn't rely on title for identification.
100
+
101
+ ```javascript
102
+ page.getByTitle("Close dialog")
103
+ screen.getByTitle(/help/i)
104
+ ```
105
+
106
+ ### 7. Test IDs (Last Resort)
107
+
108
+ Escape hatch when semantic queries fail. Provides no accessibility verification.
109
+
110
+ ```javascript
111
+ // Playwright
112
+ page.getByTestId("pricing-calculator")
113
+
114
+ // Testing Library
115
+ screen.getByTestId("data-grid")
116
+ ```
117
+
118
+ **When appropriate**:
119
+ - Complex dynamic components (data grids, charts)
120
+ - Elements with no stable accessible name
121
+ - Third-party components you can't modify
122
+
123
+ **When to avoid**:
124
+ - Any element that has text, label, or semantic role
125
+ - As a default choice to "make tests easier"
126
+
127
+ ## Playwright-Specific Patterns
128
+
129
+ ### Chaining and Filtering
130
+
131
+ Narrow down when multiple matches exist:
132
+
133
+ ```javascript
134
+ // Find delete button in specific row
135
+ const userRow = page.getByRole("row").filter({ hasText: "john@example.com" });
136
+ await userRow.getByRole("button", { name: /delete/i }).click();
137
+
138
+ // Combine locators
139
+ const submitButton = page
140
+ .getByRole("button", { name: /submit/i })
141
+ .and(page.locator("[type=submit]"));
142
+ ```
143
+
144
+ ### Scoped Queries with `within`
145
+
146
+ Test elements within a container:
147
+
148
+ ```javascript
149
+ // Playwright
150
+ const form = page.getByRole("form", { name: /checkout/i });
151
+ await form.getByLabel("Card number").fill("4111111111111111");
152
+
153
+ // Testing Library
154
+ import { within } from "@testing-library/react";
155
+ const form = screen.getByRole("form");
156
+ within(form).getByLabelText("Email");
157
+ ```
158
+
159
+ ### Handling Multiple Elements
160
+
161
+ When strict matching fails:
162
+
163
+ ```javascript
164
+ // Get all, then filter
165
+ const buttons = await page.getByRole("button").all();
166
+ const buttonTexts = await Promise.all(
167
+ buttons.map(button => button.textContent())
168
+ );
169
+ const deleteButtons = buttons.filter((button, index) =>
170
+ buttonTexts[index]?.includes("Delete")
171
+ );
172
+
173
+ // Or be more specific with the query
174
+ page.getByRole("button", { name: "Delete", exact: true });
175
+ ```
176
+
177
+ ## Testing Library Variants
178
+
179
+ ### get vs query vs find
180
+
181
+ | Variant | No match | Multiple matches | Async |
182
+ |---------|----------|------------------|-------|
183
+ | `getBy` | Throws | Throws | No |
184
+ | `queryBy` | Returns null | Throws | No |
185
+ | `findBy` | Throws | Throws | Yes (waits) |
186
+
187
+ ```javascript
188
+ // Element should exist now
189
+ screen.getByRole("button", { name: /submit/i });
190
+
191
+ // Element might not exist (testing absence)
192
+ expect(screen.queryByRole("alert")).not.toBeInTheDocument();
193
+
194
+ // Element appears after async action
195
+ await screen.findByRole("alert");
196
+ ```
197
+
198
+ ### Regex vs String Matching
199
+
200
+ ```javascript
201
+ // Exact match (case-sensitive)
202
+ screen.getByText("Submit Order");
203
+
204
+ // Flexible match (recommended for resilience)
205
+ screen.getByText(/submit order/i);
206
+
207
+ // Partial match
208
+ screen.getByText(/submit/i);
209
+ ```
210
+
211
+ ## Anti-Patterns
212
+
213
+ ### CSS Selectors as Primary Strategy
214
+
215
+ ```javascript
216
+ // BAD: Tied to implementation
217
+ page.locator(".btn-primary");
218
+ page.locator("#submit-form-btn");
219
+ page.locator("div.container > form > button");
220
+
221
+ // GOOD: Tied to user experience
222
+ page.getByRole("button", { name: /submit/i });
223
+ ```
224
+
225
+ ### XPath
226
+
227
+ ```javascript
228
+ // BAD: Brittle, hard to read
229
+ page.locator("//div[@class='form']//button[text()='Submit']");
230
+
231
+ // GOOD: Clear and semantic
232
+ page.getByRole("button", { name: /submit/i });
233
+ ```
234
+
235
+ ### Position-Based Selection
236
+
237
+ ```javascript
238
+ // BAD: Breaks when order changes
239
+ page.getByRole("button").nth(2);
240
+
241
+ // GOOD: Explicit about which element
242
+ page.getByRole("button", { name: /delete account/i });
243
+ ```
244
+
245
+ ### Test IDs for Everything
246
+
247
+ ```javascript
248
+ // BAD: No accessibility verification
249
+ page.getByTestId("submit-button");
250
+ page.getByTestId("email-input");
251
+ page.getByTestId("error-message");
252
+
253
+ // GOOD: Validates accessibility
254
+ page.getByRole("button", { name: /submit/i });
255
+ page.getByLabel("Email");
256
+ page.getByRole("alert");
257
+ ```
258
+
259
+ ## Debugging Locators
260
+
261
+ ### Playwright
262
+
263
+ ```javascript
264
+ // Log all accessible elements
265
+ await page.getByRole("button").evaluateAll(buttons =>
266
+ buttons.map(b => b.textContent)
267
+ );
268
+
269
+ // Use Playwright Inspector
270
+ // npx playwright test --debug
271
+
272
+ // Use codegen for suggestions
273
+ // npx playwright codegen example.com
274
+ ```
275
+
276
+ ### Testing Library
277
+
278
+ ```javascript
279
+ // Log the DOM
280
+ screen.debug();
281
+
282
+ // Log specific element
283
+ screen.debug(screen.getByRole("form"));
284
+
285
+ // Use Testing Playground
286
+ // testing-playground.com
287
+ // Or browser extension
288
+ ```
289
+
290
+ ## References
291
+
292
+ - [Playwright Locators](https://playwright.dev/docs/locators)
293
+ - [Testing Library Queries](https://testing-library.com/docs/queries/about/)
294
+ - [ARIA Roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles)
295
+ - [Testing Library Priority Guide](https://testing-library.com/docs/queries/about/#priority)