create-appraisejs 0.1.10-alpha.8 → 0.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. package/README.md +52 -52
  2. package/dist/cli.e2e.test.d.ts +2 -0
  3. package/dist/cli.e2e.test.d.ts.map +1 -0
  4. package/dist/cli.e2e.test.js +73 -0
  5. package/dist/cli.e2e.test.js.map +1 -0
  6. package/dist/config.test.d.ts +2 -0
  7. package/dist/config.test.d.ts.map +1 -0
  8. package/dist/config.test.js +65 -0
  9. package/dist/config.test.js.map +1 -0
  10. package/dist/copy-template.test.d.ts +2 -0
  11. package/dist/copy-template.test.d.ts.map +1 -0
  12. package/dist/copy-template.test.js +71 -0
  13. package/dist/copy-template.test.js.map +1 -0
  14. package/dist/download-repo.test.d.ts +2 -0
  15. package/dist/download-repo.test.d.ts.map +1 -0
  16. package/dist/download-repo.test.js +14 -0
  17. package/dist/download-repo.test.js.map +1 -0
  18. package/dist/install.test.d.ts +2 -0
  19. package/dist/install.test.d.ts.map +1 -0
  20. package/dist/install.test.js +119 -0
  21. package/dist/install.test.js.map +1 -0
  22. package/dist/prepare-template-utils.d.ts.map +1 -1
  23. package/dist/prepare-template-utils.js +5 -0
  24. package/dist/prepare-template-utils.js.map +1 -1
  25. package/dist/prompts.test.d.ts +2 -0
  26. package/dist/prompts.test.d.ts.map +1 -0
  27. package/dist/prompts.test.js +58 -0
  28. package/dist/prompts.test.js.map +1 -0
  29. package/dist/scaffold-gitignore.d.ts +1 -0
  30. package/dist/scaffold-gitignore.d.ts.map +1 -1
  31. package/dist/scaffold-gitignore.js +3 -0
  32. package/dist/scaffold-gitignore.js.map +1 -1
  33. package/package.json +3 -2
  34. package/templates/default/.appraise-template-meta.json +2 -2
  35. package/templates/default/.env.example +2 -2
  36. package/templates/default/.vscode/settings.json +2 -1
  37. package/templates/default/README.md +53 -53
  38. package/templates/default/automation/mapping/locator-map.json +1 -14
  39. package/templates/default/automation/steps/actions/click.step.ts +58 -58
  40. package/templates/default/automation/steps/actions/hover.step.ts +27 -27
  41. package/templates/default/automation/steps/actions/input.step.ts +84 -84
  42. package/templates/default/automation/steps/actions/navigation.step.ts +70 -70
  43. package/templates/default/automation/steps/actions/random_data.step.ts +142 -142
  44. package/templates/default/automation/steps/actions/store.step.ts +86 -86
  45. package/templates/default/automation/steps/actions/wait.step.ts +90 -90
  46. package/templates/default/automation/steps/validations/active_state_assertion.step.ts +30 -30
  47. package/templates/default/automation/steps/validations/navigation_assertion.step.ts +22 -22
  48. package/templates/default/automation/steps/validations/text_assertion.step.ts +107 -107
  49. package/templates/default/automation/steps/validations/visibility_assertion.step.ts +26 -26
  50. package/templates/default/components.json +24 -24
  51. package/templates/default/cucumber.mjs +16 -16
  52. package/templates/default/eslint.config.mjs +16 -16
  53. package/templates/default/gitignore +3 -0
  54. package/templates/default/next-env.d.ts +6 -6
  55. package/templates/default/next.config.ts +11 -11
  56. package/templates/default/package-lock.json +146 -75
  57. package/templates/default/package.json +2 -1
  58. package/templates/default/packages/cucumber-runtime/package.json +13 -13
  59. package/templates/default/packages/cucumber-runtime/src/cache.util.ts +93 -93
  60. package/templates/default/packages/cucumber-runtime/src/cli.ts +68 -68
  61. package/templates/default/packages/cucumber-runtime/src/environment.util.ts +21 -21
  62. package/templates/default/packages/cucumber-runtime/src/executor.ts +32 -32
  63. package/templates/default/packages/cucumber-runtime/src/hooks.ts +25 -3
  64. package/templates/default/packages/cucumber-runtime/src/index.ts +17 -17
  65. package/templates/default/packages/cucumber-runtime/src/locator.util.ts +234 -234
  66. package/templates/default/packages/cucumber-runtime/src/parameter-types.ts +7 -7
  67. package/templates/default/packages/cucumber-runtime/src/paths.ts +66 -1
  68. package/templates/default/packages/cucumber-runtime/src/random-data.util.ts +35 -35
  69. package/templates/default/packages/cucumber-runtime/src/types.ts +13 -13
  70. package/templates/default/packages/cucumber-runtime/src/world.ts +44 -44
  71. package/templates/default/packages/cucumber-runtime/tsconfig.json +11 -11
  72. package/templates/default/packages/locator-picker-companion/dist/cli.d.ts +1 -0
  73. package/templates/default/packages/locator-picker-companion/dist/cli.js +302 -0
  74. package/templates/default/packages/locator-picker-companion/dist/index.d.ts +3 -0
  75. package/templates/default/packages/locator-picker-companion/dist/index.js +3 -0
  76. package/templates/default/packages/locator-picker-companion/dist/injected-picker-script.d.ts +1 -0
  77. package/templates/default/packages/locator-picker-companion/dist/injected-picker-script.js +615 -0
  78. package/templates/default/packages/locator-picker-companion/dist/launcher.d.ts +11 -0
  79. package/templates/default/packages/locator-picker-companion/dist/launcher.js +59 -0
  80. package/templates/default/packages/locator-picker-companion/dist/selector-generator.d.ts +3 -0
  81. package/templates/default/packages/locator-picker-companion/dist/selector-generator.js +257 -0
  82. package/templates/default/packages/locator-picker-companion/dist/session-file.d.ts +22 -0
  83. package/templates/default/packages/locator-picker-companion/dist/session-file.js +150 -0
  84. package/templates/default/packages/locator-picker-companion/dist/types.d.ts +31 -0
  85. package/templates/default/packages/locator-picker-companion/dist/types.js +1 -0
  86. package/templates/default/packages/locator-picker-companion/package.json +17 -0
  87. package/templates/default/packages/locator-picker-companion/src/cli.ts +428 -0
  88. package/templates/default/packages/locator-picker-companion/src/index.ts +3 -0
  89. package/templates/default/packages/locator-picker-companion/src/injected-picker-script.ts +771 -0
  90. package/templates/default/packages/locator-picker-companion/src/launcher.ts +77 -0
  91. package/templates/default/packages/locator-picker-companion/src/selector-generator.ts +339 -0
  92. package/templates/default/packages/locator-picker-companion/src/session-file.ts +226 -0
  93. package/templates/default/packages/locator-picker-companion/src/types.ts +42 -0
  94. package/templates/default/packages/locator-picker-companion/tsconfig.json +14 -0
  95. package/templates/default/postcss.config.mjs +8 -8
  96. package/templates/default/prisma/dev.db +0 -0
  97. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  98. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  99. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  100. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  101. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  102. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  103. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  104. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  105. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  106. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  107. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  108. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  109. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  110. package/templates/default/prisma/migrations/20260313093000_add_report_step_screenshot_path/migration.sql +1 -0
  111. package/templates/default/prisma/migrations/20260318120000_add_test_suite_context_to_test_run_test_case/migration.sql +20 -0
  112. package/templates/default/prisma/migrations/20260318173512_add_support_of_test_suite_level_runs/migration.sql +2 -0
  113. package/templates/default/prisma/schema.prisma +4 -0
  114. package/templates/default/scripts/install-playwright.ts +20 -0
  115. package/templates/default/scripts/protect-seeded-files.ts +38 -38
  116. package/templates/default/scripts/regenerate-features.ts +94 -94
  117. package/templates/default/scripts/setup-env.ts +19 -19
  118. package/templates/default/scripts/sync-all.ts +22 -66
  119. package/templates/default/scripts/sync-appraise-base-template.ts +57 -19
  120. package/templates/default/scripts/sync-environments.ts +40 -21
  121. package/templates/default/scripts/sync-locator-groups.ts +410 -410
  122. package/templates/default/scripts/sync-locators.ts +398 -398
  123. package/templates/default/scripts/sync-modules.ts +341 -341
  124. package/templates/default/scripts/sync-tags.ts +2 -3
  125. package/templates/default/scripts/sync-template-step-groups.ts +0 -3
  126. package/templates/default/scripts/sync-template-steps.ts +37 -11
  127. package/templates/default/scripts/sync-test-cases.ts +68 -16
  128. package/templates/default/scripts/sync-test-suites.ts +7 -5
  129. package/templates/default/src/actions/conflict/conflict.action.ts +33 -33
  130. package/templates/default/src/actions/dashboard/dashboard-actions.ts +240 -240
  131. package/templates/default/src/actions/environments/environment-actions.ts +188 -188
  132. package/templates/default/src/actions/locator/locator-actions.ts +490 -490
  133. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +6 -3
  134. package/templates/default/src/actions/locator-picker/locator-picker-actions.ts +243 -0
  135. package/templates/default/src/actions/modules/module-actions.ts +135 -135
  136. package/templates/default/src/actions/reports/report-actions.ts +51 -49
  137. package/templates/default/src/actions/review/review-actions.ts +147 -147
  138. package/templates/default/src/actions/settings/sync-actions.ts +46 -0
  139. package/templates/default/src/actions/tags/tag-actions.ts +107 -107
  140. package/templates/default/src/actions/template-step/template-step-actions.ts +38 -4
  141. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +10 -3
  142. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +238 -238
  143. package/templates/default/src/actions/test-case/test-case-actions.ts +356 -356
  144. package/templates/default/src/actions/test-run/test-run-actions.ts +222 -129
  145. package/templates/default/src/actions/test-suite/test-suite-actions.ts +95 -17
  146. package/templates/default/src/actions/user/user-actions.ts +13 -13
  147. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
  148. package/templates/default/src/app/(base)/environments/environment-form.tsx +219 -219
  149. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
  150. package/templates/default/src/app/(base)/environments/environment-table.tsx +24 -24
  151. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +46 -46
  152. package/templates/default/src/app/(base)/environments/page.tsx +59 -59
  153. package/templates/default/src/app/(base)/layout.tsx +10 -10
  154. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -44
  155. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -215
  156. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -77
  157. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -28
  158. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -46
  159. package/templates/default/src/app/(base)/locator-groups/page.tsx +20 -8
  160. package/templates/default/src/app/(base)/locators/create/create-locator-workspace.tsx +542 -0
  161. package/templates/default/src/app/(base)/locators/create/page.tsx +32 -19
  162. package/templates/default/src/app/(base)/locators/locator-form.tsx +163 -163
  163. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +73 -73
  164. package/templates/default/src/app/(base)/locators/locator-table.tsx +28 -28
  165. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +56 -27
  166. package/templates/default/src/app/(base)/locators/page.tsx +13 -13
  167. package/templates/default/src/app/(base)/locators/picker/page.tsx +7 -0
  168. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +66 -66
  169. package/templates/default/src/app/(base)/modules/create/page.tsx +34 -34
  170. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +46 -46
  171. package/templates/default/src/app/(base)/modules/module-form.tsx +126 -126
  172. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +85 -85
  173. package/templates/default/src/app/(base)/modules/module-table.tsx +24 -24
  174. package/templates/default/src/app/(base)/modules/page.tsx +59 -59
  175. package/templates/default/src/app/(base)/reports/[id]/page.tsx +40 -33
  176. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
  177. package/templates/default/src/app/(base)/reports/feature-chart.tsx +78 -78
  178. package/templates/default/src/app/(base)/reports/overview-chart.tsx +49 -46
  179. package/templates/default/src/app/(base)/reports/page.tsx +98 -98
  180. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +78 -16
  181. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -189
  182. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -72
  183. package/templates/default/src/app/(base)/reports/report-view-table-columns.tsx +12 -1
  184. package/templates/default/src/app/(base)/reports/report-view-table.tsx +1 -0
  185. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +40 -42
  186. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -115
  187. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -27
  188. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -42
  189. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -79
  190. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -27
  191. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +58 -60
  192. package/templates/default/src/app/(base)/reviews/create/page.tsx +26 -26
  193. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +15 -15
  194. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +26 -26
  195. package/templates/default/src/app/(base)/reviews/page.tsx +26 -26
  196. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +26 -26
  197. package/templates/default/src/app/(base)/reviews/review-form.tsx +11 -11
  198. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +9 -9
  199. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +9 -9
  200. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +15 -15
  201. package/templates/default/src/app/(base)/settings/page.tsx +30 -0
  202. package/templates/default/src/app/(base)/settings/settings-sync-panel.tsx +261 -0
  203. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -39
  204. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -50
  205. package/templates/default/src/app/(base)/tags/page.tsx +58 -58
  206. package/templates/default/src/app/(base)/tags/tag-form.tsx +147 -147
  207. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -63
  208. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -29
  209. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -28
  210. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +45 -45
  211. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -60
  212. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +167 -167
  213. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -89
  214. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +32 -32
  215. package/templates/default/src/app/(base)/template-steps/create/page.tsx +37 -37
  216. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +49 -49
  217. package/templates/default/src/app/(base)/template-steps/page.tsx +58 -58
  218. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +213 -213
  219. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +384 -384
  220. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +158 -158
  221. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +24 -24
  222. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -56
  223. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -89
  224. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -58
  225. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +84 -84
  226. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +262 -262
  227. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -76
  228. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -32
  229. package/templates/default/src/app/(base)/test-cases/create/page.tsx +76 -76
  230. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +96 -96
  231. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +38 -38
  232. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +73 -73
  233. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -106
  234. package/templates/default/src/app/(base)/test-cases/page.tsx +2 -2
  235. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +82 -82
  236. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +395 -395
  237. package/templates/default/src/app/(base)/test-cases/test-case-table-columns.tsx +6 -71
  238. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +2 -2
  239. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +4 -1
  240. package/templates/default/src/app/(base)/test-runs/create/page.tsx +11 -9
  241. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
  242. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +37 -44
  243. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -229
  244. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -127
  245. package/templates/default/src/app/(base)/test-suites/create/page.tsx +3 -2
  246. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +2 -1
  247. package/templates/default/src/app/(base)/test-suites/page.tsx +57 -21
  248. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +16 -17
  249. package/templates/default/src/app/(base)/test-suites/test-suite-table-columns.tsx +2 -1
  250. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +29 -29
  251. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -187
  252. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +12 -12
  253. package/templates/default/src/app/(dashboard-components)/data-card.tsx +26 -26
  254. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +56 -56
  255. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -87
  256. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +44 -44
  257. package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.ts +52 -0
  258. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +97 -34
  259. package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +4 -1
  260. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -147
  261. package/templates/default/src/app/globals.css +147 -147
  262. package/templates/default/src/app/layout.tsx +4 -0
  263. package/templates/default/src/app/page.tsx +2 -0
  264. package/templates/default/src/assets/icons/empty-tube.tsx +23 -23
  265. package/templates/default/src/assets/icons/tube-plus.tsx +29 -29
  266. package/templates/default/src/components/base-node.tsx +21 -21
  267. package/templates/default/src/components/chart/pie-chart.tsx +73 -73
  268. package/templates/default/src/components/data-extraction/locator-inspector.tsx +460 -460
  269. package/templates/default/src/components/data-state/empty-state.tsx +40 -40
  270. package/templates/default/src/components/data-visualization/info-card.tsx +70 -70
  271. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -22
  272. package/templates/default/src/components/diagram/button-edge.tsx +54 -54
  273. package/templates/default/src/components/diagram/dynamic-parameters.tsx +474 -474
  274. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
  275. package/templates/default/src/components/diagram/flow-diagram.tsx +470 -470
  276. package/templates/default/src/components/diagram/node-form.tsx +262 -262
  277. package/templates/default/src/components/diagram/options-header-node.tsx +57 -57
  278. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -155
  279. package/templates/default/src/components/form/error-message.tsx +7 -7
  280. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -453
  281. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -30
  282. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -8
  283. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -8
  284. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -8
  285. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -14
  286. package/templates/default/src/components/logo.tsx +15 -15
  287. package/templates/default/src/components/navigation/command-badge.tsx +34 -34
  288. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -51
  289. package/templates/default/src/components/navigation/entity-search-command.tsx +116 -116
  290. package/templates/default/src/components/navigation/nav-card.tsx +31 -31
  291. package/templates/default/src/components/navigation/nav-command.tsx +13 -0
  292. package/templates/default/src/components/navigation/nav-link.tsx +60 -60
  293. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -112
  294. package/templates/default/src/components/node-header.tsx +159 -159
  295. package/templates/default/src/components/reports/test-case-logs-modal.tsx +310 -253
  296. package/templates/default/src/components/table/table-actions.tsx +172 -172
  297. package/templates/default/src/components/test-case/test-case-columns.tsx +76 -0
  298. package/templates/default/src/components/test-case/test-case-picker.tsx +244 -0
  299. package/templates/default/src/components/test-run/download-logs-button.tsx +92 -99
  300. package/templates/default/src/components/test-run/log-viewer.tsx +1 -14
  301. package/templates/default/src/components/test-run/test-run-details.tsx +15 -2
  302. package/templates/default/src/components/test-run/test-run-header.tsx +149 -149
  303. package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
  304. package/templates/default/src/components/test-suite/test-suite-picker.tsx +444 -0
  305. package/templates/default/src/components/theme/mode-toggle.tsx +54 -54
  306. package/templates/default/src/components/theme/theme-provider.tsx +8 -8
  307. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -7
  308. package/templates/default/src/components/typography/page-header.tsx +7 -7
  309. package/templates/default/src/components/ui/alert-dialog.tsx +106 -106
  310. package/templates/default/src/components/ui/alert.tsx +43 -43
  311. package/templates/default/src/components/ui/avatar.tsx +40 -40
  312. package/templates/default/src/components/ui/badge.tsx +29 -29
  313. package/templates/default/src/components/ui/button.tsx +47 -47
  314. package/templates/default/src/components/ui/calendar.tsx +158 -158
  315. package/templates/default/src/components/ui/card.tsx +43 -43
  316. package/templates/default/src/components/ui/checkbox.tsx +28 -28
  317. package/templates/default/src/components/ui/command.tsx +135 -135
  318. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -61
  319. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -87
  320. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -50
  321. package/templates/default/src/components/ui/data-table.tsx +261 -261
  322. package/templates/default/src/components/ui/dialog.tsx +97 -97
  323. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -182
  324. package/templates/default/src/components/ui/input.tsx +22 -22
  325. package/templates/default/src/components/ui/kbd.tsx +28 -28
  326. package/templates/default/src/components/ui/label.tsx +19 -19
  327. package/templates/default/src/components/ui/loading.tsx +12 -12
  328. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -116
  329. package/templates/default/src/components/ui/multi-select.tsx +142 -142
  330. package/templates/default/src/components/ui/navigation-menu.tsx +120 -120
  331. package/templates/default/src/components/ui/popover.tsx +33 -33
  332. package/templates/default/src/components/ui/progress.tsx +25 -25
  333. package/templates/default/src/components/ui/radio-group.tsx +44 -44
  334. package/templates/default/src/components/ui/scroll-area.tsx +40 -40
  335. package/templates/default/src/components/ui/select.tsx +151 -151
  336. package/templates/default/src/components/ui/separator.tsx +22 -22
  337. package/templates/default/src/components/ui/skeleton.tsx +7 -7
  338. package/templates/default/src/components/ui/table.tsx +76 -76
  339. package/templates/default/src/components/ui/tabs.tsx +55 -55
  340. package/templates/default/src/components/ui/textarea.tsx +21 -21
  341. package/templates/default/src/components/ui/toast.tsx +113 -113
  342. package/templates/default/src/components/ui/toaster.tsx +26 -26
  343. package/templates/default/src/components/user-prompt/delete-prompt.tsx +87 -87
  344. package/templates/default/src/config/db-config.ts +10 -10
  345. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -30
  346. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -24
  347. package/templates/default/src/constants/form-opts/locator-form-opts.ts +20 -20
  348. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -28
  349. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -21
  350. package/templates/default/src/constants/form-opts/review-form-opts.ts +23 -23
  351. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -42
  352. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -16
  353. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -24
  354. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -39
  355. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -36
  356. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -43
  357. package/templates/default/src/constants/form-opts/test-run-form-opts.ts +5 -3
  358. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -24
  359. package/templates/default/src/hooks/use-toast.ts +187 -187
  360. package/templates/default/src/lib/automation/paths.ts +42 -12
  361. package/templates/default/src/lib/automation/projection-service.ts +2 -1
  362. package/templates/default/src/lib/bidirectional-sync.ts +432 -432
  363. package/templates/default/src/lib/database-sync.ts +2 -7
  364. package/templates/default/src/lib/environment-file-utils.ts +184 -184
  365. package/templates/default/src/lib/executor/local-executor-adapter.ts +24 -25
  366. package/templates/default/src/lib/executor/types.ts +2 -2
  367. package/templates/default/src/lib/feature-file-generator.ts +5 -66
  368. package/templates/default/src/lib/gherkin-parser.ts +259 -259
  369. package/templates/default/src/lib/locator-group-file-utils.ts +307 -307
  370. package/templates/default/src/lib/locator-picker/session-manager.ts +452 -0
  371. package/templates/default/src/lib/locator-picker/suggestions.ts +124 -0
  372. package/templates/default/src/lib/metrics/metric-calculator.ts +5 -0
  373. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -205
  374. package/templates/default/src/lib/path-helpers/module-path.ts +71 -71
  375. package/templates/default/src/lib/process/task-spawner.ts +39 -2
  376. package/templates/default/src/lib/sync/projected-feature-utils.ts +113 -0
  377. package/templates/default/src/lib/sync/sync-executor.ts +130 -0
  378. package/templates/default/src/lib/sync/sync-pending-counts.test.ts +239 -0
  379. package/templates/default/src/lib/sync/sync-pending-counts.ts +1358 -0
  380. package/templates/default/src/lib/sync/sync-registry.ts +149 -0
  381. package/templates/default/src/lib/tag-utils.ts +28 -0
  382. package/templates/default/src/lib/template-sync-utils.d.ts +6 -6
  383. package/templates/default/src/lib/template-sync-utils.js +46 -46
  384. package/templates/default/src/lib/template-sync-utils.ts +63 -63
  385. package/templates/default/src/lib/test-case-utils.ts +6 -6
  386. package/templates/default/src/lib/test-run/log-formatter.ts +83 -83
  387. package/templates/default/src/lib/test-run/matching.ts +117 -0
  388. package/templates/default/src/lib/test-run/process-manager.ts +2 -0
  389. package/templates/default/src/lib/test-run/report-parser.ts +352 -316
  390. package/templates/default/src/lib/test-run/test-run-executor.ts +13 -15
  391. package/templates/default/src/lib/test-run/winston-logger.ts +64 -66
  392. package/templates/default/src/lib/test-suite-identifier-service.ts +76 -0
  393. package/templates/default/src/lib/test-suite-utils.ts +6 -0
  394. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -42
  395. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -95
  396. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -160
  397. package/templates/default/src/lib/utils/node-param-validation.ts +81 -81
  398. package/templates/default/src/lib/utils/template-step-file-generator.ts +104 -104
  399. package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +0 -8
  400. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -166
  401. package/templates/default/src/lib/utils.ts +31 -31
  402. package/templates/default/src/types/diagram/diagram.ts +34 -34
  403. package/templates/default/src/types/diagram/template-step.ts +11 -11
  404. package/templates/default/src/types/executor/browser.type.ts +1 -1
  405. package/templates/default/src/types/form/actionHandler.ts +6 -6
  406. package/templates/default/src/types/locator/locator.type.ts +11 -11
  407. package/templates/default/src/types/locator-picker.ts +65 -0
  408. package/templates/default/src/types/step/step.type.ts +1 -1
  409. package/templates/default/src/types/table/data-table.ts +6 -6
  410. package/templates/default/src/types/test-case-picker.ts +6 -0
  411. package/templates/default/src/types/test-suite-picker.ts +18 -0
  412. package/templates/default/tailwind.config.ts +62 -62
  413. package/templates/default/tsconfig.json +4 -1
  414. package/templates/default/appraisejs.config.json +0 -1
@@ -1,410 +1,410 @@
1
- #!/usr/bin/env tsx
2
-
3
- /**
4
- * Script to synchronize locator groups from filesystem to database
5
- * Scans locator-map.json and locators directory to ensure all locator groups exist in DB
6
- * Filesystem is the source of truth - locator groups in DB but not in FS will be deleted
7
- * Run this after merging changes to ensure locator group sync
8
- *
9
- * Usage: npx tsx scripts/sync-locator-groups.ts
10
- */
11
-
12
- import { promises as fs } from 'fs'
13
- import { join } from 'path'
14
- import { glob } from 'glob'
15
- import prisma from '../src/config/db-config'
16
- import { findModuleByPath, buildModuleHierarchy } from '../src/lib/module-hierarchy-builder'
17
- import { extractModulePathFromAutomationFile, getAutomationLocatorMapPath } from '../src/lib/template-sync-utils'
18
-
19
- /**
20
- * Represents a locator group from the filesystem
21
- */
22
- interface LocatorGroupFromFS {
23
- name: string
24
- route: string
25
- modulePath: string
26
- filePath: string
27
- }
28
-
29
- /**
30
- * Represents a locator group entry from locator-map.json
31
- */
32
- interface LocatorMapEntry {
33
- name: string
34
- path: string
35
- }
36
-
37
- /**
38
- * Sync result summary
39
- */
40
- interface SyncResult {
41
- locatorGroupsScanned: number
42
- locatorGroupsExisting: number
43
- locatorGroupsCreated: number
44
- locatorGroupsUpdated: number
45
- locatorGroupsDeleted: number
46
- errors: string[]
47
- createdLocatorGroups: string[]
48
- updatedLocatorGroups: string[]
49
- deletedLocatorGroups: Array<{ name: string; locatorCount: number }>
50
- }
51
-
52
- /**
53
- * Reads and parses the locator-map.json file
54
- */
55
- async function readLocatorMap(baseDir: string): Promise<LocatorMapEntry[]> {
56
- const locatorMapPath = getAutomationLocatorMapPath(baseDir)
57
-
58
- try {
59
- await fs.access(locatorMapPath)
60
- } catch {
61
- console.warn(` āš ļø Locator map file not found at ${locatorMapPath}, continuing without it`)
62
- return []
63
- }
64
-
65
- try {
66
- const fileContent = await fs.readFile(locatorMapPath, 'utf-8')
67
- const locatorMap = JSON.parse(fileContent) as LocatorMapEntry[]
68
-
69
- if (!Array.isArray(locatorMap)) {
70
- throw new Error('Invalid locator map format: expected an array')
71
- }
72
-
73
- return locatorMap
74
- } catch (error) {
75
- if (error instanceof SyntaxError) {
76
- throw new Error(`Invalid JSON in locator map file: ${error.message}`)
77
- }
78
- throw error
79
- }
80
- }
81
-
82
- /**
83
- * Scans the locators directory to find all locator group files
84
- */
85
- async function scanLocatorGroupFiles(baseDir: string): Promise<string[]> {
86
- const pattern = 'automation/locators/**/*.json'
87
-
88
- try {
89
- const files = await glob(pattern, {
90
- cwd: baseDir,
91
- })
92
- return files.map(file => join(baseDir, file))
93
- } catch (error) {
94
- throw new Error(`Error scanning locator group files: ${error}`)
95
- }
96
- }
97
-
98
- /**
99
- * Extracts module path from locator file path
100
- * Example: automation/locators/home/home.json -> /home
101
- * Example: automation/locators/users/admins/directors.json -> /users/admins
102
- */
103
- function extractModulePathFromLocatorFile(filePath: string, baseDir: string): string {
104
- return extractModulePathFromAutomationFile(filePath, baseDir, 'locators')
105
- }
106
-
107
- /**
108
- * Extracts locator group name from file path
109
- * The group name is the filename without extension
110
- */
111
- function extractLocatorGroupName(filePath: string): string {
112
- const fileName = filePath.split(/[/\\]/).pop() || ''
113
- return fileName.replace('.json', '')
114
- }
115
-
116
- /**
117
- * Builds a map of locator group names to routes from locator-map.json
118
- */
119
- function buildLocatorMapRouteMap(locatorMap: LocatorMapEntry[]): Map<string, string> {
120
- const routeMap = new Map<string, string>()
121
-
122
- for (const entry of locatorMap) {
123
- routeMap.set(entry.name, entry.path)
124
- }
125
-
126
- return routeMap
127
- }
128
-
129
- /**
130
- * Builds locator groups from filesystem
131
- * Combines information from locator-map.json and directory structure
132
- */
133
- async function buildLocatorGroupsFromFS(
134
- baseDir: string,
135
- locatorMap: LocatorMapEntry[],
136
- ): Promise<LocatorGroupFromFS[]> {
137
- const locatorGroups: LocatorGroupFromFS[] = []
138
- const routeMap = buildLocatorMapRouteMap(locatorMap)
139
-
140
- // Scan all locator group files
141
- const files = await scanLocatorGroupFiles(baseDir)
142
-
143
- for (const filePath of files) {
144
- const modulePath = extractModulePathFromLocatorFile(filePath, baseDir)
145
- const groupName = extractLocatorGroupName(filePath)
146
-
147
- // Get route from locator-map.json, or default to /{groupName}
148
- const route = routeMap.get(groupName) || `/${groupName}`
149
-
150
- locatorGroups.push({
151
- name: groupName,
152
- route,
153
- modulePath,
154
- filePath,
155
- })
156
- }
157
-
158
- return locatorGroups
159
- }
160
-
161
- /**
162
- * Creates or updates a locator group in the database
163
- */
164
- async function createOrUpdateLocatorGroup(
165
- locatorGroup: LocatorGroupFromFS,
166
- moduleId: string,
167
- result: SyncResult,
168
- ): Promise<void> {
169
- try {
170
- // Check if locator group already exists
171
- const existingGroup = await prisma.locatorGroup.findUnique({
172
- where: { name: locatorGroup.name },
173
- })
174
-
175
- if (existingGroup) {
176
- // Check if update is needed
177
- const needsUpdate = existingGroup.route !== locatorGroup.route || existingGroup.moduleId !== moduleId
178
-
179
- if (needsUpdate) {
180
- await prisma.locatorGroup.update({
181
- where: { id: existingGroup.id },
182
- data: {
183
- route: locatorGroup.route,
184
- moduleId: moduleId,
185
- },
186
- })
187
-
188
- result.locatorGroupsUpdated++
189
- result.updatedLocatorGroups.push(locatorGroup.name)
190
- console.log(` šŸ”„ Updated locator group '${locatorGroup.name}' (route: ${locatorGroup.route})`)
191
- } else {
192
- result.locatorGroupsExisting++
193
- console.log(` āœ“ Locator group '${locatorGroup.name}' already exists and is up to date`)
194
- }
195
- } else {
196
- // Create new locator group
197
- await prisma.locatorGroup.create({
198
- data: {
199
- name: locatorGroup.name,
200
- route: locatorGroup.route,
201
- moduleId: moduleId,
202
- },
203
- })
204
-
205
- result.locatorGroupsCreated++
206
- result.createdLocatorGroups.push(locatorGroup.name)
207
- console.log(` āž• Created locator group '${locatorGroup.name}' (route: ${locatorGroup.route})`)
208
- }
209
- } catch (error) {
210
- const errorMsg = `Error syncing locator group '${locatorGroup.name}': ${error}`
211
- result.errors.push(errorMsg)
212
- console.error(` āŒ ${errorMsg}`)
213
- }
214
- }
215
-
216
- /**
217
- * Syncs locator groups from filesystem to database
218
- */
219
- async function syncLocatorGroupsToDatabase(
220
- locatorGroups: LocatorGroupFromFS[],
221
- result: SyncResult,
222
- ): Promise<void> {
223
- console.log('\nāœ… Syncing locator groups to database...')
224
-
225
- for (const locatorGroup of locatorGroups) {
226
- try {
227
- // Ensure module exists (create if needed)
228
- let moduleId = await findModuleByPath(locatorGroup.modulePath)
229
-
230
- if (!moduleId) {
231
- console.log(` šŸ“¦ Creating module hierarchy for path: ${locatorGroup.modulePath}`)
232
- moduleId = await buildModuleHierarchy(locatorGroup.modulePath)
233
- }
234
-
235
- // Create or update locator group
236
- await createOrUpdateLocatorGroup(locatorGroup, moduleId, result)
237
- } catch (error) {
238
- const errorMsg = `Error processing locator group '${locatorGroup.name}': ${error}`
239
- result.errors.push(errorMsg)
240
- console.error(` āŒ ${errorMsg}`)
241
- }
242
- }
243
- }
244
-
245
- /**
246
- * Deletes orphaned locator groups (groups in DB but not in FS)
247
- */
248
- async function deleteOrphanedLocatorGroups(
249
- fsLocatorGroupNames: Set<string>,
250
- result: SyncResult,
251
- ): Promise<void> {
252
- console.log('\nšŸ” Checking for orphaned locator groups (not in filesystem)...')
253
-
254
- try {
255
- // Get all locator groups from database
256
- const dbLocatorGroups = await prisma.locatorGroup.findMany({
257
- include: {
258
- locators: {
259
- select: { id: true },
260
- },
261
- },
262
- })
263
-
264
- for (const dbGroup of dbLocatorGroups) {
265
- // Check if locator group exists in filesystem
266
- if (!fsLocatorGroupNames.has(dbGroup.name)) {
267
- try {
268
- const locatorCount = dbGroup.locators.length
269
-
270
- if (locatorCount > 0) {
271
- console.log(
272
- ` āš ļø Locator group '${dbGroup.name}' has ${locatorCount} locator(s) - will be cascade deleted`,
273
- )
274
- }
275
-
276
- // Delete the locator group (Prisma cascade will handle locators)
277
- await prisma.locatorGroup.delete({
278
- where: { id: dbGroup.id },
279
- })
280
-
281
- result.locatorGroupsDeleted++
282
- result.deletedLocatorGroups.push({
283
- name: dbGroup.name,
284
- locatorCount,
285
- })
286
- console.log(` šŸ—‘ļø Deleted locator group '${dbGroup.name}' (not in filesystem)`)
287
- } catch (error) {
288
- const errorMsg = `Error deleting locator group '${dbGroup.name}': ${error}`
289
- result.errors.push(errorMsg)
290
- console.error(` āŒ ${errorMsg}`)
291
- }
292
- }
293
- }
294
- } catch (error) {
295
- const errorMsg = `Error checking for orphaned locator groups: ${error}`
296
- result.errors.push(errorMsg)
297
- console.error(` āŒ ${errorMsg}`)
298
- }
299
- }
300
-
301
- /**
302
- * Generates and displays sync summary
303
- */
304
- function generateSummary(result: SyncResult): void {
305
- console.log('\nšŸ“Š Sync Summary:')
306
- console.log(` šŸ“ Locator groups scanned: ${result.locatorGroupsScanned}`)
307
- console.log(` āœ… Locator groups existing: ${result.locatorGroupsExisting}`)
308
- console.log(` āž• Locator groups created: ${result.locatorGroupsCreated}`)
309
- console.log(` šŸ”„ Locator groups updated: ${result.locatorGroupsUpdated}`)
310
- console.log(` šŸ—‘ļø Locator groups deleted: ${result.locatorGroupsDeleted}`)
311
- console.log(` āŒ Errors: ${result.errors.length}`)
312
-
313
- if (result.createdLocatorGroups.length > 0) {
314
- console.log('\n Created locator groups:')
315
- result.createdLocatorGroups.forEach((name, index) => {
316
- console.log(` ${index + 1}. ${name}`)
317
- })
318
- }
319
-
320
- if (result.updatedLocatorGroups.length > 0) {
321
- console.log('\n Updated locator groups:')
322
- result.updatedLocatorGroups.forEach((name, index) => {
323
- console.log(` ${index + 1}. ${name}`)
324
- })
325
- }
326
-
327
- if (result.deletedLocatorGroups.length > 0) {
328
- console.log('\n Deleted locator groups:')
329
- result.deletedLocatorGroups.forEach((group, index) => {
330
- console.log(
331
- ` ${index + 1}. ${group.name} (${group.locatorCount} locator(s) cascade deleted)`,
332
- )
333
- })
334
- }
335
-
336
- if (result.errors.length > 0) {
337
- console.log('\n Errors:')
338
- result.errors.forEach((error, index) => {
339
- console.log(` ${index + 1}. ${error}`)
340
- })
341
- }
342
- }
343
-
344
- /**
345
- * Main function
346
- */
347
- async function main() {
348
- try {
349
- console.log('šŸ”„ Starting locator groups sync...')
350
- console.log('This will scan filesystem directories and sync locator groups to database.')
351
- console.log('Filesystem is the source of truth - locator groups in DB but not in FS will be deleted.\n')
352
-
353
- const baseDir = process.cwd()
354
-
355
- // Initialize result
356
- const result: SyncResult = {
357
- locatorGroupsScanned: 0,
358
- locatorGroupsExisting: 0,
359
- locatorGroupsCreated: 0,
360
- locatorGroupsUpdated: 0,
361
- locatorGroupsDeleted: 0,
362
- errors: [],
363
- createdLocatorGroups: [],
364
- updatedLocatorGroups: [],
365
- deletedLocatorGroups: [],
366
- }
367
-
368
- // Read locator map
369
- console.log('šŸ“„ Reading locator-map.json...')
370
- const locatorMap = await readLocatorMap(baseDir)
371
- console.log(` Found ${locatorMap.length} entry(ies) in locator map`)
372
-
373
- // Build locator groups from filesystem
374
- console.log('\nšŸ“ Scanning automation/locators directory...')
375
- const locatorGroups = await buildLocatorGroupsFromFS(baseDir, locatorMap)
376
- result.locatorGroupsScanned = locatorGroups.length
377
- console.log(` Found ${locatorGroups.length} locator group(s) in filesystem`)
378
-
379
- if (locatorGroups.length > 0) {
380
- console.log('\n Locator groups found:')
381
- locatorGroups.forEach((group, index) => {
382
- console.log(` ${index + 1}. ${group.name} (route: ${group.route}, module: ${group.modulePath})`)
383
- })
384
- }
385
-
386
- // Sync to database
387
- await syncLocatorGroupsToDatabase(locatorGroups, result)
388
-
389
- // Delete orphaned locator groups
390
- const fsLocatorGroupNames = new Set(locatorGroups.map(g => g.name))
391
- await deleteOrphanedLocatorGroups(fsLocatorGroupNames, result)
392
-
393
- // Generate summary
394
- generateSummary(result)
395
-
396
- if (result.errors.length === 0) {
397
- console.log('\nāœ… Sync completed successfully!')
398
- } else {
399
- console.log('\nāš ļø Sync completed with errors. Please review the errors above.')
400
- process.exit(1)
401
- }
402
- } catch (error) {
403
- console.error('\nāŒ Error during sync:', error)
404
- process.exit(1)
405
- } finally {
406
- await prisma.$disconnect()
407
- }
408
- }
409
-
410
- main()
1
+ #!/usr/bin/env tsx
2
+
3
+ /**
4
+ * Script to synchronize locator groups from filesystem to database
5
+ * Scans locator-map.json and locators directory to ensure all locator groups exist in DB
6
+ * Filesystem is the source of truth - locator groups in DB but not in FS will be deleted
7
+ * Run this after merging changes to ensure locator group sync
8
+ *
9
+ * Usage: npx tsx scripts/sync-locator-groups.ts
10
+ */
11
+
12
+ import { promises as fs } from 'fs'
13
+ import { join } from 'path'
14
+ import { glob } from 'glob'
15
+ import prisma from '../src/config/db-config'
16
+ import { findModuleByPath, buildModuleHierarchy } from '../src/lib/module-hierarchy-builder'
17
+ import { extractModulePathFromAutomationFile, getAutomationLocatorMapPath } from '../src/lib/template-sync-utils'
18
+
19
+ /**
20
+ * Represents a locator group from the filesystem
21
+ */
22
+ interface LocatorGroupFromFS {
23
+ name: string
24
+ route: string
25
+ modulePath: string
26
+ filePath: string
27
+ }
28
+
29
+ /**
30
+ * Represents a locator group entry from locator-map.json
31
+ */
32
+ interface LocatorMapEntry {
33
+ name: string
34
+ path: string
35
+ }
36
+
37
+ /**
38
+ * Sync result summary
39
+ */
40
+ interface SyncResult {
41
+ locatorGroupsScanned: number
42
+ locatorGroupsExisting: number
43
+ locatorGroupsCreated: number
44
+ locatorGroupsUpdated: number
45
+ locatorGroupsDeleted: number
46
+ errors: string[]
47
+ createdLocatorGroups: string[]
48
+ updatedLocatorGroups: string[]
49
+ deletedLocatorGroups: Array<{ name: string; locatorCount: number }>
50
+ }
51
+
52
+ /**
53
+ * Reads and parses the locator-map.json file
54
+ */
55
+ async function readLocatorMap(baseDir: string): Promise<LocatorMapEntry[]> {
56
+ const locatorMapPath = getAutomationLocatorMapPath(baseDir)
57
+
58
+ try {
59
+ await fs.access(locatorMapPath)
60
+ } catch {
61
+ console.warn(` āš ļø Locator map file not found at ${locatorMapPath}, continuing without it`)
62
+ return []
63
+ }
64
+
65
+ try {
66
+ const fileContent = await fs.readFile(locatorMapPath, 'utf-8')
67
+ const locatorMap = JSON.parse(fileContent) as LocatorMapEntry[]
68
+
69
+ if (!Array.isArray(locatorMap)) {
70
+ throw new Error('Invalid locator map format: expected an array')
71
+ }
72
+
73
+ return locatorMap
74
+ } catch (error) {
75
+ if (error instanceof SyntaxError) {
76
+ throw new Error(`Invalid JSON in locator map file: ${error.message}`)
77
+ }
78
+ throw error
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Scans the locators directory to find all locator group files
84
+ */
85
+ async function scanLocatorGroupFiles(baseDir: string): Promise<string[]> {
86
+ const pattern = 'automation/locators/**/*.json'
87
+
88
+ try {
89
+ const files = await glob(pattern, {
90
+ cwd: baseDir,
91
+ })
92
+ return files.map(file => join(baseDir, file))
93
+ } catch (error) {
94
+ throw new Error(`Error scanning locator group files: ${error}`)
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Extracts module path from locator file path
100
+ * Example: automation/locators/home/home.json -> /home
101
+ * Example: automation/locators/users/admins/directors.json -> /users/admins
102
+ */
103
+ function extractModulePathFromLocatorFile(filePath: string, baseDir: string): string {
104
+ return extractModulePathFromAutomationFile(filePath, baseDir, 'locators')
105
+ }
106
+
107
+ /**
108
+ * Extracts locator group name from file path
109
+ * The group name is the filename without extension
110
+ */
111
+ function extractLocatorGroupName(filePath: string): string {
112
+ const fileName = filePath.split(/[/\\]/).pop() || ''
113
+ return fileName.replace('.json', '')
114
+ }
115
+
116
+ /**
117
+ * Builds a map of locator group names to routes from locator-map.json
118
+ */
119
+ function buildLocatorMapRouteMap(locatorMap: LocatorMapEntry[]): Map<string, string> {
120
+ const routeMap = new Map<string, string>()
121
+
122
+ for (const entry of locatorMap) {
123
+ routeMap.set(entry.name, entry.path)
124
+ }
125
+
126
+ return routeMap
127
+ }
128
+
129
+ /**
130
+ * Builds locator groups from filesystem
131
+ * Combines information from locator-map.json and directory structure
132
+ */
133
+ async function buildLocatorGroupsFromFS(
134
+ baseDir: string,
135
+ locatorMap: LocatorMapEntry[],
136
+ ): Promise<LocatorGroupFromFS[]> {
137
+ const locatorGroups: LocatorGroupFromFS[] = []
138
+ const routeMap = buildLocatorMapRouteMap(locatorMap)
139
+
140
+ // Scan all locator group files
141
+ const files = await scanLocatorGroupFiles(baseDir)
142
+
143
+ for (const filePath of files) {
144
+ const modulePath = extractModulePathFromLocatorFile(filePath, baseDir)
145
+ const groupName = extractLocatorGroupName(filePath)
146
+
147
+ // Get route from locator-map.json, or default to /{groupName}
148
+ const route = routeMap.get(groupName) || `/${groupName}`
149
+
150
+ locatorGroups.push({
151
+ name: groupName,
152
+ route,
153
+ modulePath,
154
+ filePath,
155
+ })
156
+ }
157
+
158
+ return locatorGroups
159
+ }
160
+
161
+ /**
162
+ * Creates or updates a locator group in the database
163
+ */
164
+ async function createOrUpdateLocatorGroup(
165
+ locatorGroup: LocatorGroupFromFS,
166
+ moduleId: string,
167
+ result: SyncResult,
168
+ ): Promise<void> {
169
+ try {
170
+ // Check if locator group already exists
171
+ const existingGroup = await prisma.locatorGroup.findUnique({
172
+ where: { name: locatorGroup.name },
173
+ })
174
+
175
+ if (existingGroup) {
176
+ // Check if update is needed
177
+ const needsUpdate = existingGroup.route !== locatorGroup.route || existingGroup.moduleId !== moduleId
178
+
179
+ if (needsUpdate) {
180
+ await prisma.locatorGroup.update({
181
+ where: { id: existingGroup.id },
182
+ data: {
183
+ route: locatorGroup.route,
184
+ moduleId: moduleId,
185
+ },
186
+ })
187
+
188
+ result.locatorGroupsUpdated++
189
+ result.updatedLocatorGroups.push(locatorGroup.name)
190
+ console.log(` šŸ”„ Updated locator group '${locatorGroup.name}' (route: ${locatorGroup.route})`)
191
+ } else {
192
+ result.locatorGroupsExisting++
193
+ console.log(` āœ“ Locator group '${locatorGroup.name}' already exists and is up to date`)
194
+ }
195
+ } else {
196
+ // Create new locator group
197
+ await prisma.locatorGroup.create({
198
+ data: {
199
+ name: locatorGroup.name,
200
+ route: locatorGroup.route,
201
+ moduleId: moduleId,
202
+ },
203
+ })
204
+
205
+ result.locatorGroupsCreated++
206
+ result.createdLocatorGroups.push(locatorGroup.name)
207
+ console.log(` āž• Created locator group '${locatorGroup.name}' (route: ${locatorGroup.route})`)
208
+ }
209
+ } catch (error) {
210
+ const errorMsg = `Error syncing locator group '${locatorGroup.name}': ${error}`
211
+ result.errors.push(errorMsg)
212
+ console.error(` āŒ ${errorMsg}`)
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Syncs locator groups from filesystem to database
218
+ */
219
+ async function syncLocatorGroupsToDatabase(
220
+ locatorGroups: LocatorGroupFromFS[],
221
+ result: SyncResult,
222
+ ): Promise<void> {
223
+ console.log('\nāœ… Syncing locator groups to database...')
224
+
225
+ for (const locatorGroup of locatorGroups) {
226
+ try {
227
+ // Ensure module exists (create if needed)
228
+ let moduleId = await findModuleByPath(locatorGroup.modulePath)
229
+
230
+ if (!moduleId) {
231
+ console.log(` šŸ“¦ Creating module hierarchy for path: ${locatorGroup.modulePath}`)
232
+ moduleId = await buildModuleHierarchy(locatorGroup.modulePath)
233
+ }
234
+
235
+ // Create or update locator group
236
+ await createOrUpdateLocatorGroup(locatorGroup, moduleId, result)
237
+ } catch (error) {
238
+ const errorMsg = `Error processing locator group '${locatorGroup.name}': ${error}`
239
+ result.errors.push(errorMsg)
240
+ console.error(` āŒ ${errorMsg}`)
241
+ }
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Deletes orphaned locator groups (groups in DB but not in FS)
247
+ */
248
+ async function deleteOrphanedLocatorGroups(
249
+ fsLocatorGroupNames: Set<string>,
250
+ result: SyncResult,
251
+ ): Promise<void> {
252
+ console.log('\nšŸ” Checking for orphaned locator groups (not in filesystem)...')
253
+
254
+ try {
255
+ // Get all locator groups from database
256
+ const dbLocatorGroups = await prisma.locatorGroup.findMany({
257
+ include: {
258
+ locators: {
259
+ select: { id: true },
260
+ },
261
+ },
262
+ })
263
+
264
+ for (const dbGroup of dbLocatorGroups) {
265
+ // Check if locator group exists in filesystem
266
+ if (!fsLocatorGroupNames.has(dbGroup.name)) {
267
+ try {
268
+ const locatorCount = dbGroup.locators.length
269
+
270
+ if (locatorCount > 0) {
271
+ console.log(
272
+ ` āš ļø Locator group '${dbGroup.name}' has ${locatorCount} locator(s) - will be cascade deleted`,
273
+ )
274
+ }
275
+
276
+ // Delete the locator group (Prisma cascade will handle locators)
277
+ await prisma.locatorGroup.delete({
278
+ where: { id: dbGroup.id },
279
+ })
280
+
281
+ result.locatorGroupsDeleted++
282
+ result.deletedLocatorGroups.push({
283
+ name: dbGroup.name,
284
+ locatorCount,
285
+ })
286
+ console.log(` šŸ—‘ļø Deleted locator group '${dbGroup.name}' (not in filesystem)`)
287
+ } catch (error) {
288
+ const errorMsg = `Error deleting locator group '${dbGroup.name}': ${error}`
289
+ result.errors.push(errorMsg)
290
+ console.error(` āŒ ${errorMsg}`)
291
+ }
292
+ }
293
+ }
294
+ } catch (error) {
295
+ const errorMsg = `Error checking for orphaned locator groups: ${error}`
296
+ result.errors.push(errorMsg)
297
+ console.error(` āŒ ${errorMsg}`)
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Generates and displays sync summary
303
+ */
304
+ function generateSummary(result: SyncResult): void {
305
+ console.log('\nšŸ“Š Sync Summary:')
306
+ console.log(` šŸ“ Locator groups scanned: ${result.locatorGroupsScanned}`)
307
+ console.log(` āœ… Locator groups existing: ${result.locatorGroupsExisting}`)
308
+ console.log(` āž• Locator groups created: ${result.locatorGroupsCreated}`)
309
+ console.log(` šŸ”„ Locator groups updated: ${result.locatorGroupsUpdated}`)
310
+ console.log(` šŸ—‘ļø Locator groups deleted: ${result.locatorGroupsDeleted}`)
311
+ console.log(` āŒ Errors: ${result.errors.length}`)
312
+
313
+ if (result.createdLocatorGroups.length > 0) {
314
+ console.log('\n Created locator groups:')
315
+ result.createdLocatorGroups.forEach((name, index) => {
316
+ console.log(` ${index + 1}. ${name}`)
317
+ })
318
+ }
319
+
320
+ if (result.updatedLocatorGroups.length > 0) {
321
+ console.log('\n Updated locator groups:')
322
+ result.updatedLocatorGroups.forEach((name, index) => {
323
+ console.log(` ${index + 1}. ${name}`)
324
+ })
325
+ }
326
+
327
+ if (result.deletedLocatorGroups.length > 0) {
328
+ console.log('\n Deleted locator groups:')
329
+ result.deletedLocatorGroups.forEach((group, index) => {
330
+ console.log(
331
+ ` ${index + 1}. ${group.name} (${group.locatorCount} locator(s) cascade deleted)`,
332
+ )
333
+ })
334
+ }
335
+
336
+ if (result.errors.length > 0) {
337
+ console.log('\n Errors:')
338
+ result.errors.forEach((error, index) => {
339
+ console.log(` ${index + 1}. ${error}`)
340
+ })
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Main function
346
+ */
347
+ async function main() {
348
+ try {
349
+ console.log('šŸ”„ Starting locator groups sync...')
350
+ console.log('This will scan filesystem directories and sync locator groups to database.')
351
+ console.log('Filesystem is the source of truth - locator groups in DB but not in FS will be deleted.\n')
352
+
353
+ const baseDir = process.cwd()
354
+
355
+ // Initialize result
356
+ const result: SyncResult = {
357
+ locatorGroupsScanned: 0,
358
+ locatorGroupsExisting: 0,
359
+ locatorGroupsCreated: 0,
360
+ locatorGroupsUpdated: 0,
361
+ locatorGroupsDeleted: 0,
362
+ errors: [],
363
+ createdLocatorGroups: [],
364
+ updatedLocatorGroups: [],
365
+ deletedLocatorGroups: [],
366
+ }
367
+
368
+ // Read locator map
369
+ console.log('šŸ“„ Reading locator-map.json...')
370
+ const locatorMap = await readLocatorMap(baseDir)
371
+ console.log(` Found ${locatorMap.length} entry(ies) in locator map`)
372
+
373
+ // Build locator groups from filesystem
374
+ console.log('\nšŸ“ Scanning automation/locators directory...')
375
+ const locatorGroups = await buildLocatorGroupsFromFS(baseDir, locatorMap)
376
+ result.locatorGroupsScanned = locatorGroups.length
377
+ console.log(` Found ${locatorGroups.length} locator group(s) in filesystem`)
378
+
379
+ if (locatorGroups.length > 0) {
380
+ console.log('\n Locator groups found:')
381
+ locatorGroups.forEach((group, index) => {
382
+ console.log(` ${index + 1}. ${group.name} (route: ${group.route}, module: ${group.modulePath})`)
383
+ })
384
+ }
385
+
386
+ // Sync to database
387
+ await syncLocatorGroupsToDatabase(locatorGroups, result)
388
+
389
+ // Delete orphaned locator groups
390
+ const fsLocatorGroupNames = new Set(locatorGroups.map(g => g.name))
391
+ await deleteOrphanedLocatorGroups(fsLocatorGroupNames, result)
392
+
393
+ // Generate summary
394
+ generateSummary(result)
395
+
396
+ if (result.errors.length === 0) {
397
+ console.log('\nāœ… Sync completed successfully!')
398
+ } else {
399
+ console.log('\nāš ļø Sync completed with errors. Please review the errors above.')
400
+ process.exit(1)
401
+ }
402
+ } catch (error) {
403
+ console.error('\nāŒ Error during sync:', error)
404
+ process.exit(1)
405
+ } finally {
406
+ await prisma.$disconnect()
407
+ }
408
+ }
409
+
410
+ main()