create-appraisejs 0.2.0 → 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 +4 -2
  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,234 +1,234 @@
1
- import { Page } from 'playwright'
2
- import { LocatorCache, LocatorMapCache } from './cache.util.js'
3
-
4
- const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
5
-
6
- export async function retry<T>(
7
- fn: () => Promise<T>,
8
- {
9
- retries = 5,
10
- delayMs = 100,
11
- backoff = 1.6,
12
- shouldRetry = () => true,
13
- }: {
14
- retries?: number
15
- delayMs?: number
16
- backoff?: number
17
- shouldRetry?: (error: unknown) => boolean
18
- } = {},
19
- ): Promise<T> {
20
- let lastErr: unknown
21
- let delay = delayMs
22
-
23
- for (let i = 0; i <= retries; i++) {
24
- try {
25
- return await fn()
26
- } catch (error) {
27
- lastErr = error
28
- if (i === retries || !shouldRetry(error)) {
29
- break
30
- }
31
- await sleep(delay)
32
- delay = Math.floor(delay * backoff)
33
- }
34
- }
35
-
36
- throw lastErr
37
- }
38
-
39
- const routeKey = (page: Page, includeSearch = false) => {
40
- const url = new URL(page.url())
41
- return includeSearch ? `${url.pathname}${url.search}` : url.pathname
42
- }
43
-
44
- export async function waitForRouteSettled(
45
- page: Page,
46
- {
47
- timeoutMs = 15000,
48
- pollMs = 50,
49
- urlStableMs = 500,
50
- domQuietMs = 300,
51
- includeSearch = false,
52
- }: {
53
- timeoutMs?: number
54
- pollMs?: number
55
- urlStableMs?: number
56
- domQuietMs?: number
57
- includeSearch?: boolean
58
- } = {},
59
- ): Promise<string> {
60
- const deadline = Date.now() + timeoutMs
61
- await page.waitForLoadState('domcontentloaded', { timeout: timeoutMs })
62
-
63
- let lastKey = routeKey(page, includeSearch)
64
- let stableSince = Date.now()
65
- let navBumpedAt = Date.now()
66
-
67
- const onFrameNav = () => {
68
- navBumpedAt = Date.now()
69
- }
70
-
71
- page.on('framenavigated', onFrameNav)
72
-
73
- try {
74
- while (Date.now() < deadline) {
75
- await sleep(pollMs)
76
-
77
- const key = routeKey(page, includeSearch)
78
- if (key !== lastKey) {
79
- lastKey = key
80
- stableSince = Date.now()
81
- continue
82
- }
83
-
84
- const now = Date.now()
85
- const urlStableLongEnough = now - stableSince >= urlStableMs
86
- const noRecentNav = now - navBumpedAt >= urlStableMs
87
- if (!urlStableLongEnough || !noRecentNav) {
88
- continue
89
- }
90
-
91
- const domQuiet = await page
92
- .evaluate(quietMs => {
93
- return new Promise<boolean>(resolve => {
94
- let lastMutation = Date.now()
95
- const observer = new MutationObserver(() => {
96
- lastMutation = Date.now()
97
- })
98
- observer.observe(document, { subtree: true, childList: true, attributes: true })
99
-
100
- const tick = () => {
101
- if (Date.now() - lastMutation >= quietMs) {
102
- observer.disconnect()
103
- resolve(true)
104
- return
105
- }
106
- setTimeout(tick, 50)
107
- }
108
-
109
- tick()
110
- })
111
- }, domQuietMs)
112
- .catch(() => false)
113
-
114
- if (domQuiet) {
115
- return lastKey
116
- }
117
- }
118
-
119
- return routeKey(page, includeSearch)
120
- } finally {
121
- page.off('framenavigated', onFrameNav)
122
- }
123
- }
124
-
125
- const getLocatorMapData = async (page: Page) => {
126
- const locatorMap = LocatorMapCache.getInstance()
127
- const currentPath = new URL(page.url()).pathname
128
- const data = locatorMap.get(currentPath)
129
-
130
- if (!data) {
131
- throw new Error(`LOCATOR_MAP_NOT_FOUND::${currentPath}`)
132
- }
133
-
134
- return data
135
- }
136
-
137
- async function validateResolvedSelector(
138
- page: Page,
139
- selector: string,
140
- {
141
- timeoutMs = 1500,
142
- requireVisible = true,
143
- requireUnique = false,
144
- }: {
145
- timeoutMs?: number
146
- requireVisible?: boolean
147
- requireUnique?: boolean
148
- } = {},
149
- ): Promise<boolean> {
150
- try {
151
- const locator = page.locator(selector)
152
- await locator.first().waitFor({ state: 'attached', timeout: timeoutMs })
153
-
154
- if (requireVisible) {
155
- await locator.first().waitFor({ state: 'visible', timeout: timeoutMs })
156
- }
157
-
158
- if (requireUnique) {
159
- const count = await locator.count()
160
- if (count !== 1) {
161
- return false
162
- }
163
- }
164
-
165
- return true
166
- } catch {
167
- return false
168
- }
169
- }
170
-
171
- export async function resolveLocator(
172
- page: Page,
173
- locatorName: string,
174
- {
175
- maxResolvePasses = 2,
176
- validate = {
177
- timeoutMs: 1500,
178
- requireVisible: true,
179
- requireUnique: false,
180
- },
181
- }: {
182
- maxResolvePasses?: number
183
- validate?: {
184
- timeoutMs?: number
185
- requireVisible?: boolean
186
- requireUnique?: boolean
187
- }
188
- } = {},
189
- ): Promise<string | null> {
190
- try {
191
- for (let pass = 0; pass < maxResolvePasses; pass++) {
192
- await waitForRouteSettled(page, {
193
- timeoutMs: 15000,
194
- urlStableMs: 500,
195
- domQuietMs: 300,
196
- pollMs: 50,
197
- includeSearch: false,
198
- })
199
-
200
- const beforePath = new URL(page.url()).pathname
201
- const locatorMapData = await retry(() => getLocatorMapData(page), {
202
- retries: 6,
203
- delayMs: 80,
204
- backoff: 1.5,
205
- shouldRetry: error => error instanceof Error && error.message.startsWith('LOCATOR_MAP_NOT_FOUND::'),
206
- })
207
-
208
- const locators = LocatorCache.getInstance().get(locatorMapData.name)
209
- if (!locators) {
210
- throw new Error(`Locator bundle not found for name ${locatorMapData.name}`)
211
- }
212
-
213
- const selector = locators[locatorName]
214
- if (!selector) {
215
- throw new Error(`Locator "${locatorName}" not found for name ${locatorMapData.name}`)
216
- }
217
-
218
- const afterPath = new URL(page.url()).pathname
219
- if (afterPath !== beforePath) {
220
- continue
221
- }
222
-
223
- const isValid = await validateResolvedSelector(page, selector as unknown as string, validate)
224
- if (isValid) {
225
- return selector as unknown as string
226
- }
227
- }
228
-
229
- throw new Error(`Failed to resolve a valid locator for "${locatorName}" after ${maxResolvePasses} passes`)
230
- } catch (error) {
231
- console.error(error)
232
- return null
233
- }
234
- }
1
+ import { Page } from 'playwright'
2
+ import { LocatorCache, LocatorMapCache } from './cache.util.js'
3
+
4
+ const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
5
+
6
+ export async function retry<T>(
7
+ fn: () => Promise<T>,
8
+ {
9
+ retries = 5,
10
+ delayMs = 100,
11
+ backoff = 1.6,
12
+ shouldRetry = () => true,
13
+ }: {
14
+ retries?: number
15
+ delayMs?: number
16
+ backoff?: number
17
+ shouldRetry?: (error: unknown) => boolean
18
+ } = {},
19
+ ): Promise<T> {
20
+ let lastErr: unknown
21
+ let delay = delayMs
22
+
23
+ for (let i = 0; i <= retries; i++) {
24
+ try {
25
+ return await fn()
26
+ } catch (error) {
27
+ lastErr = error
28
+ if (i === retries || !shouldRetry(error)) {
29
+ break
30
+ }
31
+ await sleep(delay)
32
+ delay = Math.floor(delay * backoff)
33
+ }
34
+ }
35
+
36
+ throw lastErr
37
+ }
38
+
39
+ const routeKey = (page: Page, includeSearch = false) => {
40
+ const url = new URL(page.url())
41
+ return includeSearch ? `${url.pathname}${url.search}` : url.pathname
42
+ }
43
+
44
+ export async function waitForRouteSettled(
45
+ page: Page,
46
+ {
47
+ timeoutMs = 15000,
48
+ pollMs = 50,
49
+ urlStableMs = 500,
50
+ domQuietMs = 300,
51
+ includeSearch = false,
52
+ }: {
53
+ timeoutMs?: number
54
+ pollMs?: number
55
+ urlStableMs?: number
56
+ domQuietMs?: number
57
+ includeSearch?: boolean
58
+ } = {},
59
+ ): Promise<string> {
60
+ const deadline = Date.now() + timeoutMs
61
+ await page.waitForLoadState('domcontentloaded', { timeout: timeoutMs })
62
+
63
+ let lastKey = routeKey(page, includeSearch)
64
+ let stableSince = Date.now()
65
+ let navBumpedAt = Date.now()
66
+
67
+ const onFrameNav = () => {
68
+ navBumpedAt = Date.now()
69
+ }
70
+
71
+ page.on('framenavigated', onFrameNav)
72
+
73
+ try {
74
+ while (Date.now() < deadline) {
75
+ await sleep(pollMs)
76
+
77
+ const key = routeKey(page, includeSearch)
78
+ if (key !== lastKey) {
79
+ lastKey = key
80
+ stableSince = Date.now()
81
+ continue
82
+ }
83
+
84
+ const now = Date.now()
85
+ const urlStableLongEnough = now - stableSince >= urlStableMs
86
+ const noRecentNav = now - navBumpedAt >= urlStableMs
87
+ if (!urlStableLongEnough || !noRecentNav) {
88
+ continue
89
+ }
90
+
91
+ const domQuiet = await page
92
+ .evaluate(quietMs => {
93
+ return new Promise<boolean>(resolve => {
94
+ let lastMutation = Date.now()
95
+ const observer = new MutationObserver(() => {
96
+ lastMutation = Date.now()
97
+ })
98
+ observer.observe(document, { subtree: true, childList: true, attributes: true })
99
+
100
+ const tick = () => {
101
+ if (Date.now() - lastMutation >= quietMs) {
102
+ observer.disconnect()
103
+ resolve(true)
104
+ return
105
+ }
106
+ setTimeout(tick, 50)
107
+ }
108
+
109
+ tick()
110
+ })
111
+ }, domQuietMs)
112
+ .catch(() => false)
113
+
114
+ if (domQuiet) {
115
+ return lastKey
116
+ }
117
+ }
118
+
119
+ return routeKey(page, includeSearch)
120
+ } finally {
121
+ page.off('framenavigated', onFrameNav)
122
+ }
123
+ }
124
+
125
+ const getLocatorMapData = async (page: Page) => {
126
+ const locatorMap = LocatorMapCache.getInstance()
127
+ const currentPath = new URL(page.url()).pathname
128
+ const data = locatorMap.get(currentPath)
129
+
130
+ if (!data) {
131
+ throw new Error(`LOCATOR_MAP_NOT_FOUND::${currentPath}`)
132
+ }
133
+
134
+ return data
135
+ }
136
+
137
+ async function validateResolvedSelector(
138
+ page: Page,
139
+ selector: string,
140
+ {
141
+ timeoutMs = 1500,
142
+ requireVisible = true,
143
+ requireUnique = false,
144
+ }: {
145
+ timeoutMs?: number
146
+ requireVisible?: boolean
147
+ requireUnique?: boolean
148
+ } = {},
149
+ ): Promise<boolean> {
150
+ try {
151
+ const locator = page.locator(selector)
152
+ await locator.first().waitFor({ state: 'attached', timeout: timeoutMs })
153
+
154
+ if (requireVisible) {
155
+ await locator.first().waitFor({ state: 'visible', timeout: timeoutMs })
156
+ }
157
+
158
+ if (requireUnique) {
159
+ const count = await locator.count()
160
+ if (count !== 1) {
161
+ return false
162
+ }
163
+ }
164
+
165
+ return true
166
+ } catch {
167
+ return false
168
+ }
169
+ }
170
+
171
+ export async function resolveLocator(
172
+ page: Page,
173
+ locatorName: string,
174
+ {
175
+ maxResolvePasses = 2,
176
+ validate = {
177
+ timeoutMs: 1500,
178
+ requireVisible: true,
179
+ requireUnique: false,
180
+ },
181
+ }: {
182
+ maxResolvePasses?: number
183
+ validate?: {
184
+ timeoutMs?: number
185
+ requireVisible?: boolean
186
+ requireUnique?: boolean
187
+ }
188
+ } = {},
189
+ ): Promise<string | null> {
190
+ try {
191
+ for (let pass = 0; pass < maxResolvePasses; pass++) {
192
+ await waitForRouteSettled(page, {
193
+ timeoutMs: 15000,
194
+ urlStableMs: 500,
195
+ domQuietMs: 300,
196
+ pollMs: 50,
197
+ includeSearch: false,
198
+ })
199
+
200
+ const beforePath = new URL(page.url()).pathname
201
+ const locatorMapData = await retry(() => getLocatorMapData(page), {
202
+ retries: 6,
203
+ delayMs: 80,
204
+ backoff: 1.5,
205
+ shouldRetry: error => error instanceof Error && error.message.startsWith('LOCATOR_MAP_NOT_FOUND::'),
206
+ })
207
+
208
+ const locators = LocatorCache.getInstance().get(locatorMapData.name)
209
+ if (!locators) {
210
+ throw new Error(`Locator bundle not found for name ${locatorMapData.name}`)
211
+ }
212
+
213
+ const selector = locators[locatorName]
214
+ if (!selector) {
215
+ throw new Error(`Locator "${locatorName}" not found for name ${locatorMapData.name}`)
216
+ }
217
+
218
+ const afterPath = new URL(page.url()).pathname
219
+ if (afterPath !== beforePath) {
220
+ continue
221
+ }
222
+
223
+ const isValid = await validateResolvedSelector(page, selector as unknown as string, validate)
224
+ if (isValid) {
225
+ return selector as unknown as string
226
+ }
227
+ }
228
+
229
+ throw new Error(`Failed to resolve a valid locator for "${locatorName}" after ${maxResolvePasses} passes`)
230
+ } catch (error) {
231
+ console.error(error)
232
+ return null
233
+ }
234
+ }
@@ -1,7 +1,7 @@
1
- import { defineParameterType } from '@cucumber/cucumber'
2
-
3
- defineParameterType({
4
- name: 'boolean',
5
- regexp: /true|false/,
6
- transformer: (s: string) => s === 'true',
7
- })
1
+ import { defineParameterType } from '@cucumber/cucumber'
2
+
3
+ defineParameterType({
4
+ name: 'boolean',
5
+ regexp: /true|false/,
6
+ transformer: (s: string) => s === 'true',
7
+ })
@@ -1,35 +1,35 @@
1
- import { faker } from '@faker-js/faker'
2
-
3
- export enum RandomDataType {
4
- FULL_NAME = 'fullName',
5
- FIRST_NAME = 'firstName',
6
- LAST_NAME = 'lastName',
7
- EMAIL = 'email',
8
- PASSWORD = 'password',
9
- PHONE = 'phone',
10
- ADDRESS = 'address',
11
- UNIQUE_TEXT = 'uniqueText',
12
- }
13
-
14
- export function generateRandomData(randomDataType: RandomDataType): string {
15
- switch (randomDataType) {
16
- case RandomDataType.FULL_NAME:
17
- return faker.person.fullName()
18
- case RandomDataType.FIRST_NAME:
19
- return faker.person.firstName()
20
- case RandomDataType.LAST_NAME:
21
- return faker.person.lastName()
22
- case RandomDataType.EMAIL:
23
- return faker.internet.email()
24
- case RandomDataType.PASSWORD:
25
- return faker.internet.password()
26
- case RandomDataType.PHONE:
27
- return faker.phone.number()
28
- case RandomDataType.ADDRESS:
29
- return faker.location.streetAddress()
30
- case RandomDataType.UNIQUE_TEXT:
31
- return faker.string.uuid()
32
- default:
33
- throw new Error(`Invalid random data type: ${randomDataType}`)
34
- }
35
- }
1
+ import { faker } from '@faker-js/faker'
2
+
3
+ export enum RandomDataType {
4
+ FULL_NAME = 'fullName',
5
+ FIRST_NAME = 'firstName',
6
+ LAST_NAME = 'lastName',
7
+ EMAIL = 'email',
8
+ PASSWORD = 'password',
9
+ PHONE = 'phone',
10
+ ADDRESS = 'address',
11
+ UNIQUE_TEXT = 'uniqueText',
12
+ }
13
+
14
+ export function generateRandomData(randomDataType: RandomDataType): string {
15
+ switch (randomDataType) {
16
+ case RandomDataType.FULL_NAME:
17
+ return faker.person.fullName()
18
+ case RandomDataType.FIRST_NAME:
19
+ return faker.person.firstName()
20
+ case RandomDataType.LAST_NAME:
21
+ return faker.person.lastName()
22
+ case RandomDataType.EMAIL:
23
+ return faker.internet.email()
24
+ case RandomDataType.PASSWORD:
25
+ return faker.internet.password()
26
+ case RandomDataType.PHONE:
27
+ return faker.phone.number()
28
+ case RandomDataType.ADDRESS:
29
+ return faker.location.streetAddress()
30
+ case RandomDataType.UNIQUE_TEXT:
31
+ return faker.string.uuid()
32
+ default:
33
+ throw new Error(`Invalid random data type: ${randomDataType}`)
34
+ }
35
+ }
@@ -1,13 +1,13 @@
1
- export type BrowserName = 'chromium' | 'firefox' | 'webkit'
2
-
3
- export type CSSSelector = string
4
- export type XPathSelector = `/${string}` | `//${string}`
5
- export type SelectorName = string
6
- export type Selector = CSSSelector | XPathSelector
7
- export type Locator = Record<string, Selector>
8
- export type LocatorMap = {
9
- name: string
10
- path: string
11
- }
12
-
13
- export type LocatorCollection = Record<string, Locator>
1
+ export type BrowserName = 'chromium' | 'firefox' | 'webkit'
2
+
3
+ export type CSSSelector = string
4
+ export type XPathSelector = `/${string}` | `//${string}`
5
+ export type SelectorName = string
6
+ export type Selector = CSSSelector | XPathSelector
7
+ export type Locator = Record<string, Selector>
8
+ export type LocatorMap = {
9
+ name: string
10
+ path: string
11
+ }
12
+
13
+ export type LocatorCollection = Record<string, Locator>
@@ -1,44 +1,44 @@
1
- import { World, IWorldOptions, setWorldConstructor, setDefaultTimeout } from '@cucumber/cucumber'
2
- import * as chai from 'chai'
3
- import chaiAsPromised from 'chai-as-promised'
4
- import { BrowserContext, Page } from 'playwright'
5
-
6
- setDefaultTimeout(120 * 1000)
7
-
8
- export interface ScenarioData {
9
- token?: string
10
- vars: Record<string, unknown>
11
- }
12
-
13
- export class CustomWorld extends World {
14
- context!: BrowserContext
15
- page!: Page
16
- data: ScenarioData = {
17
- vars: {},
18
- }
19
-
20
- constructor(options: IWorldOptions) {
21
- super(options)
22
- }
23
-
24
- setVar(key: string, value: unknown): void {
25
- this.data.vars[key] = value
26
- }
27
-
28
- getVar<T = unknown>(key: string): T {
29
- if (!(key in this.data.vars)) {
30
- throw new Error(`Variable ${key} not found`)
31
- }
32
-
33
- return this.data.vars[key] as T
34
- }
35
-
36
- clearVars(): void {
37
- this.data.vars = {}
38
- }
39
- }
40
-
41
- setWorldConstructor(CustomWorld)
42
- chai.use(chaiAsPromised)
43
-
44
- export const expect = chai.expect
1
+ import { World, IWorldOptions, setWorldConstructor, setDefaultTimeout } from '@cucumber/cucumber'
2
+ import * as chai from 'chai'
3
+ import chaiAsPromised from 'chai-as-promised'
4
+ import { BrowserContext, Page } from 'playwright'
5
+
6
+ setDefaultTimeout(120 * 1000)
7
+
8
+ export interface ScenarioData {
9
+ token?: string
10
+ vars: Record<string, unknown>
11
+ }
12
+
13
+ export class CustomWorld extends World {
14
+ context!: BrowserContext
15
+ page!: Page
16
+ data: ScenarioData = {
17
+ vars: {},
18
+ }
19
+
20
+ constructor(options: IWorldOptions) {
21
+ super(options)
22
+ }
23
+
24
+ setVar(key: string, value: unknown): void {
25
+ this.data.vars[key] = value
26
+ }
27
+
28
+ getVar<T = unknown>(key: string): T {
29
+ if (!(key in this.data.vars)) {
30
+ throw new Error(`Variable ${key} not found`)
31
+ }
32
+
33
+ return this.data.vars[key] as T
34
+ }
35
+
36
+ clearVars(): void {
37
+ this.data.vars = {}
38
+ }
39
+ }
40
+
41
+ setWorldConstructor(CustomWorld)
42
+ chai.use(chaiAsPromised)
43
+
44
+ export const expect = chai.expect