@sun-asterisk/sungen 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +10 -11
  2. package/dist/cli/index.js +1 -1
  3. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +1 -1
  4. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +5 -1
  5. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +7 -1
  6. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +5 -1
  7. package/dist/generators/test-generator/patterns/table-patterns.d.ts +9 -0
  8. package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +1 -1
  9. package/dist/generators/test-generator/patterns/table-patterns.js +94 -98
  10. package/dist/generators/test-generator/patterns/table-patterns.js.map +1 -1
  11. package/dist/generators/test-generator/patterns/types.d.ts +2 -0
  12. package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
  13. package/dist/generators/test-generator/step-mapper.d.ts +13 -0
  14. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  15. package/dist/generators/test-generator/step-mapper.js +80 -0
  16. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  17. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
  18. package/dist/orchestrator/ai-rules-updater.js +8 -6
  19. package/dist/orchestrator/ai-rules-updater.js.map +1 -1
  20. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  21. package/dist/orchestrator/project-initializer.js +33 -2
  22. package/dist/orchestrator/project-initializer.js.map +1 -1
  23. package/dist/orchestrator/screen-manager.js +1 -1
  24. package/dist/orchestrator/screen-manager.js.map +1 -1
  25. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
  26. package/{src/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md → dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md} +7 -7
  27. package/dist/orchestrator/templates/ai-instructions/claude-cmd-review.md +21 -0
  28. package/{src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md → dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md} +2 -2
  29. package/dist/orchestrator/templates/ai-instructions/claude-config.md +65 -12
  30. package/dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +128 -52
  31. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +9 -40
  32. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +67 -239
  33. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +40 -208
  34. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-review.md +104 -0
  35. package/dist/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +186 -0
  36. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
  37. package/dist/orchestrator/templates/ai-instructions/{copilot-cmd-make-tc.md → copilot-cmd-create-test.md} +8 -8
  38. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +24 -0
  39. package/{src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md → dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md} +3 -3
  40. package/dist/orchestrator/templates/ai-instructions/copilot-config.md +66 -13
  41. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +128 -52
  42. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +9 -40
  43. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +67 -214
  44. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -208
  45. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md +104 -0
  46. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +186 -0
  47. package/dist/orchestrator/templates/readme.md +4 -4
  48. package/package.json +1 -1
  49. package/src/cli/index.ts +1 -1
  50. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +1 -1
  51. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +5 -1
  52. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +7 -1
  53. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +5 -1
  54. package/src/generators/test-generator/patterns/table-patterns.ts +98 -111
  55. package/src/generators/test-generator/patterns/types.ts +2 -0
  56. package/src/generators/test-generator/step-mapper.ts +87 -1
  57. package/src/orchestrator/ai-rules-updater.ts +8 -6
  58. package/src/orchestrator/project-initializer.ts +38 -2
  59. package/src/orchestrator/screen-manager.ts +1 -1
  60. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
  61. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md → src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md} +7 -7
  62. package/src/orchestrator/templates/ai-instructions/claude-cmd-review.md +21 -0
  63. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md → src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md} +2 -2
  64. package/src/orchestrator/templates/ai-instructions/claude-config.md +65 -12
  65. package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +128 -52
  66. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +9 -40
  67. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +67 -239
  68. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +40 -208
  69. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-review.md +104 -0
  70. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +186 -0
  71. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
  72. package/src/orchestrator/templates/ai-instructions/{copilot-cmd-make-tc.md → copilot-cmd-create-test.md} +8 -8
  73. package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +24 -0
  74. package/{dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md → src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md} +3 -3
  75. package/src/orchestrator/templates/ai-instructions/copilot-config.md +66 -13
  76. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +128 -52
  77. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +9 -40
  78. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +67 -214
  79. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -208
  80. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md +104 -0
  81. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +186 -0
  82. package/src/orchestrator/templates/readme.md +4 -4
  83. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +0 -2
  84. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +0 -2
  85. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-review.md +0 -228
  86. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-review.md +0 -228
  87. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +0 -2
  88. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +0 -2
  89. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-review.md +0 -228
  90. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-review.md +0 -228
@@ -1,287 +1,140 @@
1
1
  ---
2
2
  name: sungen-selector-fix
3
- description: 'Selector generation and fixing strategy for make-test explores live page, generates selectors.yaml, auto-fixes on test failure. Use this when generating selectors.yaml from a live page or fixing failing selectors.'
3
+ description: 'Selector generation and fixing strategy — explore live page, generate selectors.yaml, validate, auto-fix. Auto-loaded by run-test command.'
4
4
  user-invocable: false
5
5
  ---
6
6
 
7
- ## Selector Generation & Fix Strategy
7
+ ## When to Generate
8
8
 
9
- ### When to Generate Selectors
10
-
11
- Selectors are generated during `/sungen:make-test`, NOT during `/sungen:make-tc`. This ensures selectors are always validated against the live page.
9
+ Selectors are generated during `/sungen:run-test`, NOT during `/sungen:create-test`.
12
10
 
13
11
  ---
14
12
 
15
- ### Step 1: Authenticate
13
+ ## Step 1: Authenticate & Snapshot
16
14
 
17
15
  1. Read `baseURL` from `playwright.config.ts`
18
16
  2. `browser_navigate` to `baseURL`
19
- 3. If redirected to login, ask the user to log in manually:
20
- > "This page requires login. Please log in using the browser. Confirm when you're done."
21
- 4. Once confirmed, `browser_navigate` to the target page and take `browser_snapshot`
17
+ 3. If redirected to login ask user to log in manually via MCP browser
18
+ 4. Navigate to target page `browser_snapshot`
22
19
 
23
- **Never use `sungen makeauth`.** Always let the user log in manually via the MCP browser.
24
- **Never use `browser_evaluate` to inject cookies or localStorage.** Use `browser_evaluate` only to read DOM info (e.g. detect `data-testid` attributes, check element properties).
20
+ **Never use `sungen makeauth`.** Never use `browser_evaluate` to inject cookies.
25
21
 
26
22
  ---
27
23
 
28
- ### Step 2: Build an Element Catalog from the Snapshot
29
-
30
- **Never guess or infer selector names from Gherkin labels. Always copy exact values from the snapshot.**
24
+ ## Step 2: Build Element Catalog
31
25
 
32
- After taking the snapshot, mentally catalog ALL accessible elements:
26
+ **Never guess names from Gherkin labels. Copy exact values from snapshot.**
33
27
 
34
- - **Buttons**: list each `button "..."` with its exact accessible name
35
- - **Links**: list each `link "..."` with its exact accessible name
36
- - **Headings**: list each `heading "..."` with level and exact name
37
- - **Inputs**: list each `textbox`, `searchbox`, `combobox` — note if it has an accessible name or not
38
- - **Regions/landmarks**: `region "..."`, `navigation "..."`, `dialog "..."`
39
- - **Images**: `img "..."` with accessible name (alt text)
28
+ Catalog all accessible elements from snapshot: buttons, links, headings, inputs, regions, images.
40
29
 
41
- Then check for `data-testid` attributes (more stable than accessible names):
30
+ Then check for `data-testid` attributes:
42
31
  ```js
43
- // browser_evaluate — read-only, safe:
44
32
  Array.from(document.querySelectorAll('[data-testid]'))
45
33
  .map(e => ({ testid: e.dataset.testid, tag: e.tagName, text: e.textContent.trim().slice(0, 60) }))
46
34
  ```
47
35
 
48
36
  ---
49
37
 
50
- ### Step 3: Map Gherkin Labels → Selectors
38
+ ## Step 3: Map Gherkin Labels → Selectors
51
39
 
52
- For each `[Reference]` in the `.feature` file, find the best match in the element catalog.
40
+ Selector priority (use first applicable):
53
41
 
54
- #### Selector priority (use first applicable)
55
-
56
- | Priority | type | When to use |
42
+ | Priority | type | When |
57
43
  |---|---|---|
58
- | 1 | `testid` | `data-testid` attribute exists |
59
- | 2 | `role` + exact name | Interactive elements (button, link, heading) |
60
- | 3 | `placeholder` | Input with visible placeholder text |
61
- | 4 | `label` | Form field with `<label>` association |
62
- | 5 | `locator` (CSS / aria attr) | No accessible name (e.g. bare searchbox, icon button) |
63
- | 6 | `text` | Static text only — avoid for dynamic/data-driven content |
64
-
65
- #### The exact name rule
44
+ | 1 | `testid` | `data-testid` exists |
45
+ | 2 | `role` + exact name | Interactive elements |
46
+ | 3 | `placeholder` | Input with placeholder |
47
+ | 4 | `label` | Form field with `<label>` |
48
+ | 5 | `locator` (CSS) | No accessible name |
49
+ | 6 | `text` | Static text only |
66
50
 
67
- Copy the accessible name **character-for-character** from the snapshot. Do not transform, translate, or infer from the Gherkin label.
68
-
69
- ```yaml
70
- # BAD — name inferred from Gherkin label, not from snapshot
71
- notification bell:
72
- type: 'role'
73
- value: 'button'
74
- name: 'Notification Bell'
75
-
76
- # GOOD — exact name from snapshot: button "Notifications"
77
- notification bell:
78
- type: 'role'
79
- value: 'button'
80
- name: 'Notifications'
81
- ```
82
-
83
- #### When there is no accessible name
84
-
85
- Use a `locator` with a CSS attribute selector or ARIA role:
51
+ **Exact name rule**: copy name character-for-character from snapshot. Never infer from Gherkin label.
86
52
 
53
+ **No accessible name**: use `locator` with CSS/ARIA attribute:
87
54
  ```yaml
88
55
  search field:
89
56
  type: 'locator'
90
57
  value: '[role="searchbox"]'
91
-
92
- icon button:
93
- type: 'locator'
94
- value: '[aria-label="Close"]'
95
58
  ```
96
59
 
97
- ---
98
-
99
- ### Step 4: Handle Dynamic Content
100
-
101
- For elements whose text changes between test runs (list items, card data, user-generated content, timestamps):
102
-
103
- **Never use `type: text` with a specific value** — it breaks when data changes.
104
-
105
- ```yaml
106
- # BAD — specific text breaks when data changes
107
- card title:
108
- type: 'text'
109
- value: 'Some specific title'
110
-
111
- # GOOD — structural: first heading inside a card container
112
- card title:
113
- type: 'role'
114
- value: 'heading'
115
- nth: 1 # skip page-level headings, get first card heading
60
+ **Dynamic content**: never use `type: text` with specific value. Use `testid` or structural `role` + `nth`.
116
61
 
117
- # BEST if the app has data-testid
118
- card title:
119
- type: 'testid'
120
- value: 'card-title'
121
- ```
62
+ **Multiple matches**: add `nth: 0` for first occurrence.
122
63
 
123
- To determine the correct `nth` offset, count how many matching elements appear before the target in the snapshot (e.g. how many headings come before the first card heading).
64
+ Auto-infer rules and lookup priority see `sungen-selector-keys` skill.
124
65
 
125
66
  ---
126
67
 
127
- ### Step 4b: Handle Detail Screens with Dynamic IDs
68
+ ## Step 4: Table Selectors
128
69
 
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
70
+ For table patterns using the v2.3 syntax, generate table selectors with `columns` config:
133
71
 
134
72
  ```yaml
135
- # selectors.yaml — full path with real ID
136
- user detail:
137
- type: 'page'
138
- value: '/admin/users/de42d800-0f5a-490e-9dcf-344fedbd34a5'
73
+ users:
74
+ type: 'role'
75
+ value: 'table'
76
+ name: 'Users'
77
+ columns:
78
+ username:
79
+ index: 0
80
+ header: 'Username'
81
+ email:
82
+ index: 1
83
+ header: 'Email'
84
+ status:
85
+ index: 2
86
+ header: 'Status'
139
87
  ```
140
88
 
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
-
145
- ### Step 5: Handle SPA / Client-side Routing
89
+ **How to build `columns`**: count column headers in snapshot (left to right, 0-indexed). Map each `[Col] column` reference from feature file to its index.
146
90
 
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.
148
-
149
- **Symptom**: Tests see a different page than expected (e.g. home page instead of `/dashboard`).
150
-
151
- **Fix in the feature file**: Add an explicit assertion after navigation to confirm the correct page has loaded before proceeding:
91
+ **Why**: enables exact cell assertion via `cell.nth(index).toHaveText(value)` instead of approximate `cell.filter({ hasText })`.
152
92
 
93
+ **Row scope Gherkin** — these patterns use the table selector:
153
94
  ```gherkin
154
- Scenario: VP-UI-001 Some test
155
- Given User is on [Dashboard] page
156
- Then User see [Dashboard Title] heading is visible ← confirms page loaded
157
- When User click [Some Button] button
158
- ...
95
+ Then User see [Username] row in [Users] table with {{user_name}}
96
+ Then User see [Status] column with {{expected_status}}
97
+ When User click [Edit] button in [Users] table with {{user_name}}
159
98
  ```
160
99
 
161
- The "page loaded" check should use a **stable, always-visible element** on that specific page — typically the main page heading.
162
-
163
100
  ---
164
101
 
165
- ### Step 6: Handle Multiple Matches (strict mode)
102
+ ## Step 5: Detail Screens with Dynamic IDs
166
103
 
167
- When an element appears more than once (e.g. desktop + mobile nav, repeated list items), add `nth`:
104
+ For screens like `/admin/users/:id`:
105
+ 1. Navigate to list page via MCP to find a real record ID
106
+ 2. Hardcode the ID in page selector
168
107
 
169
108
  ```yaml
170
- nav link:
171
- type: 'role'
172
- value: 'link'
173
- name: 'Dashboard'
174
- nth: 0 # first occurrence (desktop nav)
109
+ user detail:
110
+ type: 'page'
111
+ value: '/admin/users/de42d800-0f5a-490e-9dcf-344fedbd34a5'
175
112
  ```
176
113
 
177
- Use `nth: 0` for the first match of dynamic/repeated elements.
178
-
179
114
  ---
180
115
 
181
- ### Auto-Infer (skip YAML entry)
182
-
183
- Many elements don't need a YAML entry — sungen auto-infers from the Gherkin label:
184
-
185
- | Gherkin | Auto-inferred locator |
186
- |---|---|
187
- | `[Submit] button` | `getByRole('button', { name: 'Submit' })` |
188
- | `[Home] link` | `getByRole('link', { name: 'Home' })` |
189
- | `[Welcome] heading` / `header` | `getByRole('heading', { name: 'Welcome' })` |
190
- | `[Email] field` | `getByPlaceholder('Email')` |
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' })` |
199
-
200
- **Only add a YAML entry when auto-infer won't work:**
201
- - The accessible name differs from the Gherkin label
202
- - Needs `nth` to disambiguate
203
- - Needs `testid` or CSS locator
204
- - Is dynamic content (needs structural selector)
205
- - Is a bare input with no label or placeholder
116
+ ## Step 6: Validate Before Running
206
117
 
207
- ---
208
-
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:
118
+ **Fix 80%+ of selector issues before first test run.**
214
119
 
215
- #### How to validate
120
+ After generating `selectors.yaml`, verify each entry against the live snapshot:
121
+ - `role` + `name` → search snapshot for `role "name"`
122
+ - `testid` → `browser_evaluate`: `document.querySelector('[data-testid="xxx"]')`
123
+ - `placeholder` → search snapshot for textbox with placeholder
124
+ - `page` → verify URL path exists
216
125
 
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;
226
- ```
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 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.**
126
+ Common fixes: name mismatch copy from snapshot, element in iframe add `frame`, multiple matches → add `nth` or `exact: true`.
244
127
 
245
128
  ---
246
129
 
247
- ### Batched Test Execution
130
+ ## Step 7: Run & Fix Loop
248
131
 
249
- After proactive validation, run tests in **batches of 20** for faster feedback:
132
+ Run tests in batches of 20. Group failures by root cause (same selector, same error type). Fix once, verify batch, move to next.
250
133
 
251
134
  ```
252
- 1. COMPILE sungen generate --screen <screen>
253
-
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
135
+ compile batch run 20 fix root cause → recompile → re-run failures → next batch
269
136
  ```
270
137
 
271
- #### Grouping failures by root cause
272
-
273
- Common patterns where 1 fix resolves many failures:
274
- - **Same selector** — e.g., all `[Email Error]` tests fail → fix once
275
- - **Same error type** — e.g., all `strict mode violation` → add `exact: true` or `nth`
276
- - **Same assertion** — e.g., all `toHaveText` on inputs fail → change Gherkin pattern
277
-
278
- Fix the root cause first, verify with the batch, then move on.
279
-
280
- ---
281
-
282
- ### Key Rules (from sungen-selector-keys)
138
+ After all batches pass run ALL tests once for regression check.
283
139
 
284
- - `[Reference]` lowercase, keep Unicode: `[Search Content]``search content:`, `[Thời gian]` `thời gian:`
285
- - Keys use **spaces** (not dots) as word separators
286
- - Same label, different element types → add `--type` suffix
287
- - Same label, nth occurrence → add `--N` suffix
140
+ If still failing after 5 attempts per batchask user about direct `.spec.ts` fix.