@sun-asterisk/sungen 2.1.1 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +78 -51
  2. package/dist/cli/index.js +1 -1
  3. package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +1 -1
  4. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -1
  5. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +1 -1
  6. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +0 -1
  7. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +0 -1
  8. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +0 -1
  9. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +0 -1
  10. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +0 -1
  11. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +0 -1
  12. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +0 -1
  13. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +0 -1
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +0 -1
  15. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +0 -1
  16. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +0 -1
  17. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +0 -1
  18. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +0 -1
  19. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +0 -1
  20. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +0 -1
  21. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +0 -1
  22. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +0 -1
  23. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +0 -1
  24. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +0 -1
  25. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/loading-assertion.hbs +0 -1
  26. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +0 -1
  27. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +0 -1
  28. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +0 -1
  29. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/sorted-assertion.hbs +0 -1
  30. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +0 -1
  31. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +0 -1
  32. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +0 -1
  33. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +0 -1
  34. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +0 -1
  35. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +0 -1
  36. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +0 -1
  37. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +0 -1
  38. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +0 -1
  39. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +0 -1
  40. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +0 -1
  41. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +0 -1
  42. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +0 -1
  43. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
  44. package/dist/generators/test-generator/code-generator.d.ts +4 -0
  45. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  46. package/dist/generators/test-generator/code-generator.js +19 -0
  47. package/dist/generators/test-generator/code-generator.js.map +1 -1
  48. package/dist/generators/test-generator/patterns/navigation-patterns.js +2 -2
  49. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
  50. package/dist/generators/test-generator/utils/selector-resolver.d.ts +15 -8
  51. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  52. package/dist/generators/test-generator/utils/selector-resolver.js +26 -197
  53. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  54. package/dist/orchestrator/project-initializer.d.ts +8 -0
  55. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  56. package/dist/orchestrator/project-initializer.js +68 -4
  57. package/dist/orchestrator/project-initializer.js.map +1 -1
  58. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +4 -3
  59. package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -46
  60. package/dist/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -46
  61. package/dist/orchestrator/templates/ai-instructions/claude-config.md +9 -8
  62. package/dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +29 -0
  63. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +2 -2
  64. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +206 -0
  65. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +19 -21
  66. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +256 -0
  67. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +14 -17
  68. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +16 -47
  69. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +16 -47
  70. package/dist/orchestrator/templates/ai-instructions/copilot-config.md +8 -7
  71. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +56 -0
  72. package/{src/orchestrator/templates/ai-instructions/copilot-skill-gherkin-syntax.md → dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md} +5 -5
  73. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +206 -0
  74. package/dist/orchestrator/templates/ai-instructions/{copilot-skill-selector-keys.md → github-skill-sungen-selector-keys.md} +22 -24
  75. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +257 -0
  76. package/dist/orchestrator/templates/playwright.config.d.ts.map +1 -1
  77. package/dist/orchestrator/templates/playwright.config.js +3 -1
  78. package/dist/orchestrator/templates/playwright.config.js.map +1 -1
  79. package/dist/orchestrator/templates/playwright.config.ts +3 -1
  80. package/dist/orchestrator/templates/readme.md +78 -101
  81. package/dist/orchestrator/templates/specs-base.d.ts +4 -0
  82. package/dist/orchestrator/templates/specs-base.d.ts.map +1 -0
  83. package/dist/orchestrator/templates/specs-base.js +70 -0
  84. package/dist/orchestrator/templates/specs-base.js.map +1 -0
  85. package/dist/orchestrator/templates/specs-base.ts +73 -0
  86. package/package.json +1 -1
  87. package/src/cli/index.ts +1 -1
  88. package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +1 -1
  89. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -1
  90. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +1 -1
  91. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +0 -1
  92. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +0 -1
  93. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +0 -1
  94. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +0 -1
  95. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +0 -1
  96. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +0 -1
  97. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +0 -1
  98. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +0 -1
  99. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +0 -1
  100. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +0 -1
  101. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +0 -1
  102. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +0 -1
  103. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +0 -1
  104. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +0 -1
  105. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +0 -1
  106. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +0 -1
  107. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +0 -1
  108. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +0 -1
  109. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +0 -1
  110. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/loading-assertion.hbs +0 -1
  111. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +0 -1
  112. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +0 -1
  113. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +0 -1
  114. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/sorted-assertion.hbs +0 -1
  115. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +0 -1
  116. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +0 -1
  117. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +0 -1
  118. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +0 -1
  119. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +0 -1
  120. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +0 -1
  121. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +0 -1
  122. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +0 -1
  123. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +0 -1
  124. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +0 -1
  125. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +0 -1
  126. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +0 -1
  127. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +0 -1
  128. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
  129. package/src/generators/test-generator/code-generator.ts +21 -0
  130. package/src/generators/test-generator/patterns/navigation-patterns.ts +2 -2
  131. package/src/generators/test-generator/utils/selector-resolver.ts +27 -204
  132. package/src/orchestrator/project-initializer.ts +84 -5
  133. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +4 -3
  134. package/src/orchestrator/templates/ai-instructions/claude-cmd-make-tc.md +11 -46
  135. package/src/orchestrator/templates/ai-instructions/claude-cmd-make-test.md +11 -46
  136. package/src/orchestrator/templates/ai-instructions/claude-config.md +9 -8
  137. package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +29 -0
  138. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +2 -2
  139. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +206 -0
  140. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +19 -21
  141. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +256 -0
  142. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +14 -17
  143. package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-tc.md +16 -47
  144. package/src/orchestrator/templates/ai-instructions/copilot-cmd-make-test.md +16 -47
  145. package/src/orchestrator/templates/ai-instructions/copilot-config.md +8 -7
  146. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +56 -0
  147. package/{dist/orchestrator/templates/ai-instructions/copilot-skill-gherkin-syntax.md → src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md} +5 -5
  148. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +206 -0
  149. package/src/orchestrator/templates/ai-instructions/{copilot-skill-selector-keys.md → github-skill-sungen-selector-keys.md} +22 -24
  150. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +257 -0
  151. package/src/orchestrator/templates/playwright.config.ts +3 -1
  152. package/src/orchestrator/templates/readme.md +78 -101
  153. package/src/orchestrator/templates/specs-base.ts +73 -0
  154. package/dist/orchestrator/templates/ai-instructions/copilot-skill-error-mapping.md +0 -27
  155. package/src/orchestrator/templates/ai-instructions/copilot-skill-error-mapping.md +0 -27
@@ -17,11 +17,17 @@ sungen generate → compiles Gherkin + selectors + data → Playwright .spec.ts
17
17
  │ ├── selectors/ # Element locator YAML mappings
18
18
  │ └── test-data/ # Test data YAML values
19
19
  ├── specs/
20
- ├── generated/ # Auto-generated Playwright tests
21
- │ └── .auth/ # Auth storage states
20
+ └── generated/ # Auto-generated Playwright tests
21
+ ├── .claude/
22
+ │ ├── commands/sungen/ # Claude Code slash commands
23
+ │ └── skills/ # Claude Code auto-loaded skills
22
24
  ├── .github/
23
- └── copilot-instructions.md # AI rules for GitHub Copilot
24
- └── CLAUDE.md # AI rules for Claude Code
25
+ ├── copilot-instructions.md # AI rules for GitHub Copilot
26
+ └── prompts/ # Copilot slash commands
27
+ ├── .vscode/
28
+ │ └── settings.json # Copilot auto-attach settings
29
+ ├── CLAUDE.md # AI rules for Claude Code
30
+ └── playwright.config.ts # Playwright config (timeout: 10s, workers: 2)
25
31
  ```
26
32
 
27
33
  ## Workflow
@@ -32,45 +38,50 @@ Step 1 Step 2 Step 3
32
38
  │ /add- │────────▶│ /make-tc │────────▶│ /make- │
33
39
  │ screen │ │ │ │ test │
34
40
  └──────────┘ └──────────┘ └──────────┘
35
- Scaffold Design TCs Compile &
36
- directories & generate run tests
37
- 3 files
41
+ Scaffold Pick sections Generate
42
+ directories design TCs selectors
43
+ generate → compile
44
+ .feature + → run tests
45
+ test-data → auto-fix
38
46
  ```
39
47
 
40
48
  Use AI commands (Claude Code or GitHub Copilot) to drive the workflow:
41
49
 
42
50
  ### Step 1: Add a screen
43
51
 
44
- | Claude Code | GitHub Copilot |
52
+ | Claude Code | GitHub Copilot (VS Code) |
45
53
  |---|---|
46
- | `/sungen:add-screen login /login` | `@workspace /sungen-add-screen login /login` |
54
+ | `/sungen:add-screen` | `/sungen-add-screen` |
47
55
 
48
56
  Scaffolds `qa/screens/<name>/` with empty feature, selectors, and test-data files, then asks if you want to create test cases.
49
57
 
50
58
  ### Step 2: Create test cases
51
59
 
52
- | Claude Code | GitHub Copilot |
60
+ | Claude Code | GitHub Copilot (VS Code) |
53
61
  |---|---|
54
- | `/sungen:make-tc login` | `@workspace /sungen-make-tc login` |
62
+ | `/sungen:make-tc login` | `/sungen-make-tc login` |
55
63
 
56
- AI acts as a **Senior QA Engineer**: explores the live page (via Playwright MCP) or analyzes static designs, gathers test viewpoints (UI/UX, Validation, Logic, Security), and generates the 3 files.
64
+ AI acts as a **Senior QA Engineer**:
65
+ 1. Explores the live page via Playwright MCP (asks user to log in if auth needed)
66
+ 2. Identifies screen sections → asks user which to focus on
67
+ 3. Generates **20+ scenarios per viewpoint** (UI/UX, Validation, Logic, Security) for each section
68
+ 4. Confirms test plan before generating `.feature` + `test-data.yaml`
57
69
 
58
70
  ### Step 3: Compile & run tests
59
71
 
60
- | Claude Code | GitHub Copilot |
72
+ | Claude Code | GitHub Copilot (VS Code) |
61
73
  |---|---|
62
- | `/sungen:make-test login` | `@workspace /sungen-make-test login` |
74
+ | `/sungen:make-test login` | `/sungen-make-test login` |
63
75
 
64
- AI acts as a **Senior Developer**: compiles Gherkin → Playwright `.spec.ts`, runs tests, and auto-fixes selectors/test-data on failure (up to 5 attempts).
76
+ AI acts as a **Senior Developer**:
77
+ 1. Navigates to live page, takes snapshot, verifies DOM properties
78
+ 2. Generates `selectors.yaml` from exact accessible names
79
+ 3. Compiles Gherkin → Playwright `.spec.ts`
80
+ 4. Runs tests, auto-fixes selectors on failure (up to 5 attempts)
65
81
 
66
- ### Auth setup (if needed)
82
+ ### Auth setup
67
83
 
68
- If any page requires authentication, run manually before Step 2:
69
-
70
- ```bash
71
- sungen makeauth admin --url <baseURL>
72
- # Opens browser → login manually → saves specs/.auth/admin.json
73
- ```
84
+ If any page requires authentication, the AI will ask you to **log in manually via the MCP browser** during Step 2 or Step 3. No separate auth command needed.
74
85
 
75
86
  ### Manual CLI commands
76
87
 
@@ -85,7 +96,24 @@ npx playwright test --ui # Interactive mode
85
96
 
86
97
  ---
87
98
 
88
- ## Gherkin Guide
99
+ ## VS Code Copilot Setup
100
+
101
+ Add to `.vscode/settings.json` to auto-load Gherkin syntax when editing `.feature` files:
102
+
103
+ ```json
104
+ {
105
+ "github.copilot.chat.codeGeneration.instructions": [
106
+ {
107
+ "file": ".github/prompts/sungen-gherkin-syntax.prompt.md",
108
+ "applyTo": "**/*.feature"
109
+ }
110
+ ]
111
+ }
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Gherkin Quick Reference
89
117
 
90
118
  ### Syntax
91
119
 
@@ -97,101 +125,50 @@ User <action> [<Target>] <type> with {{<Value>}}
97
125
  - `{{Value}}` → test data reference → lookup in `test-data/*.yaml`
98
126
  - `<type>` → element type: button, link, field, heading, text, etc.
99
127
 
100
- ### Pattern Shapes (17 total)
101
-
102
- #### Actions
103
-
104
- | Pattern | Example |
105
- |---|---|
106
- | Simple click | `User click [Submit] button` |
107
- | Click with data | `User click [Teammate] button with {{name}}` |
108
- | Fill field | `User fill [Email] field with {{email}}` |
109
- | Check/Uncheck | `User check [Remember me] checkbox` |
110
- | Select dropdown | `User select [Country] dropdown with {{country}}` |
111
- | Upload file | `User upload [Avatar] file with {{path}}` |
112
- | Double click | `User double click [Cell] element` |
113
- | Hover | `User hover [Info] icon` |
114
- | Clear | `User clear [Search] field` |
115
-
116
- #### Keyboard
117
-
118
- | Pattern | Example |
119
- |---|---|
120
- | Global key | `User press Escape key` |
121
- | Key on element | `User press Enter on [Search] field` |
122
-
123
- #### Navigation
124
-
125
- | Pattern | Example |
126
- |---|---|
127
- | Open page | `User is on [login] page` |
128
- | See page (URL) | `User see [dashboard] page` |
129
-
130
- #### Wait
131
-
132
- | Pattern | Example |
133
- |---|---|
134
- | Wait timeout | `User wait for 3 seconds` |
135
- | Wait element | `User wait for [Modal] dialog` |
136
- | Wait hidden | `User wait for [Loading] spinner is hidden` |
137
- | Wait with data | `User wait for [Dialog] dialog with {{title}}` |
138
-
139
- #### Assertions
140
-
141
- | Pattern | Example |
142
- |---|---|
143
- | See element | `User see [Welcome] heading` |
144
- | See with value | `User see [Title] heading with {{title}}` |
145
- | State check | `User see [Submit] button is disabled` |
146
- | State + data | `User see [Panel] dialog with {{title}} is hidden` |
147
- | Text contains | `User see [Message] text contains {{partial}}` |
148
-
149
- States: `hidden`, `visible`, `disabled`, `enabled`, `checked`, `unchecked`, `focused`, `empty`
150
-
151
- #### Table
128
+ ### Key Patterns
152
129
 
153
130
  | Pattern | Example |
154
131
  |---|---|
155
- | Row exists | `User see [Users] table has row with {{name}}` |
156
- | No row | `User see [Users] table has no row with {{name}}` |
157
- | Row count | `User see [Users] table has {{count}} rows` |
158
- | Column exists | `User see [Users] table has [Email] column` |
159
- | Cell by filter | `User see [Users] table row with {{name}} has [Status] with {{status}}` |
160
- | Cell by index | `User see [Users] table row 1 [Name] cell with {{name}}` |
161
- | Action in row | `User click [Edit] in [Users] table row with {{name}}` |
162
- | Empty table | `User see [Users] table is empty` |
163
-
164
- #### Scope
165
-
166
- | Pattern | Example |
167
- |---|---|
168
- | Scroll | `User scroll to [Footer] section` |
169
- | Frame enter | `User switch to [Payment] frame` |
170
- | Frame exit | `User switch to [main] frame` |
132
+ | Navigate | `User is on [login] page` |
133
+ | Click | `User click [Submit] button` |
134
+ | Fill | `User fill [Email] field with {{email}}` |
135
+ | Assert visible | `User see [Welcome] heading is visible` |
136
+ | Assert text | `User see [Title] heading with {{title}}` |
137
+ | Assert state | `User see [Submit] button is disabled` |
138
+ | Wait for | `User wait for [Modal] dialog is visible` |
139
+ | Table row | `User see [Users] table has row with {{name}}` |
140
+ | Table column | `User see [Users] table has [Email] column` |
141
+
142
+ States: `hidden` `visible` `disabled` `enabled` `checked` `unchecked` `focused` `empty` `loading` `selected`
171
143
 
172
144
  ### Tags
173
145
 
174
146
  | Tag | Purpose |
175
147
  |---|---|
176
148
  | `@auth:role` | Use Playwright storage state for auth |
149
+ | `@no-auth` | Disable inherited auth for this scenario |
177
150
  | `@steps:name` | Define reusable step group |
178
151
  | `@extend:name` | Inherit steps from another scenario |
179
152
  | `@manual` | Skip scenario in generation |
180
153
 
181
- ### Selector YAML
154
+ ### YAML Selector Keys
155
+
156
+ Keys are **lowercase with spaces**, Unicode preserved:
157
+ - `[Search Content]` → `search content:`
158
+ - `[Thời gian]` → `thời gian:`
159
+ - Same label, different types → `add campaign--button:` / `add campaign--text:`
160
+
161
+ ### Selector Types
182
162
 
183
163
  ```yaml
184
- submit.button:
185
- type: 'role' # testid, role, text, label, placeholder, locator, page
186
- value: 'button' # role name, testid value, CSS selector, etc.
187
- name: 'Submit' # accessible name
188
- nth: 0 # element index, 0-indexed (omit for strict mode)
189
- exact: true # exact name match (avoid "Submit" matching "Submit Form")
190
- scope: 'desktop navigation' # scope to parent landmark
191
- match: 'exact' # exact text match for getByText
164
+ search:
165
+ type: 'placeholder' # testid, role, placeholder, label, text, locator, page
166
+ value: 'Search users' # placeholder text, role name, CSS selector, etc.
167
+ name: 'Search' # accessible name (for role type)
168
+ nth: 0 # element index (for multiple matches)
192
169
  ```
193
170
 
194
- Locator priority: `data-testid` > `role+name` > `label` > `text` > `CSS`
171
+ Priority: `data-testid` > `role+name` > `placeholder` > `label` > `text` > `CSS locator`
195
172
 
196
173
  ---
197
174
 
@@ -0,0 +1,4 @@
1
+ import { expect } from '@playwright/test';
2
+ declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
3
+ export { test, expect };
4
+ //# sourceMappingURL=specs-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs-base.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAQxD,QAAA,MAAM,IAAI,6OA8DR,CAAC;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expect = exports.test = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return test_1.expect; } });
6
+ // Share one context per storageState — avoids creating multiple sessions
7
+ // that trigger server rate limiting or session invalidation
8
+ const contextCache = new Map();
9
+ const GOTO_PATCHED = Symbol('goto-patched');
10
+ const test = test_1.test.extend({
11
+ page: async ({ browser, storageState }, use) => {
12
+ if (storageState) {
13
+ const cacheKey = typeof storageState === 'string' ? storageState : JSON.stringify(storageState);
14
+ let cached = contextCache.get(cacheKey);
15
+ if (!cached) {
16
+ const context = await browser.newContext({ storageState });
17
+ const page = await context.newPage();
18
+ cached = { context, page };
19
+ contextCache.set(cacheKey, cached);
20
+ }
21
+ const page = cached.page;
22
+ // Patch goto once: skip navigation if already on the target path
23
+ if (!page[GOTO_PATCHED]) {
24
+ const originalGoto = page.goto.bind(page);
25
+ page.goto = async function (url, options) {
26
+ try {
27
+ const currentPath = new URL(page.url()).pathname;
28
+ if (currentPath === url || currentPath === url + '/') {
29
+ // Dismiss any open overlays (dropdowns, dialogs) from previous test
30
+ await page.keyboard.press('Escape').catch(() => { });
31
+ await page.locator('body').click({ position: { x: 1, y: 1 }, force: true }).catch(() => { });
32
+ // Safety check: if a fixed-position overlay (modal/dialog) is still present, full reload
33
+ // eslint-disable-next-line no-eval -- runs in browser context via Playwright
34
+ const hasOverlay = await page.evaluate(`(() => {
35
+ const el = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2);
36
+ if (!el) return false;
37
+ let current = el;
38
+ while (current && current !== document.body) {
39
+ if (getComputedStyle(current).position === 'fixed') return true;
40
+ current = current.parentElement;
41
+ }
42
+ return false;
43
+ })()`).catch(() => true);
44
+ if (hasOverlay) {
45
+ return originalGoto(url, options);
46
+ }
47
+ return null;
48
+ }
49
+ }
50
+ catch {
51
+ // page.url() might be about:blank on first run
52
+ }
53
+ return originalGoto(url, options);
54
+ };
55
+ page[GOTO_PATCHED] = true;
56
+ }
57
+ await use(page);
58
+ }
59
+ else {
60
+ // No storageState: fresh context (e.g., unauthenticated tests)
61
+ const context = await browser.newContext();
62
+ const page = await context.newPage();
63
+ await use(page);
64
+ await page.close();
65
+ await context.close();
66
+ }
67
+ },
68
+ });
69
+ exports.test = test;
70
+ //# sourceMappingURL=specs-base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs-base.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-base.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { test as base, expect } from '@playwright/test';
2
+ import type { BrowserContext, Page } from '@playwright/test';
3
+
4
+ // Share one context per storageState — avoids creating multiple sessions
5
+ // that trigger server rate limiting or session invalidation
6
+ const contextCache = new Map<string, { context: BrowserContext; page: Page }>();
7
+ const GOTO_PATCHED = Symbol('goto-patched');
8
+
9
+ const test = base.extend({
10
+ page: async ({ browser, storageState }, use) => {
11
+ if (storageState) {
12
+ const cacheKey = typeof storageState === 'string' ? storageState : JSON.stringify(storageState);
13
+
14
+ let cached = contextCache.get(cacheKey);
15
+ if (!cached) {
16
+ const context = await browser.newContext({ storageState });
17
+ const page = await context.newPage();
18
+ cached = { context, page };
19
+ contextCache.set(cacheKey, cached);
20
+ }
21
+
22
+ const page = cached.page;
23
+
24
+ // Patch goto once: skip navigation if already on the target path
25
+ if (!(page as any)[GOTO_PATCHED]) {
26
+ const originalGoto = page.goto.bind(page);
27
+ page.goto = async function (url: string, options?: any) {
28
+ try {
29
+ const currentPath = new URL(page.url()).pathname;
30
+ if (currentPath === url || currentPath === url + '/') {
31
+ // Dismiss any open overlays (dropdowns, dialogs) from previous test
32
+ await page.keyboard.press('Escape').catch(() => {});
33
+ await page.locator('body').click({ position: { x: 1, y: 1 }, force: true }).catch(() => {});
34
+
35
+ // Safety check: if a fixed-position overlay (modal/dialog) is still present, full reload
36
+ // eslint-disable-next-line no-eval -- runs in browser context via Playwright
37
+ const hasOverlay = await page.evaluate(`(() => {
38
+ const el = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2);
39
+ if (!el) return false;
40
+ let current = el;
41
+ while (current && current !== document.body) {
42
+ if (getComputedStyle(current).position === 'fixed') return true;
43
+ current = current.parentElement;
44
+ }
45
+ return false;
46
+ })()`).catch(() => true);
47
+
48
+ if (hasOverlay) {
49
+ return originalGoto(url, options);
50
+ }
51
+ return null as any;
52
+ }
53
+ } catch {
54
+ // page.url() might be about:blank on first run
55
+ }
56
+ return originalGoto(url, options);
57
+ };
58
+ (page as any)[GOTO_PATCHED] = true;
59
+ }
60
+
61
+ await use(page);
62
+ } else {
63
+ // No storageState: fresh context (e.g., unauthenticated tests)
64
+ const context = await browser.newContext();
65
+ const page = await context.newPage();
66
+ await use(page);
67
+ await page.close();
68
+ await context.close();
69
+ }
70
+ },
71
+ });
72
+
73
+ export { test, expect };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "2.1.1",
3
+ "version": "2.2.1",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/cli/index.ts CHANGED
@@ -16,7 +16,7 @@ async function main() {
16
16
  program
17
17
  .name('sungen')
18
18
  .description('Deterministic E2E Test Compiler — Gherkin + Selectors → Playwright')
19
- .version('2.1.1');
19
+ .version('2.2.1');
20
20
 
21
21
  // Global options
22
22
  program
@@ -1,4 +1,4 @@
1
- import { test, expect } from '@playwright/test';
1
+ import { test, expect } from '../base';
2
2
 
3
3
  // This file is auto-generated from Gherkin feature files
4
4
  // DO NOT EDIT MANUALLY - changes will be overwritten
@@ -1 +1 @@
1
- await {{> locator}}.fill('{{fillValue}}');
1
+ await {{> locator}}.fill('{{escapeQuotes fillValue}}');
@@ -1,3 +1,3 @@
1
1
  const editorLocator = {{> locator}};
2
2
  await editorLocator.click();
3
- await editorLocator.pressSequentially('{{fillValue}}');
3
+ await editorLocator.pressSequentially('{{escapeQuotes fillValue}}');
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toBeChecked();
@@ -1,3 +1,2 @@
1
- await page.waitForLoadState('networkidle');
2
1
  const {{columnIndexVar}} = (await page.getByRole('columnheader').allTextContents()).findIndex(h => h.includes('{{columnName}}'));
3
2
  await expect(page.getByRole('row').nth({{rowNth}}).getByRole('cell').nth({{columnIndexVar}})).toHaveText('{{dataValue}}');
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toContainText('{{expectedText}}');
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toHaveCount({{expectedCount}});
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toBeDisabled();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator-base}}.filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeDisabled();
@@ -1,4 +1,3 @@
1
- await page.waitForLoadState('networkidle');
2
1
  {{#if name}}
3
2
  await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeDisabled();
4
3
  {{else}}
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeDisabled();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toBeEmpty();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toBeEnabled();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toBeFocused();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toHaveText('{{expectedText}}');
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect(page.getByRole('dialog').getByRole('heading', { name: '{{escapeQuotes dataValue}}' })).toBeHidden();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator-base}}.filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeHidden();
@@ -1,4 +1,3 @@
1
- await page.waitForLoadState('networkidle');
2
1
  {{#if name}}
3
2
  await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeHidden();
4
3
  {{else}}
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeHidden();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toBeHidden();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect(page.getByText(/{{#if label}}{{escapeRegex label}}\s*{{else}}(?:^|\s){{/if}}{{escapeRegex dataValue}}$/)).toBeVisible();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}.getByRole('listitem')).toHaveCount({{expectedCount}});
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toHaveAttribute('aria-busy', 'true');
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toBeChecked({ checked: false });
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect(page).toHaveURL(/{{pathRegex}}/);
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toHaveAttribute('aria-selected', 'true');
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toHaveAttribute('aria-sort', '{{sortDirection}}');
@@ -1,3 +1,2 @@
1
- await page.waitForLoadState('networkidle');
2
1
  { const tableRow = {{> locator}}.getByRole('row').filter({ hasText: '{{escapeQuotes filterValue}}' });
3
2
  await expect(tableRow.getByRole('cell').filter({ hasText: '{{escapeQuotes cellValue}}' })).toBeVisible(); }
@@ -1,3 +1,2 @@
1
- await page.waitForLoadState('networkidle');
2
1
  { const tableRow = {{> locator}}.getByRole('row').nth({{rowIndex}});
3
2
  await expect(tableRow.getByRole('cell').filter({ hasText: '{{escapeQuotes cellValue}}' })).toBeVisible(); }
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}.getByRole('columnheader', { name: '{{escapeQuotes columnName}}' })).toBeVisible();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}.locator('tbody').getByRole('row')).toHaveCount(0);
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}.locator('tbody').getByRole('row')).toHaveCount({{expectedCount}});
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}.getByRole('row').filter({ hasText: '{{escapeQuotes filterValue}}' })).toBeVisible();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}.getByRole('row').filter({ hasText: '{{escapeQuotes filterValue}}' })).toHaveCount(0);
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator}}).toBeVisible();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect(page.getByRole('dialog').getByRole('heading', { name: '{{escapeQuotes dataValue}}' })).toBeVisible();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect({{> locator-base}}.filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeVisible();
@@ -1,4 +1,3 @@
1
- await page.waitForLoadState('networkidle');
2
1
  {{#if name}}
3
2
  {{#if dataValue}}
4
3
  await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeVisible();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect(page.getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeVisible();
@@ -1,2 +1 @@
1
- await page.waitForLoadState('networkidle');
2
1
  await expect(page.getByText('{{escapeQuotes selectorValue}}').filter({ hasText: /.*{{escapeRegex dataValue}}$/ }){{#if (gte nth 0)}}.nth({{nth}}){{/if}}).toBeVisible();
@@ -1 +1 @@
1
- await page.goto('{{#if baseURL}}{{baseURL}}{{/if}}{{path}}', { waitUntil: 'networkidle' });
1
+ await page.goto('{{#if baseURL}}{{baseURL}}{{/if}}{{path}}', { waitUntil: 'load' });