create-appraise 0.1.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 (330) hide show
  1. package/README.md +52 -0
  2. package/package.json +63 -0
  3. package/templates/default/.env.example +2 -0
  4. package/templates/default/README.md +51 -0
  5. package/templates/default/appraise.config.json +4 -0
  6. package/templates/default/components.json +24 -0
  7. package/templates/default/eslint.config.mjs +15 -0
  8. package/templates/default/next-env.d.ts +6 -0
  9. package/templates/default/next.config.ts +7 -0
  10. package/templates/default/package-lock.json +14321 -0
  11. package/templates/default/package.json +124 -0
  12. package/templates/default/postcss.config.mjs +8 -0
  13. package/templates/default/prisma/migrations/20251026202316_migrate_back_to_sqlite/migration.sql +257 -0
  14. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -0
  15. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -0
  16. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -0
  17. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -0
  18. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -0
  19. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -0
  20. package/templates/default/prisma/migrations/20251130190737_add_trace_path_to_test_run_test_case/migration.sql +2 -0
  21. package/templates/default/prisma/migrations/20251213074835_add_log_path_to_test_run/migration.sql +2 -0
  22. package/templates/default/prisma/migrations/20251213183952_add_name_property_for_the_test_run_entities/migration.sql +30 -0
  23. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -0
  24. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -0
  25. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -0
  26. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -0
  27. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -0
  28. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -0
  29. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -0
  30. package/templates/default/prisma/migrations/migration_lock.toml +3 -0
  31. package/templates/default/prisma/schema.prisma +554 -0
  32. package/templates/default/public/favicon.ico +0 -0
  33. package/templates/default/public/file.svg +1 -0
  34. package/templates/default/public/globe.svg +1 -0
  35. package/templates/default/public/next.svg +1 -0
  36. package/templates/default/public/vercel.svg +1 -0
  37. package/templates/default/public/window.svg +1 -0
  38. package/templates/default/scripts/regenerate-features.ts +94 -0
  39. package/templates/default/scripts/setup-env.ts +19 -0
  40. package/templates/default/scripts/sync-all.ts +341 -0
  41. package/templates/default/scripts/sync-environments.ts +323 -0
  42. package/templates/default/scripts/sync-locator-groups.ts +413 -0
  43. package/templates/default/scripts/sync-locators.ts +402 -0
  44. package/templates/default/scripts/sync-modules.ts +349 -0
  45. package/templates/default/scripts/sync-tags.ts +292 -0
  46. package/templates/default/scripts/sync-template-step-groups.ts +399 -0
  47. package/templates/default/scripts/sync-template-steps.ts +806 -0
  48. package/templates/default/scripts/sync-test-cases.ts +905 -0
  49. package/templates/default/scripts/sync-test-suites.ts +411 -0
  50. package/templates/default/src/actions/conflict/conflict.action.ts +33 -0
  51. package/templates/default/src/actions/dashboard/dashboard-actions.ts +241 -0
  52. package/templates/default/src/actions/environments/environment-actions.ts +205 -0
  53. package/templates/default/src/actions/locator/locator-actions.ts +547 -0
  54. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +344 -0
  55. package/templates/default/src/actions/modules/module-actions.ts +133 -0
  56. package/templates/default/src/actions/reports/report-actions.ts +614 -0
  57. package/templates/default/src/actions/review/review-actions.ts +147 -0
  58. package/templates/default/src/actions/tags/tag-actions.ts +104 -0
  59. package/templates/default/src/actions/template-step/template-step-actions.ts +332 -0
  60. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +278 -0
  61. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +238 -0
  62. package/templates/default/src/actions/test-case/test-case-actions.ts +419 -0
  63. package/templates/default/src/actions/test-run/test-run-actions.ts +1185 -0
  64. package/templates/default/src/actions/test-suite/test-suite-actions.ts +253 -0
  65. package/templates/default/src/actions/user/user-actions.ts +13 -0
  66. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -0
  67. package/templates/default/src/app/(base)/environments/environment-form.tsx +219 -0
  68. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -0
  69. package/templates/default/src/app/(base)/environments/environment-table.tsx +24 -0
  70. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +46 -0
  71. package/templates/default/src/app/(base)/environments/page.tsx +59 -0
  72. package/templates/default/src/app/(base)/layout.tsx +10 -0
  73. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -0
  74. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -0
  75. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -0
  76. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -0
  77. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -0
  78. package/templates/default/src/app/(base)/locator-groups/page.tsx +61 -0
  79. package/templates/default/src/app/(base)/locators/create/page.tsx +38 -0
  80. package/templates/default/src/app/(base)/locators/locator-form.tsx +163 -0
  81. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +90 -0
  82. package/templates/default/src/app/(base)/locators/locator-table.tsx +28 -0
  83. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +45 -0
  84. package/templates/default/src/app/(base)/locators/page.tsx +65 -0
  85. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +66 -0
  86. package/templates/default/src/app/(base)/modules/create/page.tsx +34 -0
  87. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +46 -0
  88. package/templates/default/src/app/(base)/modules/module-form.tsx +126 -0
  89. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +85 -0
  90. package/templates/default/src/app/(base)/modules/module-table.tsx +24 -0
  91. package/templates/default/src/app/(base)/modules/page.tsx +59 -0
  92. package/templates/default/src/app/(base)/reports/[id]/page.tsx +517 -0
  93. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -0
  94. package/templates/default/src/app/(base)/reports/feature-chart.tsx +78 -0
  95. package/templates/default/src/app/(base)/reports/overview-chart.tsx +46 -0
  96. package/templates/default/src/app/(base)/reports/page.tsx +98 -0
  97. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +16 -0
  98. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -0
  99. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -0
  100. package/templates/default/src/app/(base)/reports/report-view-table-columns.tsx +131 -0
  101. package/templates/default/src/app/(base)/reports/report-view-table.tsx +82 -0
  102. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +42 -0
  103. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -0
  104. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -0
  105. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -0
  106. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -0
  107. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -0
  108. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +60 -0
  109. package/templates/default/src/app/(base)/reviews/create/page.tsx +26 -0
  110. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +15 -0
  111. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +26 -0
  112. package/templates/default/src/app/(base)/reviews/page.tsx +26 -0
  113. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +26 -0
  114. package/templates/default/src/app/(base)/reviews/review-form.tsx +11 -0
  115. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +9 -0
  116. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +9 -0
  117. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +15 -0
  118. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -0
  119. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -0
  120. package/templates/default/src/app/(base)/tags/page.tsx +58 -0
  121. package/templates/default/src/app/(base)/tags/tag-form.tsx +147 -0
  122. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -0
  123. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -0
  124. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -0
  125. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +45 -0
  126. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -0
  127. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +167 -0
  128. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -0
  129. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +32 -0
  130. package/templates/default/src/app/(base)/template-steps/create/page.tsx +37 -0
  131. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +49 -0
  132. package/templates/default/src/app/(base)/template-steps/page.tsx +59 -0
  133. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +213 -0
  134. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +384 -0
  135. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +158 -0
  136. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +24 -0
  137. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -0
  138. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -0
  139. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -0
  140. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +84 -0
  141. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +262 -0
  142. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -0
  143. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -0
  144. package/templates/default/src/app/(base)/test-cases/create/page.tsx +76 -0
  145. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +96 -0
  146. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +38 -0
  147. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +73 -0
  148. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -0
  149. package/templates/default/src/app/(base)/test-cases/page.tsx +60 -0
  150. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +82 -0
  151. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +395 -0
  152. package/templates/default/src/app/(base)/test-cases/test-case-table-columns.tsx +90 -0
  153. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +35 -0
  154. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +56 -0
  155. package/templates/default/src/app/(base)/test-runs/create/page.tsx +47 -0
  156. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -0
  157. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +512 -0
  158. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -0
  159. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -0
  160. package/templates/default/src/app/(base)/test-suites/create/page.tsx +45 -0
  161. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +55 -0
  162. package/templates/default/src/app/(base)/test-suites/page.tsx +82 -0
  163. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +269 -0
  164. package/templates/default/src/app/(base)/test-suites/test-suite-table-columns.tsx +97 -0
  165. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +29 -0
  166. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -0
  167. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +13 -0
  168. package/templates/default/src/app/(dashboard-components)/data-card.tsx +27 -0
  169. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +57 -0
  170. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -0
  171. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +45 -0
  172. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +133 -0
  173. package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +420 -0
  174. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -0
  175. package/templates/default/src/app/favicon.ico +0 -0
  176. package/templates/default/src/app/globals.css +147 -0
  177. package/templates/default/src/app/layout.tsx +171 -0
  178. package/templates/default/src/app/page.tsx +64 -0
  179. package/templates/default/src/assets/icons/empty-tube.tsx +23 -0
  180. package/templates/default/src/assets/icons/tube-plus.tsx +29 -0
  181. package/templates/default/src/components/base-node.tsx +21 -0
  182. package/templates/default/src/components/chart/pie-chart.tsx +73 -0
  183. package/templates/default/src/components/data-extraction/locator-inspector.tsx +460 -0
  184. package/templates/default/src/components/data-state/empty-state.tsx +40 -0
  185. package/templates/default/src/components/data-visualization/info-card.tsx +70 -0
  186. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -0
  187. package/templates/default/src/components/devtools/providers.tsx +13 -0
  188. package/templates/default/src/components/diagram/button-edge.tsx +54 -0
  189. package/templates/default/src/components/diagram/dynamic-parameters.tsx +438 -0
  190. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -0
  191. package/templates/default/src/components/diagram/flow-diagram.tsx +470 -0
  192. package/templates/default/src/components/diagram/node-form.tsx +262 -0
  193. package/templates/default/src/components/diagram/options-header-node.tsx +57 -0
  194. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -0
  195. package/templates/default/src/components/form/error-message.tsx +7 -0
  196. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -0
  197. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -0
  198. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -0
  199. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -0
  200. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -0
  201. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -0
  202. package/templates/default/src/components/logo.tsx +15 -0
  203. package/templates/default/src/components/navigation/command-badge.tsx +34 -0
  204. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -0
  205. package/templates/default/src/components/navigation/entity-search-command.tsx +116 -0
  206. package/templates/default/src/components/navigation/nav-card.tsx +31 -0
  207. package/templates/default/src/components/navigation/nav-command.tsx +508 -0
  208. package/templates/default/src/components/navigation/nav-link.tsx +60 -0
  209. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -0
  210. package/templates/default/src/components/node-header.tsx +159 -0
  211. package/templates/default/src/components/reports/test-case-logs-modal.tsx +253 -0
  212. package/templates/default/src/components/table/table-actions.tsx +172 -0
  213. package/templates/default/src/components/test-run/download-logs-button.tsx +99 -0
  214. package/templates/default/src/components/test-run/log-viewer.tsx +445 -0
  215. package/templates/default/src/components/test-run/test-run-details.tsx +611 -0
  216. package/templates/default/src/components/test-run/test-run-header.tsx +149 -0
  217. package/templates/default/src/components/test-run/view-report-button.tsx +102 -0
  218. package/templates/default/src/components/theme/mode-toggle.tsx +54 -0
  219. package/templates/default/src/components/theme/theme-provider.tsx +8 -0
  220. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -0
  221. package/templates/default/src/components/typography/page-header.tsx +7 -0
  222. package/templates/default/src/components/ui/alert-dialog.tsx +106 -0
  223. package/templates/default/src/components/ui/alert.tsx +43 -0
  224. package/templates/default/src/components/ui/avatar.tsx +40 -0
  225. package/templates/default/src/components/ui/badge.tsx +29 -0
  226. package/templates/default/src/components/ui/button.tsx +47 -0
  227. package/templates/default/src/components/ui/calendar.tsx +158 -0
  228. package/templates/default/src/components/ui/card.tsx +43 -0
  229. package/templates/default/src/components/ui/chart.tsx +369 -0
  230. package/templates/default/src/components/ui/checkbox.tsx +28 -0
  231. package/templates/default/src/components/ui/command.tsx +135 -0
  232. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -0
  233. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -0
  234. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -0
  235. package/templates/default/src/components/ui/data-table.tsx +267 -0
  236. package/templates/default/src/components/ui/dialog.tsx +97 -0
  237. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -0
  238. package/templates/default/src/components/ui/empty.tsx +104 -0
  239. package/templates/default/src/components/ui/input.tsx +22 -0
  240. package/templates/default/src/components/ui/kbd.tsx +28 -0
  241. package/templates/default/src/components/ui/label.tsx +19 -0
  242. package/templates/default/src/components/ui/loading.tsx +12 -0
  243. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -0
  244. package/templates/default/src/components/ui/multi-select.tsx +142 -0
  245. package/templates/default/src/components/ui/navigation-menu.tsx +120 -0
  246. package/templates/default/src/components/ui/popover.tsx +33 -0
  247. package/templates/default/src/components/ui/progress.tsx +25 -0
  248. package/templates/default/src/components/ui/radio-group.tsx +44 -0
  249. package/templates/default/src/components/ui/scroll-area.tsx +40 -0
  250. package/templates/default/src/components/ui/select.tsx +144 -0
  251. package/templates/default/src/components/ui/separator.tsx +22 -0
  252. package/templates/default/src/components/ui/skeleton.tsx +7 -0
  253. package/templates/default/src/components/ui/table.tsx +76 -0
  254. package/templates/default/src/components/ui/tabs.tsx +55 -0
  255. package/templates/default/src/components/ui/textarea.tsx +21 -0
  256. package/templates/default/src/components/ui/toast.tsx +113 -0
  257. package/templates/default/src/components/ui/toaster.tsx +26 -0
  258. package/templates/default/src/components/ui/tooltip.tsx +32 -0
  259. package/templates/default/src/components/user-prompt/delete-prompt.tsx +87 -0
  260. package/templates/default/src/config/db-config.ts +10 -0
  261. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -0
  262. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -0
  263. package/templates/default/src/constants/form-opts/locator-form-opts.ts +20 -0
  264. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -0
  265. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -0
  266. package/templates/default/src/constants/form-opts/review-form-opts.ts +23 -0
  267. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -0
  268. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -0
  269. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -0
  270. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -0
  271. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -0
  272. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -0
  273. package/templates/default/src/constants/form-opts/test-run-form-opts.ts +31 -0
  274. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -0
  275. package/templates/default/src/hooks/use-toast.ts +187 -0
  276. package/templates/default/src/lib/bidirectional-sync.ts +432 -0
  277. package/templates/default/src/lib/database-sync.ts +531 -0
  278. package/templates/default/src/lib/environment-file-utils.ts +221 -0
  279. package/templates/default/src/lib/feature-file-generator.ts +411 -0
  280. package/templates/default/src/lib/gherkin-parser.ts +259 -0
  281. package/templates/default/src/lib/locator-group-file-utils.ts +370 -0
  282. package/templates/default/src/lib/metrics/metric-calculator.ts +613 -0
  283. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -0
  284. package/templates/default/src/lib/path-helpers/module-path.ts +71 -0
  285. package/templates/default/src/lib/test-case-utils.ts +6 -0
  286. package/templates/default/src/lib/test-run/log-formatter.ts +83 -0
  287. package/templates/default/src/lib/test-run/process-manager.ts +191 -0
  288. package/templates/default/src/lib/test-run/report-parser.ts +316 -0
  289. package/templates/default/src/lib/test-run/test-run-executor.ts +144 -0
  290. package/templates/default/src/lib/test-run/winston-logger.ts +95 -0
  291. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -0
  292. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -0
  293. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -0
  294. package/templates/default/src/lib/utils/node-param-validation.ts +81 -0
  295. package/templates/default/src/lib/utils/template-step-file-generator.ts +167 -0
  296. package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +723 -0
  297. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -0
  298. package/templates/default/src/lib/utils.ts +31 -0
  299. package/templates/default/src/tests/config/environments/environments.json +14 -0
  300. package/templates/default/src/tests/config/executor/world.ts +41 -0
  301. package/templates/default/src/tests/executor.ts +80 -0
  302. package/templates/default/src/tests/hooks/hooks.ts +99 -0
  303. package/templates/default/src/tests/mapping/locator-map.json +1 -0
  304. package/templates/default/src/tests/steps/actions/click.step.ts +62 -0
  305. package/templates/default/src/tests/steps/actions/hover.step.ts +31 -0
  306. package/templates/default/src/tests/steps/actions/input.step.ts +149 -0
  307. package/templates/default/src/tests/steps/actions/navigation.step.ts +72 -0
  308. package/templates/default/src/tests/steps/actions/random_data.step.ts +146 -0
  309. package/templates/default/src/tests/steps/actions/store.step.ts +90 -0
  310. package/templates/default/src/tests/steps/actions/wait.step.ts +107 -0
  311. package/templates/default/src/tests/steps/validations/active_state_assertion.step.ts +34 -0
  312. package/templates/default/src/tests/steps/validations/navigation_assertion.step.ts +23 -0
  313. package/templates/default/src/tests/steps/validations/text_assertion.step.ts +111 -0
  314. package/templates/default/src/tests/steps/validations/visibility_assertion.step.ts +30 -0
  315. package/templates/default/src/tests/support/parameter-types.ts +12 -0
  316. package/templates/default/src/tests/utils/cache.util.ts +260 -0
  317. package/templates/default/src/tests/utils/cli.util.ts +177 -0
  318. package/templates/default/src/tests/utils/environment.util.ts +65 -0
  319. package/templates/default/src/tests/utils/locator.util.ts +248 -0
  320. package/templates/default/src/tests/utils/random-data.util.ts +45 -0
  321. package/templates/default/src/tests/utils/spawner.util.ts +617 -0
  322. package/templates/default/src/types/diagram/diagram.ts +34 -0
  323. package/templates/default/src/types/diagram/template-step.ts +11 -0
  324. package/templates/default/src/types/executor/browser.type.ts +1 -0
  325. package/templates/default/src/types/form/actionHandler.ts +6 -0
  326. package/templates/default/src/types/locator/locator.type.ts +11 -0
  327. package/templates/default/src/types/step/step.type.ts +1 -0
  328. package/templates/default/src/types/table/data-table.ts +6 -0
  329. package/templates/default/tailwind.config.ts +62 -0
  330. package/templates/default/tsconfig.json +28 -0
@@ -0,0 +1,221 @@
1
+ import { promises as fs } from 'fs'
2
+ import * as path from 'path'
3
+ import prisma from '@/config/db-config'
4
+
5
+ interface EnvironmentConfig {
6
+ baseUrl: string
7
+ apiBaseUrl: string
8
+ email: string
9
+ password: string
10
+ }
11
+
12
+ /**
13
+ * Gets the file path for the environments.json file
14
+ */
15
+ export function getEnvironmentsFilePath(): string {
16
+ return path.join('src', 'tests', 'config', 'environments', 'environments.json')
17
+ }
18
+
19
+ /**
20
+ * Ensures the config directory exists
21
+ */
22
+ export async function ensureConfigDirectoryExists(): Promise<void> {
23
+ const filePath = getEnvironmentsFilePath()
24
+ const dir = path.dirname(filePath)
25
+ try {
26
+ await fs.access(dir)
27
+ } catch {
28
+ await fs.mkdir(dir, { recursive: true })
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Generates JSON content for environments from database
34
+ */
35
+ export async function generateEnvironmentsContent(): Promise<Record<string, EnvironmentConfig>> {
36
+ try {
37
+ const environments = await prisma.environment.findMany({
38
+ orderBy: { createdAt: 'asc' },
39
+ })
40
+
41
+ const environmentsConfig: Record<string, EnvironmentConfig> = {}
42
+
43
+ environments.forEach(env => {
44
+ const envKey = env.name.toLowerCase().replace(/\s+/g, '_')
45
+ environmentsConfig[envKey] = {
46
+ baseUrl: env.baseUrl,
47
+ apiBaseUrl: env.apiBaseUrl || '',
48
+ email: env.username || '',
49
+ password: env.password || '',
50
+ }
51
+ })
52
+
53
+ return environmentsConfig
54
+ } catch (error) {
55
+ console.error('Error generating environments content:', error)
56
+ return {}
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Creates or updates the environments.json file
62
+ */
63
+ export async function createOrUpdateEnvironmentsFile(): Promise<boolean> {
64
+ try {
65
+ const filePath = getEnvironmentsFilePath()
66
+ await ensureConfigDirectoryExists()
67
+
68
+ const content = await generateEnvironmentsContent()
69
+
70
+ // If no environments exist, delete the file
71
+ if (Object.keys(content).length === 0) {
72
+ await deleteEnvironmentsFile()
73
+ return true
74
+ }
75
+
76
+ await fs.writeFile(filePath, JSON.stringify(content, null, 2))
77
+ return true
78
+ } catch (error) {
79
+ console.error('Error creating/updating environments file:', error)
80
+ return false
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Deletes the environments.json file
86
+ */
87
+ export async function deleteEnvironmentsFile(): Promise<boolean> {
88
+ try {
89
+ const filePath = getEnvironmentsFilePath()
90
+
91
+ // Check if file exists before trying to delete
92
+ try {
93
+ await fs.access(filePath)
94
+ } catch {
95
+ return true // File doesn't exist, nothing to delete
96
+ }
97
+
98
+ await fs.unlink(filePath)
99
+ return true
100
+ } catch (error) {
101
+ console.error('Error deleting environments file:', error)
102
+ return false
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Reads and parses the content of the environments.json file
108
+ */
109
+ export async function readEnvironmentsFile(): Promise<{
110
+ filePath: string
111
+ content: Record<string, EnvironmentConfig>
112
+ } | null> {
113
+ try {
114
+ const filePath = getEnvironmentsFilePath()
115
+
116
+ try {
117
+ await fs.access(filePath)
118
+ } catch {
119
+ return null // File doesn't exist
120
+ }
121
+
122
+ const fileContent = await fs.readFile(filePath, 'utf-8')
123
+ const jsonContent = JSON.parse(fileContent)
124
+
125
+ return { filePath, content: jsonContent }
126
+ } catch (error) {
127
+ console.error('Error reading environments file:', error)
128
+ return null
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Updates a specific environment entry in the environments.json file
134
+ */
135
+ export async function updateEnvironmentEntry(environmentId: string, oldName?: string): Promise<boolean> {
136
+ try {
137
+ // Get the environment from database
138
+ const environment = await prisma.environment.findUnique({
139
+ where: { id: environmentId },
140
+ })
141
+
142
+ if (!environment) {
143
+ console.error(`Environment with ID ${environmentId} not found`)
144
+ return false
145
+ }
146
+
147
+ const filePath = getEnvironmentsFilePath()
148
+
149
+ // Read existing content
150
+ let environmentsConfig: Record<string, EnvironmentConfig> = {}
151
+ try {
152
+ await fs.access(filePath)
153
+ const fileContent = await fs.readFile(filePath, 'utf-8')
154
+ environmentsConfig = JSON.parse(fileContent)
155
+ } catch {
156
+ // File doesn't exist, start with empty object
157
+ }
158
+
159
+ // Remove old entry if name changed
160
+ if (oldName) {
161
+ const oldKey = oldName.toLowerCase().replace(/\s+/g, '_')
162
+ delete environmentsConfig[oldKey]
163
+ }
164
+
165
+ // Add/update the environment entry
166
+ const envKey = environment.name.toLowerCase().replace(/\s+/g, '_')
167
+ environmentsConfig[envKey] = {
168
+ baseUrl: environment.baseUrl,
169
+ apiBaseUrl: environment.apiBaseUrl || '',
170
+ email: environment.username || '',
171
+ password: environment.password || '',
172
+ }
173
+
174
+ // Ensure directory exists
175
+ await ensureConfigDirectoryExists()
176
+
177
+ // Write updated content
178
+ await fs.writeFile(filePath, JSON.stringify(environmentsConfig, null, 2))
179
+ return true
180
+ } catch (error) {
181
+ console.error('Error updating environment entry:', error)
182
+ return false
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Removes a specific environment entry from the environments.json file
188
+ */
189
+ export async function removeEnvironmentEntry(environmentName: string): Promise<boolean> {
190
+ try {
191
+ const filePath = getEnvironmentsFilePath()
192
+
193
+ // Check if file exists
194
+ try {
195
+ await fs.access(filePath)
196
+ } catch {
197
+ return true // File doesn't exist, nothing to remove
198
+ }
199
+
200
+ // Read existing content
201
+ const fileContent = await fs.readFile(filePath, 'utf-8')
202
+ const environmentsConfig: Record<string, EnvironmentConfig> = JSON.parse(fileContent)
203
+
204
+ // Remove the environment entry
205
+ const envKey = environmentName.toLowerCase().replace(/\s+/g, '_')
206
+ delete environmentsConfig[envKey]
207
+
208
+ // If no environments left, delete the file
209
+ if (Object.keys(environmentsConfig).length === 0) {
210
+ await deleteEnvironmentsFile()
211
+ return true
212
+ }
213
+
214
+ // Write updated content
215
+ await fs.writeFile(filePath, JSON.stringify(environmentsConfig, null, 2))
216
+ return true
217
+ } catch (error) {
218
+ console.error('Error removing environment entry:', error)
219
+ return false
220
+ }
221
+ }
@@ -0,0 +1,411 @@
1
+ import { promises as fs } from 'fs'
2
+ import { join, dirname } from 'path'
3
+ import prisma from '@/config/db-config'
4
+ import { buildModulePath } from '@/lib/path-helpers/module-path'
5
+
6
+ /**
7
+ * Checks if a directory is empty (no files or subdirectories)
8
+ * @param dirPath - Path to the directory to check
9
+ * @returns Promise<boolean> - True if directory is empty, false otherwise
10
+ */
11
+ async function isDirectoryEmpty(dirPath: string): Promise<boolean> {
12
+ try {
13
+ const entries = await fs.readdir(dirPath)
14
+ return entries.length === 0
15
+ } catch (error) {
16
+ // If directory doesn't exist or can't be read, consider it empty
17
+ console.warn(`Could not read directory ${dirPath}:`, error)
18
+ return true
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Removes empty directories up the hierarchy until a non-empty directory is found
24
+ * @param dirPath - Starting directory path to clean up
25
+ * @param basePath - Base path to stop cleaning (e.g., features directory)
26
+ * @returns Promise<void>
27
+ */
28
+ async function removeEmptyDirectoriesUp(dirPath: string, basePath: string): Promise<void> {
29
+ let currentPath = dirPath
30
+
31
+ // Keep going up the directory tree until we reach the base path
32
+ while (currentPath !== basePath && currentPath !== dirname(currentPath)) {
33
+ try {
34
+ // Check if current directory is empty
35
+ if (await isDirectoryEmpty(currentPath)) {
36
+ await fs.rmdir(currentPath)
37
+ console.log(`Removed empty directory: ${currentPath}`)
38
+ // Move up one level
39
+ currentPath = dirname(currentPath)
40
+ } else {
41
+ // Directory is not empty, stop cleaning
42
+ break
43
+ }
44
+ } catch (error) {
45
+ // If we can't remove the directory or it doesn't exist, stop
46
+ console.warn(`Could not remove directory ${currentPath}:`, error)
47
+ break
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Generates a Gherkin feature file for a test suite
54
+ * @param testSuiteId - The ID of the test suite
55
+ * @param testSuiteName - The name of the test suite
56
+ * @param testSuiteDescription - The description of the test suite
57
+ * @param moduleName - The name of the module the test suite belongs to
58
+ * @returns Promise<string> - The path to the generated feature file
59
+ */
60
+ export async function generateFeatureFile(
61
+ testSuiteId: string,
62
+ testSuiteName: string,
63
+ testSuiteDescription?: string,
64
+ ): Promise<string> {
65
+ try {
66
+ // Fetch test suite with test cases, steps, tags, and all modules for path building
67
+ const [testSuite, allModules] = await Promise.all([
68
+ prisma.testSuite.findUnique({
69
+ where: { id: testSuiteId },
70
+ include: {
71
+ testCases: {
72
+ include: {
73
+ steps: {
74
+ include: {
75
+ parameters: true,
76
+ },
77
+ orderBy: {
78
+ order: 'asc',
79
+ },
80
+ },
81
+ tags: true,
82
+ },
83
+ },
84
+ module: true,
85
+ tags: true,
86
+ },
87
+ }),
88
+ prisma.module.findMany(), // Get all modules to build hierarchy path
89
+ ])
90
+
91
+ if (!testSuite) {
92
+ throw new Error(`Test suite with ID ${testSuiteId} not found`)
93
+ }
94
+
95
+ // Build the module path for directory structure
96
+ const modulePath = buildModulePath(allModules, testSuite.module)
97
+
98
+ // Generate feature file content
99
+ const featureContent = generateFeatureContent(
100
+ testSuiteDescription || testSuiteName, // Use description as feature title, fallback to name
101
+ testSuite.testCases,
102
+ testSuite.tags,
103
+ )
104
+
105
+ // Create the features directory with module path
106
+ const featuresBaseDir = join(process.cwd(), 'src', 'tests', 'features')
107
+ const moduleDir = join(featuresBaseDir, modulePath.substring(1)) // Remove leading slash
108
+ await fs.mkdir(moduleDir, { recursive: true })
109
+
110
+ // Generate a safe filename from the test suite name only
111
+ const safeFileName = generateSafeFileName(testSuiteName)
112
+ const featureFilePath = join(moduleDir, `${safeFileName}.feature`)
113
+
114
+ // Write the feature file
115
+ await fs.writeFile(featureFilePath, featureContent, 'utf8')
116
+
117
+ return featureFilePath
118
+ } catch (error) {
119
+ console.error('Error generating feature file:', error)
120
+ throw error
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Generates the content for a Gherkin feature file
126
+ */
127
+ function generateFeatureContent(
128
+ featureTitle: string,
129
+ testCases: Array<{
130
+ title: string
131
+ description: string
132
+ steps: Array<{
133
+ gherkinStep: string
134
+ order: number
135
+ }>
136
+ tags?: Array<{
137
+ tagExpression: string
138
+ }>
139
+ }>,
140
+ testSuiteTags?: Array<{
141
+ tagExpression: string
142
+ }>,
143
+ ): string {
144
+ const lines: string[] = []
145
+
146
+ // Warning header
147
+ lines.push('# AUTO-GENERATED FILE - DO NOT EDIT MANUALLY')
148
+ lines.push('# This file is automatically generated from Test Suite data.')
149
+ lines.push('# Any manual changes will be overwritten when the Test Suite is updated.')
150
+ lines.push('# To modify this feature, update the corresponding Test Suite in the application.')
151
+ lines.push('')
152
+
153
+ // Add feature-level tags (one tag per line)
154
+ if (testSuiteTags && testSuiteTags.length > 0) {
155
+ testSuiteTags.forEach(tag => {
156
+ lines.push(tag.tagExpression)
157
+ })
158
+ }
159
+
160
+ // Feature header - use description as feature title
161
+ lines.push(`Feature: ${featureTitle}`)
162
+ lines.push('')
163
+
164
+ // Get test suite tag expressions for deduplication
165
+ const testSuiteTagExpressions = new Set((testSuiteTags || []).map(tag => tag.tagExpression.toLowerCase()))
166
+
167
+ // Generate scenarios for each test case that has steps
168
+ let scenarioCount = 0
169
+ testCases.forEach(testCase => {
170
+ // Only generate scenario if test case has steps
171
+ if (testCase.steps && testCase.steps.length > 0) {
172
+ // Generate Gherkin steps from test case steps
173
+ const gherkinSteps = generateGherkinStepsFromTestCase(testCase.steps)
174
+
175
+ // Only add scenario if there are actual gherkin steps
176
+ if (gherkinSteps.length > 0) {
177
+ if (scenarioCount > 0) {
178
+ lines.push('') // Add blank line between scenarios
179
+ }
180
+
181
+ // Add scenario-level tags (skip if already present at feature level)
182
+ if (testCase.tags && testCase.tags.length > 0) {
183
+ testCase.tags.forEach(tag => {
184
+ // Only add tag if it's not already present at feature level
185
+ if (!testSuiteTagExpressions.has(tag.tagExpression.toLowerCase())) {
186
+ lines.push(` ${tag.tagExpression}`)
187
+ }
188
+ })
189
+ }
190
+
191
+ lines.push(` Scenario: [${testCase.title}] ${testCase.description}`)
192
+
193
+ gherkinSteps.forEach(step => {
194
+ lines.push(` ${step}`)
195
+ })
196
+
197
+ scenarioCount++
198
+ }
199
+ }
200
+ })
201
+
202
+ return lines.join('\n') + '\n'
203
+ }
204
+
205
+ /**
206
+ * Generates Gherkin steps from test case steps using the same logic as the frontend
207
+ */
208
+ function generateGherkinStepsFromTestCase(
209
+ steps: Array<{
210
+ gherkinStep: string
211
+ order: number
212
+ }>,
213
+ ): string[] {
214
+ if (!steps || steps.length === 0) {
215
+ return []
216
+ }
217
+
218
+ // Sort steps by order
219
+ const sortedSteps = steps.sort((a, b) => a.order - b.order)
220
+
221
+ let hasThenInPrevious = false
222
+ let hasWhenInPrevious = false
223
+
224
+ return sortedSteps.map((step, index) => {
225
+ const gherkinStep = step.gherkinStep?.trim() || ''
226
+ const firstWord = gherkinStep.split(' ')[0].toLowerCase()
227
+ const hasGherkinKeyword = ['given', 'when', 'then', 'and', 'but'].includes(firstWord)
228
+ const stepWithoutKeyword = hasGherkinKeyword ? gherkinStep.split(' ').slice(1).join(' ') : gherkinStep
229
+
230
+ // First step always starts with Given
231
+ if (index === 0) {
232
+ return `Given ${stepWithoutKeyword}`
233
+ }
234
+
235
+ // Check if this step should be a Then statement
236
+ const isThenStatement =
237
+ firstWord === 'then' ||
238
+ stepWithoutKeyword.toLowerCase().startsWith('should') ||
239
+ stepWithoutKeyword.toLowerCase().startsWith('must') ||
240
+ stepWithoutKeyword.toLowerCase().startsWith('will')
241
+
242
+ // If we haven't seen a Then yet
243
+ if (!hasThenInPrevious) {
244
+ // If this is a Then statement
245
+ if (isThenStatement) {
246
+ hasThenInPrevious = true
247
+ return `Then ${stepWithoutKeyword}`
248
+ }
249
+
250
+ // If we haven't seen a When yet, use When
251
+ if (!hasWhenInPrevious) {
252
+ hasWhenInPrevious = true
253
+ return `When ${stepWithoutKeyword}`
254
+ }
255
+ // After When, use And
256
+ return `And ${stepWithoutKeyword}`
257
+ }
258
+
259
+ // After Then
260
+ if (isThenStatement) {
261
+ // If it's another Then statement, use And
262
+ return `And ${stepWithoutKeyword}`
263
+ }
264
+ // After Then, use When for new actions
265
+ hasThenInPrevious = false
266
+ hasWhenInPrevious = true
267
+ return `When ${stepWithoutKeyword}`
268
+ })
269
+ }
270
+
271
+ /**
272
+ * Deletes a feature file for a test suite
273
+ * @param testSuiteId - The ID of the test suite
274
+ * @returns Promise<boolean> - True if file was deleted, false if file didn't exist
275
+ */
276
+ export async function deleteFeatureFile(testSuiteId: string): Promise<boolean> {
277
+ try {
278
+ // Fetch test suite and all modules to build the correct path
279
+ const [testSuite, allModules] = await Promise.all([
280
+ prisma.testSuite.findUnique({
281
+ where: { id: testSuiteId },
282
+ include: {
283
+ module: true,
284
+ },
285
+ }),
286
+ prisma.module.findMany(),
287
+ ])
288
+
289
+ if (!testSuite) {
290
+ console.warn(`Test suite with ID ${testSuiteId} not found for feature file deletion`)
291
+ return false
292
+ }
293
+
294
+ // Build the module path for directory structure
295
+ const modulePath = buildModulePath(allModules, testSuite.module)
296
+ const safeFileName = generateSafeFileName(testSuite.name)
297
+
298
+ const featuresBaseDir = join(process.cwd(), 'src', 'tests', 'features')
299
+ const moduleDir = join(featuresBaseDir, modulePath.substring(1)) // Remove leading slash
300
+ const featureFilePath = join(moduleDir, `${safeFileName}.feature`)
301
+
302
+ try {
303
+ await fs.unlink(featureFilePath)
304
+ console.log(`Feature file deleted: ${featureFilePath}`)
305
+
306
+ // Clean up empty directories up the module hierarchy
307
+ await removeEmptyDirectoriesUp(moduleDir, featuresBaseDir)
308
+
309
+ return true
310
+ } catch (error: unknown) {
311
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
312
+ console.warn(`Feature file not found for deletion: ${featureFilePath}`)
313
+ return false
314
+ }
315
+ throw error
316
+ }
317
+ } catch (error) {
318
+ console.error('Error deleting feature file:', error)
319
+ throw error
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Regenerates all feature files from the current database state
325
+ * This is useful after merging changes or database migrations to ensure sync
326
+ * @returns Promise<string[]> - Array of generated feature file paths
327
+ */
328
+ export async function regenerateAllFeatureFiles(): Promise<string[]> {
329
+ try {
330
+ console.log('Starting regeneration of all feature files...')
331
+
332
+ // Clear existing feature files directory
333
+ const featuresBaseDir = join(process.cwd(), 'src', 'tests', 'features')
334
+ try {
335
+ await fs.rm(featuresBaseDir, { recursive: true, force: true })
336
+ } catch (error) {
337
+ console.warn('Could not clear features directory:', error)
338
+ }
339
+
340
+ // Fetch all test suites from database
341
+ const testSuites = await prisma.testSuite.findMany({
342
+ include: {
343
+ testCases: {
344
+ include: {
345
+ steps: {
346
+ include: {
347
+ parameters: true,
348
+ },
349
+ orderBy: {
350
+ order: 'asc',
351
+ },
352
+ },
353
+ tags: true,
354
+ },
355
+ },
356
+ module: true,
357
+ tags: true,
358
+ },
359
+ })
360
+
361
+ // Fetch all modules for path building
362
+ const allModules = await prisma.module.findMany()
363
+
364
+ const generatedFiles: string[] = []
365
+
366
+ // Generate feature file for each test suite
367
+ for (const testSuite of testSuites) {
368
+ try {
369
+ const modulePath = buildModulePath(allModules, testSuite.module)
370
+ const featureContent = generateFeatureContent(
371
+ testSuite.description || testSuite.name,
372
+ testSuite.testCases,
373
+ testSuite.tags,
374
+ )
375
+
376
+ // Create the features directory with module path
377
+ const moduleDir = join(featuresBaseDir, modulePath.substring(1)) // Remove leading slash
378
+ await fs.mkdir(moduleDir, { recursive: true })
379
+
380
+ // Generate filename and write file
381
+ const safeFileName = generateSafeFileName(testSuite.name)
382
+ const featureFilePath = join(moduleDir, `${safeFileName}.feature`)
383
+
384
+ await fs.writeFile(featureFilePath, featureContent, 'utf8')
385
+ generatedFiles.push(featureFilePath)
386
+
387
+ console.log(`Generated: ${featureFilePath}`)
388
+ } catch (error) {
389
+ console.error(`Error generating feature file for test suite ${testSuite.name}:`, error)
390
+ }
391
+ }
392
+
393
+ console.log(`Regeneration complete. Generated ${generatedFiles.length} feature files.`)
394
+ return generatedFiles
395
+ } catch (error) {
396
+ console.error('Error during feature files regeneration:', error)
397
+ throw error
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Generates a safe filename from test suite name
403
+ */
404
+ function generateSafeFileName(testSuiteName: string): string {
405
+ // Convert to lowercase and replace spaces and special characters with hyphens
406
+ return testSuiteName
407
+ .toLowerCase()
408
+ .replace(/[^a-z0-9]+/g, '-')
409
+ .replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
410
+ .replace(/-+/g, '-') // Replace multiple consecutive hyphens with single hyphen
411
+ }