@pageai/ralph-loop 1.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.
Files changed (120) hide show
  1. package/.agent/PROMPT.md +58 -0
  2. package/.agent/STEERING.md +3 -0
  3. package/.agent/logs/LOG.md +13 -0
  4. package/.agent/prd/.gitkeep +0 -0
  5. package/.agent/screenshots/.gitkeep +0 -0
  6. package/.agent/skills/component-refactoring/SKILL.md +247 -0
  7. package/.agent/skills/component-refactoring/references/complexity-patterns.md +485 -0
  8. package/.agent/skills/component-refactoring/references/component-splitting.md +419 -0
  9. package/.agent/skills/component-refactoring/references/hook-extraction.md +317 -0
  10. package/.agent/skills/e2e-tester/SKILL.md +595 -0
  11. package/.agent/skills/frontend-code-review/SKILL.md +73 -0
  12. package/.agent/skills/frontend-code-review/references/code-quality.md +28 -0
  13. package/.agent/skills/frontend-code-review/references/performance.md +36 -0
  14. package/.agent/skills/frontend-testing/SKILL.md +316 -0
  15. package/.agent/skills/frontend-testing/assets/component-test.template.tsx +293 -0
  16. package/.agent/skills/frontend-testing/assets/hook-test.template.ts +207 -0
  17. package/.agent/skills/frontend-testing/assets/utility-test.template.ts +154 -0
  18. package/.agent/skills/frontend-testing/references/async-testing.md +345 -0
  19. package/.agent/skills/frontend-testing/references/checklist.md +188 -0
  20. package/.agent/skills/frontend-testing/references/common-patterns.md +449 -0
  21. package/.agent/skills/frontend-testing/references/mocking.md +289 -0
  22. package/.agent/skills/frontend-testing/references/workflow.md +265 -0
  23. package/.agent/skills/prd-creator/JSON.md +613 -0
  24. package/.agent/skills/prd-creator/PRD.md +196 -0
  25. package/.agent/skills/prd-creator/SKILL.md +143 -0
  26. package/.agent/skills/skill-creator/SKILL.md +355 -0
  27. package/.agent/skills/skill-creator/references/output-patterns.md +86 -0
  28. package/.agent/skills/skill-creator/references/workflows.md +28 -0
  29. package/.agent/skills/skill-creator/scripts/init_skill.py +300 -0
  30. package/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
  31. package/.agent/skills/vercel-react-best-practices/AGENTS.md +2249 -0
  32. package/.agent/skills/vercel-react-best-practices/SKILL.md +125 -0
  33. package/.agent/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  34. package/.agent/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
  35. package/.agent/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  36. package/.agent/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
  37. package/.agent/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
  38. package/.agent/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  39. package/.agent/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  40. package/.agent/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
  41. package/.agent/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  42. package/.agent/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  43. package/.agent/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  44. package/.agent/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  45. package/.agent/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  46. package/.agent/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  47. package/.agent/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +82 -0
  48. package/.agent/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  49. package/.agent/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  50. package/.agent/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  51. package/.agent/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  52. package/.agent/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  53. package/.agent/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  54. package/.agent/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  55. package/.agent/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  56. package/.agent/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  57. package/.agent/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  58. package/.agent/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  59. package/.agent/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  60. package/.agent/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  61. package/.agent/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  62. package/.agent/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  63. package/.agent/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  64. package/.agent/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  65. package/.agent/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  66. package/.agent/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  67. package/.agent/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  68. package/.agent/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  69. package/.agent/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  70. package/.agent/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  71. package/.agent/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  72. package/.agent/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  73. package/.agent/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  74. package/.agent/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  75. package/.agent/skills/vercel-react-best-practices/rules/server-cache-react.md +26 -0
  76. package/.agent/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +79 -0
  77. package/.agent/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  78. package/.agent/skills/vitest-best-practices/AGENTS.md +84 -0
  79. package/.agent/skills/vitest-best-practices/SKILL.md +130 -0
  80. package/.agent/skills/vitest-best-practices/references/aaa-pattern.md +260 -0
  81. package/.agent/skills/vitest-best-practices/references/assertions.md +393 -0
  82. package/.agent/skills/vitest-best-practices/references/async-testing.md +454 -0
  83. package/.agent/skills/vitest-best-practices/references/error-handling.md +382 -0
  84. package/.agent/skills/vitest-best-practices/references/organization.md +212 -0
  85. package/.agent/skills/vitest-best-practices/references/parameterized-tests.md +297 -0
  86. package/.agent/skills/vitest-best-practices/references/performance.md +528 -0
  87. package/.agent/skills/vitest-best-practices/references/snapshot-testing.md +483 -0
  88. package/.agent/skills/vitest-best-practices/references/test-doubles.md +499 -0
  89. package/.agent/skills/vitest-best-practices/references/vitest-features.md +529 -0
  90. package/.agent/skills/web-design-guidelines/SKILL.md +39 -0
  91. package/.agent/tasks/.gitkeep +0 -0
  92. package/.agent/tasks.json +1 -0
  93. package/.claude/agents/code-reviewer.md +172 -0
  94. package/.claude/commands/aw.md +50 -0
  95. package/.claude/hooks/play-sound.js +87 -0
  96. package/.claude/hooks/pre-tool-use.js +40 -0
  97. package/.claude/settings.json +54 -0
  98. package/.claude/settings.local.json +13 -0
  99. package/.mcp.json +31 -0
  100. package/AGENTS.md +44 -0
  101. package/CLAUDE.md +1 -0
  102. package/README.md +236 -0
  103. package/bin/cli.js +156 -0
  104. package/bin/lib/copy.js +149 -0
  105. package/bin/lib/display.js +137 -0
  106. package/package.json +65 -0
  107. package/ralph.sh +333 -0
  108. package/scripts/lib/args.sh +44 -0
  109. package/scripts/lib/cleanup.sh +53 -0
  110. package/scripts/lib/constants.sh +25 -0
  111. package/scripts/lib/display.sh +196 -0
  112. package/scripts/lib/logging.sh +30 -0
  113. package/scripts/lib/notify.sh +41 -0
  114. package/scripts/lib/output.sh +147 -0
  115. package/scripts/lib/preflight.sh +57 -0
  116. package/scripts/lib/preview.sh +77 -0
  117. package/scripts/lib/promise.sh +76 -0
  118. package/scripts/lib/spinner.sh +85 -0
  119. package/scripts/lib/terminal.sh +57 -0
  120. package/scripts/lib/timing.sh +223 -0
@@ -0,0 +1,595 @@
1
+ ---
2
+ name: e2e-tester
3
+ description: >
4
+ Playwright E2E testing patterns.
5
+ Trigger: When writing Playwright E2E tests (Page Object Model, selectors, MCP exploration workflow).
6
+ metadata:
7
+ scope: [root, ui]
8
+ auto_invoke: "Writing Playwright E2E tests"
9
+ allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
10
+ ---
11
+
12
+ Before you start:
13
+ - check if any existing tests cover the functionality you are testing
14
+ - check if a unit tests can cover the feature better
15
+ - analyze if a end to end test brings any value over a unit test
16
+
17
+ If the answer is yes to any of the above, proceed with the end to end test creation.
18
+
19
+ ## MCP Workflow (MANDATORY If Available)
20
+
21
+ **⚠️ If you have Playwright MCP tools, ALWAYS use them BEFORE creating any test:**
22
+
23
+ 1. **Navigate** to target page
24
+ 2. **Take snapshot** to see page structure and elements
25
+ 3. **Interact** with forms/elements to verify exact user flow
26
+ 4. **Take screenshots** to document expected states
27
+ 5. **Verify page transitions** through complete flow (loading, success, error)
28
+ 6. **Document actual selectors** from snapshots (use real refs and labels)
29
+ 7. **Only after exploring** create test code with verified selectors
30
+
31
+ **If MCP NOT available:** Proceed with test creation based on docs and code analysis.
32
+
33
+ **Why This Matters:**
34
+ - ✅ Precise tests - exact steps needed, no assumptions
35
+ - ✅ Accurate selectors - real DOM structure, not imagined
36
+ - ✅ Real flow validation - verify journey actually works
37
+ - ✅ Avoid over-engineering - minimal tests for what exists
38
+ - ✅ Prevent flaky tests - real exploration = stable tests
39
+ - ❌ Never assume how UI "should" work
40
+
41
+ ## Waiting Strategies (CRITICAL)
42
+
43
+ ```typescript
44
+ // ❌ NEVER use fixed waits
45
+ await page.waitForTimeout(2000);
46
+
47
+ // ✅ Wait for specific conditions
48
+ await expect(element).toBeVisible();
49
+ await page.waitForResponse(resp => resp.url().includes('/api/data'));
50
+ await page.waitForURL('**/dashboard');
51
+ await expect(page.getByText('Success')).toBeVisible({ timeout: 10000 });
52
+
53
+ // ✅ For SPAs, prefer explicit waits over networkidle
54
+ await page.goto('/app', { waitUntil: 'domcontentloaded' });
55
+ await expect(page.getByRole('main')).toBeVisible();
56
+ ```
57
+
58
+ ## File Structure
59
+
60
+ ```
61
+ tests/
62
+ ├── base-page.ts # Parent class for ALL pages
63
+ ├── helpers.ts # Shared utilities
64
+ └── {page-name}/
65
+ ├── {page-name}-page.ts # Page Object Model
66
+ ├── {page-name}.spec.ts # ALL tests here (NO separate files!)
67
+ └── {page-name}.md # Test documentation
68
+ ```
69
+
70
+ **File Naming:**
71
+ - ✅ `sign-up.spec.ts` (all sign-up tests)
72
+ - ✅ `sign-up-page.ts` (page object)
73
+ - ✅ `sign-up.md` (documentation)
74
+ - ❌ `sign-up-critical-path.spec.ts` (WRONG - no separate files)
75
+ - ❌ `sign-up-validation.spec.ts` (WRONG)
76
+
77
+ ## Selector Priority (REQUIRED)
78
+
79
+ ```typescript
80
+ // 1. BEST - getByRole for interactive elements
81
+ this.submitButton = page.getByRole("button", { name: "Submit" });
82
+ this.navLink = page.getByRole("link", { name: "Dashboard" });
83
+
84
+ // 2. BEST - getByLabel for form controls
85
+ this.emailInput = page.getByLabel("Email");
86
+ this.passwordInput = page.getByLabel("Password");
87
+
88
+ // 3. SPARINGLY - getByText for static content only
89
+ this.errorMessage = page.getByText("Invalid credentials");
90
+ this.pageTitle = page.getByText("Welcome");
91
+
92
+ // 4. LAST RESORT - getByTestId when above fail
93
+ this.customWidget = page.getByTestId("date-picker");
94
+
95
+ // ❌ AVOID fragile selectors
96
+ this.button = page.locator(".btn-primary"); // NO
97
+ this.input = page.locator("#email"); // NO
98
+ ```
99
+
100
+ ## Scope Detection (ASK IF AMBIGUOUS)
101
+
102
+ | User Says | Action |
103
+ | ------------------------------------------------------------------ | ---------------------------------- |
104
+ | "a test", "one test", "new test", "add test" | Create ONE test() in existing spec |
105
+ | "comprehensive tests", "all tests", "test suite", "generate tests" | Create full suite |
106
+
107
+ **Examples:**
108
+ - "Create a test for user sign-up" → ONE test only
109
+ - "Generate E2E tests for login page" → Full suite
110
+ - "Add a test to verify form validation" → ONE test to existing spec
111
+
112
+ ## Page Object Pattern
113
+
114
+ ```typescript
115
+ import { Page, Locator, expect } from "@playwright/test";
116
+
117
+ // BasePage - ALL pages extend this
118
+ export class BasePage {
119
+ constructor(protected page: Page) {}
120
+
121
+ async goto(path: string): Promise<void> {
122
+ await this.page.goto(path);
123
+ await this.page.waitForLoadState("networkidle");
124
+ }
125
+
126
+ // Common methods go here (see Refactoring Guidelines)
127
+ async waitForNotification(): Promise<void> {
128
+ await this.page.waitForSelector('[role="status"]');
129
+ }
130
+
131
+ async verifyNotificationMessage(message: string): Promise<void> {
132
+ const notification = this.page.locator('[role="status"]');
133
+ await expect(notification).toContainText(message);
134
+ }
135
+ }
136
+
137
+ // Page-specific implementation
138
+ export interface LoginData {
139
+ email: string;
140
+ password: string;
141
+ }
142
+
143
+ export class LoginPage extends BasePage {
144
+ readonly emailInput: Locator;
145
+ readonly passwordInput: Locator;
146
+ readonly submitButton: Locator;
147
+
148
+ constructor(page: Page) {
149
+ super(page);
150
+ this.emailInput = page.getByLabel("Email");
151
+ this.passwordInput = page.getByLabel("Password");
152
+ this.submitButton = page.getByRole("button", { name: "Sign in" });
153
+ }
154
+
155
+ async goto(): Promise<void> {
156
+ await super.goto("/login");
157
+ }
158
+
159
+ async login(data: LoginData): Promise<void> {
160
+ await this.emailInput.fill(data.email);
161
+ await this.passwordInput.fill(data.password);
162
+ await this.submitButton.click();
163
+ }
164
+
165
+ async verifyCriticalOutcome(): Promise<void> {
166
+ await expect(this.page).toHaveURL("/dashboard");
167
+ }
168
+ }
169
+ ```
170
+
171
+ ## Page Object Reuse (CRITICAL)
172
+
173
+ **Always check existing page objects before creating new ones!**
174
+
175
+ ```typescript
176
+ // ✅ GOOD: Reuse existing page objects
177
+ import { SignInPage } from "../sign-in/sign-in-page";
178
+ import { HomePage } from "../home/home-page";
179
+
180
+ test("User can sign up and login", async ({ page }) => {
181
+ const signUpPage = new SignUpPage(page);
182
+ const signInPage = new SignInPage(page); // REUSE
183
+ const homePage = new HomePage(page); // REUSE
184
+
185
+ await signUpPage.signUp(userData);
186
+ await homePage.verifyPageLoaded(); // REUSE method
187
+ await homePage.signOut(); // REUSE method
188
+ await signInPage.login(credentials); // REUSE method
189
+ });
190
+
191
+ // ❌ BAD: Recreating existing functionality
192
+ export class SignUpPage extends BasePage {
193
+ async logout() { /* ... */ } // ❌ HomePage already has this
194
+ async login() { /* ... */ } // ❌ SignInPage already has this
195
+ }
196
+ ```
197
+
198
+ **Guidelines:**
199
+ - Check `tests/` for existing page objects first
200
+ - Import and reuse existing pages
201
+ - Create page objects only when page doesn't exist
202
+ - If test requires multiple pages, ensure all page objects exist (create if needed)
203
+
204
+ ## Refactoring Guidelines
205
+
206
+ ### Move to `BasePage` when:
207
+ - ✅ Navigation helpers used by multiple pages (`waitForPageLoad()`, `getCurrentUrl()`)
208
+ - ✅ Common UI interactions (notifications, modals, theme toggles)
209
+ - ✅ Verification patterns repeated across pages (`isVisible()`, `waitForVisible()`)
210
+ - ✅ Error handling that applies to all pages
211
+ - ✅ Screenshot utilities for debugging
212
+
213
+ ### Move to `helpers.ts` when:
214
+ - ✅ Test data generation (`generateUniqueEmail()`, `generateTestUser()`)
215
+ - ✅ Setup/teardown utilities (`createTestUser()`, `cleanupTestData()`)
216
+ - ✅ Custom assertions (`expectNotificationToContain()`)
217
+ - ✅ API helpers for test setup (`seedDatabase()`, `resetState()`)
218
+ - ✅ Time utilities (`waitForCondition()`, `retryAction()`)
219
+
220
+ **Before (BAD):**
221
+ ```typescript
222
+ // Repeated in multiple page objects
223
+ export class SignUpPage extends BasePage {
224
+ async waitForNotification(): Promise<void> {
225
+ await this.page.waitForSelector('[role="status"]');
226
+ }
227
+ }
228
+ export class SignInPage extends BasePage {
229
+ async waitForNotification(): Promise<void> {
230
+ await this.page.waitForSelector('[role="status"]'); // DUPLICATED!
231
+ }
232
+ }
233
+ ```
234
+
235
+ **After (GOOD):**
236
+ ```typescript
237
+ // BasePage - shared across all pages
238
+ export class BasePage {
239
+ async waitForNotification(): Promise<void> {
240
+ await this.page.waitForSelector('[role="status"]');
241
+ }
242
+ }
243
+
244
+ // helpers.ts - data generation
245
+ export function generateUniqueEmail(): string {
246
+ return `test.${Date.now()}@example.com`;
247
+ }
248
+
249
+ export function generateTestUser() {
250
+ return {
251
+ name: "Test User",
252
+ email: generateUniqueEmail(),
253
+ password: "TestPassword123!",
254
+ };
255
+ }
256
+ ```
257
+
258
+ ## Test Pattern with Tags
259
+
260
+ ```typescript
261
+ import { test, expect } from "@playwright/test";
262
+ import { LoginPage } from "./login-page";
263
+
264
+ test.describe("Login", () => {
265
+ test("User can login successfully",
266
+ { tag: ["@critical", "@e2e", "@login", "@LOGIN-E2E-001"] },
267
+ async ({ page }) => {
268
+ const loginPage = new LoginPage(page);
269
+
270
+ await loginPage.goto();
271
+ await loginPage.login({ email: "user@test.com", password: "pass123" });
272
+
273
+ await expect(page).toHaveURL("/dashboard");
274
+ }
275
+ );
276
+ });
277
+ ```
278
+
279
+ **Tag Categories:**
280
+ - Priority: `@critical`, `@high`, `@medium`, `@low`
281
+ - Type: `@e2e`
282
+ - Feature: `@signup`, `@signin`, `@dashboard`
283
+ - Test ID: `@SIGNUP-E2E-001`, `@LOGIN-E2E-002`
284
+
285
+ ## Test Documentation Format ({page-name}.md)
286
+
287
+ ```markdown
288
+ ### E2E Tests: {Feature Name}
289
+
290
+ **Suite ID:** `{SUITE-ID}`
291
+ **Feature:** {Feature description}
292
+
293
+ ---
294
+
295
+ ## Test Case: `{TEST-ID}` - {Test case title}
296
+
297
+ **Priority:** `{critical|high|medium|low}`
298
+
299
+ **Tags:**
300
+ - type → @e2e
301
+ - feature → @{feature-name}
302
+
303
+ **Description/Objective:** {Brief description}
304
+
305
+ **Preconditions:**
306
+ - {Prerequisites for test to run}
307
+ - {Required data or state}
308
+
309
+ ### Flow Steps:
310
+ 1. {Step 1}
311
+ 2. {Step 2}
312
+ 3. {Step 3}
313
+
314
+ ### Expected Result:
315
+ - {Expected outcome 1}
316
+ - {Expected outcome 2}
317
+
318
+ ### Key verification points:
319
+ - {Assertion 1}
320
+ - {Assertion 2}
321
+
322
+ ### Notes:
323
+ - {Additional considerations}
324
+ ```
325
+
326
+ **Documentation Rules:**
327
+ - ❌ NO general test running instructions
328
+ - ❌ NO file structure explanations
329
+ - ❌ NO code examples or tutorials
330
+ - ❌ NO troubleshooting sections
331
+ - ✅ Focus ONLY on specific test case
332
+ - ✅ Keep under 60 lines when possible
333
+
334
+ ## Authentication State Reuse
335
+
336
+ ```typescript
337
+ // auth.setup.ts - Run once, reuse across tests
338
+ import { test as setup } from "@playwright/test";
339
+
340
+ setup("authenticate", async ({ page }) => {
341
+ await page.goto("/login");
342
+ await page.getByLabel("Email").fill("user@test.com");
343
+ await page.getByLabel("Password").fill("password");
344
+ await page.getByRole("button", { name: "Sign in" }).click();
345
+ await page.waitForURL("/dashboard");
346
+ await page.context().storageState({ path: ".auth/user.json" });
347
+ });
348
+
349
+ // playwright.config.ts
350
+ projects: [
351
+ { name: "auth", testMatch: /auth\.setup\.ts/ },
352
+ { name: "logged-in", dependencies: ["auth"], use: { storageState: ".auth/user.json" } },
353
+ { name: "logged-out", use: { storageState: { cookies: [], origins: [] } } }
354
+ ]
355
+ ```
356
+
357
+ ## API Mocking
358
+
359
+ ```typescript
360
+ // Mock API responses for isolated tests
361
+ await page.route("**/api/users", route =>
362
+ route.fulfill({ json: { users: [{ id: 1, name: "Test" }] } })
363
+ );
364
+
365
+ // Mock error states
366
+ await page.route("**/api/submit", route =>
367
+ route.fulfill({ status: 500, json: { error: "Server error" } })
368
+ );
369
+
370
+ // Abort requests (e.g., block analytics)
371
+ await page.route("**/analytics/**", route => route.abort());
372
+ ```
373
+
374
+ ## Test Fixtures
375
+
376
+ ```typescript
377
+ // fixtures.ts - Custom fixtures for setup/teardown
378
+ import { test as base } from "@playwright/test";
379
+ import { AdminPage } from "./admin/admin-page";
380
+
381
+ export const test = base.extend<{ adminPage: AdminPage }>({
382
+ adminPage: async ({ page }, use) => {
383
+ const admin = new AdminPage(page);
384
+ await admin.goto();
385
+ await use(admin);
386
+ // Teardown runs after test
387
+ },
388
+ });
389
+
390
+ // Usage in tests
391
+ test("admin can manage users", async ({ adminPage }) => {
392
+ await adminPage.deleteUser("test@example.com");
393
+ });
394
+ ```
395
+
396
+ ## Parallel Execution
397
+
398
+ ```typescript
399
+ // Tests run in parallel by default. Use serial when tests share state:
400
+ test.describe.configure({ mode: "serial" });
401
+
402
+ // Isolate test data to prevent conflicts
403
+ test("create user", async ({ page }) => {
404
+ const uniqueEmail = `user-${Date.now()}@test.com`; // ✅ Unique per run
405
+ });
406
+ ```
407
+
408
+ ## Assertions
409
+
410
+ ```typescript
411
+ // Soft assertions - collect multiple failures
412
+ await expect.soft(page.getByText("Title")).toBeVisible();
413
+ await expect.soft(page.getByText("Subtitle")).toBeVisible();
414
+ // Test continues, reports all failures at end
415
+
416
+ // Polling assertions - retry until condition met
417
+ await expect(async () => {
418
+ const count = await page.getByRole("listitem").count();
419
+ expect(count).toBeGreaterThan(5);
420
+ }).toPass({ timeout: 10000 });
421
+
422
+ // Visual regression
423
+ await expect(page).toHaveScreenshot("dashboard.png");
424
+ await expect(page.getByRole("dialog")).toHaveScreenshot();
425
+ ```
426
+
427
+ ## Common Pitfalls
428
+
429
+ ```typescript
430
+ // ❌ Element detached after navigation
431
+ const button = page.getByRole("button", { name: "Submit" });
432
+ await button.click();
433
+ await button.click(); // May fail if page navigated
434
+
435
+ // ✅ Re-query after navigation
436
+ await page.getByRole("button", { name: "Submit" }).click();
437
+ await page.getByRole("button", { name: "Confirm" }).click();
438
+
439
+ // ❌ Race condition with animations
440
+ await element.click();
441
+
442
+ // ✅ Wait for animation to complete
443
+ await element.click();
444
+ await expect(modal).toBeVisible();
445
+
446
+ // Iframes
447
+ const frame = page.frameLocator("#iframe-id");
448
+ await frame.getByRole("button").click();
449
+
450
+ // Shadow DOM - Playwright pierces by default, but for closed shadow:
451
+ await page.locator("custom-element").locator("internal:shadow=button").click();
452
+ ```
453
+
454
+ ## Mobile & Viewport Testing
455
+
456
+ ```typescript
457
+ // In test file
458
+ test.use({ viewport: { width: 375, height: 667 } });
459
+
460
+ // Device emulation
461
+ import { devices } from "@playwright/test";
462
+ test.use({ ...devices["iPhone 13"] });
463
+
464
+ // Or in playwright.config.ts projects
465
+ projects: [
466
+ { name: "desktop", use: { viewport: { width: 1280, height: 720 } } },
467
+ { name: "mobile", use: { ...devices["iPhone 13"] } },
468
+ ]
469
+ ```
470
+
471
+ ## Debugging & Traces
472
+
473
+ ```bash
474
+ # Enable traces on failure (recommended for CI)
475
+ npx playwright test --trace on-first-retry
476
+
477
+ # View trace file
478
+ npx playwright show-trace trace.zip
479
+
480
+ # Debug mode - step through test
481
+ npx playwright test --debug
482
+
483
+ # Headed mode to see browser
484
+ npx playwright test --headed
485
+ ```
486
+
487
+ ```typescript
488
+ // In playwright.config.ts
489
+ use: {
490
+ trace: "on-first-retry", // Capture trace on retry
491
+ screenshot: "only-on-failure", // Screenshot on failure
492
+ video: "retain-on-failure", // Video on failure
493
+ }
494
+ ```
495
+
496
+ ## CI/CD Configuration
497
+
498
+ ```typescript
499
+ // playwright.config.ts for CI
500
+ export default defineConfig({
501
+ retries: process.env.CI ? 2 : 0,
502
+ workers: process.env.CI ? 1 : undefined,
503
+ reporter: process.env.CI
504
+ ? [["html"], ["github"], ["junit", { outputFile: "results.xml" }]]
505
+ : [["html"]],
506
+ use: {
507
+ trace: "on-first-retry",
508
+ screenshot: "only-on-failure",
509
+ },
510
+ });
511
+ ```
512
+
513
+ ```yaml
514
+ # GitHub Actions example
515
+ - name: Run Playwright tests
516
+ run: npx playwright test
517
+ - uses: actions/upload-artifact@v4
518
+ if: always()
519
+ with:
520
+ name: playwright-report
521
+ path: playwright-report/
522
+ ```
523
+
524
+ ## Running Tests (PREFER PARTIAL RUNS)
525
+
526
+ **Always prefer running specific tests over the entire suite** for faster feedback:
527
+
528
+ ```bash
529
+ # Run ALL tests
530
+ npx playwright test
531
+
532
+ # ✅ PREFERRED: Run specific file
533
+ npx playwright test tests/login/login.spec.ts
534
+
535
+ # ✅ PREFERRED: Run specific folder
536
+ npx playwright test tests/login/
537
+
538
+ # ✅ PREFERRED: Run by test name (grep)
539
+ npx playwright test --grep "login"
540
+ npx playwright test --grep "user can sign up"
541
+
542
+ # ✅ PREFERRED: Run by tag
543
+ npx playwright test --grep "@critical"
544
+ npx playwright test --grep "@LOGIN-E2E-001"
545
+
546
+ # ✅ Run single test by line number
547
+ npx playwright test tests/login/login.spec.ts:42
548
+
549
+ # ✅ Run tests matching multiple patterns
550
+ npx playwright test --grep "login|signup"
551
+
552
+ # ✅ Exclude tests by pattern
553
+ npx playwright test --grep-invert "slow"
554
+ ```
555
+
556
+ ### Other Commands
557
+
558
+ ```bash
559
+ npx playwright test --debug # Debug mode (step through)
560
+ npx playwright test --headed # See browser
561
+ npx playwright test --trace on # Enable tracing
562
+ npx playwright test --project=mobile # Run specific project
563
+ npx playwright codegen # Record tests
564
+ ```
565
+
566
+ ## Partial String Matching (CRITICAL for Robustness)
567
+
568
+ **Always use partial/pattern matching instead of exact strings** to reduce maintenance:
569
+
570
+ ```typescript
571
+ // ❌ FRAGILE: Exact string matches break easily
572
+ await expect(page.getByText("Welcome to Our Application!")).toBeVisible();
573
+ await expect(page.getByRole("button", { name: "Submit Form" })).toBeVisible();
574
+ await expect(notification).toHaveText("Your account has been created successfully.");
575
+
576
+ // ✅ ROBUST: Partial matches survive copy changes
577
+ await expect(page.getByText(/welcome/i)).toBeVisible();
578
+ await expect(page.getByRole("button", { name: /submit/i })).toBeVisible();
579
+ await expect(notification).toContainText("account");
580
+ await expect(notification).toContainText(/created/i);
581
+
582
+ // ✅ ROBUST: Use toContainText over toHaveText
583
+ await expect(page.locator(".message")).toContainText("success");
584
+
585
+ // ✅ ROBUST: Pattern matching for dynamic content
586
+ await expect(page.getByText(/order #\d+/i)).toBeVisible();
587
+ await expect(page.getByText(/\d+ items? in cart/i)).toBeVisible();
588
+ ```
589
+
590
+ **Why Partial Matching:**
591
+ - ✅ Survives minor copy/text changes
592
+ - ✅ Works across locales (case-insensitive)
593
+ - ✅ Less brittle to whitespace changes
594
+ - ✅ Easier to maintain long-term
595
+ - ❌ Exact matches break on punctuation, capitalization, or wording tweaks
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: frontend-code-review
3
+ description: "Trigger when the user requests a review of frontend files (e.g., `.tsx`, `.ts`, `.js`). Support both pending-change reviews and focused file reviews while applying the checklist rules."
4
+ ---
5
+
6
+ # Frontend Code Review
7
+
8
+ ## Intent
9
+ Use this skill whenever the user asks to review frontend code (especially `.tsx`, `.ts`, or `.js` files). Support two review modes:
10
+
11
+ 1. **Pending-change review** – inspect staged/working-tree files slated for commit and flag checklist violations before submission.
12
+ 2. **File-targeted review** – review the specific file(s) the user names and report the relevant checklist findings.
13
+
14
+ Stick to the checklist below for every applicable file and mode.
15
+
16
+ ## Checklist
17
+ See [references/code-quality.md](references/code-quality.md), [references/performance.md](references/performance.md), for the living checklist split by category—treat it as the canonical set of rules to follow.
18
+
19
+ Flag each rule violation with urgency metadata so future reviewers can prioritize fixes.
20
+
21
+ ## Review Process
22
+ 1. Open the relevant component/module. Gather lines that relate to class names, React Flow hooks, prop memoization, and styling.
23
+ 2. For each rule in the review point, note where the code deviates and capture a representative snippet.
24
+ 3. Compose the review section per the template below. Group violations first by **Urgent** flag, then by category order (Code Quality, Performance, Business Logic).
25
+
26
+ ## Required output
27
+ When invoked, the response must exactly follow one of the two templates:
28
+
29
+ ### Template A (any findings)
30
+ ```
31
+ # Code review
32
+ Found <N> urgent issues need to be fixed:
33
+
34
+ ## 1 <brief description of bug>
35
+ FilePath: <path> line <line>
36
+ <relevant code snippet or pointer>
37
+
38
+
39
+ ### Suggested fix
40
+ <brief description of suggested fix>
41
+
42
+ ---
43
+ ... (repeat for each urgent issue) ...
44
+
45
+ Found <M> suggestions for improvement:
46
+
47
+ ## 1 <brief description of suggestion>
48
+ FilePath: <path> line <line>
49
+ <relevant code snippet or pointer>
50
+
51
+
52
+ ### Suggested fix
53
+ <brief description of suggested fix>
54
+
55
+ ---
56
+
57
+ ... (repeat for each suggestion) ...
58
+ ```
59
+
60
+ If there are no urgent issues, omit that section. If there are no suggestions, omit that section.
61
+
62
+ If the issue number is more than 10, summarize as "10+ urgent issues" or "10+ suggestions" and just output the first 10 issues.
63
+
64
+ Don't compress the blank lines between sections; keep them as-is for readability.
65
+
66
+ If you use Template A (i.e., there are issues to fix) and at least one issue requires code changes, append a brief follow-up question after the structured output asking whether the user wants you to apply the suggested fix(es). For example: "Would you like me to use the Suggested fix section to address these issues?"
67
+
68
+ ### Template B (no issues)
69
+ ```
70
+ ## Code review
71
+ No issues found.
72
+ ```
73
+
@@ -0,0 +1,28 @@
1
+ # Rule Catalog — Code Quality
2
+
3
+ ## Tailwind-first styling
4
+
5
+ IsUrgent: True
6
+ Category: Code Quality
7
+
8
+ ### Description
9
+
10
+ Favor Tailwind CSS utility classes instead of adding new `.module.css` files unless a Tailwind combination cannot achieve the required styling. Keeping styles in Tailwind improves consistency and reduces maintenance overhead.
11
+
12
+ Update this file when adding, editing, or removing Code Quality rules so the catalog remains accurate.
13
+
14
+ ## Classname ordering for easy overrides
15
+
16
+ ### Description
17
+
18
+ When writing components, always place the incoming `className` prop after the component’s own class values so that downstream consumers can override or extend the styling. This keeps your component’s defaults but still lets external callers change or remove specific styles.
19
+
20
+ Example:
21
+
22
+ ```tsx
23
+ import { cn } from '@/utils/classnames'
24
+
25
+ const Button = ({ className }) => {
26
+ return <div className={cn('bg-primary-600', className)}></div>
27
+ }
28
+ ```