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,87 +1,87 @@
1
- 'use client'
2
-
3
- import { Button } from '@/components/ui/button'
4
- import { Card, CardContent, CardTitle, CardHeader } from '@/components/ui/card'
5
- import { ExternalLink } from 'lucide-react'
6
- import { useRouter } from 'next/navigation'
7
- import { useEffect, useState, useRef } from 'react'
8
- import { getRunningTestRunsCountAction } from '@/actions/dashboard/dashboard-actions'
9
-
10
- interface OngoingTestRunsCardProps {
11
- initialCount: number
12
- link: string
13
- }
14
-
15
- export default function OngoingTestRunsCard({ initialCount, link }: OngoingTestRunsCardProps) {
16
- const router = useRouter()
17
- const [count, setCount] = useState(initialCount)
18
- const pollingRef = useRef<NodeJS.Timeout | null>(null)
19
-
20
- // Poll for count updates while there are ongoing test runs
21
- useEffect(() => {
22
- // Clear any existing polling interval
23
- if (pollingRef.current) {
24
- clearInterval(pollingRef.current)
25
- pollingRef.current = null
26
- }
27
-
28
- // If no ongoing test runs, don't poll
29
- if (count === 0) {
30
- return
31
- }
32
-
33
- pollingRef.current = setInterval(async () => {
34
- try {
35
- const { data, error } = await getRunningTestRunsCountAction()
36
- if (error) {
37
- console.error('Error polling running test runs count:', error)
38
- return
39
- }
40
-
41
- const newCount = data as number
42
- setCount(newCount)
43
-
44
- // If count reaches 0, stop polling
45
- if (newCount === 0 && pollingRef.current) {
46
- clearInterval(pollingRef.current)
47
- pollingRef.current = null
48
- }
49
- } catch (error) {
50
- console.error('Error polling running test runs count:', error)
51
- }
52
- }, 2000) // Poll every 2 seconds
53
-
54
- return () => {
55
- if (pollingRef.current) {
56
- clearInterval(pollingRef.current)
57
- pollingRef.current = null
58
- }
59
- }
60
- }, [count])
61
-
62
- return (
63
- <Card className="h-fit border-gray-600/10 bg-gray-600/10 min-w-40">
64
- <CardHeader className="flex items-center justify-between flex-row p-2">
65
- <CardTitle className={`text-xs font-normal ${count > 0 ? 'text-primary' : 'text-gray-400'}`}>
66
- Ongoing Test Runs
67
- </CardTitle>
68
- <div className="flex items-center gap-2">
69
- <Button
70
- variant="outline"
71
- className="text-primary hover:text-primary/80 px-2 py-1 bg-inherit border-gray-600/15 hover:bg-emerald-400/10"
72
- disabled={count === 0}
73
- onClick={() => router.push(link)}
74
- size="sm"
75
- >
76
- <ExternalLink className="h-4 w-4" />
77
- </Button>
78
- </div>
79
- </CardHeader>
80
- <CardContent className="h-full px-2 py-1">
81
- <div className={`text-2xl h-full flex items-center font-bold ${count > 0 ? 'text-primary' : 'text-gray-400'}`}>
82
- {count}
83
- </div>
84
- </CardContent>
85
- </Card>
86
- )
87
- }
1
+ 'use client'
2
+
3
+ import { Button } from '@/components/ui/button'
4
+ import { Card, CardContent, CardTitle, CardHeader } from '@/components/ui/card'
5
+ import { ExternalLink } from 'lucide-react'
6
+ import { useRouter } from 'next/navigation'
7
+ import { useEffect, useState, useRef } from 'react'
8
+ import { getRunningTestRunsCountAction } from '@/actions/dashboard/dashboard-actions'
9
+
10
+ interface OngoingTestRunsCardProps {
11
+ initialCount: number
12
+ link: string
13
+ }
14
+
15
+ export default function OngoingTestRunsCard({ initialCount, link }: OngoingTestRunsCardProps) {
16
+ const router = useRouter()
17
+ const [count, setCount] = useState(initialCount)
18
+ const pollingRef = useRef<NodeJS.Timeout | null>(null)
19
+
20
+ // Poll for count updates while there are ongoing test runs
21
+ useEffect(() => {
22
+ // Clear any existing polling interval
23
+ if (pollingRef.current) {
24
+ clearInterval(pollingRef.current)
25
+ pollingRef.current = null
26
+ }
27
+
28
+ // If no ongoing test runs, don't poll
29
+ if (count === 0) {
30
+ return
31
+ }
32
+
33
+ pollingRef.current = setInterval(async () => {
34
+ try {
35
+ const { data, error } = await getRunningTestRunsCountAction()
36
+ if (error) {
37
+ console.error('Error polling running test runs count:', error)
38
+ return
39
+ }
40
+
41
+ const newCount = data as number
42
+ setCount(newCount)
43
+
44
+ // If count reaches 0, stop polling
45
+ if (newCount === 0 && pollingRef.current) {
46
+ clearInterval(pollingRef.current)
47
+ pollingRef.current = null
48
+ }
49
+ } catch (error) {
50
+ console.error('Error polling running test runs count:', error)
51
+ }
52
+ }, 2000) // Poll every 2 seconds
53
+
54
+ return () => {
55
+ if (pollingRef.current) {
56
+ clearInterval(pollingRef.current)
57
+ pollingRef.current = null
58
+ }
59
+ }
60
+ }, [count])
61
+
62
+ return (
63
+ <Card className="h-fit border-gray-600/10 bg-gray-600/10 min-w-40">
64
+ <CardHeader className="flex items-center justify-between flex-row p-2">
65
+ <CardTitle className={`text-xs font-normal ${count > 0 ? 'text-primary' : 'text-gray-400'}`}>
66
+ Ongoing Test Runs
67
+ </CardTitle>
68
+ <div className="flex items-center gap-2">
69
+ <Button
70
+ variant="outline"
71
+ className="text-primary hover:text-primary/80 px-2 py-1 bg-inherit border-gray-600/15 hover:bg-emerald-400/10"
72
+ disabled={count === 0}
73
+ onClick={() => router.push(link)}
74
+ size="sm"
75
+ >
76
+ <ExternalLink className="h-4 w-4" />
77
+ </Button>
78
+ </div>
79
+ </CardHeader>
80
+ <CardContent className="h-full px-2 py-1">
81
+ <div className={`text-2xl h-full flex items-center font-bold ${count > 0 ? 'text-primary' : 'text-gray-400'}`}>
82
+ {count}
83
+ </div>
84
+ </CardContent>
85
+ </Card>
86
+ )
87
+ }
@@ -1,45 +1,45 @@
1
- 'use client'
2
- import { Button } from "@/components/ui/button";
3
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
4
- import { Blocks, FileCheck, LayoutTemplate, ListChecks, TestTubeDiagonal, TestTubes } from "lucide-react";
5
- import { useRouter } from "next/navigation";
6
-
7
- export default function QuickActionsDrawer() {
8
- const router = useRouter()
9
- return (
10
- <Card id="container" className="w-fit border-gray-600/10 bg-gray-600/10 h-fit">
11
- <CardHeader id="header">
12
- <CardTitle className="text-primary">Quick Actions</CardTitle>
13
- <CardDescription>Quickly create new entities to get started</CardDescription>
14
- </CardHeader>
15
- <CardContent id="content">
16
- <div className="grid grid-cols-5 gap-4">
17
- <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/test-suites/create')}>
18
- <TestTubes />
19
- <span className="text-xs font-medium">Create Suite</span>
20
- </Button>
21
- <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/test-cases/create')}>
22
- <TestTubeDiagonal />
23
- <span className="text-xs font-medium">Create Test</span>
24
- </Button>
25
- <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/template-steps/create')}>
26
- <LayoutTemplate />
27
- <span className="text-xs font-medium">Create Step</span>
28
- </Button>
29
- <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/test-runs/create')}>
30
- <ListChecks />
31
- <span className="text-xs font-medium">Create Run</span>
32
- </Button>
33
- <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/template-test-cases/create')}>
34
- <Blocks />
35
- <span className="text-xs font-medium">Create Template</span>
36
- </Button>
37
- <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/reports')}>
38
- <FileCheck />
39
- <span className="text-xs font-medium">View Reports</span>
40
- </Button>
41
- </div>
42
- </CardContent>
43
- </Card>
44
- )
1
+ 'use client'
2
+ import { Button } from "@/components/ui/button";
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
4
+ import { Blocks, FileCheck, LayoutTemplate, ListChecks, TestTubeDiagonal, TestTubes } from "lucide-react";
5
+ import { useRouter } from "next/navigation";
6
+
7
+ export default function QuickActionsDrawer() {
8
+ const router = useRouter()
9
+ return (
10
+ <Card id="container" className="w-fit border-gray-600/10 bg-gray-600/10 h-fit">
11
+ <CardHeader id="header">
12
+ <CardTitle className="text-primary">Quick Actions</CardTitle>
13
+ <CardDescription>Quickly create new entities to get started</CardDescription>
14
+ </CardHeader>
15
+ <CardContent id="content">
16
+ <div className="grid grid-cols-5 gap-4">
17
+ <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/test-suites/create')}>
18
+ <TestTubes />
19
+ <span className="text-xs font-medium">Create Suite</span>
20
+ </Button>
21
+ <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/test-cases/create')}>
22
+ <TestTubeDiagonal />
23
+ <span className="text-xs font-medium">Create Test</span>
24
+ </Button>
25
+ <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/template-steps/create')}>
26
+ <LayoutTemplate />
27
+ <span className="text-xs font-medium">Create Step</span>
28
+ </Button>
29
+ <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/test-runs/create')}>
30
+ <ListChecks />
31
+ <span className="text-xs font-medium">Create Run</span>
32
+ </Button>
33
+ <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/template-test-cases/create')}>
34
+ <Blocks />
35
+ <span className="text-xs font-medium">Create Template</span>
36
+ </Button>
37
+ <Button variant="outline" className="flex flex-col items-center gap-2 h-fit [&_svg]:!h-6 [&_svg]:!w-6 bg-emerald-500/10 hover:bg-emerald-500/25 text-emerald-500 hover:text-emerald-400 border-none" onClick={() => router.push('/reports')}>
38
+ <FileCheck />
39
+ <span className="text-xs font-medium">View Reports</span>
40
+ </Button>
41
+ </div>
42
+ </CardContent>
43
+ </Card>
44
+ )
45
45
  }
@@ -0,0 +1,52 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { promises as fs, createReadStream } from 'fs'
3
+ import { Readable } from 'stream'
4
+ import prisma from '@/config/db-config'
5
+ import { resolveStoredPath } from '@/lib/automation/paths'
6
+
7
+ export const runtime = 'nodejs'
8
+
9
+ export async function GET(_request: Request, { params }: { params: Promise<{ stepId: string }> }) {
10
+ const { stepId } = await params
11
+
12
+ try {
13
+ const step = await prisma.reportStep.findUnique({
14
+ where: { id: stepId },
15
+ select: {
16
+ screenshotPath: true,
17
+ },
18
+ })
19
+
20
+ if (!step) {
21
+ return NextResponse.json({ error: 'Report step not found' }, { status: 404 })
22
+ }
23
+
24
+ if (!step.screenshotPath) {
25
+ return NextResponse.json({ error: 'Screenshot not available for this step' }, { status: 404 })
26
+ }
27
+
28
+ const screenshotPath = resolveStoredPath(step.screenshotPath)
29
+
30
+ try {
31
+ await fs.access(screenshotPath)
32
+ } catch {
33
+ return NextResponse.json({ error: 'Screenshot file not found' }, { status: 404 })
34
+ }
35
+
36
+ return new NextResponse(Readable.toWeb(createReadStream(screenshotPath)) as ReadableStream, {
37
+ headers: {
38
+ 'Content-Type': 'image/png',
39
+ 'Cache-Control': 'private, max-age=60',
40
+ },
41
+ })
42
+ } catch (error) {
43
+ console.error(`[ReportScreenshot] Failed to serve screenshot for step ${stepId}:`, error)
44
+ return NextResponse.json(
45
+ {
46
+ error: 'Failed to load screenshot',
47
+ details: error instanceof Error ? error.message : 'Unknown error',
48
+ },
49
+ { status: 500 },
50
+ )
51
+ }
52
+ }
@@ -3,31 +3,83 @@ import prisma from '@/config/db-config'
3
3
  import archiver from 'archiver'
4
4
  import { promises as fs } from 'fs'
5
5
  import path from 'path'
6
+ import { getAutomationReportRunDir, resolveStoredPath } from '@/lib/automation/paths'
6
7
 
7
8
  // Ensure this route runs in Node.js runtime (not Edge) for file system access
8
9
  export const runtime = 'nodejs'
9
10
 
11
+ async function collectRunArtifactFiles(
12
+ dir: string,
13
+ baseDir = dir,
14
+ ): Promise<Array<{ absolutePath: string; archivePath: string }>> {
15
+ try {
16
+ const entries = await fs.readdir(dir, { withFileTypes: true })
17
+ const files: Array<{ absolutePath: string; archivePath: string }> = []
18
+
19
+ for (const entry of entries) {
20
+ const absolutePath = path.join(dir, entry.name)
21
+ if (entry.isDirectory()) {
22
+ files.push(...(await collectRunArtifactFiles(absolutePath, baseDir)))
23
+ continue
24
+ }
25
+
26
+ files.push({
27
+ absolutePath,
28
+ archivePath: path.relative(baseDir, absolutePath).replace(/\\/g, '/'),
29
+ })
30
+ }
31
+
32
+ return files
33
+ } catch {
34
+ return []
35
+ }
36
+ }
37
+
38
+ async function addStoredArtifactFile(
39
+ archive: ReturnType<typeof archiver>,
40
+ storedPath: string,
41
+ archivePath: string,
42
+ warningLabel: string,
43
+ ): Promise<boolean> {
44
+ try {
45
+ const resolvedPath = resolveStoredPath(storedPath)
46
+ await fs.access(resolvedPath)
47
+ archive.file(resolvedPath, { name: archivePath })
48
+ return true
49
+ } catch {
50
+ console.warn(`[Download] ${warningLabel} not found at path: ${storedPath}`)
51
+ return false
52
+ }
53
+ }
54
+
55
+ function isPathWithinDirectory(targetPath: string, directoryPath: string): boolean {
56
+ const relativePath = path.relative(directoryPath, targetPath)
57
+ return relativePath !== '' && !relativePath.startsWith('..') && !path.isAbsolute(relativePath)
58
+ }
59
+
10
60
  /**
11
- * GET handler for downloading test run logs and traces as a zip file
61
+ * GET handler for downloading a test run's stored artifacts as a zip file
12
62
  *
13
63
  * This endpoint:
14
64
  * - Verifies the test run exists
15
- * - Collects the log file (if exists)
16
- * - Collects all trace files from test cases (if any)
65
+ * - Collects the run folder artifacts when present
66
+ * - Falls back to legacy log/trace paths for older runs
17
67
  * - Creates a zip file containing all files
18
68
  * - Returns the zip file as a downloadable response
19
69
  */
20
- export async function GET(request: NextRequest, { params }: { params: Promise<{ runId: string }> }) {
70
+ export async function GET(_request: NextRequest, { params }: { params: Promise<{ runId: string }> }) {
21
71
  const { runId } = await params
72
+ const runArtifactDir = getAutomationReportRunDir(runId)
22
73
 
23
74
  try {
24
75
  // Verify test run exists
25
76
  const testRun = await prisma.testRun.findUnique({
26
77
  where: { runId },
27
- include: {
78
+ select: {
79
+ logPath: true,
80
+ reportPath: true,
28
81
  testCases: {
29
82
  select: {
30
- id: true,
31
83
  tracePath: true,
32
84
  },
33
85
  },
@@ -43,47 +95,59 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
43
95
  zlib: { level: 9 }, // Maximum compression
44
96
  })
45
97
 
46
- // Track if we have any files to add
47
- let hasFiles = false
98
+ const runArtifactFiles = await collectRunArtifactFiles(runArtifactDir)
99
+ let hasFiles = runArtifactFiles.length > 0
100
+ const archivedPaths = new Set<string>(runArtifactFiles.map(artifactFile => artifactFile.archivePath))
101
+
102
+ for (const artifactFile of runArtifactFiles) {
103
+ archive.file(artifactFile.absolutePath, { name: artifactFile.archivePath })
104
+ }
105
+
106
+ if (testRun.reportPath) {
107
+ const resolvedReportPath = resolveStoredPath(testRun.reportPath)
108
+ if (!isPathWithinDirectory(resolvedReportPath, runArtifactDir) && !archivedPaths.has('cucumber.json')) {
109
+ const didAddReportFile = await addStoredArtifactFile(archive, testRun.reportPath, 'cucumber.json', 'Report file')
110
+ hasFiles = didAddReportFile || hasFiles
111
+ if (didAddReportFile) {
112
+ archivedPaths.add('cucumber.json')
113
+ }
114
+ }
115
+ }
48
116
 
49
- // Add log file if it exists
50
117
  if (testRun.logPath) {
51
- try {
52
- await fs.access(testRun.logPath)
53
- const logFileName = path.basename(testRun.logPath)
54
- archive.file(testRun.logPath, { name: `logs/${logFileName}` })
55
- hasFiles = true
56
- } catch {
57
- // Log file doesn't exist, skip it
58
- console.warn(`[Download] Log file not found at path: ${testRun.logPath}`)
118
+ const resolvedLogPath = resolveStoredPath(testRun.logPath)
119
+ const archivePath = `logs/${path.basename(testRun.logPath)}`
120
+ if (!isPathWithinDirectory(resolvedLogPath, runArtifactDir) && !archivedPaths.has(archivePath)) {
121
+ const didAddLogFile = await addStoredArtifactFile(archive, testRun.logPath, archivePath, 'Log file')
122
+ hasFiles = didAddLogFile || hasFiles
123
+ if (didAddLogFile) {
124
+ archivedPaths.add(archivePath)
125
+ }
59
126
  }
60
127
  }
61
128
 
62
- // Add trace files if they exist
63
- const traceFiles = testRun.testCases.filter(tc => tc.tracePath).map(tc => tc.tracePath!)
129
+ const traceFiles = testRun.testCases.flatMap(({ tracePath }) => (tracePath ? [tracePath] : []))
64
130
  for (const tracePath of traceFiles) {
65
- try {
66
- await fs.access(tracePath)
67
- const traceFileName = path.basename(tracePath)
68
- archive.file(tracePath, { name: `traces/${traceFileName}` })
69
- hasFiles = true
70
- } catch {
71
- // Trace file doesn't exist, skip it
72
- console.warn(`[Download] Trace file not found at path: ${tracePath}`)
131
+ const resolvedTracePath = resolveStoredPath(tracePath)
132
+ const archivePath = `traces/${path.basename(tracePath)}`
133
+
134
+ if (!isPathWithinDirectory(resolvedTracePath, runArtifactDir) && !archivedPaths.has(archivePath)) {
135
+ const didAddTraceFile = await addStoredArtifactFile(archive, tracePath, archivePath, 'Trace file')
136
+ hasFiles = didAddTraceFile || hasFiles
137
+ if (didAddTraceFile) {
138
+ archivedPaths.add(archivePath)
139
+ }
73
140
  }
74
141
  }
75
142
 
76
143
  // If no files to add, return an error
77
144
  if (!hasFiles) {
78
- return NextResponse.json(
79
- { error: 'No log or trace files available for this test run' },
80
- { status: 404 },
81
- )
145
+ return NextResponse.json({ error: 'No run artifacts available for this test run' }, { status: 404 })
82
146
  }
83
147
 
84
148
  // Create a readable stream to collect the archive data
85
149
  const chunks: Buffer[] = []
86
-
150
+
87
151
  // Set up event handlers before finalizing
88
152
  const archivePromise = new Promise<Buffer>((resolve, reject) => {
89
153
  archive.on('data', (chunk: Buffer) => {
@@ -95,7 +159,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
95
159
  resolve(zipBuffer)
96
160
  })
97
161
 
98
- archive.on('error', (err) => {
162
+ archive.on('error', err => {
99
163
  reject(err)
100
164
  })
101
165
  })
@@ -130,4 +194,3 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
130
194
  )
131
195
  }
132
196
  }
133
-
@@ -312,6 +312,8 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
312
312
  scenarioName: string
313
313
  status: string
314
314
  tracePath?: string
315
+ featureName?: string
316
+ scenarioTags?: string[]
315
317
  }) => {
316
318
  // Early return if error occurred to prevent infinite loops
317
319
  if (errorOccurred) return
@@ -325,7 +327,9 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
325
327
  sendSSE(
326
328
  'scenario::end',
327
329
  JSON.stringify({
330
+ featureName: eventData.featureName,
328
331
  scenarioName: eventData.scenarioName,
332
+ scenarioTags: eventData.scenarioTags,
329
333
  status: eventData.status,
330
334
  tracePath: eventData.tracePath,
331
335
  }),
@@ -418,4 +422,3 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
418
422
  },
419
423
  })
420
424
  }
421
-