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,28 +1,67 @@
1
1
  'use client'
2
2
 
3
3
  import { Button } from '@/components/ui/button'
4
+ import ErrorMessage from '@/components/form/error-message'
4
5
  import TestSuitePicker from '@/components/test-suite/test-suite-picker'
5
6
  import { Input } from '@/components/ui/input'
6
7
  import { Label } from '@/components/ui/label'
7
8
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
8
9
  import { MultiSelect } from '@/components/ui/multi-select'
9
- import { formOpts, TestRun } from '@/constants/form-opts/test-run-form-opts'
10
+ import { formOpts, type TestRun } from '@/constants/form-opts/test-run-form-opts'
10
11
  import { toast } from '@/hooks/use-toast'
11
- import { ActionResponse } from '@/types/form/actionHandler'
12
12
  import { BrowserEngine, Environment, Tag } from '@prisma/client'
13
13
  import { useForm } from '@tanstack/react-form'
14
14
  import { useRouter } from 'next/navigation'
15
- import { useState, useRef, useEffect, useCallback } from 'react'
16
- import { z } from 'zod'
15
+ import { useRef, useState } from 'react'
17
16
  import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
18
17
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
19
- import { checkTestRunNameUniqueAction } from '@/actions/test-run/test-run-actions'
20
18
  import { Info, Play } from 'lucide-react'
21
- import { TestSuitePickerRow } from '@/types/test-suite-picker'
19
+ import type { TestSuitePickerRow } from '@/types/test-suite-picker'
20
+ import {
21
+ buildTestRunSubmitValue,
22
+ getActionErrorMessage,
23
+ getBrowserEngineOptions,
24
+ getFieldErrorMessage,
25
+ getInitialTestSelectionType,
26
+ getTestRunSuccessPath,
27
+ testRunFieldValidators,
28
+ testRunQuickTips,
29
+ testSelectionTypes,
30
+ type TestRunFormSubmitAction,
31
+ type TestSelectionType,
32
+ validateTagSelections,
33
+ validateTestSuiteSelections,
34
+ } from './test-run-form-helpers'
35
+ import { useTestRunNameValidation } from './use-test-run-name-validation'
22
36
 
23
- enum TestSelectionType {
24
- TAGS = 'tags',
25
- TEST_SUITES = 'testSuites',
37
+ type TestRunFormProps = {
38
+ defaultValues?: TestRun
39
+ successTitle: string
40
+ successMessage: string
41
+ testSuites: TestSuitePickerRow[]
42
+ environments: Environment[]
43
+ tags: Tag[]
44
+ id?: string
45
+ onSubmitAction: TestRunFormSubmitAction
46
+ }
47
+
48
+ type TestRunFieldErrorsProps = {
49
+ errors: unknown[]
50
+ isTouched: boolean
51
+ }
52
+
53
+ function TestRunFieldErrors({ errors, isTouched }: TestRunFieldErrorsProps) {
54
+ if (!isTouched) {
55
+ return null
56
+ }
57
+
58
+ return (
59
+ <div className="flex flex-col gap-1" aria-live="polite">
60
+ {errors.map((error, index) => (
61
+ <ErrorMessage key={`${String(error)}-${index}`} message={getFieldErrorMessage(error)} visible={true} />
62
+ ))}
63
+ </div>
64
+ )
26
65
  }
27
66
 
28
67
  const TestRunForm = ({
@@ -34,125 +73,58 @@ const TestRunForm = ({
34
73
  tags,
35
74
  id,
36
75
  onSubmitAction,
37
- }: {
38
- defaultValues?: TestRun
39
- successTitle: string
40
- successMessage: string
41
- testSuites: TestSuitePickerRow[]
42
- environments: Environment[]
43
- tags: Tag[]
44
- id?: string
45
- onSubmitAction: (_prev: unknown, value: TestRun, id?: string) => Promise<ActionResponse>
46
- }) => {
76
+ }: TestRunFormProps) => {
47
77
  const router = useRouter()
78
+ const { debouncedNameValidation } = useTestRunNameValidation(id)
48
79
  const [testSelectionType, setTestSelectionType] = useState<TestSelectionType>(() =>
49
- defaultValues?.testSuites?.length ? TestSelectionType.TEST_SUITES : TestSelectionType.TAGS,
80
+ getInitialTestSelectionType(defaultValues),
50
81
  )
51
82
  const testSelectionTypeRef = useRef<TestSelectionType>(testSelectionType)
52
- const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null)
53
-
54
- // Keep ref in sync with state
55
- useEffect(() => {
56
- testSelectionTypeRef.current = testSelectionType
57
- }, [testSelectionType])
58
-
59
- // Debounced function to check name uniqueness
60
- // Returns: { isValid: boolean, error?: string }
61
- // isValid: false only if name is confirmed to be duplicate
62
- // error: error message to display (only returned when isValid is false to block submission)
63
- const checkNameUniqueness = useCallback(
64
- async (name: string): Promise<{ isValid: boolean; error?: string }> => {
65
- if (!name || name.length < 1) return { isValid: true }
66
-
67
- try {
68
- const response = await checkTestRunNameUniqueAction(name, id)
69
-
70
- // Handle server errors - allow submission but log the error
71
- if (response.status === 500) {
72
- console.error('Server error checking name uniqueness:', response.error)
73
- // Return valid to allow submission (server will handle validation)
74
- return { isValid: true }
75
- }
76
-
77
- // Handle successful response
78
- if (response.status === 200) {
79
- const isUnique = (response.data as { isUnique: boolean })?.isUnique ?? true
80
- if (!isUnique) {
81
- return {
82
- isValid: false,
83
- error: 'A test run with this name already exists. Please choose a different name.',
84
- }
85
- }
86
- return { isValid: true }
87
- }
88
-
89
- // Unexpected status code - allow submission
90
- console.warn('Unexpected response status when checking name uniqueness:', response.status)
91
- return { isValid: true }
92
- } catch (error) {
93
- console.error('Error checking name uniqueness:', error)
94
- // Allow submission if check fails (network error, etc.)
95
- // Server will handle validation on submit
96
- return { isValid: true }
97
- }
98
- },
99
- [id],
100
- )
101
-
102
- // Debounced validation function
103
- const debouncedNameValidation = useCallback(
104
- (name: string): Promise<{ isValid: boolean; error?: string }> => {
105
- return new Promise(resolve => {
106
- if (debounceTimeoutRef.current) {
107
- clearTimeout(debounceTimeoutRef.current)
108
- }
109
-
110
- debounceTimeoutRef.current = setTimeout(async () => {
111
- const result = await checkNameUniqueness(name)
112
- resolve(result)
113
- }, 500) // 500ms debounce
114
- })
115
- },
116
- [checkNameUniqueness],
117
- )
118
83
 
119
84
  const form = useForm({
120
- defaultValues: defaultValues ?? formOpts?.defaultValues,
85
+ defaultValues: defaultValues ?? formOpts.defaultValues,
86
+ validators: formOpts.validators,
121
87
  onSubmit: async ({ value }) => {
122
- const submitValue = {
123
- ...value,
124
- tags: testSelectionType === TestSelectionType.TAGS ? value.tags : [],
125
- testSuites: testSelectionType === TestSelectionType.TEST_SUITES ? value.testSuites : [],
126
- }
88
+ const submitValue = buildTestRunSubmitValue(value, testSelectionType)
127
89
  const res = await onSubmitAction(undefined, submitValue, id)
128
90
  if (res.status === 200) {
129
91
  toast({
130
92
  title: successTitle,
131
93
  description: successMessage,
132
94
  })
133
- // Redirect to test run detail page if test run ID is available
134
- if (res.data && typeof res.data === 'object' && 'id' in res.data) {
135
- router.push(`/test-runs/${res.data.id}`)
136
- } else {
137
- router.push('/test-runs')
138
- }
95
+ router.push(getTestRunSuccessPath(res.data))
139
96
  }
140
97
  if (res.status === 400) {
141
98
  toast({
142
99
  title: 'Error',
143
- description: res.error,
100
+ description: getActionErrorMessage(res),
144
101
  variant: 'destructive',
145
102
  })
146
103
  }
147
104
  if (res.status === 500) {
148
105
  toast({
149
106
  title: 'Error',
150
- description: res.error,
107
+ description: getActionErrorMessage(res),
151
108
  variant: 'destructive',
152
109
  })
153
110
  }
154
111
  },
155
112
  })
113
+
114
+ const handleSelectionTypeChange = (nextType: TestSelectionType) => {
115
+ testSelectionTypeRef.current = nextType
116
+ setTestSelectionType(nextType)
117
+
118
+ if (nextType === testSelectionTypes.TAGS) {
119
+ form.setFieldValue('testSuites', [])
120
+ } else {
121
+ form.setFieldValue('tags', [])
122
+ }
123
+
124
+ form.validateField('tags', 'change')
125
+ form.validateField('testSuites', 'change')
126
+ }
127
+
156
128
  return (
157
129
  <form
158
130
  onSubmit={e => {
@@ -170,55 +142,30 @@ const TestRunForm = ({
170
142
  </CardHeader>
171
143
  <CardContent>
172
144
  <RadioGroup
173
- defaultValue={testSelectionType}
174
- onValueChange={value => {
175
- const newType = value as TestSelectionType
176
- // Update ref first so validators have the correct value
177
- testSelectionTypeRef.current = newType
178
- setTestSelectionType(newType)
179
- // Clear the other field when switching filter types
180
- if (newType === TestSelectionType.TAGS) {
181
- form.setFieldValue('testSuites', [])
182
- // Validate both fields to clear any errors
183
- form.validateField('tags', 'change')
184
- form.validateField('testSuites', 'change')
185
- } else {
186
- form.setFieldValue('tags', [])
187
- // Validate both fields to clear any errors
188
- form.validateField('tags', 'change')
189
- form.validateField('testSuites', 'change')
190
- }
191
- }}
145
+ value={testSelectionType}
146
+ onValueChange={value => handleSelectionTypeChange(value as TestSelectionType)}
192
147
  className="mb-4 flex gap-4"
193
148
  >
194
149
  <div className="flex items-center space-x-2">
195
- <RadioGroupItem value={TestSelectionType.TAGS} id={TestSelectionType.TAGS} />
196
- <Label htmlFor={TestSelectionType.TAGS}>By Tags</Label>
150
+ <RadioGroupItem value={testSelectionTypes.TAGS} id={testSelectionTypes.TAGS} />
151
+ <Label htmlFor={testSelectionTypes.TAGS}>By Tags</Label>
197
152
  </div>
198
153
  <div className="flex items-center space-x-2">
199
- <RadioGroupItem value={TestSelectionType.TEST_SUITES} id={TestSelectionType.TEST_SUITES} />
200
- <Label htmlFor={TestSelectionType.TEST_SUITES}>By Test Suites</Label>
154
+ <RadioGroupItem value={testSelectionTypes.TEST_SUITES} id={testSelectionTypes.TEST_SUITES} />
155
+ <Label htmlFor={testSelectionTypes.TEST_SUITES}>By Test Suites</Label>
201
156
  </div>
202
157
  </RadioGroup>
203
158
 
204
159
  <form.Field
205
160
  name="tags"
206
161
  validators={{
207
- onChange: ({ value }) => {
208
- const currentSelectionType = testSelectionTypeRef.current
209
- if (currentSelectionType === TestSelectionType.TAGS) {
210
- if (!Array.isArray(value) || value.length === 0) {
211
- return 'Tags are required'
212
- }
213
- }
214
- return undefined
215
- },
162
+ onChange: ({ value }) => validateTagSelections(value, testSelectionTypeRef.current),
216
163
  }}
217
164
  >
218
165
  {field => {
219
166
  return (
220
167
  <div
221
- className={`mb-4 flex flex-col gap-2 ${testSelectionType === TestSelectionType.TAGS ? 'block' : 'hidden'}`}
168
+ className={`mb-4 flex flex-col gap-2 ${testSelectionType === testSelectionTypes.TAGS ? 'block' : 'hidden'}`}
222
169
  >
223
170
  <MultiSelect
224
171
  options={tags.map(tag => ({ label: tag.name, value: tag.id }))}
@@ -226,13 +173,9 @@ const TestRunForm = ({
226
173
  onChange={field.handleChange}
227
174
  placeholder="Select tags"
228
175
  emptyMessage="No tags available"
176
+ label="Test tags"
229
177
  />
230
- {field.state.meta.isTouched &&
231
- field.state.meta.errors.map((error, index) => (
232
- <p key={index} className="text-xs text-pink-500">
233
- {typeof error === 'string' ? error : String(error)}
234
- </p>
235
- ))}
178
+ <TestRunFieldErrors errors={field.state.meta.errors} isTouched={field.state.meta.isTouched} />
236
179
  </div>
237
180
  )
238
181
  }}
@@ -241,28 +184,13 @@ const TestRunForm = ({
241
184
  <form.Field
242
185
  name="testSuites"
243
186
  validators={{
244
- onChange: ({ value }) => {
245
- const currentSelectionType = testSelectionTypeRef.current
246
- if (currentSelectionType === TestSelectionType.TEST_SUITES) {
247
- if (!Array.isArray(value) || value.length === 0) {
248
- return 'At least one test suite is required'
249
- }
250
-
251
- const invalidSuite = value.find(
252
- suiteSelection => !suiteSelection.runAll && suiteSelection.testCaseIds.length === 0,
253
- )
254
- if (invalidSuite) {
255
- return 'Partial suite selections must include at least one test case'
256
- }
257
- }
258
- return undefined
259
- },
187
+ onChange: ({ value }) => validateTestSuiteSelections(value, testSelectionTypeRef.current),
260
188
  }}
261
189
  >
262
190
  {field => {
263
191
  return (
264
192
  <div
265
- className={`mb-4 flex flex-col gap-2 ${testSelectionType === TestSelectionType.TEST_SUITES ? 'block' : 'hidden'}`}
193
+ className={`mb-4 flex flex-col gap-2 ${testSelectionType === testSelectionTypes.TEST_SUITES ? 'block' : 'hidden'}`}
266
194
  >
267
195
  <TestSuitePicker
268
196
  testSuites={testSuites}
@@ -273,12 +201,7 @@ const TestRunForm = ({
273
201
  dialogDescription="Browse suites, expand child test cases, and save the suite-scoped selection for this test run."
274
202
  selectedLabel="Selected test suite(s)"
275
203
  />
276
- {field.state.meta.isTouched &&
277
- field.state.meta.errors.map((error, index) => (
278
- <p key={index} className="text-xs text-pink-500">
279
- {typeof error === 'string' ? error : String(error)}
280
- </p>
281
- ))}
204
+ <TestRunFieldErrors errors={field.state.meta.errors} isTouched={field.state.meta.isTouched} />
282
205
  </div>
283
206
  )
284
207
  }}
@@ -295,12 +218,10 @@ const TestRunForm = ({
295
218
  <form.Field
296
219
  name="name"
297
220
  validators={{
298
- onChange: z.string().min(1, { message: 'Name is required' }),
221
+ onChange: testRunFieldValidators.name,
299
222
  onChangeAsync: async ({ value }) => {
300
223
  if (!value || value.length < 1) return undefined
301
224
  const result = await debouncedNameValidation(value)
302
- // Only return error if validation explicitly failed (duplicate name)
303
- // Server errors are handled by allowing submission and letting server validate
304
225
  if (!result.isValid && result.error) {
305
226
  return result.error
306
227
  }
@@ -318,12 +239,7 @@ const TestRunForm = ({
318
239
  onChange={e => field.handleChange(e.target.value)}
319
240
  placeholder="Enter name for your test run"
320
241
  />
321
- {field.state.meta.isTouched &&
322
- field.state.meta.errors.map((error, index) => (
323
- <p key={index} className="text-xs text-pink-500">
324
- {typeof error === 'string' ? error : error?.message || String(error)}
325
- </p>
326
- ))}
242
+ <TestRunFieldErrors errors={field.state.meta.errors} isTouched={field.state.meta.isTouched} />
327
243
  </div>
328
244
  )
329
245
  }}
@@ -331,7 +247,7 @@ const TestRunForm = ({
331
247
  <form.Field
332
248
  name="environmentId"
333
249
  validators={{
334
- onChange: z.string().min(1, { message: 'Environment is required' }),
250
+ onChange: testRunFieldValidators.environmentId,
335
251
  }}
336
252
  >
337
253
  {field => {
@@ -339,7 +255,7 @@ const TestRunForm = ({
339
255
  <div className="mb-4 flex flex-col gap-2">
340
256
  <Label htmlFor={field.name}>Environment</Label>
341
257
  <Select value={field.state.value} onValueChange={value => field.handleChange(value)}>
342
- <SelectTrigger>
258
+ <SelectTrigger id={field.name}>
343
259
  <SelectValue placeholder="Select an environment" />
344
260
  </SelectTrigger>
345
261
  <SelectContent isEmpty={environments.length === 0} emptyMessage="No environments available">
@@ -350,12 +266,7 @@ const TestRunForm = ({
350
266
  ))}
351
267
  </SelectContent>
352
268
  </Select>
353
- {field.state.meta.isTouched &&
354
- field.state.meta.errors.map((error, index) => (
355
- <p key={index} className="text-xs text-pink-500">
356
- {typeof error === 'string' ? error : error?.message || String(error)}
357
- </p>
358
- ))}
269
+ <TestRunFieldErrors errors={field.state.meta.errors} isTouched={field.state.meta.isTouched} />
359
270
  </div>
360
271
  )
361
272
  }}
@@ -363,7 +274,7 @@ const TestRunForm = ({
363
274
 
364
275
  <form.Field
365
276
  name="testWorkersCount"
366
- validators={{ onChange: z.number().min(1, { message: 'Test workers count must be at least 1' }) }}
277
+ validators={{ onChange: testRunFieldValidators.testWorkersCount }}
367
278
  >
368
279
  {field => {
369
280
  return (
@@ -374,40 +285,29 @@ const TestRunForm = ({
374
285
  value={field.state.value}
375
286
  onChange={e => field.handleChange(Number(e.target.value))}
376
287
  />
377
- {field.state.meta.isTouched &&
378
- field.state.meta.errors.map((error, index) => (
379
- <p key={index} className="text-xs text-pink-500">
380
- {typeof error === 'string' ? error : String(error)}
381
- </p>
382
- ))}
288
+ <TestRunFieldErrors errors={field.state.meta.errors} isTouched={field.state.meta.isTouched} />
383
289
  </div>
384
290
  )
385
291
  }}
386
292
  </form.Field>
387
- <form.Field name="browserEngine" validators={{ onChange: z.nativeEnum(BrowserEngine) }}>
293
+ <form.Field name="browserEngine" validators={{ onChange: testRunFieldValidators.browserEngine }}>
388
294
  {field => {
389
295
  return (
390
296
  <div className="mb-4 flex flex-col gap-2">
391
297
  <Label htmlFor={field.name}>Browser Engine</Label>
392
- <Select
393
- value={field.state.value}
394
- onValueChange={value => field.handleChange(value as unknown as BrowserEngine)}
395
- >
396
- <SelectTrigger>
298
+ <Select value={field.state.value} onValueChange={value => field.handleChange(value as BrowserEngine)}>
299
+ <SelectTrigger id={field.name}>
397
300
  <SelectValue placeholder="Select a browser engine" />
398
301
  </SelectTrigger>
399
302
  <SelectContent>
400
- <SelectItem value={BrowserEngine.CHROMIUM}>Chromium</SelectItem>
401
- <SelectItem value={BrowserEngine.FIREFOX}>Firefox</SelectItem>
402
- <SelectItem value={BrowserEngine.WEBKIT}>WebKit</SelectItem>
303
+ {getBrowserEngineOptions().map(option => (
304
+ <SelectItem key={option.value} value={option.value}>
305
+ {option.label}
306
+ </SelectItem>
307
+ ))}
403
308
  </SelectContent>
404
309
  </Select>
405
- {field.state.meta.isTouched &&
406
- field.state.meta.errors.map((error, index) => (
407
- <p key={index} className="text-xs text-pink-500">
408
- {typeof error === 'string' ? error : String(error)}
409
- </p>
410
- ))}
310
+ <TestRunFieldErrors errors={field.state.meta.errors} isTouched={field.state.meta.isTouched} />
411
311
  </div>
412
312
  )
413
313
  }}
@@ -424,69 +324,24 @@ const TestRunForm = ({
424
324
  </CardTitle>
425
325
  </CardHeader>
426
326
  <CardContent className="flex flex-col gap-6">
427
- <div className="flex items-start gap-4">
428
- <span className="flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500">
429
- 1
430
- </span>
431
- <div className="flex flex-col gap-1">
432
- <span className="text-base font-bold">Choose a descriptive name</span>
433
- <span className="text-sm text-muted-foreground">
434
- Use clear, specific names that indicate the purpose for your test run
435
- </span>
436
- </div>
437
- </div>
438
- <div className="flex items-start gap-4">
439
- <span className="flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500">
440
- 2
441
- </span>
442
- <div className="flex flex-col gap-1">
443
- <span className="text-base font-bold">Select the environment for your test run</span>
444
- <span className="text-sm text-muted-foreground">
445
- Choose the environment that best suits your selected tests
446
- </span>
447
- </div>
448
- </div>
449
- <div className="flex items-start gap-4">
450
- <span className="flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500">
451
- 3
452
- </span>
453
- <div className="flex flex-col gap-1">
454
- <span className="text-base font-bold">Select the browser engine for your test run</span>
455
- <span className="text-sm text-muted-foreground">
456
- Select the browser engine that is compatible with your selected test cases
457
- </span>
458
- </div>
459
- </div>
460
- <div className="flex items-start gap-4">
461
- <span className="flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500">
462
- 4
463
- </span>
464
- <div className="flex flex-col gap-1">
465
- <span className="text-base font-bold">Select the test suites or tags for your test run</span>
466
- <span className="text-sm text-muted-foreground">
467
- You can filter by tags or browse suites and choose full suites or child subsets
468
- </span>
469
- </div>
470
- </div>
471
- <div className="flex items-start gap-4">
472
- <span className="flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500">
473
- 5
474
- </span>
475
- <div className="flex flex-col gap-1">
476
- <span className="text-base font-bold">Select the test workers count for your test run</span>
477
- <span className="text-sm text-muted-foreground">
478
- Parallel workers can be used to run your test cases in parallel to speed up the test execution
327
+ {testRunQuickTips.map((tip, index) => (
328
+ <div key={tip.title} className="flex items-start gap-4">
329
+ <span className="flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500">
330
+ {index + 1}
479
331
  </span>
332
+ <div className="flex flex-col gap-1">
333
+ <span className="text-base font-bold">{tip.title}</span>
334
+ <span className="text-sm text-muted-foreground">{tip.description}</span>
335
+ </div>
480
336
  </div>
481
- </div>
337
+ ))}
482
338
  </CardContent>
483
339
  </Card>
484
340
  </div>
485
341
  </div>
486
342
  <form.Subscribe selector={formState => [formState.canSubmit, formState.isSubmitting]}>
487
- {([canSubmit, isSubmitting, errors]) => (
343
+ {([canSubmit, isSubmitting]) => (
488
344
  <>
489
- <span>{errors}</span>
490
345
  <Button type="submit" disabled={!canSubmit} className="hover:bg-emerald-500">
491
346
  <Play className="h-4 w-4" />
492
347
  <span className="font-bold">{isSubmitting ? '...' : 'Start'}</span>