@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.
- package/README.md +66 -37
- package/dist/cli/commands/update.d.ts +3 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +21 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +2 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +17 -3
- 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/attribute-assertion.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +12 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +12 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.js +95 -57
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +9 -0
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +32 -0
- package/dist/generators/test-generator/patterns/index.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/patterns/table-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/table-patterns.js +8 -5
- package/dist/generators/test-generator/patterns/table-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/ai-rules-updater.d.ts +13 -0
- package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -0
- package/dist/orchestrator/ai-rules-updater.js +157 -0
- package/dist/orchestrator/ai-rules-updater.js.map +1 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +2 -27
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts +1 -0
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +70 -3
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +18 -9
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -4
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -6
- package/dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +22 -9
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +170 -24
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +118 -12
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +16 -2
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +124 -71
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -5
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +12 -4
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +11 -6
- 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 +170 -24
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +93 -12
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +16 -2
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +124 -72
- package/dist/orchestrator/templates/readme.md +13 -8
- package/package.json +1 -1
- package/src/cli/commands/update.ts +18 -0
- package/src/cli/index.ts +3 -1
- package/src/generators/gherkin-parser/index.ts +20 -3
- 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/attribute-assertion.hbs +3 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-value-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +12 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +12 -1
- package/src/generators/test-generator/patterns/assertion-patterns.ts +106 -65
- package/src/generators/test-generator/patterns/index.ts +41 -0
- package/src/generators/test-generator/patterns/interaction-patterns.ts +58 -1
- package/src/generators/test-generator/patterns/table-patterns.ts +8 -5
- package/src/generators/test-generator/utils/selector-resolver.ts +16 -0
- package/src/orchestrator/ai-rules-updater.ts +139 -0
- package/src/orchestrator/project-initializer.ts +2 -32
- package/src/orchestrator/screen-manager.ts +72 -3
- package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +18 -9
- package/src/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -4
- package/src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -6
- package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +22 -9
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +170 -24
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +118 -12
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +16 -2
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +124 -71
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -5
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +12 -4
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +11 -6
- 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 +170 -24
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +93 -12
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +16 -2
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +124 -72
- package/src/orchestrator/templates/readme.md +13 -8
- package/docs/gherkin standards/gherkin-core-standard.md +0 -428
- package/docs/gherkin standards/gherkin-core-standard.vi.md +0 -513
- package/docs/gherkin-dictionary.md +0 -1071
- 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.
|
|
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
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
###
|
|
47
|
+
### 4. Confirm
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
-
|
|
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.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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` exists → read 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
|
|
20
|
-
3.
|
|
21
|
-
4.
|
|
22
|
-
5.
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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,35 @@ description: 'Sungen Gherkin patterns, selector types, and YAML key rules. Auto-
|
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
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
|
|
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]
|
|
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
|
|
45
|
-
User press Enter on [T] field
|
|
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
|
|
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
|
-
|
|
69
|
-
User see [T]
|
|
70
|
-
User see [T]
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
User see [T]
|
|
75
|
-
User see {{v}}
|
|
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 [
|
|
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
|
|
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
|
-
|
|
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
|
|
127
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
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.).
|