@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
@@ -179,6 +179,24 @@ export class CodeGenerator {
179
179
  fs.writeFileSync(generatedTest.filePath, generatedTest.code, 'utf-8');
180
180
  }
181
181
 
182
+ /**
183
+ * Ensure specs/base.ts exists in the output directory
184
+ */
185
+ ensureBaseFile(outputDir: string): void {
186
+ const basePath = path.join(outputDir, 'base.ts');
187
+ if (fs.existsSync(basePath)) return;
188
+
189
+ const templatePath = path.join(__dirname, '..', '..', '..', 'orchestrator', 'templates', 'specs-base.ts');
190
+ if (fs.existsSync(templatePath)) {
191
+ const baseDir = path.dirname(basePath);
192
+ if (!fs.existsSync(baseDir)) {
193
+ fs.mkdirSync(baseDir, { recursive: true });
194
+ }
195
+ fs.copyFileSync(templatePath, basePath);
196
+ console.log('✓ Created: specs/base.ts');
197
+ }
198
+ }
199
+
182
200
  private async generateTestCode(feature: ParsedFeature): Promise<string> {
183
201
  // Extract feature name from source file if available, otherwise derive from feature name
184
202
  let featureName: string;
@@ -415,6 +433,9 @@ export class CodeGenerator {
415
433
  const { GherkinParser } = require('../gherkin-parser');
416
434
  const parser = new GherkinParser();
417
435
 
436
+ // Ensure specs/base.ts exists
437
+ this.ensureBaseFile(outputDir);
438
+
418
439
  // Parse all features first
419
440
  const features: ParsedFeature[] = [];
420
441
  const parseErrors: Array<{ file: string; error: any }> = [];
@@ -9,7 +9,7 @@ export const navigationPatterns: StepPattern[] = [
9
9
  {
10
10
  name: 'open-page-type',
11
11
  matcher: (step: ParsedStep) =>
12
- (step.text.includes('open') || step.text.includes('opens') || step.text.includes('is on')) &&
12
+ (step.text.includes('open') || step.text.includes('opens') || step.text.includes('is on') || step.text.includes('navigate to')) &&
13
13
  step.elementType === 'page',
14
14
  resolver: (step, context) => {
15
15
  let path = context.featurePath || '/';
@@ -64,7 +64,7 @@ export const navigationPatterns: StepPattern[] = [
64
64
  {
65
65
  name: 'navigate-to-route',
66
66
  matcher: (step: ParsedStep) =>
67
- (step.text.includes('navigates to') || step.text.includes('is on')) &&
67
+ (step.text.includes('navigate to') || step.text.includes('navigates to') || step.text.includes('is on')) &&
68
68
  !!(step.selectorRef || step.dataRef),
69
69
  resolver: (step, context) => {
70
70
  const route = step.selectorRef || step.dataRef;
@@ -3,191 +3,6 @@ import * as path from 'path';
3
3
  import yaml from 'yaml';
4
4
  import { readYaml, readYamlIfExists } from '../../../utils/yaml-io';
5
5
 
6
- /**
7
- * Remove Vietnamese diacritics (accents) from text
8
- * Example: "Thời gian" → "Thoi gian", "Địa điểm" → "Dia diem"
9
- */
10
- function removeVietnameseTones(text: string): string {
11
- const vietnameseTones: { [key: string]: string } = {
12
- 'à': 'a', 'á': 'a', 'ả': 'a', 'ã': 'a', 'ạ': 'a',
13
- 'ă': 'a', 'ằ': 'a', 'ắ': 'a', 'ẳ': 'a', 'ẵ': 'a', 'ặ': 'a',
14
- 'â': 'a', 'ầ': 'a', 'ấ': 'a', 'ẩ': 'a', 'ẫ': 'a', 'ậ': 'a',
15
- 'đ': 'd',
16
- 'è': 'e', 'é': 'e', 'ẻ': 'e', 'ẽ': 'e', 'ẹ': 'e',
17
- 'ê': 'e', 'ề': 'e', 'ế': 'e', 'ể': 'e', 'ễ': 'e', 'ệ': 'e',
18
- 'ì': 'i', 'í': 'i', 'ỉ': 'i', 'ĩ': 'i', 'ị': 'i',
19
- 'ò': 'o', 'ó': 'o', 'ỏ': 'o', 'õ': 'o', 'ọ': 'o',
20
- 'ô': 'o', 'ồ': 'o', 'ố': 'o', 'ổ': 'o', 'ỗ': 'o', 'ộ': 'o',
21
- 'ơ': 'o', 'ờ': 'o', 'ớ': 'o', 'ở': 'o', 'ỡ': 'o', 'ợ': 'o',
22
- 'ù': 'u', 'ú': 'u', 'ủ': 'u', 'ũ': 'u', 'ụ': 'u',
23
- 'ư': 'u', 'ừ': 'u', 'ứ': 'u', 'ử': 'u', 'ữ': 'u', 'ự': 'u',
24
- 'ỳ': 'y', 'ý': 'y', 'ỷ': 'y', 'ỹ': 'y', 'ỵ': 'y',
25
- // Uppercase
26
- 'À': 'A', 'Á': 'A', 'Ả': 'A', 'Ã': 'A', 'Ạ': 'A',
27
- 'Ă': 'A', 'Ằ': 'A', 'Ắ': 'A', 'Ẳ': 'A', 'Ẵ': 'A', 'Ặ': 'A',
28
- 'Â': 'A', 'Ầ': 'A', 'Ấ': 'A', 'Ẩ': 'A', 'Ẫ': 'A', 'Ậ': 'A',
29
- 'Đ': 'D',
30
- 'È': 'E', 'É': 'E', 'Ẻ': 'E', 'Ẽ': 'E', 'Ẹ': 'E',
31
- 'Ê': 'E', 'Ề': 'E', 'Ế': 'E', 'Ể': 'E', 'Ễ': 'E', 'Ệ': 'E',
32
- 'Ì': 'I', 'Í': 'I', 'Ỉ': 'I', 'Ĩ': 'I', 'Ị': 'I',
33
- 'Ò': 'O', 'Ó': 'O', 'Ỏ': 'O', 'Õ': 'O', 'Ọ': 'O',
34
- 'Ô': 'O', 'Ồ': 'O', 'Ố': 'O', 'Ổ': 'O', 'Ỗ': 'O', 'Ộ': 'O',
35
- 'Ơ': 'O', 'Ờ': 'O', 'Ớ': 'O', 'Ở': 'O', 'Ỡ': 'O', 'Ợ': 'O',
36
- 'Ù': 'U', 'Ú': 'U', 'Ủ': 'U', 'Ũ': 'U', 'Ụ': 'U',
37
- 'Ư': 'U', 'Ừ': 'U', 'Ứ': 'U', 'Ử': 'U', 'Ữ': 'U', 'Ự': 'U',
38
- 'Ỳ': 'Y', 'Ý': 'Y', 'Ỷ': 'Y', 'Ỹ': 'Y', 'Ỵ': 'Y',
39
- };
40
-
41
- return text.split('').map(char => vietnameseTones[char] || char).join('');
42
- }
43
-
44
- /**
45
- * Convert Japanese Hiragana and Katakana to Romaji
46
- * Example: "ひらがな" → "hiragana", "カタカナ" → "katakana"
47
- */
48
- function convertJapaneseToRomaji(text: string): string {
49
- // Hiragana to Romaji mapping
50
- const hiraganaMap: { [key: string]: string } = {
51
- // Basic vowels
52
- 'あ': 'a', 'い': 'i', 'う': 'u', 'え': 'e', 'お': 'o',
53
- // K-row
54
- 'か': 'ka', 'き': 'ki', 'く': 'ku', 'け': 'ke', 'こ': 'ko',
55
- 'きゃ': 'kya', 'きゅ': 'kyu', 'きょ': 'kyo',
56
- // G-row
57
- 'が': 'ga', 'ぎ': 'gi', 'ぐ': 'gu', 'げ': 'ge', 'ご': 'go',
58
- 'ぎゃ': 'gya', 'ぎゅ': 'gyu', 'ぎょ': 'gyo',
59
- // S-row
60
- 'さ': 'sa', 'し': 'shi', 'す': 'su', 'せ': 'se', 'そ': 'so',
61
- 'しゃ': 'sha', 'しゅ': 'shu', 'しょ': 'sho',
62
- // Z-row
63
- 'ざ': 'za', 'じ': 'ji', 'ず': 'zu', 'ぜ': 'ze', 'ぞ': 'zo',
64
- 'じゃ': 'ja', 'じゅ': 'ju', 'じょ': 'jo',
65
- // T-row
66
- 'た': 'ta', 'ち': 'chi', 'つ': 'tsu', 'て': 'te', 'と': 'to',
67
- 'ちゃ': 'cha', 'ちゅ': 'chu', 'ちょ': 'cho',
68
- // D-row
69
- 'だ': 'da', 'ぢ': 'ji', 'づ': 'zu', 'で': 'de', 'ど': 'do',
70
- // N-row
71
- 'な': 'na', 'に': 'ni', 'ぬ': 'nu', 'ね': 'ne', 'の': 'no',
72
- 'にゃ': 'nya', 'にゅ': 'nyu', 'にょ': 'nyo',
73
- // H-row
74
- 'は': 'ha', 'ひ': 'hi', 'ふ': 'fu', 'へ': 'he', 'ほ': 'ho',
75
- 'ひゃ': 'hya', 'ひゅ': 'hyu', 'ひょ': 'hyo',
76
- // B-row
77
- 'ば': 'ba', 'び': 'bi', 'ぶ': 'bu', 'べ': 'be', 'ぼ': 'bo',
78
- 'びゃ': 'bya', 'びゅ': 'byu', 'びょ': 'byo',
79
- // P-row
80
- 'ぱ': 'pa', 'ぴ': 'pi', 'ぷ': 'pu', 'ぺ': 'pe', 'ぽ': 'po',
81
- 'ぴゃ': 'pya', 'ぴゅ': 'pyu', 'ぴょ': 'pyo',
82
- // M-row
83
- 'ま': 'ma', 'み': 'mi', 'む': 'mu', 'め': 'me', 'も': 'mo',
84
- 'みゃ': 'mya', 'みゅ': 'myu', 'みょ': 'myo',
85
- // Y-row
86
- 'や': 'ya', 'ゆ': 'yu', 'よ': 'yo',
87
- // R-row
88
- 'ら': 'ra', 'り': 'ri', 'る': 'ru', 'れ': 're', 'ろ': 'ro',
89
- 'りゃ': 'rya', 'りゅ': 'ryu', 'りょ': 'ryo',
90
- // W-row
91
- 'わ': 'wa', 'ゐ': 'wi', 'ゑ': 'we', 'を': 'wo', 'ん': 'n',
92
- // Small tsu (doubles next consonant)
93
- 'っ': '',
94
- };
95
-
96
- // Katakana to Romaji mapping
97
- const katakanaMap: { [key: string]: string } = {
98
- // Basic vowels
99
- 'ア': 'a', 'イ': 'i', 'ウ': 'u', 'エ': 'e', 'オ': 'o',
100
- // K-row
101
- 'カ': 'ka', 'キ': 'ki', 'ク': 'ku', 'ケ': 'ke', 'コ': 'ko',
102
- 'キャ': 'kya', 'キュ': 'kyu', 'キョ': 'kyo',
103
- // G-row
104
- 'ガ': 'ga', 'ギ': 'gi', 'グ': 'gu', 'ゲ': 'ge', 'ゴ': 'go',
105
- 'ギャ': 'gya', 'ギュ': 'gyu', 'ギョ': 'gyo',
106
- // S-row
107
- 'サ': 'sa', 'シ': 'shi', 'ス': 'su', 'セ': 'se', 'ソ': 'so',
108
- 'シャ': 'sha', 'シュ': 'shu', 'ショ': 'sho',
109
- // Z-row
110
- 'ザ': 'za', 'ジ': 'ji', 'ズ': 'zu', 'ゼ': 'ze', 'ゾ': 'zo',
111
- 'ジャ': 'ja', 'ジュ': 'ju', 'ジョ': 'jo',
112
- // T-row
113
- 'タ': 'ta', 'チ': 'chi', 'ツ': 'tsu', 'テ': 'te', 'ト': 'to',
114
- 'チャ': 'cha', 'チュ': 'chu', 'チョ': 'cho',
115
- // D-row
116
- 'ダ': 'da', 'ヂ': 'ji', 'ヅ': 'zu', 'デ': 'de', 'ド': 'do',
117
- // N-row
118
- 'ナ': 'na', 'ニ': 'ni', 'ヌ': 'nu', 'ネ': 'ne', 'ノ': 'no',
119
- 'ニャ': 'nya', 'ニュ': 'nyu', 'ニョ': 'nyo',
120
- // H-row
121
- 'ハ': 'ha', 'ヒ': 'hi', 'フ': 'fu', 'ヘ': 'he', 'ホ': 'ho',
122
- 'ヒャ': 'hya', 'ヒュ': 'hyu', 'ヒョ': 'hyo',
123
- // B-row
124
- 'バ': 'ba', 'ビ': 'bi', 'ブ': 'bu', 'ベ': 'be', 'ボ': 'bo',
125
- 'ビャ': 'bya', 'ビュ': 'byu', 'ビョ': 'byo',
126
- // P-row
127
- 'パ': 'pa', 'ピ': 'pi', 'プ': 'pu', 'ペ': 'pe', 'ポ': 'po',
128
- 'ピャ': 'pya', 'ピュ': 'pyu', 'ピョ': 'pyo',
129
- // M-row
130
- 'マ': 'ma', 'ミ': 'mi', 'ム': 'mu', 'メ': 'me', 'モ': 'mo',
131
- 'ミャ': 'mya', 'ミュ': 'myu', 'ミョ': 'myo',
132
- // Y-row
133
- 'ヤ': 'ya', 'ユ': 'yu', 'ヨ': 'yo',
134
- // R-row
135
- 'ラ': 'ra', 'リ': 'ri', 'ル': 'ru', 'レ': 're', 'ロ': 'ro',
136
- 'リャ': 'rya', 'リュ': 'ryu', 'リョ': 'ryo',
137
- // W-row
138
- 'ワ': 'wa', 'ヰ': 'wi', 'ヱ': 'we', 'ヲ': 'wo', 'ン': 'n',
139
- // Small tsu (doubles next consonant)
140
- 'ッ': '',
141
- // Extended katakana for foreign words
142
- 'ヴ': 'vu',
143
- 'ファ': 'fa', 'フィ': 'fi', 'フェ': 'fe', 'フォ': 'fo',
144
- 'ウィ': 'wi', 'ウェ': 'we', 'ウォ': 'wo',
145
- 'ティ': 'ti', 'ディ': 'di',
146
- 'トゥ': 'tu', 'ドゥ': 'du',
147
- };
148
-
149
- const allMaps = { ...hiraganaMap, ...katakanaMap };
150
-
151
- let result = '';
152
- let i = 0;
153
-
154
- while (i < text.length) {
155
- // Try to match 2-character combinations first (like きゃ, シャ)
156
- if (i < text.length - 1) {
157
- const twoChar = text.substring(i, i + 2);
158
- if (allMaps[twoChar]) {
159
- result += allMaps[twoChar];
160
- i += 2;
161
- continue;
162
- }
163
- }
164
-
165
- // Then try single character
166
- const oneChar = text[i];
167
- if (allMaps[oneChar]) {
168
- // Handle small tsu (っ/ッ) - double next consonant
169
- if (oneChar === 'っ' || oneChar === 'ッ') {
170
- if (i < text.length - 1) {
171
- const nextChar = text[i + 1];
172
- const nextRomaji = allMaps[nextChar];
173
- if (nextRomaji && nextRomaji.length > 0) {
174
- result += nextRomaji[0]; // Add first letter of next romaji
175
- }
176
- }
177
- } else {
178
- result += allMaps[oneChar];
179
- }
180
- i++;
181
- } else {
182
- // Not Japanese character, keep as is
183
- result += oneChar;
184
- i++;
185
- }
186
- }
187
-
188
- return result;
189
- }
190
-
191
6
  import { SelectorType } from '../../../utils/selector-types';
192
7
 
193
8
  // Structured selector format v2
@@ -288,23 +103,31 @@ export class SelectorResolver {
288
103
  }
289
104
 
290
105
  /**
291
- * Generate selector key from natural language label
292
- * "Email Address" "email.address"
293
- * "Submit Button" → "submit.button"
294
- * "User's Profile" → "users.profile"
295
- * "Thời gian" → "thoi.gian"
296
- * "Địa điểm" → "dia.diem"
297
- * "ログイン" → "roguin"
298
- * "パスワード" → "pasuwaado"
106
+ * Generate selector key from natural language label using Unicode NFC normalization.
107
+ * Preserves all Unicode characters (Vietnamese, Japanese, etc.) as-is.
108
+ *
109
+ * "Email Address" → "email address"
110
+ * "Submit Button" → "submit button"
111
+ * "User's Profile" → "user's profile"
112
+ * "Giới thiệu" → "giới thiệu"
113
+ * "Thời gian" → "thời gian"
114
+ * "ログイン" → "ログイン"
115
+ * "書類一覧" → "書類一覧"
299
116
  */
300
117
  static generateKey(label: string): string {
301
- return convertJapaneseToRomaji(removeVietnameseTones(label)) // Convert Japanese and Vietnamese first
302
- .toLowerCase()
303
- .replace(/['\u2019]s/g, 's') // Apostrophe s to just s ("User's" → "users")
304
- .replace(/['\u2019]/g, '') // Remove remaining apostrophes
305
- .replace(/[^a-z0-9\s]/g, '') // Remove special chars except spaces
306
- .trim()
307
- .replace(/\s+/g, '.'); // Spaces to dots
118
+ return label.normalize('NFC').toLowerCase().trim().replace(/\s+/g, ' ');
119
+ }
120
+
121
+ /**
122
+ * Normalize all keys in a SelectorFile through generateKey()
123
+ * so that YAML keys are case-insensitive and Unicode-normalized on lookup.
124
+ */
125
+ private static normalizeKeys(file: SelectorFile): SelectorFile {
126
+ const normalized: SelectorFile = {};
127
+ for (const [key, value] of Object.entries(file)) {
128
+ normalized[SelectorResolver.generateKey(key)] = value;
129
+ }
130
+ return normalized;
308
131
  }
309
132
 
310
133
  /**
@@ -742,7 +565,7 @@ export class SelectorResolver {
742
565
  if (basePath) {
743
566
  const baseSelectors = readYamlIfExists<SelectorFile>(basePath);
744
567
  if (baseSelectors) {
745
- Object.assign(selectors, baseSelectors);
568
+ Object.assign(selectors, SelectorResolver.normalizeKeys(baseSelectors));
746
569
  }
747
570
  }
748
571
 
@@ -751,7 +574,7 @@ export class SelectorResolver {
751
574
  if (baseOverridePath) {
752
575
  const overrideSelectors = readYamlIfExists<SelectorFile>(baseOverridePath);
753
576
  if (overrideSelectors) {
754
- Object.assign(selectors, overrideSelectors);
577
+ Object.assign(selectors, SelectorResolver.normalizeKeys(overrideSelectors));
755
578
  }
756
579
  }
757
580
  }
@@ -769,7 +592,7 @@ export class SelectorResolver {
769
592
  } else {
770
593
  const featureSelectors = readYamlIfExists<SelectorFile>(featurePath);
771
594
  if (featureSelectors) {
772
- Object.assign(selectors, featureSelectors);
595
+ Object.assign(selectors, SelectorResolver.normalizeKeys(featureSelectors));
773
596
  }
774
597
  }
775
598
 
@@ -778,7 +601,7 @@ export class SelectorResolver {
778
601
  if (featureOverridePath) {
779
602
  const overrideSelectors = readYamlIfExists<SelectorFile>(featureOverridePath);
780
603
  if (overrideSelectors) {
781
- Object.assign(selectors, overrideSelectors);
604
+ Object.assign(selectors, SelectorResolver.normalizeKeys(overrideSelectors));
782
605
  }
783
606
  }
784
607
 
@@ -44,6 +44,9 @@ export class ProjectInitializer {
44
44
  // Create playwright config if doesn't exist
45
45
  this.createPlaywrightConfig();
46
46
 
47
+ // Create specs/base.ts for shared context
48
+ this.createSpecsBase();
49
+
47
50
  // Create/update .gitignore
48
51
  this.updateGitignore();
49
52
 
@@ -53,6 +56,9 @@ export class ProjectInitializer {
53
56
  // Create AI rules files
54
57
  this.createAIRules();
55
58
 
59
+ // Create VS Code settings for Copilot auto-attach
60
+ this.createVSCodeSettings();
61
+
56
62
  // Display summary
57
63
  this.displaySummary(normalizedProjectName);
58
64
  }
@@ -159,6 +165,54 @@ export class ProjectInitializer {
159
165
  this.createdItems.push('README.md');
160
166
  }
161
167
 
168
+ /**
169
+ * Create VS Code settings for Copilot auto-attach
170
+ */
171
+ private createVSCodeSettings(): void {
172
+ const settingsPath = path.join(this.cwd, '.vscode', 'settings.json');
173
+
174
+ if (fs.existsSync(settingsPath)) {
175
+ this.skippedItems.push('.vscode/settings.json');
176
+ return;
177
+ }
178
+
179
+ const settingsDir = path.dirname(settingsPath);
180
+ if (!fs.existsSync(settingsDir)) {
181
+ fs.mkdirSync(settingsDir, { recursive: true });
182
+ }
183
+
184
+ const settings = {
185
+ 'github.copilot.chat.agent.runTasks': true,
186
+ 'chat.tools.terminal.autoApprove': {
187
+ sungen: true,
188
+ 'npx playwright': true,
189
+ },
190
+ };
191
+
192
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
193
+ this.createdItems.push('.vscode/settings.json');
194
+
195
+ // Create MCP config for Playwright browser tools
196
+ const mcpPath = path.join(this.cwd, '.vscode', 'mcp.json');
197
+
198
+ if (fs.existsSync(mcpPath)) {
199
+ this.skippedItems.push('.vscode/mcp.json');
200
+ return;
201
+ }
202
+
203
+ const mcpConfig = {
204
+ servers: {
205
+ playwright: {
206
+ command: 'npx',
207
+ args: ['@playwright/mcp@latest'],
208
+ },
209
+ },
210
+ };
211
+
212
+ fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + '\n', 'utf-8');
213
+ this.createdItems.push('.vscode/mcp.json');
214
+ }
215
+
162
216
  /**
163
217
  * Display initialization summary
164
218
  */
@@ -216,11 +270,15 @@ export class ProjectInitializer {
216
270
  ['claude-skill-gherkin-syntax.md', '.claude/skills/sungen-gherkin-syntax/SKILL.md'],
217
271
  ['claude-skill-selector-keys.md', '.claude/skills/sungen-selector-keys/SKILL.md'],
218
272
  ['claude-skill-error-mapping.md', '.claude/skills/sungen-error-mapping/SKILL.md'],
219
-
220
- // Skills — GitHub Copilot
221
- ['copilot-skill-gherkin-syntax.md', '.github/prompts/sungen-gherkin-syntax.prompt.md'],
222
- ['copilot-skill-selector-keys.md', '.github/prompts/sungen-selector-keys.prompt.md'],
223
- ['copilot-skill-error-mapping.md', '.github/prompts/sungen-error-mapping.prompt.md'],
273
+ ['claude-skill-tc-generation.md', '.claude/skills/sungen-tc-generation/SKILL.md'],
274
+ ['claude-skill-selector-fix.md', '.claude/skills/sungen-selector-fix/SKILL.md'],
275
+
276
+ // Skills — GitHub Copilot (separate copies with Copilot-friendly descriptions)
277
+ ['github-skill-sungen-gherkin-syntax.md', '.github/skills/sungen-gherkin-syntax/SKILL.md'],
278
+ ['github-skill-sungen-selector-keys.md', '.github/skills/sungen-selector-keys/SKILL.md'],
279
+ ['github-skill-sungen-error-mapping.md', '.github/skills/sungen-error-mapping/SKILL.md'],
280
+ ['github-skill-sungen-tc-generation.md', '.github/skills/sungen-tc-generation/SKILL.md'],
281
+ ['github-skill-sungen-selector-fix.md', '.github/skills/sungen-selector-fix/SKILL.md'],
224
282
  ];
225
283
 
226
284
  for (const [templateFile, outputRelPath] of fileMapping) {
@@ -244,6 +302,27 @@ export class ProjectInitializer {
244
302
 
245
303
  }
246
304
 
305
+ /**
306
+ * Create specs/base.ts for shared browser context
307
+ */
308
+ private createSpecsBase(): void {
309
+ const basePath = path.join(this.cwd, 'specs', 'generated', 'base.ts');
310
+
311
+ if (fs.existsSync(basePath)) {
312
+ this.skippedItems.push('specs/generated/base.ts');
313
+ return;
314
+ }
315
+
316
+ const baseDir = path.dirname(basePath);
317
+ if (!fs.existsSync(baseDir)) {
318
+ fs.mkdirSync(baseDir, { recursive: true });
319
+ }
320
+
321
+ const content = this.readTemplate('specs-base.ts');
322
+ fs.writeFileSync(basePath, content, 'utf-8');
323
+ this.createdItems.push('specs/generated/base.ts');
324
+ }
325
+
247
326
  /**
248
327
  * Read a template file from the templates directory
249
328
  */
@@ -2,7 +2,7 @@
2
2
  name: add-screen
3
3
  description: 'Add a new Sungen screen — scaffolds directories and delegates to make-tc for test case creation'
4
4
  argument-hint: [screen-name] [url-path]
5
- allowed-tools: Read, Grep, Bash, Glob
5
+ allowed-tools: Read, Grep, Bash, Glob, AskUserQuestion
6
6
  ---
7
7
 
8
8
  You are adding a new Sungen screen for test generation.
@@ -37,6 +37,7 @@ If yes, delegate to `/sungen:make-tc <screen>` to handle:
37
37
  ### 3. Confirm
38
38
 
39
39
  Tell the user what was created and next steps:
40
- - If the page requires authentication before exploring via browser, read `baseURL` from `playwright.config.ts` and tell the user to manually run: `sungen makeauth <role> --url <baseURL>` (e.g., `sungen makeauth admin --url http://localhost:3000`). **Do NOT execute this command yourself.**
40
+ - If the page requires authentication, the user will be asked to log in via the MCP browser during `/sungen:make-tc`
41
41
  - Edit the generated files as needed
42
- - Run `sungen generate --screen <screen>` to compile to Playwright `.spec.ts`
42
+ - Run `/sungen:make-tc <screen>` to create test cases
43
+ - Run `/sungen:make-test <screen>` to generate selectors, compile, and run tests
@@ -1,60 +1,25 @@
1
1
  ---
2
2
  name: make-tc
3
- description: 'Create test cases for a Sungen screen — gathers viewpoints, explores live page or static designs, generates feature/selectors/test-data files'
3
+ description: 'Create or update test cases for a Sungen screen — generates feature + test-data files (20+ scenarios per viewpoint)'
4
4
  argument-hint: [screen-name]
5
- allowed-tools: Read, Grep, Bash, Glob
5
+ allowed-tools: Read, Grep, Bash, Glob, AskUserQuestion
6
6
  ---
7
7
 
8
8
  ## Role
9
9
 
10
- You are a **Senior QA Engineer** specialized in test case design. You structure test cases by viewpoint categories and translate UI into Gherkin test cases following the `sungen-gherkin-syntax` and `sungen-selector-keys` skills.
10
+ You are a **Senior QA Engineer** specialized in exhaustive test case design. You structure test cases by viewpoint categories and translate UI into comprehensive Gherkin test cases following the `sungen-gherkin-syntax` and `sungen-tc-generation` skills. Your focus is on **Gherkin scenarios and test data only** — selectors are handled later during `/sungen:make-test`.
11
11
 
12
12
  ## Parameters
13
13
 
14
- Parse from `$ARGUMENTS`:
15
- - **screen** — screen name (e.g., `login`, `dashboard`)
16
-
17
- If missing, ask the user.
14
+ Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
18
15
 
19
16
  ## Steps
20
17
 
21
- ### 1. Verify screen exists
22
-
23
- Check `qa/screens/<screen>/` exists. If not, tell user to run `/sungen:add-screen` first.
24
-
25
- ### 2. Determine source mode
26
-
27
- Ask: "Do you have a **live page** or **static designs** (screenshots, Figma, images)?"
28
-
29
- **Live page →** explore via browser (see CLAUDE.md for Playwright MCP rules). If auth needed, check `specs/.auth/<role>.json` exists — if not, read `baseURL` from `playwright.config.ts` and tell the user to manually run: `sungen makeauth <role> --url <baseURL>` (e.g., `sungen makeauth admin --url http://localhost:3000`). **Do NOT execute `sungen makeauth` yourself.** Wait for the user to confirm auth is ready before proceeding.
30
-
31
- **Static designs →** ask user to provide screenshot paths, Figma exports, or UI descriptions. Note: selectors will be best-guess until live page is available.
32
-
33
- Present discovered elements to user, then proceed.
34
-
35
- ### 3. Gather test viewpoints
36
-
37
- | VP Category | Description |
38
- |---|---|
39
- | **UI/UX** | Default screen appearance, static elements, default states |
40
- | **Validation** | Field/form validation, error messages |
41
- | **Logic** | Business logic, happy paths, redirects |
42
- | **Security** | Auth guards, permission checks |
43
-
44
- For each viewpoint, gather: **UI Target**, **Test Viewpoint**, **Expected Result**.
45
-
46
- User can provide all at once as a table:
47
- ```
48
- | VP Category | UI Target | Test Viewpoint | Expected Result |
49
- ```
50
-
51
- ### 4. Generate files
52
-
53
- Generate the 3 files following `sungen-gherkin-syntax` and `sungen-selector-keys` skill rules:
54
- - `qa/screens/<screen>/features/<screen>.feature` — one Scenario per viewpoint
55
- - `qa/screens/<screen>/selectors/<screen>.yaml`
56
- - `qa/screens/<screen>/test-data/<screen>.yaml`
57
-
58
- ### 5. Confirm
18
+ 1. Verify `qa/screens/<screen>/` exists. If not → `/sungen:add-screen` first.
19
+ 2. Check if `.feature` file already has scenarios. If yes → use `AskUserQuestion` to ask the update mode (see `sungen-tc-generation` skill for details). If no → fresh creation.
20
+ 3. Use `AskUserQuestion` to ask: **Live page** (explore via Playwright MCP) or **Static designs** (screenshots, Figma)? Explore accordingly (see CLAUDE.md for MCP rules).
21
+ 4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format.
22
+ 5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills.
23
+ 6. Show summary → next: `/sungen:make-test <screen>`
59
24
 
60
- Show what was generated. Next: `sungen generate --screen <screen>`
25
+ **No selectors.yaml** selectors are generated during `/sungen:make-test`.
@@ -1,59 +1,24 @@
1
1
  ---
2
2
  name: make-test
3
- description: 'Compile and run Playwright tests for a Sungen screen — auto-fixes selectors on failure, falls back to AI .spec.ts fix after 5 attempts'
3
+ description: 'Generate selectors, compile, and run Playwright tests — auto-fixes selectors on failure'
4
4
  argument-hint: [screen-name]
5
- allowed-tools: Read, Grep, Bash, Glob, Edit
5
+ allowed-tools: Read, Grep, Bash, Glob, Edit, AskUserQuestion
6
6
  ---
7
7
 
8
8
  ## Role
9
9
 
10
- You are a **Senior Developer** specialized in Playwright test debugging. You diagnose test failures, fix selectors/test-data, and patch `.spec.ts` when needed. Use the `sungen-error-mapping` and `sungen-selector-keys` skills for error diagnosis and key fixes.
10
+ You are a **Senior Developer** specialized in Playwright test debugging. You generate selectors from live pages, diagnose test failures, and fix selectors/test-data using the `sungen-selector-fix`, `sungen-selector-keys`, and `sungen-error-mapping` skills.
11
11
 
12
12
  ## Parameters
13
13
 
14
- Parse from `$ARGUMENTS`:
15
- - **screen** — screen name (e.g., `login`, `dashboard`)
16
-
17
- If missing, ask the user.
14
+ Parse **screen** from `$ARGUMENTS`. If missing, ask the user.
18
15
 
19
16
  ## Steps
20
17
 
21
- ### 1. Verify screen exists
22
-
23
- Check `qa/screens/<screen>/` has all 3 source files. If not, tell user to run `/sungen:add-screen` and `/sungen:make-tc` first.
24
-
25
- ### 2. Compile
26
-
27
- ```bash
28
- sungen generate --screen <screen>
29
- ```
30
-
31
- If fails, fix source files per `sungen-error-mapping` skill and retry.
32
-
33
- ### 3. Run tests
34
-
35
- ```bash
36
- npx playwright test specs/screens/<screen>/<screen>.spec.ts
37
- ```
38
-
39
- If pass → Step 5. If fail → Step 4.
40
-
41
- ### 4. Fix and retry (max 5 attempts)
42
-
43
- Per attempt:
44
- 1. Read Playwright error output
45
- 2. Map error to fix using `sungen-error-mapping` skill
46
- 3. Fix `selectors.yaml` or `test-data.yaml`
47
- 4. Recompile: `sungen generate --screen <screen>`
48
- 5. Retest
49
-
50
- ### 5. Fallback — AI fix .spec.ts
51
-
52
- After 5 failed attempts, ask user:
53
- > Tests still failing. Would you like me to directly fix the `.spec.ts` file?
54
-
55
- If yes: read `.spec.ts`, fix locators/assertions, mark with `// AI-fixed: <reason>`
56
-
57
- ### 6. Confirm
58
-
59
- Show: pass/fail, attempt count, files changed.
18
+ 1. Verify `qa/screens/<screen>/` has `.feature` + `test-data.yaml`. If not → `/sungen:make-tc` first.
19
+ 2. Generate `selectors.yaml` from live page using `sungen-selector-fix` and `sungen-selector-keys` skills.
20
+ 3. Compile: `sungen generate --screen <screen>`
21
+ 4. Run: `npx playwright test specs/screens/<screen>/<screen>.spec.ts`
22
+ 5. If fail → fix selectors/test-data per `sungen-selector-fix` + `sungen-error-mapping` skills, retry (max 5).
23
+ 6. After 5 fails → ask user about direct `.spec.ts` fix.
24
+ 7. Show: pass/fail, attempt count, files changed.
@@ -12,7 +12,7 @@ Given a login page, here are the 3 files you generate:
12
12
 
13
13
  **qa/screens/login/features/login.feature**
14
14
  ```gherkin
15
- @auth:admin
15
+ @no-auth
16
16
  Feature: Login Screen
17
17
 
18
18
  Scenario: Successful login
@@ -73,12 +73,14 @@ When exploring a page to generate test files:
73
73
  Only use: `browser_navigate`, `browser_snapshot`, `browser_click`, `browser_fill_form`, `browser_evaluate`.
74
74
 
75
75
  To browse authenticated pages via MCP:
76
- 1. Check if `specs/.auth/<role>.json` exists
77
- 2. If not, read `baseURL` from `playwright.config.ts` and tell the user to manually run: `sungen makeauth <role> --url <baseURL>`. **Do NOT execute `sungen makeauth` yourself.** Wait for user confirmation.
78
- 3. Read `specs/.auth/<role>.json`
79
- 4. `browser_navigate` to the page
80
- 5. `browser_evaluate` to inject localStorage and cookies from the JSON
81
- 6. `browser_navigate` again to reload with auth
76
+ 1. `browser_navigate` to `baseURL`
77
+ 2. If redirected to login, ask the user to log in manually:
78
+ > "This page requires login. Please log in using the browser. Confirm when you're done."
79
+ 3. Once confirmed, `browser_navigate` to the target page and take `browser_snapshot`
80
+
81
+ **Never use `sungen makeauth`.** Always let the user log in manually via the MCP browser.
82
+ **NEVER use `browser_evaluate` to inject cookies or localStorage.** It causes size limit issues and server 500 errors.
83
+ **Minimize navigations** — take one snapshot per page, do not navigate repeatedly.
82
84
 
83
85
  ## Commands
84
86
 
@@ -86,5 +88,4 @@ To browse authenticated pages via MCP:
86
88
  sungen add --screen <name> --path <url-path> # Create screen
87
89
  sungen generate --screen <name> # Compile to .spec.ts
88
90
  sungen generate --all # Compile all
89
- sungen makeauth <role> # Capture auth state
90
91
  ```