create-appraisejs 0.2.0-alpha.6 → 0.3.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (485) hide show
  1. package/README.md +12 -1
  2. package/package.json +1 -1
  3. package/templates/default/.appraise-template-meta.json +2 -2
  4. package/templates/default/.env.example +2 -2
  5. package/templates/default/README.md +57 -53
  6. package/templates/default/automation/steps/actions/click.step.ts +58 -58
  7. package/templates/default/automation/steps/actions/hover.step.ts +27 -27
  8. package/templates/default/automation/steps/actions/navigation.step.ts +70 -70
  9. package/templates/default/automation/steps/actions/random_data.step.ts +142 -142
  10. package/templates/default/automation/steps/actions/store.step.ts +86 -86
  11. package/templates/default/automation/steps/actions/wait.step.ts +110 -90
  12. package/templates/default/automation/steps/validations/active_state_assertion.step.ts +30 -30
  13. package/templates/default/automation/steps/validations/navigation_assertion.step.ts +22 -22
  14. package/templates/default/automation/steps/validations/text_assertion.step.ts +107 -107
  15. package/templates/default/automation/steps/validations/visibility_assertion.step.ts +26 -26
  16. package/templates/default/components.json +24 -24
  17. package/templates/default/cucumber.mjs +16 -16
  18. package/templates/default/eslint.config.mjs +20 -16
  19. package/templates/default/next-env.d.ts +6 -6
  20. package/templates/default/next.config.ts +20 -11
  21. package/templates/default/package-lock.json +1775 -74
  22. package/templates/default/package.json +8 -1
  23. package/templates/default/packages/cucumber-runtime/package.json +13 -13
  24. package/templates/default/packages/cucumber-runtime/src/cache.util.ts +93 -93
  25. package/templates/default/packages/cucumber-runtime/src/cli.ts +68 -68
  26. package/templates/default/packages/cucumber-runtime/src/environment.util.ts +21 -21
  27. package/templates/default/packages/cucumber-runtime/src/executor.ts +32 -32
  28. package/templates/default/packages/cucumber-runtime/src/index.ts +17 -17
  29. package/templates/default/packages/cucumber-runtime/src/locator.util.ts +234 -234
  30. package/templates/default/packages/cucumber-runtime/src/parameter-types.ts +7 -7
  31. package/templates/default/packages/cucumber-runtime/src/random-data.util.ts +35 -35
  32. package/templates/default/packages/cucumber-runtime/src/types.ts +13 -13
  33. package/templates/default/packages/cucumber-runtime/src/world.ts +44 -44
  34. package/templates/default/packages/cucumber-runtime/tsconfig.json +11 -11
  35. package/templates/default/postcss.config.mjs +8 -8
  36. package/templates/default/prisma/dev.db +0 -0
  37. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  38. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  39. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  40. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  41. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  42. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  43. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  44. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  45. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  46. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  47. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  48. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  49. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  50. package/templates/default/prisma/migrations/20260313093000_add_report_step_screenshot_path/migration.sql +1 -1
  51. package/templates/default/scripts/build-step-registry.ts +33 -0
  52. package/templates/default/scripts/install-playwright.ts +17 -8
  53. package/templates/default/scripts/install-template-step.ts +128 -0
  54. package/templates/default/scripts/lib/filename-utils.test.ts +24 -0
  55. package/templates/default/scripts/lib/filename-utils.ts +24 -0
  56. package/templates/default/scripts/lib/jsdoc-parser.test.ts +63 -0
  57. package/templates/default/scripts/lib/jsdoc-parser.ts +88 -0
  58. package/templates/default/scripts/lib/step-file-parser.test.ts +71 -0
  59. package/templates/default/scripts/lib/step-file-parser.ts +315 -0
  60. package/templates/default/scripts/lib/step-matcher.test.ts +86 -0
  61. package/templates/default/scripts/lib/step-matcher.ts +120 -0
  62. package/templates/default/scripts/lib/sync-script-runner.ts +23 -0
  63. package/templates/default/scripts/lib/sync-summary.ts +29 -0
  64. package/templates/default/scripts/lib/tag-parsing.test.ts +20 -0
  65. package/templates/default/scripts/lib/tag-parsing.ts +10 -0
  66. package/templates/default/scripts/lib/template-step-installer.test.ts +225 -0
  67. package/templates/default/scripts/lib/template-step-installer.ts +404 -0
  68. package/templates/default/scripts/lib/template-step-registry.test.ts +118 -0
  69. package/templates/default/scripts/lib/template-step-registry.ts +137 -0
  70. package/templates/default/scripts/protect-seeded-files.ts +51 -38
  71. package/templates/default/scripts/regenerate-features.ts +98 -94
  72. package/templates/default/scripts/run-vitest.ts +59 -0
  73. package/templates/default/scripts/setup-env.ts +26 -19
  74. package/templates/default/scripts/sync-all.ts +44 -54
  75. package/templates/default/scripts/sync-appraise-base-template.ts +44 -16
  76. package/templates/default/scripts/sync-environments.ts +22 -66
  77. package/templates/default/scripts/sync-locator-groups.ts +358 -410
  78. package/templates/default/scripts/sync-locators.ts +348 -398
  79. package/templates/default/scripts/sync-modules.ts +302 -341
  80. package/templates/default/scripts/sync-tags.ts +29 -65
  81. package/templates/default/scripts/sync-template-step-groups.ts +24 -182
  82. package/templates/default/scripts/sync-template-steps.ts +36 -493
  83. package/templates/default/scripts/sync-test-cases.ts +296 -539
  84. package/templates/default/scripts/sync-test-suites.ts +32 -79
  85. package/templates/default/src/actions/dashboard/dashboard-actions.ts +70 -241
  86. package/templates/default/src/actions/environments/environment-actions.ts +102 -188
  87. package/templates/default/src/actions/locator/locator-actions.ts +77 -490
  88. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +34 -212
  89. package/templates/default/src/actions/locator-picker/locator-picker-actions.test.ts +81 -0
  90. package/templates/default/src/actions/locator-picker/locator-picker-actions.ts +20 -161
  91. package/templates/default/src/actions/modules/module-actions.ts +99 -135
  92. package/templates/default/src/actions/reports/report-actions.ts +28 -565
  93. package/templates/default/src/actions/settings/sync-actions.test.ts +58 -0
  94. package/templates/default/src/actions/tags/tag-actions.ts +99 -107
  95. package/templates/default/src/actions/template-step/template-step-actions.ts +33 -194
  96. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +35 -92
  97. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +98 -238
  98. package/templates/default/src/actions/test-case/test-case-actions.ts +108 -356
  99. package/templates/default/src/actions/test-run/test-run-actions.ts +74 -1081
  100. package/templates/default/src/actions/test-suite/test-suite-actions.ts +35 -202
  101. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
  102. package/templates/default/src/app/(base)/environments/environment-form.test.tsx +92 -0
  103. package/templates/default/src/app/(base)/environments/environment-form.tsx +228 -219
  104. package/templates/default/src/app/(base)/environments/environment-helpers.ts +58 -0
  105. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
  106. package/templates/default/src/app/(base)/environments/environment-table.tsx +25 -24
  107. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +49 -46
  108. package/templates/default/src/app/(base)/environments/page.tsx +59 -59
  109. package/templates/default/src/app/(base)/layout.tsx +10 -10
  110. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -44
  111. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -215
  112. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -77
  113. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -28
  114. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -46
  115. package/templates/default/src/app/(base)/locators/create/create-locator-workspace-helpers.test.ts +71 -0
  116. package/templates/default/src/app/(base)/locators/create/create-locator-workspace-helpers.ts +333 -0
  117. package/templates/default/src/app/(base)/locators/create/create-locator-workspace.test.tsx +125 -0
  118. package/templates/default/src/app/(base)/locators/create/create-locator-workspace.tsx +56 -274
  119. package/templates/default/src/app/(base)/locators/create/page.tsx +8 -4
  120. package/templates/default/src/app/(base)/locators/create/use-locator-workspace.ts +183 -0
  121. package/templates/default/src/app/(base)/locators/locator-helpers.test.ts +28 -0
  122. package/templates/default/src/app/(base)/locators/locator-helpers.ts +59 -0
  123. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +74 -73
  124. package/templates/default/src/app/(base)/locators/locator-table.tsx +30 -28
  125. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +20 -8
  126. package/templates/default/src/app/(base)/locators/page.tsx +3 -6
  127. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +67 -66
  128. package/templates/default/src/app/(base)/modules/create/page.tsx +33 -34
  129. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +43 -46
  130. package/templates/default/src/app/(base)/modules/module-form.test.tsx +84 -0
  131. package/templates/default/src/app/(base)/modules/module-form.tsx +159 -126
  132. package/templates/default/src/app/(base)/modules/module-helpers.ts +64 -0
  133. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +81 -85
  134. package/templates/default/src/app/(base)/modules/module-table.tsx +25 -24
  135. package/templates/default/src/app/(base)/modules/page.tsx +59 -59
  136. package/templates/default/src/app/(base)/reports/[id]/page.tsx +20 -260
  137. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
  138. package/templates/default/src/app/(base)/reports/feature-chart.tsx +79 -78
  139. package/templates/default/src/app/(base)/reports/overview-chart.tsx +49 -49
  140. package/templates/default/src/app/(base)/reports/page.tsx +98 -98
  141. package/templates/default/src/app/(base)/reports/report-detail-helpers.test.ts +109 -0
  142. package/templates/default/src/app/(base)/reports/report-detail-helpers.ts +247 -0
  143. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +78 -78
  144. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -189
  145. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -72
  146. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +40 -40
  147. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -115
  148. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -27
  149. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -42
  150. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -79
  151. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -27
  152. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +58 -58
  153. package/templates/default/src/app/(base)/settings/settings-sync-panel-helpers.test.tsx +40 -0
  154. package/templates/default/src/app/(base)/settings/settings-sync-panel-helpers.tsx +110 -0
  155. package/templates/default/src/app/(base)/settings/settings-sync-panel.test.tsx +127 -0
  156. package/templates/default/src/app/(base)/settings/settings-sync-panel.tsx +19 -134
  157. package/templates/default/src/app/(base)/settings/use-settings-sync.ts +66 -0
  158. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -39
  159. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -50
  160. package/templates/default/src/app/(base)/tags/page.tsx +58 -58
  161. package/templates/default/src/app/(base)/tags/tag-form-helpers.ts +13 -0
  162. package/templates/default/src/app/(base)/tags/tag-form.test.tsx +83 -0
  163. package/templates/default/src/app/(base)/tags/tag-form.tsx +143 -147
  164. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -63
  165. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -29
  166. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -28
  167. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +43 -45
  168. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -60
  169. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.test.tsx +82 -0
  170. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +181 -167
  171. package/templates/default/src/app/(base)/template-step-groups/template-step-group-helpers.ts +54 -0
  172. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -89
  173. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +34 -32
  174. package/templates/default/src/app/(base)/template-steps/create/page.tsx +40 -37
  175. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +54 -49
  176. package/templates/default/src/app/(base)/template-steps/page.tsx +59 -58
  177. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +233 -213
  178. package/templates/default/src/app/(base)/template-steps/template-step-form.test.tsx +132 -0
  179. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +342 -384
  180. package/templates/default/src/app/(base)/template-steps/template-step-helpers.test.ts +99 -0
  181. package/templates/default/src/app/(base)/template-steps/template-step-helpers.ts +176 -0
  182. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +153 -158
  183. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +26 -24
  184. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -56
  185. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -89
  186. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -58
  187. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.test.tsx +109 -0
  188. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +45 -84
  189. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.test.tsx +140 -0
  190. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +154 -262
  191. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -76
  192. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -32
  193. package/templates/default/src/app/(base)/test-cases/create/page.tsx +90 -76
  194. package/templates/default/src/app/(base)/test-cases/create-from-template/create-from-template-helpers.test.ts +94 -0
  195. package/templates/default/src/app/(base)/test-cases/create-from-template/create-from-template-helpers.ts +171 -0
  196. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +105 -96
  197. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +40 -38
  198. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.test.tsx +87 -0
  199. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +83 -73
  200. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -106
  201. package/templates/default/src/app/(base)/test-cases/page.tsx +3 -2
  202. package/templates/default/src/app/(base)/test-cases/test-case-flow.test.tsx +108 -0
  203. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +43 -82
  204. package/templates/default/src/app/(base)/test-cases/test-case-form.test.tsx +202 -0
  205. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +263 -395
  206. package/templates/default/src/app/(base)/test-cases/test-case-route-helpers.test.ts +95 -0
  207. package/templates/default/src/app/(base)/test-cases/test-case-route-helpers.ts +147 -0
  208. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +4 -2
  209. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +11 -10
  210. package/templates/default/src/app/(base)/test-runs/create/page.tsx +4 -5
  211. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
  212. package/templates/default/src/app/(base)/test-runs/test-run-form-helpers.test.ts +50 -0
  213. package/templates/default/src/app/(base)/test-runs/test-run-form-helpers.ts +168 -0
  214. package/templates/default/src/app/(base)/test-runs/test-run-form.test.tsx +138 -0
  215. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +111 -256
  216. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -229
  217. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -127
  218. package/templates/default/src/app/(base)/test-runs/use-test-run-name-validation.ts +74 -0
  219. package/templates/default/src/app/(base)/test-suites/create/page.tsx +17 -12
  220. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +22 -19
  221. package/templates/default/src/app/(base)/test-suites/page.tsx +14 -56
  222. package/templates/default/src/app/(base)/test-suites/test-suite-form.test.tsx +127 -0
  223. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +45 -64
  224. package/templates/default/src/app/(base)/test-suites/test-suite-helpers.test.ts +67 -0
  225. package/templates/default/src/app/(base)/test-suites/test-suite-helpers.ts +215 -0
  226. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +32 -29
  227. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -187
  228. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +12 -12
  229. package/templates/default/src/app/(dashboard-components)/data-card.tsx +26 -26
  230. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +56 -56
  231. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -87
  232. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +44 -44
  233. package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.test.ts +83 -0
  234. package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.ts +52 -52
  235. package/templates/default/src/app/api/test-runs/[runId]/download/route.test.ts +169 -0
  236. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +1 -1
  237. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.test.ts +135 -0
  238. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -146
  239. package/templates/default/src/app/globals.css +147 -147
  240. package/templates/default/src/app/page.tsx +1 -1
  241. package/templates/default/src/assets/icons/empty-tube.tsx +23 -23
  242. package/templates/default/src/assets/icons/tube-plus.tsx +29 -29
  243. package/templates/default/src/components/base-node.tsx +21 -21
  244. package/templates/default/src/components/chart/pie-chart.tsx +73 -73
  245. package/templates/default/src/components/data-extraction/locator-inspector-helpers.test.ts +32 -0
  246. package/templates/default/src/components/data-extraction/locator-inspector-helpers.ts +183 -0
  247. package/templates/default/src/components/data-extraction/locator-inspector.tsx +349 -460
  248. package/templates/default/src/components/data-state/empty-state.tsx +40 -40
  249. package/templates/default/src/components/data-visualization/info-card.tsx +70 -70
  250. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -22
  251. package/templates/default/src/components/diagram/button-edge.tsx +54 -54
  252. package/templates/default/src/components/diagram/dynamic-parameters-helpers.test.ts +83 -0
  253. package/templates/default/src/components/diagram/dynamic-parameters-helpers.ts +158 -0
  254. package/templates/default/src/components/diagram/dynamic-parameters.tsx +350 -474
  255. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
  256. package/templates/default/src/components/diagram/flow-diagram-helpers.test.ts +117 -0
  257. package/templates/default/src/components/diagram/flow-diagram-helpers.ts +251 -0
  258. package/templates/default/src/components/diagram/flow-diagram.tsx +247 -470
  259. package/templates/default/src/components/diagram/flow-host-helpers.test.ts +74 -0
  260. package/templates/default/src/components/diagram/flow-host-helpers.ts +51 -0
  261. package/templates/default/src/components/diagram/node-form-helpers.test.ts +92 -0
  262. package/templates/default/src/components/diagram/node-form-helpers.ts +100 -0
  263. package/templates/default/src/components/diagram/node-form.test.tsx +168 -0
  264. package/templates/default/src/components/diagram/node-form.tsx +199 -262
  265. package/templates/default/src/components/diagram/options-header-node.tsx +57 -57
  266. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -155
  267. package/templates/default/src/components/diagram/use-flow-node-order.ts +49 -0
  268. package/templates/default/src/components/form/error-message.tsx +7 -7
  269. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -453
  270. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -30
  271. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -8
  272. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -8
  273. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -8
  274. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -14
  275. package/templates/default/src/components/navigation/command-badge.tsx +34 -34
  276. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -51
  277. package/templates/default/src/components/navigation/entity-search-command.tsx +118 -116
  278. package/templates/default/src/components/navigation/nav-card.tsx +31 -31
  279. package/templates/default/src/components/navigation/nav-command-helpers.ts +122 -0
  280. package/templates/default/src/components/navigation/nav-command-search.tsx +125 -0
  281. package/templates/default/src/components/navigation/nav-command.test.tsx +106 -0
  282. package/templates/default/src/components/navigation/nav-command.tsx +49 -472
  283. package/templates/default/src/components/navigation/nav-link.tsx +60 -60
  284. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -112
  285. package/templates/default/src/components/navigation/use-nav-command.ts +58 -0
  286. package/templates/default/src/components/node-header.tsx +159 -159
  287. package/templates/default/src/components/reports/test-case-logs-modal.tsx +310 -310
  288. package/templates/default/src/components/table/table-actions.tsx +174 -172
  289. package/templates/default/src/components/test-case/test-case-form-helpers.test.ts +100 -0
  290. package/templates/default/src/components/test-case/test-case-form-helpers.ts +140 -0
  291. package/templates/default/src/components/test-case/test-case-picker-helpers.test.ts +40 -0
  292. package/templates/default/src/components/test-case/test-case-picker-helpers.ts +41 -0
  293. package/templates/default/src/components/test-case/test-case-picker.test.tsx +44 -0
  294. package/templates/default/src/components/test-case/test-case-picker.tsx +16 -35
  295. package/templates/default/src/components/test-case/test-scenario-preview.tsx +34 -0
  296. package/templates/default/src/components/test-run/download-logs-button.tsx +92 -92
  297. package/templates/default/src/components/test-run/log-viewer-helpers.test.ts +37 -0
  298. package/templates/default/src/components/test-run/log-viewer-helpers.ts +80 -0
  299. package/templates/default/src/components/test-run/log-viewer.test.tsx +118 -0
  300. package/templates/default/src/components/test-run/log-viewer.tsx +51 -350
  301. package/templates/default/src/components/test-run/test-run-details-helpers.test.ts +31 -0
  302. package/templates/default/src/components/test-run/test-run-details-helpers.ts +208 -0
  303. package/templates/default/src/components/test-run/test-run-details.test.tsx +174 -0
  304. package/templates/default/src/components/test-run/test-run-details.tsx +155 -457
  305. package/templates/default/src/components/test-run/test-run-header-helpers.test.ts +31 -0
  306. package/templates/default/src/components/test-run/test-run-header-helpers.ts +23 -0
  307. package/templates/default/src/components/test-run/test-run-header.test.tsx +103 -0
  308. package/templates/default/src/components/test-run/test-run-header.tsx +27 -149
  309. package/templates/default/src/components/test-run/use-log-viewer.ts +213 -0
  310. package/templates/default/src/components/test-run/use-test-run-details.ts +184 -0
  311. package/templates/default/src/components/test-run/use-test-run-header.ts +89 -0
  312. package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
  313. package/templates/default/src/components/test-suite/test-suite-picker-helpers.test.ts +68 -0
  314. package/templates/default/src/components/test-suite/test-suite-picker-helpers.ts +76 -0
  315. package/templates/default/src/components/test-suite/test-suite-picker.test.tsx +65 -0
  316. package/templates/default/src/components/test-suite/test-suite-picker.tsx +4 -72
  317. package/templates/default/src/components/theme/mode-toggle.tsx +54 -54
  318. package/templates/default/src/components/theme/theme-provider.tsx +8 -8
  319. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -7
  320. package/templates/default/src/components/typography/page-header.tsx +7 -7
  321. package/templates/default/src/components/ui/alert-dialog.tsx +106 -106
  322. package/templates/default/src/components/ui/alert.tsx +43 -43
  323. package/templates/default/src/components/ui/avatar.tsx +40 -40
  324. package/templates/default/src/components/ui/badge.tsx +29 -29
  325. package/templates/default/src/components/ui/button.tsx +47 -47
  326. package/templates/default/src/components/ui/calendar.tsx +158 -158
  327. package/templates/default/src/components/ui/card.tsx +43 -43
  328. package/templates/default/src/components/ui/checkbox.tsx +28 -28
  329. package/templates/default/src/components/ui/command.tsx +135 -135
  330. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -61
  331. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -87
  332. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -50
  333. package/templates/default/src/components/ui/data-table.test.tsx +122 -0
  334. package/templates/default/src/components/ui/data-table.tsx +298 -261
  335. package/templates/default/src/components/ui/dialog.tsx +97 -97
  336. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -182
  337. package/templates/default/src/components/ui/input.tsx +22 -22
  338. package/templates/default/src/components/ui/kbd.tsx +28 -28
  339. package/templates/default/src/components/ui/label.tsx +19 -19
  340. package/templates/default/src/components/ui/loading.tsx +12 -12
  341. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -116
  342. package/templates/default/src/components/ui/multi-select.test.tsx +45 -0
  343. package/templates/default/src/components/ui/multi-select.tsx +158 -142
  344. package/templates/default/src/components/ui/navigation-menu.tsx +120 -120
  345. package/templates/default/src/components/ui/popover.tsx +33 -33
  346. package/templates/default/src/components/ui/progress.tsx +25 -25
  347. package/templates/default/src/components/ui/radio-group.tsx +44 -44
  348. package/templates/default/src/components/ui/scroll-area.tsx +40 -40
  349. package/templates/default/src/components/ui/select.tsx +151 -151
  350. package/templates/default/src/components/ui/separator.tsx +22 -22
  351. package/templates/default/src/components/ui/skeleton.tsx +7 -7
  352. package/templates/default/src/components/ui/table.tsx +76 -76
  353. package/templates/default/src/components/ui/tabs.tsx +55 -55
  354. package/templates/default/src/components/ui/textarea.tsx +21 -21
  355. package/templates/default/src/components/ui/toast.tsx +113 -113
  356. package/templates/default/src/components/ui/toaster.tsx +26 -26
  357. package/templates/default/src/components/user-prompt/delete-prompt.test.tsx +60 -0
  358. package/templates/default/src/components/user-prompt/delete-prompt.tsx +118 -87
  359. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -30
  360. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -24
  361. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -28
  362. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -21
  363. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -42
  364. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -16
  365. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -24
  366. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -39
  367. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -36
  368. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -43
  369. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -24
  370. package/templates/default/src/hooks/use-toast.ts +187 -187
  371. package/templates/default/src/lib/automation/automation-path-roots.ts +95 -0
  372. package/templates/default/src/lib/automation/automation-workspace.ts +147 -0
  373. package/templates/default/src/lib/automation/paths.ts +6 -211
  374. package/templates/default/src/lib/bidirectional-sync.ts +432 -432
  375. package/templates/default/src/lib/environment-file-utils.ts +2 -1
  376. package/templates/default/src/lib/executor/local-executor-adapter.ts +2 -5
  377. package/templates/default/src/lib/feature-file-generator.ts +2 -1
  378. package/templates/default/src/lib/gherkin-parser.ts +0 -2
  379. package/templates/default/src/lib/locator-group-file-utils.ts +304 -307
  380. package/templates/default/src/lib/locator-picker/session-manager.ts +0 -21
  381. package/templates/default/src/lib/locator-picker/suggestions.ts +13 -11
  382. package/templates/default/src/lib/metrics/metric-calculator.ts +2 -6
  383. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -205
  384. package/templates/default/src/lib/path-helpers/module-path.ts +71 -71
  385. package/templates/default/src/lib/sync/sync-executor.test.ts +76 -0
  386. package/templates/default/src/lib/sync/sync-pending-counts.test.ts +227 -226
  387. package/templates/default/src/lib/sync/sync-pending-counts.ts +2 -5
  388. package/templates/default/src/lib/template-sync-utils.d.ts +6 -6
  389. package/templates/default/src/lib/template-sync-utils.js +46 -46
  390. package/templates/default/src/lib/template-sync-utils.ts +63 -63
  391. package/templates/default/src/lib/test-case-utils.ts +6 -6
  392. package/templates/default/src/lib/test-run/log-formatter.ts +83 -83
  393. package/templates/default/src/lib/test-run/report-parser.ts +352 -352
  394. package/templates/default/src/lib/test-run/test-run-executor.ts +13 -13
  395. package/templates/default/src/lib/test-run/winston-logger.ts +65 -64
  396. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -42
  397. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -95
  398. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -160
  399. package/templates/default/src/lib/utils/node-param-validation.ts +81 -81
  400. package/templates/default/src/lib/utils/template-step-file-generator.ts +2 -2
  401. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -166
  402. package/templates/default/src/lib/utils.ts +31 -31
  403. package/templates/default/src/services/dashboard/dashboard-service.test.ts +106 -0
  404. package/templates/default/src/services/dashboard/dashboard-service.ts +173 -0
  405. package/templates/default/src/services/environment/environment-service.test.ts +137 -0
  406. package/templates/default/src/services/environment/environment-service.ts +96 -0
  407. package/templates/default/src/services/locator/locator-path-utils.test.ts +14 -0
  408. package/templates/default/src/services/locator/locator-path-utils.ts +14 -0
  409. package/templates/default/src/services/locator/locator-service.test.ts +63 -0
  410. package/templates/default/src/services/locator/locator-service.ts +479 -0
  411. package/templates/default/src/services/locator/locator-sync-utils.test.ts +19 -0
  412. package/templates/default/src/services/locator/locator-sync-utils.ts +21 -0
  413. package/templates/default/src/services/locator-group/locator-group-service.test.ts +123 -0
  414. package/templates/default/src/services/locator-group/locator-group-service.ts +180 -0
  415. package/templates/default/src/services/module/module-service.test.ts +89 -0
  416. package/templates/default/src/services/module/module-service.ts +66 -0
  417. package/templates/default/src/services/report/report-service.test.ts +244 -0
  418. package/templates/default/src/services/report/report-service.ts +438 -0
  419. package/templates/default/src/services/shared/constants.ts +2 -0
  420. package/templates/default/src/services/shared/errors.test.ts +38 -0
  421. package/templates/default/src/services/shared/errors.ts +44 -0
  422. package/templates/default/src/services/shared/index.ts +7 -0
  423. package/templates/default/src/services/tag/tag-service.test.ts +22 -0
  424. package/templates/default/src/services/tag/tag-service.ts +41 -0
  425. package/templates/default/src/services/template-step/template-step-service.test.ts +22 -0
  426. package/templates/default/src/services/template-step/template-step-service.ts +171 -0
  427. package/templates/default/src/services/template-step-group/template-step-group-service.test.ts +22 -0
  428. package/templates/default/src/services/template-step-group/template-step-group-service.ts +81 -0
  429. package/templates/default/src/services/template-test-case/template-test-case-service.test.ts +22 -0
  430. package/templates/default/src/services/template-test-case/template-test-case-service.ts +128 -0
  431. package/templates/default/src/services/test-case/test-case-service.test.ts +175 -0
  432. package/templates/default/src/services/test-case/test-case-service.ts +298 -0
  433. package/templates/default/src/services/test-run/test-run-helpers.ts +61 -0
  434. package/templates/default/src/services/test-run/test-run-service.test.ts +647 -0
  435. package/templates/default/src/services/test-run/test-run-service.ts +917 -0
  436. package/templates/default/src/services/test-suite/test-suite-service.test.ts +127 -0
  437. package/templates/default/src/services/test-suite/test-suite-service.ts +197 -0
  438. package/templates/default/src/types/diagram/diagram.ts +34 -34
  439. package/templates/default/src/types/diagram/template-step.ts +11 -11
  440. package/templates/default/src/types/executor/browser.type.ts +1 -1
  441. package/templates/default/src/types/form/actionHandler.ts +19 -6
  442. package/templates/default/src/types/locator/locator.type.ts +11 -11
  443. package/templates/default/src/types/step/step.type.ts +1 -1
  444. package/templates/default/src/types/table/data-table.ts +6 -6
  445. package/templates/default/tailwind.config.ts +62 -62
  446. package/templates/default/tsconfig.json +1 -1
  447. package/dist/cli.e2e.test.d.ts +0 -2
  448. package/dist/cli.e2e.test.d.ts.map +0 -1
  449. package/dist/cli.e2e.test.js +0 -73
  450. package/dist/cli.e2e.test.js.map +0 -1
  451. package/dist/config.test.d.ts +0 -2
  452. package/dist/config.test.d.ts.map +0 -1
  453. package/dist/config.test.js +0 -65
  454. package/dist/config.test.js.map +0 -1
  455. package/dist/copy-template.test.d.ts +0 -2
  456. package/dist/copy-template.test.d.ts.map +0 -1
  457. package/dist/copy-template.test.js +0 -71
  458. package/dist/copy-template.test.js.map +0 -1
  459. package/dist/download-repo.test.d.ts +0 -2
  460. package/dist/download-repo.test.d.ts.map +0 -1
  461. package/dist/download-repo.test.js +0 -14
  462. package/dist/download-repo.test.js.map +0 -1
  463. package/dist/install.test.d.ts +0 -2
  464. package/dist/install.test.d.ts.map +0 -1
  465. package/dist/install.test.js +0 -119
  466. package/dist/install.test.js.map +0 -1
  467. package/dist/prompts.test.d.ts +0 -2
  468. package/dist/prompts.test.d.ts.map +0 -1
  469. package/dist/prompts.test.js +0 -58
  470. package/dist/prompts.test.js.map +0 -1
  471. package/templates/default/src/actions/conflict/conflict.action.ts +0 -33
  472. package/templates/default/src/actions/review/review-actions.ts +0 -147
  473. package/templates/default/src/actions/user/user-actions.ts +0 -13
  474. package/templates/default/src/app/(base)/locators/locator-form.tsx +0 -163
  475. package/templates/default/src/app/(base)/reviews/create/page.tsx +0 -26
  476. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +0 -15
  477. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +0 -26
  478. package/templates/default/src/app/(base)/reviews/page.tsx +0 -26
  479. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +0 -26
  480. package/templates/default/src/app/(base)/reviews/review-form.tsx +0 -11
  481. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +0 -9
  482. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +0 -9
  483. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +0 -15
  484. package/templates/default/src/constants/form-opts/locator-form-opts.ts +0 -20
  485. package/templates/default/src/constants/form-opts/review-form-opts.ts +0 -23
@@ -0,0 +1,63 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { parseGroupJSDoc } from './jsdoc-parser'
3
+
4
+ describe('parseGroupJSDoc', () => {
5
+ it('parses ACTION group metadata', () => {
6
+ const content = `/**
7
+ * @name Auth Actions
8
+ * @description auth flows
9
+ * @type ACTION
10
+ */`
11
+ expect(parseGroupJSDoc(content)).toEqual({
12
+ name: 'Auth Actions',
13
+ description: 'auth flows',
14
+ type: 'ACTION',
15
+ })
16
+ })
17
+
18
+ it('parses VALIDATION group metadata', () => {
19
+ const content = `/**
20
+ * @name Auth Validations
21
+ * @type VALIDATION
22
+ */`
23
+ expect(parseGroupJSDoc(content)).toEqual({
24
+ name: 'Auth Validations',
25
+ description: null,
26
+ type: 'VALIDATION',
27
+ })
28
+ })
29
+
30
+ it('returns null when @type is missing', () => {
31
+ const content = `/**
32
+ * @name Missing Type
33
+ */`
34
+ expect(parseGroupJSDoc(content)).toBeNull()
35
+ })
36
+
37
+ it('returns null when @name is missing', () => {
38
+ const content = `/**
39
+ * @type ACTION
40
+ */`
41
+ expect(parseGroupJSDoc(content)).toBeNull()
42
+ })
43
+
44
+ it('returns null for empty content', () => {
45
+ expect(parseGroupJSDoc('')).toBeNull()
46
+ })
47
+
48
+ it('parses when imports are before jsdoc', () => {
49
+ const content = `import { x } from 'y'
50
+ /**
51
+ * @name Imported Group
52
+ * @type ACTION
53
+ */`
54
+ expect(parseGroupJSDoc(content)?.name).toBe('Imported Group')
55
+ })
56
+
57
+ it('returns null for malformed close', () => {
58
+ const content = `/**
59
+ * @name Broken
60
+ * @type ACTION`
61
+ expect(parseGroupJSDoc(content)).toBeNull()
62
+ })
63
+ })
@@ -0,0 +1,88 @@
1
+ import { TemplateStepGroupType } from '@prisma/client'
2
+
3
+ export interface StepGroupJSDoc {
4
+ name: string
5
+ description: string | null
6
+ type: TemplateStepGroupType
7
+ }
8
+
9
+ /**
10
+ * Parses JSDoc comment to extract top-of-file step group metadata.
11
+ * Returns null if no valid group JSDoc is found.
12
+ */
13
+ export function parseGroupJSDoc(content: string): StepGroupJSDoc | null {
14
+ const lines = content.split('\n')
15
+
16
+ if (lines.length === 0) {
17
+ return null
18
+ }
19
+
20
+ let startLine = 0
21
+ while (startLine < lines.length) {
22
+ const line = lines[startLine].trim()
23
+ if (line === '' || line.startsWith('import ')) {
24
+ startLine++
25
+ continue
26
+ }
27
+ break
28
+ }
29
+
30
+ if (startLine >= lines.length || !lines[startLine].trim().startsWith('/**')) {
31
+ return null
32
+ }
33
+
34
+ let hasType = false
35
+ let endLine = -1
36
+ let name: string | null = null
37
+ let description: string | null = null
38
+ let type: string | null = null
39
+
40
+ const maxLines = Math.min(lines.length, startLine + 50)
41
+ for (let i = startLine; i < maxLines; i++) {
42
+ const line = lines[i].trim()
43
+
44
+ if (line.includes('*/')) {
45
+ const beforeClose = line.split('*/')[0].trim()
46
+
47
+ if (beforeClose.startsWith('* @name') || beforeClose.startsWith('*@name')) {
48
+ const match = beforeClose.match(/@name\s+(.+)/)
49
+ if (match) name = match[1].trim()
50
+ } else if (beforeClose.startsWith('* @description') || beforeClose.startsWith('*@description')) {
51
+ const match = beforeClose.match(/@description\s+(.+)/)
52
+ if (match) description = match[1].trim() || null
53
+ } else if (beforeClose.startsWith('* @type') || beforeClose.startsWith('*@type')) {
54
+ hasType = true
55
+ const match = beforeClose.match(/@type\s+(.+)/)
56
+ if (match) type = match[1].trim()
57
+ }
58
+
59
+ endLine = i
60
+ break
61
+ } else if (line.startsWith('* @name') || line.startsWith('*@name')) {
62
+ const match = line.match(/@name\s+(.+)/)
63
+ if (match) name = match[1].trim()
64
+ } else if (line.startsWith('* @description') || line.startsWith('*@description')) {
65
+ const match = line.match(/@description\s+(.+)/)
66
+ if (match) description = match[1].trim() || null
67
+ } else if (line.startsWith('* @type') || line.startsWith('*@type')) {
68
+ hasType = true
69
+ const match = line.match(/@type\s+(.+)/)
70
+ if (match) type = match[1].trim()
71
+ }
72
+ }
73
+
74
+ if (hasType && endLine >= 0 && name && type) {
75
+ const normalizedType = type.toUpperCase()
76
+ if (normalizedType !== 'ACTION' && normalizedType !== 'VALIDATION') {
77
+ throw new Error(`Invalid @type value: ${type}. Must be ACTION or VALIDATION`)
78
+ }
79
+
80
+ return {
81
+ name: name.trim(),
82
+ description: description ? description.trim() : null,
83
+ type: normalizedType as TemplateStepGroupType,
84
+ }
85
+ }
86
+
87
+ return null
88
+ }
@@ -0,0 +1,71 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { mapTypeToParameterType, parseStepFile, parseStepJSDoc } from './step-file-parser'
3
+
4
+ describe('parseStepJSDoc', () => {
5
+ it('parses valid step jsdoc', () => {
6
+ const content = `/**
7
+ * @name Click Save
8
+ * @description clicks save button
9
+ * @icon MOUSE
10
+ */
11
+ When("click save", async function () {})`
12
+ expect(parseStepJSDoc(content, 5)).toEqual({
13
+ name: 'Click Save',
14
+ description: 'clicks save button',
15
+ icon: 'MOUSE',
16
+ })
17
+ })
18
+
19
+ it('returns null when @icon is missing', () => {
20
+ const content = `/**
21
+ * @name Click Save
22
+ */
23
+ When("click save", async function () {})`
24
+ expect(parseStepJSDoc(content, 4)).toBeNull()
25
+ })
26
+
27
+ it('returns null when @name is missing', () => {
28
+ const content = `/**
29
+ * @icon MOUSE
30
+ */
31
+ When("click save", async function () {})`
32
+ expect(parseStepJSDoc(content, 4)).toBeNull()
33
+ })
34
+ })
35
+
36
+ describe('mapTypeToParameterType', () => {
37
+ it('maps SelectorName', () => expect(mapTypeToParameterType('SelectorName')).toBe('LOCATOR'))
38
+ it('maps string', () => expect(mapTypeToParameterType('string')).toBe('STRING'))
39
+ it('maps number', () => expect(mapTypeToParameterType('number')).toBe('NUMBER'))
40
+ it('maps int', () => expect(mapTypeToParameterType('int')).toBe('NUMBER'))
41
+ it('maps boolean', () => expect(mapTypeToParameterType('boolean')).toBe('BOOLEAN'))
42
+ it('maps Date', () => expect(mapTypeToParameterType('Date')).toBe('DATE'))
43
+ it('throws on unsupported type', () => expect(() => mapTypeToParameterType('unknown')).toThrow())
44
+ })
45
+
46
+ describe('parseStepFile', () => {
47
+ it('parses minimal valid step file with one When step', () => {
48
+ const content = `/**
49
+ * @name Auth Steps
50
+ * @type ACTION
51
+ */
52
+ import { When } from "@cucumber/cucumber";
53
+ /**
54
+ * @name Enter Email
55
+ * @description fills email
56
+ * @icon MOUSE
57
+ */
58
+ When("enter {string}", async function (value: string) {
59
+ // no-op
60
+ });`
61
+
62
+ const parsed = parseStepFile(content, 'automation/steps/actions/auth.step.ts')
63
+ expect(parsed).not.toBeNull()
64
+ expect(parsed?.group.name).toBe('Auth Steps')
65
+ expect(parsed?.steps).toHaveLength(1)
66
+ expect(parsed?.steps[0].signature).toBe('enter {string}')
67
+ expect(parsed?.steps[0].keyword).toBe('When')
68
+ expect(parsed?.steps[0].source).toContain('@name Enter Email')
69
+ expect(parsed?.steps[0].source).toContain('When("enter {string}"')
70
+ })
71
+ })
@@ -0,0 +1,315 @@
1
+ import { parse } from '@babel/parser'
2
+ import _traverse from '@babel/traverse'
3
+ import type { NodePath } from '@babel/traverse'
4
+ import * as t from '@babel/types'
5
+ import { StepParameterType, TemplateStepIcon } from '@prisma/client'
6
+ import { parseGroupJSDoc, StepGroupJSDoc } from './jsdoc-parser'
7
+
8
+ const traverse = (_traverse as { default?: typeof _traverse }).default ?? _traverse
9
+
10
+ export interface StepJSDoc {
11
+ name: string
12
+ description: string | null
13
+ icon: TemplateStepIcon
14
+ }
15
+
16
+ export interface StepParameter {
17
+ name: string
18
+ type: StepParameterType
19
+ order: number
20
+ }
21
+
22
+ export interface ParsedStep {
23
+ jsdoc: StepJSDoc
24
+ signature: string
25
+ source: string
26
+ start: number
27
+ end: number
28
+ functionDefinition: string
29
+ parameters: StepParameter[]
30
+ keyword: 'When' | 'Then' | 'Given'
31
+ }
32
+
33
+ export interface StepData {
34
+ group: StepGroupJSDoc
35
+ steps: ParsedStep[]
36
+ filePath: string
37
+ }
38
+
39
+ /**
40
+ * Reads the nearest preceding JSDoc block for a step call and extracts metadata.
41
+ */
42
+ export function parseStepJSDoc(content: string, startLine: number): StepJSDoc | null {
43
+ const lines = content.split('\n')
44
+ let jsdocStart = -1
45
+ for (let i = startLine - 1; i >= 0 && i >= startLine - 20; i--) {
46
+ const line = lines[i]?.trim()
47
+ if (line?.includes('*/')) {
48
+ jsdocStart = i
49
+ for (let j = i - 1; j >= 0 && j >= i - 10; j--) {
50
+ const prevLine = lines[j]?.trim()
51
+ if (prevLine?.startsWith('/**')) {
52
+ jsdocStart = j
53
+ break
54
+ }
55
+ }
56
+ break
57
+ } else if (line?.startsWith('/**')) {
58
+ jsdocStart = i
59
+ break
60
+ }
61
+ }
62
+
63
+ if (jsdocStart === -1) return null
64
+
65
+ let name: string | null = null
66
+ let description: string | null = null
67
+ let icon: string | null = null
68
+ let foundJSDoc = false
69
+
70
+ for (let i = jsdocStart; i < Math.min(lines.length, jsdocStart + 20); i++) {
71
+ const line = lines[i]?.trim()
72
+
73
+ if (line?.startsWith('/**')) {
74
+ foundJSDoc = true
75
+ continue
76
+ }
77
+
78
+ if (line?.includes('*/')) {
79
+ const beforeClose = line.split('*/')[0].trim()
80
+ if (beforeClose.startsWith('* @name') || beforeClose.startsWith('*@name')) {
81
+ const match = beforeClose.match(/@name\s+(.+)/)
82
+ if (match) name = match[1].trim()
83
+ } else if (beforeClose.startsWith('* @description') || beforeClose.startsWith('*@description')) {
84
+ const match = beforeClose.match(/@description\s+(.+)/)
85
+ if (match) description = match[1].trim() || null
86
+ } else if (beforeClose.startsWith('* @icon') || beforeClose.startsWith('*@icon')) {
87
+ const match = beforeClose.match(/@icon\s+(.+)/)
88
+ if (match) icon = match[1].trim()
89
+ }
90
+ break
91
+ }
92
+
93
+ if (foundJSDoc) {
94
+ if (line?.startsWith('* @name') || line?.startsWith('*@name')) {
95
+ const match = line.match(/@name\s+(.+)/)
96
+ if (match) name = match[1].trim()
97
+ } else if (line?.startsWith('* @description') || line?.startsWith('*@description')) {
98
+ const match = line.match(/@description\s+(.+)/)
99
+ if (match) description = match[1].trim() || null
100
+ } else if (line?.startsWith('* @icon') || line?.startsWith('*@icon')) {
101
+ const match = line.match(/@icon\s+(.+)/)
102
+ if (match) icon = match[1].trim()
103
+ }
104
+ }
105
+ }
106
+
107
+ if (!name || !icon) return null
108
+
109
+ const iconUpper = icon.toUpperCase()
110
+ const validIcons = Object.values(TemplateStepIcon)
111
+ if (!validIcons.includes(iconUpper as TemplateStepIcon)) {
112
+ throw new Error(`Invalid @icon value: ${icon}. Must be one of: ${validIcons.join(', ')}`)
113
+ }
114
+
115
+ return {
116
+ name: name.trim(),
117
+ description: description ? description.trim() : null,
118
+ icon: iconUpper as TemplateStepIcon,
119
+ }
120
+ }
121
+
122
+ function findStepJSDocStartOffset(content: string, startLine: number): number | null {
123
+ const lines = content.split('\n')
124
+ let jsdocStart = -1
125
+
126
+ for (let i = startLine - 1; i >= 0 && i >= startLine - 20; i--) {
127
+ const line = lines[i]?.trim()
128
+ if (line?.includes('*/')) {
129
+ jsdocStart = i
130
+ for (let j = i - 1; j >= 0 && j >= i - 10; j--) {
131
+ const prevLine = lines[j]?.trim()
132
+ if (prevLine?.startsWith('/**')) {
133
+ jsdocStart = j
134
+ break
135
+ }
136
+ }
137
+ break
138
+ } else if (line?.startsWith('/**')) {
139
+ jsdocStart = i
140
+ break
141
+ }
142
+ }
143
+
144
+ if (jsdocStart === -1) {
145
+ return null
146
+ }
147
+
148
+ let offset = 0
149
+ for (let i = 0; i < jsdocStart; i++) {
150
+ offset += (lines[i]?.length ?? 0) + 1
151
+ }
152
+ return offset
153
+ }
154
+
155
+ /**
156
+ * Maps TypeScript parameter annotations to persisted step-parameter enum values.
157
+ */
158
+ export function mapTypeToParameterType(typeName: string): StepParameterType {
159
+ const normalized = typeName.trim()
160
+ if (normalized === 'SelectorName') return StepParameterType.LOCATOR
161
+ if (normalized === 'string') return StepParameterType.STRING
162
+ if (normalized === 'number' || normalized === 'int') return StepParameterType.NUMBER
163
+ if (normalized === 'boolean') return StepParameterType.BOOLEAN
164
+ if (normalized === 'Date') return StepParameterType.DATE
165
+ throw new Error(
166
+ `Unsupported parameter type: ${typeName}. Supported types: SelectorName, string, number, int, boolean, Date`,
167
+ )
168
+ }
169
+
170
+ /**
171
+ * Extracts exact source for a step definition call expression.
172
+ */
173
+ export function extractFunctionDefinition(callExpr: t.CallExpression, sourceCode: string): string {
174
+ const start = callExpr.start
175
+ const end = callExpr.end
176
+ if (start == null || end == null) {
177
+ throw new Error('Cannot extract function definition: missing position information')
178
+ }
179
+
180
+ let code = sourceCode.slice(start, end).trim()
181
+ const afterEnd = sourceCode.slice(end, end + 10).trim()
182
+ if (afterEnd.startsWith(';')) code += ';'
183
+ return code
184
+ }
185
+
186
+ /**
187
+ * Extracts exact source for a step definition including its leading step JSDoc.
188
+ */
189
+ export function extractStepSource(callExpr: t.CallExpression, sourceCode: string, startLine?: number): string {
190
+ const { start, end } = extractStepSourceRange(callExpr, sourceCode, startLine)
191
+ return sourceCode.slice(start, end).trim()
192
+ }
193
+
194
+ /**
195
+ * Finds the byte range for a step definition including its leading step JSDoc and trailing semicolon.
196
+ */
197
+ export function extractStepSourceRange(
198
+ callExpr: t.CallExpression,
199
+ sourceCode: string,
200
+ startLine?: number,
201
+ ): { start: number; end: number } {
202
+ const callStart = callExpr.start
203
+ const callEnd = callExpr.end
204
+ if (callStart == null || callEnd == null) {
205
+ throw new Error('Cannot extract step source: missing position information')
206
+ }
207
+
208
+ const jsdocStart = startLine == null ? null : findStepJSDocStartOffset(sourceCode, startLine)
209
+ const start = jsdocStart ?? callStart
210
+
211
+ let end = callEnd
212
+ const trailing = sourceCode.slice(callEnd)
213
+ const semicolonMatch = trailing.match(/^\s*;/)
214
+ if (semicolonMatch) {
215
+ end += semicolonMatch[0].length
216
+ }
217
+
218
+ return { start, end }
219
+ }
220
+
221
+ /**
222
+ * Parses a `.step.ts` file into group + step definitions from AST.
223
+ */
224
+ export function parseStepFile(content: string, filePath: string): StepData | null {
225
+ const group = parseGroupJSDoc(content)
226
+ if (!group) return null
227
+
228
+ let ast
229
+ try {
230
+ ast = parse(content, {
231
+ sourceType: 'module',
232
+ plugins: ['typescript', 'decorators-legacy'],
233
+ })
234
+ } catch (error) {
235
+ throw new Error(`Failed to parse TypeScript: ${error}`)
236
+ }
237
+
238
+ const steps: ParsedStep[] = []
239
+
240
+ traverse(ast, {
241
+ CallExpression(path: NodePath<t.CallExpression>) {
242
+ const node = path.node
243
+ const callee = node.callee
244
+ let keyword: 'When' | 'Then' | 'Given' | null = null
245
+ if (t.isIdentifier(callee) && (callee.name === 'When' || callee.name === 'Then' || callee.name === 'Given')) {
246
+ keyword = callee.name as 'When' | 'Then' | 'Given'
247
+ }
248
+
249
+ if (!keyword || node.arguments.length < 2) return
250
+
251
+ const patternArg = node.arguments[0]
252
+ if (!t.isStringLiteral(patternArg)) return
253
+ const signature = patternArg.value
254
+
255
+ const funcArg = node.arguments[1]
256
+ if (!t.isFunction(funcArg)) return
257
+
258
+ const lineNumber = node.loc?.start?.line
259
+ if (lineNumber === undefined) return
260
+
261
+ const jsdoc = parseStepJSDoc(content, lineNumber - 1)
262
+ if (!jsdoc) return
263
+
264
+ const parameters: StepParameter[] = []
265
+ let order = 0
266
+ for (const param of funcArg.params) {
267
+ if (t.isIdentifier(param) && param.name === 'this') continue
268
+ if (t.isObjectPattern(param) && param.properties.length === 1) {
269
+ const prop = param.properties[0]
270
+ if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === 'this') continue
271
+ }
272
+
273
+ let paramName: string | null = null
274
+ let paramType: string | null = null
275
+
276
+ if (t.isIdentifier(param)) {
277
+ paramName = param.name
278
+ if (param.typeAnnotation && t.isTSTypeAnnotation(param.typeAnnotation)) {
279
+ const typeAnnotation = param.typeAnnotation.typeAnnotation
280
+ if (t.isTSTypeReference(typeAnnotation) && t.isIdentifier(typeAnnotation.typeName)) {
281
+ paramType = typeAnnotation.typeName.name
282
+ } else if (t.isTSStringKeyword(typeAnnotation)) {
283
+ paramType = 'string'
284
+ } else if (t.isTSNumberKeyword(typeAnnotation)) {
285
+ paramType = 'number'
286
+ } else if (t.isTSBooleanKeyword(typeAnnotation)) {
287
+ paramType = 'boolean'
288
+ }
289
+ }
290
+ }
291
+
292
+ if (paramName && paramType) {
293
+ const mappedType = mapTypeToParameterType(paramType)
294
+ parameters.push({ name: paramName, type: mappedType, order: order++ })
295
+ }
296
+ }
297
+
298
+ const functionDefinition = extractFunctionDefinition(node, content)
299
+ const { start, end } = extractStepSourceRange(node, content, lineNumber - 1)
300
+ const source = content.slice(start, end).trim()
301
+ steps.push({
302
+ jsdoc,
303
+ signature,
304
+ source,
305
+ start,
306
+ end,
307
+ functionDefinition,
308
+ parameters,
309
+ keyword,
310
+ })
311
+ },
312
+ })
313
+
314
+ return { group, steps, filePath }
315
+ }
@@ -0,0 +1,86 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { StepParameterType } from '@prisma/client'
3
+ import {
4
+ determineStepTypeAndIcon,
5
+ extractParametersFromGherkinStep,
6
+ sameResolvedParameters,
7
+ signatureToRegex,
8
+ } from './step-matcher'
9
+
10
+ describe('signatureToRegex', () => {
11
+ it('matches {string} placeholder', () => {
12
+ expect(signatureToRegex('click {string}').test('click "submit"')).toBe(true)
13
+ })
14
+
15
+ it('matches {int} placeholder', () => {
16
+ expect(signatureToRegex('wait {int} seconds').test('wait 15 seconds')).toBe(true)
17
+ })
18
+
19
+ it('matches {boolean} placeholder', () => {
20
+ expect(signatureToRegex('flag is {boolean}').test('flag is true')).toBe(true)
21
+ })
22
+
23
+ it('matches {number} placeholder', () => {
24
+ expect(signatureToRegex('value is {number}').test('value is 4.25')).toBe(true)
25
+ })
26
+
27
+ it('matches mixed placeholders', () => {
28
+ expect(signatureToRegex('set {string} to {int}').test('set "count" to 2')).toBe(true)
29
+ })
30
+
31
+ it('matches no placeholder signature', () => {
32
+ expect(signatureToRegex('open home').test('open home')).toBe(true)
33
+ })
34
+ })
35
+
36
+ describe('extractParametersFromGherkinStep', () => {
37
+ it('extracts parameters for matching text', () => {
38
+ const result = extractParametersFromGherkinStep('set "count" to 2', 'set {string} to {int}', [
39
+ { name: 'name', order: 0, type: StepParameterType.STRING },
40
+ { name: 'value', order: 1, type: StepParameterType.NUMBER },
41
+ ])
42
+ expect(result).toEqual([
43
+ { name: 'name', value: 'count', order: 0, type: StepParameterType.STRING },
44
+ { name: 'value', value: '2', order: 1, type: StepParameterType.NUMBER },
45
+ ])
46
+ })
47
+
48
+ it('returns null for non-matching text', () => {
49
+ expect(extractParametersFromGherkinStep('open dashboard', 'set {string} to {int}', [])).toBeNull()
50
+ })
51
+
52
+ it('handles parameter count mismatch by truncating to template params', () => {
53
+ const result = extractParametersFromGherkinStep('set "a" to 2', 'set {string} to {int}', [
54
+ { name: 'onlyOne', order: 0, type: StepParameterType.STRING },
55
+ ])
56
+ expect(result).toEqual([{ name: 'onlyOne', value: 'a', order: 0, type: StepParameterType.STRING }])
57
+ })
58
+ })
59
+
60
+ describe('determineStepTypeAndIcon', () => {
61
+ it('maps Given', () => expect(determineStepTypeAndIcon('Given')).toEqual({ type: 'ACTION', icon: 'NAVIGATION' }))
62
+ it('maps When', () => expect(determineStepTypeAndIcon('When')).toEqual({ type: 'ACTION', icon: 'MOUSE' }))
63
+ it('maps Then', () => expect(determineStepTypeAndIcon('Then')).toEqual({ type: 'ASSERTION', icon: 'VALIDATION' }))
64
+ it('maps And', () => expect(determineStepTypeAndIcon('And')).toEqual({ type: 'ACTION', icon: 'MOUSE' }))
65
+ it('maps But', () => expect(determineStepTypeAndIcon('But')).toEqual({ type: 'ACTION', icon: 'MOUSE' }))
66
+ it('maps unknown keyword to default', () =>
67
+ expect(determineStepTypeAndIcon('Unknown')).toEqual({ type: 'ACTION', icon: 'MOUSE' }))
68
+ })
69
+
70
+ describe('sameResolvedParameters', () => {
71
+ const sample = [{ name: 'x', value: '1', order: 0, type: StepParameterType.NUMBER }]
72
+
73
+ it('returns true for equal arrays', () => {
74
+ expect(sameResolvedParameters(sample, [...sample])).toBe(true)
75
+ })
76
+
77
+ it('returns false for different length', () => {
78
+ expect(sameResolvedParameters(sample, [])).toBe(false)
79
+ })
80
+
81
+ it('returns false for different values', () => {
82
+ expect(
83
+ sameResolvedParameters(sample, [{ name: 'x', value: '2', order: 0, type: StepParameterType.NUMBER }]),
84
+ ).toBe(false)
85
+ })
86
+ })