@sun-asterisk/sungen 1.0.20 → 1.0.21

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 (286) hide show
  1. package/dist/cli/commands/add.d.ts +3 -0
  2. package/dist/cli/commands/add.d.ts.map +1 -0
  3. package/dist/cli/commands/add.js +27 -0
  4. package/dist/cli/commands/add.js.map +1 -0
  5. package/dist/cli/commands/cache-clear.d.ts +3 -0
  6. package/dist/cli/commands/cache-clear.d.ts.map +1 -0
  7. package/dist/cli/commands/cache-clear.js +24 -0
  8. package/dist/cli/commands/cache-clear.js.map +1 -0
  9. package/dist/cli/commands/full.d.ts +3 -0
  10. package/dist/cli/commands/full.d.ts.map +1 -0
  11. package/dist/cli/commands/full.js +37 -0
  12. package/dist/cli/commands/full.js.map +1 -0
  13. package/dist/cli/commands/generate.d.ts +3 -0
  14. package/dist/cli/commands/generate.d.ts.map +1 -0
  15. package/dist/cli/commands/generate.js +53 -0
  16. package/dist/cli/commands/generate.js.map +1 -0
  17. package/dist/cli/commands/init.d.ts +3 -0
  18. package/dist/cli/commands/init.d.ts.map +1 -0
  19. package/dist/cli/commands/init.js +20 -0
  20. package/dist/cli/commands/init.js.map +1 -0
  21. package/dist/cli/commands/live-scan.d.ts +3 -0
  22. package/dist/cli/commands/live-scan.d.ts.map +1 -0
  23. package/dist/cli/commands/{live-scan-command.js → live-scan.js} +8 -15
  24. package/dist/cli/commands/live-scan.js.map +1 -0
  25. package/dist/cli/commands/makeauth.d.ts +3 -0
  26. package/dist/cli/commands/makeauth.d.ts.map +1 -0
  27. package/dist/cli/commands/makeauth.js +76 -0
  28. package/dist/cli/commands/makeauth.js.map +1 -0
  29. package/dist/cli/commands/map.d.ts +3 -0
  30. package/dist/cli/commands/map.d.ts.map +1 -0
  31. package/dist/cli/commands/map.js +93 -0
  32. package/dist/cli/commands/map.js.map +1 -0
  33. package/dist/cli/commands/validate.d.ts +3 -0
  34. package/dist/cli/commands/validate.d.ts.map +1 -0
  35. package/dist/cli/commands/validate.js +43 -0
  36. package/dist/cli/commands/validate.js.map +1 -0
  37. package/dist/cli/index.js +29 -442
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/cli/types.d.ts +9 -0
  40. package/dist/cli/types.d.ts.map +1 -0
  41. package/dist/cli/types.js +7 -0
  42. package/dist/cli/types.js.map +1 -0
  43. package/dist/cli/utils.d.ts +6 -0
  44. package/dist/cli/utils.d.ts.map +1 -0
  45. package/dist/cli/utils.js +101 -0
  46. package/dist/cli/utils.js.map +1 -0
  47. package/dist/core/live-scanner/matrix-reader.d.ts.map +1 -1
  48. package/dist/core/live-scanner/matrix-reader.js +2 -40
  49. package/dist/core/live-scanner/matrix-reader.js.map +1 -1
  50. package/dist/core/live-scanner/step-replayer.d.ts.map +1 -1
  51. package/dist/core/live-scanner/step-replayer.js +7 -0
  52. package/dist/core/live-scanner/step-replayer.js.map +1 -1
  53. package/dist/core/validator/selector-validator.d.ts.map +1 -1
  54. package/dist/core/validator/selector-validator.js +2 -1
  55. package/dist/core/validator/selector-validator.js.map +1 -1
  56. package/dist/generators/scaffold-generator/index.d.ts +2 -1
  57. package/dist/generators/scaffold-generator/index.d.ts.map +1 -1
  58. package/dist/generators/scaffold-generator/index.js +21 -1
  59. package/dist/generators/scaffold-generator/index.js.map +1 -1
  60. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +2 -0
  61. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role-with-data.hbs +1 -0
  62. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role.hbs +1 -0
  63. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +2 -1
  64. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -0
  65. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +2 -1
  66. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +2 -1
  67. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -1
  68. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +2 -1
  69. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +4 -3
  70. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +2 -1
  71. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -1
  72. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -1
  73. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +2 -1
  74. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +2 -1
  75. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +2 -0
  76. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +2 -1
  77. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +4 -3
  78. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +2 -1
  79. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
  80. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +2 -1
  81. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
  82. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +1 -0
  83. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +2 -1
  84. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +2 -0
  85. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +2 -1
  86. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +4 -3
  87. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +2 -1
  88. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -0
  89. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
  90. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -1
  91. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  92. package/dist/generators/test-generator/patterns/assertion-patterns.js +95 -58
  93. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  94. package/dist/generators/test-generator/patterns/form-patterns.d.ts +0 -2
  95. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
  96. package/dist/generators/test-generator/patterns/form-patterns.js +34 -47
  97. package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
  98. package/dist/generators/test-generator/patterns/index.d.ts +3 -1
  99. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  100. package/dist/generators/test-generator/patterns/index.js +20 -3
  101. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  102. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +0 -1
  103. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
  104. package/dist/generators/test-generator/patterns/interaction-patterns.js +44 -85
  105. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
  106. package/dist/generators/test-generator/patterns/legacy-patterns.d.ts +7 -0
  107. package/dist/generators/test-generator/patterns/legacy-patterns.d.ts.map +1 -0
  108. package/dist/generators/test-generator/patterns/legacy-patterns.js +98 -0
  109. package/dist/generators/test-generator/patterns/legacy-patterns.js.map +1 -0
  110. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts +0 -2
  111. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -1
  112. package/dist/generators/test-generator/patterns/navigation-patterns.js +14 -42
  113. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
  114. package/dist/generators/test-generator/patterns/setup-patterns.d.ts +0 -1
  115. package/dist/generators/test-generator/patterns/setup-patterns.d.ts.map +1 -1
  116. package/dist/generators/test-generator/patterns/setup-patterns.js +23 -35
  117. package/dist/generators/test-generator/patterns/setup-patterns.js.map +1 -1
  118. package/dist/generators/test-generator/patterns/types.d.ts +18 -3
  119. package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
  120. package/dist/generators/test-generator/step-mapper.d.ts +0 -15
  121. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  122. package/dist/generators/test-generator/step-mapper.js +4 -106
  123. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  124. package/dist/{executor/test-generator.d.ts → generators/test-generator/types.d.ts} +4 -25
  125. package/dist/generators/test-generator/types.d.ts.map +1 -0
  126. package/dist/generators/test-generator/types.js +106 -0
  127. package/dist/generators/test-generator/types.js.map +1 -0
  128. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  129. package/dist/generators/test-generator/utils/data-resolver.js +8 -17
  130. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  131. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  132. package/dist/generators/test-generator/utils/selector-resolver.js +10 -18
  133. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  134. package/dist/orchestrator/cache-manager.d.ts +1 -23
  135. package/dist/orchestrator/cache-manager.d.ts.map +1 -1
  136. package/dist/orchestrator/cache-manager.js +1 -87
  137. package/dist/orchestrator/cache-manager.js.map +1 -1
  138. package/dist/orchestrator/pipeline.d.ts +11 -28
  139. package/dist/orchestrator/pipeline.d.ts.map +1 -1
  140. package/dist/orchestrator/pipeline.js +52 -371
  141. package/dist/orchestrator/pipeline.js.map +1 -1
  142. package/dist/orchestrator/reporter.d.ts +1 -1
  143. package/dist/orchestrator/reporter.d.ts.map +1 -1
  144. package/dist/orchestrator/screen-manager.js +1 -1
  145. package/dist/orchestrator/screen-manager.js.map +1 -1
  146. package/dist/utils/feature-finder.d.ts +9 -0
  147. package/dist/utils/feature-finder.d.ts.map +1 -0
  148. package/dist/utils/feature-finder.js +67 -0
  149. package/dist/utils/feature-finder.js.map +1 -0
  150. package/dist/utils/screen-paths.d.ts +10 -0
  151. package/dist/utils/screen-paths.d.ts.map +1 -0
  152. package/dist/utils/screen-paths.js +73 -0
  153. package/dist/utils/screen-paths.js.map +1 -0
  154. package/dist/utils/selector-loader.d.ts +6 -0
  155. package/dist/utils/selector-loader.d.ts.map +1 -0
  156. package/dist/utils/selector-loader.js +20 -0
  157. package/dist/utils/selector-loader.js.map +1 -0
  158. package/dist/utils/selector-types.d.ts +7 -0
  159. package/dist/utils/selector-types.d.ts.map +1 -0
  160. package/dist/utils/selector-types.js +19 -0
  161. package/dist/utils/selector-types.js.map +1 -0
  162. package/dist/utils/test-data-loader.d.ts +6 -0
  163. package/dist/utils/test-data-loader.d.ts.map +1 -0
  164. package/dist/utils/test-data-loader.js +20 -0
  165. package/dist/utils/test-data-loader.js.map +1 -0
  166. package/dist/utils/yaml-io.d.ts +14 -0
  167. package/dist/utils/yaml-io.d.ts.map +1 -0
  168. package/dist/utils/yaml-io.js +72 -0
  169. package/dist/utils/yaml-io.js.map +1 -0
  170. package/package.json +1 -1
  171. package/src/cli/commands/add.ts +25 -0
  172. package/src/cli/commands/cache-clear.ts +22 -0
  173. package/src/cli/commands/full.ts +35 -0
  174. package/src/cli/commands/generate.ts +55 -0
  175. package/src/cli/commands/init.ts +17 -0
  176. package/src/cli/commands/{live-scan-command.ts → live-scan.ts} +8 -17
  177. package/src/cli/commands/makeauth.ts +77 -0
  178. package/src/cli/commands/map.ts +97 -0
  179. package/src/cli/commands/validate.ts +43 -0
  180. package/src/cli/index.ts +32 -473
  181. package/src/cli/types.ts +9 -0
  182. package/src/cli/utils.ts +106 -0
  183. package/src/core/live-scanner/matrix-reader.ts +2 -8
  184. package/src/core/live-scanner/step-replayer.ts +7 -0
  185. package/src/core/validator/selector-validator.ts +3 -2
  186. package/src/generators/scaffold-generator/index.ts +23 -2
  187. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +2 -0
  188. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role-with-data.hbs +1 -0
  189. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-role.hbs +1 -0
  190. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +2 -1
  191. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -0
  192. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +2 -1
  193. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +2 -1
  194. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -1
  195. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-filter-assertion.hbs +2 -1
  196. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +4 -3
  197. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +2 -1
  198. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -1
  199. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -1
  200. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +2 -1
  201. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +2 -1
  202. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-dialog-heading-assertion.hbs +2 -0
  203. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-filter-assertion.hbs +2 -1
  204. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +4 -3
  205. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +2 -1
  206. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
  207. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/list-item-count-assertion.hbs +2 -1
  208. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
  209. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/page-assertion.hbs +1 -0
  210. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +2 -1
  211. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-dialog-heading-assertion.hbs +2 -0
  212. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-locator-variable-assertion.hbs +2 -1
  213. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +4 -3
  214. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +2 -1
  215. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -0
  216. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -1
  217. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element-with-text.hbs +1 -1
  218. package/src/generators/test-generator/patterns/assertion-patterns.ts +102 -62
  219. package/src/generators/test-generator/patterns/form-patterns.ts +38 -60
  220. package/src/generators/test-generator/patterns/index.ts +22 -4
  221. package/src/generators/test-generator/patterns/interaction-patterns.ts +47 -107
  222. package/src/generators/test-generator/patterns/legacy-patterns.ts +104 -0
  223. package/src/generators/test-generator/patterns/navigation-patterns.ts +27 -69
  224. package/src/generators/test-generator/patterns/setup-patterns.ts +23 -41
  225. package/src/generators/test-generator/patterns/types.ts +26 -9
  226. package/src/generators/test-generator/step-mapper.ts +4 -124
  227. package/src/generators/test-generator/types.ts +131 -0
  228. package/src/generators/test-generator/utils/data-resolver.ts +8 -13
  229. package/src/generators/test-generator/utils/selector-resolver.ts +15 -17
  230. package/src/orchestrator/cache-manager.ts +1 -107
  231. package/src/orchestrator/pipeline.ts +58 -433
  232. package/src/orchestrator/reporter.ts +1 -1
  233. package/src/orchestrator/screen-manager.ts +1 -1
  234. package/src/utils/feature-finder.ts +33 -0
  235. package/src/utils/screen-paths.ts +37 -0
  236. package/src/utils/selector-loader.ts +23 -0
  237. package/src/utils/selector-types.ts +17 -0
  238. package/src/utils/test-data-loader.ts +23 -0
  239. package/src/utils/yaml-io.ts +33 -0
  240. package/dist/cli/commands/auto-tag-command.d.ts +0 -8
  241. package/dist/cli/commands/auto-tag-command.d.ts.map +0 -1
  242. package/dist/cli/commands/auto-tag-command.js +0 -104
  243. package/dist/cli/commands/auto-tag-command.js.map +0 -1
  244. package/dist/cli/commands/live-scan-command.d.ts +0 -9
  245. package/dist/cli/commands/live-scan-command.d.ts.map +0 -1
  246. package/dist/cli/commands/live-scan-command.js.map +0 -1
  247. package/dist/executor/playwright/playwright-generator.d.ts +0 -33
  248. package/dist/executor/playwright/playwright-generator.d.ts.map +0 -1
  249. package/dist/executor/playwright/playwright-generator.js +0 -136
  250. package/dist/executor/playwright/playwright-generator.js.map +0 -1
  251. package/dist/executor/test-generator.d.ts.map +0 -1
  252. package/dist/executor/test-generator.js +0 -30
  253. package/dist/executor/test-generator.js.map +0 -1
  254. package/dist/generators/cli.d.ts +0 -7
  255. package/dist/generators/cli.d.ts.map +0 -1
  256. package/dist/generators/cli.js +0 -570
  257. package/dist/generators/cli.js.map +0 -1
  258. package/dist/input/cli-adapter.d.ts +0 -75
  259. package/dist/input/cli-adapter.d.ts.map +0 -1
  260. package/dist/input/cli-adapter.js +0 -218
  261. package/dist/input/cli-adapter.js.map +0 -1
  262. package/dist/input/config-adapter.d.ts +0 -25
  263. package/dist/input/config-adapter.d.ts.map +0 -1
  264. package/dist/input/config-adapter.js +0 -70
  265. package/dist/input/config-adapter.js.map +0 -1
  266. package/dist/input/input-adapter.d.ts +0 -28
  267. package/dist/input/input-adapter.d.ts.map +0 -1
  268. package/dist/input/input-adapter.js +0 -17
  269. package/dist/input/input-adapter.js.map +0 -1
  270. package/dist/input/vscode-adapter.d.ts +0 -62
  271. package/dist/input/vscode-adapter.d.ts.map +0 -1
  272. package/dist/input/vscode-adapter.js +0 -64
  273. package/dist/input/vscode-adapter.js.map +0 -1
  274. package/dist/tools/auto-tagger.d.ts +0 -107
  275. package/dist/tools/auto-tagger.d.ts.map +0 -1
  276. package/dist/tools/auto-tagger.js +0 -502
  277. package/dist/tools/auto-tagger.js.map +0 -1
  278. package/src/cli/commands/auto-tag-command.ts +0 -80
  279. package/src/executor/playwright/playwright-generator.ts +0 -125
  280. package/src/executor/test-generator.ts +0 -90
  281. package/src/generators/cli.ts +0 -640
  282. package/src/input/cli-adapter.ts +0 -233
  283. package/src/input/config-adapter.ts +0 -71
  284. package/src/input/input-adapter.ts +0 -32
  285. package/src/input/vscode-adapter.ts +0 -90
  286. package/src/tools/auto-tagger.ts +0 -572
@@ -1,572 +0,0 @@
1
- /**
2
- * Auto-Tagging Tool - v3.1
3
- *
4
- * Automatically injects stable data-testid attributes into React/Vue/HTML source code
5
- *
6
- * Key Features:
7
- * 1. Idempotent (skips if data-testid already exists)
8
- * 2. Preserves code formatting (Prettier/ESLint compatible)
9
- * 3. Generates stable, semantic IDs (screen-element-type)
10
- * 4. Supports React, Vue, HTML
11
- * 5. Dry-run mode for preview
12
- *
13
- * Philosophy: Proactive solution - add test IDs BEFORE you need them
14
- */
15
-
16
- import * as parser from '@babel/parser';
17
- import traverse, { NodePath } from '@babel/traverse';
18
- import generate from '@babel/generator';
19
- import * as t from '@babel/types';
20
- import * as fs from 'fs';
21
- import * as path from 'path';
22
-
23
- // ============================================================================
24
- // TYPES
25
- // ============================================================================
26
-
27
- export interface AutoTagConfig {
28
- sourceRoot: string;
29
- projectRoot: string;
30
- screenName?: string; // Specific screen or all
31
- dryRun: boolean; // Preview changes without writing
32
- force: boolean; // Overwrite existing data-testid
33
- verbose: boolean;
34
- preserveFormatting: boolean; // Use Prettier if available
35
- }
36
-
37
- export interface TaggingResult {
38
- filePath: string;
39
- totalElements: number;
40
- taggedElements: number;
41
- skippedElements: number;
42
- changes: ElementChange[];
43
- modified: boolean;
44
- }
45
-
46
- export interface ElementChange {
47
- line: number;
48
- tag: string;
49
- generatedId: string;
50
- action: 'added' | 'skipped' | 'overwritten';
51
- reason?: string;
52
- }
53
-
54
- export interface TaggingStats {
55
- totalFiles: number;
56
- modifiedFiles: number;
57
- totalElements: number;
58
- taggedElements: number;
59
- skippedElements: number;
60
- processingTime: number;
61
- }
62
-
63
- // ============================================================================
64
- // TEST ID GENERATOR
65
- // ============================================================================
66
-
67
- class TestIDGenerator {
68
- private screenName: string;
69
- private usedIds = new Set<string>();
70
-
71
- constructor(screenName: string) {
72
- this.screenName = this.normalize(screenName);
73
- }
74
-
75
- /**
76
- * Generate stable, semantic test ID
77
- *
78
- * Format: {screen}-{element}-{type}
79
- * Examples:
80
- * - login-email-input
81
- * - login-password-input
82
- * - login-submit-btn
83
- * - dashboard-user-menu-btn
84
- */
85
- generate(element: any, index: number): string {
86
- const tag = element.tag?.toLowerCase() || 'element';
87
- const props = element.props || {};
88
-
89
- let elementName = '';
90
-
91
- // Strategy 1: Use existing name/id/placeholder
92
- if (props.name) {
93
- elementName = this.normalize(props.name);
94
- } else if (props.id) {
95
- elementName = this.normalize(props.id);
96
- } else if (props.placeholder) {
97
- elementName = this.normalize(props.placeholder);
98
- } else if (element.text && ['button', 'a'].includes(tag)) {
99
- elementName = this.normalize(element.text);
100
- } else if (props['aria-label']) {
101
- elementName = this.normalize(props['aria-label']);
102
- }
103
-
104
- // Strategy 2: Use type-based naming
105
- if (!elementName && props.type) {
106
- elementName = this.normalize(props.type);
107
- }
108
-
109
- // Strategy 3: Fallback to index
110
- if (!elementName) {
111
- elementName = `${tag}-${index}`;
112
- }
113
-
114
- // Add suffix based on tag
115
- const suffix = this.getSuffix(tag, props.type);
116
-
117
- // Combine: screen-element-suffix
118
- const baseId = `${this.screenName}-${elementName}${suffix}`;
119
-
120
- // Ensure uniqueness
121
- let finalId = baseId;
122
- let counter = 1;
123
- while (this.usedIds.has(finalId)) {
124
- finalId = `${baseId}-${counter}`;
125
- counter++;
126
- }
127
-
128
- this.usedIds.add(finalId);
129
- return finalId;
130
- }
131
-
132
- private getSuffix(tag: string, type?: string): string {
133
- if (tag === 'input') {
134
- if (type === 'submit') return '-btn';
135
- return '-input';
136
- }
137
-
138
- const suffixMap: Record<string, string> = {
139
- 'button': '-btn',
140
- 'a': '-link',
141
- 'select': '-select',
142
- 'textarea': '-textarea',
143
- 'form': '-form',
144
- };
145
-
146
- return suffixMap[tag] || '';
147
- }
148
-
149
- private normalize(str: string): string {
150
- return str
151
- .toLowerCase()
152
- .replace(/[^a-z0-9]+/g, '-')
153
- .replace(/^-+|-+$/g, '');
154
- }
155
-
156
- reset() {
157
- this.usedIds.clear();
158
- }
159
- }
160
-
161
- // ============================================================================
162
- // AST ELEMENT EXTRACTOR
163
- // ============================================================================
164
-
165
- class ElementExtractor {
166
- /**
167
- * Extract element data from JSX node
168
- */
169
- extract(node: t.JSXElement): any {
170
- const openingElement = node.openingElement;
171
- const tag = this.getTagName(openingElement.name);
172
-
173
- // Extract props
174
- const props: Record<string, any> = {};
175
- for (const attr of openingElement.attributes) {
176
- if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
177
- const name = attr.name.name;
178
- const value = this.getAttributeValue(attr.value);
179
- props[name] = value;
180
- }
181
- }
182
-
183
- // Extract text content
184
- const text = this.extractTextContent(node);
185
-
186
- return { tag, props, text, node, openingElement };
187
- }
188
-
189
- private getTagName(name: any): string {
190
- if (t.isJSXIdentifier(name)) {
191
- return name.name.toLowerCase();
192
- }
193
- if (t.isJSXMemberExpression(name)) {
194
- // Handle <Form.Input> → "input"
195
- return this.getTagName(name.property);
196
- }
197
- return 'unknown';
198
- }
199
-
200
- private getAttributeValue(value: any): any {
201
- if (!value) return true; // Boolean attribute
202
- if (t.isStringLiteral(value)) return value.value;
203
- if (t.isJSXExpressionContainer(value)) {
204
- if (t.isStringLiteral(value.expression)) {
205
- return value.expression.value;
206
- }
207
- if (t.isBooleanLiteral(value.expression)) {
208
- return value.expression.value;
209
- }
210
- }
211
- return undefined;
212
- }
213
-
214
- private extractTextContent(node: t.JSXElement): string | null {
215
- const children = node.children || [];
216
-
217
- for (const child of children) {
218
- if (t.isJSXText(child)) {
219
- const text = child.value.trim();
220
- if (text) return text;
221
- }
222
- if (t.isJSXExpressionContainer(child)) {
223
- if (t.isStringLiteral(child.expression)) {
224
- return child.expression.value;
225
- }
226
- }
227
- }
228
-
229
- return null;
230
- }
231
- }
232
-
233
- // ============================================================================
234
- // AUTO TAGGER (Main Class)
235
- // ============================================================================
236
-
237
- export class AutoTagger {
238
- private config: AutoTagConfig;
239
- private extractor = new ElementExtractor();
240
- private stats: TaggingStats = {
241
- totalFiles: 0,
242
- modifiedFiles: 0,
243
- totalElements: 0,
244
- taggedElements: 0,
245
- skippedElements: 0,
246
- processingTime: 0,
247
- };
248
-
249
- // Interactive elements that should have data-testid
250
- private readonly INTERACTIVE_TAGS = ['input', 'button', 'a', 'select', 'textarea', 'form'];
251
-
252
- constructor(config: AutoTagConfig) {
253
- this.config = config;
254
- }
255
-
256
- /**
257
- * Tag a specific screen or all screens
258
- */
259
- async tag(): Promise<TaggingStats> {
260
- const startTime = Date.now();
261
-
262
- if (this.config.screenName) {
263
- await this.tagScreen(this.config.screenName);
264
- } else {
265
- await this.tagAllScreens();
266
- }
267
-
268
- this.stats.processingTime = Date.now() - startTime;
269
- return this.stats;
270
- }
271
-
272
- /**
273
- * Tag a specific screen
274
- */
275
- private async tagScreen(screenName: string): Promise<void> {
276
- const files = this.findSourceFiles(screenName);
277
-
278
- if (files.length === 0) {
279
- console.log(`⚠️ No source files found for screen: ${screenName}`);
280
- return;
281
- }
282
-
283
- console.log(`📂 Found ${files.length} files for screen: ${screenName}\n`);
284
-
285
- for (const file of files) {
286
- await this.tagFile(file, screenName);
287
- }
288
- }
289
-
290
- /**
291
- * Tag all screens (scan entire source root)
292
- */
293
- private async tagAllScreens(): Promise<void> {
294
- console.log('📂 Scanning all source files...\n');
295
-
296
- const allFiles = this.findAllSourceFiles(this.config.sourceRoot);
297
- console.log(`Found ${allFiles.length} files\n`);
298
-
299
- for (const file of allFiles) {
300
- const screenName = this.inferScreenName(file);
301
- await this.tagFile(file, screenName);
302
- }
303
- }
304
-
305
- /**
306
- * Tag a single file
307
- */
308
- private async tagFile(filePath: string, screenName: string): Promise<TaggingResult> {
309
- this.stats.totalFiles++;
310
-
311
- const relativePath = path.relative(this.config.projectRoot, filePath);
312
- console.log(`📄 Processing: ${relativePath}`);
313
-
314
- // Read file
315
- const code = fs.readFileSync(filePath, 'utf-8');
316
-
317
- // Parse with Babel
318
- let ast;
319
- try {
320
- ast = parser.parse(code, {
321
- sourceType: 'module',
322
- plugins: ['jsx', 'typescript'],
323
- });
324
- } catch (error) {
325
- console.log(` ⚠️ Parse error, skipping\n`);
326
- return this.createEmptyResult(filePath);
327
- }
328
-
329
- // Tag elements in AST
330
- const result = await this.tagAST(ast, filePath, screenName);
331
-
332
- // Write back if modified and not dry-run
333
- if (result.modified && !this.config.dryRun) {
334
- const newCode = generate(ast, {
335
- retainLines: true,
336
- comments: true,
337
- }).code;
338
-
339
- fs.writeFileSync(filePath, newCode, 'utf-8');
340
- this.stats.modifiedFiles++;
341
-
342
- console.log(` ✓ Modified (${result.taggedElements} elements tagged)\n`);
343
- } else if (result.modified && this.config.dryRun) {
344
- console.log(` 🔍 Would modify (${result.taggedElements} elements)\n`);
345
- } else {
346
- console.log(` ○ No changes needed\n`);
347
- }
348
-
349
- return result;
350
- }
351
-
352
- /**
353
- * Tag elements in AST
354
- */
355
- private async tagAST(ast: any, filePath: string, screenName: string): Promise<TaggingResult> {
356
- const idGenerator = new TestIDGenerator(screenName);
357
- const changes: ElementChange[] = [];
358
- let totalElements = 0;
359
- let taggedElements = 0;
360
- let skippedElements = 0;
361
- let modified = false;
362
-
363
- let elementIndex = 0;
364
-
365
- traverse(ast, {
366
- JSXElement: (path: NodePath<t.JSXElement>) => {
367
- const node = path.node;
368
- const element = this.extractor.extract(node);
369
-
370
- // Only process interactive elements
371
- if (!this.shouldTag(element)) {
372
- return;
373
- }
374
-
375
- totalElements++;
376
- elementIndex++;
377
-
378
- // Check if already has data-testid
379
- const existingTestId = this.findAttribute(element.openingElement, 'data-testid');
380
-
381
- if (existingTestId && !this.config.force) {
382
- skippedElements++;
383
- changes.push({
384
- line: node.loc?.start.line || 0,
385
- tag: element.tag,
386
- generatedId: existingTestId,
387
- action: 'skipped',
388
- reason: 'Already has data-testid',
389
- });
390
- return;
391
- }
392
-
393
- // Generate test ID
394
- const testId = idGenerator.generate(element, elementIndex);
395
-
396
- // Add or replace data-testid attribute
397
- if (existingTestId && this.config.force) {
398
- // Overwrite
399
- this.setAttribute(element.openingElement, 'data-testid', testId);
400
- taggedElements++;
401
- modified = true;
402
- changes.push({
403
- line: node.loc?.start.line || 0,
404
- tag: element.tag,
405
- generatedId: testId,
406
- action: 'overwritten',
407
- reason: `Replaced ${existingTestId}`,
408
- });
409
- } else {
410
- // Add new
411
- this.addAttribute(element.openingElement, 'data-testid', testId);
412
- taggedElements++;
413
- modified = true;
414
- changes.push({
415
- line: node.loc?.start.line || 0,
416
- tag: element.tag,
417
- generatedId: testId,
418
- action: 'added',
419
- });
420
- }
421
- },
422
- });
423
-
424
- this.stats.totalElements += totalElements;
425
- this.stats.taggedElements += taggedElements;
426
- this.stats.skippedElements += skippedElements;
427
-
428
- return {
429
- filePath,
430
- totalElements,
431
- taggedElements,
432
- skippedElements,
433
- changes,
434
- modified,
435
- };
436
- }
437
-
438
- /**
439
- * Determine if element should be tagged
440
- */
441
- private shouldTag(element: any): boolean {
442
- const tag = element.tag?.toLowerCase();
443
- return this.INTERACTIVE_TAGS.includes(tag);
444
- }
445
-
446
- /**
447
- * Find existing attribute value
448
- */
449
- private findAttribute(openingElement: t.JSXOpeningElement, name: string): string | null {
450
- for (const attr of openingElement.attributes) {
451
- if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === name) {
452
- if (t.isStringLiteral(attr.value)) {
453
- return attr.value.value;
454
- }
455
- }
456
- }
457
- return null;
458
- }
459
-
460
- /**
461
- * Set attribute value (replace if exists)
462
- */
463
- private setAttribute(openingElement: t.JSXOpeningElement, name: string, value: string): void {
464
- for (const attr of openingElement.attributes) {
465
- if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === name) {
466
- attr.value = t.stringLiteral(value);
467
- return;
468
- }
469
- }
470
- }
471
-
472
- /**
473
- * Add new attribute
474
- */
475
- private addAttribute(openingElement: t.JSXOpeningElement, name: string, value: string): void {
476
- const newAttr = t.jsxAttribute(
477
- t.jsxIdentifier(name),
478
- t.stringLiteral(value)
479
- );
480
- openingElement.attributes.push(newAttr);
481
- }
482
-
483
- /**
484
- * Find source files for a screen
485
- */
486
- private findSourceFiles(screenName: string): string[] {
487
- const patterns = [
488
- path.join(this.config.sourceRoot, `${screenName}/page.tsx`),
489
- path.join(this.config.sourceRoot, `${screenName}/index.tsx`),
490
- path.join(this.config.sourceRoot, `${screenName}.tsx`),
491
- path.join(this.config.sourceRoot, `${screenName}/page.js`),
492
- path.join(this.config.sourceRoot, `${screenName}/index.js`),
493
- path.join(this.config.sourceRoot, `${screenName}.js`),
494
- ];
495
-
496
- const found: string[] = [];
497
- for (const pattern of patterns) {
498
- if (fs.existsSync(pattern)) {
499
- found.push(pattern);
500
- }
501
- }
502
-
503
- return found;
504
- }
505
-
506
- /**
507
- * Find all source files recursively
508
- */
509
- private findAllSourceFiles(dir: string): string[] {
510
- const files: string[] = [];
511
-
512
- const scan = (currentDir: string) => {
513
- const entries = fs.readdirSync(currentDir, { withFileTypes: true });
514
-
515
- for (const entry of entries) {
516
- const fullPath = path.join(currentDir, entry.name);
517
-
518
- // Skip node_modules, .git, etc.
519
- if (entry.name.startsWith('.') || entry.name === 'node_modules') {
520
- continue;
521
- }
522
-
523
- if (entry.isDirectory()) {
524
- scan(fullPath);
525
- } else if (entry.isFile()) {
526
- // Only process .tsx, .jsx, .ts, .js files
527
- if (/\.(tsx|jsx|ts|js)$/.test(entry.name)) {
528
- files.push(fullPath);
529
- }
530
- }
531
- }
532
- };
533
-
534
- scan(dir);
535
- return files;
536
- }
537
-
538
- /**
539
- * Infer screen name from file path
540
- */
541
- private inferScreenName(filePath: string): string {
542
- const relativePath = path.relative(this.config.sourceRoot, filePath);
543
- const parts = relativePath.split(path.sep);
544
-
545
- // Use first directory as screen name
546
- if (parts.length > 1) {
547
- return parts[0];
548
- }
549
-
550
- // Use filename without extension
551
- const fileName = path.basename(filePath, path.extname(filePath));
552
- return fileName === 'page' || fileName === 'index' ? 'app' : fileName;
553
- }
554
-
555
- private createEmptyResult(filePath: string): TaggingResult {
556
- return {
557
- filePath,
558
- totalElements: 0,
559
- taggedElements: 0,
560
- skippedElements: 0,
561
- changes: [],
562
- modified: false,
563
- };
564
- }
565
-
566
- /**
567
- * Get statistics
568
- */
569
- getStats(): TaggingStats {
570
- return { ...this.stats };
571
- }
572
- }