@sun-asterisk/sungen 1.0.24 → 2.0.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 (378) hide show
  1. package/README.md +198 -74
  2. package/dist/cli/commands/add.d.ts.map +1 -1
  3. package/dist/cli/commands/add.js +5 -3
  4. package/dist/cli/commands/add.js.map +1 -1
  5. package/dist/cli/commands/generate.d.ts.map +1 -1
  6. package/dist/cli/commands/generate.js +110 -35
  7. package/dist/cli/commands/generate.js.map +1 -1
  8. package/dist/cli/index.d.ts +2 -2
  9. package/dist/cli/index.js +5 -16
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/frame-enter-action.hbs +1 -0
  12. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/frame-exit-action.hbs +1 -0
  13. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/keyboard-global-action.hbs +1 -0
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/scroll-action.hbs +1 -0
  15. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +2 -0
  16. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +3 -0
  17. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +3 -0
  18. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +2 -0
  19. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +2 -0
  20. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +2 -0
  21. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +2 -0
  22. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +2 -0
  23. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
  24. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +11 -2
  25. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +11 -2
  26. package/dist/generators/test-generator/code-generator.d.ts +0 -1
  27. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  28. package/dist/generators/test-generator/code-generator.js +10 -47
  29. package/dist/generators/test-generator/code-generator.js.map +1 -1
  30. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  31. package/dist/generators/test-generator/patterns/assertion-patterns.js +2 -0
  32. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  33. package/dist/generators/test-generator/patterns/index.d.ts +4 -1
  34. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  35. package/dist/generators/test-generator/patterns/index.js +17 -5
  36. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  37. package/dist/generators/test-generator/patterns/interaction-patterns.js +1 -1
  38. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
  39. package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts +7 -0
  40. package/dist/generators/test-generator/patterns/keyboard-patterns.d.ts.map +1 -0
  41. package/dist/generators/test-generator/patterns/keyboard-patterns.js +47 -0
  42. package/dist/generators/test-generator/patterns/keyboard-patterns.js.map +1 -0
  43. package/dist/generators/test-generator/patterns/scope-patterns.d.ts +7 -0
  44. package/dist/generators/test-generator/patterns/scope-patterns.d.ts.map +1 -0
  45. package/dist/generators/test-generator/patterns/scope-patterns.js +36 -0
  46. package/dist/generators/test-generator/patterns/scope-patterns.js.map +1 -0
  47. package/dist/generators/test-generator/patterns/scroll-patterns.d.ts +7 -0
  48. package/dist/generators/test-generator/patterns/scroll-patterns.d.ts.map +1 -0
  49. package/dist/generators/test-generator/patterns/scroll-patterns.js +25 -0
  50. package/dist/generators/test-generator/patterns/scroll-patterns.js.map +1 -0
  51. package/dist/generators/test-generator/patterns/table-patterns.d.ts +7 -0
  52. package/dist/generators/test-generator/patterns/table-patterns.d.ts.map +1 -0
  53. package/dist/generators/test-generator/patterns/table-patterns.js +192 -0
  54. package/dist/generators/test-generator/patterns/table-patterns.js.map +1 -0
  55. package/dist/generators/test-generator/step-mapper.d.ts +5 -3
  56. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  57. package/dist/generators/test-generator/step-mapper.js +38 -27
  58. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  59. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  60. package/dist/generators/test-generator/template-engine.js +4 -1
  61. package/dist/generators/test-generator/template-engine.js.map +1 -1
  62. package/dist/generators/test-generator/types.d.ts +7 -24
  63. package/dist/generators/test-generator/types.d.ts.map +1 -1
  64. package/dist/generators/test-generator/types.js +2 -101
  65. package/dist/generators/test-generator/types.js.map +1 -1
  66. package/dist/generators/test-generator/utils/selector-resolver.d.ts +14 -0
  67. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  68. package/dist/generators/test-generator/utils/selector-resolver.js +37 -11
  69. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  70. package/dist/orchestrator/project-initializer.d.ts +12 -0
  71. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  72. package/dist/orchestrator/project-initializer.js +72 -160
  73. package/dist/orchestrator/project-initializer.js.map +1 -1
  74. package/dist/orchestrator/screen-manager.d.ts +1 -32
  75. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  76. package/dist/orchestrator/screen-manager.js +55 -216
  77. package/dist/orchestrator/screen-manager.js.map +1 -1
  78. package/dist/orchestrator/templates/ai-rules.md +189 -0
  79. package/dist/orchestrator/templates/gitignore +16 -0
  80. package/dist/orchestrator/templates/playwright.config.d.ts +10 -0
  81. package/dist/orchestrator/templates/playwright.config.d.ts.map +1 -0
  82. package/dist/orchestrator/templates/playwright.config.js +77 -0
  83. package/dist/orchestrator/templates/playwright.config.js.map +1 -0
  84. package/dist/orchestrator/templates/playwright.config.ts +80 -0
  85. package/dist/orchestrator/templates/readme.md +197 -0
  86. package/dist/utils/selector-types.d.ts +1 -1
  87. package/dist/utils/selector-types.d.ts.map +1 -1
  88. package/dist/utils/selector-types.js +3 -0
  89. package/dist/utils/selector-types.js.map +1 -1
  90. package/docs/gherkin standards/gherkin-core-standard.md +377 -0
  91. package/docs/gherkin standards/gherkin-core-standard.vi.md +303 -0
  92. package/docs/gherkin-dictionary.md +1071 -0
  93. package/docs/makeauth.md +225 -0
  94. package/package.json +4 -3
  95. package/src/cli/commands/add.ts +5 -3
  96. package/src/cli/commands/generate.ts +90 -38
  97. package/src/cli/index.ts +5 -16
  98. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/frame-enter-action.hbs +1 -0
  99. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/frame-exit-action.hbs +1 -0
  100. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/keyboard-global-action.hbs +1 -0
  101. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/scroll-action.hbs +1 -0
  102. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +2 -0
  103. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-filter.hbs +3 -0
  104. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-cell-by-index.hbs +3 -0
  105. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-column-exists.hbs +2 -0
  106. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +2 -0
  107. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +2 -0
  108. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +2 -0
  109. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +2 -0
  110. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
  111. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +11 -2
  112. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +11 -2
  113. package/src/generators/test-generator/code-generator.ts +11 -59
  114. package/src/generators/test-generator/patterns/assertion-patterns.ts +2 -0
  115. package/src/generators/test-generator/patterns/index.ts +12 -3
  116. package/src/generators/test-generator/patterns/interaction-patterns.ts +1 -1
  117. package/src/generators/test-generator/patterns/keyboard-patterns.ts +51 -0
  118. package/src/generators/test-generator/patterns/scope-patterns.ts +40 -0
  119. package/src/generators/test-generator/patterns/scroll-patterns.ts +27 -0
  120. package/src/generators/test-generator/patterns/table-patterns.ts +232 -0
  121. package/src/generators/test-generator/step-mapper.ts +42 -27
  122. package/src/generators/test-generator/template-engine.ts +3 -1
  123. package/src/generators/test-generator/types.ts +7 -112
  124. package/src/generators/test-generator/utils/selector-resolver.ts +70 -25
  125. package/src/orchestrator/project-initializer.ts +75 -160
  126. package/src/orchestrator/screen-manager.ts +61 -233
  127. package/src/orchestrator/templates/ai-rules.md +189 -0
  128. package/src/orchestrator/templates/gitignore +16 -0
  129. package/src/orchestrator/templates/playwright.config.ts +80 -0
  130. package/src/orchestrator/templates/readme.md +197 -0
  131. package/src/utils/selector-types.ts +3 -0
  132. package/dist/cli/commands/cache-clear.d.ts +0 -3
  133. package/dist/cli/commands/cache-clear.d.ts.map +0 -1
  134. package/dist/cli/commands/cache-clear.js +0 -24
  135. package/dist/cli/commands/cache-clear.js.map +0 -1
  136. package/dist/cli/commands/full.d.ts +0 -3
  137. package/dist/cli/commands/full.d.ts.map +0 -1
  138. package/dist/cli/commands/full.js +0 -37
  139. package/dist/cli/commands/full.js.map +0 -1
  140. package/dist/cli/commands/live-scan.d.ts +0 -3
  141. package/dist/cli/commands/live-scan.d.ts.map +0 -1
  142. package/dist/cli/commands/live-scan.js +0 -78
  143. package/dist/cli/commands/live-scan.js.map +0 -1
  144. package/dist/cli/commands/map.d.ts +0 -3
  145. package/dist/cli/commands/map.d.ts.map +0 -1
  146. package/dist/cli/commands/map.js +0 -93
  147. package/dist/cli/commands/map.js.map +0 -1
  148. package/dist/cli/commands/validate.d.ts +0 -3
  149. package/dist/cli/commands/validate.d.ts.map +0 -1
  150. package/dist/cli/commands/validate.js +0 -43
  151. package/dist/cli/commands/validate.js.map +0 -1
  152. package/dist/cli/utils.d.ts +0 -6
  153. package/dist/cli/utils.d.ts.map +0 -1
  154. package/dist/cli/utils.js +0 -101
  155. package/dist/cli/utils.js.map +0 -1
  156. package/dist/config/config-loader.d.ts +0 -51
  157. package/dist/config/config-loader.d.ts.map +0 -1
  158. package/dist/config/config-loader.js +0 -216
  159. package/dist/config/config-loader.js.map +0 -1
  160. package/dist/config/config-schema.d.ts +0 -121
  161. package/dist/config/config-schema.d.ts.map +0 -1
  162. package/dist/config/config-schema.js +0 -7
  163. package/dist/config/config-schema.js.map +0 -1
  164. package/dist/core/live-scanner/config-reader.d.ts +0 -10
  165. package/dist/core/live-scanner/config-reader.d.ts.map +0 -1
  166. package/dist/core/live-scanner/config-reader.js +0 -87
  167. package/dist/core/live-scanner/config-reader.js.map +0 -1
  168. package/dist/core/live-scanner/element-finder.d.ts +0 -20
  169. package/dist/core/live-scanner/element-finder.d.ts.map +0 -1
  170. package/dist/core/live-scanner/element-finder.js +0 -481
  171. package/dist/core/live-scanner/element-finder.js.map +0 -1
  172. package/dist/core/live-scanner/index.d.ts +0 -8
  173. package/dist/core/live-scanner/index.d.ts.map +0 -1
  174. package/dist/core/live-scanner/index.js +0 -33
  175. package/dist/core/live-scanner/index.js.map +0 -1
  176. package/dist/core/live-scanner/matrix-reader.d.ts +0 -17
  177. package/dist/core/live-scanner/matrix-reader.d.ts.map +0 -1
  178. package/dist/core/live-scanner/matrix-reader.js +0 -60
  179. package/dist/core/live-scanner/matrix-reader.js.map +0 -1
  180. package/dist/core/live-scanner/matrix-writer.d.ts +0 -7
  181. package/dist/core/live-scanner/matrix-writer.d.ts.map +0 -1
  182. package/dist/core/live-scanner/matrix-writer.js +0 -103
  183. package/dist/core/live-scanner/matrix-writer.js.map +0 -1
  184. package/dist/core/live-scanner/role-fallback.d.ts +0 -15
  185. package/dist/core/live-scanner/role-fallback.d.ts.map +0 -1
  186. package/dist/core/live-scanner/role-fallback.js +0 -46
  187. package/dist/core/live-scanner/role-fallback.js.map +0 -1
  188. package/dist/core/live-scanner/scanner.d.ts +0 -22
  189. package/dist/core/live-scanner/scanner.d.ts.map +0 -1
  190. package/dist/core/live-scanner/scanner.js +0 -303
  191. package/dist/core/live-scanner/scanner.js.map +0 -1
  192. package/dist/core/live-scanner/step-replayer.d.ts +0 -26
  193. package/dist/core/live-scanner/step-replayer.d.ts.map +0 -1
  194. package/dist/core/live-scanner/step-replayer.js +0 -473
  195. package/dist/core/live-scanner/step-replayer.js.map +0 -1
  196. package/dist/core/live-scanner/types.d.ts +0 -52
  197. package/dist/core/live-scanner/types.d.ts.map +0 -1
  198. package/dist/core/live-scanner/types.js +0 -14
  199. package/dist/core/live-scanner/types.js.map +0 -1
  200. package/dist/core/selector-base/annotation-handler.d.ts +0 -45
  201. package/dist/core/selector-base/annotation-handler.d.ts.map +0 -1
  202. package/dist/core/selector-base/annotation-handler.js +0 -102
  203. package/dist/core/selector-base/annotation-handler.js.map +0 -1
  204. package/dist/core/selector-base/base-generator.d.ts +0 -49
  205. package/dist/core/selector-base/base-generator.d.ts.map +0 -1
  206. package/dist/core/selector-base/base-generator.js +0 -214
  207. package/dist/core/selector-base/base-generator.js.map +0 -1
  208. package/dist/core/selector-base/gherkin-parser.d.ts +0 -24
  209. package/dist/core/selector-base/gherkin-parser.d.ts.map +0 -1
  210. package/dist/core/selector-base/gherkin-parser.js +0 -42
  211. package/dist/core/selector-base/gherkin-parser.js.map +0 -1
  212. package/dist/core/selector-mapper/priority-mapper.d.ts +0 -74
  213. package/dist/core/selector-mapper/priority-mapper.d.ts.map +0 -1
  214. package/dist/core/selector-mapper/priority-mapper.js +0 -477
  215. package/dist/core/selector-mapper/priority-mapper.js.map +0 -1
  216. package/dist/core/ui-scanner/heuristics/base-heuristic.d.ts +0 -91
  217. package/dist/core/ui-scanner/heuristics/base-heuristic.d.ts.map +0 -1
  218. package/dist/core/ui-scanner/heuristics/base-heuristic.js +0 -175
  219. package/dist/core/ui-scanner/heuristics/base-heuristic.js.map +0 -1
  220. package/dist/core/ui-scanner/react-scanner.d.ts +0 -32
  221. package/dist/core/ui-scanner/react-scanner.d.ts.map +0 -1
  222. package/dist/core/ui-scanner/react-scanner.js +0 -163
  223. package/dist/core/ui-scanner/react-scanner.js.map +0 -1
  224. package/dist/core/ui-scanner/scanner-interface.d.ts +0 -94
  225. package/dist/core/ui-scanner/scanner-interface.d.ts.map +0 -1
  226. package/dist/core/ui-scanner/scanner-interface.js +0 -33
  227. package/dist/core/ui-scanner/scanner-interface.js.map +0 -1
  228. package/dist/core/ui-scanner/strict-scanner.d.ts +0 -81
  229. package/dist/core/ui-scanner/strict-scanner.d.ts.map +0 -1
  230. package/dist/core/ui-scanner/strict-scanner.js +0 -511
  231. package/dist/core/ui-scanner/strict-scanner.js.map +0 -1
  232. package/dist/core/validator/data-validator.d.ts +0 -38
  233. package/dist/core/validator/data-validator.d.ts.map +0 -1
  234. package/dist/core/validator/data-validator.js +0 -212
  235. package/dist/core/validator/data-validator.js.map +0 -1
  236. package/dist/core/validator/feature-validator.d.ts +0 -27
  237. package/dist/core/validator/feature-validator.d.ts.map +0 -1
  238. package/dist/core/validator/feature-validator.js +0 -182
  239. package/dist/core/validator/feature-validator.js.map +0 -1
  240. package/dist/core/validator/index.d.ts +0 -46
  241. package/dist/core/validator/index.d.ts.map +0 -1
  242. package/dist/core/validator/index.js +0 -17
  243. package/dist/core/validator/index.js.map +0 -1
  244. package/dist/core/validator/screen-validator.d.ts +0 -35
  245. package/dist/core/validator/screen-validator.d.ts.map +0 -1
  246. package/dist/core/validator/screen-validator.js +0 -195
  247. package/dist/core/validator/screen-validator.js.map +0 -1
  248. package/dist/core/validator/selector-validator.d.ts +0 -36
  249. package/dist/core/validator/selector-validator.d.ts.map +0 -1
  250. package/dist/core/validator/selector-validator.js +0 -210
  251. package/dist/core/validator/selector-validator.js.map +0 -1
  252. package/dist/external/ai-provider.d.ts +0 -60
  253. package/dist/external/ai-provider.d.ts.map +0 -1
  254. package/dist/external/ai-provider.js +0 -30
  255. package/dist/external/ai-provider.js.map +0 -1
  256. package/dist/external/anthropic-provider.d.ts +0 -29
  257. package/dist/external/anthropic-provider.d.ts.map +0 -1
  258. package/dist/external/anthropic-provider.js +0 -85
  259. package/dist/external/anthropic-provider.js.map +0 -1
  260. package/dist/generators/cache/cache-manager.d.ts +0 -66
  261. package/dist/generators/cache/cache-manager.d.ts.map +0 -1
  262. package/dist/generators/cache/cache-manager.js +0 -286
  263. package/dist/generators/cache/cache-manager.js.map +0 -1
  264. package/dist/generators/dsl-writer/index.d.ts +0 -33
  265. package/dist/generators/dsl-writer/index.d.ts.map +0 -1
  266. package/dist/generators/dsl-writer/index.js +0 -226
  267. package/dist/generators/dsl-writer/index.js.map +0 -1
  268. package/dist/generators/scaffold-generator/index.d.ts +0 -162
  269. package/dist/generators/scaffold-generator/index.d.ts.map +0 -1
  270. package/dist/generators/scaffold-generator/index.js +0 -877
  271. package/dist/generators/scaffold-generator/index.js.map +0 -1
  272. package/dist/generators/selector-mapper/ai-mapper.d.ts +0 -56
  273. package/dist/generators/selector-mapper/ai-mapper.d.ts.map +0 -1
  274. package/dist/generators/selector-mapper/ai-mapper.js +0 -457
  275. package/dist/generators/selector-mapper/ai-mapper.js.map +0 -1
  276. package/dist/generators/selector-mapper/hybrid-mapper.d.ts +0 -67
  277. package/dist/generators/selector-mapper/hybrid-mapper.d.ts.map +0 -1
  278. package/dist/generators/selector-mapper/hybrid-mapper.js +0 -349
  279. package/dist/generators/selector-mapper/hybrid-mapper.js.map +0 -1
  280. package/dist/generators/selector-mapper/index.d.ts +0 -8
  281. package/dist/generators/selector-mapper/index.d.ts.map +0 -1
  282. package/dist/generators/selector-mapper/index.js +0 -12
  283. package/dist/generators/selector-mapper/index.js.map +0 -1
  284. package/dist/generators/selector-mapper/intelligent-mapper.d.ts +0 -125
  285. package/dist/generators/selector-mapper/intelligent-mapper.d.ts.map +0 -1
  286. package/dist/generators/selector-mapper/intelligent-mapper.js +0 -391
  287. package/dist/generators/selector-mapper/intelligent-mapper.js.map +0 -1
  288. package/dist/generators/test-generator/ai-step-mapper.d.ts +0 -27
  289. package/dist/generators/test-generator/ai-step-mapper.d.ts.map +0 -1
  290. package/dist/generators/test-generator/ai-step-mapper.js +0 -204
  291. package/dist/generators/test-generator/ai-step-mapper.js.map +0 -1
  292. package/dist/generators/test-generator/auth-setup-generator.d.ts +0 -18
  293. package/dist/generators/test-generator/auth-setup-generator.d.ts.map +0 -1
  294. package/dist/generators/test-generator/auth-setup-generator.js +0 -82
  295. package/dist/generators/test-generator/auth-setup-generator.js.map +0 -1
  296. package/dist/generators/test-generator/patterns/legacy-patterns.d.ts +0 -7
  297. package/dist/generators/test-generator/patterns/legacy-patterns.d.ts.map +0 -1
  298. package/dist/generators/test-generator/patterns/legacy-patterns.js +0 -98
  299. package/dist/generators/test-generator/patterns/legacy-patterns.js.map +0 -1
  300. package/dist/generators/test-generator/templates/auth-setup.ts.hbs +0 -36
  301. package/dist/generators/ui-model-builder/deep-scanner.d.ts +0 -121
  302. package/dist/generators/ui-model-builder/deep-scanner.d.ts.map +0 -1
  303. package/dist/generators/ui-model-builder/deep-scanner.js +0 -1113
  304. package/dist/generators/ui-model-builder/deep-scanner.js.map +0 -1
  305. package/dist/generators/ui-model-builder/enhanced-deep-scanner.d.ts +0 -110
  306. package/dist/generators/ui-model-builder/enhanced-deep-scanner.d.ts.map +0 -1
  307. package/dist/generators/ui-model-builder/enhanced-deep-scanner.js +0 -608
  308. package/dist/generators/ui-model-builder/enhanced-deep-scanner.js.map +0 -1
  309. package/dist/generators/ui-model-builder/react-scanner.d.ts +0 -107
  310. package/dist/generators/ui-model-builder/react-scanner.d.ts.map +0 -1
  311. package/dist/generators/ui-model-builder/react-scanner.js +0 -797
  312. package/dist/generators/ui-model-builder/react-scanner.js.map +0 -1
  313. package/dist/orchestrator/cache-manager.d.ts +0 -15
  314. package/dist/orchestrator/cache-manager.d.ts.map +0 -1
  315. package/dist/orchestrator/cache-manager.js +0 -62
  316. package/dist/orchestrator/cache-manager.js.map +0 -1
  317. package/dist/orchestrator/pipeline.d.ts +0 -56
  318. package/dist/orchestrator/pipeline.d.ts.map +0 -1
  319. package/dist/orchestrator/pipeline.js +0 -298
  320. package/dist/orchestrator/pipeline.js.map +0 -1
  321. package/dist/orchestrator/reporter.d.ts +0 -15
  322. package/dist/orchestrator/reporter.d.ts.map +0 -1
  323. package/dist/orchestrator/reporter.js +0 -30
  324. package/dist/orchestrator/reporter.js.map +0 -1
  325. package/src/cli/commands/cache-clear.ts +0 -22
  326. package/src/cli/commands/full.ts +0 -35
  327. package/src/cli/commands/live-scan.ts +0 -82
  328. package/src/cli/commands/map.ts +0 -97
  329. package/src/cli/commands/validate.ts +0 -43
  330. package/src/cli/utils.ts +0 -106
  331. package/src/config/ai-providers.yaml +0 -56
  332. package/src/config/config-loader.ts +0 -248
  333. package/src/config/config-schema.ts +0 -148
  334. package/src/config/default.config.yaml +0 -107
  335. package/src/config/framework.config.yaml +0 -52
  336. package/src/config/routes.yaml +0 -31
  337. package/src/core/live-scanner/config-reader.ts +0 -57
  338. package/src/core/live-scanner/element-finder.ts +0 -534
  339. package/src/core/live-scanner/index.ts +0 -7
  340. package/src/core/live-scanner/matrix-reader.ts +0 -65
  341. package/src/core/live-scanner/matrix-writer.ts +0 -77
  342. package/src/core/live-scanner/role-fallback.ts +0 -44
  343. package/src/core/live-scanner/scanner.ts +0 -321
  344. package/src/core/live-scanner/step-replayer.ts +0 -503
  345. package/src/core/live-scanner/types.ts +0 -58
  346. package/src/core/selector-base/annotation-handler.ts +0 -127
  347. package/src/core/selector-base/base-generator.ts +0 -234
  348. package/src/core/selector-base/gherkin-parser.ts +0 -57
  349. package/src/core/selector-mapper/priority-mapper.ts +0 -607
  350. package/src/core/ui-scanner/heuristics/base-heuristic.ts +0 -216
  351. package/src/core/ui-scanner/react-scanner.ts +0 -156
  352. package/src/core/ui-scanner/scanner-interface.ts +0 -133
  353. package/src/core/ui-scanner/strict-scanner.ts +0 -629
  354. package/src/core/validator/data-validator.ts +0 -202
  355. package/src/core/validator/feature-validator.ts +0 -176
  356. package/src/core/validator/index.ts +0 -57
  357. package/src/core/validator/screen-validator.ts +0 -209
  358. package/src/core/validator/selector-validator.ts +0 -209
  359. package/src/external/ai-provider.ts +0 -90
  360. package/src/external/anthropic-provider.ts +0 -114
  361. package/src/generators/README.md +0 -410
  362. package/src/generators/cache/cache-manager.ts +0 -322
  363. package/src/generators/dsl-writer/index.ts +0 -253
  364. package/src/generators/scaffold-generator/index.ts +0 -1029
  365. package/src/generators/selector-mapper/ai-mapper.ts +0 -528
  366. package/src/generators/selector-mapper/hybrid-mapper.ts +0 -427
  367. package/src/generators/selector-mapper/index.ts +0 -10
  368. package/src/generators/selector-mapper/intelligent-mapper.ts +0 -530
  369. package/src/generators/test-generator/ai-step-mapper.ts +0 -224
  370. package/src/generators/test-generator/auth-setup-generator.ts +0 -59
  371. package/src/generators/test-generator/patterns/legacy-patterns.ts +0 -104
  372. package/src/generators/test-generator/templates/auth-setup.ts.hbs +0 -36
  373. package/src/generators/ui-model-builder/deep-scanner.ts +0 -1244
  374. package/src/generators/ui-model-builder/enhanced-deep-scanner.ts +0 -731
  375. package/src/generators/ui-model-builder/react-scanner.ts +0 -959
  376. package/src/orchestrator/cache-manager.ts +0 -32
  377. package/src/orchestrator/pipeline.ts +0 -354
  378. package/src/orchestrator/reporter.ts +0 -36
@@ -1,877 +0,0 @@
1
- "use strict";
2
- /**
3
- * Scaffold Generator
4
- * Parses Gherkin feature files and generates selector YAML scaffolds
5
- */
6
- var __importDefault = (this && this.__importDefault) || function (mod) {
7
- return (mod && mod.__esModule) ? mod : { "default": mod };
8
- };
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.ScaffoldGenerator = void 0;
11
- const fs_1 = __importDefault(require("fs"));
12
- const path_1 = __importDefault(require("path"));
13
- const yaml_1 = __importDefault(require("yaml"));
14
- const glob_1 = require("glob");
15
- const selector_resolver_1 = require("../test-generator/utils/selector-resolver");
16
- class ScaffoldGenerator {
17
- /**
18
- * Generate scaffold YAML from a feature file
19
- */
20
- generateFromFile(featureFilePath) {
21
- const content = fs_1.default.readFileSync(featureFilePath, 'utf-8');
22
- return this.generateFromContent(content);
23
- }
24
- /**
25
- * Generate scaffold YAML from feature content
26
- */
27
- generateFromContent(content) {
28
- // Extract feature path from Gherkin metadata (e.g., "Path: login-guest?user=inactive")
29
- const featurePath = this.extractFeaturePath(content);
30
- const elements = this.extractElements(content, featurePath);
31
- return this.buildScaffold(elements);
32
- }
33
- /**
34
- * Extract elements from Gherkin content with their actions
35
- */
36
- extractElements(content, featurePath) {
37
- const elements = [];
38
- const lines = content.split('\n');
39
- // Regex to match elements in brackets [...] with optional nth index
40
- // Captures: [Element] field 1, [Element] button 2, [Element] text 3, etc.
41
- const elementRegex = /\[([^\]]+)\]/g;
42
- for (const line of lines) {
43
- const trimmedLine = line.trim();
44
- // Skip comments and empty lines
45
- if (trimmedLine.startsWith('#') || !trimmedLine) {
46
- continue;
47
- }
48
- // Determine action from the line
49
- const action = this.detectAction(trimmedLine);
50
- let match;
51
- while ((match = elementRegex.exec(trimmedLine)) !== null) {
52
- const rawText = match[1];
53
- const baseKey = selector_resolver_1.SelectorResolver.generateKey(rawText);
54
- // Extract nth index and target type from text after the element reference
55
- // Matches patterns like: [Email] field 1, [Submit] button 2, [Name] 3
56
- const afterElement = trimmedLine.slice(match.index + match[0].length);
57
- const nth = selector_resolver_1.SelectorResolver.extractNthFromStep(afterElement);
58
- const targetType = this.extractTargetType(afterElement, action);
59
- // Append nth to key so elements at different positions get distinct keys
60
- // e.g., [Hãy gửi lời cảm ơn] field 3 → "hay.gui.loi.cam.on--3"
61
- const key = nth > 0 ? `${baseKey}--${nth}` : baseKey;
62
- // Check if element starts with "target" (semantic indicator for dynamic content)
63
- // e.g., [target order], [Target Item] → element should have empty value
64
- const isTargetElement = /^target\s/i.test(rawText);
65
- elements.push({
66
- rawText: isTargetElement ? '' : rawText, // Empty value when element is a target element
67
- key,
68
- action,
69
- targetType,
70
- lineText: trimmedLine,
71
- nth,
72
- featurePath: targetType === 'page' ? featurePath : undefined,
73
- });
74
- }
75
- }
76
- return elements;
77
- }
78
- /**
79
- * Extract target type from text after element reference
80
- * e.g., " button" -> "button", " link 1" -> "link", " field with" -> "field"
81
- */
82
- extractTargetType(text, action) {
83
- const lowerText = text.toLowerCase().trim();
84
- // NEW: Check for 'page' keyword FIRST (highest priority)
85
- if (/^page\b/.test(lowerText) || /^\d*\s*page\b/.test(lowerText)) {
86
- return 'page';
87
- }
88
- // Check for specific target types in order of specificity
89
- if (/^link\b/.test(lowerText) || /^\d*\s*link\b/.test(lowerText)) {
90
- return 'link';
91
- }
92
- if (/^button\b/.test(lowerText) || /^\d*\s*button\b/.test(lowerText)) {
93
- return 'button';
94
- }
95
- if (/^radio\b/.test(lowerText) || /^\d*\s*radio\b/.test(lowerText)) {
96
- return 'radio';
97
- }
98
- if (/^dropdown\b/.test(lowerText) || /^\d*\s*dropdown\b/.test(lowerText) || /^select\b/.test(lowerText)) {
99
- return 'dropdown';
100
- }
101
- if (/^checkbox\b/.test(lowerText) || /^\d*\s*checkbox\b/.test(lowerText)) {
102
- return 'checkbox';
103
- }
104
- if (/^field\b/.test(lowerText) || /^\d*\s*field\b/.test(lowerText) || /^input\b/.test(lowerText)) {
105
- return 'field';
106
- }
107
- if (/^textbox\b/.test(lowerText) || /^\d*\s*textbox\b/.test(lowerText)) {
108
- return 'textbox';
109
- }
110
- if (/^textarea\b/.test(lowerText) || /^\d*\s*textarea\b/.test(lowerText)) {
111
- return 'textarea';
112
- }
113
- if (/^editor\b/.test(lowerText) || /^\d*\s*editor\b/.test(lowerText)) {
114
- return 'editor';
115
- }
116
- if (/^text\b/.test(lowerText) || /^\d*\s*text\b/.test(lowerText)) {
117
- return 'text';
118
- }
119
- if (/^label\b/.test(lowerText) || /^\d*\s*label\b/.test(lowerText)) {
120
- return 'label';
121
- }
122
- if (/^uploader\b/.test(lowerText) || /^\d*\s*uploader\b/.test(lowerText)) {
123
- return 'uploader';
124
- }
125
- if (/^element\b/.test(lowerText) || /^\d*\s*element\b/.test(lowerText)) {
126
- return 'element';
127
- }
128
- if (/^(column|columnheader)\b/.test(lowerText) || /^\d*\s*(column|columnheader)\b/.test(lowerText)) {
129
- return 'column';
130
- }
131
- if (/^(logo|image|img|icon)\b/.test(lowerText) || /^\d*\s*(logo|image|img|icon)\b/.test(lowerText)) {
132
- return 'img';
133
- }
134
- if (/^(option|item|listitem)\b/.test(lowerText) || /^\d*\s*(option|item|listitem)\b/.test(lowerText)) {
135
- return 'option';
136
- }
137
- if (/^(dialog|modal)\b/.test(lowerText) || /^\d*\s*(dialog|modal)\b/.test(lowerText)) {
138
- return 'dialog';
139
- }
140
- if (/^(heading|header)\b/.test(lowerText) || /^\d*\s*(heading|header)\b/.test(lowerText)) {
141
- return 'heading';
142
- }
143
- if (/^(title|caption|message)\b/.test(lowerText) || /^\d*\s*(title|caption|message)\b/.test(lowerText)) {
144
- return 'text';
145
- }
146
- // Default based on action
147
- switch (action) {
148
- case 'click':
149
- return 'text'; // Default click to text
150
- case 'fill':
151
- return 'field'; // Default fill to field
152
- case 'see':
153
- return 'text'; // Default see to text
154
- case 'select':
155
- return 'dropdown'; // Default select to dropdown
156
- default:
157
- return 'element';
158
- }
159
- }
160
- /**
161
- * Extract nth index from text after element reference
162
- * e.g., " field 1 with" -> 1, " button 2" -> 2, " row 1" -> 1, " text" -> 0
163
- */
164
- /**
165
- * Detect the action type from a Gherkin step line
166
- */
167
- detectAction(line) {
168
- const lowerLine = line.toLowerCase();
169
- // Check for specific action keywords
170
- // Order matters: check "see" before "fill" because text like "fill in all fields" may appear in assertions
171
- if (/\b(see|verify|check|expect|should see|visible|displayed)\b/.test(lowerLine)) {
172
- return 'see';
173
- }
174
- if (/\b(fill|enter|type|input)\b/.test(lowerLine)) {
175
- return 'fill';
176
- }
177
- if (/\b(click|press|tap|submit)\b/.test(lowerLine)) {
178
- return 'click';
179
- }
180
- if (/\b(open|navigate|go to|visit)\b/.test(lowerLine)) {
181
- return 'open';
182
- }
183
- if (/\b(select|choose|pick)\b/.test(lowerLine)) {
184
- return 'select';
185
- }
186
- if (/\b(wait|wait for|waits for)\b/.test(lowerLine)) {
187
- return 'wait';
188
- }
189
- // Default to placeholder for unknown actions
190
- return 'default';
191
- }
192
- /**
193
- * Extract feature path from Gherkin metadata
194
- * Looks for "Path: <value>" line in the feature file
195
- * e.g., "Path: login-guest?user=inactive" -> "login-guest?user=inactive"
196
- */
197
- extractFeaturePath(content) {
198
- const lines = content.split('\n');
199
- for (const line of lines) {
200
- const trimmedLine = line.trim();
201
- // Look for Path: pattern (case-insensitive)
202
- const match = trimmedLine.match(/^Path:\s*(.+?)$/i);
203
- if (match) {
204
- return match[1].trim();
205
- }
206
- }
207
- return undefined;
208
- }
209
- /**
210
- * Convert element text to YAML key format
211
- * "Email Address" -> "email.address"
212
- * "Your email or password is incorrect!" -> "your.email.or.password.is.incorrect"
213
- */
214
- /**
215
- * Get action priority (higher number = higher priority)
216
- */
217
- getActionPriority(action) {
218
- const priorities = {
219
- 'click': 4, // Highest priority - most specific interaction
220
- 'fill': 3, // Form input
221
- 'select': 3, // Form select
222
- 'see': 2, // Assertion
223
- 'open': 1, // Navigation - lowest priority
224
- 'default': 0,
225
- };
226
- return priorities[action] ?? 0;
227
- }
228
- /**
229
- * Build scaffold result from extracted elements
230
- */
231
- buildScaffold(elements) {
232
- const result = {};
233
- // Group elements by key to detect duplicates with different target types
234
- const elementsByKey = {};
235
- for (const element of elements) {
236
- if (!elementsByKey[element.key]) {
237
- elementsByKey[element.key] = [];
238
- }
239
- elementsByKey[element.key].push(element);
240
- }
241
- for (const key of Object.keys(elementsByKey)) {
242
- const group = elementsByKey[key];
243
- // Deduplicate by targetType, keeping highest priority action per type
244
- const byType = {};
245
- for (const element of group) {
246
- const existing = byType[element.targetType];
247
- if (!existing ||
248
- this.getActionPriority(element.action) > this.getActionPriority(existing.action)) {
249
- byType[element.targetType] = element;
250
- }
251
- }
252
- const uniqueTypes = Object.keys(byType);
253
- if (uniqueTypes.length <= 1) {
254
- // No type conflict — use base key as before
255
- const element = byType[uniqueTypes[0]];
256
- result[key] = this.createScaffoldElement(element);
257
- }
258
- else {
259
- // Multiple types for same name — generate type-suffixed keys
260
- for (const type of uniqueTypes) {
261
- const suffixedKey = `${key}--${type}`;
262
- result[suffixedKey] = this.createScaffoldElement(byType[type]);
263
- }
264
- }
265
- }
266
- return result;
267
- }
268
- /**
269
- * Create a scaffold element based on the action type and target type
270
- */
271
- createScaffoldElement(element) {
272
- const { nth, targetType } = element;
273
- // NEW: Handle page type
274
- if (targetType === 'page') {
275
- return {
276
- locator: '',
277
- type: 'page',
278
- value: element.featurePath || '/',
279
- nth,
280
- };
281
- }
282
- switch (element.action) {
283
- case 'fill':
284
- // For fill action with uploader type, use label type (file input triggered by label)
285
- if (targetType === 'uploader') {
286
- return {
287
- locator: '',
288
- type: 'label',
289
- value: element.rawText,
290
- nth,
291
- };
292
- }
293
- // For fill action with label type, use label type
294
- if (targetType === 'label') {
295
- return {
296
- locator: '',
297
- type: 'label',
298
- value: element.rawText,
299
- nth,
300
- };
301
- }
302
- // For fill action with field/textbox/textarea types, use role-based locator
303
- return {
304
- locator: '',
305
- type: 'role',
306
- value: this.getFillRoleValue(targetType),
307
- name: element.rawText,
308
- nth,
309
- };
310
- case 'click':
311
- // Special case: label and text clicks use text locator
312
- if (targetType === 'label' || targetType === 'text') {
313
- return {
314
- locator: '',
315
- type: 'text',
316
- value: element.rawText,
317
- nth,
318
- };
319
- }
320
- // Regular click actions use role
321
- const clickRoleValue = this.getClickRoleValue(targetType);
322
- return {
323
- locator: '',
324
- type: 'role',
325
- value: clickRoleValue,
326
- name: element.rawText, // TODO: Update with actual accessible name from inspection
327
- nth,
328
- };
329
- case 'see':
330
- // Column type: used for table column cell assertions, no DOM selector needed
331
- if (targetType === 'column') {
332
- return {
333
- locator: '',
334
- type: 'column',
335
- value: element.rawText,
336
- nth,
337
- };
338
- }
339
- // For assertions - respect targetType for role-based elements
340
- if (targetType === 'img' || targetType === 'button' || targetType === 'link' ||
341
- targetType === 'checkbox' || targetType === 'radio' || targetType === 'heading' || targetType === 'dialog' || targetType === 'dropdown') {
342
- const seeRoleValue = this.getRoleValue(targetType);
343
- return {
344
- locator: '',
345
- type: 'role',
346
- value: seeRoleValue,
347
- name: element.rawText,
348
- nth,
349
- };
350
- }
351
- return {
352
- locator: '',
353
- type: 'text',
354
- value: element.rawText,
355
- nth,
356
- };
357
- case 'select':
358
- // Determine role based on target type (radio, dropdown/combobox, checkbox)
359
- const selectRoleValue = this.getSelectRoleValue(targetType);
360
- return {
361
- locator: '',
362
- type: 'role',
363
- value: selectRoleValue,
364
- name: element.rawText, // TODO: Update with actual accessible name from inspection
365
- nth,
366
- };
367
- case 'open':
368
- // For page navigation, we typically don't need a selector
369
- // But include it for reference
370
- return {
371
- locator: '',
372
- type: 'text',
373
- value: element.rawText,
374
- nth,
375
- };
376
- default:
377
- // Default to placeholder type
378
- return {
379
- locator: '',
380
- type: 'placeholder',
381
- value: element.rawText,
382
- nth,
383
- };
384
- }
385
- }
386
- /**
387
- * Get ARIA role value from target type (shared across actions)
388
- */
389
- getRoleValue(targetType) {
390
- switch (targetType) {
391
- case 'link':
392
- return 'link';
393
- case 'button':
394
- return 'button';
395
- case 'checkbox':
396
- return 'checkbox';
397
- case 'radio':
398
- return 'radio';
399
- case 'tab':
400
- return 'tab';
401
- case 'menuitem':
402
- return 'menuitem';
403
- case 'img':
404
- return 'img';
405
- case 'heading':
406
- return 'heading';
407
- case 'dialog':
408
- return 'dialog';
409
- case 'dropdown':
410
- return 'button';
411
- default:
412
- return 'button'; // Default to button
413
- }
414
- }
415
- /**
416
- * Get role value for click actions based on target type
417
- */
418
- getClickRoleValue(targetType) {
419
- return this.getRoleValue(targetType);
420
- }
421
- /**
422
- * Get role value for select actions based on target type
423
- */
424
- getSelectRoleValue(targetType) {
425
- switch (targetType) {
426
- case 'radio':
427
- return 'radio';
428
- case 'checkbox':
429
- return 'checkbox';
430
- case 'dropdown':
431
- case 'select':
432
- return 'combobox';
433
- case 'listbox':
434
- return 'listbox';
435
- default:
436
- return 'combobox'; // Default to combobox
437
- }
438
- }
439
- /**
440
- * Get ARIA role value for fill actions — all fillable inputs use 'textbox'
441
- */
442
- getFillRoleValue(targetType) {
443
- switch (targetType) {
444
- case 'field':
445
- case 'textbox':
446
- case 'textarea':
447
- default:
448
- return 'textbox';
449
- }
450
- }
451
- /**
452
- * Generate YAML content from scaffold result
453
- */
454
- generateYamlContent(scaffold, screenName) {
455
- // For header, use name (for role type) or value (for other types)
456
- const getDisplayValue = (e) => e.name || e.value;
457
- const header = screenName
458
- ? `# ${this.capitalizeFirst(screenName)} Screen Selectors (Auto-generated)
459
- # Reference in features: ${Object.values(scaffold).map(e => `[${getDisplayValue(e)}]`).join(', ')}
460
- # Directory: qa/selectors/screens/
461
-
462
- `
463
- : `# Screen Selectors (Auto-generated)
464
-
465
- `;
466
- // Convert to YAML with proper formatting
467
- const yamlContent = yaml_1.default.stringify(scaffold, {
468
- indent: 2,
469
- lineWidth: 100,
470
- defaultKeyType: 'PLAIN',
471
- defaultStringType: 'QUOTE_SINGLE',
472
- });
473
- return header + yamlContent;
474
- }
475
- /**
476
- * Write scaffold to file
477
- */
478
- writeScaffold(scaffold, outputPath, screenName) {
479
- const dir = path_1.default.dirname(outputPath);
480
- if (!fs_1.default.existsSync(dir)) {
481
- fs_1.default.mkdirSync(dir, { recursive: true });
482
- }
483
- const content = this.generateYamlContent(scaffold, screenName);
484
- fs_1.default.writeFileSync(outputPath, content, 'utf-8');
485
- }
486
- /**
487
- * Process a feature file and write scaffold to output
488
- */
489
- processFeatureFile(featureFilePath, outputDir, forceOverwrite = false) {
490
- // Extract screen name from feature file name
491
- const featureFileName = path_1.default.basename(featureFilePath, '.feature');
492
- const screenName = featureFileName;
493
- // Generate scaffold
494
- const scaffold = this.generateFromFile(featureFilePath);
495
- const elementCount = Object.keys(scaffold).length;
496
- // Write to output
497
- const outputPath = path_1.default.join(outputDir, `${screenName}.yaml`);
498
- // Check if file exists and skip if not forcing overwrite
499
- if (fs_1.default.existsSync(outputPath) && !forceOverwrite) {
500
- return { outputPath, elementCount };
501
- }
502
- this.writeScaffold(scaffold, outputPath, screenName);
503
- return { outputPath, elementCount };
504
- }
505
- /**
506
- * Process all feature files in a directory
507
- */
508
- processAllFeatures(featuresDir, outputDir) {
509
- const featureFiles = glob_1.glob.sync('**/*.feature', {
510
- cwd: featuresDir,
511
- absolute: true,
512
- });
513
- const results = [];
514
- for (const featureFile of featureFiles) {
515
- const result = this.processFeatureFile(featureFile, outputDir);
516
- results.push({
517
- file: featureFile,
518
- ...result,
519
- });
520
- }
521
- return results;
522
- }
523
- /**
524
- * Enrich scaffold with live-scan data if a .live-scan.yaml file exists.
525
- * Uses the Playwright locator strategy detected by live-scan:
526
- * testid → type: testid, value: <testid>
527
- * role → type: role, value: <aria-role>, name: <accessible-name>
528
- * label → type: label, value: <label-text>
529
- * placeholder → type: placeholder, value: <placeholder-text>
530
- * text → type: text, value: <visible-text>
531
- */
532
- enrichWithLiveScan(scaffold, liveScanPath) {
533
- if (!fs_1.default.existsSync(liveScanPath)) {
534
- return scaffold;
535
- }
536
- let matrixData;
537
- try {
538
- const { readMatrix, mergeMatrixElements } = require('../../core/live-scanner/matrix-reader');
539
- matrixData = readMatrix(liveScanPath);
540
- if (!matrixData)
541
- return scaffold;
542
- const elements = mergeMatrixElements(matrixData);
543
- if (!elements || Object.keys(elements).length === 0)
544
- return scaffold;
545
- console.log(` 🔗 Using live-scan data from ${path_1.default.basename(liveScanPath)}`);
546
- let enrichedCount = 0;
547
- let warningCount = 0;
548
- const enriched = { ...scaffold };
549
- for (const [key, scaffoldElement] of Object.entries(enriched)) {
550
- // Column-type elements use header text matching, skip live-scan enrichment
551
- if (scaffoldElement.type === 'column')
552
- continue;
553
- // Target elements (empty value) are matched by data value at runtime, skip enrichment
554
- if (!scaffoldElement.value && !scaffoldElement.name)
555
- continue;
556
- // Try exact key first, then base key without --N suffix or --type suffix
557
- let liveElement = elements[key];
558
- if (!liveElement && key.includes('--')) {
559
- const baseKey = key.replace(/--.*$/, '');
560
- liveElement = elements[baseKey];
561
- }
562
- if (!liveElement) {
563
- continue;
564
- }
565
- // If live-scan couldn't find the element, clear the identifying field so user must fill manually
566
- if (liveElement.matchMethod === 'unresolved') {
567
- if (scaffoldElement.name !== undefined) {
568
- enriched[key] = { ...scaffoldElement, name: '' };
569
- }
570
- else {
571
- enriched[key] = { ...scaffoldElement, value: '' };
572
- }
573
- continue;
574
- }
575
- // Use the Playwright locator strategy that live-scan detected
576
- const selectorType = liveElement.selectorType;
577
- // Only set exact when true (omit from YAML when false for cleaner output)
578
- const exactField = liveElement.exact ? { exact: true } : {};
579
- // Strip 'name' from scaffold base — only 'role' type uses name
580
- const { name: _scaffoldName, ...scaffoldBase } = scaffoldElement;
581
- if (selectorType === 'testid' && liveElement.testid) {
582
- // page.getByTestId('value')
583
- enriched[key] = {
584
- ...scaffoldBase,
585
- type: 'testid',
586
- value: liveElement.testid,
587
- };
588
- }
589
- else if (selectorType === 'role' && liveElement.role) {
590
- // page.getByRole('button', { name: 'Submit' })
591
- // When name is empty (e.g. data-value fallback), keep it as '' —
592
- // the actual name comes from test-data at runtime
593
- enriched[key] = {
594
- ...scaffoldBase,
595
- type: 'role',
596
- value: liveElement.role,
597
- name: liveElement.name,
598
- ...exactField,
599
- };
600
- }
601
- else if (selectorType === 'label' && liveElement.label) {
602
- // page.getByLabel('Username')
603
- enriched[key] = {
604
- ...scaffoldBase,
605
- type: 'label',
606
- value: liveElement.label,
607
- ...exactField,
608
- };
609
- }
610
- else if (selectorType === 'placeholder' && (liveElement.placeholder || liveElement.selectorValue)) {
611
- // page.getByPlaceholder('Enter email')
612
- enriched[key] = {
613
- ...scaffoldBase,
614
- type: 'placeholder',
615
- value: liveElement.placeholder || liveElement.selectorValue,
616
- ...exactField,
617
- };
618
- }
619
- else if (selectorType === 'text') {
620
- // page.getByText('Welcome')
621
- // When name is empty (data-value fallback), keep value empty —
622
- // the actual text comes from test-data at runtime
623
- enriched[key] = {
624
- ...scaffoldBase,
625
- type: 'text',
626
- value: liveElement.name,
627
- ...exactField,
628
- };
629
- }
630
- else if (selectorType === 'locator' && liveElement.selectorValue) {
631
- // page.locator('[contenteditable="true"]') — CSS locator
632
- // Reset nth to 0: the original nth was for text matches, not this locator type
633
- enriched[key] = {
634
- ...scaffoldBase,
635
- type: 'locator',
636
- value: liveElement.selectorValue,
637
- nth: 0,
638
- };
639
- }
640
- else {
641
- continue;
642
- }
643
- // Detect contenteditable div-based editors (rich text editors like TipTap, Quill, ProseMirror)
644
- // When the DOM element is contenteditable but NOT a native input/textarea,
645
- // mark it so the generator uses click + pressSequentially instead of .fill()
646
- if (liveElement.contenteditable && liveElement.tag !== 'textarea' && liveElement.tag !== 'input') {
647
- enriched[key] = { ...enriched[key], inputMethod: 'pressSequentially' };
648
- }
649
- enrichedCount++;
650
- if (liveElement.warning) {
651
- warningCount++;
652
- console.log(` ⚠️ ${key}: ${liveElement.warning}`);
653
- }
654
- }
655
- const textOnlyCount = Object.keys(enriched).length - enrichedCount;
656
- console.log(` 📊 Live-scan: ${enrichedCount} enriched, ${textOnlyCount} text-inferred${warningCount > 0 ? `, ${warningCount} warnings` : ''}`);
657
- return enriched;
658
- }
659
- catch (error) {
660
- console.warn(` ⚠️ Failed to read live-scan data: ${error.message}`);
661
- return scaffold;
662
- }
663
- }
664
- /**
665
- * Helper to capitalize first letter
666
- */
667
- capitalizeFirst(str) {
668
- return str.charAt(0).toUpperCase() + str.slice(1);
669
- }
670
- /**
671
- * Process a screen and generate both selectors and test-data YAML
672
- * Folder structure: qa/screens/<screen>/features/, selectors/, test-data/
673
- */
674
- processScreen(screenName, screensDir = 'qa/screens', forceOverwrite = false) {
675
- const screenDir = path_1.default.join(screensDir, screenName);
676
- const featuresDir = path_1.default.join(screenDir, 'features');
677
- const selectorsDir = path_1.default.join(screenDir, 'selectors');
678
- const testDataDir = path_1.default.join(screenDir, 'test-data');
679
- // Find all feature files in the screen's features directory
680
- const featureFiles = glob_1.glob.sync('**/*.feature', {
681
- cwd: featuresDir,
682
- absolute: true,
683
- });
684
- if (featureFiles.length === 0) {
685
- throw new Error(`No feature files found in ${featuresDir}`);
686
- }
687
- // Ensure directories exist
688
- fs_1.default.mkdirSync(selectorsDir, { recursive: true });
689
- fs_1.default.mkdirSync(testDataDir, { recursive: true });
690
- const results = [];
691
- // Process each feature file individually
692
- for (const featureFile of featureFiles) {
693
- const filename = path_1.default.basename(featureFile, '.feature');
694
- const content = fs_1.default.readFileSync(featureFile, 'utf-8');
695
- // Extract feature path from Gherkin metadata
696
- const featurePath = this.extractFeaturePath(content);
697
- // Extract elements and data refs from this file
698
- const elements = this.extractElements(content, featurePath);
699
- const dataRefs = this.extractDataRefs(content);
700
- // Build scaffolds from this file
701
- const selectorScaffold = this.buildScaffold(elements);
702
- const testDataScaffold = this.buildTestData(dataRefs);
703
- // Enrich scaffold with live-scan data if available
704
- const liveScanPath = path_1.default.join(selectorsDir, `${filename}.live-scan.yaml`);
705
- const liveScanEnriched = this.enrichWithLiveScan(selectorScaffold, liveScanPath);
706
- // Check if files exist
707
- const selectorsPath = path_1.default.join(selectorsDir, `${filename}.yaml`);
708
- const selectorsOverridePath = path_1.default.join(selectorsDir, `${filename}.override.yaml`);
709
- const selectorsExists = fs_1.default.existsSync(selectorsPath);
710
- const testDataPath = path_1.default.join(testDataDir, `${filename}.yaml`);
711
- const testDataOverridePath = path_1.default.join(testDataDir, `${filename}.override.yaml`);
712
- const testDataExists = fs_1.default.existsSync(testDataPath);
713
- // Write selectors YAML (BASE ONLY - never touch override files)
714
- if (!selectorsExists || forceOverwrite) {
715
- this.writeSelectorsYaml(liveScanEnriched, selectorsPath, filename);
716
- }
717
- // Override file is NEVER touched by map command
718
- // Write test-data YAML — always smart-merge regardless of --force:
719
- // • existing keys keep their current value
720
- // • new keys from features are added (empty string)
721
- // • keys no longer referenced in features are removed
722
- if (testDataExists) {
723
- const existingRaw = yaml_1.default.parse(fs_1.default.readFileSync(testDataPath, 'utf-8')) || {};
724
- const merged = this.mergeTestData(existingRaw, testDataScaffold);
725
- this.writeTestDataYaml(merged, testDataPath, filename);
726
- }
727
- else {
728
- this.writeTestDataYaml(testDataScaffold, testDataPath, filename);
729
- }
730
- // Override file is NEVER touched by map command
731
- results.push({
732
- featureFile: filename,
733
- selectorsPath,
734
- testDataPath,
735
- elementCount: Object.keys(liveScanEnriched).length,
736
- dataRefCount: dataRefs.length,
737
- selectorsSkipped: selectorsExists && !forceOverwrite,
738
- testDataMerged: testDataExists,
739
- });
740
- }
741
- return results;
742
- }
743
- /**
744
- * Extract data references {{...}} from Gherkin content
745
- */
746
- extractDataRefs(content) {
747
- const dataRefs = [];
748
- const lines = content.split('\n');
749
- const seen = new Set();
750
- // Regex to match data refs {{...}}
751
- const dataRefRegex = /\{\{([^}]+)\}\}/g;
752
- for (const line of lines) {
753
- const trimmedLine = line.trim();
754
- // Skip comments and empty lines
755
- if (trimmedLine.startsWith('#') || !trimmedLine) {
756
- continue;
757
- }
758
- let match;
759
- while ((match = dataRefRegex.exec(trimmedLine)) !== null) {
760
- const rawRef = match[1].trim();
761
- // Skip duplicates
762
- if (seen.has(rawRef)) {
763
- continue;
764
- }
765
- seen.add(rawRef);
766
- dataRefs.push({
767
- rawRef,
768
- lineText: trimmedLine,
769
- });
770
- }
771
- }
772
- return dataRefs;
773
- }
774
- /**
775
- * Build test data structure from data refs
776
- * Supports nested structure: "valid.login_password" -> { valid: { login_password: '' } }
777
- */
778
- buildTestData(dataRefs) {
779
- const result = {};
780
- for (const dataRef of dataRefs) {
781
- const parts = dataRef.rawRef.split('.');
782
- if (parts.length === 1) {
783
- // Simple variable: "valid_login_email" -> { valid_login_email: '' }
784
- result[parts[0]] = '';
785
- }
786
- else {
787
- // Nested variable: "valid.login_password" -> { valid: { login_password: '' } }
788
- let current = result;
789
- for (let i = 0; i < parts.length - 1; i++) {
790
- const part = parts[i];
791
- if (!current[part] || typeof current[part] === 'string') {
792
- current[part] = {};
793
- }
794
- current = current[part];
795
- }
796
- current[parts[parts.length - 1]] = '';
797
- }
798
- }
799
- return result;
800
- }
801
- /**
802
- * Write selectors YAML file
803
- */
804
- writeSelectorsYaml(scaffold, outputPath, screenName) {
805
- const getDisplayValue = (e) => e.name || e.value;
806
- // Deduplicate display values for header comment
807
- const displayValues = [...new Set(Object.values(scaffold).map(e => `[${getDisplayValue(e)}]`))];
808
- const header = `# ${this.capitalizeFirst(screenName)} Screen Selectors (Auto-generated)
809
- # ⚠️ AUTO-GENERATED - Do not edit directly
810
- # To override values, edit ${screenName}.override.yaml
811
- # Manual edits to this file will be lost on regeneration
812
- #
813
- # Reference in features: ${displayValues.join(', ')}
814
-
815
- `;
816
- const yamlContent = yaml_1.default.stringify(scaffold, {
817
- indent: 2,
818
- lineWidth: 100,
819
- defaultKeyType: 'PLAIN',
820
- defaultStringType: 'QUOTE_SINGLE',
821
- });
822
- fs_1.default.writeFileSync(outputPath, header + yamlContent, 'utf-8');
823
- }
824
- /**
825
- * Smart-merge test-data objects:
826
- * - Keys present in both: keep the existing value (user may have filled it in)
827
- * - Keys only in fresh (new refs in features): add with empty value
828
- * - Keys only in existing (removed from features): drop them
829
- */
830
- mergeTestData(existing, fresh) {
831
- const merged = {};
832
- for (const key of Object.keys(fresh)) {
833
- if (key in existing) {
834
- const existingVal = existing[key];
835
- const freshVal = fresh[key];
836
- if (typeof freshVal === 'object' && typeof existingVal === 'object') {
837
- // Both are nested — recurse
838
- merged[key] = this.mergeTestData(existingVal, freshVal);
839
- }
840
- else {
841
- // Keep the existing (user-supplied) value
842
- merged[key] = existingVal;
843
- }
844
- }
845
- else {
846
- // New key — add with the scaffold default (empty string or nested object)
847
- merged[key] = fresh[key];
848
- }
849
- // Keys present in existing but absent from fresh are intentionally dropped (no longer referenced)
850
- }
851
- return merged;
852
- }
853
- /**
854
- * Write test-data YAML file
855
- */
856
- writeTestDataYaml(testData, outputPath, screenName) {
857
- const header = `# ${this.capitalizeFirst(screenName)} Screen Test Data
858
- # Managed by: sungen map (smart-merge)
859
- # • New {{variable}} refs are added automatically (empty value)
860
- # • Removed refs are dropped on the next sungen map run
861
- # • Your existing values are never overwritten
862
- # To fix a value permanently, copy the key to ${screenName}.override.yaml
863
- #
864
- # Reference in features using {{variable}} syntax
865
-
866
- `;
867
- const yamlContent = yaml_1.default.stringify(testData, {
868
- indent: 2,
869
- lineWidth: 100,
870
- defaultKeyType: 'PLAIN',
871
- defaultStringType: 'QUOTE_SINGLE',
872
- });
873
- fs_1.default.writeFileSync(outputPath, header + yamlContent, 'utf-8');
874
- }
875
- }
876
- exports.ScaffoldGenerator = ScaffoldGenerator;
877
- //# sourceMappingURL=index.js.map