create-appraisejs 0.2.0-alpha.6 → 0.3.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (485) hide show
  1. package/README.md +12 -1
  2. package/package.json +1 -1
  3. package/templates/default/.appraise-template-meta.json +2 -2
  4. package/templates/default/.env.example +2 -2
  5. package/templates/default/README.md +57 -53
  6. package/templates/default/automation/steps/actions/click.step.ts +58 -58
  7. package/templates/default/automation/steps/actions/hover.step.ts +27 -27
  8. package/templates/default/automation/steps/actions/navigation.step.ts +70 -70
  9. package/templates/default/automation/steps/actions/random_data.step.ts +142 -142
  10. package/templates/default/automation/steps/actions/store.step.ts +86 -86
  11. package/templates/default/automation/steps/actions/wait.step.ts +110 -90
  12. package/templates/default/automation/steps/validations/active_state_assertion.step.ts +30 -30
  13. package/templates/default/automation/steps/validations/navigation_assertion.step.ts +22 -22
  14. package/templates/default/automation/steps/validations/text_assertion.step.ts +107 -107
  15. package/templates/default/automation/steps/validations/visibility_assertion.step.ts +26 -26
  16. package/templates/default/components.json +24 -24
  17. package/templates/default/cucumber.mjs +16 -16
  18. package/templates/default/eslint.config.mjs +20 -16
  19. package/templates/default/next-env.d.ts +6 -6
  20. package/templates/default/next.config.ts +20 -11
  21. package/templates/default/package-lock.json +1775 -74
  22. package/templates/default/package.json +8 -1
  23. package/templates/default/packages/cucumber-runtime/package.json +13 -13
  24. package/templates/default/packages/cucumber-runtime/src/cache.util.ts +93 -93
  25. package/templates/default/packages/cucumber-runtime/src/cli.ts +68 -68
  26. package/templates/default/packages/cucumber-runtime/src/environment.util.ts +21 -21
  27. package/templates/default/packages/cucumber-runtime/src/executor.ts +32 -32
  28. package/templates/default/packages/cucumber-runtime/src/index.ts +17 -17
  29. package/templates/default/packages/cucumber-runtime/src/locator.util.ts +234 -234
  30. package/templates/default/packages/cucumber-runtime/src/parameter-types.ts +7 -7
  31. package/templates/default/packages/cucumber-runtime/src/random-data.util.ts +35 -35
  32. package/templates/default/packages/cucumber-runtime/src/types.ts +13 -13
  33. package/templates/default/packages/cucumber-runtime/src/world.ts +44 -44
  34. package/templates/default/packages/cucumber-runtime/tsconfig.json +11 -11
  35. package/templates/default/postcss.config.mjs +8 -8
  36. package/templates/default/prisma/dev.db +0 -0
  37. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  38. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  39. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  40. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  41. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  42. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  43. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  44. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  45. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  46. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  47. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  48. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  49. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  50. package/templates/default/prisma/migrations/20260313093000_add_report_step_screenshot_path/migration.sql +1 -1
  51. package/templates/default/scripts/build-step-registry.ts +33 -0
  52. package/templates/default/scripts/install-playwright.ts +17 -8
  53. package/templates/default/scripts/install-template-step.ts +128 -0
  54. package/templates/default/scripts/lib/filename-utils.test.ts +24 -0
  55. package/templates/default/scripts/lib/filename-utils.ts +24 -0
  56. package/templates/default/scripts/lib/jsdoc-parser.test.ts +63 -0
  57. package/templates/default/scripts/lib/jsdoc-parser.ts +88 -0
  58. package/templates/default/scripts/lib/step-file-parser.test.ts +71 -0
  59. package/templates/default/scripts/lib/step-file-parser.ts +315 -0
  60. package/templates/default/scripts/lib/step-matcher.test.ts +86 -0
  61. package/templates/default/scripts/lib/step-matcher.ts +120 -0
  62. package/templates/default/scripts/lib/sync-script-runner.ts +23 -0
  63. package/templates/default/scripts/lib/sync-summary.ts +29 -0
  64. package/templates/default/scripts/lib/tag-parsing.test.ts +20 -0
  65. package/templates/default/scripts/lib/tag-parsing.ts +10 -0
  66. package/templates/default/scripts/lib/template-step-installer.test.ts +225 -0
  67. package/templates/default/scripts/lib/template-step-installer.ts +404 -0
  68. package/templates/default/scripts/lib/template-step-registry.test.ts +118 -0
  69. package/templates/default/scripts/lib/template-step-registry.ts +137 -0
  70. package/templates/default/scripts/protect-seeded-files.ts +51 -38
  71. package/templates/default/scripts/regenerate-features.ts +98 -94
  72. package/templates/default/scripts/run-vitest.ts +59 -0
  73. package/templates/default/scripts/setup-env.ts +26 -19
  74. package/templates/default/scripts/sync-all.ts +44 -54
  75. package/templates/default/scripts/sync-appraise-base-template.ts +44 -16
  76. package/templates/default/scripts/sync-environments.ts +22 -66
  77. package/templates/default/scripts/sync-locator-groups.ts +358 -410
  78. package/templates/default/scripts/sync-locators.ts +348 -398
  79. package/templates/default/scripts/sync-modules.ts +302 -341
  80. package/templates/default/scripts/sync-tags.ts +29 -65
  81. package/templates/default/scripts/sync-template-step-groups.ts +24 -182
  82. package/templates/default/scripts/sync-template-steps.ts +36 -493
  83. package/templates/default/scripts/sync-test-cases.ts +296 -539
  84. package/templates/default/scripts/sync-test-suites.ts +32 -79
  85. package/templates/default/src/actions/dashboard/dashboard-actions.ts +70 -241
  86. package/templates/default/src/actions/environments/environment-actions.ts +102 -188
  87. package/templates/default/src/actions/locator/locator-actions.ts +77 -490
  88. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +34 -212
  89. package/templates/default/src/actions/locator-picker/locator-picker-actions.test.ts +81 -0
  90. package/templates/default/src/actions/locator-picker/locator-picker-actions.ts +20 -161
  91. package/templates/default/src/actions/modules/module-actions.ts +99 -135
  92. package/templates/default/src/actions/reports/report-actions.ts +28 -565
  93. package/templates/default/src/actions/settings/sync-actions.test.ts +58 -0
  94. package/templates/default/src/actions/tags/tag-actions.ts +99 -107
  95. package/templates/default/src/actions/template-step/template-step-actions.ts +33 -194
  96. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +35 -92
  97. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +98 -238
  98. package/templates/default/src/actions/test-case/test-case-actions.ts +108 -356
  99. package/templates/default/src/actions/test-run/test-run-actions.ts +74 -1081
  100. package/templates/default/src/actions/test-suite/test-suite-actions.ts +35 -202
  101. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
  102. package/templates/default/src/app/(base)/environments/environment-form.test.tsx +92 -0
  103. package/templates/default/src/app/(base)/environments/environment-form.tsx +228 -219
  104. package/templates/default/src/app/(base)/environments/environment-helpers.ts +58 -0
  105. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
  106. package/templates/default/src/app/(base)/environments/environment-table.tsx +25 -24
  107. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +49 -46
  108. package/templates/default/src/app/(base)/environments/page.tsx +59 -59
  109. package/templates/default/src/app/(base)/layout.tsx +10 -10
  110. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -44
  111. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -215
  112. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -77
  113. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -28
  114. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -46
  115. package/templates/default/src/app/(base)/locators/create/create-locator-workspace-helpers.test.ts +71 -0
  116. package/templates/default/src/app/(base)/locators/create/create-locator-workspace-helpers.ts +333 -0
  117. package/templates/default/src/app/(base)/locators/create/create-locator-workspace.test.tsx +125 -0
  118. package/templates/default/src/app/(base)/locators/create/create-locator-workspace.tsx +56 -274
  119. package/templates/default/src/app/(base)/locators/create/page.tsx +8 -4
  120. package/templates/default/src/app/(base)/locators/create/use-locator-workspace.ts +183 -0
  121. package/templates/default/src/app/(base)/locators/locator-helpers.test.ts +28 -0
  122. package/templates/default/src/app/(base)/locators/locator-helpers.ts +59 -0
  123. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +74 -73
  124. package/templates/default/src/app/(base)/locators/locator-table.tsx +30 -28
  125. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +20 -8
  126. package/templates/default/src/app/(base)/locators/page.tsx +3 -6
  127. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +67 -66
  128. package/templates/default/src/app/(base)/modules/create/page.tsx +33 -34
  129. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +43 -46
  130. package/templates/default/src/app/(base)/modules/module-form.test.tsx +84 -0
  131. package/templates/default/src/app/(base)/modules/module-form.tsx +159 -126
  132. package/templates/default/src/app/(base)/modules/module-helpers.ts +64 -0
  133. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +81 -85
  134. package/templates/default/src/app/(base)/modules/module-table.tsx +25 -24
  135. package/templates/default/src/app/(base)/modules/page.tsx +59 -59
  136. package/templates/default/src/app/(base)/reports/[id]/page.tsx +20 -260
  137. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
  138. package/templates/default/src/app/(base)/reports/feature-chart.tsx +79 -78
  139. package/templates/default/src/app/(base)/reports/overview-chart.tsx +49 -49
  140. package/templates/default/src/app/(base)/reports/page.tsx +98 -98
  141. package/templates/default/src/app/(base)/reports/report-detail-helpers.test.ts +109 -0
  142. package/templates/default/src/app/(base)/reports/report-detail-helpers.ts +247 -0
  143. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +78 -78
  144. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -189
  145. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -72
  146. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +40 -40
  147. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -115
  148. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -27
  149. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -42
  150. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -79
  151. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -27
  152. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +58 -58
  153. package/templates/default/src/app/(base)/settings/settings-sync-panel-helpers.test.tsx +40 -0
  154. package/templates/default/src/app/(base)/settings/settings-sync-panel-helpers.tsx +110 -0
  155. package/templates/default/src/app/(base)/settings/settings-sync-panel.test.tsx +127 -0
  156. package/templates/default/src/app/(base)/settings/settings-sync-panel.tsx +19 -134
  157. package/templates/default/src/app/(base)/settings/use-settings-sync.ts +66 -0
  158. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -39
  159. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -50
  160. package/templates/default/src/app/(base)/tags/page.tsx +58 -58
  161. package/templates/default/src/app/(base)/tags/tag-form-helpers.ts +13 -0
  162. package/templates/default/src/app/(base)/tags/tag-form.test.tsx +83 -0
  163. package/templates/default/src/app/(base)/tags/tag-form.tsx +143 -147
  164. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -63
  165. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -29
  166. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -28
  167. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +43 -45
  168. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -60
  169. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.test.tsx +82 -0
  170. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +181 -167
  171. package/templates/default/src/app/(base)/template-step-groups/template-step-group-helpers.ts +54 -0
  172. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -89
  173. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +34 -32
  174. package/templates/default/src/app/(base)/template-steps/create/page.tsx +40 -37
  175. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +54 -49
  176. package/templates/default/src/app/(base)/template-steps/page.tsx +59 -58
  177. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +233 -213
  178. package/templates/default/src/app/(base)/template-steps/template-step-form.test.tsx +132 -0
  179. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +342 -384
  180. package/templates/default/src/app/(base)/template-steps/template-step-helpers.test.ts +99 -0
  181. package/templates/default/src/app/(base)/template-steps/template-step-helpers.ts +176 -0
  182. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +153 -158
  183. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +26 -24
  184. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -56
  185. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -89
  186. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -58
  187. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.test.tsx +109 -0
  188. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +45 -84
  189. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.test.tsx +140 -0
  190. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +154 -262
  191. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -76
  192. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -32
  193. package/templates/default/src/app/(base)/test-cases/create/page.tsx +90 -76
  194. package/templates/default/src/app/(base)/test-cases/create-from-template/create-from-template-helpers.test.ts +94 -0
  195. package/templates/default/src/app/(base)/test-cases/create-from-template/create-from-template-helpers.ts +171 -0
  196. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +105 -96
  197. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +40 -38
  198. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.test.tsx +87 -0
  199. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +83 -73
  200. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -106
  201. package/templates/default/src/app/(base)/test-cases/page.tsx +3 -2
  202. package/templates/default/src/app/(base)/test-cases/test-case-flow.test.tsx +108 -0
  203. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +43 -82
  204. package/templates/default/src/app/(base)/test-cases/test-case-form.test.tsx +202 -0
  205. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +263 -395
  206. package/templates/default/src/app/(base)/test-cases/test-case-route-helpers.test.ts +95 -0
  207. package/templates/default/src/app/(base)/test-cases/test-case-route-helpers.ts +147 -0
  208. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +4 -2
  209. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +11 -10
  210. package/templates/default/src/app/(base)/test-runs/create/page.tsx +4 -5
  211. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
  212. package/templates/default/src/app/(base)/test-runs/test-run-form-helpers.test.ts +50 -0
  213. package/templates/default/src/app/(base)/test-runs/test-run-form-helpers.ts +168 -0
  214. package/templates/default/src/app/(base)/test-runs/test-run-form.test.tsx +138 -0
  215. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +111 -256
  216. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -229
  217. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -127
  218. package/templates/default/src/app/(base)/test-runs/use-test-run-name-validation.ts +74 -0
  219. package/templates/default/src/app/(base)/test-suites/create/page.tsx +17 -12
  220. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +22 -19
  221. package/templates/default/src/app/(base)/test-suites/page.tsx +14 -56
  222. package/templates/default/src/app/(base)/test-suites/test-suite-form.test.tsx +127 -0
  223. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +45 -64
  224. package/templates/default/src/app/(base)/test-suites/test-suite-helpers.test.ts +67 -0
  225. package/templates/default/src/app/(base)/test-suites/test-suite-helpers.ts +215 -0
  226. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +32 -29
  227. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -187
  228. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +12 -12
  229. package/templates/default/src/app/(dashboard-components)/data-card.tsx +26 -26
  230. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +56 -56
  231. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -87
  232. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +44 -44
  233. package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.test.ts +83 -0
  234. package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.ts +52 -52
  235. package/templates/default/src/app/api/test-runs/[runId]/download/route.test.ts +169 -0
  236. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +1 -1
  237. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.test.ts +135 -0
  238. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -146
  239. package/templates/default/src/app/globals.css +147 -147
  240. package/templates/default/src/app/page.tsx +1 -1
  241. package/templates/default/src/assets/icons/empty-tube.tsx +23 -23
  242. package/templates/default/src/assets/icons/tube-plus.tsx +29 -29
  243. package/templates/default/src/components/base-node.tsx +21 -21
  244. package/templates/default/src/components/chart/pie-chart.tsx +73 -73
  245. package/templates/default/src/components/data-extraction/locator-inspector-helpers.test.ts +32 -0
  246. package/templates/default/src/components/data-extraction/locator-inspector-helpers.ts +183 -0
  247. package/templates/default/src/components/data-extraction/locator-inspector.tsx +349 -460
  248. package/templates/default/src/components/data-state/empty-state.tsx +40 -40
  249. package/templates/default/src/components/data-visualization/info-card.tsx +70 -70
  250. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -22
  251. package/templates/default/src/components/diagram/button-edge.tsx +54 -54
  252. package/templates/default/src/components/diagram/dynamic-parameters-helpers.test.ts +83 -0
  253. package/templates/default/src/components/diagram/dynamic-parameters-helpers.ts +158 -0
  254. package/templates/default/src/components/diagram/dynamic-parameters.tsx +350 -474
  255. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
  256. package/templates/default/src/components/diagram/flow-diagram-helpers.test.ts +117 -0
  257. package/templates/default/src/components/diagram/flow-diagram-helpers.ts +251 -0
  258. package/templates/default/src/components/diagram/flow-diagram.tsx +247 -470
  259. package/templates/default/src/components/diagram/flow-host-helpers.test.ts +74 -0
  260. package/templates/default/src/components/diagram/flow-host-helpers.ts +51 -0
  261. package/templates/default/src/components/diagram/node-form-helpers.test.ts +92 -0
  262. package/templates/default/src/components/diagram/node-form-helpers.ts +100 -0
  263. package/templates/default/src/components/diagram/node-form.test.tsx +168 -0
  264. package/templates/default/src/components/diagram/node-form.tsx +199 -262
  265. package/templates/default/src/components/diagram/options-header-node.tsx +57 -57
  266. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -155
  267. package/templates/default/src/components/diagram/use-flow-node-order.ts +49 -0
  268. package/templates/default/src/components/form/error-message.tsx +7 -7
  269. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -453
  270. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -30
  271. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -8
  272. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -8
  273. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -8
  274. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -14
  275. package/templates/default/src/components/navigation/command-badge.tsx +34 -34
  276. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -51
  277. package/templates/default/src/components/navigation/entity-search-command.tsx +118 -116
  278. package/templates/default/src/components/navigation/nav-card.tsx +31 -31
  279. package/templates/default/src/components/navigation/nav-command-helpers.ts +122 -0
  280. package/templates/default/src/components/navigation/nav-command-search.tsx +125 -0
  281. package/templates/default/src/components/navigation/nav-command.test.tsx +106 -0
  282. package/templates/default/src/components/navigation/nav-command.tsx +49 -472
  283. package/templates/default/src/components/navigation/nav-link.tsx +60 -60
  284. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -112
  285. package/templates/default/src/components/navigation/use-nav-command.ts +58 -0
  286. package/templates/default/src/components/node-header.tsx +159 -159
  287. package/templates/default/src/components/reports/test-case-logs-modal.tsx +310 -310
  288. package/templates/default/src/components/table/table-actions.tsx +174 -172
  289. package/templates/default/src/components/test-case/test-case-form-helpers.test.ts +100 -0
  290. package/templates/default/src/components/test-case/test-case-form-helpers.ts +140 -0
  291. package/templates/default/src/components/test-case/test-case-picker-helpers.test.ts +40 -0
  292. package/templates/default/src/components/test-case/test-case-picker-helpers.ts +41 -0
  293. package/templates/default/src/components/test-case/test-case-picker.test.tsx +44 -0
  294. package/templates/default/src/components/test-case/test-case-picker.tsx +16 -35
  295. package/templates/default/src/components/test-case/test-scenario-preview.tsx +34 -0
  296. package/templates/default/src/components/test-run/download-logs-button.tsx +92 -92
  297. package/templates/default/src/components/test-run/log-viewer-helpers.test.ts +37 -0
  298. package/templates/default/src/components/test-run/log-viewer-helpers.ts +80 -0
  299. package/templates/default/src/components/test-run/log-viewer.test.tsx +118 -0
  300. package/templates/default/src/components/test-run/log-viewer.tsx +51 -350
  301. package/templates/default/src/components/test-run/test-run-details-helpers.test.ts +31 -0
  302. package/templates/default/src/components/test-run/test-run-details-helpers.ts +208 -0
  303. package/templates/default/src/components/test-run/test-run-details.test.tsx +174 -0
  304. package/templates/default/src/components/test-run/test-run-details.tsx +155 -457
  305. package/templates/default/src/components/test-run/test-run-header-helpers.test.ts +31 -0
  306. package/templates/default/src/components/test-run/test-run-header-helpers.ts +23 -0
  307. package/templates/default/src/components/test-run/test-run-header.test.tsx +103 -0
  308. package/templates/default/src/components/test-run/test-run-header.tsx +27 -149
  309. package/templates/default/src/components/test-run/use-log-viewer.ts +213 -0
  310. package/templates/default/src/components/test-run/use-test-run-details.ts +184 -0
  311. package/templates/default/src/components/test-run/use-test-run-header.ts +89 -0
  312. package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
  313. package/templates/default/src/components/test-suite/test-suite-picker-helpers.test.ts +68 -0
  314. package/templates/default/src/components/test-suite/test-suite-picker-helpers.ts +76 -0
  315. package/templates/default/src/components/test-suite/test-suite-picker.test.tsx +65 -0
  316. package/templates/default/src/components/test-suite/test-suite-picker.tsx +4 -72
  317. package/templates/default/src/components/theme/mode-toggle.tsx +54 -54
  318. package/templates/default/src/components/theme/theme-provider.tsx +8 -8
  319. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -7
  320. package/templates/default/src/components/typography/page-header.tsx +7 -7
  321. package/templates/default/src/components/ui/alert-dialog.tsx +106 -106
  322. package/templates/default/src/components/ui/alert.tsx +43 -43
  323. package/templates/default/src/components/ui/avatar.tsx +40 -40
  324. package/templates/default/src/components/ui/badge.tsx +29 -29
  325. package/templates/default/src/components/ui/button.tsx +47 -47
  326. package/templates/default/src/components/ui/calendar.tsx +158 -158
  327. package/templates/default/src/components/ui/card.tsx +43 -43
  328. package/templates/default/src/components/ui/checkbox.tsx +28 -28
  329. package/templates/default/src/components/ui/command.tsx +135 -135
  330. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -61
  331. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -87
  332. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -50
  333. package/templates/default/src/components/ui/data-table.test.tsx +122 -0
  334. package/templates/default/src/components/ui/data-table.tsx +298 -261
  335. package/templates/default/src/components/ui/dialog.tsx +97 -97
  336. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -182
  337. package/templates/default/src/components/ui/input.tsx +22 -22
  338. package/templates/default/src/components/ui/kbd.tsx +28 -28
  339. package/templates/default/src/components/ui/label.tsx +19 -19
  340. package/templates/default/src/components/ui/loading.tsx +12 -12
  341. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -116
  342. package/templates/default/src/components/ui/multi-select.test.tsx +45 -0
  343. package/templates/default/src/components/ui/multi-select.tsx +158 -142
  344. package/templates/default/src/components/ui/navigation-menu.tsx +120 -120
  345. package/templates/default/src/components/ui/popover.tsx +33 -33
  346. package/templates/default/src/components/ui/progress.tsx +25 -25
  347. package/templates/default/src/components/ui/radio-group.tsx +44 -44
  348. package/templates/default/src/components/ui/scroll-area.tsx +40 -40
  349. package/templates/default/src/components/ui/select.tsx +151 -151
  350. package/templates/default/src/components/ui/separator.tsx +22 -22
  351. package/templates/default/src/components/ui/skeleton.tsx +7 -7
  352. package/templates/default/src/components/ui/table.tsx +76 -76
  353. package/templates/default/src/components/ui/tabs.tsx +55 -55
  354. package/templates/default/src/components/ui/textarea.tsx +21 -21
  355. package/templates/default/src/components/ui/toast.tsx +113 -113
  356. package/templates/default/src/components/ui/toaster.tsx +26 -26
  357. package/templates/default/src/components/user-prompt/delete-prompt.test.tsx +60 -0
  358. package/templates/default/src/components/user-prompt/delete-prompt.tsx +118 -87
  359. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -30
  360. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -24
  361. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -28
  362. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -21
  363. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -42
  364. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -16
  365. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -24
  366. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -39
  367. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -36
  368. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -43
  369. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -24
  370. package/templates/default/src/hooks/use-toast.ts +187 -187
  371. package/templates/default/src/lib/automation/automation-path-roots.ts +95 -0
  372. package/templates/default/src/lib/automation/automation-workspace.ts +147 -0
  373. package/templates/default/src/lib/automation/paths.ts +6 -211
  374. package/templates/default/src/lib/bidirectional-sync.ts +432 -432
  375. package/templates/default/src/lib/environment-file-utils.ts +2 -1
  376. package/templates/default/src/lib/executor/local-executor-adapter.ts +2 -5
  377. package/templates/default/src/lib/feature-file-generator.ts +2 -1
  378. package/templates/default/src/lib/gherkin-parser.ts +0 -2
  379. package/templates/default/src/lib/locator-group-file-utils.ts +304 -307
  380. package/templates/default/src/lib/locator-picker/session-manager.ts +0 -21
  381. package/templates/default/src/lib/locator-picker/suggestions.ts +13 -11
  382. package/templates/default/src/lib/metrics/metric-calculator.ts +2 -6
  383. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -205
  384. package/templates/default/src/lib/path-helpers/module-path.ts +71 -71
  385. package/templates/default/src/lib/sync/sync-executor.test.ts +76 -0
  386. package/templates/default/src/lib/sync/sync-pending-counts.test.ts +227 -226
  387. package/templates/default/src/lib/sync/sync-pending-counts.ts +2 -5
  388. package/templates/default/src/lib/template-sync-utils.d.ts +6 -6
  389. package/templates/default/src/lib/template-sync-utils.js +46 -46
  390. package/templates/default/src/lib/template-sync-utils.ts +63 -63
  391. package/templates/default/src/lib/test-case-utils.ts +6 -6
  392. package/templates/default/src/lib/test-run/log-formatter.ts +83 -83
  393. package/templates/default/src/lib/test-run/report-parser.ts +352 -352
  394. package/templates/default/src/lib/test-run/test-run-executor.ts +13 -13
  395. package/templates/default/src/lib/test-run/winston-logger.ts +65 -64
  396. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -42
  397. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -95
  398. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -160
  399. package/templates/default/src/lib/utils/node-param-validation.ts +81 -81
  400. package/templates/default/src/lib/utils/template-step-file-generator.ts +2 -2
  401. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -166
  402. package/templates/default/src/lib/utils.ts +31 -31
  403. package/templates/default/src/services/dashboard/dashboard-service.test.ts +106 -0
  404. package/templates/default/src/services/dashboard/dashboard-service.ts +173 -0
  405. package/templates/default/src/services/environment/environment-service.test.ts +137 -0
  406. package/templates/default/src/services/environment/environment-service.ts +96 -0
  407. package/templates/default/src/services/locator/locator-path-utils.test.ts +14 -0
  408. package/templates/default/src/services/locator/locator-path-utils.ts +14 -0
  409. package/templates/default/src/services/locator/locator-service.test.ts +63 -0
  410. package/templates/default/src/services/locator/locator-service.ts +479 -0
  411. package/templates/default/src/services/locator/locator-sync-utils.test.ts +19 -0
  412. package/templates/default/src/services/locator/locator-sync-utils.ts +21 -0
  413. package/templates/default/src/services/locator-group/locator-group-service.test.ts +123 -0
  414. package/templates/default/src/services/locator-group/locator-group-service.ts +180 -0
  415. package/templates/default/src/services/module/module-service.test.ts +89 -0
  416. package/templates/default/src/services/module/module-service.ts +66 -0
  417. package/templates/default/src/services/report/report-service.test.ts +244 -0
  418. package/templates/default/src/services/report/report-service.ts +438 -0
  419. package/templates/default/src/services/shared/constants.ts +2 -0
  420. package/templates/default/src/services/shared/errors.test.ts +38 -0
  421. package/templates/default/src/services/shared/errors.ts +44 -0
  422. package/templates/default/src/services/shared/index.ts +7 -0
  423. package/templates/default/src/services/tag/tag-service.test.ts +22 -0
  424. package/templates/default/src/services/tag/tag-service.ts +41 -0
  425. package/templates/default/src/services/template-step/template-step-service.test.ts +22 -0
  426. package/templates/default/src/services/template-step/template-step-service.ts +171 -0
  427. package/templates/default/src/services/template-step-group/template-step-group-service.test.ts +22 -0
  428. package/templates/default/src/services/template-step-group/template-step-group-service.ts +81 -0
  429. package/templates/default/src/services/template-test-case/template-test-case-service.test.ts +22 -0
  430. package/templates/default/src/services/template-test-case/template-test-case-service.ts +128 -0
  431. package/templates/default/src/services/test-case/test-case-service.test.ts +175 -0
  432. package/templates/default/src/services/test-case/test-case-service.ts +298 -0
  433. package/templates/default/src/services/test-run/test-run-helpers.ts +61 -0
  434. package/templates/default/src/services/test-run/test-run-service.test.ts +647 -0
  435. package/templates/default/src/services/test-run/test-run-service.ts +917 -0
  436. package/templates/default/src/services/test-suite/test-suite-service.test.ts +127 -0
  437. package/templates/default/src/services/test-suite/test-suite-service.ts +197 -0
  438. package/templates/default/src/types/diagram/diagram.ts +34 -34
  439. package/templates/default/src/types/diagram/template-step.ts +11 -11
  440. package/templates/default/src/types/executor/browser.type.ts +1 -1
  441. package/templates/default/src/types/form/actionHandler.ts +19 -6
  442. package/templates/default/src/types/locator/locator.type.ts +11 -11
  443. package/templates/default/src/types/step/step.type.ts +1 -1
  444. package/templates/default/src/types/table/data-table.ts +6 -6
  445. package/templates/default/tailwind.config.ts +62 -62
  446. package/templates/default/tsconfig.json +1 -1
  447. package/dist/cli.e2e.test.d.ts +0 -2
  448. package/dist/cli.e2e.test.d.ts.map +0 -1
  449. package/dist/cli.e2e.test.js +0 -73
  450. package/dist/cli.e2e.test.js.map +0 -1
  451. package/dist/config.test.d.ts +0 -2
  452. package/dist/config.test.d.ts.map +0 -1
  453. package/dist/config.test.js +0 -65
  454. package/dist/config.test.js.map +0 -1
  455. package/dist/copy-template.test.d.ts +0 -2
  456. package/dist/copy-template.test.d.ts.map +0 -1
  457. package/dist/copy-template.test.js +0 -71
  458. package/dist/copy-template.test.js.map +0 -1
  459. package/dist/download-repo.test.d.ts +0 -2
  460. package/dist/download-repo.test.d.ts.map +0 -1
  461. package/dist/download-repo.test.js +0 -14
  462. package/dist/download-repo.test.js.map +0 -1
  463. package/dist/install.test.d.ts +0 -2
  464. package/dist/install.test.d.ts.map +0 -1
  465. package/dist/install.test.js +0 -119
  466. package/dist/install.test.js.map +0 -1
  467. package/dist/prompts.test.d.ts +0 -2
  468. package/dist/prompts.test.d.ts.map +0 -1
  469. package/dist/prompts.test.js +0 -58
  470. package/dist/prompts.test.js.map +0 -1
  471. package/templates/default/src/actions/conflict/conflict.action.ts +0 -33
  472. package/templates/default/src/actions/review/review-actions.ts +0 -147
  473. package/templates/default/src/actions/user/user-actions.ts +0 -13
  474. package/templates/default/src/app/(base)/locators/locator-form.tsx +0 -163
  475. package/templates/default/src/app/(base)/reviews/create/page.tsx +0 -26
  476. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +0 -15
  477. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +0 -26
  478. package/templates/default/src/app/(base)/reviews/page.tsx +0 -26
  479. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +0 -26
  480. package/templates/default/src/app/(base)/reviews/review-form.tsx +0 -11
  481. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +0 -9
  482. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +0 -9
  483. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +0 -15
  484. package/templates/default/src/constants/form-opts/locator-form-opts.ts +0 -20
  485. package/templates/default/src/constants/form-opts/review-form-opts.ts +0 -23
@@ -1,127 +1,127 @@
1
- 'use client'
2
-
3
- import { deleteTestRunAction, getTestRunByIdAction } from '@/actions/test-run/test-run-actions'
4
- import { DataTable } from '@/components/ui/data-table'
5
- import { Environment, Tag, TestRun, TestRunTestCase, TestRunStatus } from '@prisma/client'
6
- import React, { useEffect, useState, useRef, useMemo } from 'react'
7
- import { testRunTableCols } from './test-run-table-columns'
8
-
9
- type TestRunData = TestRun & { testCases: TestRunTestCase[]; tags: Tag[]; environment: Environment }
10
-
11
- interface TestRunTableProps {
12
- initialData: TestRunData[]
13
- filter: 'recentFailed' | 'all'
14
- }
15
-
16
- const TestRunTable = ({ initialData, filter = 'all' }: TestRunTableProps) => {
17
- const [testRuns, setTestRuns] = useState<TestRunData[]>(initialData)
18
- const pollingRef = useRef<NodeJS.Timeout | null>(null)
19
-
20
- // Memoize running test run IDs to avoid unnecessary effect re-runs
21
- // Include RUNNING, QUEUED, and CANCELLING statuses for polling
22
- const runningTestRunIds = useMemo(() => {
23
- return testRuns
24
- .filter(
25
- tr =>
26
- tr.status === TestRunStatus.RUNNING ||
27
- tr.status === TestRunStatus.QUEUED ||
28
- tr.status === TestRunStatus.CANCELLING,
29
- )
30
- .map(tr => tr.id)
31
- }, [testRuns])
32
-
33
- // Create a stable dependency string for useEffect
34
- const runningTestRunIdsKey = useMemo(() => runningTestRunIds.join(','), [runningTestRunIds])
35
-
36
- // Poll for status updates while test runs are running
37
- useEffect(() => {
38
- // Clear any existing polling interval
39
- if (pollingRef.current) {
40
- clearInterval(pollingRef.current)
41
- pollingRef.current = null
42
- }
43
-
44
- // If no running test runs, don't poll
45
- if (runningTestRunIds.length === 0) {
46
- return
47
- }
48
-
49
- pollingRef.current = setInterval(async () => {
50
- try {
51
- // Poll only running test runs in parallel
52
- const updatePromises = runningTestRunIds.map(async id => {
53
- const { data, error } = await getTestRunByIdAction(id)
54
- if (error || !data) {
55
- return null
56
- }
57
- // getTestRunByIdAction returns testCases with nested testCase, but we need to flatten it
58
- const testRun = data as TestRun & {
59
- testCases: (TestRunTestCase & { testCase: { title: string } })[]
60
- tags: Tag[]
61
- environment: Environment
62
- }
63
- // Convert to the format expected by the table (flatten testCases)
64
- return {
65
- ...testRun,
66
- testCases: testRun.testCases.map(tc => ({
67
- id: tc.id,
68
- testRunId: tc.testRunId,
69
- testCaseId: tc.testCaseId,
70
- status: tc.status,
71
- result: tc.result,
72
- })) as TestRunTestCase[],
73
- } as TestRunData
74
- })
75
-
76
- const updates = await Promise.all(updatePromises)
77
- const validUpdates = updates.filter((u): u is TestRunData => u !== null)
78
-
79
- // Update only the changed test runs
80
- setTestRuns(prev =>
81
- prev.map(tr => {
82
- const update = validUpdates.find(u => u.id === tr.id)
83
- return update || tr
84
- }),
85
- )
86
-
87
- // Check if any are still running (including CANCELLING)
88
- const stillRunning = validUpdates.filter(
89
- u =>
90
- u.status === TestRunStatus.RUNNING ||
91
- u.status === TestRunStatus.QUEUED ||
92
- u.status === TestRunStatus.CANCELLING,
93
- )
94
-
95
- // If no test runs are running anymore, stop polling
96
- if (stillRunning.length === 0 && pollingRef.current) {
97
- clearInterval(pollingRef.current)
98
- pollingRef.current = null
99
- }
100
- } catch (error) {
101
- console.error('Error polling test runs status:', error)
102
- }
103
- }, 2000) // Poll every 2 seconds
104
-
105
- return () => {
106
- if (pollingRef.current) {
107
- clearInterval(pollingRef.current)
108
- pollingRef.current = null
109
- }
110
- }
111
- }, [runningTestRunIdsKey, runningTestRunIds])
112
-
113
- return (
114
- <>
115
- <DataTable
116
- columns={testRunTableCols}
117
- data={testRuns}
118
- filterColumn="name"
119
- filterPlaceholder="Filter by name..."
120
- deleteAction={deleteTestRunAction}
121
- createLink={filter === 'all' ? '/test-runs/create' : undefined}
122
- />
123
- </>
124
- )
125
- }
126
-
127
- export default TestRunTable
1
+ 'use client'
2
+
3
+ import { deleteTestRunAction, getTestRunByIdAction } from '@/actions/test-run/test-run-actions'
4
+ import { DataTable } from '@/components/ui/data-table'
5
+ import { Environment, Tag, TestRun, TestRunTestCase, TestRunStatus } from '@prisma/client'
6
+ import React, { useEffect, useState, useRef, useMemo } from 'react'
7
+ import { testRunTableCols } from './test-run-table-columns'
8
+
9
+ type TestRunData = TestRun & { testCases: TestRunTestCase[]; tags: Tag[]; environment: Environment }
10
+
11
+ interface TestRunTableProps {
12
+ initialData: TestRunData[]
13
+ filter: 'recentFailed' | 'all'
14
+ }
15
+
16
+ const TestRunTable = ({ initialData, filter = 'all' }: TestRunTableProps) => {
17
+ const [testRuns, setTestRuns] = useState<TestRunData[]>(initialData)
18
+ const pollingRef = useRef<NodeJS.Timeout | null>(null)
19
+
20
+ // Memoize running test run IDs to avoid unnecessary effect re-runs
21
+ // Include RUNNING, QUEUED, and CANCELLING statuses for polling
22
+ const runningTestRunIds = useMemo(() => {
23
+ return testRuns
24
+ .filter(
25
+ tr =>
26
+ tr.status === TestRunStatus.RUNNING ||
27
+ tr.status === TestRunStatus.QUEUED ||
28
+ tr.status === TestRunStatus.CANCELLING,
29
+ )
30
+ .map(tr => tr.id)
31
+ }, [testRuns])
32
+
33
+ // Create a stable dependency string for useEffect
34
+ const runningTestRunIdsKey = useMemo(() => runningTestRunIds.join(','), [runningTestRunIds])
35
+
36
+ // Poll for status updates while test runs are running
37
+ useEffect(() => {
38
+ // Clear any existing polling interval
39
+ if (pollingRef.current) {
40
+ clearInterval(pollingRef.current)
41
+ pollingRef.current = null
42
+ }
43
+
44
+ // If no running test runs, don't poll
45
+ if (runningTestRunIds.length === 0) {
46
+ return
47
+ }
48
+
49
+ pollingRef.current = setInterval(async () => {
50
+ try {
51
+ // Poll only running test runs in parallel
52
+ const updatePromises = runningTestRunIds.map(async id => {
53
+ const { data, error } = await getTestRunByIdAction(id)
54
+ if (error || !data) {
55
+ return null
56
+ }
57
+ // getTestRunByIdAction returns testCases with nested testCase, but we need to flatten it
58
+ const testRun = data as TestRun & {
59
+ testCases: (TestRunTestCase & { testCase: { title: string } })[]
60
+ tags: Tag[]
61
+ environment: Environment
62
+ }
63
+ // Convert to the format expected by the table (flatten testCases)
64
+ return {
65
+ ...testRun,
66
+ testCases: testRun.testCases.map(tc => ({
67
+ id: tc.id,
68
+ testRunId: tc.testRunId,
69
+ testCaseId: tc.testCaseId,
70
+ status: tc.status,
71
+ result: tc.result,
72
+ })) as TestRunTestCase[],
73
+ } as TestRunData
74
+ })
75
+
76
+ const updates = await Promise.all(updatePromises)
77
+ const validUpdates = updates.filter((u): u is TestRunData => u !== null)
78
+
79
+ // Update only the changed test runs
80
+ setTestRuns(prev =>
81
+ prev.map(tr => {
82
+ const update = validUpdates.find(u => u.id === tr.id)
83
+ return update || tr
84
+ }),
85
+ )
86
+
87
+ // Check if any are still running (including CANCELLING)
88
+ const stillRunning = validUpdates.filter(
89
+ u =>
90
+ u.status === TestRunStatus.RUNNING ||
91
+ u.status === TestRunStatus.QUEUED ||
92
+ u.status === TestRunStatus.CANCELLING,
93
+ )
94
+
95
+ // If no test runs are running anymore, stop polling
96
+ if (stillRunning.length === 0 && pollingRef.current) {
97
+ clearInterval(pollingRef.current)
98
+ pollingRef.current = null
99
+ }
100
+ } catch (error) {
101
+ console.error('Error polling test runs status:', error)
102
+ }
103
+ }, 2000) // Poll every 2 seconds
104
+
105
+ return () => {
106
+ if (pollingRef.current) {
107
+ clearInterval(pollingRef.current)
108
+ pollingRef.current = null
109
+ }
110
+ }
111
+ }, [runningTestRunIdsKey, runningTestRunIds])
112
+
113
+ return (
114
+ <>
115
+ <DataTable
116
+ columns={testRunTableCols}
117
+ data={testRuns}
118
+ filterColumn="name"
119
+ filterPlaceholder="Filter by name..."
120
+ deleteAction={deleteTestRunAction}
121
+ createLink={filter === 'all' ? '/test-runs/create' : undefined}
122
+ />
123
+ </>
124
+ )
125
+ }
126
+
127
+ export default TestRunTable
@@ -0,0 +1,74 @@
1
+ 'use client'
2
+
3
+ import { useCallback, useEffect, useRef } from 'react'
4
+
5
+ import { checkTestRunNameUniqueAction } from '@/actions/test-run/test-run-actions'
6
+
7
+ type NameValidationResult = {
8
+ isValid: boolean
9
+ error?: string
10
+ }
11
+
12
+ export function useTestRunNameValidation(id?: string) {
13
+ const debounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
14
+
15
+ const checkNameUniqueness = useCallback(
16
+ async (name: string): Promise<NameValidationResult> => {
17
+ if (!name) {
18
+ return { isValid: true }
19
+ }
20
+
21
+ try {
22
+ const response = await checkTestRunNameUniqueAction(name, id)
23
+
24
+ if (response.status !== 200) {
25
+ return { isValid: true }
26
+ }
27
+
28
+ const isUnique =
29
+ typeof response.data === 'object' &&
30
+ response.data !== null &&
31
+ 'isUnique' in response.data &&
32
+ typeof response.data.isUnique === 'boolean'
33
+ ? response.data.isUnique
34
+ : true
35
+
36
+ return isUnique
37
+ ? { isValid: true }
38
+ : {
39
+ isValid: false,
40
+ error: 'A test run with this name already exists. Please choose a different name.',
41
+ }
42
+ } catch {
43
+ return { isValid: true }
44
+ }
45
+ },
46
+ [id],
47
+ )
48
+
49
+ useEffect(() => {
50
+ return () => {
51
+ if (debounceTimeoutRef.current) {
52
+ clearTimeout(debounceTimeoutRef.current)
53
+ }
54
+ }
55
+ }, [])
56
+
57
+ const debouncedNameValidation = useCallback(
58
+ (name: string): Promise<NameValidationResult> =>
59
+ new Promise(resolve => {
60
+ if (debounceTimeoutRef.current) {
61
+ clearTimeout(debounceTimeoutRef.current)
62
+ }
63
+
64
+ debounceTimeoutRef.current = setTimeout(async () => {
65
+ resolve(await checkNameUniqueness(name))
66
+ }, 500)
67
+ }),
68
+ [checkNameUniqueness],
69
+ )
70
+
71
+ return {
72
+ debouncedNameValidation,
73
+ }
74
+ }
@@ -3,28 +3,33 @@ import { TestSuiteForm } from '../test-suite-form'
3
3
  import PageHeader from '@/components/typography/page-header'
4
4
  import HeaderSubtitle from '@/components/typography/page-header-subtitle'
5
5
  import { getAllTestCasesAction } from '@/actions/test-case/test-case-actions'
6
- import { TestCasePickerRow } from '@/types/test-case-picker'
7
- import { Module, Tag } from '@prisma/client'
8
6
  import { getAllModulesAction } from '@/actions/modules/module-actions'
9
7
  import { getAllTagsAction } from '@/actions/tags/tag-actions'
10
8
  import { Metadata } from 'next'
11
9
 
10
+ import { getModuleRows, getTagRows, getTestCasePickerRows } from '../test-suite-helpers'
11
+
12
12
  export const metadata: Metadata = {
13
13
  title: 'Appraise | Create Test Suite',
14
14
  description: 'Create a new test suite to run your tests against',
15
15
  }
16
16
 
17
17
  const CreateTestSuite = async () => {
18
- const { data: testCases, error: testCasesError } = await getAllTestCasesAction()
19
-
20
- const { data: moduleList, error: moduleListError } = await getAllModulesAction()
18
+ const [testCasesResponse, moduleListResponse, tagsResponse] = await Promise.all([
19
+ getAllTestCasesAction(),
20
+ getAllModulesAction(),
21
+ getAllTagsAction(),
22
+ ])
21
23
 
22
- const { data: tags, error: tagsError } = await getAllTagsAction()
23
-
24
- if (testCasesError || moduleListError || tagsError) {
25
- return <div>Error: {testCasesError || moduleListError || tagsError}</div>
24
+ const loadError = testCasesResponse.error || moduleListResponse.error || tagsResponse.error
25
+ if (loadError) {
26
+ return <div>Error: {loadError}</div>
26
27
  }
27
28
 
29
+ const testCases = getTestCasePickerRows(testCasesResponse.data)
30
+ const moduleList = getModuleRows(moduleListResponse.data)
31
+ const tags = getTagRows(tagsResponse.data)
32
+
28
33
  return (
29
34
  <>
30
35
  <div className="mb-8">
@@ -35,9 +40,9 @@ const CreateTestSuite = async () => {
35
40
  successTitle="Suite created"
36
41
  successMessage="Test suite created successfully"
37
42
  onSubmitAction={createTestSuiteAction}
38
- testCases={testCases as TestCasePickerRow[]}
39
- moduleList={moduleList as Module[]}
40
- tags={tags as Tag[]}
43
+ testCases={testCases}
44
+ moduleList={moduleList}
45
+ tags={tags}
41
46
  />
42
47
  </>
43
48
  )
@@ -1,13 +1,12 @@
1
1
  import { getTestSuiteByIdAction, updateTestSuiteAction } from '@/actions/test-suite/test-suite-actions'
2
2
  import { TestSuiteForm } from '../../test-suite-form'
3
3
  import React from 'react'
4
- import { TestSuite, Tag } from '@prisma/client'
5
4
  import { getAllTestCasesAction } from '@/actions/test-case/test-case-actions'
6
- import { Module, TestCase } from '@prisma/client'
7
5
  import { getAllModulesAction } from '@/actions/modules/module-actions'
8
6
  import { getAllTagsAction } from '@/actions/tags/tag-actions'
9
7
  import { Metadata } from 'next'
10
- import { TestCasePickerRow } from '@/types/test-case-picker'
8
+
9
+ import { getEditableTestSuite, getModuleRows, getTagRows, getTestCasePickerRows } from '../../test-suite-helpers'
11
10
 
12
11
  export const metadata: Metadata = {
13
12
  title: 'Appraise | Modify Test Suite',
@@ -16,23 +15,27 @@ export const metadata: Metadata = {
16
15
 
17
16
  const ModifyTestSuite = async ({ params }: { params: Promise<{ id: string }> }) => {
18
17
  const { id } = await params
19
- const { data: testSuite, error } = await getTestSuiteByIdAction(id)
20
- const { data: testCases } = await getAllTestCasesAction()
21
-
22
- const { data: moduleList, error: moduleListError } = await getAllModulesAction()
23
-
24
- const { data: tags, error: tagsError } = await getAllTagsAction()
25
-
26
- if (error || moduleListError || tagsError) {
27
- return <div>Error: {error || moduleListError || tagsError}</div>
18
+ const [testSuiteResponse, testCasesResponse, moduleListResponse, tagsResponse] = await Promise.all([
19
+ getTestSuiteByIdAction(id),
20
+ getAllTestCasesAction(),
21
+ getAllModulesAction(),
22
+ getAllTagsAction(),
23
+ ])
24
+
25
+ const loadError = testSuiteResponse.error || moduleListResponse.error || tagsResponse.error
26
+ if (loadError) {
27
+ return <div>Error: {loadError}</div>
28
28
  }
29
29
 
30
- const testSuiteData = testSuite as TestSuite & {
31
- testCases: TestCase[]
32
- module: Module
33
- tags: Tag[]
30
+ const testSuiteData = getEditableTestSuite(testSuiteResponse.data)
31
+ if (!testSuiteData) {
32
+ return <div>Error: Invalid test suite</div>
34
33
  }
35
34
 
35
+ const testCases = getTestCasePickerRows(testCasesResponse.data)
36
+ const moduleList = getModuleRows(moduleListResponse.data)
37
+ const tags = getTagRows(tagsResponse.data)
38
+
36
39
  return (
37
40
  <TestSuiteForm
38
41
  defaultValues={{
@@ -46,9 +49,9 @@ const ModifyTestSuite = async ({ params }: { params: Promise<{ id: string }> })
46
49
  successMessage="Test suite updated successfully"
47
50
  onSubmitAction={updateTestSuiteAction}
48
51
  id={id}
49
- testCases={testCases as TestCasePickerRow[]}
50
- moduleList={moduleList as Module[]}
51
- tags={tags as Tag[]}
52
+ testCases={testCases}
53
+ moduleList={moduleList}
54
+ tags={tags}
52
55
  />
53
56
  )
54
57
  }
@@ -5,14 +5,14 @@ import TestSuiteTable from './test-suite-table'
5
5
  import { Suspense } from 'react'
6
6
  import DataTableSkeleton from '@/components/loading-skeleton/data-table/data-table-skeleton'
7
7
  import { getAllTestSuitesAction } from '@/actions/test-suite/test-suite-actions'
8
- import { Tag, TestCase, TestSuite } from '@prisma/client'
9
8
  import InfoGrid from '@/components/data-visualization/info-grid'
10
9
  import EmptyState from '@/components/data-state/empty-state'
11
10
  import { Metadata } from 'next'
12
11
  import { TubePlus } from '@/assets/icons/tube-plus'
13
12
  import EmptyTube from '@/assets/icons/empty-tube'
14
13
  import { Tag as TagIcon } from 'lucide-react'
15
- import { getFilterTags } from '@/lib/tag-utils'
14
+
15
+ import { buildTestSuiteInfoCards, getTestSuiteTableRows } from './test-suite-helpers'
16
16
 
17
17
  export const metadata: Metadata = {
18
18
  title: 'Appraise | Test Suites',
@@ -26,60 +26,18 @@ const TestSuites = async () => {
26
26
  return <div>Error: {testSuitesError}</div>
27
27
  }
28
28
 
29
- const testSuitesData = testSuites as (TestSuite & { testCases: TestCase[]; tags: Tag[] })[]
30
- const emptyTestSuites = testSuitesData.filter(testSuite => testSuite.testCases.length === 0)
31
- const latestCreatedTestSuite = [...testSuitesData].sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0]
32
- const tagSuiteCountMap = new Map<string, { name: string; suiteCount: number }>()
33
-
34
- for (const testSuite of testSuitesData) {
35
- for (const tag of getFilterTags(testSuite.tags)) {
36
- const currentTag = tagSuiteCountMap.get(tag.id)
37
-
38
- if (currentTag) {
39
- currentTag.suiteCount += 1
40
- } else {
41
- tagSuiteCountMap.set(tag.id, {
42
- name: tag.name,
43
- suiteCount: 1,
44
- })
45
- }
46
- }
47
- }
48
-
49
- const mostCommonTagWithSuites = [...tagSuiteCountMap.values()].sort((a, b) => {
50
- if (b.suiteCount !== a.suiteCount) {
51
- return b.suiteCount - a.suiteCount
52
- }
53
-
54
- return a.name.localeCompare(b.name)
55
- })[0]
56
- const infoCards = [
57
- {
58
- showHighlightGroup: testSuitesData.length > 0,
59
- highlight: emptyTestSuites.length.toString(),
60
- legend: 'Empty test suite(s)',
61
- defaultText: 'Empty test suites count. Will update when test suites are created.',
62
- icon: <EmptyTube className="h-12 w-12 rounded-sm bg-muted p-2" />,
63
- },
64
- {
65
- showHighlightGroup: testSuitesData.length > 0,
66
- highlight: latestCreatedTestSuite ? latestCreatedTestSuite.name : 'N/A',
67
- legend: 'Latest test suite',
68
- defaultText: 'Latest created test suite. Will update when test suites are created.',
69
- icon: <TubePlus className="h-12 w-12 rounded-sm bg-muted p-2" />,
70
- },
71
- ...(mostCommonTagWithSuites
72
- ? [
73
- {
74
- showHighlightGroup: true,
75
- highlight: mostCommonTagWithSuites.name,
76
- legend: 'Most common tag',
77
- defaultText: 'Most common suite tag will appear here when test suites have tags.',
78
- icon: <TagIcon className="h-12 w-12 rounded-sm bg-muted p-2" />,
79
- },
80
- ]
81
- : []),
82
- ]
29
+ const testSuitesData = getTestSuiteTableRows(testSuites)
30
+ const infoCards = buildTestSuiteInfoCards(testSuitesData).map(card => ({
31
+ ...card,
32
+ icon:
33
+ card.legend === 'Empty test suite(s)' ? (
34
+ <EmptyTube className="h-12 w-12 rounded-sm bg-muted p-2" />
35
+ ) : card.legend === 'Latest test suite' ? (
36
+ <TubePlus className="h-12 w-12 rounded-sm bg-muted p-2" />
37
+ ) : (
38
+ <TagIcon className="h-12 w-12 rounded-sm bg-muted p-2" />
39
+ ),
40
+ }))
83
41
 
84
42
  if (testSuitesData.length === 0) {
85
43
  return (
@@ -0,0 +1,127 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { render, screen, waitFor } from '@testing-library/react'
4
+ import userEvent from '@testing-library/user-event'
5
+ import { describe, expect, it, vi } from 'vitest'
6
+
7
+ import { TestSuiteForm } from './test-suite-form'
8
+
9
+ const { push, toast } = vi.hoisted(() => ({
10
+ push: vi.fn(),
11
+ toast: vi.fn(),
12
+ }))
13
+
14
+ vi.mock('next/navigation', () => ({
15
+ useRouter: () => ({
16
+ push,
17
+ }),
18
+ }))
19
+
20
+ vi.mock('@/hooks/use-toast', () => ({
21
+ toast,
22
+ }))
23
+
24
+ vi.mock('@/components/test-case/test-case-picker', () => ({
25
+ __esModule: true,
26
+ default: ({
27
+ onSave,
28
+ }: {
29
+ onSave: (value: string[]) => void
30
+ }) => (
31
+ <button type="button" onClick={() => onSave(['case-1'])}>
32
+ Select Cases
33
+ </button>
34
+ ),
35
+ }))
36
+
37
+ vi.mock('@/components/ui/multi-select-with-preview', () => ({
38
+ __esModule: true,
39
+ default: ({
40
+ onSelectChange,
41
+ }: {
42
+ onSelectChange: (value: string[]) => void
43
+ }) => (
44
+ <button type="button" onClick={() => onSelectChange(['tag-1'])}>
45
+ Select Tags
46
+ </button>
47
+ ),
48
+ }))
49
+
50
+ vi.mock('@/components/ui/select', async () => {
51
+ const React = await import('react')
52
+ const SelectContext = React.createContext<{ value?: string; onValueChange: (value: string) => void }>({
53
+ value: '',
54
+ onValueChange: () => {},
55
+ })
56
+
57
+ return {
58
+ Select: ({
59
+ value,
60
+ onValueChange,
61
+ children,
62
+ }: {
63
+ value?: string
64
+ onValueChange: (value: string) => void
65
+ children: React.ReactNode
66
+ }) => <SelectContext.Provider value={{ value, onValueChange }}>{children}</SelectContext.Provider>,
67
+ SelectTrigger: ({ id, children }: { id?: string; children: React.ReactNode }) => <div id={id}>{children}</div>,
68
+ SelectValue: ({ placeholder }: { placeholder?: string }) => <span>{placeholder}</span>,
69
+ SelectContent: ({ children }: { children: React.ReactNode }) => {
70
+ const { value, onValueChange } = React.useContext(SelectContext)
71
+ return (
72
+ <select aria-label="Module" value={value || ''} onChange={event => onValueChange(event.target.value)}>
73
+ <option value="">Select a module</option>
74
+ {children}
75
+ </select>
76
+ )
77
+ },
78
+ SelectItem: ({ value, children }: { value: string; children: React.ReactNode }) => (
79
+ <option value={value}>{children}</option>
80
+ ),
81
+ }
82
+ })
83
+
84
+ describe('TestSuiteForm', () => {
85
+ it('submits valid values and navigates back to the suites list', async () => {
86
+ const user = userEvent.setup()
87
+ const onSubmitAction = vi.fn().mockResolvedValue({ status: 200 })
88
+
89
+ render(
90
+ <TestSuiteForm
91
+ successTitle="Suite created"
92
+ successMessage="Test suite created successfully"
93
+ onSubmitAction={onSubmitAction}
94
+ testCases={[]}
95
+ moduleList={[{ id: 'module-1', name: 'Payments' } as never]}
96
+ tags={[{ id: 'tag-1', name: 'smoke' } as never]}
97
+ />,
98
+ )
99
+
100
+ await user.type(screen.getByLabelText('Name'), 'Smoke Suite')
101
+ await user.type(screen.getByLabelText('Description'), 'Important tests')
102
+ await user.click(screen.getByRole('button', { name: 'Select Cases' }))
103
+ await user.selectOptions(screen.getByRole('combobox', { name: 'Module' }), 'module-1')
104
+ await user.click(screen.getByRole('button', { name: 'Select Tags' }))
105
+ await user.click(screen.getByRole('button', { name: 'Save' }))
106
+
107
+ await waitFor(() => {
108
+ expect(onSubmitAction).toHaveBeenCalledWith(
109
+ undefined,
110
+ {
111
+ name: 'Smoke Suite',
112
+ description: 'Important tests',
113
+ testCases: ['case-1'],
114
+ moduleId: 'module-1',
115
+ tagIds: ['tag-1'],
116
+ },
117
+ undefined,
118
+ )
119
+ })
120
+
121
+ expect(toast).toHaveBeenCalledWith({
122
+ title: 'Suite created',
123
+ description: 'Test suite created successfully',
124
+ })
125
+ expect(push).toHaveBeenCalledWith('/test-suites')
126
+ })
127
+ })