@sun-asterisk/sungen 2.2.1 → 2.2.3
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.
- package/README.md +63 -34
- package/dist/cli/index.js +1 -1
- package/dist/generators/gherkin-parser/index.js +1 -1
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/alert-accept-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/alert-dismiss-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/alert-fill-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/alert-text-assertion.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +1 -0
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.js +83 -57
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js +56 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +16 -0
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -4
- package/dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +22 -9
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +148 -21
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +51 -11
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +16 -2
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +1 -1
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +1 -1
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +12 -5
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +22 -9
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +148 -21
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +51 -11
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +16 -2
- package/docs/gherkin standards/gherkin-core-standard.md +163 -160
- package/docs/gherkin standards/gherkin-core-standard.vi.md +290 -404
- package/docs/gherkin-dictionary.md +71 -16
- package/package.json +1 -1
- package/src/cli/index.ts +1 -1
- package/src/generators/gherkin-parser/index.ts +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/alert-accept-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/alert-dismiss-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/alert-fill-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/alert-text-assertion.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +1 -0
- package/src/generators/test-generator/patterns/assertion-patterns.ts +93 -65
- package/src/generators/test-generator/patterns/interaction-patterns.ts +58 -1
- package/src/generators/test-generator/utils/selector-resolver.ts +16 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -4
- package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +22 -9
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +148 -21
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +51 -11
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +16 -2
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +1 -1
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +1 -1
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +12 -5
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +22 -9
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +148 -21
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +51 -11
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +16 -2
|
@@ -168,9 +168,16 @@ Many elements don't need a YAML entry — sungen auto-infers from the Gherkin la
|
|
|
168
168
|
|---|---|
|
|
169
169
|
| `[Submit] button` | `getByRole('button', { name: 'Submit' })` |
|
|
170
170
|
| `[Home] link` | `getByRole('link', { name: 'Home' })` |
|
|
171
|
-
| `[Welcome] heading` | `getByRole('heading', { name: 'Welcome' })` |
|
|
171
|
+
| `[Welcome] heading` / `header` | `getByRole('heading', { name: 'Welcome' })` |
|
|
172
172
|
| `[Email] field` | `getByPlaceholder('Email')` |
|
|
173
|
-
| `[Success] text` | `getByText('Success')` |
|
|
173
|
+
| `[Success] text` / `message` / `label` | `getByText('Success')` |
|
|
174
|
+
| `[Terms] checkbox` | `getByRole('checkbox', { name: 'Terms' })` |
|
|
175
|
+
| `[Global] search` | `getByRole('searchbox', { name: 'Global' })` |
|
|
176
|
+
| `[Vietnam] option` | `getByRole('option', { name: 'Vietnam' })` |
|
|
177
|
+
| `[Price] slider` | `getByRole('slider', { name: 'Price' })` |
|
|
178
|
+
| `[Notification] toggle` | `getByRole('switch', { name: 'Notification' })` |
|
|
179
|
+
| `[Profile] tab` | `getByRole('tab', { name: 'Profile' })` |
|
|
180
|
+
| `[Confirm] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'Confirm' })` |
|
|
174
181
|
|
|
175
182
|
**Only add a YAML entry when auto-infer won't work:**
|
|
176
183
|
- The accessible name differs from the Gherkin label
|
|
@@ -181,19 +188,52 @@ Many elements don't need a YAML entry — sungen auto-infers from the Gherkin la
|
|
|
181
188
|
|
|
182
189
|
---
|
|
183
190
|
|
|
184
|
-
### Fix Loop on Test Failure
|
|
191
|
+
### Fix Loop on Test Failure (Batched Strategy)
|
|
185
192
|
|
|
193
|
+
Running all tests every iteration is slow. Use a batched approach to fix faster:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
1. INITIAL RUN — run ALL tests, collect full failure list
|
|
197
|
+
npx playwright test <spec> --reporter=line
|
|
198
|
+
|
|
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.
|
|
213
|
+
|
|
214
|
+
4. If still failing after 5 fix attempts → ask user about direct .spec.ts fix
|
|
186
215
|
```
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if still failing → ask user about direct .spec.ts fix
|
|
216
|
+
|
|
217
|
+
#### Building the `--grep` pattern
|
|
218
|
+
|
|
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
|
|
195
223
|
```
|
|
196
224
|
|
|
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
|
|
227
|
+
|
|
228
|
+
#### Grouping failures by root cause
|
|
229
|
+
|
|
230
|
+
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
|
|
232
|
+
- **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)
|
|
234
|
+
|
|
235
|
+
Fix the root cause first, verify with the batch, then move on.
|
|
236
|
+
|
|
197
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.
|
|
198
238
|
|
|
199
239
|
---
|
|
@@ -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.).
|
|
@@ -3,7 +3,7 @@ name: sungen-add-screen
|
|
|
3
3
|
description: 'Add a new Sungen screen — scaffolds directories and delegates to /sungen-make-tc for test case creation'
|
|
4
4
|
argument-hint: '[screen-name] [url-path]'
|
|
5
5
|
agent: 'agent'
|
|
6
|
-
tools: [
|
|
6
|
+
tools: [vscode, execute, read, agent, edit, search, web, browser, todo]
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
**Input**: Screen name and URL path (e.g., `/sungen-add-screen login /login`).
|
|
@@ -3,7 +3,7 @@ name: sungen-make-tc
|
|
|
3
3
|
description: 'Create or update test cases for a Sungen screen — generates feature + test-data files (20+ scenarios per viewpoint). Uses sungen-gherkin-syntax and sungen-tc-generation skills.'
|
|
4
4
|
argument-hint: '[screen-name]'
|
|
5
5
|
agent: 'agent'
|
|
6
|
-
tools: [
|
|
6
|
+
tools: [vscode, execute, read, agent, edit, search, web, browser, todo, 'playwright/*']
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
**Input**: Screen name (e.g., `/sungen-make-tc admin-users`).
|
|
@@ -3,7 +3,7 @@ name: sungen-make-test
|
|
|
3
3
|
description: 'Generate selectors, compile, and run Playwright tests — auto-fixes selectors on failure. Uses sungen-selector-fix, sungen-selector-keys, and sungen-error-mapping skills.'
|
|
4
4
|
argument-hint: '[screen-name]'
|
|
5
5
|
agent: 'agent'
|
|
6
|
-
tools: [
|
|
6
|
+
tools: [vscode, execute, read, agent, edit, search, web, browser, todo, 'playwright/*']
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
**Input**: Screen name (e.g., `/sungen-make-test admin-users`).
|
|
@@ -21,7 +21,14 @@ You are a **Senior Developer** specialized in Playwright test debugging. You gen
|
|
|
21
21
|
1. Verify `qa/screens/${input:screen}/` has `.feature` + `test-data.yaml`. If not → run `/sungen-make-tc` first.
|
|
22
22
|
2. Explore live page via #tool:playwright to generate `selectors.yaml` (per `sungen-selector-fix` skill).
|
|
23
23
|
3. Compile with #tool:terminal: `sungen generate --screen ${input:screen}`
|
|
24
|
-
4.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
4. **Initial run** — run ALL tests to get the full failure picture:
|
|
25
|
+
`npx playwright test specs/generated/${input:screen}/*.spec.ts --reporter=line`
|
|
26
|
+
5. **Batched fix loop** (max 5 attempts) — see `sungen-selector-fix` skill for details:
|
|
27
|
+
- Group failures by root cause (same selector, same error type)
|
|
28
|
+
- Fix selectors/test-data for the current batch
|
|
29
|
+
- Recompile, then re-run ONLY the previously-failing tests (max 20 at a time via `--grep`)
|
|
30
|
+
- If the batch passes, pick the next batch of remaining failures
|
|
31
|
+
- Repeat until no failures remain or max attempts reached
|
|
32
|
+
6. **Final confirmation** — run ALL tests once to ensure no regressions.
|
|
33
|
+
7. After 5 fix attempts still failing → ask user about direct `.spec.ts` fix.
|
|
34
|
+
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
|
|
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
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
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,34 @@ description: 'Sungen Gherkin patterns, selector types, and YAML key rules. Use t
|
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Standard Syntax
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
[Keyword] User <Action> [Target Name] <Target 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
|
+
|
|
17
|
+
## Keyword → Action Rules
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
GIVEN → is on
|
|
21
|
+
WHEN → click · fill · select · press · clear · check · uncheck · hover
|
|
22
|
+
[⚠️ wait for — only for Spinner/Modal, minimize usage]
|
|
23
|
+
THEN → see
|
|
24
|
+
AND → inherits from preceding keyword
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Step Patterns (70 patterns)
|
|
8
28
|
|
|
9
29
|
### Setup
|
|
10
30
|
|
|
11
31
|
```
|
|
12
32
|
User is on [T] page # navigate to page
|
|
13
33
|
User is on [T] page with {{v}} # navigate with data (e.g., detail page with ID)
|
|
34
|
+
User is on [T] dialog # enter dialog scope
|
|
14
35
|
User is logged in | is not logged in # auth state
|
|
15
36
|
```
|
|
16
37
|
|
|
@@ -18,31 +39,57 @@ User is logged in | is not logged in # auth state
|
|
|
18
39
|
|
|
19
40
|
```
|
|
20
41
|
User fill [T] field with {{v}} # text input
|
|
42
|
+
User fill [T] textarea with {{v}} # multiline input
|
|
43
|
+
User fill [T] search with {{v}} # search input
|
|
44
|
+
User fill [T] slider with {{v}} # set slider value
|
|
45
|
+
User fill [T] date picker with {{v}} # date input
|
|
21
46
|
User clear [T] field # clear input
|
|
22
47
|
User check [T] checkbox # check
|
|
48
|
+
User check [T] toggle # toggle on (same as check)
|
|
49
|
+
User check [T] radio # select radio
|
|
23
50
|
User uncheck [T] checkbox # uncheck
|
|
51
|
+
User uncheck [T] toggle # toggle off
|
|
24
52
|
User select [T] dropdown with {{v}} # select option
|
|
25
|
-
User
|
|
26
|
-
User upload [T] file with {{f}} # file upload
|
|
53
|
+
User upload [T] uploader with {{f}} # file upload
|
|
27
54
|
```
|
|
28
55
|
|
|
29
56
|
### Interaction
|
|
30
57
|
|
|
31
58
|
```
|
|
32
|
-
User click [T] button # click
|
|
33
|
-
User click [T]
|
|
59
|
+
User click [T] button # click (static element, no value)
|
|
60
|
+
User click [T] row with {{v}} # click dynamic list item (row/item/card/option)
|
|
61
|
+
User click [T] tab # switch tab
|
|
62
|
+
User click [T] column # sort column
|
|
63
|
+
User click [T] breadcrumb # navigate breadcrumb
|
|
34
64
|
User double click [T] element # double click
|
|
35
|
-
User hover [T] icon # hover
|
|
65
|
+
User hover [T] icon # hover (must have see/click after)
|
|
66
|
+
User hover [T] row # hover row for hidden actions
|
|
36
67
|
User drag [T] to [T2] # drag and drop
|
|
37
68
|
User expand [T] row # expand (aria-expanded)
|
|
38
69
|
User collapse [T] row # collapse (aria-expanded)
|
|
39
70
|
```
|
|
40
71
|
|
|
72
|
+
#### click + with {{Value}} rule
|
|
73
|
+
|
|
74
|
+
- **NO value** for static elements: `button`, `link`, `icon`, `tab`, `toggle`
|
|
75
|
+
- **WITH value** only for dynamic lists: `row`, `item`, `card`, `option`
|
|
76
|
+
|
|
77
|
+
### Browser Alert (system dialog)
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
User click [OK] alert # accept (OK/Accept/Yes/Confirm)
|
|
81
|
+
User click [Cancel] alert # dismiss (Cancel/Dismiss/No)
|
|
82
|
+
User fill [T] alert with {{v}} # fill prompt + accept
|
|
83
|
+
User see [message text] alert # assert dialog message
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> Alert steps must appear BEFORE the action that triggers the dialog.
|
|
87
|
+
|
|
41
88
|
### Keyboard
|
|
42
89
|
|
|
43
90
|
```
|
|
44
|
-
User press Escape key
|
|
45
|
-
User press Enter on [T] field
|
|
91
|
+
User press [Escape] key # global key press
|
|
92
|
+
User press [Enter] on [T] field # key on element
|
|
46
93
|
```
|
|
47
94
|
|
|
48
95
|
### Wait
|
|
@@ -53,26 +100,51 @@ User wait for [T] dialog # wait for element
|
|
|
53
100
|
User wait for [T] dialog is STATE # wait for state
|
|
54
101
|
User wait for [T] dialog with {{v}} # wait for element with text
|
|
55
102
|
User wait for [T] page # wait for navigation
|
|
103
|
+
User wait for [T] message # wait for toast/feedback
|
|
104
|
+
User wait for [T] button # wait for button to appear
|
|
56
105
|
```
|
|
57
106
|
|
|
58
107
|
### Scroll & Frame
|
|
59
108
|
|
|
60
109
|
```
|
|
61
110
|
User scroll to [T] section # scroll into view
|
|
62
|
-
User switch to [T] frame # enter
|
|
111
|
+
User switch to [T] frame # enter iframe
|
|
112
|
+
User switch to [main] frame # exit iframe
|
|
63
113
|
```
|
|
64
114
|
|
|
65
|
-
### Assertions
|
|
115
|
+
### Assertions (6 verify patterns)
|
|
66
116
|
|
|
67
117
|
```
|
|
68
|
-
|
|
69
|
-
User see [T]
|
|
70
|
-
User see [T]
|
|
118
|
+
# 1. Visibility
|
|
119
|
+
User see [T] message # visible (default)
|
|
120
|
+
User see [T] modal is hidden # hidden
|
|
121
|
+
|
|
122
|
+
# 2. Text Content (exact full match — toHaveText)
|
|
123
|
+
User see [T] message with {{v}} # exact text match
|
|
124
|
+
User see [T] header with {{v}} # heading text
|
|
125
|
+
User see [T] label with {{v}} # label text
|
|
126
|
+
|
|
127
|
+
# 3. Partial Text Match (toContainText)
|
|
71
128
|
User see [T] text contains {{v}} # partial text match
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
User see [T]
|
|
75
|
-
User see {{v}}
|
|
129
|
+
|
|
130
|
+
# 4. Input Value (toHaveValue)
|
|
131
|
+
User see [T] field with {{v}} # input value
|
|
132
|
+
User see [T] dropdown with {{v}} # selected value
|
|
133
|
+
User see [T] date picker with {{v}} # date value
|
|
134
|
+
User see [T] search with {{v}} # search input value
|
|
135
|
+
User see [T] slider with {{v}} # slider value
|
|
136
|
+
|
|
137
|
+
# 5. Component State
|
|
138
|
+
User see [T] button is disabled # state assertion
|
|
139
|
+
User see [T] checkbox is checked # checked state
|
|
140
|
+
User see [T] toggle is unchecked # unchecked state
|
|
141
|
+
User see [T] dialog with {{v}} is hidden # text + state combined
|
|
142
|
+
|
|
143
|
+
# 6. Count
|
|
144
|
+
User see [T] row with {{count}} # element count
|
|
145
|
+
|
|
146
|
+
# 7. Page Context
|
|
147
|
+
User see [T] page # URL assertion
|
|
76
148
|
```
|
|
77
149
|
|
|
78
150
|
### Table
|
|
@@ -94,12 +166,45 @@ User click [Act] in [Table] table row with {{f}} # action in row
|
|
|
94
166
|
|
|
95
167
|
### Element Types
|
|
96
168
|
|
|
97
|
-
|
|
169
|
+
| Group | Types |
|
|
170
|
+
|---|---|
|
|
171
|
+
| **Context** | `page` `dialog` `modal` `drawer` `tab` `alert` `overlay` `step` |
|
|
172
|
+
| **Input** | `field` `textarea` `search` `dropdown` `option` `checkbox` `radio` `toggle` `uploader` `slider` `date-picker` |
|
|
173
|
+
| **Trigger** | `button` `link` `icon` `menuitem` `tag` |
|
|
174
|
+
| **Data** | `table` `row` `column` `cell` `list` `item` `card` `section` |
|
|
175
|
+
| **Feedback** | `message` `header` `label` `text` `tooltip` `badge` `breadcrumb` `image` |
|
|
176
|
+
| **System** | `key` `frame` `spinner` `progressbar` |
|
|
177
|
+
|
|
178
|
+
### Auto-infer (no YAML entry needed)
|
|
179
|
+
|
|
180
|
+
| Gherkin | Playwright locator |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `[Submit] button` | `getByRole('button', { name: 'Submit' })` |
|
|
183
|
+
| `[Home] link` | `getByRole('link', { name: 'Home' })` |
|
|
184
|
+
| `[Welcome] heading` / `header` | `getByRole('heading', { name: 'Welcome' })` |
|
|
185
|
+
| `[Email] field` | `getByPlaceholder('Email')` |
|
|
186
|
+
| `[Success] text` / `message` / `label` | `getByText('Success')` |
|
|
187
|
+
| `[Terms] checkbox` | `getByRole('checkbox', { name: 'Terms' })` |
|
|
188
|
+
| `[Male] radio` | `getByRole('radio', { name: 'Male' })` |
|
|
189
|
+
| `[Global] search` | `getByRole('searchbox', { name: 'Global' })` |
|
|
190
|
+
| `[Vietnam] option` | `getByRole('option', { name: 'Vietnam' })` |
|
|
191
|
+
| `[Price] slider` | `getByRole('slider', { name: 'Price' })` |
|
|
192
|
+
| `[Notification] toggle` | `getByRole('switch', { name: 'Notification' })` |
|
|
193
|
+
| `[Profile] tab` | `getByRole('tab', { name: 'Profile' })` |
|
|
194
|
+
| `[Orders] table` | `getByRole('table', { name: 'Orders' })` |
|
|
195
|
+
| `[Products] list` | `getByRole('list', { name: 'Products' })` |
|
|
196
|
+
| `[Name] column` | `getByRole('columnheader', { name: 'Name' })` |
|
|
197
|
+
| `[Confirm] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'Confirm' })` |
|
|
98
198
|
|
|
99
199
|
## YAML Keys
|
|
100
200
|
|
|
101
201
|
`[Reference]` → **lowercase, keep Unicode**: `[Search Content]` → `search content:`, `[Thời gian]` → `thời gian:`
|
|
102
202
|
|
|
203
|
+
- Keys use **spaces** (not dots) as word separators
|
|
204
|
+
- Same label, different element types → add `--type` suffix
|
|
205
|
+
- Same label, nth occurrence → add `--N` suffix
|
|
206
|
+
- Target Name > 30 chars → shorten to 1–3 meaningful words
|
|
207
|
+
|
|
103
208
|
## Selectors (priority order)
|
|
104
209
|
|
|
105
210
|
| type | value | name | use |
|
|
@@ -120,8 +225,30 @@ Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `colu
|
|
|
120
225
|
|
|
121
226
|
| Tag | Effect |
|
|
122
227
|
|---|---|
|
|
228
|
+
| `@auto` | Standard scenario, ready for automation |
|
|
229
|
+
| `@manual` | Skip in generation |
|
|
230
|
+
| `@smoke` / `@regression` | Test suite grouping |
|
|
123
231
|
| `@auth:role` | Use auth storage state for role |
|
|
124
232
|
| `@no-auth` | Disable inherited auth |
|
|
125
|
-
| `@steps:name` | Define reusable step block |
|
|
126
|
-
| `@extend:name` | Prepend
|
|
127
|
-
|
|
233
|
+
| `@steps:name` | Define reusable step block (base scenario) |
|
|
234
|
+
| `@extend:name` | Prepend Given→When from @steps block (skip Then) |
|
|
235
|
+
|
|
236
|
+
### @extend behavior
|
|
237
|
+
|
|
238
|
+
- Tool executes **only Given→When** of `@steps` scenario (skips Then)
|
|
239
|
+
- The `Given` in `@extend` scenario is the **entry assertion** (confirms state after base steps)
|
|
240
|
+
- If `@steps` scenario fails, `@extend` scenario is **skipped**
|
|
241
|
+
- Name format: `snake_case` or `kebab-case` with module prefix: `@steps:kudos__open_modal`
|
|
242
|
+
|
|
243
|
+
## Common Syntax Errors
|
|
244
|
+
|
|
245
|
+
| Error | Wrong | Correct |
|
|
246
|
+
|---|---|---|
|
|
247
|
+
| Wrong keyword | `Given User click [T] button` | `When User click [T] button` |
|
|
248
|
+
| Wrong action for type | `When User click [T] checkbox` | `When User check [T] checkbox` |
|
|
249
|
+
| press wrong target | `When User press [Submit] button` | `When User press [Enter] key` |
|
|
250
|
+
| uncheck radio | `When User uncheck [Male] radio` | `When User check [Female] radio` |
|
|
251
|
+
| Hardcode data | `with {{admin@mail.com}}` | `with {{invalid_email}}` |
|
|
252
|
+
| Missing `is` for state | `with {{text}} hidden` | `with {{text}} is hidden` |
|
|
253
|
+
| State as value | `with {{disabled}}` | `is disabled` |
|
|
254
|
+
| Missing target type | `fill [email] with {{v}}` | `fill [email] field with {{v}}` |
|
|
@@ -168,9 +168,16 @@ Many elements don't need a YAML entry — sungen auto-infers from the Gherkin la
|
|
|
168
168
|
|---|---|
|
|
169
169
|
| `[Submit] button` | `getByRole('button', { name: 'Submit' })` |
|
|
170
170
|
| `[Home] link` | `getByRole('link', { name: 'Home' })` |
|
|
171
|
-
| `[Welcome] heading` | `getByRole('heading', { name: 'Welcome' })` |
|
|
171
|
+
| `[Welcome] heading` / `header` | `getByRole('heading', { name: 'Welcome' })` |
|
|
172
172
|
| `[Email] field` | `getByPlaceholder('Email')` |
|
|
173
|
-
| `[Success] text` | `getByText('Success')` |
|
|
173
|
+
| `[Success] text` / `message` / `label` | `getByText('Success')` |
|
|
174
|
+
| `[Terms] checkbox` | `getByRole('checkbox', { name: 'Terms' })` |
|
|
175
|
+
| `[Global] search` | `getByRole('searchbox', { name: 'Global' })` |
|
|
176
|
+
| `[Vietnam] option` | `getByRole('option', { name: 'Vietnam' })` |
|
|
177
|
+
| `[Price] slider` | `getByRole('slider', { name: 'Price' })` |
|
|
178
|
+
| `[Notification] toggle` | `getByRole('switch', { name: 'Notification' })` |
|
|
179
|
+
| `[Profile] tab` | `getByRole('tab', { name: 'Profile' })` |
|
|
180
|
+
| `[Confirm] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'Confirm' })` |
|
|
174
181
|
|
|
175
182
|
**Only add a YAML entry when auto-infer won't work:**
|
|
176
183
|
- The accessible name differs from the Gherkin label
|
|
@@ -181,19 +188,52 @@ Many elements don't need a YAML entry — sungen auto-infers from the Gherkin la
|
|
|
181
188
|
|
|
182
189
|
---
|
|
183
190
|
|
|
184
|
-
### Fix Loop on Test Failure
|
|
191
|
+
### Fix Loop on Test Failure (Batched Strategy)
|
|
185
192
|
|
|
193
|
+
Running all tests every iteration is slow. Use a batched approach to fix faster:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
1. INITIAL RUN — run ALL tests, collect full failure list
|
|
197
|
+
npx playwright test <spec> --reporter=line
|
|
198
|
+
|
|
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.
|
|
213
|
+
|
|
214
|
+
4. If still failing after 5 fix attempts → ask user about direct .spec.ts fix
|
|
186
215
|
```
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if still failing → ask user about direct .spec.ts fix
|
|
216
|
+
|
|
217
|
+
#### Building the `--grep` pattern
|
|
218
|
+
|
|
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
|
|
195
223
|
```
|
|
196
224
|
|
|
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
|
|
227
|
+
|
|
228
|
+
#### Grouping failures by root cause
|
|
229
|
+
|
|
230
|
+
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
|
|
232
|
+
- **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)
|
|
234
|
+
|
|
235
|
+
Fix the root cause first, verify with the batch, then move on.
|
|
236
|
+
|
|
197
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.
|
|
198
238
|
|
|
199
239
|
---
|
|
@@ -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.).
|