create-appraisejs 0.1.6 → 0.1.8-alpha

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 (320) hide show
  1. package/README.md +45 -45
  2. package/dist/cli.js +4 -4
  3. package/dist/cli.js.map +1 -1
  4. package/dist/copy-template.d.ts +2 -1
  5. package/dist/copy-template.d.ts.map +1 -1
  6. package/dist/copy-template.js +10 -7
  7. package/dist/copy-template.js.map +1 -1
  8. package/dist/copy-template.test.js +19 -0
  9. package/dist/copy-template.test.js.map +1 -1
  10. package/package.json +69 -67
  11. package/templates/default/.vscode/settings.json +5 -0
  12. package/templates/default/appraisejs.config.json +1 -1
  13. package/templates/default/components.json +24 -24
  14. package/templates/default/cucumber.mjs +16 -0
  15. package/templates/default/eslint.config.mjs +15 -15
  16. package/templates/default/next.config.ts +13 -7
  17. package/templates/default/package-lock.json +13732 -14321
  18. package/templates/default/package.json +11 -9
  19. package/templates/default/postcss.config.mjs +8 -8
  20. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  21. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  22. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  23. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  24. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  25. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  26. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  27. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  28. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  29. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  30. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  31. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  32. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  33. package/templates/default/prisma/schema.prisma +554 -554
  34. package/templates/default/scripts/regenerate-features.ts +94 -94
  35. package/templates/default/scripts/setup-env.ts +19 -19
  36. package/templates/default/scripts/sync-all.ts +341 -341
  37. package/templates/default/scripts/sync-appraise-base-template.ts +52 -2
  38. package/templates/default/scripts/sync-environments.ts +323 -323
  39. package/templates/default/scripts/sync-locator-groups.ts +413 -413
  40. package/templates/default/scripts/sync-locators.ts +402 -402
  41. package/templates/default/scripts/sync-modules.ts +349 -349
  42. package/templates/default/scripts/sync-tags.ts +292 -292
  43. package/templates/default/scripts/sync-template-step-groups.ts +399 -399
  44. package/templates/default/scripts/sync-template-steps.ts +806 -806
  45. package/templates/default/scripts/sync-test-cases.ts +905 -905
  46. package/templates/default/scripts/sync-test-suites.ts +411 -411
  47. package/templates/default/src/actions/conflict/conflict.action.ts +33 -33
  48. package/templates/default/src/actions/dashboard/dashboard-actions.ts +240 -240
  49. package/templates/default/src/actions/environments/environment-actions.ts +205 -205
  50. package/templates/default/src/actions/locator/locator-actions.ts +547 -547
  51. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +344 -344
  52. package/templates/default/src/actions/modules/module-actions.ts +133 -133
  53. package/templates/default/src/actions/reports/report-actions.ts +613 -613
  54. package/templates/default/src/actions/review/review-actions.ts +147 -147
  55. package/templates/default/src/actions/tags/tag-actions.ts +104 -104
  56. package/templates/default/src/actions/template-step/template-step-actions.ts +332 -332
  57. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +278 -278
  58. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +238 -238
  59. package/templates/default/src/actions/test-case/test-case-actions.ts +419 -419
  60. package/templates/default/src/actions/test-run/test-run-actions.ts +1185 -1185
  61. package/templates/default/src/actions/test-suite/test-suite-actions.ts +253 -253
  62. package/templates/default/src/actions/user/user-actions.ts +13 -13
  63. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
  64. package/templates/default/src/app/(base)/environments/environment-form.tsx +219 -219
  65. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
  66. package/templates/default/src/app/(base)/environments/environment-table.tsx +24 -24
  67. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +46 -46
  68. package/templates/default/src/app/(base)/environments/page.tsx +59 -59
  69. package/templates/default/src/app/(base)/layout.tsx +10 -10
  70. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -44
  71. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -215
  72. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -77
  73. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -28
  74. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -46
  75. package/templates/default/src/app/(base)/locator-groups/page.tsx +61 -61
  76. package/templates/default/src/app/(base)/locators/create/page.tsx +38 -38
  77. package/templates/default/src/app/(base)/locators/locator-form.tsx +163 -163
  78. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +73 -90
  79. package/templates/default/src/app/(base)/locators/locator-table.tsx +28 -28
  80. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +45 -45
  81. package/templates/default/src/app/(base)/locators/page.tsx +65 -65
  82. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +66 -66
  83. package/templates/default/src/app/(base)/modules/create/page.tsx +34 -34
  84. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +46 -46
  85. package/templates/default/src/app/(base)/modules/module-form.tsx +126 -126
  86. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +85 -85
  87. package/templates/default/src/app/(base)/modules/module-table.tsx +24 -24
  88. package/templates/default/src/app/(base)/modules/page.tsx +59 -59
  89. package/templates/default/src/app/(base)/reports/[id]/page.tsx +517 -517
  90. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
  91. package/templates/default/src/app/(base)/reports/feature-chart.tsx +78 -78
  92. package/templates/default/src/app/(base)/reports/overview-chart.tsx +46 -46
  93. package/templates/default/src/app/(base)/reports/page.tsx +98 -98
  94. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +16 -16
  95. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -189
  96. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -72
  97. package/templates/default/src/app/(base)/reports/report-view-table-columns.tsx +131 -131
  98. package/templates/default/src/app/(base)/reports/report-view-table.tsx +82 -82
  99. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +42 -42
  100. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -115
  101. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -27
  102. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -42
  103. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -79
  104. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -27
  105. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +60 -60
  106. package/templates/default/src/app/(base)/reviews/create/page.tsx +26 -26
  107. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +15 -15
  108. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +26 -26
  109. package/templates/default/src/app/(base)/reviews/page.tsx +26 -26
  110. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +26 -26
  111. package/templates/default/src/app/(base)/reviews/review-form.tsx +11 -11
  112. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +9 -9
  113. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +9 -9
  114. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +15 -15
  115. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -39
  116. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -50
  117. package/templates/default/src/app/(base)/tags/page.tsx +58 -58
  118. package/templates/default/src/app/(base)/tags/tag-form.tsx +147 -147
  119. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -63
  120. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -29
  121. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -28
  122. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +45 -45
  123. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -60
  124. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +167 -167
  125. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -89
  126. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +32 -32
  127. package/templates/default/src/app/(base)/template-steps/create/page.tsx +37 -37
  128. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +49 -49
  129. package/templates/default/src/app/(base)/template-steps/page.tsx +59 -59
  130. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +213 -213
  131. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +384 -384
  132. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +158 -158
  133. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +24 -24
  134. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -56
  135. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -89
  136. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -58
  137. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +84 -84
  138. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +262 -262
  139. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -76
  140. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -32
  141. package/templates/default/src/app/(base)/test-cases/create/page.tsx +76 -76
  142. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +96 -96
  143. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +38 -38
  144. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +73 -73
  145. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -106
  146. package/templates/default/src/app/(base)/test-cases/page.tsx +60 -60
  147. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +82 -82
  148. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +395 -395
  149. package/templates/default/src/app/(base)/test-cases/test-case-table-columns.tsx +90 -90
  150. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +35 -35
  151. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +56 -56
  152. package/templates/default/src/app/(base)/test-runs/create/page.tsx +47 -47
  153. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
  154. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +508 -512
  155. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -229
  156. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -127
  157. package/templates/default/src/app/(base)/test-suites/create/page.tsx +45 -45
  158. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +55 -55
  159. package/templates/default/src/app/(base)/test-suites/page.tsx +82 -82
  160. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +269 -269
  161. package/templates/default/src/app/(base)/test-suites/test-suite-table-columns.tsx +97 -97
  162. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +29 -29
  163. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -187
  164. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +12 -12
  165. package/templates/default/src/app/(dashboard-components)/data-card.tsx +26 -26
  166. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +56 -56
  167. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -87
  168. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +44 -44
  169. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +133 -133
  170. package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +420 -420
  171. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -146
  172. package/templates/default/src/app/globals.css +147 -147
  173. package/templates/default/src/app/layout.tsx +171 -171
  174. package/templates/default/src/app/page.tsx +64 -64
  175. package/templates/default/src/assets/icons/empty-tube.tsx +23 -23
  176. package/templates/default/src/assets/icons/tube-plus.tsx +29 -29
  177. package/templates/default/src/components/base-node.tsx +21 -21
  178. package/templates/default/src/components/chart/pie-chart.tsx +73 -73
  179. package/templates/default/src/components/data-extraction/locator-inspector.tsx +460 -460
  180. package/templates/default/src/components/data-state/empty-state.tsx +40 -40
  181. package/templates/default/src/components/data-visualization/info-card.tsx +70 -70
  182. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -22
  183. package/templates/default/src/components/devtools/providers.tsx +19 -13
  184. package/templates/default/src/components/diagram/button-edge.tsx +54 -54
  185. package/templates/default/src/components/diagram/dynamic-parameters.tsx +438 -438
  186. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
  187. package/templates/default/src/components/diagram/flow-diagram.tsx +470 -470
  188. package/templates/default/src/components/diagram/node-form.tsx +262 -262
  189. package/templates/default/src/components/diagram/options-header-node.tsx +57 -57
  190. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -155
  191. package/templates/default/src/components/form/error-message.tsx +7 -7
  192. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -453
  193. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -30
  194. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -8
  195. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -8
  196. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -8
  197. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -14
  198. package/templates/default/src/components/logo.tsx +15 -15
  199. package/templates/default/src/components/navigation/command-badge.tsx +34 -34
  200. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -51
  201. package/templates/default/src/components/navigation/entity-search-command.tsx +116 -116
  202. package/templates/default/src/components/navigation/nav-card.tsx +31 -31
  203. package/templates/default/src/components/navigation/nav-command.tsx +508 -508
  204. package/templates/default/src/components/navigation/nav-link.tsx +60 -60
  205. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -112
  206. package/templates/default/src/components/node-header.tsx +159 -159
  207. package/templates/default/src/components/reports/test-case-logs-modal.tsx +253 -253
  208. package/templates/default/src/components/table/table-actions.tsx +172 -172
  209. package/templates/default/src/components/test-run/download-logs-button.tsx +99 -99
  210. package/templates/default/src/components/test-run/log-viewer.tsx +445 -445
  211. package/templates/default/src/components/test-run/test-run-details.tsx +611 -611
  212. package/templates/default/src/components/test-run/test-run-header.tsx +149 -149
  213. package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
  214. package/templates/default/src/components/theme/mode-toggle.tsx +54 -54
  215. package/templates/default/src/components/theme/theme-provider.tsx +8 -8
  216. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -7
  217. package/templates/default/src/components/typography/page-header.tsx +7 -7
  218. package/templates/default/src/components/ui/alert-dialog.tsx +106 -106
  219. package/templates/default/src/components/ui/alert.tsx +43 -43
  220. package/templates/default/src/components/ui/avatar.tsx +40 -40
  221. package/templates/default/src/components/ui/badge.tsx +29 -29
  222. package/templates/default/src/components/ui/button.tsx +47 -47
  223. package/templates/default/src/components/ui/calendar.tsx +158 -158
  224. package/templates/default/src/components/ui/card.tsx +43 -43
  225. package/templates/default/src/components/ui/checkbox.tsx +28 -28
  226. package/templates/default/src/components/ui/command.tsx +135 -135
  227. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -61
  228. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -87
  229. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -50
  230. package/templates/default/src/components/ui/data-table.tsx +267 -267
  231. package/templates/default/src/components/ui/dialog.tsx +97 -97
  232. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -182
  233. package/templates/default/src/components/ui/input.tsx +22 -22
  234. package/templates/default/src/components/ui/kbd.tsx +28 -28
  235. package/templates/default/src/components/ui/label.tsx +19 -19
  236. package/templates/default/src/components/ui/loading.tsx +12 -12
  237. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -116
  238. package/templates/default/src/components/ui/multi-select.tsx +142 -142
  239. package/templates/default/src/components/ui/navigation-menu.tsx +120 -120
  240. package/templates/default/src/components/ui/popover.tsx +33 -33
  241. package/templates/default/src/components/ui/progress.tsx +25 -25
  242. package/templates/default/src/components/ui/radio-group.tsx +44 -44
  243. package/templates/default/src/components/ui/scroll-area.tsx +40 -40
  244. package/templates/default/src/components/ui/select.tsx +151 -144
  245. package/templates/default/src/components/ui/separator.tsx +22 -22
  246. package/templates/default/src/components/ui/skeleton.tsx +7 -7
  247. package/templates/default/src/components/ui/table.tsx +76 -76
  248. package/templates/default/src/components/ui/tabs.tsx +55 -55
  249. package/templates/default/src/components/ui/textarea.tsx +21 -21
  250. package/templates/default/src/components/ui/toast.tsx +113 -113
  251. package/templates/default/src/components/ui/toaster.tsx +26 -26
  252. package/templates/default/src/components/user-prompt/delete-prompt.tsx +87 -87
  253. package/templates/default/src/config/db-config.ts +10 -10
  254. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -30
  255. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -24
  256. package/templates/default/src/constants/form-opts/locator-form-opts.ts +20 -20
  257. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -28
  258. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -21
  259. package/templates/default/src/constants/form-opts/review-form-opts.ts +23 -23
  260. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -42
  261. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -16
  262. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -24
  263. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -39
  264. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -36
  265. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -43
  266. package/templates/default/src/constants/form-opts/test-run-form-opts.ts +31 -31
  267. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -24
  268. package/templates/default/src/hooks/use-toast.ts +187 -187
  269. package/templates/default/src/lib/bidirectional-sync.ts +432 -432
  270. package/templates/default/src/lib/database-sync.ts +531 -531
  271. package/templates/default/src/lib/environment-file-utils.ts +221 -221
  272. package/templates/default/src/lib/feature-file-generator.ts +411 -411
  273. package/templates/default/src/lib/gherkin-parser.ts +259 -259
  274. package/templates/default/src/lib/locator-group-file-utils.ts +370 -370
  275. package/templates/default/src/lib/metrics/metric-calculator.ts +613 -613
  276. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -205
  277. package/templates/default/src/lib/path-helpers/module-path.ts +71 -71
  278. package/templates/default/src/lib/test-case-utils.ts +6 -6
  279. package/templates/default/src/lib/test-run/log-formatter.ts +83 -83
  280. package/templates/default/src/lib/test-run/process-manager.ts +191 -191
  281. package/templates/default/src/lib/test-run/report-parser.ts +316 -316
  282. package/templates/default/src/lib/test-run/test-run-executor.ts +144 -144
  283. package/templates/default/src/lib/test-run/winston-logger.ts +95 -95
  284. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -42
  285. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -95
  286. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -160
  287. package/templates/default/src/lib/utils/node-param-validation.ts +81 -81
  288. package/templates/default/src/lib/utils/template-step-file-generator.ts +167 -167
  289. package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +723 -723
  290. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -166
  291. package/templates/default/src/lib/utils.ts +31 -31
  292. package/templates/default/src/tests/config/executor/world.ts +41 -41
  293. package/templates/default/src/tests/executor.ts +80 -80
  294. package/templates/default/src/tests/hooks/hooks.ts +99 -99
  295. package/templates/default/src/tests/mapping/locator-map.json +1 -1
  296. package/templates/default/src/tests/steps/actions/click.step.ts +62 -62
  297. package/templates/default/src/tests/steps/actions/navigation.step.ts +73 -72
  298. package/templates/default/src/tests/steps/validations/active_state_assertion.step.ts +34 -34
  299. package/templates/default/src/tests/steps/validations/navigation_assertion.step.ts +24 -23
  300. package/templates/default/src/tests/steps/validations/text_assertion.step.ts +111 -111
  301. package/templates/default/src/tests/steps/validations/visibility_assertion.step.ts +30 -30
  302. package/templates/default/src/tests/support/parameter-types.ts +12 -12
  303. package/templates/default/src/tests/utils/cache.util.ts +260 -260
  304. package/templates/default/src/tests/utils/cli.util.ts +177 -177
  305. package/templates/default/src/tests/utils/environment.util.ts +65 -65
  306. package/templates/default/src/tests/utils/locator.util.ts +248 -248
  307. package/templates/default/src/tests/utils/random-data.util.ts +44 -44
  308. package/templates/default/src/tests/utils/spawner.util.ts +617 -617
  309. package/templates/default/src/types/diagram/diagram.ts +34 -34
  310. package/templates/default/src/types/diagram/template-step.ts +11 -11
  311. package/templates/default/src/types/executor/browser.type.ts +1 -1
  312. package/templates/default/src/types/form/actionHandler.ts +6 -6
  313. package/templates/default/src/types/locator/locator.type.ts +11 -11
  314. package/templates/default/src/types/step/step.type.ts +1 -1
  315. package/templates/default/src/types/table/data-table.ts +6 -6
  316. package/templates/default/tailwind.config.ts +62 -62
  317. package/templates/default/.env +0 -2
  318. package/templates/default/next-env.d.ts +0 -6
  319. package/templates/default/prisma/prisma/dev.db +0 -0
  320. package/templates/default/src/tests/config/environments/environments.json +0 -14
@@ -1,411 +1,411 @@
1
- #!/usr/bin/env tsx
2
-
3
- /**
4
- * Script to synchronize test suites from feature files to database
5
- * Scans feature files to ensure all test suites exist in DB
6
- * Filesystem is the source of truth - test suites in DB but not in FS will be deleted
7
- * Run this after merging changes to ensure test suite sync
8
- *
9
- * Usage: npx tsx scripts/sync-test-suites.ts
10
- */
11
-
12
- import { join } from 'path'
13
- import prisma from '../src/config/db-config'
14
- import { scanFeatureFiles, extractModulePathFromFilePath, ParsedFeature } from '../src/lib/gherkin-parser'
15
- import { buildModuleHierarchy, findModuleByPath, getAllModulesWithPaths } from '../src/lib/module-hierarchy-builder'
16
-
17
- interface TestSuiteFromFS {
18
- name: string // From filename (without .feature extension)
19
- description: string | null // From Feature: line
20
- modulePath: string // From folder hierarchy
21
- tags: string[] // Feature-level tags (lines before Feature:)
22
- filePath: string // Full path to feature file
23
- }
24
-
25
- interface SyncResult {
26
- testSuitesScanned: number
27
- testSuitesExisting: number
28
- testSuitesCreated: number
29
- testSuitesUpdated: number
30
- testSuitesDeleted: number
31
- errors: string[]
32
- createdTestSuites: Array<{ name: string; modulePath: string }>
33
- updatedTestSuites: Array<{ name: string; modulePath: string }>
34
- deletedTestSuites: Array<{ name: string; modulePath: string }>
35
- }
36
-
37
- /**
38
- * Extracts test suite name from filename
39
- * Example: "login-validation.feature" -> "login-validation"
40
- */
41
- function extractTestSuiteNameFromFilename(filePath: string): string {
42
- const fileName = filePath.split(/[/\\]/).pop() || ''
43
- return fileName.replace(/\.feature$/, '')
44
- }
45
-
46
- /**
47
- * Splits a tag line that may contain multiple tags separated by spaces
48
- * Example: "@smoke @demo" -> ["@smoke", "@demo"]
49
- */
50
- function splitTagLine(tagLine: string): string[] {
51
- return tagLine
52
- .split(/\s+/)
53
- .filter(tag => tag.trim().startsWith('@'))
54
- .map(tag => tag.trim())
55
- }
56
-
57
- /**
58
- * Extracts feature-level tags from parsed feature
59
- * Handles tags on the same line separated by spaces
60
- */
61
- function extractFeatureLevelTags(parsedFeature: ParsedFeature): string[] {
62
- const tags: string[] = []
63
-
64
- for (const tagLine of parsedFeature.tags) {
65
- if (tagLine.startsWith('@')) {
66
- const splitTags = splitTagLine(tagLine)
67
- tags.push(...splitTags)
68
- }
69
- }
70
-
71
- return tags
72
- }
73
-
74
- /**
75
- * Scans feature files and extracts test suite information
76
- */
77
- async function scanTestSuitesFromFilesystem(featuresDir: string): Promise<TestSuiteFromFS[]> {
78
- const testSuites: TestSuiteFromFS[] = []
79
-
80
- console.log('šŸ“ Scanning feature files...')
81
- const parsedFeatures = await scanFeatureFiles(featuresDir)
82
- console.log(` Found ${parsedFeatures.length} feature file(s)`)
83
-
84
- for (const parsedFeature of parsedFeatures) {
85
- try {
86
- const testSuiteName = extractTestSuiteNameFromFilename(parsedFeature.filePath)
87
- const modulePath = extractModulePathFromFilePath(parsedFeature.filePath, featuresDir)
88
- const tags = extractFeatureLevelTags(parsedFeature)
89
-
90
- testSuites.push({
91
- name: testSuiteName,
92
- description: parsedFeature.featureDescription || null,
93
- modulePath,
94
- tags,
95
- filePath: parsedFeature.filePath,
96
- })
97
- } catch (error) {
98
- console.error(` āŒ Error processing feature file '${parsedFeature.filePath}': ${error}`)
99
- }
100
- }
101
-
102
- return testSuites
103
- }
104
-
105
- /**
106
- * Finds tags by tagExpression and returns their IDs
107
- * Returns empty array for tags that don't exist (logs warning)
108
- */
109
- async function findTagIdsByExpressions(tagExpressions: string[]): Promise<string[]> {
110
- const tagIds: string[] = []
111
-
112
- for (const tagExpression of tagExpressions) {
113
- try {
114
- const tag = await prisma.tag.findFirst({
115
- where: { tagExpression },
116
- })
117
-
118
- if (tag) {
119
- tagIds.push(tag.id)
120
- } else {
121
- console.log(` āš ļø Tag '${tagExpression}' not found in database (skipping tag link)`)
122
- }
123
- } catch (error) {
124
- console.error(` āŒ Error finding tag '${tagExpression}': ${error}`)
125
- }
126
- }
127
-
128
- return tagIds
129
- }
130
-
131
- /**
132
- * Syncs test suites from filesystem to database
133
- */
134
- async function syncTestSuitesToDatabase(
135
- testSuitesFromFS: TestSuiteFromFS[],
136
- result: SyncResult,
137
- ): Promise<void> {
138
- console.log('\nāœ… Syncing test suites to database...')
139
-
140
- // Track test suites from filesystem (by name + modulePath)
141
- const fsTestSuiteKeys = new Set<string>()
142
-
143
- for (const testSuite of testSuitesFromFS) {
144
- try {
145
- const key = `${testSuite.name}::${testSuite.modulePath}`
146
- fsTestSuiteKeys.add(key)
147
-
148
- // Ensure module exists
149
- let moduleId = await findModuleByPath(testSuite.modulePath)
150
-
151
- if (!moduleId) {
152
- console.log(` šŸ“¦ Creating module hierarchy for path: ${testSuite.modulePath}`)
153
- moduleId = await buildModuleHierarchy(testSuite.modulePath)
154
- }
155
-
156
- // Find existing test suite by name and moduleId
157
- const existingTestSuite = await prisma.testSuite.findFirst({
158
- where: {
159
- name: testSuite.name,
160
- moduleId: moduleId,
161
- },
162
- include: {
163
- tags: true,
164
- },
165
- })
166
-
167
- // Find tag IDs
168
- const tagIds = await findTagIdsByExpressions(testSuite.tags)
169
-
170
- if (existingTestSuite) {
171
- // Check if update is needed
172
- const currentTagIds = existingTestSuite.tags.map(t => t.id).sort()
173
- const newTagIds = tagIds.sort()
174
- const tagsChanged = JSON.stringify(currentTagIds) !== JSON.stringify(newTagIds)
175
-
176
- const needsUpdate =
177
- existingTestSuite.description !== (testSuite.description || null) ||
178
- existingTestSuite.moduleId !== moduleId ||
179
- tagsChanged
180
-
181
- if (needsUpdate) {
182
- // Get existing test cases to explicitly preserve them
183
- const existingTestCases = await prisma.testCase.findMany({
184
- where: {
185
- TestSuite: {
186
- some: {
187
- id: existingTestSuite.id,
188
- },
189
- },
190
- },
191
- select: { id: true },
192
- })
193
-
194
- const testCaseCount = existingTestCases.length
195
- if (testCaseCount > 0) {
196
- console.log(
197
- ` ā„¹ļø Preserving ${testCaseCount} existing test case(s) for '${testSuite.name}' (${testSuite.modulePath})`,
198
- )
199
- }
200
-
201
- // Update test suite - explicitly preserve test cases by reconnecting them
202
- // Only update description, moduleId, and tags
203
- await prisma.testSuite.update({
204
- where: { id: existingTestSuite.id },
205
- data: {
206
- description: testSuite.description || null,
207
- moduleId: moduleId,
208
- tags: {
209
- set: tagIds.map(id => ({ id })),
210
- },
211
- // Explicitly preserve test cases by reconnecting them
212
- // This ensures they are not removed during the update
213
- testCases: existingTestCases.length > 0
214
- ? {
215
- set: existingTestCases.map(tc => ({ id: tc.id })),
216
- }
217
- : undefined,
218
- },
219
- })
220
-
221
- result.testSuitesUpdated++
222
- result.updatedTestSuites.push({
223
- name: testSuite.name,
224
- modulePath: testSuite.modulePath,
225
- })
226
- console.log(` šŸ”„ Updated test suite '${testSuite.name}' (${testSuite.modulePath})`)
227
- } else {
228
- result.testSuitesExisting++
229
- console.log(` āœ“ Test suite '${testSuite.name}' (${testSuite.modulePath}) already up to date`)
230
- }
231
- } else {
232
- // Create new test suite
233
- await prisma.testSuite.create({
234
- data: {
235
- name: testSuite.name,
236
- description: testSuite.description || null,
237
- moduleId: moduleId,
238
- tags: tagIds.length > 0 ? { connect: tagIds.map(id => ({ id })) } : undefined,
239
- },
240
- })
241
-
242
- result.testSuitesCreated++
243
- result.createdTestSuites.push({
244
- name: testSuite.name,
245
- modulePath: testSuite.modulePath,
246
- })
247
- console.log(` āž• Created test suite '${testSuite.name}' (${testSuite.modulePath})`)
248
- }
249
- } catch (error) {
250
- const errorMsg = `Error processing test suite '${testSuite.name}' from ${testSuite.filePath}: ${error}`
251
- result.errors.push(errorMsg)
252
- console.error(` āŒ ${errorMsg}`)
253
- }
254
- }
255
-
256
- // Delete orphaned test suites (test suites in DB but not in FS)
257
- console.log('\nšŸ” Checking for orphaned test suites (not in filesystem)...')
258
- const allDbTestSuites = await prisma.testSuite.findMany({
259
- include: {
260
- module: true,
261
- },
262
- })
263
-
264
- // Get all modules with their paths
265
- const modulesWithPaths = await getAllModulesWithPaths()
266
- const modulePathMap = new Map<string, string>()
267
- for (const mod of modulesWithPaths) {
268
- modulePathMap.set(mod.id, mod.path)
269
- }
270
-
271
- for (const dbTestSuite of allDbTestSuites) {
272
- try {
273
- const modulePath = modulePathMap.get(dbTestSuite.moduleId) || '/'
274
- const key = `${dbTestSuite.name}::${modulePath}`
275
-
276
- if (!fsTestSuiteKeys.has(key)) {
277
- // Check if test suite has test cases (for logging)
278
- const testSuiteWithCases = await prisma.testSuite.findUnique({
279
- where: { id: dbTestSuite.id },
280
- include: {
281
- testCases: { select: { id: true } },
282
- },
283
- })
284
-
285
- if (testSuiteWithCases && testSuiteWithCases.testCases.length > 0) {
286
- console.log(
287
- ` āš ļø Test suite '${dbTestSuite.name}' (${modulePath}) has ${testSuiteWithCases.testCases.length} test case(s) - will be cascade deleted`,
288
- )
289
- }
290
-
291
- // Delete the test suite (Prisma cascade will handle test cases)
292
- await prisma.testSuite.delete({
293
- where: { id: dbTestSuite.id },
294
- })
295
-
296
- result.testSuitesDeleted++
297
- result.deletedTestSuites.push({
298
- name: dbTestSuite.name,
299
- modulePath: modulePath,
300
- })
301
- console.log(` šŸ—‘ļø Deleted test suite '${dbTestSuite.name}' (${modulePath}) (not in filesystem)`)
302
- }
303
- } catch (error) {
304
- const errorMsg = `Error deleting test suite '${dbTestSuite.name}': ${error}`
305
- result.errors.push(errorMsg)
306
- console.error(` āŒ ${errorMsg}`)
307
- }
308
- }
309
- }
310
-
311
- /**
312
- * Generates and displays sync summary
313
- */
314
- function generateSummary(result: SyncResult): void {
315
- console.log('\nšŸ“Š Sync Summary:')
316
- console.log(` šŸ“ Test suites scanned: ${result.testSuitesScanned}`)
317
- console.log(` āœ… Test suites existing: ${result.testSuitesExisting}`)
318
- console.log(` āž• Test suites created: ${result.testSuitesCreated}`)
319
- console.log(` šŸ”„ Test suites updated: ${result.testSuitesUpdated}`)
320
- console.log(` šŸ—‘ļø Test suites deleted: ${result.testSuitesDeleted}`)
321
- console.log(` āŒ Errors: ${result.errors.length}`)
322
-
323
- if (result.createdTestSuites.length > 0) {
324
- console.log('\n Created test suites:')
325
- result.createdTestSuites.forEach((ts, index) => {
326
- console.log(` ${index + 1}. ${ts.name} (${ts.modulePath})`)
327
- })
328
- }
329
-
330
- if (result.updatedTestSuites.length > 0) {
331
- console.log('\n Updated test suites:')
332
- result.updatedTestSuites.forEach((ts, index) => {
333
- console.log(` ${index + 1}. ${ts.name} (${ts.modulePath})`)
334
- })
335
- }
336
-
337
- if (result.deletedTestSuites.length > 0) {
338
- console.log('\n Deleted test suites:')
339
- result.deletedTestSuites.forEach((ts, index) => {
340
- console.log(` ${index + 1}. ${ts.name} (${ts.modulePath})`)
341
- })
342
- }
343
-
344
- if (result.errors.length > 0) {
345
- console.log('\n Errors:')
346
- result.errors.forEach((error, index) => {
347
- console.log(` ${index + 1}. ${error}`)
348
- })
349
- }
350
- }
351
-
352
- /**
353
- * Main function
354
- */
355
- async function main() {
356
- try {
357
- console.log('šŸ”„ Starting test suites sync...')
358
- console.log('This will scan feature files and sync test suites to database.')
359
- console.log('Filesystem is the source of truth - test suites in DB but not in FS will be deleted.')
360
- console.log('Note: Test cases are not synced by this script (they will be handled separately).\n')
361
-
362
- const baseDir = process.cwd()
363
- const featuresDir = join(baseDir, 'src', 'tests', 'features')
364
-
365
- // Scan test suites from filesystem
366
- const testSuitesFromFS = await scanTestSuitesFromFilesystem(featuresDir)
367
-
368
- if (testSuitesFromFS.length === 0) {
369
- console.log('\nāš ļø No feature files found. Nothing to sync.')
370
- return
371
- }
372
-
373
- console.log(`\nšŸ“‹ Found ${testSuitesFromFS.length} test suite(s) from feature files:`)
374
- for (const ts of testSuitesFromFS) {
375
- console.log(` - ${ts.name} (${ts.modulePath})`)
376
- }
377
-
378
- // Initialize result
379
- const result: SyncResult = {
380
- testSuitesScanned: testSuitesFromFS.length,
381
- testSuitesExisting: 0,
382
- testSuitesCreated: 0,
383
- testSuitesUpdated: 0,
384
- testSuitesDeleted: 0,
385
- errors: [],
386
- createdTestSuites: [],
387
- updatedTestSuites: [],
388
- deletedTestSuites: [],
389
- }
390
-
391
- // Sync to database
392
- await syncTestSuitesToDatabase(testSuitesFromFS, result)
393
-
394
- // Generate summary
395
- generateSummary(result)
396
-
397
- if (result.errors.length === 0) {
398
- console.log('\nāœ… Sync completed successfully!')
399
- } else {
400
- console.log('\nāš ļø Sync completed with errors. Please review the errors above.')
401
- process.exit(1)
402
- }
403
- } catch (error) {
404
- console.error('\nāŒ Error during sync:', error)
405
- process.exit(1)
406
- } finally {
407
- await prisma.$disconnect()
408
- }
409
- }
410
-
411
- main()
1
+ #!/usr/bin/env tsx
2
+
3
+ /**
4
+ * Script to synchronize test suites from feature files to database
5
+ * Scans feature files to ensure all test suites exist in DB
6
+ * Filesystem is the source of truth - test suites in DB but not in FS will be deleted
7
+ * Run this after merging changes to ensure test suite sync
8
+ *
9
+ * Usage: npx tsx scripts/sync-test-suites.ts
10
+ */
11
+
12
+ import { join } from 'path'
13
+ import prisma from '../src/config/db-config'
14
+ import { scanFeatureFiles, extractModulePathFromFilePath, ParsedFeature } from '../src/lib/gherkin-parser'
15
+ import { buildModuleHierarchy, findModuleByPath, getAllModulesWithPaths } from '../src/lib/module-hierarchy-builder'
16
+
17
+ interface TestSuiteFromFS {
18
+ name: string // From filename (without .feature extension)
19
+ description: string | null // From Feature: line
20
+ modulePath: string // From folder hierarchy
21
+ tags: string[] // Feature-level tags (lines before Feature:)
22
+ filePath: string // Full path to feature file
23
+ }
24
+
25
+ interface SyncResult {
26
+ testSuitesScanned: number
27
+ testSuitesExisting: number
28
+ testSuitesCreated: number
29
+ testSuitesUpdated: number
30
+ testSuitesDeleted: number
31
+ errors: string[]
32
+ createdTestSuites: Array<{ name: string; modulePath: string }>
33
+ updatedTestSuites: Array<{ name: string; modulePath: string }>
34
+ deletedTestSuites: Array<{ name: string; modulePath: string }>
35
+ }
36
+
37
+ /**
38
+ * Extracts test suite name from filename
39
+ * Example: "login-validation.feature" -> "login-validation"
40
+ */
41
+ function extractTestSuiteNameFromFilename(filePath: string): string {
42
+ const fileName = filePath.split(/[/\\]/).pop() || ''
43
+ return fileName.replace(/\.feature$/, '')
44
+ }
45
+
46
+ /**
47
+ * Splits a tag line that may contain multiple tags separated by spaces
48
+ * Example: "@smoke @demo" -> ["@smoke", "@demo"]
49
+ */
50
+ function splitTagLine(tagLine: string): string[] {
51
+ return tagLine
52
+ .split(/\s+/)
53
+ .filter(tag => tag.trim().startsWith('@'))
54
+ .map(tag => tag.trim())
55
+ }
56
+
57
+ /**
58
+ * Extracts feature-level tags from parsed feature
59
+ * Handles tags on the same line separated by spaces
60
+ */
61
+ function extractFeatureLevelTags(parsedFeature: ParsedFeature): string[] {
62
+ const tags: string[] = []
63
+
64
+ for (const tagLine of parsedFeature.tags) {
65
+ if (tagLine.startsWith('@')) {
66
+ const splitTags = splitTagLine(tagLine)
67
+ tags.push(...splitTags)
68
+ }
69
+ }
70
+
71
+ return tags
72
+ }
73
+
74
+ /**
75
+ * Scans feature files and extracts test suite information
76
+ */
77
+ async function scanTestSuitesFromFilesystem(featuresDir: string): Promise<TestSuiteFromFS[]> {
78
+ const testSuites: TestSuiteFromFS[] = []
79
+
80
+ console.log('šŸ“ Scanning feature files...')
81
+ const parsedFeatures = await scanFeatureFiles(featuresDir)
82
+ console.log(` Found ${parsedFeatures.length} feature file(s)`)
83
+
84
+ for (const parsedFeature of parsedFeatures) {
85
+ try {
86
+ const testSuiteName = extractTestSuiteNameFromFilename(parsedFeature.filePath)
87
+ const modulePath = extractModulePathFromFilePath(parsedFeature.filePath, featuresDir)
88
+ const tags = extractFeatureLevelTags(parsedFeature)
89
+
90
+ testSuites.push({
91
+ name: testSuiteName,
92
+ description: parsedFeature.featureDescription || null,
93
+ modulePath,
94
+ tags,
95
+ filePath: parsedFeature.filePath,
96
+ })
97
+ } catch (error) {
98
+ console.error(` āŒ Error processing feature file '${parsedFeature.filePath}': ${error}`)
99
+ }
100
+ }
101
+
102
+ return testSuites
103
+ }
104
+
105
+ /**
106
+ * Finds tags by tagExpression and returns their IDs
107
+ * Returns empty array for tags that don't exist (logs warning)
108
+ */
109
+ async function findTagIdsByExpressions(tagExpressions: string[]): Promise<string[]> {
110
+ const tagIds: string[] = []
111
+
112
+ for (const tagExpression of tagExpressions) {
113
+ try {
114
+ const tag = await prisma.tag.findFirst({
115
+ where: { tagExpression },
116
+ })
117
+
118
+ if (tag) {
119
+ tagIds.push(tag.id)
120
+ } else {
121
+ console.log(` āš ļø Tag '${tagExpression}' not found in database (skipping tag link)`)
122
+ }
123
+ } catch (error) {
124
+ console.error(` āŒ Error finding tag '${tagExpression}': ${error}`)
125
+ }
126
+ }
127
+
128
+ return tagIds
129
+ }
130
+
131
+ /**
132
+ * Syncs test suites from filesystem to database
133
+ */
134
+ async function syncTestSuitesToDatabase(
135
+ testSuitesFromFS: TestSuiteFromFS[],
136
+ result: SyncResult,
137
+ ): Promise<void> {
138
+ console.log('\nāœ… Syncing test suites to database...')
139
+
140
+ // Track test suites from filesystem (by name + modulePath)
141
+ const fsTestSuiteKeys = new Set<string>()
142
+
143
+ for (const testSuite of testSuitesFromFS) {
144
+ try {
145
+ const key = `${testSuite.name}::${testSuite.modulePath}`
146
+ fsTestSuiteKeys.add(key)
147
+
148
+ // Ensure module exists
149
+ let moduleId = await findModuleByPath(testSuite.modulePath)
150
+
151
+ if (!moduleId) {
152
+ console.log(` šŸ“¦ Creating module hierarchy for path: ${testSuite.modulePath}`)
153
+ moduleId = await buildModuleHierarchy(testSuite.modulePath)
154
+ }
155
+
156
+ // Find existing test suite by name and moduleId
157
+ const existingTestSuite = await prisma.testSuite.findFirst({
158
+ where: {
159
+ name: testSuite.name,
160
+ moduleId: moduleId,
161
+ },
162
+ include: {
163
+ tags: true,
164
+ },
165
+ })
166
+
167
+ // Find tag IDs
168
+ const tagIds = await findTagIdsByExpressions(testSuite.tags)
169
+
170
+ if (existingTestSuite) {
171
+ // Check if update is needed
172
+ const currentTagIds = existingTestSuite.tags.map(t => t.id).sort()
173
+ const newTagIds = tagIds.sort()
174
+ const tagsChanged = JSON.stringify(currentTagIds) !== JSON.stringify(newTagIds)
175
+
176
+ const needsUpdate =
177
+ existingTestSuite.description !== (testSuite.description || null) ||
178
+ existingTestSuite.moduleId !== moduleId ||
179
+ tagsChanged
180
+
181
+ if (needsUpdate) {
182
+ // Get existing test cases to explicitly preserve them
183
+ const existingTestCases = await prisma.testCase.findMany({
184
+ where: {
185
+ TestSuite: {
186
+ some: {
187
+ id: existingTestSuite.id,
188
+ },
189
+ },
190
+ },
191
+ select: { id: true },
192
+ })
193
+
194
+ const testCaseCount = existingTestCases.length
195
+ if (testCaseCount > 0) {
196
+ console.log(
197
+ ` ā„¹ļø Preserving ${testCaseCount} existing test case(s) for '${testSuite.name}' (${testSuite.modulePath})`,
198
+ )
199
+ }
200
+
201
+ // Update test suite - explicitly preserve test cases by reconnecting them
202
+ // Only update description, moduleId, and tags
203
+ await prisma.testSuite.update({
204
+ where: { id: existingTestSuite.id },
205
+ data: {
206
+ description: testSuite.description || null,
207
+ moduleId: moduleId,
208
+ tags: {
209
+ set: tagIds.map(id => ({ id })),
210
+ },
211
+ // Explicitly preserve test cases by reconnecting them
212
+ // This ensures they are not removed during the update
213
+ testCases: existingTestCases.length > 0
214
+ ? {
215
+ set: existingTestCases.map(tc => ({ id: tc.id })),
216
+ }
217
+ : undefined,
218
+ },
219
+ })
220
+
221
+ result.testSuitesUpdated++
222
+ result.updatedTestSuites.push({
223
+ name: testSuite.name,
224
+ modulePath: testSuite.modulePath,
225
+ })
226
+ console.log(` šŸ”„ Updated test suite '${testSuite.name}' (${testSuite.modulePath})`)
227
+ } else {
228
+ result.testSuitesExisting++
229
+ console.log(` āœ“ Test suite '${testSuite.name}' (${testSuite.modulePath}) already up to date`)
230
+ }
231
+ } else {
232
+ // Create new test suite
233
+ await prisma.testSuite.create({
234
+ data: {
235
+ name: testSuite.name,
236
+ description: testSuite.description || null,
237
+ moduleId: moduleId,
238
+ tags: tagIds.length > 0 ? { connect: tagIds.map(id => ({ id })) } : undefined,
239
+ },
240
+ })
241
+
242
+ result.testSuitesCreated++
243
+ result.createdTestSuites.push({
244
+ name: testSuite.name,
245
+ modulePath: testSuite.modulePath,
246
+ })
247
+ console.log(` āž• Created test suite '${testSuite.name}' (${testSuite.modulePath})`)
248
+ }
249
+ } catch (error) {
250
+ const errorMsg = `Error processing test suite '${testSuite.name}' from ${testSuite.filePath}: ${error}`
251
+ result.errors.push(errorMsg)
252
+ console.error(` āŒ ${errorMsg}`)
253
+ }
254
+ }
255
+
256
+ // Delete orphaned test suites (test suites in DB but not in FS)
257
+ console.log('\nšŸ” Checking for orphaned test suites (not in filesystem)...')
258
+ const allDbTestSuites = await prisma.testSuite.findMany({
259
+ include: {
260
+ module: true,
261
+ },
262
+ })
263
+
264
+ // Get all modules with their paths
265
+ const modulesWithPaths = await getAllModulesWithPaths()
266
+ const modulePathMap = new Map<string, string>()
267
+ for (const mod of modulesWithPaths) {
268
+ modulePathMap.set(mod.id, mod.path)
269
+ }
270
+
271
+ for (const dbTestSuite of allDbTestSuites) {
272
+ try {
273
+ const modulePath = modulePathMap.get(dbTestSuite.moduleId) || '/'
274
+ const key = `${dbTestSuite.name}::${modulePath}`
275
+
276
+ if (!fsTestSuiteKeys.has(key)) {
277
+ // Check if test suite has test cases (for logging)
278
+ const testSuiteWithCases = await prisma.testSuite.findUnique({
279
+ where: { id: dbTestSuite.id },
280
+ include: {
281
+ testCases: { select: { id: true } },
282
+ },
283
+ })
284
+
285
+ if (testSuiteWithCases && testSuiteWithCases.testCases.length > 0) {
286
+ console.log(
287
+ ` āš ļø Test suite '${dbTestSuite.name}' (${modulePath}) has ${testSuiteWithCases.testCases.length} test case(s) - will be cascade deleted`,
288
+ )
289
+ }
290
+
291
+ // Delete the test suite (Prisma cascade will handle test cases)
292
+ await prisma.testSuite.delete({
293
+ where: { id: dbTestSuite.id },
294
+ })
295
+
296
+ result.testSuitesDeleted++
297
+ result.deletedTestSuites.push({
298
+ name: dbTestSuite.name,
299
+ modulePath: modulePath,
300
+ })
301
+ console.log(` šŸ—‘ļø Deleted test suite '${dbTestSuite.name}' (${modulePath}) (not in filesystem)`)
302
+ }
303
+ } catch (error) {
304
+ const errorMsg = `Error deleting test suite '${dbTestSuite.name}': ${error}`
305
+ result.errors.push(errorMsg)
306
+ console.error(` āŒ ${errorMsg}`)
307
+ }
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Generates and displays sync summary
313
+ */
314
+ function generateSummary(result: SyncResult): void {
315
+ console.log('\nšŸ“Š Sync Summary:')
316
+ console.log(` šŸ“ Test suites scanned: ${result.testSuitesScanned}`)
317
+ console.log(` āœ… Test suites existing: ${result.testSuitesExisting}`)
318
+ console.log(` āž• Test suites created: ${result.testSuitesCreated}`)
319
+ console.log(` šŸ”„ Test suites updated: ${result.testSuitesUpdated}`)
320
+ console.log(` šŸ—‘ļø Test suites deleted: ${result.testSuitesDeleted}`)
321
+ console.log(` āŒ Errors: ${result.errors.length}`)
322
+
323
+ if (result.createdTestSuites.length > 0) {
324
+ console.log('\n Created test suites:')
325
+ result.createdTestSuites.forEach((ts, index) => {
326
+ console.log(` ${index + 1}. ${ts.name} (${ts.modulePath})`)
327
+ })
328
+ }
329
+
330
+ if (result.updatedTestSuites.length > 0) {
331
+ console.log('\n Updated test suites:')
332
+ result.updatedTestSuites.forEach((ts, index) => {
333
+ console.log(` ${index + 1}. ${ts.name} (${ts.modulePath})`)
334
+ })
335
+ }
336
+
337
+ if (result.deletedTestSuites.length > 0) {
338
+ console.log('\n Deleted test suites:')
339
+ result.deletedTestSuites.forEach((ts, index) => {
340
+ console.log(` ${index + 1}. ${ts.name} (${ts.modulePath})`)
341
+ })
342
+ }
343
+
344
+ if (result.errors.length > 0) {
345
+ console.log('\n Errors:')
346
+ result.errors.forEach((error, index) => {
347
+ console.log(` ${index + 1}. ${error}`)
348
+ })
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Main function
354
+ */
355
+ async function main() {
356
+ try {
357
+ console.log('šŸ”„ Starting test suites sync...')
358
+ console.log('This will scan feature files and sync test suites to database.')
359
+ console.log('Filesystem is the source of truth - test suites in DB but not in FS will be deleted.')
360
+ console.log('Note: Test cases are not synced by this script (they will be handled separately).\n')
361
+
362
+ const baseDir = process.cwd()
363
+ const featuresDir = join(baseDir, 'src', 'tests', 'features')
364
+
365
+ // Scan test suites from filesystem
366
+ const testSuitesFromFS = await scanTestSuitesFromFilesystem(featuresDir)
367
+
368
+ if (testSuitesFromFS.length === 0) {
369
+ console.log('\nāš ļø No feature files found. Nothing to sync.')
370
+ return
371
+ }
372
+
373
+ console.log(`\nšŸ“‹ Found ${testSuitesFromFS.length} test suite(s) from feature files:`)
374
+ for (const ts of testSuitesFromFS) {
375
+ console.log(` - ${ts.name} (${ts.modulePath})`)
376
+ }
377
+
378
+ // Initialize result
379
+ const result: SyncResult = {
380
+ testSuitesScanned: testSuitesFromFS.length,
381
+ testSuitesExisting: 0,
382
+ testSuitesCreated: 0,
383
+ testSuitesUpdated: 0,
384
+ testSuitesDeleted: 0,
385
+ errors: [],
386
+ createdTestSuites: [],
387
+ updatedTestSuites: [],
388
+ deletedTestSuites: [],
389
+ }
390
+
391
+ // Sync to database
392
+ await syncTestSuitesToDatabase(testSuitesFromFS, result)
393
+
394
+ // Generate summary
395
+ generateSummary(result)
396
+
397
+ if (result.errors.length === 0) {
398
+ console.log('\nāœ… Sync completed successfully!')
399
+ } else {
400
+ console.log('\nāš ļø Sync completed with errors. Please review the errors above.')
401
+ process.exit(1)
402
+ }
403
+ } catch (error) {
404
+ console.error('\nāŒ Error during sync:', error)
405
+ process.exit(1)
406
+ } finally {
407
+ await prisma.$disconnect()
408
+ }
409
+ }
410
+
411
+ main()