@sun-asterisk/sungen 2.2.3 → 2.3.1

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 (86) hide show
  1. package/README.md +6 -6
  2. package/dist/cli/commands/update.d.ts +3 -0
  3. package/dist/cli/commands/update.d.ts.map +1 -0
  4. package/dist/cli/commands/update.js +21 -0
  5. package/dist/cli/commands/update.js.map +1 -0
  6. package/dist/cli/index.js +3 -1
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/generators/gherkin-parser/index.d.ts +2 -0
  9. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  10. package/dist/generators/gherkin-parser/index.js +16 -2
  11. package/dist/generators/gherkin-parser/index.js.map +1 -1
  12. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +3 -0
  13. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +12 -1
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +12 -1
  15. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  16. package/dist/generators/test-generator/patterns/assertion-patterns.js +12 -0
  17. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  18. package/dist/generators/test-generator/patterns/index.d.ts +9 -0
  19. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  20. package/dist/generators/test-generator/patterns/index.js +32 -0
  21. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  22. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
  23. package/dist/generators/test-generator/patterns/interaction-patterns.js +0 -13
  24. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
  25. package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +1 -1
  26. package/dist/generators/test-generator/patterns/table-patterns.js +8 -5
  27. package/dist/generators/test-generator/patterns/table-patterns.js.map +1 -1
  28. package/dist/orchestrator/ai-rules-updater.d.ts +13 -0
  29. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -0
  30. package/dist/orchestrator/ai-rules-updater.js +159 -0
  31. package/dist/orchestrator/ai-rules-updater.js.map +1 -0
  32. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  33. package/dist/orchestrator/project-initializer.js +2 -27
  34. package/dist/orchestrator/project-initializer.js.map +1 -1
  35. package/dist/orchestrator/screen-manager.d.ts +1 -0
  36. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  37. package/dist/orchestrator/screen-manager.js +70 -3
  38. package/dist/orchestrator/screen-manager.js.map +1 -1
  39. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +18 -9
  40. package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +12 -4
  41. package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +9 -11
  42. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-review.md +228 -0
  43. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +30 -11
  44. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +91 -25
  45. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +92 -71
  46. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -5
  47. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +13 -4
  48. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +9 -11
  49. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-review.md +228 -0
  50. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +30 -11
  51. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +72 -31
  52. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +92 -72
  53. package/dist/orchestrator/templates/readme.md +13 -8
  54. package/package.json +1 -1
  55. package/src/cli/commands/update.ts +18 -0
  56. package/src/cli/index.ts +3 -1
  57. package/src/generators/gherkin-parser/index.ts +19 -2
  58. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +3 -0
  59. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +12 -1
  60. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +12 -1
  61. package/src/generators/test-generator/patterns/assertion-patterns.ts +13 -0
  62. package/src/generators/test-generator/patterns/index.ts +41 -0
  63. package/src/generators/test-generator/patterns/interaction-patterns.ts +0 -14
  64. package/src/generators/test-generator/patterns/table-patterns.ts +8 -5
  65. package/src/orchestrator/ai-rules-updater.ts +141 -0
  66. package/src/orchestrator/project-initializer.ts +2 -32
  67. package/src/orchestrator/screen-manager.ts +72 -3
  68. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +18 -9
  69. package/src/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +12 -4
  70. package/src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +9 -11
  71. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-review.md +228 -0
  72. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +30 -11
  73. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +91 -25
  74. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +92 -71
  75. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -5
  76. package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +13 -4
  77. package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +9 -11
  78. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-review.md +228 -0
  79. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +30 -11
  80. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +72 -31
  81. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +92 -72
  82. package/src/orchestrator/templates/readme.md +13 -8
  83. package/docs/gherkin standards/gherkin-core-standard.md +0 -431
  84. package/docs/gherkin standards/gherkin-core-standard.vi.md +0 -399
  85. package/docs/gherkin-dictionary.md +0 -1126
  86. package/docs/makeauth.md +0 -225
@@ -124,6 +124,24 @@ To determine the correct `nth` offset, count how many matching elements appear b
124
124
 
125
125
  ---
126
126
 
127
+ ### Step 4b: Handle Detail Screens with Dynamic IDs
128
+
129
+ For screens like `/admin/users/:id` or `/products/:slug`:
130
+ 1. Navigate to the **list page** first via MCP browser to find a real record ID
131
+ 2. Use that ID in the page selector value
132
+ 3. Use `User is on [X] page` — sungen resolves the path from the selector
133
+
134
+ ```yaml
135
+ # selectors.yaml — full path with real ID
136
+ user detail:
137
+ type: 'page'
138
+ value: '/admin/users/de42d800-0f5a-490e-9dcf-344fedbd34a5'
139
+ ```
140
+
141
+ Note: the selector uses a hardcoded ID from the live page. If the record is deleted, update the ID in `selectors.yaml`.
142
+
143
+ ---
144
+
127
145
  ### Step 5: Handle SPA / Client-side Routing
128
146
 
129
147
  Many modern apps (Next.js, Nuxt, SvelteKit, etc.) return the same HTML shell for all URLs and route client-side. `domcontentloaded` fires on the shell before the target page renders.
@@ -188,54 +206,77 @@ Many elements don't need a YAML entry — sungen auto-infers from the Gherkin la
188
206
 
189
207
  ---
190
208
 
191
- ### Fix Loop on Test Failure (Batched Strategy)
209
+ ### Proactive Selector Validation (before running tests)
192
210
 
193
- Running all tests every iteration is slow. Use a batched approach to fix faster:
211
+ **Most failures are selector mismatches.** Validate selectors against the live page BEFORE running any test — this eliminates slow compile→run→read→fix cycles.
194
212
 
195
- ```
196
- 1. INITIAL RUN — run ALL tests, collect full failure list
197
- npx playwright test <spec> --reporter=line
213
+ After generating `selectors.yaml` (Step 3), verify each entry:
198
214
 
199
- 2. BATCHED FIX LOOP (max 5 attempts):
200
- a. Read test output → group failures by root cause:
201
- - Same selector broken → 1 fix covers many tests
202
- - Same error type (strict mode, timeout, text mismatch)
203
- b. Fix selectors.yaml or test-data.yaml for current batch
204
- c. Recompile: sungen generate --screen <screen>
205
- d. Re-run ONLY previously-failing tests (max 20):
206
- npx playwright test <spec> --grep "VP-UI-001|VP-UI-002|VP-VAL-001" --reporter=line
207
- e. If batch passes → pick next batch of remaining failures
208
- f. If batch still fails → fix and retry (counts toward max 5)
209
-
210
- 3. FINAL CONFIRMATION — run ALL tests once:
211
- npx playwright test <spec> --reporter=line
212
- This catches regressions from selector changes.
215
+ #### How to validate
213
216
 
214
- 4. If still failing after 5 fix attempts ask user about direct .spec.ts fix
217
+ Use `browser_evaluate` to check if each selector actually finds an element on the page:
218
+
219
+ ```js
220
+ // Validate a role selector
221
+ document.querySelectorAll('[role="button"]').length;
222
+ // or check accessible name
223
+ Array.from(document.querySelectorAll('button'))
224
+ .filter(el => el.textContent.includes('Submit') || el.getAttribute('aria-label')?.includes('Submit'))
225
+ .length;
215
226
  ```
216
227
 
217
- #### Building the `--grep` pattern
228
+ Or use `browser_snapshot` and cross-check:
229
+ 1. Read all `[Reference]` entries from `selectors.yaml`
230
+ 2. Take a `browser_snapshot`
231
+ 3. For each entry, verify the element exists in the snapshot
232
+ 4. Fix mismatches immediately — no test run needed
233
+
234
+ #### What to check
235
+
236
+ | Selector type | Validation method |
237
+ |---|---|
238
+ | `role` + `name` | Search snapshot for `role "name"` text |
239
+ | `testid` | `browser_evaluate`: `document.querySelector('[data-testid="xxx"]')` |
240
+ | `placeholder` | Search snapshot for textbox with placeholder |
241
+ | `locator` (CSS) | `browser_evaluate`: `document.querySelector('xxx')` |
242
+
243
+ **Target: fix 80%+ of selector issues before the first test run.**
244
+
245
+ ---
246
+
247
+ ### Batched Test Execution
248
+
249
+ After proactive validation, run tests in **batches of 20** for faster feedback:
218
250
 
219
- Extract scenario names from the failure output and join with `|`:
220
- ```bash
221
- # Example: re-run only 3 failing tests
222
- npx playwright test <spec> --grep "VP-VAL-001|VP-VAL-002|VP-VAL-003" --reporter=line
223
251
  ```
252
+ 1. COMPILE — sungen generate --screen <screen>
224
253
 
225
- - Max 20 test names per `--grep` to keep the pattern manageable
226
- - If >20 failures share the same root cause, fix the cause and run the first 20 to verify
254
+ 2. BATCH RUN run 20 tests at a time:
255
+ npx playwright test <spec> --grep "VP-UI-001|VP-UI-002|...|VP-UI-020" --reporter=line
256
+
257
+ 3. IF FAILURES in batch:
258
+ a. Group failures by root cause (same selector, same error type)
259
+ b. Fix selectors.yaml or test-data.yaml
260
+ c. Recompile, re-run only failing tests from this batch
261
+ d. If fixed → move to next batch
262
+
263
+ 4. NEXT BATCH — repeat with next 20 tests
264
+
265
+ 5. FINAL CONFIRMATION — run ALL tests once:
266
+ npx playwright test <spec> --reporter=line
267
+
268
+ 6. If still failing after 5 fix attempts per batch → ask user about direct .spec.ts fix
269
+ ```
227
270
 
228
271
  #### Grouping failures by root cause
229
272
 
230
273
  Common patterns where 1 fix resolves many failures:
231
- - **Same selector** — e.g., all `[Email Error]` tests fail → fix `email error` in selectors.yaml once
274
+ - **Same selector** — e.g., all `[Email Error]` tests fail → fix once
232
275
  - **Same error type** — e.g., all `strict mode violation` → add `exact: true` or `nth`
233
- - **Same assertion** — e.g., all `toHaveText` on inputs fail → change Gherkin pattern (inputs have no text)
276
+ - **Same assertion** — e.g., all `toHaveText` on inputs fail → change Gherkin pattern
234
277
 
235
278
  Fix the root cause first, verify with the batch, then move on.
236
279
 
237
- **Always read the error context snapshot first** — it shows the exact page state when the test failed, which is more reliable than re-navigating with MCP.
238
-
239
280
  ---
240
281
 
241
282
  ### Key Rules (from sungen-selector-keys)
@@ -33,6 +33,46 @@ For options 1 and 2:
33
33
  For option 3:
34
34
  - Overwrite both `.feature` and `test-data.yaml` completely
35
35
 
36
+ ### Requirements-Driven Generation
37
+
38
+ When `qa/screens/<screen>/requirements/` exists, use it as the **primary source** for test case generation. This produces higher quality tests because requirements contain exact validation messages, field constraints, business rules, and states.
39
+
40
+ #### Reading Requirements
41
+
42
+ 1. **`spec.md`** (primary) — structured screen specification:
43
+ - **Sections** → map directly to test case sections
44
+ - **Fields table** → generate boundary value tests (min/max constraints), required field tests, format tests
45
+ - **Validation Rules table** → generate exact assertion tests using the error messages as `{{test_data}}` values
46
+ - **Actions table** → generate interaction tests (click, submit, navigate)
47
+ - **States table** → generate state transition tests (loading, error, success, disabled)
48
+ - **Business Rules** → generate logic tests (limits, permissions, conditional behavior)
49
+ - **Accessibility** → generate tab-order and aria tests
50
+
51
+ 2. **`ui/`** (supplementary) — screenshots, mockups, design images:
52
+ - Read images to understand layout, element positions, visual states
53
+ - Cross-reference with spec.md — identify elements visible in UI but missing from spec
54
+ - Use for UI/UX viewpoint tests (element visibility, placement, responsive)
55
+
56
+ 3. **`notes.md`** (supplementary) — free-form edge cases, decisions, known issues:
57
+ - Extract edge cases → add to test coverage
58
+ - Flag known issues → add TODO scenarios
59
+
60
+ #### How Requirements Improve Each Viewpoint
61
+
62
+ | Viewpoint | Without Requirements | With Requirements |
63
+ |-----------|---------------------|-------------------|
64
+ | **UI/UX** | "see [Field] is visible" (generic) | "see [Field] field" + verify label, placeholder, default from spec |
65
+ | **Validation** | "submit empty → see error" (vague) | "submit empty email → see {{email_required_error}}" with exact message from spec |
66
+ | **Logic** | Based on observed page behavior | Business rules drive specific tests (lockout after N attempts, session expiry) |
67
+ | **Security** | Generic injection tests | Role-based tests from auth requirements, permission-specific scenarios |
68
+
69
+ #### Merging Requirements + Live Page
70
+
71
+ If the user also explores the live page:
72
+ - **Verify** spec.md against actual page — flag mismatches (e.g., field in spec but not on page)
73
+ - **Supplement** — discover elements on page not in spec (e.g., footer links, tooltips)
74
+ - **Exact text** — capture actual placeholder text, button labels, error messages from live page and update test-data accordingly
75
+
36
76
  ### Page Exploration & Auth
37
77
 
38
78
  Use Playwright MCP to explore the live page. If the page requires authentication:
@@ -55,26 +95,7 @@ Use Playwright MCP to explore the live page. If the page requires authentication
55
95
 
56
96
  #### Detail screens with dynamic IDs
57
97
 
58
- For screens like `/admin/users/:id` or `/products/:slug`:
59
- 1. Navigate to the **list page** first via MCP browser to find a real record ID
60
- 2. Use that ID in the page selector value
61
- 3. Use `User is on [X] page` — sungen resolves the path from the selector
62
-
63
- ```yaml
64
- # selectors.yaml — full path with real ID (generated during make-test)
65
- user detail:
66
- type: 'page'
67
- value: '/admin/users/de42d800-0f5a-490e-9dcf-344fedbd34a5'
68
- ```
69
-
70
- ```gherkin
71
- Scenario: VP-UI-001 User detail displays name
72
- Given User is on [User Detail] page
73
- And User wait for [User Name] heading is visible
74
- Then User see [User Name] heading with {{user_name}}
75
- ```
76
-
77
- Note: the selector uses a hardcoded ID from the live page. If the record is deleted, update the ID in `selectors.yaml`.
98
+ For screens like `/admin/users/:id` or `/products/:slug`, write Gherkin normally (`User is on [User Detail] page`). The real ID will be resolved during `/sungen-make-test` when selectors are generated from the live page. See `sungen-selector-fix` skill for details.
78
99
 
79
100
  ### Section-Focused Approach
80
101
 
@@ -113,72 +134,58 @@ For each selected section, generate **20+ scenarios per applicable viewpoint**.
113
134
 
114
135
  Present a test plan summary and wait for user confirmation before generating files.
115
136
 
137
+ ### Assertion & Review Quality
138
+
139
+ For assertion quality rules, action-result coherence rules, and the 9-point quality review checklist, see the `sungen-gherkin-review` skill. That skill is auto-loaded during the self-review step.
140
+
116
141
  ### Viewpoint Categories
117
142
 
118
143
  | VP Category | Description |
119
144
  |---|---|
120
- | **UI/UX** | Default appearance, element visibility, default states |
121
- | **Validation** | Input validation, error messages, edge cases. **Must explore live page via MCP to capture actual error messages before writing tests. Use `User see {{error_var}}` pattern never assert just "is visible".** |
145
+ | **UI/UX** | Default appearance, default values/text, default states. Group related elements per scenario. |
146
+ | **Validation** | Input validation, error messages, edge cases. **Assert exact error messages via `User see [T] with {{error_var}}`**store texts in test-data.yaml. |
122
147
  | **Logic** | Business logic, interactions, state changes |
123
148
  | **Security** | Auth guards, injection, permission checks |
124
149
 
125
150
  ### Coverage Checklist per Section Pattern
126
151
 
127
- Apply the relevant checklist based on the section pattern:
128
-
129
152
  #### Form sections
130
- - **UI/UX**: All fields visible, labels correct, default values, placeholder text, required indicators
131
- - **Validation**: Empty submit, each required field empty, invalid format (email, phone, URL), min/max length, special chars, unicode, XSS, SQL injection, whitespace-only, boundary values
132
- - **Validation**: Empty submit, each required field empty, invalid format (email, phone, URL), min/max length, special chars, unicode, XSS, SQL injection, whitespace-only, boundary values **assert exact error messages via `User see {{error_var}}`**, store texts in test-data.yaml
133
- - **Logic**: Successful submit, submit with all optional fields, edit existing record, cancel resets form, field dependencies (show/hide), auto-complete, date range validation
134
- - **Security**: CSRF protection, unauthorized submit, role-based field visibility, input sanitization
153
+ - **UI/UX**: All fields + labels + default values in 1-2 grouped scenarios. Default states (button enabled/disabled, checkbox unchecked). Placeholder text via `with {{placeholder}}`.
154
+ - **Validation**: Empty submit verify all error messages. Each required field empty individually verify specific error. Invalid formats → verify specific error. Boundary values (min/max length). Special chars, unicode, XSS, SQL injection.
155
+ - **Logic**: Successful submit verify success state/redirect. Edit existing verify pre-filled values. Cancel verify form resets. Field dependencies (show/hide).
156
+ - **Security**: Unauthorized submit, role-based field visibility, input sanitization
135
157
 
136
158
  #### Data Table sections
137
- - **UI/UX**: All columns visible, column headers, row data displays correctly, empty state, loading state, action buttons visible
138
- - **Validation**: N/A (read-only) or inline edit validation
139
- - **Logic**: Sort ascending/descending per sortable column, filter by each column, search within table, row action menu (edit/delete/view), bulk select, bulk action, row click navigation, column resize
140
- - **Security**: Cannot access other users' data, action permissions per role, data not exposed in DOM
159
+ - **UI/UX**: All column headers in one scenario (`User see [Col] column in [Table] table`). Row data displays with correct values. Empty state message. Action buttons per row.
160
+ - **Logic**: Sort ascending/descending. Filter by column. Search. Row actions (edit/delete/view). Bulk select + action.
161
+ - **Security**: Action permissions per role
141
162
 
142
163
  #### Search & Filter sections
143
- - **UI/UX**: Search field visible, filter buttons visible, clear button state
144
- - **Validation**: Empty search, special chars, SQL injection, XSS, very long text, unicode/emoji, whitespace, partial match, case insensitive
145
- - **Logic**: Apply single filter, combine multiple filters, clear single filter, clear all filters, search + filter combined, filter persists across pagination, no results state
146
- - **Security**: Injection via search, injection via filter params
164
+ - **UI/UX**: Search field + filter buttons + clear button states in one scenario
165
+ - **Validation**: Empty search, special chars, injection, long text, unicode
166
+ - **Logic**: Apply/clear single filter, combine filters, no results state with message
167
+ - **Security**: Injection via search/filter params
147
168
 
148
169
  #### Pagination sections
149
- - **UI/UX**: Page indicator visible, previous disabled on page 1, next enabled
150
- - **Logic**: Next page, previous page, navigate to last page, boundary (first/last), page indicator updates, data refreshes on navigation
151
- - **Security**: Filters persist across pages, search persists, cannot access page beyond max
170
+ - **UI/UX**: Page indicator + button states (previous disabled on page 1, next enabled)
171
+ - **Logic**: Navigate pages, boundary behavior, indicator updates
152
172
 
153
173
  #### Modal / Dialog sections
154
- - **UI/UX**: Modal opens, close button visible, overlay visible, form fields inside modal
155
- - **Validation**: Submit empty form in modal, field validation inside modal
156
- - **Logic**: Open modal, close with X, close with Escape, close with overlay click, submit success closes modal, submit error keeps modal open
157
- - **Security**: Cannot open unauthorized modals, CSRF on modal submit
158
-
159
- #### Card Grid / List sections
160
- - **UI/UX**: Cards display all expected fields (title, image, description, metadata), empty state, loading state
161
- - **Logic**: Click card navigates to detail, load more / infinite scroll, card action buttons (like, share, delete), responsive layout
162
- - **Security**: User-generated content sanitized, cannot access other users' cards
163
-
164
- #### Carousel / Slider sections
165
- - **UI/UX**: Arrows visible, indicators visible, current slide highlighted
166
- - **Logic**: Next slide, previous slide, wrap at end, auto-play, indicator click navigates, swipe gesture
167
- - **Security**: N/A
174
+ - **UI/UX**: Modal content + all fields + close button in one scenario
175
+ - **Validation**: Field validation inside modal with exact errors
176
+ - **Logic**: Open/close methods (X, Escape, overlay), submit success/error behavior
168
177
 
169
178
  #### Tabs / Accordion sections
170
- - **UI/UX**: All tabs visible, active tab highlighted, default tab selected
171
- - **Logic**: Switch tabs, content updates per tab, deep link to tab, accordion expand/collapse, only one accordion open at a time
172
- - **Security**: Tab content respects permissions
179
+ - **UI/UX**: All tab labels + active tab highlighted + default tab content
180
+ - **Logic**: Switch tabs verify content changes, accordion expand/collapse
173
181
 
174
182
  ### General Coverage Dimensions (aim for 20+ total per viewpoint)
175
183
 
176
- **Happy paths (3-5):** Standard flow, all optional fields, minimum required
177
- **Edge cases (5-8):** Empty, max length, special chars, unicode/CJK/emoji, whitespace, leading/trailing spaces, overflow
178
- **Boundary values (3-5):** At min/max limit, one above/below, zero/null
179
- **Negative cases (3-5):** Invalid format, missing required, wrong type, injection attempts
180
- **State transitions (2-4):** Before/after action, undo, cancel mid-flow
181
- **Combinatorial (2-4):** Multiple invalid fields, valid+invalid combos, different roles
184
+ **Happy paths (3-5):** Standard flow with full assertion of result state
185
+ **Edge cases (5-8):** Empty, max length, special chars, unicode, whitespace, boundary values
186
+ **Negative cases (3-5):** Invalid format, missing required, wrong type, exact error messages
187
+ **State transitions (2-4):** Before/after action, verify both old and new state
188
+ **Combinatorial (2-4):** Multiple invalid fields, valid+invalid combos
182
189
 
183
190
  ### SPA Wait-For Steps
184
191
 
@@ -209,24 +216,37 @@ Feature: <Screen> Screen
209
216
  # Section: Create User Form
210
217
  # ============================================================
211
218
 
212
- # --- UI/UX (20+) ---
219
+ # --- UI/UX ---
213
220
 
214
- Scenario: VP-UI-001 Name field is visible
215
- ...
221
+ Scenario: VP-UI-001 Form displays all fields with correct defaults
222
+ Given User is on [Create User] page
223
+ Then User see [Create User] heading with {{form_title}}
224
+ And User see [Name] field
225
+ And User see [Email] field
226
+ And User see [Role] dropdown with {{default_role}}
227
+ And User see [Submit] button is disabled
228
+ And User see [Cancel] button is enabled
216
229
 
217
- # --- Validation (20+) ---
230
+ # --- Validation ---
218
231
 
219
- Scenario: VP-VAL-001 Submit with empty name
220
- ...
232
+ Scenario: VP-VAL-001 Submit with all empty fields shows errors
233
+ Given User is on [Create User] page
234
+ When User click [Submit] button
235
+ Then User see [Name error] message with {{name_required_error}}
236
+ And User see [Email error] message with {{email_required_error}}
221
237
 
222
238
  # ============================================================
223
239
  # Section: User Table
224
240
  # ============================================================
225
241
 
226
- # --- UI/UX (20+) ---
242
+ # --- UI/UX ---
227
243
 
228
- Scenario: VP-UI-025 Table displays all columns
229
- ...
244
+ Scenario: VP-UI-010 Table displays all columns
245
+ Given User is on [Users] page
246
+ Then User see [Name] column in [Users] table
247
+ And User see [Email] column in [Users] table
248
+ And User see [Status] column in [Users] table
249
+ And User see [Actions] column in [Users] table
230
250
  ```
231
251
 
232
252
  **Naming convention:** `VP-<CATEGORY>-<NNN>` prefix in Scenario name.
@@ -15,7 +15,11 @@ sungen generate → compiles Gherkin + selectors + data → Playwright .spec.ts
15
15
  ├── qa/screens/<name>/
16
16
  │ ├── features/ # .feature files (Gherkin)
17
17
  │ ├── selectors/ # Element locator YAML mappings
18
- └── test-data/ # Test data YAML values
18
+ ├── test-data/ # Test data YAML values
19
+ │ └── requirements/ # Screen specs, UI designs, notes
20
+ │ ├── spec.md # Structured screen specification
21
+ │ ├── ui/ # Screenshots, mockups, design images
22
+ │ └── notes.md # Edge cases, decisions (optional)
19
23
  ├── specs/
20
24
  │ └── generated/ # Auto-generated Playwright tests
21
25
  ├── .claude/
@@ -53,7 +57,7 @@ Use AI commands (Claude Code or GitHub Copilot) to drive the workflow:
53
57
  |---|---|
54
58
  | `/sungen:add-screen` | `/sungen-add-screen` |
55
59
 
56
- Scaffolds `qa/screens/<name>/` with empty feature, selectors, and test-data files, then asks if you want to create test cases.
60
+ Scaffolds `qa/screens/<name>/` with empty feature, selectors, test-data, and requirements files. Fill `requirements/spec.md` with screen specs before creating test cases for higher quality output.
57
61
 
58
62
  ### Step 2: Create test cases
59
63
 
@@ -62,10 +66,11 @@ Scaffolds `qa/screens/<name>/` with empty feature, selectors, and test-data file
62
66
  | `/sungen:make-tc login` | `/sungen-make-tc login` |
63
67
 
64
68
  AI acts as a **Senior QA Engineer**:
65
- 1. Explores the live page via Playwright MCP (asks user to log in if auth needed)
66
- 2. Identifies screen sections asks user which to focus on
67
- 3. Generates **20+ scenarios per viewpoint** (UI/UX, Validation, Logic, Security) for each section
68
- 4. Confirms test plan before generating `.feature` + `test-data.yaml`
69
+ 1. Reads `requirements/spec.md` for screen specs (fields, validation, business rules, states)
70
+ 2. Optionally explores the live page via Playwright MCP to verify and supplement
71
+ 3. Identifies screen sections asks user which to focus on
72
+ 4. Generates **20+ scenarios per viewpoint** (UI/UX, Validation, Logic, Security) for each section
73
+ 5. Confirms test plan before generating `.feature` + `test-data.yaml`
69
74
 
70
75
  ### Step 3: Compile & run tests
71
76
 
@@ -136,8 +141,8 @@ User <action> [<Target>] <type> with {{<Value>}}
136
141
  | Assert text | `User see [Title] heading with {{title}}` |
137
142
  | Assert state | `User see [Submit] button is disabled` |
138
143
  | Wait for | `User wait for [Modal] dialog is visible` |
139
- | Table row | `User see [Users] table has row with {{name}}` |
140
- | Table column | `User see [Users] table has [Email] column` |
144
+ | Table row | `User see [Users] table row with {{name}}` |
145
+ | Table column | `User see [Email] column in [Users] table` |
141
146
 
142
147
  States: `hidden` `visible` `disabled` `enabled` `checked` `unchecked` `focused` `empty` `loading` `selected`
143
148
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "2.2.3",
3
+ "version": "2.3.1",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,18 @@
1
+ import { Command } from 'commander';
2
+
3
+ export function registerUpdateCommand(program: Command): void {
4
+ program
5
+ .command('update')
6
+ .description('Update AI rules, commands, and skills to the latest version')
7
+ .option('--dry-run', 'Show what would be updated without making changes')
8
+ .action(async (options: { dryRun?: boolean }) => {
9
+ try {
10
+ const { AIRulesUpdater } = require('../../orchestrator/ai-rules-updater');
11
+ const updater = new AIRulesUpdater(process.cwd());
12
+ await updater.update(options.dryRun ?? false);
13
+ } catch (error) {
14
+ console.error('❌ Update failed:', error);
15
+ process.exit(1);
16
+ }
17
+ });
18
+ }
package/src/cli/index.ts CHANGED
@@ -9,6 +9,7 @@ import { registerInitCommand } from './commands/init';
9
9
  import { registerAddCommand } from './commands/add';
10
10
  import { registerGenerateCommand } from './commands/generate';
11
11
  import { registerMakeauthCommand } from './commands/makeauth';
12
+ import { registerUpdateCommand } from './commands/update';
12
13
 
13
14
  async function main() {
14
15
  const program = new Command();
@@ -16,7 +17,7 @@ async function main() {
16
17
  program
17
18
  .name('sungen')
18
19
  .description('Deterministic E2E Test Compiler — Gherkin + Selectors → Playwright')
19
- .version('2.2.3');
20
+ .version('2.3.1');
20
21
 
21
22
  // Global options
22
23
  program
@@ -27,6 +28,7 @@ async function main() {
27
28
  registerAddCommand(program);
28
29
  registerGenerateCommand(program);
29
30
  registerMakeauthCommand(program);
31
+ registerUpdateCommand(program);
30
32
 
31
33
  await program.parseAsync(process.argv);
32
34
  }
@@ -14,6 +14,8 @@ export interface ParsedStep {
14
14
  elementType?: string; // Element type after [Selector] e.g., "button", "text", "link", "field"
15
15
  nth?: number; // Positional index from "[Element] field 3" → 3 (0 = not specified)
16
16
  featurePath?: string; // NEW: Feature path for page type (e.g., "/", "/dashboard")
17
+ parentRef?: string; // Parent scope reference: "in [Parent Name] parentType" → parentRef = "Parent Name"
18
+ parentType?: string; // Parent scope type: table, list, section, dialog, form
17
19
  }
18
20
 
19
21
  export interface ParsedScenario {
@@ -122,7 +124,20 @@ export class GherkinParser {
122
124
  * Parse a single step and extract selector/data references
123
125
  */
124
126
  private parseStep(step: Messages.Step): ParsedStep {
125
- const text = step.text;
127
+ let text = step.text;
128
+
129
+ // === Extract parent scoping: "in [Parent Name] parentType" ===
130
+ // Must be extracted first so the parent [brackets] don't interfere with target selector extraction.
131
+ // Valid parent types: table, list, section, dialog, form
132
+ const parentMatch = text.match(/\s+in\s+\[([^\]]+)\]\s+(table|list|section|dialog|form)\b/i);
133
+ let parentRef: string | undefined;
134
+ let parentType: string | undefined;
135
+ if (parentMatch) {
136
+ parentRef = parentMatch[1];
137
+ parentType = parentMatch[2].toLowerCase();
138
+ // Remove parent scoping from text so it doesn't interfere with other parsing
139
+ text = text.slice(0, parentMatch.index) + text.slice(parentMatch.index! + parentMatch[0].length);
140
+ }
126
141
 
127
142
  // Extract selector reference: [Email Address] or [login:email_field] (legacy)
128
143
  // Support natural language and legacy formats
@@ -159,12 +174,14 @@ export class GherkinParser {
159
174
 
160
175
  return {
161
176
  keyword: step.keyword.trim(),
162
- text,
177
+ text: step.text, // Preserve original text (with parent scoping) for pattern matching
163
178
  selectorRef,
164
179
  dataRef,
165
180
  value,
166
181
  elementType,
167
182
  nth,
183
+ parentRef,
184
+ parentType,
168
185
  };
169
186
  }
170
187
 
@@ -0,0 +1,3 @@
1
+ {{#if pattern}}await expect({{> locator}}).toHaveAttribute('{{attribute}}', /{{dataValue}}/);
2
+ {{else}}await expect({{> locator}}).toHaveAttribute('{{attribute}}', '{{dataValue}}');
3
+ {{/if}}
@@ -1,3 +1,13 @@
1
+ {{#if parentLocator}}{{parentLocator}}.{{#switch strategy~}}
2
+ {{~#case 'testid'}}getByTestId('{{value}}'){{/case~}}
3
+ {{~#case 'role'}}{{#if name}}getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{else}}getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}){{/if}}{{/case~}}
4
+ {{~#case 'placeholder'}}getByPlaceholder('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
5
+ {{~#case 'label'}}getByLabel('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
6
+ {{~#case 'text'}}getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
7
+ {{~#case 'locator'}}locator('{{value}}'){{/case~}}
8
+ {{~#case 'id'}}locator('{{value}}'){{/case~}}
9
+ {{~#default}}locator('{{value}}'){{/default~}}
10
+ {{~/switch}}{{else~}}
1
11
  {{#if scope}}{{pageRoot}}.getByLabel('{{escapeQuotes scope}}').{{#switch strategy~}}
2
12
  {{~#case 'testid'}}getByTestId('{{value}}'){{/case~}}
3
13
  {{~#case 'role'}}{{#if name}}getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{else}}getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}){{/if}}{{/case~}}
@@ -16,4 +26,5 @@
16
26
  {{~#case 'locator'}}{{> locator-strategies/locator}}{{/case~}}
17
27
  {{~#case 'id'}}{{> locator-strategies/id}}{{/case~}}
18
28
  {{~#default}}{{> locator-strategies/default}}{{/default~}}
19
- {{~/switch}}{{/if}}
29
+ {{~/switch}}{{/if~}}
30
+ {{/if}}
@@ -1,3 +1,13 @@
1
+ {{#if parentLocator}}{{parentLocator}}.{{#switch strategy~}}
2
+ {{~#case 'testid'}}getByTestId('{{value}}'){{/case~}}
3
+ {{~#case 'role'}}{{#if name}}getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{else}}getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}){{/if}}{{/case~}}
4
+ {{~#case 'placeholder'}}getByPlaceholder('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
5
+ {{~#case 'label'}}getByLabel('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
6
+ {{~#case 'text'}}getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
7
+ {{~#case 'locator'}}locator('{{value}}'){{/case~}}
8
+ {{~#case 'id'}}locator('{{value}}'){{/case~}}
9
+ {{~#default}}locator('{{value}}'){{/default~}}
10
+ {{~/switch}}{{> locator-nth}}{{else~}}
1
11
  {{#if scope}}{{pageRoot}}.getByLabel('{{escapeQuotes scope}}').{{#switch strategy~}}
2
12
  {{~#case 'testid'}}getByTestId('{{value}}'){{/case~}}
3
13
  {{~#case 'role'}}{{#if name}}getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{else}}getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}){{/if}}{{/case~}}
@@ -16,4 +26,5 @@
16
26
  {{~#case 'locator'}}{{> locator-strategies/locator}}{{/case~}}
17
27
  {{~#case 'id'}}{{> locator-strategies/id}}{{/case~}}
18
28
  {{~#default}}{{> locator-strategies/default}}{{/default~}}
19
- {{~/switch}}{{> locator-nth}}{{/if}}
29
+ {{~/switch}}{{> locator-nth}}{{/if~}}
30
+ {{/if}}
@@ -279,6 +279,19 @@ export const assertionPatterns: StepPattern[] = [
279
279
  // Selector not in YAML or context issue - will use variable-only
280
280
  }
281
281
 
282
+ // --- Attribute assertion: toHaveAttribute ---
283
+ // When selector YAML has `attribute` field (e.g., attribute: 'src', 'href')
284
+ if (resolved.attribute) {
285
+ const code = context.templateEngine.renderStep('attribute-assertion', {
286
+ ...resolved,
287
+ dataValue,
288
+ });
289
+ return {
290
+ code,
291
+ comment: `Assert ${step.selectorRef} ${resolved.attribute} matches ${step.dataRef}`,
292
+ };
293
+ }
294
+
282
295
  // --- Input types: toHaveValue ---
283
296
  if (step.elementType && INPUT_TYPES.has(step.elementType)) {
284
297
  const code = context.templateEngine.renderStep('have-value-assertion', {
@@ -72,6 +72,14 @@ export class PatternRegistry {
72
72
  // Prefer resolver (framework-agnostic) over generator (legacy)
73
73
  if (pattern.resolver) {
74
74
  const resolved = pattern.resolver(step, context);
75
+
76
+ // Auto-inject parent scoping if step has parentRef
77
+ if (step.parentRef && step.parentType) {
78
+ resolved.data.parentLocator = PatternRegistry.resolveParentLocator(
79
+ step.parentRef, step.parentType, context
80
+ );
81
+ }
82
+
75
83
  const code = context.templateEngine.renderStep(resolved.templateName, resolved.data);
76
84
  return {
77
85
  code,
@@ -86,6 +94,39 @@ export class PatternRegistry {
86
94
  return null;
87
95
  }
88
96
 
97
+ /**
98
+ * Resolve parent scoping to a Playwright locator string.
99
+ * Tries YAML lookup first, falls back to auto-infer from parentType.
100
+ *
101
+ * Parent type → Playwright role:
102
+ * table → 'table', list → 'list', section → 'region',
103
+ * dialog → 'dialog', form → 'form'
104
+ */
105
+ private static resolveParentLocator(
106
+ parentRef: string, parentType: string, context: PatternContext
107
+ ): string {
108
+ // Try resolving from selectors YAML
109
+ try {
110
+ const resolved = context.selectorResolver.resolveSelector(
111
+ parentRef, context.featureName, parentType, 0
112
+ );
113
+ return context.renderLocator(resolved);
114
+ } catch {
115
+ // Fallback: auto-infer from parentType + parentRef as accessible name
116
+ }
117
+
118
+ const roleMap: Record<string, string> = {
119
+ table: 'table',
120
+ list: 'list',
121
+ section: 'region',
122
+ dialog: 'dialog',
123
+ form: 'form',
124
+ };
125
+ const role = roleMap[parentType] || parentType;
126
+ const escapedName = parentRef.replace(/'/g, "\\'");
127
+ return `page.getByRole('${role}', { name: '${escapedName}' })`;
128
+ }
129
+
89
130
  /**
90
131
  * Check if step matches a pattern matcher
91
132
  */