@sun-asterisk/sungen 2.2.2 → 2.3.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 (105) hide show
  1. package/README.md +66 -37
  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 +17 -3
  11. package/dist/generators/gherkin-parser/index.js.map +1 -1
  12. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/alert-accept-action.hbs +1 -0
  13. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/alert-dismiss-action.hbs +1 -0
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/alert-fill-action.hbs +1 -0
  15. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/alert-text-assertion.hbs +8 -0
  16. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +3 -0
  17. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +1 -0
  18. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +12 -1
  19. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +12 -1
  20. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  21. package/dist/generators/test-generator/patterns/assertion-patterns.js +95 -57
  22. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  23. package/dist/generators/test-generator/patterns/index.d.ts +9 -0
  24. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  25. package/dist/generators/test-generator/patterns/index.js +32 -0
  26. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  27. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +1 -1
  28. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
  29. package/dist/generators/test-generator/patterns/interaction-patterns.js +56 -1
  30. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
  31. package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +1 -1
  32. package/dist/generators/test-generator/patterns/table-patterns.js +8 -5
  33. package/dist/generators/test-generator/patterns/table-patterns.js.map +1 -1
  34. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  35. package/dist/generators/test-generator/utils/selector-resolver.js +16 -0
  36. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  37. package/dist/orchestrator/ai-rules-updater.d.ts +13 -0
  38. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -0
  39. package/dist/orchestrator/ai-rules-updater.js +157 -0
  40. package/dist/orchestrator/ai-rules-updater.js.map +1 -0
  41. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  42. package/dist/orchestrator/project-initializer.js +2 -27
  43. package/dist/orchestrator/project-initializer.js.map +1 -1
  44. package/dist/orchestrator/screen-manager.d.ts +1 -0
  45. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  46. package/dist/orchestrator/screen-manager.js +70 -3
  47. package/dist/orchestrator/screen-manager.js.map +1 -1
  48. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +18 -9
  49. package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -4
  50. package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -6
  51. package/dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +22 -9
  52. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +170 -24
  53. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +118 -12
  54. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +16 -2
  55. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +124 -71
  56. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -5
  57. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +12 -4
  58. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +11 -6
  59. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +22 -9
  60. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +170 -24
  61. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +93 -12
  62. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +16 -2
  63. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +124 -72
  64. package/dist/orchestrator/templates/readme.md +13 -8
  65. package/package.json +1 -1
  66. package/src/cli/commands/update.ts +18 -0
  67. package/src/cli/index.ts +3 -1
  68. package/src/generators/gherkin-parser/index.ts +20 -3
  69. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/alert-accept-action.hbs +1 -0
  70. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/alert-dismiss-action.hbs +1 -0
  71. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/alert-fill-action.hbs +1 -0
  72. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/alert-text-assertion.hbs +8 -0
  73. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/attribute-assertion.hbs +3 -0
  74. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +1 -0
  75. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +12 -1
  76. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +12 -1
  77. package/src/generators/test-generator/patterns/assertion-patterns.ts +106 -65
  78. package/src/generators/test-generator/patterns/index.ts +41 -0
  79. package/src/generators/test-generator/patterns/interaction-patterns.ts +58 -1
  80. package/src/generators/test-generator/patterns/table-patterns.ts +8 -5
  81. package/src/generators/test-generator/utils/selector-resolver.ts +16 -0
  82. package/src/orchestrator/ai-rules-updater.ts +139 -0
  83. package/src/orchestrator/project-initializer.ts +2 -32
  84. package/src/orchestrator/screen-manager.ts +72 -3
  85. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +18 -9
  86. package/src/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -4
  87. package/src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -6
  88. package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +22 -9
  89. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +170 -24
  90. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +118 -12
  91. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +16 -2
  92. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +124 -71
  93. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -5
  94. package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +12 -4
  95. package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +11 -6
  96. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +22 -9
  97. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +170 -24
  98. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +93 -12
  99. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +16 -2
  100. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +124 -72
  101. package/src/orchestrator/templates/readme.md +13 -8
  102. package/docs/gherkin standards/gherkin-core-standard.md +0 -428
  103. package/docs/gherkin standards/gherkin-core-standard.vi.md +0 -513
  104. package/docs/gherkin-dictionary.md +0 -1071
  105. package/docs/makeauth.md +0 -225
@@ -25,19 +25,28 @@ Run:
25
25
  sungen add --screen <screen> --path <path>
26
26
  ```
27
27
 
28
- ### 2. Create test cases
28
+ ### 2. Fill requirements (recommended)
29
+
30
+ Ask the user: "Would you like to fill in `requirements/spec.md` now? This helps generate higher quality test cases."
31
+
32
+ - If yes → open `qa/screens/<screen>/requirements/spec.md` and help the user fill sections, fields, validation rules, business rules, and states.
33
+ - If they have UI designs (screenshots, Figma exports, mockups) → suggest copying them to `requirements/ui/`.
34
+ - If no → proceed to step 3.
35
+
36
+ ### 3. Create test cases
29
37
 
30
38
  Ask the user: "Would you like to create test cases now?"
31
39
 
32
- If yes, delegate to `/sungen:make-tc <screen>` to handle:
33
- - Exploring the live page or analyzing static designs
34
- - Gathering test viewpoints (UI/UX, Validation, Logic, Security)
35
- - Generating the 3 files (feature, selectors, test-data)
40
+ If yes **you MUST use the Skill tool** to invoke `/sungen:make-tc <screen>`. This is critical because `make-tc` auto-loads the `sungen-gherkin-syntax` and `sungen-tc-generation` skills which contain the full Gherkin syntax rules, pattern shapes, viewpoint checklists, and output format. **Do NOT attempt to generate test cases yourself** — always invoke the Skill tool so these skills are properly loaded.
41
+
42
+ ```
43
+ Skill: make-tc
44
+ Args: <screen>
45
+ ```
36
46
 
37
- ### 3. Confirm
47
+ ### 4. Confirm
38
48
 
39
- Tell the user what was created and next steps:
40
- - If the page requires authentication, the user will be asked to log in via the MCP browser during `/sungen:make-tc`
41
- - Edit the generated files as needed
49
+ If the user declined test case creation, tell them next steps:
50
+ - Fill `requirements/spec.md` with screen specs (if not done)
42
51
  - Run `/sungen:make-tc <screen>` to create test cases
43
52
  - Run `/sungen:make-test <screen>` to generate selectors, compile, and run tests
@@ -17,9 +17,16 @@ Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
17
17
 
18
18
  1. Verify `qa/screens/<screen>/` exists. If not → `/sungen:add-screen` first.
19
19
  2. Check if `.feature` file already has scenarios. If yes → use `AskUserQuestion` to ask the update mode (see `sungen-tc-generation` skill for details). If no → fresh creation.
20
- 3. Use `AskUserQuestion` to ask: **Live page** (explore via Playwright MCP) or **Static designs** (screenshots, Figma)? Explore accordingly (see CLAUDE.md for MCP rules).
21
- 4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format.
22
- 5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills.
23
- 6. Show summary next: `/sungen:make-test <screen>`
20
+ 3. **Read requirements** check `qa/screens/<screen>/requirements/`:
21
+ - If `spec.md` exists read it as PRIMARY source (sections, fields, validation rules, business rules, states).
22
+ - If `ui/` has images read them for visual context (layout, element positions, states).
23
+ - If `notes.md` existsread for edge cases and additional context.
24
+ - Summarize what you found in requirements and present to the user.
25
+ 4. **Explore page** (supplements requirements, or is primary source if no requirements):
26
+ - Use `AskUserQuestion` to ask: **Live page** (explore via Playwright MCP) or **Static designs** (screenshots, Figma)? Or **Skip** (if requirements are sufficient)?
27
+ - If exploring, verify and supplement requirements — flag any discrepancies found.
28
+ 5. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. When requirements exist, use the "Requirements-Driven Generation" strategy.
29
+ 6. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills.
30
+ 7. Show summary → next: `/sungen:make-test <screen>`
24
31
 
25
32
  **No selectors.yaml** — selectors are generated during `/sungen:make-test`.
@@ -16,9 +16,14 @@ Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
16
16
  ## Steps
17
17
 
18
18
  1. Verify `qa/screens/<screen>/` has `.feature` + `test-data.yaml`. If not → `/sungen:make-tc` first.
19
- 2. Generate `selectors.yaml` from live page using `sungen-selector-fix` and `sungen-selector-keys` skills.
20
- 3. Compile: `sungen generate --screen <screen>`
21
- 4. Run: `npx playwright test specs/screens/<screen>/<screen>.spec.ts`
22
- 5. If fail fix selectors/test-data per `sungen-selector-fix` + `sungen-error-mapping` skills, retry (max 5).
23
- 6. After 5 fails ask user about direct `.spec.ts` fix.
24
- 7. Show: pass/fail, attempt count, files changed.
19
+ 2. **Generate selectors** explore live page via MCP, build `selectors.yaml` using `sungen-selector-fix` and `sungen-selector-keys` skills.
20
+ 3. **Proactive validation** — verify EVERY selector against the live page using `browser_snapshot` + `browser_evaluate` BEFORE running any test. Fix mismatches immediately. See `sungen-selector-fix` skill "Proactive Selector Validation" section. Target: 80%+ issues fixed before first run.
21
+ 4. **Compile**: `sungen generate --screen <screen>`
22
+ 5. **Batched test run** run tests in batches of 20 via `--grep`:
23
+ `npx playwright test specs/generated/<screen>/*.spec.ts --grep "VP-UI-001|...|VP-UI-020" --reporter=line`
24
+ - If failures in batch → group by root cause, fix, recompile, re-run only failing tests
25
+ - If batch passes → move to next 20 tests
26
+ - Max 5 fix attempts per batch
27
+ 6. **Final confirmation** — run ALL tests once to catch regressions.
28
+ 7. After 5 fix attempts still failing → ask user about direct `.spec.ts` fix.
29
+ 8. Show: pass/fail, attempt count, files changed.
@@ -6,16 +6,29 @@ user-invocable: false
6
6
 
7
7
  ## Playwright Errors → Fix
8
8
 
9
- | Error | Fix in `selectors.yaml` |
9
+ | Error | Fix |
10
10
  |---|---|
11
- | strict mode violation | add `nth`, `exact: true`, or specific `name` |
12
- | Element is not an input | change `type` or `value` |
13
- | Timeout waiting for selector | fix `type`/`value`/`name` to match element |
14
- | toBeVisible Timeout | verify accessible name or use `testid` |
15
- | toHaveText mismatch | fix in `test-data.yaml` |
16
- | Navigation failed | fix page `value` path |
17
- | not a select | set `variant: 'custom'` |
18
- | Frame not found | fix `frame` value |
11
+ | strict mode violation | add `nth`, `exact: true`, or specific `name` in selectors.yaml |
12
+ | Element is not an input | change `type` or `value` in selectors.yaml |
13
+ | Timeout waiting for selector | fix `type`/`value`/`name` to match element in selectors.yaml |
14
+ | toBeVisible Timeout | verify accessible name or use `testid` in selectors.yaml |
15
+ | toHaveText mismatch | fix expected text in `test-data.yaml` — OR if element is an input (field/textarea/search/dropdown), change Gherkin type so compiler uses `toHaveValue` instead |
16
+ | toHaveValue mismatch | fix expected value in `test-data.yaml` this is for input elements (field, textarea, search, dropdown, slider, date-picker) |
17
+ | toContainText mismatch | fix expected partial text in `test-data.yaml` |
18
+ | Navigation failed | fix page `value` path in selectors.yaml |
19
+ | not a select | set `variant: 'custom'` in selectors.yaml |
20
+ | Frame not found | fix `frame` value in selectors.yaml |
21
+ | dialog was dismissed | alert step is AFTER the trigger — move `click [OK] alert` BEFORE the button click that opens the dialog |
22
+ | page.once('dialog') never fired | no browser dialog appeared — check if the action actually triggers a `window.alert/confirm/prompt`, not a CSS modal |
23
+
24
+ ### Assertion type mismatch (toHaveText vs toHaveValue)
25
+
26
+ Sungen picks the assertion based on element type:
27
+ - **Input types** (`field`, `textarea`, `search`, `dropdown`, `slider`, `date-picker`) → `toHaveValue()`
28
+ - **Text types** (everything else: `message`, `header`, `label`, `row`, etc.) → `toHaveText()`
29
+ - **Partial match** (uses `contains` keyword) → `toContainText()`
30
+
31
+ If you see `toHaveText` failing on an input element, the Gherkin step has the wrong target type. Fix: change the target type in the `.feature` file (e.g., `see [Email] message with {{v}}` → `see [Email] field with {{v}}`).
19
32
 
20
33
  ## Auth Errors → Fix
21
34
 
@@ -4,13 +4,35 @@ description: 'Sungen Gherkin patterns, selector types, and YAML key rules. Auto-
4
4
  user-invocable: false
5
5
  ---
6
6
 
7
- ## Step Patterns (65 patterns)
7
+ ## Standard Syntax
8
+
9
+ ```
10
+ [Keyword] User <Action> [Target Name] <Target Type> <in [Parent Name] <Parent Type>> <with {{Value}}> <is State>
11
+ ```
12
+
13
+ - **Actor**: Always `User`, always active voice.
14
+ - **Value**: `with {{snake_case}}` — never hardcode static data.
15
+ - **State**: `is <keyword>` — never use `{{}}` for states.
16
+ - **Parent scope**: `in [Parent] parentType` — optional, only when page has 2+ similar blocks needing disambiguation.
17
+
18
+ ## Keyword → Action Rules
19
+
20
+ ```
21
+ GIVEN → is on
22
+ WHEN → click · fill · select · press · clear · check · uncheck · hover
23
+ [⚠️ wait for — only for Spinner/Modal, minimize usage]
24
+ THEN → see
25
+ AND → inherits from preceding keyword
26
+ ```
27
+
28
+ ## Step Patterns (70 patterns)
8
29
 
9
30
  ### Setup
10
31
 
11
32
  ```
12
33
  User is on [T] page # navigate to page
13
34
  User is on [T] page with {{v}} # navigate with data (e.g., detail page with ID)
35
+ User is on [T] dialog # enter dialog scope
14
36
  User is logged in | is not logged in # auth state
15
37
  ```
16
38
 
@@ -18,31 +40,57 @@ User is logged in | is not logged in # auth state
18
40
 
19
41
  ```
20
42
  User fill [T] field with {{v}} # text input
43
+ User fill [T] textarea with {{v}} # multiline input
44
+ User fill [T] search with {{v}} # search input
45
+ User fill [T] slider with {{v}} # set slider value
46
+ User fill [T] date picker with {{v}} # date input
21
47
  User clear [T] field # clear input
22
48
  User check [T] checkbox # check
49
+ User check [T] toggle # toggle on (same as check)
50
+ User check [T] radio # select radio
23
51
  User uncheck [T] checkbox # uncheck
52
+ User uncheck [T] toggle # toggle off
24
53
  User select [T] dropdown with {{v}} # select option
25
- User toggle [T] switch # toggle switch/checkbox
26
- User upload [T] file with {{f}} # file upload
54
+ User upload [T] uploader with {{f}} # file upload
27
55
  ```
28
56
 
29
57
  ### Interaction
30
58
 
31
59
  ```
32
- User click [T] button # click
33
- User click [T] button with {{v}} # click with text match
60
+ User click [T] button # click (static element, no value)
61
+ User click [T] row with {{v}} # click dynamic list item (row/item/card/option)
62
+ User click [T] tab # switch tab
63
+ User click [T] column # sort column
64
+ User click [T] breadcrumb # navigate breadcrumb
34
65
  User double click [T] element # double click
35
- User hover [T] icon # hover
66
+ User hover [T] icon # hover (must have see/click after)
67
+ User hover [T] row # hover row for hidden actions
36
68
  User drag [T] to [T2] # drag and drop
37
69
  User expand [T] row # expand (aria-expanded)
38
70
  User collapse [T] row # collapse (aria-expanded)
39
71
  ```
40
72
 
73
+ #### click + with {{Value}} rule
74
+
75
+ - **NO value** for static elements: `button`, `link`, `icon`, `tab`, `toggle`
76
+ - **WITH value** only for dynamic lists: `row`, `item`, `card`, `option`
77
+
78
+ ### Browser Alert (system dialog)
79
+
80
+ ```
81
+ User click [OK] alert # accept (OK/Accept/Yes/Confirm)
82
+ User click [Cancel] alert # dismiss (Cancel/Dismiss/No)
83
+ User fill [T] alert with {{v}} # fill prompt + accept
84
+ User see [message text] alert # assert dialog message
85
+ ```
86
+
87
+ > Alert steps must appear BEFORE the action that triggers the dialog.
88
+
41
89
  ### Keyboard
42
90
 
43
91
  ```
44
- User press Escape key # global key press
45
- User press Enter on [T] field # key on element
92
+ User press [Escape] key # global key press
93
+ User press [Enter] on [T] field # key on element
46
94
  ```
47
95
 
48
96
  ### Wait
@@ -53,53 +101,129 @@ User wait for [T] dialog # wait for element
53
101
  User wait for [T] dialog is STATE # wait for state
54
102
  User wait for [T] dialog with {{v}} # wait for element with text
55
103
  User wait for [T] page # wait for navigation
104
+ User wait for [T] message # wait for toast/feedback
105
+ User wait for [T] button # wait for button to appear
56
106
  ```
57
107
 
58
108
  ### Scroll & Frame
59
109
 
60
110
  ```
61
111
  User scroll to [T] section # scroll into view
62
- User switch to [T] frame # enter/exit iframe
112
+ User switch to [T] frame # enter iframe
113
+ User switch to [main] frame # exit iframe
63
114
  ```
64
115
 
65
- ### Assertions
116
+ ### Assertions (8 verify patterns)
66
117
 
67
118
  ```
68
- User see [T] page # URL assertion
69
- User see [T] heading # visibility
70
- User see [T] heading with {{v}} # visibility + text
119
+ # 1. Visibility
120
+ User see [T] message # visible (default — NEVER add "is visible")
121
+ User see [T] modal is hidden # hidden
122
+
123
+ # 2. Text Content (exact full match — toHaveText)
124
+ User see [T] message with {{v}} # exact text match
125
+ User see [T] header with {{v}} # heading text
126
+ User see [T] label with {{v}} # label text
127
+
128
+ # 3. Partial Text Match (toContainText)
71
129
  User see [T] text contains {{v}} # partial text match
72
- User see [T] text has text {{v}} # exact text match
73
- User see [T] button is STATE # state assertion
74
- User see [T] dialog with {{v}} is STATE # text + state
75
- User see {{v}} # see data text
130
+
131
+ # 4. Input Value (toHaveValue)
132
+ User see [T] field with {{v}} # input value
133
+ User see [T] dropdown with {{v}} # selected value
134
+ User see [T] date picker with {{v}} # date value
135
+ User see [T] search with {{v}} # search input value
136
+ User see [T] slider with {{v}} # slider value
137
+
138
+ # 5. Component State
139
+ User see [T] button is disabled # state assertion
140
+ User see [T] checkbox is checked # checked state
141
+ User see [T] toggle is unchecked # unchecked state
142
+ User see [T] dialog with {{v}} is hidden # text + state combined
143
+
144
+ # 6. Attribute (toHaveAttribute — when selector YAML has `attribute` field)
145
+ User see [T] image with {{v}} # image src
146
+ User see [T] link with {{v}} # link href
147
+
148
+ # 7. Count
149
+ User see [T] row with {{count}} # element count
150
+
151
+ # 8. Page Context
152
+ User see [T] page # URL assertion
76
153
  ```
77
154
 
78
155
  ### Table
79
156
 
80
157
  ```
81
- User see [Table] table has row with {{f}} # row exists
158
+ User see [Col] column in [Table] table # column exists (parent scoping)
159
+ User see [Table] table row with {{f}} # row exists
82
160
  User see [Table] table has no row with {{f}} # row not exists
83
- User see [Table] table has {{count}} rows # row count
84
- User see [Table] table has [Col] column # column exists
161
+ User see [Table] table with {{count}} rows # row count
85
162
  User see [Table] table is empty # empty table
86
163
  User see [Table] table row with {{f}} has [Col] with {{v}} # cell by filter
87
164
  User see [Table] table row 1 [Col] cell with {{v}} # cell by index
88
165
  User click [Act] in [Table] table row with {{f}} # action in row
89
166
  ```
90
167
 
168
+ ### Parent Scoping (disambiguation)
169
+
170
+ ```
171
+ User click [Submit] button in [User Info] form # button inside specific form
172
+ User fill [Email] field in [Registration] form with {{v}} # field inside specific form
173
+ User see [Total] text in [Summary] section with {{v}} # text inside specific section
174
+ User click [Delete] button in [Active Users] table # button inside specific table
175
+ ```
176
+
177
+ - **Optional** — only use when page has 2+ similar UI blocks
178
+ - **Valid parent types**: `table`, `list`, `section`, `dialog`, `form`
179
+ - **Max 2 levels**: `[Target] in [Parent]`. **NEVER** nest 3 levels: `[A] in [B] in [C]`
180
+ - Parent resolves from selectors YAML first, falls back to auto-infer `getByRole(parentType, { name })`
181
+
91
182
  ### States
92
183
 
93
184
  `hidden` `visible` `disabled` `enabled` `checked` `unchecked` `focused` `empty` `loading` `selected` `sorted ascending` `sorted descending`
94
185
 
95
186
  ### Element Types
96
187
 
97
- `page` `button` `link` `field` `textarea` `heading` `text` `checkbox` `radio` `switch` `dropdown` `dialog` `modal` `menu` `menuitem` `tab` `tabpanel` `table` `row` `cell` `list` `listitem` `icon` `image` `alert` `spinner` `progressbar` `section` `region` `nav` `frame` `uploader` `file` `columnheader` `tooltip` `slider` `treeitem`
188
+ | Group | Types |
189
+ |---|---|
190
+ | **Context** | `page` `dialog` `modal` `drawer` `tab` `alert` `overlay` `step` |
191
+ | **Input** | `field` `textarea` `search` `dropdown` `option` `checkbox` `radio` `toggle` `uploader` `slider` `date-picker` |
192
+ | **Trigger** | `button` `link` `icon` `menuitem` `tag` |
193
+ | **Data** | `table` `row` `column` `cell` `list` `item` `card` `section` |
194
+ | **Feedback** | `message` `header` `label` `text` `tooltip` `badge` `breadcrumb` `image` |
195
+ | **System** | `key` `frame` `spinner` `progressbar` |
196
+
197
+ ### Auto-infer (no YAML entry needed)
198
+
199
+ | Gherkin | Playwright locator |
200
+ |---|---|
201
+ | `[Submit] button` | `getByRole('button', { name: 'Submit' })` |
202
+ | `[Home] link` | `getByRole('link', { name: 'Home' })` |
203
+ | `[Welcome] heading` / `header` | `getByRole('heading', { name: 'Welcome' })` |
204
+ | `[Email] field` | `getByPlaceholder('Email')` |
205
+ | `[Success] text` / `message` / `label` | `getByText('Success')` |
206
+ | `[Terms] checkbox` | `getByRole('checkbox', { name: 'Terms' })` |
207
+ | `[Male] radio` | `getByRole('radio', { name: 'Male' })` |
208
+ | `[Global] search` | `getByRole('searchbox', { name: 'Global' })` |
209
+ | `[Vietnam] option` | `getByRole('option', { name: 'Vietnam' })` |
210
+ | `[Price] slider` | `getByRole('slider', { name: 'Price' })` |
211
+ | `[Notification] toggle` | `getByRole('switch', { name: 'Notification' })` |
212
+ | `[Profile] tab` | `getByRole('tab', { name: 'Profile' })` |
213
+ | `[Orders] table` | `getByRole('table', { name: 'Orders' })` |
214
+ | `[Products] list` | `getByRole('list', { name: 'Products' })` |
215
+ | `[Name] column` | `getByRole('columnheader', { name: 'Name' })` |
216
+ | `[Confirm] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'Confirm' })` |
98
217
 
99
218
  ## YAML Keys
100
219
 
101
220
  `[Reference]` → **lowercase, keep Unicode**: `[Search Content]` → `search content:`, `[Thời gian]` → `thời gian:`
102
221
 
222
+ - Keys use **spaces** (not dots) as word separators
223
+ - Same label, different element types → add `--type` suffix
224
+ - Same label, nth occurrence → add `--N` suffix
225
+ - Target Name > 30 chars → shorten to 1–3 meaningful words
226
+
103
227
  ## Selectors (priority order)
104
228
 
105
229
  | type | value | name | use |
@@ -120,8 +244,30 @@ Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `colu
120
244
 
121
245
  | Tag | Effect |
122
246
  |---|---|
247
+ | `@auto` | Standard scenario, ready for automation |
248
+ | `@manual` | Skip in generation |
249
+ | `@smoke` / `@regression` | Test suite grouping |
123
250
  | `@auth:role` | Use auth storage state for role |
124
251
  | `@no-auth` | Disable inherited auth |
125
- | `@steps:name` | Define reusable step block |
126
- | `@extend:name` | Prepend steps from @steps block |
127
- | `@manual` | Skip in generation |
252
+ | `@steps:name` | Define reusable step block (base scenario) |
253
+ | `@extend:name` | Prepend Given→When from @steps block (skip Then) |
254
+
255
+ ### @extend behavior
256
+
257
+ - Tool executes **only Given→When** of `@steps` scenario (skips Then)
258
+ - The `Given` in `@extend` scenario is the **entry assertion** (confirms state after base steps)
259
+ - If `@steps` scenario fails, `@extend` scenario is **skipped**
260
+ - Name format: `snake_case` or `kebab-case` with module prefix: `@steps:kudos__open_modal`
261
+
262
+ ## Common Syntax Errors
263
+
264
+ | Error | Wrong | Correct |
265
+ |---|---|---|
266
+ | Wrong keyword | `Given User click [T] button` | `When User click [T] button` |
267
+ | Wrong action for type | `When User click [T] checkbox` | `When User check [T] checkbox` |
268
+ | press wrong target | `When User press [Submit] button` | `When User press [Enter] key` |
269
+ | uncheck radio | `When User uncheck [Male] radio` | `When User check [Female] radio` |
270
+ | Hardcode data | `with {{admin@mail.com}}` | `with {{invalid_email}}` |
271
+ | Missing `is` for state | `with {{text}} hidden` | `with {{text}} is hidden` |
272
+ | State as value | `with {{disabled}}` | `is disabled` |
273
+ | Missing target type | `fill [email] with {{v}}` | `fill [email] field with {{v}}` |
@@ -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.
@@ -168,9 +186,16 @@ Many elements don't need a YAML entry — sungen auto-infers from the Gherkin la
168
186
  |---|---|
169
187
  | `[Submit] button` | `getByRole('button', { name: 'Submit' })` |
170
188
  | `[Home] link` | `getByRole('link', { name: 'Home' })` |
171
- | `[Welcome] heading` | `getByRole('heading', { name: 'Welcome' })` |
189
+ | `[Welcome] heading` / `header` | `getByRole('heading', { name: 'Welcome' })` |
172
190
  | `[Email] field` | `getByPlaceholder('Email')` |
173
- | `[Success] text` | `getByText('Success')` |
191
+ | `[Success] text` / `message` / `label` | `getByText('Success')` |
192
+ | `[Terms] checkbox` | `getByRole('checkbox', { name: 'Terms' })` |
193
+ | `[Global] search` | `getByRole('searchbox', { name: 'Global' })` |
194
+ | `[Vietnam] option` | `getByRole('option', { name: 'Vietnam' })` |
195
+ | `[Price] slider` | `getByRole('slider', { name: 'Price' })` |
196
+ | `[Notification] toggle` | `getByRole('switch', { name: 'Notification' })` |
197
+ | `[Profile] tab` | `getByRole('tab', { name: 'Profile' })` |
198
+ | `[Confirm] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'Confirm' })` |
174
199
 
175
200
  **Only add a YAML entry when auto-infer won't work:**
176
201
  - The accessible name differs from the Gherkin label
@@ -181,20 +206,101 @@ Many elements don't need a YAML entry — sungen auto-infers from the Gherkin la
181
206
 
182
207
  ---
183
208
 
184
- ### Fix Loop on Test Failure
209
+ ### Proactive Selector Validation (before running tests)
210
+
211
+ **Most failures are selector mismatches.** Validate selectors against the live page BEFORE running any test — this eliminates slow compile→run→read→fix cycles.
212
+
213
+ After generating `selectors.yaml` (Step 3), verify each entry:
185
214
 
215
+ #### How to validate
216
+
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 exists
223
+ Array.from(document.querySelectorAll('button'))
224
+ .filter(el => el.textContent.includes('Submit') || el.getAttribute('aria-label')?.includes('Submit'))
225
+ .length;
186
226
  ```
187
- attempt = 0
188
- while attempt < 5:
189
- compile run test
190
- if pass done
191
- read error-context.md in test-results/ check exact snapshot state at failure time
192
- fix selectors.yaml or test-data.yaml
193
- recompile attempt++
194
- if still failing ask user about direct .spec.ts fix
227
+
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:
232
+ - **role + name**: does the snapshot contain `button "Submit"` or `link "Home"`?
233
+ - **testid**: does `browser_evaluate` find `[data-testid="xxx"]`?
234
+ - **placeholder**: does the snapshot contain a textbox with that placeholder?
235
+ - **locator**: does `browser_evaluate` find `document.querySelector('xxx')`?
236
+ 4. Fix mismatches immediately — no need to run tests
237
+
238
+ #### What to check
239
+
240
+ | Selector type | Validation method |
241
+ |---|---|
242
+ | `role` + `name` | Search snapshot for `role "name"` text |
243
+ | `testid` | `browser_evaluate`: `document.querySelector('[data-testid="xxx"]')` |
244
+ | `placeholder` | Search snapshot for textbox with placeholder |
245
+ | `locator` (CSS) | `browser_evaluate`: `document.querySelector('xxx')` |
246
+ | `page` | Verify URL path exists (navigate and check) |
247
+
248
+ #### Common proactive fixes
249
+
250
+ - Name mismatch → copy exact name from snapshot
251
+ - Element not found → check if element is inside iframe/dialog (needs `frame` or scope)
252
+ - Multiple matches → add `nth: 0` or `exact: true`
253
+ - No accessible name → switch to `testid` or `locator`
254
+
255
+ **Target: fix 80%+ of selector issues before the first test run.**
256
+
257
+ ---
258
+
259
+ ### Batched Test Execution
260
+
261
+ After proactive validation, run tests in **batches of 20** for faster feedback:
262
+
263
+ ```
264
+ 1. COMPILE — sungen generate --screen <screen>
265
+
266
+ 2. BATCH RUN — run 20 tests at a time:
267
+ npx playwright test <spec> --grep "VP-UI-001|VP-UI-002|...|VP-UI-020" --reporter=line
268
+
269
+ 3. IF FAILURES in batch:
270
+ a. Group failures by root cause (same selector, same error type)
271
+ b. Fix selectors.yaml or test-data.yaml
272
+ c. Recompile: sungen generate --screen <screen>
273
+ d. Re-run ONLY the failing tests from this batch
274
+ e. If fixed → move to next batch
275
+
276
+ 4. NEXT BATCH — repeat with next 20 tests (VP-UI-021...VP-UI-040)
277
+
278
+ 5. FINAL CONFIRMATION — after all batches pass, run ALL tests once:
279
+ npx playwright test <spec> --reporter=line
280
+ This catches regressions from selector changes.
281
+
282
+ 6. If still failing after 5 fix attempts per batch → ask user about direct .spec.ts fix
195
283
  ```
196
284
 
197
- **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.
285
+ #### Building batch `--grep` patterns
286
+
287
+ Extract scenario names and batch them:
288
+ ```bash
289
+ # Batch 1: first 20
290
+ npx playwright test <spec> --grep "VP-UI-001|VP-UI-002|...|VP-UI-020" --reporter=line
291
+
292
+ # Batch 2: next 20
293
+ npx playwright test <spec> --grep "VP-VAL-001|VP-VAL-002|...|VP-VAL-020" --reporter=line
294
+ ```
295
+
296
+ #### Grouping failures by root cause
297
+
298
+ Common patterns where 1 fix resolves many failures:
299
+ - **Same selector** — e.g., all `[Email Error]` tests fail → fix `email error` in selectors.yaml once
300
+ - **Same error type** — e.g., all `strict mode violation` → add `exact: true` or `nth`
301
+ - **Same assertion** — e.g., all `toHaveText` on inputs fail → change Gherkin pattern
302
+
303
+ Fix the root cause first, verify with the batch, then move on.
198
304
 
199
305
  ---
200
306
 
@@ -51,6 +51,10 @@ Type aliases (normalized automatically):
51
51
  | logo, image, icon | `img` |
52
52
  | btn | `button` |
53
53
  | input, textbox, textarea, editor | `field` |
54
+ | search | `searchbox` |
55
+ | toggle | `switch` |
56
+ | alert | `alertdialog` |
57
+ | modal, drawer | `dialog` |
54
58
  | column | `columnheader` |
55
59
  | list-item | `listitem` |
56
60
 
@@ -83,10 +87,20 @@ If no YAML key exists, the resolver infers from the Gherkin element type:
83
87
  |---|---|
84
88
  | `[X] button` | `getByRole('button', { name: 'X' })` |
85
89
  | `[X] link` | `getByRole('link', { name: 'X' })` |
86
- | `[X] heading` | `getByRole('heading', { name: 'X' })` |
90
+ | `[X] heading` / `header` | `getByRole('heading', { name: 'X' })` |
87
91
  | `[X] checkbox` | `getByRole('checkbox', { name: 'X' })` |
92
+ | `[X] radio` | `getByRole('radio', { name: 'X' })` |
88
93
  | `[X] field` | `getByPlaceholder('X')` |
89
- | `[X] text` | `getByText('X')` |
94
+ | `[X] text` / `message` / `label` | `getByText('X')` |
90
95
  | `[X] logo/image/icon` | `getByRole('img', { name: 'X' })` |
96
+ | `[X] search` | `getByRole('searchbox', { name: 'X' })` |
97
+ | `[X] option` | `getByRole('option', { name: 'X' })` |
98
+ | `[X] slider` | `getByRole('slider', { name: 'X' })` |
99
+ | `[X] toggle` | `getByRole('switch', { name: 'X' })` |
100
+ | `[X] tab` | `getByRole('tab', { name: 'X' })` |
101
+ | `[X] table` | `getByRole('table', { name: 'X' })` |
102
+ | `[X] list` | `getByRole('list', { name: 'X' })` |
103
+ | `[X] column` | `getByRole('columnheader', { name: 'X' })` |
104
+ | `[X] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'X' })` |
91
105
 
92
106
  **Only add a YAML entry when** the auto-inferred locator won't work (wrong name, need testid, need nth, etc.).