create-appraisejs 0.2.0 → 0.3.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (485) hide show
  1. package/README.md +12 -1
  2. package/package.json +1 -1
  3. package/templates/default/.appraise-template-meta.json +2 -2
  4. package/templates/default/.env.example +2 -2
  5. package/templates/default/README.md +57 -53
  6. package/templates/default/automation/steps/actions/click.step.ts +58 -58
  7. package/templates/default/automation/steps/actions/hover.step.ts +27 -27
  8. package/templates/default/automation/steps/actions/navigation.step.ts +70 -70
  9. package/templates/default/automation/steps/actions/random_data.step.ts +142 -142
  10. package/templates/default/automation/steps/actions/store.step.ts +86 -86
  11. package/templates/default/automation/steps/actions/wait.step.ts +110 -90
  12. package/templates/default/automation/steps/validations/active_state_assertion.step.ts +30 -30
  13. package/templates/default/automation/steps/validations/navigation_assertion.step.ts +22 -22
  14. package/templates/default/automation/steps/validations/text_assertion.step.ts +107 -107
  15. package/templates/default/automation/steps/validations/visibility_assertion.step.ts +26 -26
  16. package/templates/default/components.json +24 -24
  17. package/templates/default/cucumber.mjs +16 -16
  18. package/templates/default/eslint.config.mjs +20 -16
  19. package/templates/default/next-env.d.ts +6 -6
  20. package/templates/default/next.config.ts +20 -11
  21. package/templates/default/package-lock.json +1775 -74
  22. package/templates/default/package.json +8 -1
  23. package/templates/default/packages/cucumber-runtime/package.json +13 -13
  24. package/templates/default/packages/cucumber-runtime/src/cache.util.ts +93 -93
  25. package/templates/default/packages/cucumber-runtime/src/cli.ts +68 -68
  26. package/templates/default/packages/cucumber-runtime/src/environment.util.ts +21 -21
  27. package/templates/default/packages/cucumber-runtime/src/executor.ts +32 -32
  28. package/templates/default/packages/cucumber-runtime/src/index.ts +17 -17
  29. package/templates/default/packages/cucumber-runtime/src/locator.util.ts +234 -234
  30. package/templates/default/packages/cucumber-runtime/src/parameter-types.ts +7 -7
  31. package/templates/default/packages/cucumber-runtime/src/random-data.util.ts +35 -35
  32. package/templates/default/packages/cucumber-runtime/src/types.ts +13 -13
  33. package/templates/default/packages/cucumber-runtime/src/world.ts +44 -44
  34. package/templates/default/packages/cucumber-runtime/tsconfig.json +11 -11
  35. package/templates/default/postcss.config.mjs +8 -8
  36. package/templates/default/prisma/dev.db +0 -0
  37. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  38. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  39. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  40. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  41. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  42. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  43. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  44. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  45. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  46. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  47. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  48. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  49. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  50. package/templates/default/prisma/migrations/20260313093000_add_report_step_screenshot_path/migration.sql +1 -1
  51. package/templates/default/scripts/build-step-registry.ts +33 -0
  52. package/templates/default/scripts/install-playwright.ts +17 -8
  53. package/templates/default/scripts/install-template-step.ts +128 -0
  54. package/templates/default/scripts/lib/filename-utils.test.ts +24 -0
  55. package/templates/default/scripts/lib/filename-utils.ts +24 -0
  56. package/templates/default/scripts/lib/jsdoc-parser.test.ts +63 -0
  57. package/templates/default/scripts/lib/jsdoc-parser.ts +88 -0
  58. package/templates/default/scripts/lib/step-file-parser.test.ts +71 -0
  59. package/templates/default/scripts/lib/step-file-parser.ts +315 -0
  60. package/templates/default/scripts/lib/step-matcher.test.ts +86 -0
  61. package/templates/default/scripts/lib/step-matcher.ts +120 -0
  62. package/templates/default/scripts/lib/sync-script-runner.ts +23 -0
  63. package/templates/default/scripts/lib/sync-summary.ts +29 -0
  64. package/templates/default/scripts/lib/tag-parsing.test.ts +20 -0
  65. package/templates/default/scripts/lib/tag-parsing.ts +10 -0
  66. package/templates/default/scripts/lib/template-step-installer.test.ts +225 -0
  67. package/templates/default/scripts/lib/template-step-installer.ts +404 -0
  68. package/templates/default/scripts/lib/template-step-registry.test.ts +118 -0
  69. package/templates/default/scripts/lib/template-step-registry.ts +137 -0
  70. package/templates/default/scripts/protect-seeded-files.ts +51 -38
  71. package/templates/default/scripts/regenerate-features.ts +98 -94
  72. package/templates/default/scripts/run-vitest.ts +59 -0
  73. package/templates/default/scripts/setup-env.ts +26 -19
  74. package/templates/default/scripts/sync-all.ts +44 -54
  75. package/templates/default/scripts/sync-appraise-base-template.ts +44 -16
  76. package/templates/default/scripts/sync-environments.ts +22 -66
  77. package/templates/default/scripts/sync-locator-groups.ts +358 -410
  78. package/templates/default/scripts/sync-locators.ts +348 -398
  79. package/templates/default/scripts/sync-modules.ts +302 -341
  80. package/templates/default/scripts/sync-tags.ts +29 -65
  81. package/templates/default/scripts/sync-template-step-groups.ts +24 -182
  82. package/templates/default/scripts/sync-template-steps.ts +36 -493
  83. package/templates/default/scripts/sync-test-cases.ts +296 -539
  84. package/templates/default/scripts/sync-test-suites.ts +32 -79
  85. package/templates/default/src/actions/dashboard/dashboard-actions.ts +70 -241
  86. package/templates/default/src/actions/environments/environment-actions.ts +102 -188
  87. package/templates/default/src/actions/locator/locator-actions.ts +77 -490
  88. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +34 -212
  89. package/templates/default/src/actions/locator-picker/locator-picker-actions.test.ts +81 -0
  90. package/templates/default/src/actions/locator-picker/locator-picker-actions.ts +20 -161
  91. package/templates/default/src/actions/modules/module-actions.ts +99 -135
  92. package/templates/default/src/actions/reports/report-actions.ts +28 -565
  93. package/templates/default/src/actions/settings/sync-actions.test.ts +58 -0
  94. package/templates/default/src/actions/tags/tag-actions.ts +99 -107
  95. package/templates/default/src/actions/template-step/template-step-actions.ts +33 -194
  96. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +35 -92
  97. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +98 -238
  98. package/templates/default/src/actions/test-case/test-case-actions.ts +108 -356
  99. package/templates/default/src/actions/test-run/test-run-actions.ts +74 -1081
  100. package/templates/default/src/actions/test-suite/test-suite-actions.ts +35 -202
  101. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
  102. package/templates/default/src/app/(base)/environments/environment-form.test.tsx +92 -0
  103. package/templates/default/src/app/(base)/environments/environment-form.tsx +228 -219
  104. package/templates/default/src/app/(base)/environments/environment-helpers.ts +58 -0
  105. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
  106. package/templates/default/src/app/(base)/environments/environment-table.tsx +25 -24
  107. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +49 -46
  108. package/templates/default/src/app/(base)/environments/page.tsx +59 -59
  109. package/templates/default/src/app/(base)/layout.tsx +10 -10
  110. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -44
  111. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -215
  112. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -77
  113. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -28
  114. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -46
  115. package/templates/default/src/app/(base)/locators/create/create-locator-workspace-helpers.test.ts +71 -0
  116. package/templates/default/src/app/(base)/locators/create/create-locator-workspace-helpers.ts +333 -0
  117. package/templates/default/src/app/(base)/locators/create/create-locator-workspace.test.tsx +125 -0
  118. package/templates/default/src/app/(base)/locators/create/create-locator-workspace.tsx +56 -274
  119. package/templates/default/src/app/(base)/locators/create/page.tsx +8 -4
  120. package/templates/default/src/app/(base)/locators/create/use-locator-workspace.ts +183 -0
  121. package/templates/default/src/app/(base)/locators/locator-helpers.test.ts +28 -0
  122. package/templates/default/src/app/(base)/locators/locator-helpers.ts +59 -0
  123. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +74 -73
  124. package/templates/default/src/app/(base)/locators/locator-table.tsx +30 -28
  125. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +20 -8
  126. package/templates/default/src/app/(base)/locators/page.tsx +3 -6
  127. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +67 -66
  128. package/templates/default/src/app/(base)/modules/create/page.tsx +33 -34
  129. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +43 -46
  130. package/templates/default/src/app/(base)/modules/module-form.test.tsx +84 -0
  131. package/templates/default/src/app/(base)/modules/module-form.tsx +159 -126
  132. package/templates/default/src/app/(base)/modules/module-helpers.ts +64 -0
  133. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +81 -85
  134. package/templates/default/src/app/(base)/modules/module-table.tsx +25 -24
  135. package/templates/default/src/app/(base)/modules/page.tsx +59 -59
  136. package/templates/default/src/app/(base)/reports/[id]/page.tsx +20 -260
  137. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
  138. package/templates/default/src/app/(base)/reports/feature-chart.tsx +79 -78
  139. package/templates/default/src/app/(base)/reports/overview-chart.tsx +49 -49
  140. package/templates/default/src/app/(base)/reports/page.tsx +98 -98
  141. package/templates/default/src/app/(base)/reports/report-detail-helpers.test.ts +109 -0
  142. package/templates/default/src/app/(base)/reports/report-detail-helpers.ts +247 -0
  143. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +78 -78
  144. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -189
  145. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -72
  146. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +40 -40
  147. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -115
  148. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -27
  149. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -42
  150. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -79
  151. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -27
  152. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +58 -58
  153. package/templates/default/src/app/(base)/settings/settings-sync-panel-helpers.test.tsx +40 -0
  154. package/templates/default/src/app/(base)/settings/settings-sync-panel-helpers.tsx +110 -0
  155. package/templates/default/src/app/(base)/settings/settings-sync-panel.test.tsx +127 -0
  156. package/templates/default/src/app/(base)/settings/settings-sync-panel.tsx +19 -134
  157. package/templates/default/src/app/(base)/settings/use-settings-sync.ts +66 -0
  158. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -39
  159. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -50
  160. package/templates/default/src/app/(base)/tags/page.tsx +58 -58
  161. package/templates/default/src/app/(base)/tags/tag-form-helpers.ts +13 -0
  162. package/templates/default/src/app/(base)/tags/tag-form.test.tsx +83 -0
  163. package/templates/default/src/app/(base)/tags/tag-form.tsx +143 -147
  164. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -63
  165. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -29
  166. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -28
  167. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +43 -45
  168. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -60
  169. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.test.tsx +82 -0
  170. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +181 -167
  171. package/templates/default/src/app/(base)/template-step-groups/template-step-group-helpers.ts +54 -0
  172. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -89
  173. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +34 -32
  174. package/templates/default/src/app/(base)/template-steps/create/page.tsx +40 -37
  175. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +54 -49
  176. package/templates/default/src/app/(base)/template-steps/page.tsx +59 -58
  177. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +233 -213
  178. package/templates/default/src/app/(base)/template-steps/template-step-form.test.tsx +132 -0
  179. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +342 -384
  180. package/templates/default/src/app/(base)/template-steps/template-step-helpers.test.ts +99 -0
  181. package/templates/default/src/app/(base)/template-steps/template-step-helpers.ts +176 -0
  182. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +153 -158
  183. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +26 -24
  184. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -56
  185. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -89
  186. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -58
  187. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.test.tsx +109 -0
  188. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +45 -84
  189. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.test.tsx +140 -0
  190. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +154 -262
  191. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -76
  192. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -32
  193. package/templates/default/src/app/(base)/test-cases/create/page.tsx +90 -76
  194. package/templates/default/src/app/(base)/test-cases/create-from-template/create-from-template-helpers.test.ts +94 -0
  195. package/templates/default/src/app/(base)/test-cases/create-from-template/create-from-template-helpers.ts +171 -0
  196. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +105 -96
  197. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +40 -38
  198. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.test.tsx +87 -0
  199. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +83 -73
  200. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -106
  201. package/templates/default/src/app/(base)/test-cases/page.tsx +3 -2
  202. package/templates/default/src/app/(base)/test-cases/test-case-flow.test.tsx +108 -0
  203. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +43 -82
  204. package/templates/default/src/app/(base)/test-cases/test-case-form.test.tsx +202 -0
  205. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +263 -395
  206. package/templates/default/src/app/(base)/test-cases/test-case-route-helpers.test.ts +95 -0
  207. package/templates/default/src/app/(base)/test-cases/test-case-route-helpers.ts +147 -0
  208. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +4 -2
  209. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +11 -10
  210. package/templates/default/src/app/(base)/test-runs/create/page.tsx +4 -5
  211. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
  212. package/templates/default/src/app/(base)/test-runs/test-run-form-helpers.test.ts +50 -0
  213. package/templates/default/src/app/(base)/test-runs/test-run-form-helpers.ts +168 -0
  214. package/templates/default/src/app/(base)/test-runs/test-run-form.test.tsx +138 -0
  215. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +111 -256
  216. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -229
  217. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -127
  218. package/templates/default/src/app/(base)/test-runs/use-test-run-name-validation.ts +74 -0
  219. package/templates/default/src/app/(base)/test-suites/create/page.tsx +17 -12
  220. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +22 -19
  221. package/templates/default/src/app/(base)/test-suites/page.tsx +14 -56
  222. package/templates/default/src/app/(base)/test-suites/test-suite-form.test.tsx +127 -0
  223. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +45 -64
  224. package/templates/default/src/app/(base)/test-suites/test-suite-helpers.test.ts +67 -0
  225. package/templates/default/src/app/(base)/test-suites/test-suite-helpers.ts +215 -0
  226. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +32 -29
  227. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -187
  228. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +12 -12
  229. package/templates/default/src/app/(dashboard-components)/data-card.tsx +26 -26
  230. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +56 -56
  231. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -87
  232. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +44 -44
  233. package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.test.ts +83 -0
  234. package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.ts +52 -52
  235. package/templates/default/src/app/api/test-runs/[runId]/download/route.test.ts +169 -0
  236. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +1 -1
  237. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.test.ts +135 -0
  238. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -146
  239. package/templates/default/src/app/globals.css +147 -147
  240. package/templates/default/src/app/page.tsx +1 -1
  241. package/templates/default/src/assets/icons/empty-tube.tsx +23 -23
  242. package/templates/default/src/assets/icons/tube-plus.tsx +29 -29
  243. package/templates/default/src/components/base-node.tsx +21 -21
  244. package/templates/default/src/components/chart/pie-chart.tsx +73 -73
  245. package/templates/default/src/components/data-extraction/locator-inspector-helpers.test.ts +32 -0
  246. package/templates/default/src/components/data-extraction/locator-inspector-helpers.ts +183 -0
  247. package/templates/default/src/components/data-extraction/locator-inspector.tsx +349 -460
  248. package/templates/default/src/components/data-state/empty-state.tsx +40 -40
  249. package/templates/default/src/components/data-visualization/info-card.tsx +70 -70
  250. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -22
  251. package/templates/default/src/components/diagram/button-edge.tsx +54 -54
  252. package/templates/default/src/components/diagram/dynamic-parameters-helpers.test.ts +83 -0
  253. package/templates/default/src/components/diagram/dynamic-parameters-helpers.ts +158 -0
  254. package/templates/default/src/components/diagram/dynamic-parameters.tsx +350 -474
  255. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
  256. package/templates/default/src/components/diagram/flow-diagram-helpers.test.ts +117 -0
  257. package/templates/default/src/components/diagram/flow-diagram-helpers.ts +251 -0
  258. package/templates/default/src/components/diagram/flow-diagram.tsx +247 -470
  259. package/templates/default/src/components/diagram/flow-host-helpers.test.ts +74 -0
  260. package/templates/default/src/components/diagram/flow-host-helpers.ts +51 -0
  261. package/templates/default/src/components/diagram/node-form-helpers.test.ts +92 -0
  262. package/templates/default/src/components/diagram/node-form-helpers.ts +100 -0
  263. package/templates/default/src/components/diagram/node-form.test.tsx +168 -0
  264. package/templates/default/src/components/diagram/node-form.tsx +199 -262
  265. package/templates/default/src/components/diagram/options-header-node.tsx +57 -57
  266. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -155
  267. package/templates/default/src/components/diagram/use-flow-node-order.ts +49 -0
  268. package/templates/default/src/components/form/error-message.tsx +7 -7
  269. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -453
  270. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -30
  271. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -8
  272. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -8
  273. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -8
  274. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -14
  275. package/templates/default/src/components/navigation/command-badge.tsx +34 -34
  276. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -51
  277. package/templates/default/src/components/navigation/entity-search-command.tsx +118 -116
  278. package/templates/default/src/components/navigation/nav-card.tsx +31 -31
  279. package/templates/default/src/components/navigation/nav-command-helpers.ts +122 -0
  280. package/templates/default/src/components/navigation/nav-command-search.tsx +125 -0
  281. package/templates/default/src/components/navigation/nav-command.test.tsx +106 -0
  282. package/templates/default/src/components/navigation/nav-command.tsx +49 -472
  283. package/templates/default/src/components/navigation/nav-link.tsx +60 -60
  284. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -112
  285. package/templates/default/src/components/navigation/use-nav-command.ts +58 -0
  286. package/templates/default/src/components/node-header.tsx +159 -159
  287. package/templates/default/src/components/reports/test-case-logs-modal.tsx +310 -310
  288. package/templates/default/src/components/table/table-actions.tsx +174 -172
  289. package/templates/default/src/components/test-case/test-case-form-helpers.test.ts +100 -0
  290. package/templates/default/src/components/test-case/test-case-form-helpers.ts +140 -0
  291. package/templates/default/src/components/test-case/test-case-picker-helpers.test.ts +40 -0
  292. package/templates/default/src/components/test-case/test-case-picker-helpers.ts +41 -0
  293. package/templates/default/src/components/test-case/test-case-picker.test.tsx +44 -0
  294. package/templates/default/src/components/test-case/test-case-picker.tsx +16 -35
  295. package/templates/default/src/components/test-case/test-scenario-preview.tsx +34 -0
  296. package/templates/default/src/components/test-run/download-logs-button.tsx +92 -92
  297. package/templates/default/src/components/test-run/log-viewer-helpers.test.ts +37 -0
  298. package/templates/default/src/components/test-run/log-viewer-helpers.ts +80 -0
  299. package/templates/default/src/components/test-run/log-viewer.test.tsx +118 -0
  300. package/templates/default/src/components/test-run/log-viewer.tsx +51 -350
  301. package/templates/default/src/components/test-run/test-run-details-helpers.test.ts +31 -0
  302. package/templates/default/src/components/test-run/test-run-details-helpers.ts +208 -0
  303. package/templates/default/src/components/test-run/test-run-details.test.tsx +174 -0
  304. package/templates/default/src/components/test-run/test-run-details.tsx +155 -457
  305. package/templates/default/src/components/test-run/test-run-header-helpers.test.ts +31 -0
  306. package/templates/default/src/components/test-run/test-run-header-helpers.ts +23 -0
  307. package/templates/default/src/components/test-run/test-run-header.test.tsx +103 -0
  308. package/templates/default/src/components/test-run/test-run-header.tsx +27 -149
  309. package/templates/default/src/components/test-run/use-log-viewer.ts +213 -0
  310. package/templates/default/src/components/test-run/use-test-run-details.ts +184 -0
  311. package/templates/default/src/components/test-run/use-test-run-header.ts +89 -0
  312. package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
  313. package/templates/default/src/components/test-suite/test-suite-picker-helpers.test.ts +68 -0
  314. package/templates/default/src/components/test-suite/test-suite-picker-helpers.ts +76 -0
  315. package/templates/default/src/components/test-suite/test-suite-picker.test.tsx +65 -0
  316. package/templates/default/src/components/test-suite/test-suite-picker.tsx +4 -72
  317. package/templates/default/src/components/theme/mode-toggle.tsx +54 -54
  318. package/templates/default/src/components/theme/theme-provider.tsx +8 -8
  319. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -7
  320. package/templates/default/src/components/typography/page-header.tsx +7 -7
  321. package/templates/default/src/components/ui/alert-dialog.tsx +106 -106
  322. package/templates/default/src/components/ui/alert.tsx +43 -43
  323. package/templates/default/src/components/ui/avatar.tsx +40 -40
  324. package/templates/default/src/components/ui/badge.tsx +29 -29
  325. package/templates/default/src/components/ui/button.tsx +47 -47
  326. package/templates/default/src/components/ui/calendar.tsx +158 -158
  327. package/templates/default/src/components/ui/card.tsx +43 -43
  328. package/templates/default/src/components/ui/checkbox.tsx +28 -28
  329. package/templates/default/src/components/ui/command.tsx +135 -135
  330. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -61
  331. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -87
  332. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -50
  333. package/templates/default/src/components/ui/data-table.test.tsx +122 -0
  334. package/templates/default/src/components/ui/data-table.tsx +298 -261
  335. package/templates/default/src/components/ui/dialog.tsx +97 -97
  336. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -182
  337. package/templates/default/src/components/ui/input.tsx +22 -22
  338. package/templates/default/src/components/ui/kbd.tsx +28 -28
  339. package/templates/default/src/components/ui/label.tsx +19 -19
  340. package/templates/default/src/components/ui/loading.tsx +12 -12
  341. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -116
  342. package/templates/default/src/components/ui/multi-select.test.tsx +45 -0
  343. package/templates/default/src/components/ui/multi-select.tsx +158 -142
  344. package/templates/default/src/components/ui/navigation-menu.tsx +120 -120
  345. package/templates/default/src/components/ui/popover.tsx +33 -33
  346. package/templates/default/src/components/ui/progress.tsx +25 -25
  347. package/templates/default/src/components/ui/radio-group.tsx +44 -44
  348. package/templates/default/src/components/ui/scroll-area.tsx +40 -40
  349. package/templates/default/src/components/ui/select.tsx +151 -151
  350. package/templates/default/src/components/ui/separator.tsx +22 -22
  351. package/templates/default/src/components/ui/skeleton.tsx +7 -7
  352. package/templates/default/src/components/ui/table.tsx +76 -76
  353. package/templates/default/src/components/ui/tabs.tsx +55 -55
  354. package/templates/default/src/components/ui/textarea.tsx +21 -21
  355. package/templates/default/src/components/ui/toast.tsx +113 -113
  356. package/templates/default/src/components/ui/toaster.tsx +26 -26
  357. package/templates/default/src/components/user-prompt/delete-prompt.test.tsx +60 -0
  358. package/templates/default/src/components/user-prompt/delete-prompt.tsx +118 -87
  359. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -30
  360. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -24
  361. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -28
  362. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -21
  363. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -42
  364. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -16
  365. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -24
  366. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -39
  367. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -36
  368. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -43
  369. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -24
  370. package/templates/default/src/hooks/use-toast.ts +187 -187
  371. package/templates/default/src/lib/automation/automation-path-roots.ts +95 -0
  372. package/templates/default/src/lib/automation/automation-workspace.ts +147 -0
  373. package/templates/default/src/lib/automation/paths.ts +6 -211
  374. package/templates/default/src/lib/bidirectional-sync.ts +432 -432
  375. package/templates/default/src/lib/environment-file-utils.ts +2 -1
  376. package/templates/default/src/lib/executor/local-executor-adapter.ts +2 -5
  377. package/templates/default/src/lib/feature-file-generator.ts +2 -1
  378. package/templates/default/src/lib/gherkin-parser.ts +0 -2
  379. package/templates/default/src/lib/locator-group-file-utils.ts +304 -307
  380. package/templates/default/src/lib/locator-picker/session-manager.ts +0 -21
  381. package/templates/default/src/lib/locator-picker/suggestions.ts +4 -2
  382. package/templates/default/src/lib/metrics/metric-calculator.ts +2 -6
  383. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -205
  384. package/templates/default/src/lib/path-helpers/module-path.ts +71 -71
  385. package/templates/default/src/lib/sync/sync-executor.test.ts +76 -0
  386. package/templates/default/src/lib/sync/sync-pending-counts.test.ts +227 -226
  387. package/templates/default/src/lib/sync/sync-pending-counts.ts +2 -5
  388. package/templates/default/src/lib/template-sync-utils.d.ts +6 -6
  389. package/templates/default/src/lib/template-sync-utils.js +46 -46
  390. package/templates/default/src/lib/template-sync-utils.ts +63 -63
  391. package/templates/default/src/lib/test-case-utils.ts +6 -6
  392. package/templates/default/src/lib/test-run/log-formatter.ts +83 -83
  393. package/templates/default/src/lib/test-run/report-parser.ts +352 -352
  394. package/templates/default/src/lib/test-run/test-run-executor.ts +13 -13
  395. package/templates/default/src/lib/test-run/winston-logger.ts +65 -64
  396. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -42
  397. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -95
  398. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -160
  399. package/templates/default/src/lib/utils/node-param-validation.ts +81 -81
  400. package/templates/default/src/lib/utils/template-step-file-generator.ts +2 -2
  401. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -166
  402. package/templates/default/src/lib/utils.ts +31 -31
  403. package/templates/default/src/services/dashboard/dashboard-service.test.ts +106 -0
  404. package/templates/default/src/services/dashboard/dashboard-service.ts +173 -0
  405. package/templates/default/src/services/environment/environment-service.test.ts +137 -0
  406. package/templates/default/src/services/environment/environment-service.ts +96 -0
  407. package/templates/default/src/services/locator/locator-path-utils.test.ts +14 -0
  408. package/templates/default/src/services/locator/locator-path-utils.ts +14 -0
  409. package/templates/default/src/services/locator/locator-service.test.ts +63 -0
  410. package/templates/default/src/services/locator/locator-service.ts +479 -0
  411. package/templates/default/src/services/locator/locator-sync-utils.test.ts +19 -0
  412. package/templates/default/src/services/locator/locator-sync-utils.ts +21 -0
  413. package/templates/default/src/services/locator-group/locator-group-service.test.ts +123 -0
  414. package/templates/default/src/services/locator-group/locator-group-service.ts +180 -0
  415. package/templates/default/src/services/module/module-service.test.ts +89 -0
  416. package/templates/default/src/services/module/module-service.ts +66 -0
  417. package/templates/default/src/services/report/report-service.test.ts +244 -0
  418. package/templates/default/src/services/report/report-service.ts +438 -0
  419. package/templates/default/src/services/shared/constants.ts +2 -0
  420. package/templates/default/src/services/shared/errors.test.ts +38 -0
  421. package/templates/default/src/services/shared/errors.ts +44 -0
  422. package/templates/default/src/services/shared/index.ts +7 -0
  423. package/templates/default/src/services/tag/tag-service.test.ts +22 -0
  424. package/templates/default/src/services/tag/tag-service.ts +41 -0
  425. package/templates/default/src/services/template-step/template-step-service.test.ts +22 -0
  426. package/templates/default/src/services/template-step/template-step-service.ts +171 -0
  427. package/templates/default/src/services/template-step-group/template-step-group-service.test.ts +22 -0
  428. package/templates/default/src/services/template-step-group/template-step-group-service.ts +81 -0
  429. package/templates/default/src/services/template-test-case/template-test-case-service.test.ts +22 -0
  430. package/templates/default/src/services/template-test-case/template-test-case-service.ts +128 -0
  431. package/templates/default/src/services/test-case/test-case-service.test.ts +175 -0
  432. package/templates/default/src/services/test-case/test-case-service.ts +298 -0
  433. package/templates/default/src/services/test-run/test-run-helpers.ts +61 -0
  434. package/templates/default/src/services/test-run/test-run-service.test.ts +647 -0
  435. package/templates/default/src/services/test-run/test-run-service.ts +917 -0
  436. package/templates/default/src/services/test-suite/test-suite-service.test.ts +127 -0
  437. package/templates/default/src/services/test-suite/test-suite-service.ts +197 -0
  438. package/templates/default/src/types/diagram/diagram.ts +34 -34
  439. package/templates/default/src/types/diagram/template-step.ts +11 -11
  440. package/templates/default/src/types/executor/browser.type.ts +1 -1
  441. package/templates/default/src/types/form/actionHandler.ts +19 -6
  442. package/templates/default/src/types/locator/locator.type.ts +11 -11
  443. package/templates/default/src/types/step/step.type.ts +1 -1
  444. package/templates/default/src/types/table/data-table.ts +6 -6
  445. package/templates/default/tailwind.config.ts +62 -62
  446. package/templates/default/tsconfig.json +1 -1
  447. package/dist/cli.e2e.test.d.ts +0 -2
  448. package/dist/cli.e2e.test.d.ts.map +0 -1
  449. package/dist/cli.e2e.test.js +0 -73
  450. package/dist/cli.e2e.test.js.map +0 -1
  451. package/dist/config.test.d.ts +0 -2
  452. package/dist/config.test.d.ts.map +0 -1
  453. package/dist/config.test.js +0 -65
  454. package/dist/config.test.js.map +0 -1
  455. package/dist/copy-template.test.d.ts +0 -2
  456. package/dist/copy-template.test.d.ts.map +0 -1
  457. package/dist/copy-template.test.js +0 -71
  458. package/dist/copy-template.test.js.map +0 -1
  459. package/dist/download-repo.test.d.ts +0 -2
  460. package/dist/download-repo.test.d.ts.map +0 -1
  461. package/dist/download-repo.test.js +0 -14
  462. package/dist/download-repo.test.js.map +0 -1
  463. package/dist/install.test.d.ts +0 -2
  464. package/dist/install.test.d.ts.map +0 -1
  465. package/dist/install.test.js +0 -119
  466. package/dist/install.test.js.map +0 -1
  467. package/dist/prompts.test.d.ts +0 -2
  468. package/dist/prompts.test.d.ts.map +0 -1
  469. package/dist/prompts.test.js +0 -58
  470. package/dist/prompts.test.js.map +0 -1
  471. package/templates/default/src/actions/conflict/conflict.action.ts +0 -33
  472. package/templates/default/src/actions/review/review-actions.ts +0 -147
  473. package/templates/default/src/actions/user/user-actions.ts +0 -13
  474. package/templates/default/src/app/(base)/locators/locator-form.tsx +0 -163
  475. package/templates/default/src/app/(base)/reviews/create/page.tsx +0 -26
  476. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +0 -15
  477. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +0 -26
  478. package/templates/default/src/app/(base)/reviews/page.tsx +0 -26
  479. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +0 -26
  480. package/templates/default/src/app/(base)/reviews/review-form.tsx +0 -11
  481. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +0 -9
  482. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +0 -9
  483. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +0 -15
  484. package/templates/default/src/constants/form-opts/locator-form-opts.ts +0 -20
  485. package/templates/default/src/constants/form-opts/review-form-opts.ts +0 -23
@@ -1,398 +1,348 @@
1
- #!/usr/bin/env tsx
2
-
3
- /**
4
- * Script to synchronize locators from filesystem to database
5
- * Scans locator JSON files to ensure all locators exist in DB
6
- * Filesystem is the source of truth - locators in DB but not in FS will be deleted
7
- * Run this after merging changes to ensure locator sync
8
- *
9
- * Usage: npx tsx scripts/sync-locators.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 { buildModuleHierarchy } from '../src/lib/module-hierarchy-builder'
17
- import { getLocatorGroupFilePath } from '../src/lib/locator-group-file-utils'
18
- import { extractModulePathFromAutomationFile } from '../src/lib/template-sync-utils'
19
-
20
- interface SyncResult {
21
- locatorsScanned: number
22
- locatorsExisting: number
23
- locatorsCreated: number
24
- locatorsDeleted: number
25
- locatorsUpdated: number
26
- locatorGroupsDeleted: number
27
- errors: string[]
28
- createdLocators: Array<{ name: string; group: string }>
29
- deletedLocators: Array<{ name: string; group: string }>
30
- updatedLocators: Array<{ name: string; group: string }>
31
- deletedLocatorGroups: Array<{ name: string; locatorCount: number }>
32
- }
33
-
34
- /**
35
- * Scans locator directory for all JSON files
36
- */
37
- async function scanLocatorFiles(baseDir: string): Promise<string[]> {
38
- const pattern = 'automation/locators/**/*.json'
39
- try {
40
- const files = await glob(pattern, {
41
- cwd: baseDir,
42
- })
43
- return files.map(file => join(baseDir, file))
44
- } catch (error) {
45
- throw new Error(`Error scanning locator files: ${error}`)
46
- }
47
- }
48
-
49
- /**
50
- * Extracts module path from locator file path
51
- * Example: automation/locators/users/admins/directors/directors.json -> /users/admins/directors
52
- */
53
- function extractModulePathFromLocatorFile(filePath: string, baseDir: string): string {
54
- return extractModulePathFromAutomationFile(filePath, baseDir, 'locators')
55
- }
56
-
57
- /**
58
- * Extracts locator group name from file path
59
- * The group name is just the filename without extension
60
- */
61
- function extractLocatorGroupName(filePath: string): string {
62
- const fileName = filePath.split(/[/\\]/).pop() || ''
63
- return fileName.replace('.json', '')
64
- }
65
-
66
- /**
67
- * Reads and parses a locator JSON file
68
- */
69
- async function readLocatorFile(filePath: string): Promise<Record<string, string>> {
70
- try {
71
- await fs.access(filePath)
72
- } catch {
73
- throw new Error(`Locator file not found at ${filePath}`)
74
- }
75
-
76
- try {
77
- const fileContent = await fs.readFile(filePath, 'utf-8')
78
- const jsonContent = JSON.parse(fileContent) as Record<string, string>
79
-
80
- if (!jsonContent || typeof jsonContent !== 'object') {
81
- throw new Error('Invalid JSON structure: expected an object')
82
- }
83
-
84
- return jsonContent
85
- } catch (error) {
86
- if (error instanceof SyntaxError) {
87
- throw new Error(`Invalid JSON in locator file: ${error.message}`)
88
- }
89
- throw error
90
- }
91
- }
92
-
93
- /**
94
- * Finds or creates a LocatorGroup
95
- */
96
- async function findOrCreateLocatorGroup(
97
- groupName: string,
98
- moduleId: string,
99
- _modulePath: string,
100
- ): Promise<string> {
101
- // Try to find existing locator group
102
- const existingGroup = await prisma.locatorGroup.findFirst({
103
- where: {
104
- name: groupName,
105
- moduleId: moduleId,
106
- },
107
- })
108
-
109
- if (existingGroup) {
110
- return existingGroup.id
111
- }
112
-
113
- // Create new locator group
114
- const newGroup = await prisma.locatorGroup.create({
115
- data: {
116
- name: groupName,
117
- route: `/${groupName}`,
118
- moduleId: moduleId,
119
- },
120
- })
121
-
122
- return newGroup.id
123
- }
124
-
125
- /**
126
- * Syncs locators from a single file to database
127
- */
128
- async function syncLocatorsFromFile(
129
- filePath: string,
130
- baseDir: string,
131
- result: SyncResult,
132
- processedGroupIds: Set<string>,
133
- ): Promise<void> {
134
- try {
135
- // Extract module path and group name
136
- const modulePath = extractModulePathFromLocatorFile(filePath, baseDir)
137
- const groupName = extractLocatorGroupName(filePath)
138
-
139
- console.log(`\n šŸ“„ Processing file: ${filePath.replace(baseDir, '')}`)
140
- console.log(` Module path: ${modulePath}`)
141
- console.log(` Group name: ${groupName}`)
142
-
143
- // Build module hierarchy
144
- const moduleId = await buildModuleHierarchy(modulePath)
145
-
146
- // Find or create locator group
147
- const locatorGroupId = await findOrCreateLocatorGroup(groupName, moduleId, modulePath)
148
-
149
- // Track this group as processed (has a file)
150
- processedGroupIds.add(locatorGroupId)
151
-
152
- // Read locators from file
153
- const fileLocators = await readLocatorFile(filePath)
154
- const fileLocatorNames = Object.keys(fileLocators)
155
- result.locatorsScanned += fileLocatorNames.length
156
-
157
- console.log(` Found ${fileLocatorNames.length} locator(s) in file`)
158
-
159
- // Get existing locators from database for this group
160
- const dbLocators = await prisma.locator.findMany({
161
- where: { locatorGroupId: locatorGroupId },
162
- select: { id: true, name: true, value: true },
163
- })
164
-
165
- const dbLocatorMap = new Map(dbLocators.map(loc => [loc.name, loc]))
166
-
167
- // Add or update locators from file
168
- for (const [locatorName, locatorValue] of Object.entries(fileLocators)) {
169
- const existingLocator = dbLocatorMap.get(locatorName)
170
-
171
- if (existingLocator) {
172
- // Check if value changed
173
- if (existingLocator.value !== locatorValue) {
174
- // Update locator value (file takes precedence)
175
- await prisma.locator.update({
176
- where: { id: existingLocator.id },
177
- data: { value: locatorValue },
178
- })
179
- result.locatorsUpdated++
180
- result.updatedLocators.push({ name: locatorName, group: groupName })
181
- console.log(` šŸ”„ Updated locator '${locatorName}'`)
182
- } else {
183
- result.locatorsExisting++
184
- }
185
- } else {
186
- // Create new locator
187
- await prisma.locator.create({
188
- data: {
189
- name: locatorName,
190
- value: locatorValue,
191
- locatorGroupId: locatorGroupId,
192
- },
193
- })
194
- result.locatorsCreated++
195
- result.createdLocators.push({ name: locatorName, group: groupName })
196
- console.log(` āž• Created locator '${locatorName}'`)
197
- }
198
- }
199
-
200
- // Delete locators that exist in DB but not in file (FS is source of truth)
201
- for (const dbLocator of dbLocators) {
202
- if (!fileLocatorNames.includes(dbLocator.name)) {
203
- await prisma.locator.delete({
204
- where: { id: dbLocator.id },
205
- })
206
- result.locatorsDeleted++
207
- result.deletedLocators.push({ name: dbLocator.name, group: groupName })
208
- console.log(` šŸ—‘ļø Deleted locator '${dbLocator.name}' (not in file)`)
209
- }
210
- }
211
- } catch (error) {
212
- const errorMsg = `Error syncing locator file ${filePath}: ${error}`
213
- result.errors.push(errorMsg)
214
- console.error(` āŒ ${errorMsg}`)
215
- }
216
- }
217
-
218
- /**
219
- * Deletes locator groups that don't have corresponding files
220
- */
221
- async function deleteOrphanedLocatorGroups(
222
- processedGroupIds: Set<string>,
223
- baseDir: string,
224
- result: SyncResult,
225
- ): Promise<void> {
226
- console.log('\nšŸ” Checking for orphaned locator groups (no file in filesystem)...')
227
-
228
- try {
229
- // Get all locator groups from database
230
- const allLocatorGroups = await prisma.locatorGroup.findMany({
231
- include: {
232
- locators: {
233
- select: { id: true },
234
- },
235
- },
236
- })
237
-
238
- for (const group of allLocatorGroups) {
239
- // Skip if this group was processed (has a file)
240
- if (processedGroupIds.has(group.id)) {
241
- continue
242
- }
243
-
244
- // Check if file exists for this group
245
- const relativeFilePath = await getLocatorGroupFilePath(group.id)
246
- if (!relativeFilePath) {
247
- // Can't determine file path, skip
248
- continue
249
- }
250
-
251
- const fullPath = join(baseDir, relativeFilePath)
252
-
253
- try {
254
- // Check if file exists
255
- await fs.access(fullPath)
256
- // File exists, so this group is valid
257
- } catch {
258
- // File doesn't exist - delete the locator group (cascade will delete locators)
259
- const locatorCount = group.locators.length
260
- await prisma.locatorGroup.delete({
261
- where: { id: group.id },
262
- })
263
- result.locatorGroupsDeleted++
264
- result.deletedLocatorGroups.push({ name: group.name, locatorCount })
265
- console.log(` šŸ—‘ļø Deleted locator group '${group.name}' (${locatorCount} locator(s) deleted)`)
266
- }
267
- }
268
- } catch (error) {
269
- const errorMsg = `Error deleting orphaned locator groups: ${error}`
270
- result.errors.push(errorMsg)
271
- console.error(` āŒ ${errorMsg}`)
272
- }
273
- }
274
-
275
- /**
276
- * Syncs all locators to database
277
- */
278
- async function syncLocatorsToDatabase(files: string[], baseDir: string): Promise<SyncResult> {
279
- const result: SyncResult = {
280
- locatorsScanned: 0,
281
- locatorsExisting: 0,
282
- locatorsCreated: 0,
283
- locatorsDeleted: 0,
284
- locatorsUpdated: 0,
285
- locatorGroupsDeleted: 0,
286
- errors: [],
287
- createdLocators: [],
288
- deletedLocators: [],
289
- updatedLocators: [],
290
- deletedLocatorGroups: [],
291
- }
292
-
293
- // Track which locator groups have files
294
- const processedGroupIds = new Set<string>()
295
-
296
- // Process all files
297
- for (const filePath of files) {
298
- await syncLocatorsFromFile(filePath, baseDir, result, processedGroupIds)
299
- }
300
-
301
- // Delete locator groups that don't have files
302
- await deleteOrphanedLocatorGroups(processedGroupIds, baseDir, result)
303
-
304
- return result
305
- }
306
-
307
- /**
308
- * Generates and displays sync summary
309
- */
310
- function generateSummary(result: SyncResult): void {
311
- console.log('\nšŸ“Š Sync Summary:')
312
- console.log(` šŸ“ Locators scanned: ${result.locatorsScanned}`)
313
- console.log(` āœ… Locators existing: ${result.locatorsExisting}`)
314
- console.log(` āž• Locators created: ${result.locatorsCreated}`)
315
- console.log(` šŸ”„ Locators updated: ${result.locatorsUpdated}`)
316
- console.log(` šŸ—‘ļø Locators deleted: ${result.locatorsDeleted}`)
317
- console.log(` šŸ—‘ļø Locator groups deleted: ${result.locatorGroupsDeleted}`)
318
- console.log(` āŒ Errors: ${result.errors.length}`)
319
-
320
- if (result.createdLocators.length > 0) {
321
- console.log('\n Created locators:')
322
- result.createdLocators.forEach((loc, index) => {
323
- console.log(` ${index + 1}. ${loc.name} (group: ${loc.group})`)
324
- })
325
- }
326
-
327
- if (result.updatedLocators.length > 0) {
328
- console.log('\n Updated locators:')
329
- result.updatedLocators.forEach((loc, index) => {
330
- console.log(` ${index + 1}. ${loc.name} (group: ${loc.group})`)
331
- })
332
- }
333
-
334
- if (result.deletedLocators.length > 0) {
335
- console.log('\n Deleted locators:')
336
- result.deletedLocators.forEach((loc, index) => {
337
- console.log(` ${index + 1}. ${loc.name} (group: ${loc.group})`)
338
- })
339
- }
340
-
341
- if (result.deletedLocatorGroups.length > 0) {
342
- console.log('\n Deleted locator groups:')
343
- result.deletedLocatorGroups.forEach((group, index) => {
344
- console.log(` ${index + 1}. ${group.name} (${group.locatorCount} locator(s) deleted)`)
345
- })
346
- }
347
-
348
- if (result.errors.length > 0) {
349
- console.log('\n Errors:')
350
- result.errors.forEach((error, index) => {
351
- console.log(` ${index + 1}. ${error}`)
352
- })
353
- }
354
- }
355
-
356
- /**
357
- * Main function
358
- */
359
- async function main() {
360
- try {
361
- console.log('šŸ”„ Starting locators sync...')
362
- console.log('This will scan locator JSON files and sync locators to database.')
363
- console.log('Filesystem is the source of truth - locators in DB but not in FS will be deleted.\n')
364
-
365
- const baseDir = process.cwd()
366
-
367
- // Scan locator files
368
- console.log('šŸ“ Scanning automation/locators...')
369
- const files = await scanLocatorFiles(baseDir)
370
- console.log(` Found ${files.length} locator file(s)`)
371
-
372
- if (files.length === 0) {
373
- console.log('\nāš ļø No locator files found. Nothing to sync.')
374
- return
375
- }
376
-
377
- // Sync to database
378
- console.log('\nāœ… Syncing locators to database...')
379
- const result = await syncLocatorsToDatabase(files, baseDir)
380
-
381
- // Generate summary
382
- generateSummary(result)
383
-
384
- if (result.errors.length === 0) {
385
- console.log('\nāœ… Sync completed successfully!')
386
- } else {
387
- console.log('\nāš ļø Sync completed with errors. Please review the errors above.')
388
- process.exit(1)
389
- }
390
- } catch (error) {
391
- console.error('\nāŒ Error during sync:', error)
392
- process.exit(1)
393
- } finally {
394
- await prisma.$disconnect()
395
- }
396
- }
397
-
398
- main()
1
+ #!/usr/bin/env tsx
2
+
3
+ /**
4
+ * Script to synchronize locators from filesystem to database
5
+ * Scans locator JSON files to ensure all locators exist in DB
6
+ * Filesystem is the source of truth - locators in DB but not in FS will be deleted
7
+ * Run this after merging changes to ensure locator sync
8
+ *
9
+ * Usage: npx tsx scripts/sync-locators.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 { buildModuleHierarchy } from '../src/lib/module-hierarchy-builder'
17
+ import { getLocatorGroupFilePath } from '../src/lib/locator-group-file-utils'
18
+ import { extractLocatorGroupName, extractModulePathFromLocatorFile } from './lib/filename-utils'
19
+ import { printSyncSummary } from './lib/sync-summary'
20
+ import { runSyncScript } from './lib/sync-script-runner'
21
+
22
+ interface SyncResult {
23
+ locatorsScanned: number
24
+ locatorsExisting: number
25
+ locatorsCreated: number
26
+ locatorsDeleted: number
27
+ locatorsUpdated: number
28
+ locatorGroupsDeleted: number
29
+ errors: string[]
30
+ createdLocators: Array<{ name: string; group: string }>
31
+ deletedLocators: Array<{ name: string; group: string }>
32
+ updatedLocators: Array<{ name: string; group: string }>
33
+ deletedLocatorGroups: Array<{ name: string; locatorCount: number }>
34
+ }
35
+
36
+ /**
37
+ * Scans locator directory for all JSON files
38
+ */
39
+ async function scanLocatorFiles(baseDir: string): Promise<string[]> {
40
+ const pattern = 'automation/locators/**/*.json'
41
+ try {
42
+ const files = await glob(pattern, {
43
+ cwd: baseDir,
44
+ })
45
+ return files.map(file => join(baseDir, file))
46
+ } catch (error) {
47
+ throw new Error(`Error scanning locator files: ${error}`)
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Reads and parses a locator JSON file
53
+ */
54
+ async function readLocatorFile(filePath: string): Promise<Record<string, string>> {
55
+ try {
56
+ await fs.access(filePath)
57
+ } catch {
58
+ throw new Error(`Locator file not found at ${filePath}`)
59
+ }
60
+
61
+ try {
62
+ const fileContent = await fs.readFile(filePath, 'utf-8')
63
+ const jsonContent = JSON.parse(fileContent) as Record<string, string>
64
+
65
+ if (!jsonContent || typeof jsonContent !== 'object') {
66
+ throw new Error('Invalid JSON structure: expected an object')
67
+ }
68
+
69
+ return jsonContent
70
+ } catch (error) {
71
+ if (error instanceof SyntaxError) {
72
+ throw new Error(`Invalid JSON in locator file: ${error.message}`)
73
+ }
74
+ throw error
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Finds or creates a LocatorGroup
80
+ */
81
+ async function findOrCreateLocatorGroup(
82
+ groupName: string,
83
+ moduleId: string,
84
+ ): Promise<string> {
85
+ // Try to find existing locator group
86
+ const existingGroup = await prisma.locatorGroup.findFirst({
87
+ where: {
88
+ name: groupName,
89
+ moduleId: moduleId,
90
+ },
91
+ })
92
+
93
+ if (existingGroup) {
94
+ return existingGroup.id
95
+ }
96
+
97
+ // Create new locator group
98
+ const newGroup = await prisma.locatorGroup.create({
99
+ data: {
100
+ name: groupName,
101
+ route: `/${groupName}`,
102
+ moduleId: moduleId,
103
+ },
104
+ })
105
+
106
+ return newGroup.id
107
+ }
108
+
109
+ /**
110
+ * Syncs locators from a single file to database
111
+ */
112
+ async function syncLocatorsFromFile(
113
+ filePath: string,
114
+ baseDir: string,
115
+ result: SyncResult,
116
+ processedGroupIds: Set<string>,
117
+ ): Promise<void> {
118
+ try {
119
+ // Extract module path and group name
120
+ const modulePath = extractModulePathFromLocatorFile(filePath, baseDir)
121
+ const groupName = extractLocatorGroupName(filePath)
122
+
123
+ console.log(`\n šŸ“„ Processing file: ${filePath.replace(baseDir, '')}`)
124
+ console.log(` Module path: ${modulePath}`)
125
+ console.log(` Group name: ${groupName}`)
126
+
127
+ // Build module hierarchy
128
+ const moduleId = await buildModuleHierarchy(modulePath)
129
+
130
+ // Find or create locator group
131
+ const locatorGroupId = await findOrCreateLocatorGroup(groupName, moduleId)
132
+
133
+ // Track this group as processed (has a file)
134
+ processedGroupIds.add(locatorGroupId)
135
+
136
+ // Read locators from file
137
+ const fileLocators = await readLocatorFile(filePath)
138
+ const fileLocatorNames = Object.keys(fileLocators)
139
+ result.locatorsScanned += fileLocatorNames.length
140
+
141
+ console.log(` Found ${fileLocatorNames.length} locator(s) in file`)
142
+
143
+ // Get existing locators from database for this group
144
+ const dbLocators = await prisma.locator.findMany({
145
+ where: { locatorGroupId: locatorGroupId },
146
+ select: { id: true, name: true, value: true },
147
+ })
148
+
149
+ const dbLocatorMap = new Map(dbLocators.map(loc => [loc.name, loc]))
150
+
151
+ // Add or update locators from file
152
+ for (const [locatorName, locatorValue] of Object.entries(fileLocators)) {
153
+ const existingLocator = dbLocatorMap.get(locatorName)
154
+
155
+ if (existingLocator) {
156
+ // Check if value changed
157
+ if (existingLocator.value !== locatorValue) {
158
+ // Update locator value (file takes precedence)
159
+ await prisma.locator.update({
160
+ where: { id: existingLocator.id },
161
+ data: { value: locatorValue },
162
+ })
163
+ result.locatorsUpdated++
164
+ result.updatedLocators.push({ name: locatorName, group: groupName })
165
+ console.log(` šŸ”„ Updated locator '${locatorName}'`)
166
+ } else {
167
+ result.locatorsExisting++
168
+ }
169
+ } else {
170
+ // Create new locator
171
+ await prisma.locator.create({
172
+ data: {
173
+ name: locatorName,
174
+ value: locatorValue,
175
+ locatorGroupId: locatorGroupId,
176
+ },
177
+ })
178
+ result.locatorsCreated++
179
+ result.createdLocators.push({ name: locatorName, group: groupName })
180
+ console.log(` āž• Created locator '${locatorName}'`)
181
+ }
182
+ }
183
+
184
+ // Delete locators that exist in DB but not in file (FS is source of truth)
185
+ for (const dbLocator of dbLocators) {
186
+ if (!fileLocatorNames.includes(dbLocator.name)) {
187
+ await prisma.locator.delete({
188
+ where: { id: dbLocator.id },
189
+ })
190
+ result.locatorsDeleted++
191
+ result.deletedLocators.push({ name: dbLocator.name, group: groupName })
192
+ console.log(` šŸ—‘ļø Deleted locator '${dbLocator.name}' (not in file)`)
193
+ }
194
+ }
195
+ } catch (error) {
196
+ const errorMsg = `Error syncing locator file ${filePath}: ${error}`
197
+ result.errors.push(errorMsg)
198
+ console.error(` āŒ ${errorMsg}`)
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Deletes locator groups that don't have corresponding files
204
+ */
205
+ async function deleteOrphanedLocatorGroups(
206
+ processedGroupIds: Set<string>,
207
+ baseDir: string,
208
+ result: SyncResult,
209
+ ): Promise<void> {
210
+ console.log('\nšŸ” Checking for orphaned locator groups (no file in filesystem)...')
211
+
212
+ try {
213
+ // Get all locator groups from database
214
+ const allLocatorGroups = await prisma.locatorGroup.findMany({
215
+ include: {
216
+ locators: {
217
+ select: { id: true },
218
+ },
219
+ },
220
+ })
221
+
222
+ for (const group of allLocatorGroups) {
223
+ // Skip if this group was processed (has a file)
224
+ if (processedGroupIds.has(group.id)) {
225
+ continue
226
+ }
227
+
228
+ // Check if file exists for this group
229
+ const relativeFilePath = await getLocatorGroupFilePath(group.id)
230
+ if (!relativeFilePath) {
231
+ // Can't determine file path, skip
232
+ continue
233
+ }
234
+
235
+ const fullPath = join(baseDir, relativeFilePath)
236
+
237
+ try {
238
+ // Check if file exists
239
+ await fs.access(fullPath)
240
+ // File exists, so this group is valid
241
+ } catch {
242
+ // File doesn't exist - delete the locator group (cascade will delete locators)
243
+ const locatorCount = group.locators.length
244
+ await prisma.locatorGroup.delete({
245
+ where: { id: group.id },
246
+ })
247
+ result.locatorGroupsDeleted++
248
+ result.deletedLocatorGroups.push({ name: group.name, locatorCount })
249
+ console.log(` šŸ—‘ļø Deleted locator group '${group.name}' (${locatorCount} locator(s) deleted)`)
250
+ }
251
+ }
252
+ } catch (error) {
253
+ const errorMsg = `Error deleting orphaned locator groups: ${error}`
254
+ result.errors.push(errorMsg)
255
+ console.error(` āŒ ${errorMsg}`)
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Syncs all locators to database
261
+ */
262
+ async function syncLocatorsToDatabase(files: string[], baseDir: string): Promise<SyncResult> {
263
+ const result: SyncResult = {
264
+ locatorsScanned: 0,
265
+ locatorsExisting: 0,
266
+ locatorsCreated: 0,
267
+ locatorsDeleted: 0,
268
+ locatorsUpdated: 0,
269
+ locatorGroupsDeleted: 0,
270
+ errors: [],
271
+ createdLocators: [],
272
+ deletedLocators: [],
273
+ updatedLocators: [],
274
+ deletedLocatorGroups: [],
275
+ }
276
+
277
+ // Track which locator groups have files
278
+ const processedGroupIds = new Set<string>()
279
+
280
+ // Process all files
281
+ for (const filePath of files) {
282
+ await syncLocatorsFromFile(filePath, baseDir, result, processedGroupIds)
283
+ }
284
+
285
+ // Delete locator groups that don't have files
286
+ await deleteOrphanedLocatorGroups(processedGroupIds, baseDir, result)
287
+
288
+ return result
289
+ }
290
+
291
+ /**
292
+ * Generates and displays sync summary
293
+ */
294
+ async function main(): Promise<SyncResult | void> {
295
+ console.log('šŸ”„ Starting locators sync...')
296
+ console.log('This will scan locator JSON files and sync locators to database.')
297
+ console.log('Filesystem is the source of truth - locators in DB but not in FS will be deleted.\n')
298
+
299
+ const baseDir = process.cwd()
300
+
301
+ // Scan locator files
302
+ console.log('šŸ“ Scanning automation/locators...')
303
+ const files = await scanLocatorFiles(baseDir)
304
+ console.log(` Found ${files.length} locator file(s)`)
305
+
306
+ if (files.length === 0) {
307
+ console.log('\nāš ļø No locator files found. Nothing to sync.')
308
+ return
309
+ }
310
+
311
+ // Sync to database
312
+ console.log('\nāœ… Syncing locators to database...')
313
+ const result = await syncLocatorsToDatabase(files, baseDir)
314
+
315
+ printSyncSummary(
316
+ [
317
+ { label: 'šŸ“ Locators scanned', value: result.locatorsScanned },
318
+ { label: 'āœ… Locators existing', value: result.locatorsExisting },
319
+ { label: 'āž• Locators created', value: result.locatorsCreated },
320
+ { label: 'šŸ”„ Locators updated', value: result.locatorsUpdated },
321
+ { label: 'šŸ—‘ļø Locators deleted', value: result.locatorsDeleted },
322
+ { label: 'šŸ—‘ļø Locator groups deleted', value: result.locatorGroupsDeleted },
323
+ { label: 'āŒ Errors', value: result.errors.length },
324
+ ],
325
+ [
326
+ {
327
+ title: 'Created locators',
328
+ items: result.createdLocators.map(loc => `${loc.name} (group: ${loc.group})`),
329
+ },
330
+ {
331
+ title: 'Updated locators',
332
+ items: result.updatedLocators.map(loc => `${loc.name} (group: ${loc.group})`),
333
+ },
334
+ {
335
+ title: 'Deleted locators',
336
+ items: result.deletedLocators.map(loc => `${loc.name} (group: ${loc.group})`),
337
+ },
338
+ {
339
+ title: 'Deleted locator groups',
340
+ items: result.deletedLocatorGroups.map(group => `${group.name} (${group.locatorCount} locator(s) deleted)`),
341
+ },
342
+ { title: 'Errors', items: result.errors },
343
+ ],
344
+ )
345
+ return result
346
+ }
347
+
348
+ runSyncScript(main)