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,99 +1,92 @@
1
- 'use client'
2
-
3
- import { useState } from 'react'
4
- import { Button } from '@/components/ui/button'
5
- import { Download, LoaderCircle } from 'lucide-react'
6
- import { AnimatePresence, motion } from 'motion/react'
7
- import { toast } from '@/hooks/use-toast'
8
-
9
- interface DownloadLogsButtonProps {
10
- testRunId: string
11
- className?: string
12
- }
13
-
14
- export function DownloadLogsButton({ testRunId, className }: DownloadLogsButtonProps) {
15
- const [isDownloading, setIsDownloading] = useState(false)
16
-
17
- const handleDownload = async () => {
18
- setIsDownloading(true)
19
- try {
20
- const response = await fetch(`/api/test-runs/${testRunId}/download`)
21
-
22
- if (!response.ok) {
23
- const errorData = await response.json().catch(() => ({ error: 'Unknown error' }))
24
- throw new Error(errorData.error || `Failed to download: ${response.statusText}`)
25
- }
26
-
27
- // Get the filename from Content-Disposition header or use a default
28
- const contentDisposition = response.headers.get('Content-Disposition')
29
- let filename = `test-run-${testRunId}.zip`
30
- if (contentDisposition) {
31
- const filenameMatch = contentDisposition.match(/filename="?(.+?)"?$/)
32
- if (filenameMatch) {
33
- filename = filenameMatch[1]
34
- }
35
- }
36
-
37
- // Create a blob from the response
38
- const blob = await response.blob()
39
-
40
- // Create a download link and trigger it
41
- const url = window.URL.createObjectURL(blob)
42
- const a = document.createElement('a')
43
- a.href = url
44
- a.download = filename
45
- document.body.appendChild(a)
46
- a.click()
47
- document.body.removeChild(a)
48
- window.URL.revokeObjectURL(url)
49
-
50
- toast({
51
- title: 'Download started',
52
- description: 'Logs and traces are being downloaded',
53
- })
54
- } catch (error) {
55
- console.error('[DownloadLogsButton] Error downloading logs:', error)
56
- toast({
57
- title: 'Download failed',
58
- description: error instanceof Error ? error.message : 'Failed to download logs and traces',
59
- variant: 'destructive',
60
- })
61
- } finally {
62
- setIsDownloading(false)
63
- }
64
- }
65
-
66
- return (
67
- <Button
68
- onClick={handleDownload}
69
- disabled={isDownloading}
70
- variant="outline"
71
- size="sm"
72
- className={className}
73
- >
74
- <AnimatePresence mode="wait" initial={false}>
75
- <motion.div
76
- key={isDownloading ? 'downloading' : 'idle'}
77
- initial={{ opacity: 0, y: 4 }}
78
- animate={{ opacity: 1, y: 0 }}
79
- exit={{ opacity: 0, y: -4 }}
80
- transition={{ duration: 0.15 }}
81
- className="flex items-center gap-2"
82
- >
83
- {isDownloading ? (
84
- <>
85
- <LoaderCircle className="h-4 w-4 animate-spin" />
86
- Downloading...
87
- </>
88
- ) : (
89
- <>
90
- <Download className="h-4 w-4" />
91
- Download Logs & Traces
92
- </>
93
- )}
94
- </motion.div>
95
- </AnimatePresence>
96
- </Button>
97
- )
98
- }
99
-
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { Button } from '@/components/ui/button'
5
+ import { Download, LoaderCircle } from 'lucide-react'
6
+ import { AnimatePresence, motion } from 'motion/react'
7
+ import { toast } from '@/hooks/use-toast'
8
+
9
+ interface DownloadLogsButtonProps {
10
+ testRunId: string
11
+ className?: string
12
+ }
13
+
14
+ export function DownloadLogsButton({ testRunId, className }: DownloadLogsButtonProps) {
15
+ const [isDownloading, setIsDownloading] = useState(false)
16
+
17
+ const handleDownload = async () => {
18
+ setIsDownloading(true)
19
+ try {
20
+ const response = await fetch(`/api/test-runs/${testRunId}/download`)
21
+
22
+ if (!response.ok) {
23
+ const errorData = await response.json().catch(() => ({ error: 'Unknown error' }))
24
+ throw new Error(errorData.error || `Failed to download: ${response.statusText}`)
25
+ }
26
+
27
+ // Get the filename from Content-Disposition header or use a default
28
+ const contentDisposition = response.headers.get('Content-Disposition')
29
+ let filename = `test-run-${testRunId}.zip`
30
+ if (contentDisposition) {
31
+ const filenameMatch = contentDisposition.match(/filename="?(.+?)"?$/)
32
+ if (filenameMatch) {
33
+ filename = filenameMatch[1]
34
+ }
35
+ }
36
+
37
+ // Create a blob from the response
38
+ const blob = await response.blob()
39
+
40
+ // Create a download link and trigger it
41
+ const url = window.URL.createObjectURL(blob)
42
+ const a = document.createElement('a')
43
+ a.href = url
44
+ a.download = filename
45
+ document.body.appendChild(a)
46
+ a.click()
47
+ document.body.removeChild(a)
48
+ window.URL.revokeObjectURL(url)
49
+
50
+ toast({
51
+ title: 'Download started',
52
+ description: 'Run artifacts are being downloaded',
53
+ })
54
+ } catch (error) {
55
+ console.error('[DownloadLogsButton] Error downloading logs:', error)
56
+ toast({
57
+ title: 'Download failed',
58
+ description: error instanceof Error ? error.message : 'Failed to download run artifacts',
59
+ variant: 'destructive',
60
+ })
61
+ } finally {
62
+ setIsDownloading(false)
63
+ }
64
+ }
65
+
66
+ return (
67
+ <Button onClick={handleDownload} disabled={isDownloading} variant="outline" size="sm" className={className}>
68
+ <AnimatePresence mode="wait" initial={false}>
69
+ <motion.div
70
+ key={isDownloading ? 'downloading' : 'idle'}
71
+ initial={{ opacity: 0, y: 4 }}
72
+ animate={{ opacity: 1, y: 0 }}
73
+ exit={{ opacity: 0, y: -4 }}
74
+ transition={{ duration: 0.15 }}
75
+ className="flex items-center gap-2"
76
+ >
77
+ {isDownloading ? (
78
+ <>
79
+ <LoaderCircle className="h-4 w-4 animate-spin" />
80
+ Downloading...
81
+ </>
82
+ ) : (
83
+ <>
84
+ <Download className="h-4 w-4" />
85
+ Download Run Artifacts
86
+ </>
87
+ )}
88
+ </motion.div>
89
+ </AnimatePresence>
90
+ </Button>
91
+ )
92
+ }
@@ -6,7 +6,7 @@ import { Badge } from '@/components/ui/badge'
6
6
  import { Alert, AlertDescription } from '@/components/ui/alert'
7
7
  import { LoaderCircle, Wifi, WifiOff, CheckCircle, XCircle, Logs } from 'lucide-react'
8
8
  import { cn } from '@/lib/utils'
9
- import { getTestRunLogsAction, updateTestRunTestCaseStatusAction } from '@/actions/test-run/test-run-actions'
9
+ import { getTestRunLogsAction } from '@/actions/test-run/test-run-actions'
10
10
  import { TestRunStatus } from '@prisma/client'
11
11
  import { DownloadLogsButton } from './download-logs-button'
12
12
  import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'
@@ -278,19 +278,6 @@ export function LogViewer({ testRunId, status, className }: LogViewerProps) {
278
278
  `[LogViewer] Scenario ended: ${scenarioName} with status: ${status}${tracePath ? `, tracePath: ${tracePath}` : ''}`,
279
279
  )
280
280
 
281
- // Update test case status in database
282
- // This will gracefully handle test runs filtered by tags (no test cases)
283
- const response = await updateTestRunTestCaseStatusAction(testRunId, scenarioName, status, tracePath)
284
- if (response.error && response.status !== 200) {
285
- // Only log as error if it's not a 200 status (which means it was skipped gracefully)
286
- console.error('[LogViewer] Error updating test case status:', response.error)
287
- } else if (response.status === 200) {
288
- // Log success or graceful skip (200 status means it was handled correctly)
289
- console.log(
290
- `[LogViewer] ${response.message || `Successfully updated test case status for scenario: ${scenarioName}`}`,
291
- )
292
- }
293
-
294
281
  // Also add to logs for visibility
295
282
  setLogs(prev => [
296
283
  ...prev,
@@ -46,7 +46,10 @@ import { toast } from '@/hooks/use-toast'
46
46
 
47
47
  interface TestRunDetailsProps {
48
48
  testRun: TestRun & {
49
- testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
49
+ testCases: (TestRunTestCase & {
50
+ testCase: { title: string; description: string }
51
+ testSuite: { id: string; name: string } | null
52
+ })[]
50
53
  tags: Tag[]
51
54
  environment: Environment
52
55
  reports: Report[]
@@ -81,7 +84,10 @@ export function TestRunDetails({ testRun: initialTestRun }: TestRunDetailsProps)
81
84
  }
82
85
  // TypeScript now knows updatedTestRun is defined - cast to proper type
83
86
  const typedTestRun = updatedTestRun as TestRun & {
84
- testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
87
+ testCases: (TestRunTestCase & {
88
+ testCase: { title: string; description: string }
89
+ testSuite: { id: string; name: string } | null
90
+ })[]
85
91
  tags: Tag[]
86
92
  environment: Environment
87
93
  reports: Report[]
@@ -544,6 +550,13 @@ export function TestRunDetails({ testRun: initialTestRun }: TestRunDetailsProps)
544
550
  <div className="flex flex-col gap-1">
545
551
  <span className="text-sm font-semibold">{testCase.testCase.title}</span>
546
552
  <span className="text-xs text-muted-foreground">{testCase.testCase.description}</span>
553
+ {testCase.testSuite && (
554
+ <div className="pt-1">
555
+ <Badge variant="outline" className="w-fit text-[11px]">
556
+ Suite: {testCase.testSuite.name}
557
+ </Badge>
558
+ </div>
559
+ )}
547
560
  </div>
548
561
  </div>
549
562
  <div className="flex items-center gap-2">
@@ -1,149 +1,149 @@
1
- 'use client'
2
-
3
- import { useEffect, useState } from 'react'
4
- import { getTestRunByIdAction } from '@/actions/test-run/test-run-actions'
5
- import PageHeader from '@/components/typography/page-header'
6
- import HeaderSubtitle from '@/components/typography/page-header-subtitle'
7
- import { ViewReportButton } from '@/components/test-run/view-report-button'
8
- import { TestRun, TestRunTestCase, Tag, Environment, TestRunStatus, Report } from '@prisma/client'
9
-
10
- interface TestRunHeaderProps {
11
- initialTestRun: TestRun & {
12
- testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
13
- tags: Tag[]
14
- environment: Environment
15
- reports: Report[]
16
- }
17
- }
18
-
19
- export function TestRunHeader({ initialTestRun }: TestRunHeaderProps) {
20
- const [testRun, setTestRun] = useState(initialTestRun)
21
-
22
- // Poll for status updates while test run is running or queued
23
- useEffect(() => {
24
- // Only poll if test run is not completed/cancelled
25
- if (testRun.status === TestRunStatus.COMPLETED || testRun.status === TestRunStatus.CANCELLED) {
26
- return
27
- }
28
-
29
- const pollInterval = setInterval(async () => {
30
- try {
31
- const { data: updatedTestRun, error } = await getTestRunByIdAction(testRun.id)
32
- if (error || !updatedTestRun) {
33
- console.error('Error polling test run status:', error)
34
- return
35
- }
36
-
37
- const typedTestRun = updatedTestRun as TestRun & {
38
- testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
39
- tags: Tag[]
40
- environment: Environment
41
- reports: Report[]
42
- }
43
-
44
- // Update state with latest test run data
45
- setTestRun(typedTestRun)
46
-
47
- // Stop polling if test run is completed or cancelled
48
- if (typedTestRun.status === TestRunStatus.COMPLETED || typedTestRun.status === TestRunStatus.CANCELLED) {
49
- clearInterval(pollInterval)
50
- }
51
- } catch (error) {
52
- console.error('Error polling test run status:', error)
53
- }
54
- }, 2000) // Poll every 2 seconds
55
-
56
- return () => clearInterval(pollInterval)
57
- }, [testRun.id, testRun.status])
58
-
59
- // Listen for SSE exit events from LogViewer as a backup signal
60
- useEffect(() => {
61
- const handleTestRunExit = (event: CustomEvent<{ testRunId: string }>) => {
62
- // Only handle events for this test run
63
- if (event.detail.testRunId === testRun.runId) {
64
- // Trigger a status refresh
65
- getTestRunByIdAction(testRun.id)
66
- .then(response => {
67
- if (response.data && !response.error) {
68
- const typedTestRun = response.data as TestRun & {
69
- testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
70
- tags: Tag[]
71
- environment: Environment
72
- reports: Report[]
73
- }
74
- setTestRun(typedTestRun)
75
- }
76
- })
77
- .catch(error => {
78
- console.error('Error refreshing test run status after exit event:', error)
79
- })
80
- }
81
- }
82
-
83
- // Listen for custom event dispatched by LogViewer
84
- window.addEventListener('testrun:exit', handleTestRunExit as EventListener)
85
-
86
- return () => {
87
- window.removeEventListener('testrun:exit', handleTestRunExit as EventListener)
88
- }
89
- }, [testRun.id, testRun.runId])
90
-
91
- // Poll for report updates after test run completes
92
- useEffect(() => {
93
- // Only poll if test run is completed (not cancelled) but no report exists yet
94
- // Cancelled runs won't generate reports, so we don't poll for them
95
- const shouldPoll = testRun.status === TestRunStatus.COMPLETED && testRun.reports.length === 0
96
-
97
- if (!shouldPoll) {
98
- return
99
- }
100
-
101
- // Poll for reports (they might be created asynchronously after test run completes)
102
- const pollInterval = setInterval(async () => {
103
- try {
104
- const { data: updatedTestRun, error } = await getTestRunByIdAction(testRun.id)
105
- if (error || !updatedTestRun) {
106
- console.error('Error polling for report:', error)
107
- return
108
- }
109
-
110
- const typedTestRun = updatedTestRun as TestRun & {
111
- testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
112
- tags: Tag[]
113
- environment: Environment
114
- reports: Report[]
115
- }
116
-
117
- // Update if reports are now available
118
- if (typedTestRun.reports.length > 0) {
119
- setTestRun(typedTestRun)
120
- clearInterval(pollInterval)
121
- }
122
- } catch (error) {
123
- console.error('Error polling for report:', error)
124
- }
125
- }, 2000) // Poll every 2 seconds
126
-
127
- // Stop polling after 30 seconds to avoid infinite polling
128
- const timeout = setTimeout(() => {
129
- clearInterval(pollInterval)
130
- }, 30000)
131
-
132
- return () => {
133
- clearInterval(pollInterval)
134
- clearTimeout(timeout)
135
- }
136
- }, [testRun.id, testRun.status, testRun.reports.length])
137
-
138
- return (
139
- <div className="mb-8">
140
- <div className="flex items-start justify-between gap-4">
141
- <div className="flex-1">
142
- <PageHeader>Test Run Details</PageHeader>
143
- <HeaderSubtitle>View test run execution details and live logs</HeaderSubtitle>
144
- </div>
145
- <ViewReportButton testRunStatus={testRun.status} reports={testRun.reports} className="mt-2" />
146
- </div>
147
- </div>
148
- )
149
- }
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+ import { getTestRunByIdAction } from '@/actions/test-run/test-run-actions'
5
+ import PageHeader from '@/components/typography/page-header'
6
+ import HeaderSubtitle from '@/components/typography/page-header-subtitle'
7
+ import { ViewReportButton } from '@/components/test-run/view-report-button'
8
+ import { TestRun, TestRunTestCase, Tag, Environment, TestRunStatus, Report } from '@prisma/client'
9
+
10
+ interface TestRunHeaderProps {
11
+ initialTestRun: TestRun & {
12
+ testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
13
+ tags: Tag[]
14
+ environment: Environment
15
+ reports: Report[]
16
+ }
17
+ }
18
+
19
+ export function TestRunHeader({ initialTestRun }: TestRunHeaderProps) {
20
+ const [testRun, setTestRun] = useState(initialTestRun)
21
+
22
+ // Poll for status updates while test run is running or queued
23
+ useEffect(() => {
24
+ // Only poll if test run is not completed/cancelled
25
+ if (testRun.status === TestRunStatus.COMPLETED || testRun.status === TestRunStatus.CANCELLED) {
26
+ return
27
+ }
28
+
29
+ const pollInterval = setInterval(async () => {
30
+ try {
31
+ const { data: updatedTestRun, error } = await getTestRunByIdAction(testRun.id)
32
+ if (error || !updatedTestRun) {
33
+ console.error('Error polling test run status:', error)
34
+ return
35
+ }
36
+
37
+ const typedTestRun = updatedTestRun as TestRun & {
38
+ testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
39
+ tags: Tag[]
40
+ environment: Environment
41
+ reports: Report[]
42
+ }
43
+
44
+ // Update state with latest test run data
45
+ setTestRun(typedTestRun)
46
+
47
+ // Stop polling if test run is completed or cancelled
48
+ if (typedTestRun.status === TestRunStatus.COMPLETED || typedTestRun.status === TestRunStatus.CANCELLED) {
49
+ clearInterval(pollInterval)
50
+ }
51
+ } catch (error) {
52
+ console.error('Error polling test run status:', error)
53
+ }
54
+ }, 2000) // Poll every 2 seconds
55
+
56
+ return () => clearInterval(pollInterval)
57
+ }, [testRun.id, testRun.status])
58
+
59
+ // Listen for SSE exit events from LogViewer as a backup signal
60
+ useEffect(() => {
61
+ const handleTestRunExit = (event: CustomEvent<{ testRunId: string }>) => {
62
+ // Only handle events for this test run
63
+ if (event.detail.testRunId === testRun.runId) {
64
+ // Trigger a status refresh
65
+ getTestRunByIdAction(testRun.id)
66
+ .then(response => {
67
+ if (response.data && !response.error) {
68
+ const typedTestRun = response.data as TestRun & {
69
+ testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
70
+ tags: Tag[]
71
+ environment: Environment
72
+ reports: Report[]
73
+ }
74
+ setTestRun(typedTestRun)
75
+ }
76
+ })
77
+ .catch(error => {
78
+ console.error('Error refreshing test run status after exit event:', error)
79
+ })
80
+ }
81
+ }
82
+
83
+ // Listen for custom event dispatched by LogViewer
84
+ window.addEventListener('testrun:exit', handleTestRunExit as EventListener)
85
+
86
+ return () => {
87
+ window.removeEventListener('testrun:exit', handleTestRunExit as EventListener)
88
+ }
89
+ }, [testRun.id, testRun.runId])
90
+
91
+ // Poll for report updates after test run completes
92
+ useEffect(() => {
93
+ // Only poll if test run is completed (not cancelled) but no report exists yet
94
+ // Cancelled runs won't generate reports, so we don't poll for them
95
+ const shouldPoll = testRun.status === TestRunStatus.COMPLETED && testRun.reports.length === 0
96
+
97
+ if (!shouldPoll) {
98
+ return
99
+ }
100
+
101
+ // Poll for reports (they might be created asynchronously after test run completes)
102
+ const pollInterval = setInterval(async () => {
103
+ try {
104
+ const { data: updatedTestRun, error } = await getTestRunByIdAction(testRun.id)
105
+ if (error || !updatedTestRun) {
106
+ console.error('Error polling for report:', error)
107
+ return
108
+ }
109
+
110
+ const typedTestRun = updatedTestRun as TestRun & {
111
+ testCases: (TestRunTestCase & { testCase: { title: string; description: string } })[]
112
+ tags: Tag[]
113
+ environment: Environment
114
+ reports: Report[]
115
+ }
116
+
117
+ // Update if reports are now available
118
+ if (typedTestRun.reports.length > 0) {
119
+ setTestRun(typedTestRun)
120
+ clearInterval(pollInterval)
121
+ }
122
+ } catch (error) {
123
+ console.error('Error polling for report:', error)
124
+ }
125
+ }, 2000) // Poll every 2 seconds
126
+
127
+ // Stop polling after 30 seconds to avoid infinite polling
128
+ const timeout = setTimeout(() => {
129
+ clearInterval(pollInterval)
130
+ }, 30000)
131
+
132
+ return () => {
133
+ clearInterval(pollInterval)
134
+ clearTimeout(timeout)
135
+ }
136
+ }, [testRun.id, testRun.status, testRun.reports.length])
137
+
138
+ return (
139
+ <div className="mb-8">
140
+ <div className="flex items-start justify-between gap-4">
141
+ <div className="flex-1">
142
+ <PageHeader>Test Run Details</PageHeader>
143
+ <HeaderSubtitle>View test run execution details and live logs</HeaderSubtitle>
144
+ </div>
145
+ <ViewReportButton testRunStatus={testRun.status} reports={testRun.reports} className="mt-2" />
146
+ </div>
147
+ </div>
148
+ )
149
+ }