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,316 +1,352 @@
1
- import { readFile } from 'fs/promises'
2
- import { StepStatus, StepKeyword } from '@prisma/client'
3
-
4
- /**
5
- * Parsed report structure matching cucumber.json format
6
- */
7
- export interface ParsedReport {
8
- features: Array<{
9
- name: string
10
- description: string
11
- uri: string
12
- line: number
13
- keyword: string
14
- tags: Array<{ name: string; line: number }>
15
- scenarios: Array<{
16
- name: string
17
- description: string
18
- line: number
19
- keyword: string
20
- type: string
21
- cucumberId: string
22
- tags: Array<{ name: string; line: number }>
23
- steps: Array<{
24
- keyword: string
25
- line: number | null
26
- name: string
27
- matchLocation?: string
28
- status: string
29
- duration: number
30
- errorMessage?: string
31
- errorTrace?: string
32
- hidden: boolean
33
- order: number
34
- }>
35
- hooks: Array<{
36
- keyword: string
37
- status: string
38
- duration: number
39
- errorMessage?: string
40
- errorTrace?: string
41
- hidden: boolean
42
- }>
43
- }>
44
- }>
45
- }
46
-
47
- /**
48
- * Cucumber JSON report structure (as received from cucumber-js)
49
- */
50
- interface CucumberJsonFeature {
51
- description: string
52
- elements: Array<{
53
- description: string
54
- id: string
55
- keyword: string
56
- line: number
57
- name: string
58
- steps: Array<{
59
- keyword: string
60
- line?: number
61
- name?: string
62
- hidden?: boolean
63
- match?: {
64
- location: string
65
- }
66
- result?: {
67
- status: string
68
- duration?: number
69
- error_message?: string
70
- }
71
- }>
72
- tags: Array<{
73
- name: string
74
- line: number
75
- }>
76
- type: string
77
- }>
78
- id: string
79
- line: number
80
- keyword: string
81
- name: string
82
- tags: Array<{
83
- name: string
84
- line: number
85
- }>
86
- uri: string
87
- }
88
-
89
- /**
90
- * Maps cucumber step status to Prisma StepStatus enum
91
- */
92
- function mapStepStatus(status: string): StepStatus {
93
- const upperStatus = status.toUpperCase()
94
- switch (upperStatus) {
95
- case 'PASSED':
96
- return StepStatus.PASSED
97
- case 'FAILED':
98
- return StepStatus.FAILED
99
- case 'SKIPPED':
100
- return StepStatus.SKIPPED
101
- case 'PENDING':
102
- return StepStatus.PENDING
103
- case 'UNDEFINED':
104
- return StepStatus.UNDEFINED
105
- default:
106
- return StepStatus.PENDING
107
- }
108
- }
109
-
110
- /**
111
- * Maps cucumber step keyword to Prisma StepKeyword enum
112
- */
113
- function mapStepKeyword(keyword: string): StepKeyword {
114
- const trimmedKeyword = keyword.trim()
115
- const upperKeyword = trimmedKeyword.toUpperCase()
116
- switch (upperKeyword) {
117
- case 'GIVEN':
118
- return StepKeyword.GIVEN
119
- case 'WHEN':
120
- return StepKeyword.WHEN
121
- case 'THEN':
122
- return StepKeyword.THEN
123
- case 'AND':
124
- return StepKeyword.AND
125
- case 'BUT':
126
- return StepKeyword.BUT
127
- case 'BEFORE':
128
- return StepKeyword.BEFORE
129
- case 'AFTER':
130
- return StepKeyword.AFTER
131
- default:
132
- // Default to GIVEN for unknown keywords
133
- return StepKeyword.GIVEN
134
- }
135
- }
136
-
137
- /**
138
- * Separates error message from stack trace
139
- * Cucumber's error_message typically contains both the error message and stack trace
140
- * The stack trace usually starts with "at" or contains file paths
141
- *
142
- * @param errorMessage - The full error message from Cucumber
143
- * @returns Object with separated message and trace
144
- */
145
- function separateErrorMessageAndTrace(errorMessage: string | undefined): {
146
- message: string | undefined
147
- trace: string | undefined
148
- } {
149
- if (!errorMessage) {
150
- return { message: undefined, trace: undefined }
151
- }
152
-
153
- // Look for common stack trace indicators
154
- // Stack traces typically start with "at", " at", or contain file paths with line numbers
155
- const stackTracePatterns = [
156
- /\n\s*at\s/, // "at" at the start of a line (with optional whitespace)
157
- /\n\s+at\s/, // "at" with leading whitespace
158
- /Error:\s*$/m, // Error: at end of line (message ends, trace starts)
159
- ]
160
-
161
- // Find the first occurrence of a stack trace pattern
162
- let splitIndex = -1
163
- for (const pattern of stackTracePatterns) {
164
- const match = errorMessage.match(pattern)
165
- if (match && match.index !== undefined) {
166
- // If we find "at" pattern, split before it (include the newline before "at")
167
- // If we find "Error:" pattern, split after it
168
- if (pattern.source.includes('Error:')) {
169
- splitIndex = match.index + match[0].length
170
- } else {
171
- splitIndex = match.index
172
- }
173
- break
174
- }
175
- }
176
-
177
- // If no stack trace pattern found, check if it looks like just a message (short, no file paths)
178
- if (splitIndex === -1) {
179
- // Check if it contains file paths (common in stack traces)
180
- const hasFilePath = /[\/\\][\w\-\.]+:\d+/.test(errorMessage)
181
- if (hasFilePath && errorMessage.length > 200) {
182
- // Likely contains a trace, try to find where message ends
183
- // Usually the first few lines are the message, rest is trace
184
- const lines = errorMessage.split('\n')
185
- // Look for the first line that starts with whitespace and "at" or contains a file path
186
- for (let i = 0; i < lines.length; i++) {
187
- if (/^\s*at\s/.test(lines[i]) || /[\/\\][\w\-\.]+:\d+/.test(lines[i])) {
188
- splitIndex = errorMessage.indexOf(lines[i])
189
- break
190
- }
191
- }
192
- }
193
- }
194
-
195
- if (splitIndex > 0) {
196
- const message = errorMessage.substring(0, splitIndex).trim()
197
- const trace = errorMessage.substring(splitIndex).trim()
198
- return {
199
- message: message || undefined,
200
- trace: trace || undefined,
201
- }
202
- }
203
-
204
- // If we can't separate them, treat the whole thing as the message
205
- // and leave trace as undefined (better than duplicating)
206
- return {
207
- message: errorMessage.trim() || undefined,
208
- trace: undefined,
209
- }
210
- }
211
-
212
- /**
213
- * Parses a cucumber.json report file into a structured ParsedReport object
214
- *
215
- * @param reportPath - Path to the cucumber.json file
216
- * @returns Parsed report structure
217
- * @throws Error if file cannot be read or parsed
218
- */
219
- export async function parseCucumberReport(reportPath: string): Promise<ParsedReport> {
220
- try {
221
- // Read the report file
222
- const fileContent = await readFile(reportPath, 'utf-8')
223
- const cucumberData: CucumberJsonFeature[] = JSON.parse(fileContent)
224
-
225
- // Parse features
226
- const features = cucumberData.map(feature => {
227
- // Separate steps into regular steps and hooks
228
- const scenarios = feature.elements.map(element => {
229
- const steps: ParsedReport['features'][0]['scenarios'][0]['steps'] = []
230
- const hooks: ParsedReport['features'][0]['scenarios'][0]['hooks'] = []
231
-
232
- element.steps.forEach((step, index) => {
233
- const isHook = step.keyword === 'Before' || step.keyword === 'After'
234
- const stepStatus = step.result?.status || 'pending'
235
- const duration = step.result?.duration || 0
236
-
237
- // Separate error message from stack trace
238
- const { message: errorMessage, trace: errorTrace } = separateErrorMessageAndTrace(step.result?.error_message)
239
-
240
- if (isHook) {
241
- hooks.push({
242
- keyword: step.keyword,
243
- status: stepStatus,
244
- duration,
245
- errorMessage,
246
- errorTrace,
247
- hidden: step.hidden || false,
248
- })
249
- } else {
250
- steps.push({
251
- keyword: step.keyword.trim(),
252
- line: step.line || null,
253
- name: step.name || '',
254
- matchLocation: step.match?.location,
255
- status: stepStatus,
256
- duration,
257
- errorMessage,
258
- errorTrace,
259
- hidden: step.hidden || false,
260
- order: index,
261
- })
262
- }
263
- })
264
-
265
- return {
266
- name: element.name,
267
- description: element.description || '',
268
- line: element.line,
269
- keyword: element.keyword,
270
- type: element.type,
271
- cucumberId: element.id,
272
- tags: element.tags.map(tag => ({
273
- name: tag.name,
274
- line: tag.line,
275
- })),
276
- steps,
277
- hooks,
278
- }
279
- })
280
-
281
- return {
282
- name: feature.name,
283
- description: feature.description || '',
284
- uri: feature.uri,
285
- line: feature.line,
286
- keyword: feature.keyword,
287
- tags: feature.tags.map(tag => ({
288
- name: tag.name,
289
- line: tag.line,
290
- })),
291
- scenarios,
292
- }
293
- })
294
-
295
- return { features }
296
- } catch (error) {
297
- if (error instanceof Error) {
298
- throw new Error(`Failed to parse cucumber report at ${reportPath}: ${error.message}`)
299
- }
300
- throw new Error(`Failed to parse cucumber report at ${reportPath}: Unknown error`)
301
- }
302
- }
303
-
304
- /**
305
- * Helper function to convert parsed step status to Prisma enum
306
- */
307
- export function getStepStatusEnum(status: string): StepStatus {
308
- return mapStepStatus(status)
309
- }
310
-
311
- /**
312
- * Helper function to convert parsed step keyword to Prisma enum
313
- */
314
- export function getStepKeywordEnum(keyword: string): StepKeyword {
315
- return mapStepKeyword(keyword)
316
- }
1
+ import { readFile } from 'fs/promises'
2
+ import { StepStatus, StepKeyword } from '@prisma/client'
3
+
4
+ const SCREENSHOT_ATTACHMENT_MEDIA_TYPE = 'application/vnd.appraisejs.report-step-screenshot+json'
5
+
6
+ /**
7
+ * Parsed report structure matching cucumber.json format
8
+ */
9
+ export interface ParsedReport {
10
+ features: Array<{
11
+ name: string
12
+ description: string
13
+ uri: string
14
+ line: number
15
+ keyword: string
16
+ tags: Array<{ name: string; line: number }>
17
+ scenarios: Array<{
18
+ name: string
19
+ description: string
20
+ line: number
21
+ keyword: string
22
+ type: string
23
+ cucumberId: string
24
+ tags: Array<{ name: string; line: number }>
25
+ steps: Array<{
26
+ keyword: string
27
+ line: number | null
28
+ name: string
29
+ matchLocation?: string
30
+ status: string
31
+ duration: number
32
+ errorMessage?: string
33
+ errorTrace?: string
34
+ screenshotPath?: string
35
+ hidden: boolean
36
+ order: number
37
+ }>
38
+ hooks: Array<{
39
+ keyword: string
40
+ status: string
41
+ duration: number
42
+ errorMessage?: string
43
+ errorTrace?: string
44
+ hidden: boolean
45
+ }>
46
+ }>
47
+ }>
48
+ }
49
+
50
+ /**
51
+ * Cucumber JSON report structure (as received from cucumber-js)
52
+ */
53
+ interface CucumberJsonFeature {
54
+ description: string
55
+ elements: Array<{
56
+ description: string
57
+ id: string
58
+ keyword: string
59
+ line: number
60
+ name: string
61
+ steps: Array<{
62
+ keyword: string
63
+ line?: number
64
+ name?: string
65
+ hidden?: boolean
66
+ match?: {
67
+ location: string
68
+ }
69
+ result?: {
70
+ status: string
71
+ duration?: number
72
+ error_message?: string
73
+ }
74
+ embeddings?: Array<{
75
+ data?: string
76
+ mime_type?: string
77
+ }>
78
+ }>
79
+ tags: Array<{
80
+ name: string
81
+ line: number
82
+ }>
83
+ type: string
84
+ }>
85
+ id: string
86
+ line: number
87
+ keyword: string
88
+ name: string
89
+ tags: Array<{
90
+ name: string
91
+ line: number
92
+ }>
93
+ uri: string
94
+ }
95
+
96
+ function extractScreenshotPath(
97
+ embeddings: Array<{ data?: string; mime_type?: string }> | undefined,
98
+ ): string | undefined {
99
+ const screenshotAttachment = embeddings?.find(embedding => embedding.mime_type === SCREENSHOT_ATTACHMENT_MEDIA_TYPE)
100
+
101
+ if (!screenshotAttachment?.data) {
102
+ return undefined
103
+ }
104
+
105
+ const parsePayload = (payload: string): string | undefined => {
106
+ try {
107
+ const parsedPayload = JSON.parse(payload) as { screenshotPath?: unknown }
108
+ return typeof parsedPayload.screenshotPath === 'string' ? parsedPayload.screenshotPath : undefined
109
+ } catch {
110
+ return undefined
111
+ }
112
+ }
113
+
114
+ const directPayload = parsePayload(screenshotAttachment.data)
115
+ if (directPayload) {
116
+ return directPayload
117
+ }
118
+
119
+ const decodedPayload = Buffer.from(screenshotAttachment.data, 'base64').toString('utf8')
120
+ return parsePayload(decodedPayload)
121
+ }
122
+
123
+ /**
124
+ * Maps cucumber step status to Prisma StepStatus enum
125
+ */
126
+ function mapStepStatus(status: string): StepStatus {
127
+ const upperStatus = status.toUpperCase()
128
+ switch (upperStatus) {
129
+ case 'PASSED':
130
+ return StepStatus.PASSED
131
+ case 'FAILED':
132
+ return StepStatus.FAILED
133
+ case 'SKIPPED':
134
+ return StepStatus.SKIPPED
135
+ case 'PENDING':
136
+ return StepStatus.PENDING
137
+ case 'UNDEFINED':
138
+ return StepStatus.UNDEFINED
139
+ default:
140
+ return StepStatus.PENDING
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Maps cucumber step keyword to Prisma StepKeyword enum
146
+ */
147
+ function mapStepKeyword(keyword: string): StepKeyword {
148
+ const trimmedKeyword = keyword.trim()
149
+ const upperKeyword = trimmedKeyword.toUpperCase()
150
+ switch (upperKeyword) {
151
+ case 'GIVEN':
152
+ return StepKeyword.GIVEN
153
+ case 'WHEN':
154
+ return StepKeyword.WHEN
155
+ case 'THEN':
156
+ return StepKeyword.THEN
157
+ case 'AND':
158
+ return StepKeyword.AND
159
+ case 'BUT':
160
+ return StepKeyword.BUT
161
+ case 'BEFORE':
162
+ return StepKeyword.BEFORE
163
+ case 'AFTER':
164
+ return StepKeyword.AFTER
165
+ default:
166
+ // Default to GIVEN for unknown keywords
167
+ return StepKeyword.GIVEN
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Separates error message from stack trace
173
+ * Cucumber's error_message typically contains both the error message and stack trace
174
+ * The stack trace usually starts with "at" or contains file paths
175
+ *
176
+ * @param errorMessage - The full error message from Cucumber
177
+ * @returns Object with separated message and trace
178
+ */
179
+ function separateErrorMessageAndTrace(errorMessage: string | undefined): {
180
+ message: string | undefined
181
+ trace: string | undefined
182
+ } {
183
+ if (!errorMessage) {
184
+ return { message: undefined, trace: undefined }
185
+ }
186
+
187
+ // Look for common stack trace indicators
188
+ // Stack traces typically start with "at", " at", or contain file paths with line numbers
189
+ const stackTracePatterns = [
190
+ /\n\s*at\s/, // "at" at the start of a line (with optional whitespace)
191
+ /\n\s+at\s/, // "at" with leading whitespace
192
+ /Error:\s*$/m, // Error: at end of line (message ends, trace starts)
193
+ ]
194
+
195
+ // Find the first occurrence of a stack trace pattern
196
+ let splitIndex = -1
197
+ for (const pattern of stackTracePatterns) {
198
+ const match = errorMessage.match(pattern)
199
+ if (match && match.index !== undefined) {
200
+ // If we find "at" pattern, split before it (include the newline before "at")
201
+ // If we find "Error:" pattern, split after it
202
+ if (pattern.source.includes('Error:')) {
203
+ splitIndex = match.index + match[0].length
204
+ } else {
205
+ splitIndex = match.index
206
+ }
207
+ break
208
+ }
209
+ }
210
+
211
+ // If no stack trace pattern found, check if it looks like just a message (short, no file paths)
212
+ if (splitIndex === -1) {
213
+ // Check if it contains file paths (common in stack traces)
214
+ const hasFilePath = /[\/\\][\w\-\.]+:\d+/.test(errorMessage)
215
+ if (hasFilePath && errorMessage.length > 200) {
216
+ // Likely contains a trace, try to find where message ends
217
+ // Usually the first few lines are the message, rest is trace
218
+ const lines = errorMessage.split('\n')
219
+ // Look for the first line that starts with whitespace and "at" or contains a file path
220
+ for (let i = 0; i < lines.length; i++) {
221
+ if (/^\s*at\s/.test(lines[i]) || /[\/\\][\w\-\.]+:\d+/.test(lines[i])) {
222
+ splitIndex = errorMessage.indexOf(lines[i])
223
+ break
224
+ }
225
+ }
226
+ }
227
+ }
228
+
229
+ if (splitIndex > 0) {
230
+ const message = errorMessage.substring(0, splitIndex).trim()
231
+ const trace = errorMessage.substring(splitIndex).trim()
232
+ return {
233
+ message: message || undefined,
234
+ trace: trace || undefined,
235
+ }
236
+ }
237
+
238
+ // If we can't separate them, treat the whole thing as the message
239
+ // and leave trace as undefined (better than duplicating)
240
+ return {
241
+ message: errorMessage.trim() || undefined,
242
+ trace: undefined,
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Parses a cucumber.json report file into a structured ParsedReport object
248
+ *
249
+ * @param reportPath - Path to the cucumber.json file
250
+ * @returns Parsed report structure
251
+ * @throws Error if file cannot be read or parsed
252
+ */
253
+ export async function parseCucumberReport(reportPath: string): Promise<ParsedReport> {
254
+ try {
255
+ // Read the report file
256
+ const fileContent = await readFile(reportPath, 'utf-8')
257
+ const cucumberData: CucumberJsonFeature[] = JSON.parse(fileContent)
258
+
259
+ // Parse features
260
+ const features = cucumberData.map(feature => {
261
+ // Separate steps into regular steps and hooks
262
+ const scenarios = feature.elements.map(element => {
263
+ const steps: ParsedReport['features'][0]['scenarios'][0]['steps'] = []
264
+ const hooks: ParsedReport['features'][0]['scenarios'][0]['hooks'] = []
265
+
266
+ element.steps.forEach((step, index) => {
267
+ const isHook = step.keyword === 'Before' || step.keyword === 'After'
268
+ const stepStatus = step.result?.status || 'pending'
269
+ const duration = step.result?.duration || 0
270
+
271
+ // Separate error message from stack trace
272
+ const { message: errorMessage, trace: errorTrace } = separateErrorMessageAndTrace(step.result?.error_message)
273
+ const screenshotPath = extractScreenshotPath(step.embeddings)
274
+
275
+ if (isHook) {
276
+ hooks.push({
277
+ keyword: step.keyword,
278
+ status: stepStatus,
279
+ duration,
280
+ errorMessage,
281
+ errorTrace,
282
+ hidden: step.hidden || false,
283
+ })
284
+ } else {
285
+ steps.push({
286
+ keyword: step.keyword.trim(),
287
+ line: step.line || null,
288
+ name: step.name || '',
289
+ matchLocation: step.match?.location,
290
+ status: stepStatus,
291
+ duration,
292
+ errorMessage,
293
+ errorTrace,
294
+ screenshotPath,
295
+ hidden: step.hidden || false,
296
+ order: index,
297
+ })
298
+ }
299
+ })
300
+
301
+ return {
302
+ name: element.name,
303
+ description: element.description || '',
304
+ line: element.line,
305
+ keyword: element.keyword,
306
+ type: element.type,
307
+ cucumberId: element.id,
308
+ tags: element.tags.map(tag => ({
309
+ name: tag.name,
310
+ line: tag.line,
311
+ })),
312
+ steps,
313
+ hooks,
314
+ }
315
+ })
316
+
317
+ return {
318
+ name: feature.name,
319
+ description: feature.description || '',
320
+ uri: feature.uri,
321
+ line: feature.line,
322
+ keyword: feature.keyword,
323
+ tags: feature.tags.map(tag => ({
324
+ name: tag.name,
325
+ line: tag.line,
326
+ })),
327
+ scenarios,
328
+ }
329
+ })
330
+
331
+ return { features }
332
+ } catch (error) {
333
+ if (error instanceof Error) {
334
+ throw new Error(`Failed to parse cucumber report at ${reportPath}: ${error.message}`)
335
+ }
336
+ throw new Error(`Failed to parse cucumber report at ${reportPath}: Unknown error`)
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Helper function to convert parsed step status to Prisma enum
342
+ */
343
+ export function getStepStatusEnum(status: string): StepStatus {
344
+ return mapStepStatus(status)
345
+ }
346
+
347
+ /**
348
+ * Helper function to convert parsed step keyword to Prisma enum
349
+ */
350
+ export function getStepKeywordEnum(keyword: string): StepKeyword {
351
+ return mapStepKeyword(keyword)
352
+ }