@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,466 @@
1
+ # Visual Regression Testing
2
+
3
+ Visual regression testing (VRT) catches unintended visual changes by comparing screenshots against approved baselines.
4
+
5
+ ## When to Use VRT
6
+
7
+ **Good candidates:**
8
+ - Design system components
9
+ - Critical landing pages
10
+ - Complex layouts (grids, dashboards)
11
+ - Components with CSS that changes frequently
12
+ - Cross-browser visual consistency
13
+
14
+ **Poor candidates:**
15
+ - Pages with frequently changing dynamic content
16
+ - Components with animations
17
+ - Time-dependent displays
18
+ - Pages with third-party content (ads, embeds)
19
+
20
+ ## Playwright Visual Testing
21
+
22
+ ### Basic Screenshot Comparison
23
+
24
+ ```javascript
25
+ import { test, expect } from "@playwright/test";
26
+
27
+ test("homepage looks correct", async ({ page }) => {
28
+ await page.goto("/");
29
+
30
+ await expect(page).toHaveScreenshot();
31
+ });
32
+ ```
33
+
34
+ First run creates the baseline. Subsequent runs compare against it.
35
+
36
+ ### Named Screenshots
37
+
38
+ ```javascript
39
+ test("hero section renders correctly", async ({ page }) => {
40
+ await page.goto("/");
41
+
42
+ await expect(page).toHaveScreenshot("homepage-hero.png");
43
+ });
44
+ ```
45
+
46
+ ### Element Screenshots
47
+
48
+ More stable than full-page screenshots:
49
+
50
+ ```javascript
51
+ test("product card renders correctly", async ({ page }) => {
52
+ await page.goto("/products/123");
53
+
54
+ const productCard = page.getByTestId("product-card");
55
+ await expect(productCard).toHaveScreenshot("product-card.png");
56
+ });
57
+ ```
58
+
59
+ ### Full Page Screenshots
60
+
61
+ ```javascript
62
+ test("full page layout", async ({ page }) => {
63
+ await page.goto("/about");
64
+
65
+ await expect(page).toHaveScreenshot("about-page-full.png", {
66
+ fullPage: true,
67
+ });
68
+ });
69
+ ```
70
+
71
+ ## Handling Instability
72
+
73
+ Visual tests fail due to timing, rendering, or environment differences. Stabilize tests before trusting results.
74
+
75
+ ### Wait for Network and Fonts
76
+
77
+ ```javascript
78
+ test("page renders after loading", async ({ page }) => {
79
+ await page.goto("/");
80
+
81
+ // Wait for network to be idle
82
+ await page.waitForLoadState("networkidle");
83
+
84
+ // Wait for web fonts to load
85
+ await page.evaluate(() => document.fonts.ready);
86
+
87
+ await expect(page).toHaveScreenshot();
88
+ });
89
+ ```
90
+
91
+ ### Disable Animations
92
+
93
+ ```javascript
94
+ // playwright.config.js
95
+ export default defineConfig({
96
+ use: {
97
+ // Disable CSS animations and transitions
98
+ contextOptions: {
99
+ reducedMotion: "reduce",
100
+ },
101
+ },
102
+ });
103
+ ```
104
+
105
+ Or in test:
106
+
107
+ ```javascript
108
+ test("static screenshot", async ({ page }) => {
109
+ await page.goto("/");
110
+
111
+ // Inject CSS to disable animations
112
+ await page.addStyleTag({
113
+ content: `
114
+ *, *::before, *::after {
115
+ animation-duration: 0s !important;
116
+ transition-duration: 0s !important;
117
+ }
118
+ `,
119
+ });
120
+
121
+ await expect(page).toHaveScreenshot();
122
+ });
123
+ ```
124
+
125
+ ### Mask Dynamic Content
126
+
127
+ Hide elements that change between runs:
128
+
129
+ ```javascript
130
+ test("page with masked dynamic content", async ({ page }) => {
131
+ await page.goto("/dashboard");
132
+
133
+ await expect(page).toHaveScreenshot({
134
+ mask: [
135
+ page.locator("[data-testid='current-time']"),
136
+ page.locator("[data-testid='user-avatar']"),
137
+ page.locator(".ad-container"),
138
+ ],
139
+ });
140
+ });
141
+ ```
142
+
143
+ ### Fixed Viewport
144
+
145
+ Lock viewport size for consistent screenshots:
146
+
147
+ ```javascript
148
+ // playwright.config.js
149
+ export default defineConfig({
150
+ use: {
151
+ viewport: { width: 1280, height: 720 },
152
+ },
153
+ });
154
+ ```
155
+
156
+ Or per-test:
157
+
158
+ ```javascript
159
+ test("mobile view", async ({ page }) => {
160
+ await page.setViewportSize({ width: 375, height: 667 });
161
+ await page.goto("/");
162
+ await expect(page).toHaveScreenshot("homepage-mobile.png");
163
+ });
164
+ ```
165
+
166
+ ### Hide Scrollbars
167
+
168
+ Scrollbars vary by OS:
169
+
170
+ ```javascript
171
+ await page.addStyleTag({
172
+ content: `
173
+ ::-webkit-scrollbar { display: none; }
174
+ * { scrollbar-width: none; }
175
+ `,
176
+ });
177
+ ```
178
+
179
+ ## Tolerance Settings
180
+
181
+ Allow minor pixel differences to reduce flakiness.
182
+
183
+ ### Global Configuration
184
+
185
+ ```javascript
186
+ // playwright.config.js
187
+ export default defineConfig({
188
+ expect: {
189
+ toHaveScreenshot: {
190
+ maxDiffPixels: 100, // Allow up to 100 different pixels
191
+ maxDiffPixelRatio: 0.01, // Or 1% of total pixels
192
+ threshold: 0.2, // Color difference threshold (0-1)
193
+ },
194
+ },
195
+ });
196
+ ```
197
+
198
+ ### Per-Screenshot Settings
199
+
200
+ ```javascript
201
+ await expect(page).toHaveScreenshot({
202
+ maxDiffPixels: 50,
203
+ threshold: 0.3,
204
+ });
205
+ ```
206
+
207
+ ### Threshold Guidelines
208
+
209
+ | Setting | Value | Use Case |
210
+ |---------|-------|----------|
211
+ | `threshold` | 0.1 | Strict pixel matching |
212
+ | `threshold` | 0.2 | Standard tolerance (default) |
213
+ | `threshold` | 0.3 | Generous for anti-aliasing |
214
+ | `maxDiffPixels` | 50 | Small components |
215
+ | `maxDiffPixels` | 200 | Full pages |
216
+ | `maxDiffPixelRatio` | 0.01 | 1% tolerance |
217
+
218
+ ## Updating Baselines
219
+
220
+ ### When to Update
221
+
222
+ - Intentional design changes
223
+ - New features affecting layout
224
+ - Browser rendering engine updates
225
+ - After fixing visual bugs
226
+
227
+ ### Update Command
228
+
229
+ ```bash
230
+ npx playwright test --update-snapshots
231
+ ```
232
+
233
+ **Never update blindly.** Review changes before committing:
234
+
235
+ ```bash
236
+ # See what changed
237
+ git diff --stat
238
+ # Review each screenshot
239
+ open tests/screenshots/
240
+ ```
241
+
242
+ ### Workflow
243
+
244
+ 1. Test fails due to visual difference
245
+ 2. Open test report, review diff image
246
+ 3. If change is intentional:
247
+ - Update baseline with `--update-snapshots`
248
+ - Commit new baseline
249
+ 4. If change is unintentional:
250
+ - Fix the code
251
+ - Run test again
252
+
253
+ ## Cross-Browser Testing
254
+
255
+ Screenshots differ between browsers due to rendering differences.
256
+
257
+ ### Browser-Specific Baselines
258
+
259
+ Playwright automatically creates separate baselines per browser:
260
+
261
+ ```
262
+ example.spec.ts-snapshots/
263
+ ├── homepage-chromium-linux.png
264
+ ├── homepage-firefox-linux.png
265
+ └── homepage-webkit-linux.png
266
+ ```
267
+
268
+ ### Testing Specific Browsers
269
+
270
+ ```javascript
271
+ // playwright.config.js
272
+ export default defineConfig({
273
+ projects: [
274
+ {
275
+ name: "chromium",
276
+ use: { ...devices["Desktop Chrome"] },
277
+ },
278
+ {
279
+ name: "firefox",
280
+ use: { ...devices["Desktop Firefox"] },
281
+ },
282
+ {
283
+ name: "webkit",
284
+ use: { ...devices["Desktop Safari"] },
285
+ },
286
+ ],
287
+ });
288
+ ```
289
+
290
+ ### OS Differences
291
+
292
+ Font rendering varies by OS. Options:
293
+ - Run visual tests in CI only (consistent environment)
294
+ - Use Docker with consistent fonts
295
+ - Generate baselines in CI, not locally
296
+
297
+ ## CI/CD Configuration
298
+
299
+ ### GitHub Actions Example
300
+
301
+ ```yaml
302
+ - name: Run visual tests
303
+ run: npx playwright test --project=chromium
304
+
305
+ - name: Upload diff artifacts on failure
306
+ if: failure()
307
+ uses: actions/upload-artifact@v7
308
+ with:
309
+ name: visual-diff
310
+ path: test-results/
311
+ ```
312
+
313
+ ### Generate Baselines in CI
314
+
315
+ For consistent baselines, generate in CI:
316
+
317
+ ```yaml
318
+ - name: Update baselines
319
+ run: npx playwright test --update-snapshots
320
+
321
+ - name: Commit baselines
322
+ run: |
323
+ git config user.name "CI Bot"
324
+ git add "**/*.png"
325
+ git commit -m "Update visual baselines"
326
+ git push
327
+ ```
328
+
329
+ ## Organizing Screenshots
330
+
331
+ ### Directory Structure
332
+
333
+ ```
334
+ tests/
335
+ ├── visual/
336
+ │ ├── homepage.spec.ts
337
+ │ └── components/
338
+ │ ├── button.spec.ts
339
+ │ └── card.spec.ts
340
+ └── visual.spec.ts-snapshots/
341
+ ├── homepage-chromium.png
342
+ └── button-default-chromium.png
343
+ ```
344
+
345
+ ### Custom Path Template
346
+
347
+ ```javascript
348
+ // playwright.config.js
349
+ export default defineConfig({
350
+ snapshotPathTemplate: "{testDir}/__snapshots__/{projectName}/{testFilePath}/{arg}{ext}",
351
+ });
352
+ ```
353
+
354
+ ## Reporting and Review
355
+
356
+ ### Test Report
357
+
358
+ ```bash
359
+ npx playwright show-report
360
+ ```
361
+
362
+ Shows:
363
+ - Expected (baseline) image
364
+ - Actual (current) image
365
+ - Diff highlighting changed pixels
366
+
367
+ ### Artifacts on Failure
368
+
369
+ Configure to save all artifacts:
370
+
371
+ ```javascript
372
+ // playwright.config.js
373
+ export default defineConfig({
374
+ use: {
375
+ trace: "on-first-retry",
376
+ screenshot: "only-on-failure",
377
+ },
378
+ outputDir: "test-results/",
379
+ });
380
+ ```
381
+
382
+ ## Anti-Patterns
383
+
384
+ ### Removing Platform Suffix
385
+
386
+ ```javascript
387
+ // BAD: Removes platform/browser suffix from snapshot names
388
+ test.beforeEach(async ({}, testInfo) => {
389
+ testInfo.snapshotSuffix = "";
390
+ });
391
+
392
+ // Results in single baseline used across all platforms:
393
+ // button.png ← Used for Chrome/Mac, Firefox/Linux, Safari/Windows...
394
+ ```
395
+
396
+ This seems appealing for "cleaner" snapshot names, but it breaks visual testing. Per [Playwright documentation](https://playwright.dev/docs/test-snapshots):
397
+
398
+ > "Different snapshots are needed for different browsers and platforms due to variations in rendering and fonts."
399
+
400
+ **Why it fails:**
401
+ - Font rendering differs between macOS, Windows, and Linux
402
+ - Anti-aliasing algorithms vary by OS and browser
403
+ - Subpixel rendering produces different results
404
+ - You'll get constant false positives or false negatives
405
+
406
+ **Keep the default naming:**
407
+ ```
408
+ button-chromium-darwin.png
409
+ button-chromium-linux.png
410
+ button-firefox-linux.png
411
+ ```
412
+
413
+ Yes, it's more files. But each baseline accurately represents that specific environment.
414
+
415
+ ### Too Many Full-Page Screenshots
416
+
417
+ ```javascript
418
+ // BAD: Full page with dynamic content
419
+ await expect(page).toHaveScreenshot({ fullPage: true });
420
+
421
+ // GOOD: Target stable regions
422
+ const header = page.getByRole("banner");
423
+ await expect(header).toHaveScreenshot("header.png");
424
+ ```
425
+
426
+ ### No Stabilization
427
+
428
+ ```javascript
429
+ // BAD: Screenshot immediately
430
+ await page.goto("/");
431
+ await expect(page).toHaveScreenshot();
432
+
433
+ // GOOD: Wait for stable state
434
+ await page.goto("/");
435
+ await page.waitForLoadState("networkidle");
436
+ await page.evaluate(() => document.fonts.ready);
437
+ await expect(page).toHaveScreenshot();
438
+ ```
439
+
440
+ ### Overly Strict Tolerance
441
+
442
+ ```javascript
443
+ // BAD: Zero tolerance causes flaky tests
444
+ await expect(page).toHaveScreenshot({ maxDiffPixels: 0 });
445
+
446
+ // GOOD: Allow minor rendering differences
447
+ await expect(page).toHaveScreenshot({ maxDiffPixels: 50 });
448
+ ```
449
+
450
+ ### Updating Without Review
451
+
452
+ ```bash
453
+ # BAD: Blindly update all baselines
454
+ npx playwright test --update-snapshots
455
+
456
+ # GOOD: Review changes first
457
+ npx playwright test
458
+ npx playwright show-report
459
+ # Review diffs, then update if intentional
460
+ npx playwright test --update-snapshots
461
+ ```
462
+
463
+ ## References
464
+
465
+ - [Playwright Visual Comparisons](https://playwright.dev/docs/test-snapshots)
466
+ - [Pixelmatch](https://github.com/mapbox/pixelmatch) — Image comparison library used by Playwright
@@ -0,0 +1,84 @@
1
+ # Refined Plan Mode
2
+
3
+ Use this skill when the user starts a planning session using plan mode and wants the Refined Plan Mode review loop.
4
+
5
+ This skill is additive to the agent's current plan-mode guidance. It turns the plan into a versioned Markdown artifact that can be reviewed with line, range, and text-selection comments. The agent remains responsible for reading the feedback, revising the plan, and moving only when the user has approved the plan or explicitly asks to proceed.
6
+
7
+ ## Core Protocol
8
+
9
+ 1. Clarify only what is necessary to produce a useful plan.
10
+ 2. Write the complete plan to `.plan-review/plans/plan-vN.md`.
11
+ 3. Write the active version, such as `v1`, to `.plan-review/.current-version`.
12
+ 4. Present a short summary to the user and point them at Refined Plan Mode for review.
13
+ 5. After review, read `.plan-review/feedback/plan-vN-feedback.json`.
14
+ 6. Address every feedback item in the next plan version.
15
+ 7. Update `.plan-review/.current-version` to the new version.
16
+ 8. Repeat until the plan is approved.
17
+ 9. When the plan is approved, read `.plan-review/approved-plan.md` and execute it carefully.
18
+
19
+ ## File Convention
20
+
21
+ ```text
22
+ .plan-review/
23
+ .current-version
24
+ plans/
25
+ plan-v1.md
26
+ plan-v2.md
27
+ feedback/
28
+ plan-v1-feedback.json
29
+ plan-v2-feedback.json
30
+ approved-plan.md
31
+ ```
32
+
33
+ Create missing directories when needed. Never summarize or truncate the plan file itself. The file should be self-contained enough for another agent to understand the goal, context, constraints, implementation steps, validation steps, and open questions.
34
+
35
+ ## Plan Shape
36
+
37
+ Prefer this structure unless the task clearly calls for something else:
38
+
39
+ ```markdown
40
+ # Plan vN: Short Title
41
+
42
+ ## Goal
43
+
44
+ ## Current Understanding
45
+
46
+ ## Assumptions
47
+
48
+ ## Open Questions
49
+
50
+ ## Proposed Changes
51
+
52
+ ## Validation
53
+
54
+ ## Risks
55
+
56
+ ## Rollback or Recovery
57
+ ```
58
+
59
+ Keep the plan practical. Include file paths, commands, and decision points when known. Call out assumptions explicitly instead of hiding uncertainty inside confident prose.
60
+
61
+ ## Feedback Handling
62
+
63
+ When feedback exists:
64
+
65
+ - Read the relevant JSON feedback file before revising.
66
+ - Treat every unresolved comment as actionable until clearly addressed.
67
+ - Preserve useful parts of the previous plan.
68
+ - Add a short `Feedback Addressed` section to the revised plan that maps comments to the changes made.
69
+ - If feedback conflicts or cannot be satisfied safely, explain that in the revised plan and ask the user for the smallest useful decision.
70
+
71
+ ## Execution Gate
72
+
73
+ Do not begin implementation from an unapproved plan unless the user explicitly asks you to proceed. Once `.plan-review/approved-plan.md` exists or the user directly approves the plan in conversation, execute the approved plan and keep the normal agent workflow: inspect files, make focused edits, validate, and report the outcome.
74
+
75
+ ## User Updates
76
+
77
+ In conversation, keep updates brief:
78
+
79
+ - Say which plan version was written.
80
+ - Say where feedback should be submitted.
81
+ - Say which feedback file was read when revising.
82
+ - Say when the plan is approved and execution is beginning.
83
+
84
+ The plan file carries the detail; the chat message should help the user orient without duplicating the full artifact.