create-appraise 0.1.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.
- package/README.md +52 -0
- package/package.json +63 -0
- package/templates/default/.env.example +2 -0
- package/templates/default/README.md +51 -0
- package/templates/default/appraise.config.json +4 -0
- package/templates/default/components.json +24 -0
- package/templates/default/eslint.config.mjs +15 -0
- package/templates/default/next-env.d.ts +6 -0
- package/templates/default/next.config.ts +7 -0
- package/templates/default/package-lock.json +14321 -0
- package/templates/default/package.json +124 -0
- package/templates/default/postcss.config.mjs +8 -0
- package/templates/default/prisma/migrations/20251026202316_migrate_back_to_sqlite/migration.sql +257 -0
- package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -0
- package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -0
- package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -0
- package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -0
- package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -0
- package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -0
- package/templates/default/prisma/migrations/20251130190737_add_trace_path_to_test_run_test_case/migration.sql +2 -0
- package/templates/default/prisma/migrations/20251213074835_add_log_path_to_test_run/migration.sql +2 -0
- package/templates/default/prisma/migrations/20251213183952_add_name_property_for_the_test_run_entities/migration.sql +30 -0
- package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -0
- package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -0
- package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -0
- package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -0
- package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -0
- package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -0
- package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -0
- package/templates/default/prisma/migrations/migration_lock.toml +3 -0
- package/templates/default/prisma/schema.prisma +554 -0
- package/templates/default/public/favicon.ico +0 -0
- package/templates/default/public/file.svg +1 -0
- package/templates/default/public/globe.svg +1 -0
- package/templates/default/public/next.svg +1 -0
- package/templates/default/public/vercel.svg +1 -0
- package/templates/default/public/window.svg +1 -0
- package/templates/default/scripts/regenerate-features.ts +94 -0
- package/templates/default/scripts/setup-env.ts +19 -0
- package/templates/default/scripts/sync-all.ts +341 -0
- package/templates/default/scripts/sync-environments.ts +323 -0
- package/templates/default/scripts/sync-locator-groups.ts +413 -0
- package/templates/default/scripts/sync-locators.ts +402 -0
- package/templates/default/scripts/sync-modules.ts +349 -0
- package/templates/default/scripts/sync-tags.ts +292 -0
- package/templates/default/scripts/sync-template-step-groups.ts +399 -0
- package/templates/default/scripts/sync-template-steps.ts +806 -0
- package/templates/default/scripts/sync-test-cases.ts +905 -0
- package/templates/default/scripts/sync-test-suites.ts +411 -0
- package/templates/default/src/actions/conflict/conflict.action.ts +33 -0
- package/templates/default/src/actions/dashboard/dashboard-actions.ts +241 -0
- package/templates/default/src/actions/environments/environment-actions.ts +205 -0
- package/templates/default/src/actions/locator/locator-actions.ts +547 -0
- package/templates/default/src/actions/locator-groups/locator-group-actions.ts +344 -0
- package/templates/default/src/actions/modules/module-actions.ts +133 -0
- package/templates/default/src/actions/reports/report-actions.ts +614 -0
- package/templates/default/src/actions/review/review-actions.ts +147 -0
- package/templates/default/src/actions/tags/tag-actions.ts +104 -0
- package/templates/default/src/actions/template-step/template-step-actions.ts +332 -0
- package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +278 -0
- package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +238 -0
- package/templates/default/src/actions/test-case/test-case-actions.ts +419 -0
- package/templates/default/src/actions/test-run/test-run-actions.ts +1185 -0
- package/templates/default/src/actions/test-suite/test-suite-actions.ts +253 -0
- package/templates/default/src/actions/user/user-actions.ts +13 -0
- package/templates/default/src/app/(base)/environments/create/page.tsx +28 -0
- package/templates/default/src/app/(base)/environments/environment-form.tsx +219 -0
- package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -0
- package/templates/default/src/app/(base)/environments/environment-table.tsx +24 -0
- package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +46 -0
- package/templates/default/src/app/(base)/environments/page.tsx +59 -0
- package/templates/default/src/app/(base)/layout.tsx +10 -0
- package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -0
- package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -0
- package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -0
- package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -0
- package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -0
- package/templates/default/src/app/(base)/locator-groups/page.tsx +61 -0
- package/templates/default/src/app/(base)/locators/create/page.tsx +38 -0
- package/templates/default/src/app/(base)/locators/locator-form.tsx +163 -0
- package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +90 -0
- package/templates/default/src/app/(base)/locators/locator-table.tsx +28 -0
- package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +45 -0
- package/templates/default/src/app/(base)/locators/page.tsx +65 -0
- package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +66 -0
- package/templates/default/src/app/(base)/modules/create/page.tsx +34 -0
- package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +46 -0
- package/templates/default/src/app/(base)/modules/module-form.tsx +126 -0
- package/templates/default/src/app/(base)/modules/module-table-columns.tsx +85 -0
- package/templates/default/src/app/(base)/modules/module-table.tsx +24 -0
- package/templates/default/src/app/(base)/modules/page.tsx +59 -0
- package/templates/default/src/app/(base)/reports/[id]/page.tsx +517 -0
- package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -0
- package/templates/default/src/app/(base)/reports/feature-chart.tsx +78 -0
- package/templates/default/src/app/(base)/reports/overview-chart.tsx +46 -0
- package/templates/default/src/app/(base)/reports/page.tsx +98 -0
- package/templates/default/src/app/(base)/reports/report-metric-card.tsx +16 -0
- package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -0
- package/templates/default/src/app/(base)/reports/report-table.tsx +72 -0
- package/templates/default/src/app/(base)/reports/report-view-table-columns.tsx +131 -0
- package/templates/default/src/app/(base)/reports/report-view-table.tsx +82 -0
- package/templates/default/src/app/(base)/reports/test-cases/page.tsx +42 -0
- package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -0
- package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -0
- package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -0
- package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -0
- package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -0
- package/templates/default/src/app/(base)/reports/view-logs-button.tsx +60 -0
- package/templates/default/src/app/(base)/reviews/create/page.tsx +26 -0
- package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +15 -0
- package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +26 -0
- package/templates/default/src/app/(base)/reviews/page.tsx +26 -0
- package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +26 -0
- package/templates/default/src/app/(base)/reviews/review-form.tsx +11 -0
- package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +9 -0
- package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +9 -0
- package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +15 -0
- package/templates/default/src/app/(base)/tags/create/page.tsx +39 -0
- package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -0
- package/templates/default/src/app/(base)/tags/page.tsx +58 -0
- package/templates/default/src/app/(base)/tags/tag-form.tsx +147 -0
- package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -0
- package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -0
- package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -0
- package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +45 -0
- package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -0
- package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +167 -0
- package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -0
- package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +32 -0
- package/templates/default/src/app/(base)/template-steps/create/page.tsx +37 -0
- package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +49 -0
- package/templates/default/src/app/(base)/template-steps/page.tsx +59 -0
- package/templates/default/src/app/(base)/template-steps/paramChip.tsx +213 -0
- package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +384 -0
- package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +158 -0
- package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +24 -0
- package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -0
- package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -0
- package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -0
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +84 -0
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +262 -0
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -0
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -0
- package/templates/default/src/app/(base)/test-cases/create/page.tsx +76 -0
- package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +96 -0
- package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +38 -0
- package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +73 -0
- package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -0
- package/templates/default/src/app/(base)/test-cases/page.tsx +60 -0
- package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +82 -0
- package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +395 -0
- package/templates/default/src/app/(base)/test-cases/test-case-table-columns.tsx +90 -0
- package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +35 -0
- package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +56 -0
- package/templates/default/src/app/(base)/test-runs/create/page.tsx +47 -0
- package/templates/default/src/app/(base)/test-runs/page.tsx +60 -0
- package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +512 -0
- package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -0
- package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -0
- package/templates/default/src/app/(base)/test-suites/create/page.tsx +45 -0
- package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +55 -0
- package/templates/default/src/app/(base)/test-suites/page.tsx +82 -0
- package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +269 -0
- package/templates/default/src/app/(base)/test-suites/test-suite-table-columns.tsx +97 -0
- package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +29 -0
- package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -0
- package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +13 -0
- package/templates/default/src/app/(dashboard-components)/data-card.tsx +27 -0
- package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +57 -0
- package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -0
- package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +45 -0
- package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +133 -0
- package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +420 -0
- package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -0
- package/templates/default/src/app/favicon.ico +0 -0
- package/templates/default/src/app/globals.css +147 -0
- package/templates/default/src/app/layout.tsx +171 -0
- package/templates/default/src/app/page.tsx +64 -0
- package/templates/default/src/assets/icons/empty-tube.tsx +23 -0
- package/templates/default/src/assets/icons/tube-plus.tsx +29 -0
- package/templates/default/src/components/base-node.tsx +21 -0
- package/templates/default/src/components/chart/pie-chart.tsx +73 -0
- package/templates/default/src/components/data-extraction/locator-inspector.tsx +460 -0
- package/templates/default/src/components/data-state/empty-state.tsx +40 -0
- package/templates/default/src/components/data-visualization/info-card.tsx +70 -0
- package/templates/default/src/components/data-visualization/info-grid.tsx +22 -0
- package/templates/default/src/components/devtools/providers.tsx +13 -0
- package/templates/default/src/components/diagram/button-edge.tsx +54 -0
- package/templates/default/src/components/diagram/dynamic-parameters.tsx +438 -0
- package/templates/default/src/components/diagram/edit-header-option.tsx +36 -0
- package/templates/default/src/components/diagram/flow-diagram.tsx +470 -0
- package/templates/default/src/components/diagram/node-form.tsx +262 -0
- package/templates/default/src/components/diagram/options-header-node.tsx +57 -0
- package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -0
- package/templates/default/src/components/form/error-message.tsx +7 -0
- package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -0
- package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -0
- package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -0
- package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -0
- package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -0
- package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -0
- package/templates/default/src/components/logo.tsx +15 -0
- package/templates/default/src/components/navigation/command-badge.tsx +34 -0
- package/templates/default/src/components/navigation/command-chain-input.tsx +51 -0
- package/templates/default/src/components/navigation/entity-search-command.tsx +116 -0
- package/templates/default/src/components/navigation/nav-card.tsx +31 -0
- package/templates/default/src/components/navigation/nav-command.tsx +508 -0
- package/templates/default/src/components/navigation/nav-link.tsx +60 -0
- package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -0
- package/templates/default/src/components/node-header.tsx +159 -0
- package/templates/default/src/components/reports/test-case-logs-modal.tsx +253 -0
- package/templates/default/src/components/table/table-actions.tsx +172 -0
- package/templates/default/src/components/test-run/download-logs-button.tsx +99 -0
- package/templates/default/src/components/test-run/log-viewer.tsx +445 -0
- package/templates/default/src/components/test-run/test-run-details.tsx +611 -0
- package/templates/default/src/components/test-run/test-run-header.tsx +149 -0
- package/templates/default/src/components/test-run/view-report-button.tsx +102 -0
- package/templates/default/src/components/theme/mode-toggle.tsx +54 -0
- package/templates/default/src/components/theme/theme-provider.tsx +8 -0
- package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -0
- package/templates/default/src/components/typography/page-header.tsx +7 -0
- package/templates/default/src/components/ui/alert-dialog.tsx +106 -0
- package/templates/default/src/components/ui/alert.tsx +43 -0
- package/templates/default/src/components/ui/avatar.tsx +40 -0
- package/templates/default/src/components/ui/badge.tsx +29 -0
- package/templates/default/src/components/ui/button.tsx +47 -0
- package/templates/default/src/components/ui/calendar.tsx +158 -0
- package/templates/default/src/components/ui/card.tsx +43 -0
- package/templates/default/src/components/ui/chart.tsx +369 -0
- package/templates/default/src/components/ui/checkbox.tsx +28 -0
- package/templates/default/src/components/ui/command.tsx +135 -0
- package/templates/default/src/components/ui/data-table-column-header.tsx +61 -0
- package/templates/default/src/components/ui/data-table-pagination.tsx +87 -0
- package/templates/default/src/components/ui/data-table-view-options.tsx +50 -0
- package/templates/default/src/components/ui/data-table.tsx +267 -0
- package/templates/default/src/components/ui/dialog.tsx +97 -0
- package/templates/default/src/components/ui/dropdown-menu.tsx +182 -0
- package/templates/default/src/components/ui/empty.tsx +104 -0
- package/templates/default/src/components/ui/input.tsx +22 -0
- package/templates/default/src/components/ui/kbd.tsx +28 -0
- package/templates/default/src/components/ui/label.tsx +19 -0
- package/templates/default/src/components/ui/loading.tsx +12 -0
- package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -0
- package/templates/default/src/components/ui/multi-select.tsx +142 -0
- package/templates/default/src/components/ui/navigation-menu.tsx +120 -0
- package/templates/default/src/components/ui/popover.tsx +33 -0
- package/templates/default/src/components/ui/progress.tsx +25 -0
- package/templates/default/src/components/ui/radio-group.tsx +44 -0
- package/templates/default/src/components/ui/scroll-area.tsx +40 -0
- package/templates/default/src/components/ui/select.tsx +144 -0
- package/templates/default/src/components/ui/separator.tsx +22 -0
- package/templates/default/src/components/ui/skeleton.tsx +7 -0
- package/templates/default/src/components/ui/table.tsx +76 -0
- package/templates/default/src/components/ui/tabs.tsx +55 -0
- package/templates/default/src/components/ui/textarea.tsx +21 -0
- package/templates/default/src/components/ui/toast.tsx +113 -0
- package/templates/default/src/components/ui/toaster.tsx +26 -0
- package/templates/default/src/components/ui/tooltip.tsx +32 -0
- package/templates/default/src/components/user-prompt/delete-prompt.tsx +87 -0
- package/templates/default/src/config/db-config.ts +10 -0
- package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -0
- package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -0
- package/templates/default/src/constants/form-opts/locator-form-opts.ts +20 -0
- package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -0
- package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -0
- package/templates/default/src/constants/form-opts/review-form-opts.ts +23 -0
- package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -0
- package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -0
- package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -0
- package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -0
- package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -0
- package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -0
- package/templates/default/src/constants/form-opts/test-run-form-opts.ts +31 -0
- package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -0
- package/templates/default/src/hooks/use-toast.ts +187 -0
- package/templates/default/src/lib/bidirectional-sync.ts +432 -0
- package/templates/default/src/lib/database-sync.ts +531 -0
- package/templates/default/src/lib/environment-file-utils.ts +221 -0
- package/templates/default/src/lib/feature-file-generator.ts +411 -0
- package/templates/default/src/lib/gherkin-parser.ts +259 -0
- package/templates/default/src/lib/locator-group-file-utils.ts +370 -0
- package/templates/default/src/lib/metrics/metric-calculator.ts +613 -0
- package/templates/default/src/lib/module-hierarchy-builder.ts +205 -0
- package/templates/default/src/lib/path-helpers/module-path.ts +71 -0
- package/templates/default/src/lib/test-case-utils.ts +6 -0
- package/templates/default/src/lib/test-run/log-formatter.ts +83 -0
- package/templates/default/src/lib/test-run/process-manager.ts +191 -0
- package/templates/default/src/lib/test-run/report-parser.ts +316 -0
- package/templates/default/src/lib/test-run/test-run-executor.ts +144 -0
- package/templates/default/src/lib/test-run/winston-logger.ts +95 -0
- package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -0
- package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -0
- package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -0
- package/templates/default/src/lib/utils/node-param-validation.ts +81 -0
- package/templates/default/src/lib/utils/template-step-file-generator.ts +167 -0
- package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +723 -0
- package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -0
- package/templates/default/src/lib/utils.ts +31 -0
- package/templates/default/src/tests/config/environments/environments.json +14 -0
- package/templates/default/src/tests/config/executor/world.ts +41 -0
- package/templates/default/src/tests/executor.ts +80 -0
- package/templates/default/src/tests/hooks/hooks.ts +99 -0
- package/templates/default/src/tests/mapping/locator-map.json +1 -0
- package/templates/default/src/tests/steps/actions/click.step.ts +62 -0
- package/templates/default/src/tests/steps/actions/hover.step.ts +31 -0
- package/templates/default/src/tests/steps/actions/input.step.ts +149 -0
- package/templates/default/src/tests/steps/actions/navigation.step.ts +72 -0
- package/templates/default/src/tests/steps/actions/random_data.step.ts +146 -0
- package/templates/default/src/tests/steps/actions/store.step.ts +90 -0
- package/templates/default/src/tests/steps/actions/wait.step.ts +107 -0
- package/templates/default/src/tests/steps/validations/active_state_assertion.step.ts +34 -0
- package/templates/default/src/tests/steps/validations/navigation_assertion.step.ts +23 -0
- package/templates/default/src/tests/steps/validations/text_assertion.step.ts +111 -0
- package/templates/default/src/tests/steps/validations/visibility_assertion.step.ts +30 -0
- package/templates/default/src/tests/support/parameter-types.ts +12 -0
- package/templates/default/src/tests/utils/cache.util.ts +260 -0
- package/templates/default/src/tests/utils/cli.util.ts +177 -0
- package/templates/default/src/tests/utils/environment.util.ts +65 -0
- package/templates/default/src/tests/utils/locator.util.ts +248 -0
- package/templates/default/src/tests/utils/random-data.util.ts +45 -0
- package/templates/default/src/tests/utils/spawner.util.ts +617 -0
- package/templates/default/src/types/diagram/diagram.ts +34 -0
- package/templates/default/src/types/diagram/template-step.ts +11 -0
- package/templates/default/src/types/executor/browser.type.ts +1 -0
- package/templates/default/src/types/form/actionHandler.ts +6 -0
- package/templates/default/src/types/locator/locator.type.ts +11 -0
- package/templates/default/src/types/step/step.type.ts +1 -0
- package/templates/default/src/types/table/data-table.ts +6 -0
- package/templates/default/tailwind.config.ts +62 -0
- package/templates/default/tsconfig.json +28 -0
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import { TemplateStep, TemplateStepGroupType } from '@prisma/client'
|
|
3
|
+
import { ensureStepsDirectory, getFilePath, formatFileContent } from './template-step-file-generator'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates JSDoc comments for a template step group
|
|
7
|
+
*/
|
|
8
|
+
function generateGroupJSDocComment(
|
|
9
|
+
name: string,
|
|
10
|
+
description: string | null,
|
|
11
|
+
type: TemplateStepGroupType | string,
|
|
12
|
+
): string {
|
|
13
|
+
const lines = ['/**']
|
|
14
|
+
lines.push(` * @name ${name}`)
|
|
15
|
+
if (description) {
|
|
16
|
+
lines.push(` * @description ${description}`)
|
|
17
|
+
}
|
|
18
|
+
lines.push(` * @type ${type}`)
|
|
19
|
+
lines.push(' */')
|
|
20
|
+
return lines.join('\n')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Extracts the bounds of the group JSDoc comment block at the top of the file
|
|
25
|
+
* Returns null if no group JSDoc is found
|
|
26
|
+
* Group JSDoc must be at the very top (line 0) and contain @type (which distinguishes it from step JSDoc)
|
|
27
|
+
*/
|
|
28
|
+
function extractGroupJSDocBounds(content: string): { startLine: number; endLine: number } | null {
|
|
29
|
+
const lines = content.split('\n')
|
|
30
|
+
|
|
31
|
+
// Look for JSDoc comment at the very top of the file
|
|
32
|
+
if (lines.length === 0) {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const firstLine = lines[0].trim()
|
|
37
|
+
if (!firstLine.startsWith('/**')) {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if this JSDoc contains group metadata (@type distinguishes group from step JSDoc)
|
|
42
|
+
let hasType = false
|
|
43
|
+
let endLine = -1
|
|
44
|
+
|
|
45
|
+
// Look through the JSDoc block (should end within first few lines)
|
|
46
|
+
for (let i = 0; i < lines.length && i < 10; i++) {
|
|
47
|
+
const line = lines[i].trim()
|
|
48
|
+
|
|
49
|
+
if (line === '*/') {
|
|
50
|
+
// Found end of JSDoc
|
|
51
|
+
endLine = i
|
|
52
|
+
break
|
|
53
|
+
} else if (line.startsWith('* @type') || line.startsWith('*@type')) {
|
|
54
|
+
// Found @type - this is group metadata (steps have @icon, not @type)
|
|
55
|
+
hasType = true
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// If we found a JSDoc block at the top with @type, return its bounds
|
|
60
|
+
if (hasType && endLine >= 0) {
|
|
61
|
+
return { startLine: 0, endLine }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Ensures group JSDoc exists and is up-to-date at the top of the file
|
|
69
|
+
* Preserves all other content including imports and template steps
|
|
70
|
+
*/
|
|
71
|
+
export function ensureGroupJSDoc(
|
|
72
|
+
content: string,
|
|
73
|
+
name: string,
|
|
74
|
+
description: string | null,
|
|
75
|
+
type: TemplateStepGroupType | string,
|
|
76
|
+
): string {
|
|
77
|
+
const jsdocBounds = extractGroupJSDocBounds(content)
|
|
78
|
+
const newJSDoc = generateGroupJSDocComment(name, description, type)
|
|
79
|
+
|
|
80
|
+
if (jsdocBounds) {
|
|
81
|
+
// Replace existing group JSDoc
|
|
82
|
+
const lines = content.split('\n')
|
|
83
|
+
const afterJSDoc = lines.slice(jsdocBounds.endLine + 1).join('\n')
|
|
84
|
+
|
|
85
|
+
// Combine: new JSDoc + content after old JSDoc
|
|
86
|
+
// Handle spacing - ensure there's proper spacing after JSDoc
|
|
87
|
+
if (afterJSDoc.trim()) {
|
|
88
|
+
return `${newJSDoc}\n${afterJSDoc.trimStart()}`
|
|
89
|
+
}
|
|
90
|
+
return `${newJSDoc}\n${afterJSDoc}`
|
|
91
|
+
} else {
|
|
92
|
+
// No existing group JSDoc, add it at the very top
|
|
93
|
+
if (content.trim()) {
|
|
94
|
+
return `${newJSDoc}\n${content.trimStart()}`
|
|
95
|
+
}
|
|
96
|
+
return `${newJSDoc}\n${content}`
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generates JSDoc comments for a template step
|
|
102
|
+
*/
|
|
103
|
+
function generateJSDocComment(templateStep: TemplateStep): string {
|
|
104
|
+
const lines = ['/**']
|
|
105
|
+
lines.push(` * @name ${templateStep.name}`)
|
|
106
|
+
if (templateStep.description) {
|
|
107
|
+
lines.push(` * @description ${templateStep.description}`)
|
|
108
|
+
}
|
|
109
|
+
lines.push(` * @icon ${templateStep.icon}`)
|
|
110
|
+
lines.push(' */')
|
|
111
|
+
return lines.join('\n')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Wraps a function definition with JSDoc comments
|
|
116
|
+
* If the function already has JSDoc comments, replaces them
|
|
117
|
+
*/
|
|
118
|
+
function wrapFunctionWithJSDoc(functionDefinition: string, templateStep: TemplateStep): string {
|
|
119
|
+
const jsdoc = generateJSDocComment(templateStep)
|
|
120
|
+
|
|
121
|
+
// Remove existing JSDoc comments if present
|
|
122
|
+
const cleanedDefinition = functionDefinition.replace(/\/\*\*[\s\S]*?\*\/\s*/g, '').trim()
|
|
123
|
+
|
|
124
|
+
// Prepend JSDoc and return
|
|
125
|
+
return `${jsdoc}\n${cleanedDefinition}`
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Checks if a template step update requires file changes
|
|
130
|
+
* Signature, parameter, and metadata (name, description, icon) changes require file updates
|
|
131
|
+
*/
|
|
132
|
+
function requiresFileUpdate(oldStep: TemplateStep, newStep: TemplateStep): boolean {
|
|
133
|
+
// Check if signature changed
|
|
134
|
+
if (oldStep.signature !== newStep.signature) {
|
|
135
|
+
return true
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check if function definition changed (this includes parameter changes)
|
|
139
|
+
if (oldStep.functionDefinition !== newStep.functionDefinition) {
|
|
140
|
+
return true
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check if metadata changed (name, description, icon) - these affect JSDoc comments
|
|
144
|
+
if (oldStep.name !== newStep.name) {
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (oldStep.description !== newStep.description) {
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (oldStep.icon !== newStep.icon) {
|
|
153
|
+
return true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// No file changes needed for other updates
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Finds the start and end lines of a step definition function in the file
|
|
162
|
+
* Uses flexible signature matching to handle prettier formatting variations
|
|
163
|
+
* Handles JSDoc comments that may precede the function
|
|
164
|
+
*/
|
|
165
|
+
function findStepFunctionBounds(content: string, signature: string): { startLine: number; endLine: number } | null {
|
|
166
|
+
const lines = content.split('\n')
|
|
167
|
+
|
|
168
|
+
// Search for the signature content across multiple lines (handles prettier formatting)
|
|
169
|
+
for (let i = 0; i < lines.length; i++) {
|
|
170
|
+
const line = lines[i]
|
|
171
|
+
const trimmedLine = line.trim()
|
|
172
|
+
|
|
173
|
+
// Skip JSDoc comment lines (but not the function definition itself)
|
|
174
|
+
if (
|
|
175
|
+
trimmedLine.startsWith('/**') ||
|
|
176
|
+
trimmedLine === '*/' ||
|
|
177
|
+
(trimmedLine.startsWith('*') && !trimmedLine.startsWith('When(') && !trimmedLine.startsWith('Then('))
|
|
178
|
+
) {
|
|
179
|
+
continue
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check if this line starts a step definition
|
|
183
|
+
if (trimmedLine.startsWith('When(') || trimmedLine.startsWith('Then(')) {
|
|
184
|
+
// Look ahead to find the complete signature across multiple lines
|
|
185
|
+
let signatureFound = false
|
|
186
|
+
let currentSignature = ''
|
|
187
|
+
|
|
188
|
+
// Collect signature content from current and following lines until we hit the function start
|
|
189
|
+
for (let j = i; j < lines.length; j++) {
|
|
190
|
+
const currentLine = lines[j]
|
|
191
|
+
currentSignature += currentLine
|
|
192
|
+
|
|
193
|
+
// Check if we've found our signature
|
|
194
|
+
if (currentSignature.includes(signature)) {
|
|
195
|
+
signatureFound = true
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// If we hit the function opening brace, stop looking for signature
|
|
200
|
+
if (currentLine.includes('async function') || currentLine.includes('function(')) {
|
|
201
|
+
break
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (signatureFound) {
|
|
206
|
+
// Found the start, now find the end using bracket and parenthesis counting
|
|
207
|
+
// Also check backwards for JSDoc comments to include them in the bounds
|
|
208
|
+
let functionStartLine = i
|
|
209
|
+
let braceCount = 0
|
|
210
|
+
let parenCount = 0
|
|
211
|
+
let startParenFound = false
|
|
212
|
+
|
|
213
|
+
// Check if there are JSDoc comments before this function
|
|
214
|
+
if (i > 0) {
|
|
215
|
+
let jsdocStart = i - 1
|
|
216
|
+
// Look backwards for JSDoc comment block
|
|
217
|
+
// First, check if the previous line is the end of a JSDoc block
|
|
218
|
+
if (lines[jsdocStart].trim() === '*/') {
|
|
219
|
+
// Found end of JSDoc, continue looking backwards for the start
|
|
220
|
+
jsdocStart--
|
|
221
|
+
while (jsdocStart >= 0) {
|
|
222
|
+
const prevLine = lines[jsdocStart].trim()
|
|
223
|
+
if (prevLine.startsWith('/**')) {
|
|
224
|
+
// Found start of JSDoc block
|
|
225
|
+
functionStartLine = jsdocStart
|
|
226
|
+
break
|
|
227
|
+
} else if (prevLine.startsWith('*') || prevLine === '') {
|
|
228
|
+
jsdocStart--
|
|
229
|
+
} else {
|
|
230
|
+
break
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (let j = i; j < lines.length; j++) {
|
|
237
|
+
const currentLine = lines[j]
|
|
238
|
+
|
|
239
|
+
// Count opening and closing parentheses and braces
|
|
240
|
+
for (const char of currentLine) {
|
|
241
|
+
if (char === '(') {
|
|
242
|
+
parenCount++
|
|
243
|
+
startParenFound = true
|
|
244
|
+
} else if (char === ')') {
|
|
245
|
+
parenCount--
|
|
246
|
+
} else if (char === '{') {
|
|
247
|
+
braceCount++
|
|
248
|
+
} else if (char === '}') {
|
|
249
|
+
braceCount--
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// If we found the opening parenthesis and both parentheses and braces are balanced, we're done
|
|
254
|
+
// This ensures we capture the complete When(...) or Then(...) call including the closing )
|
|
255
|
+
if (startParenFound && parenCount === 0 && braceCount === 0) {
|
|
256
|
+
return { startLine: functionStartLine, endLine: j }
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// If we reach here, something went wrong with bracket counting
|
|
261
|
+
console.warn(`Could not find end of function for signature: ${signature}`)
|
|
262
|
+
return null
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return null
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Required import definitions
|
|
272
|
+
*/
|
|
273
|
+
interface RequiredImport {
|
|
274
|
+
module: string
|
|
275
|
+
namedExports: string[]
|
|
276
|
+
from: string
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const REQUIRED_IMPORTS: RequiredImport[] = [
|
|
280
|
+
{
|
|
281
|
+
module: '@cucumber/cucumber',
|
|
282
|
+
namedExports: ['When'],
|
|
283
|
+
from: '@cucumber/cucumber',
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
module: '../../config/executor/world',
|
|
287
|
+
namedExports: ['CustomWorld'],
|
|
288
|
+
from: '../../config/executor/world.js',
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
module: '@/types/locator/locator.type',
|
|
292
|
+
namedExports: ['SelectorName'],
|
|
293
|
+
from: '@/types/locator/locator.type',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
module: '../../utils/locator.util',
|
|
297
|
+
namedExports: ['resolveLocator'],
|
|
298
|
+
from: '../../utils/locator.util.js',
|
|
299
|
+
},
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Parses import statements from file content
|
|
304
|
+
* Returns an array of import objects with their line numbers
|
|
305
|
+
*/
|
|
306
|
+
interface ParsedImport {
|
|
307
|
+
line: number
|
|
308
|
+
fullLine: string
|
|
309
|
+
namedExports: string[]
|
|
310
|
+
from: string
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function parseImports(content: string): ParsedImport[] {
|
|
314
|
+
const lines = content.split('\n')
|
|
315
|
+
const imports: ParsedImport[] = []
|
|
316
|
+
|
|
317
|
+
for (let i = 0; i < lines.length; i++) {
|
|
318
|
+
const line = lines[i].trim()
|
|
319
|
+
// Match import statements: import { ... } from '...'
|
|
320
|
+
const importMatch = line.match(/^import\s+{([^}]+)}\s+from\s+['"]([^'"]+)['"];?$/)
|
|
321
|
+
if (importMatch) {
|
|
322
|
+
const namedExportsStr = importMatch[1]
|
|
323
|
+
const fromPath = importMatch[2]
|
|
324
|
+
// Parse named exports, handling whitespace
|
|
325
|
+
const namedExports = namedExportsStr
|
|
326
|
+
.split(',')
|
|
327
|
+
.map(exp => exp.trim())
|
|
328
|
+
.filter(Boolean)
|
|
329
|
+
|
|
330
|
+
imports.push({
|
|
331
|
+
line: i,
|
|
332
|
+
fullLine: line,
|
|
333
|
+
namedExports,
|
|
334
|
+
from: fromPath,
|
|
335
|
+
})
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return imports
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Checks if a required import is present in the parsed imports
|
|
344
|
+
* Handles variations like with/without .js extension, different import styles
|
|
345
|
+
*/
|
|
346
|
+
function hasRequiredImport(parsedImports: ParsedImport[], required: RequiredImport): boolean {
|
|
347
|
+
for (const parsed of parsedImports) {
|
|
348
|
+
// Normalize paths for comparison (remove .js extension if present)
|
|
349
|
+
const normalizedParsedFrom = parsed.from.replace(/\.js$/, '')
|
|
350
|
+
const normalizedRequiredFrom = required.from.replace(/\.js$/, '')
|
|
351
|
+
|
|
352
|
+
// Check if the module path matches (with or without .js)
|
|
353
|
+
if (normalizedParsedFrom === normalizedRequiredFrom || parsed.from === required.from) {
|
|
354
|
+
// Check if all required named exports are present
|
|
355
|
+
const hasAllExports = required.namedExports.every(exp => parsed.namedExports.includes(exp))
|
|
356
|
+
if (hasAllExports) {
|
|
357
|
+
return true
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return false
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Generates an import statement string for a required import
|
|
366
|
+
*/
|
|
367
|
+
function generateImportStatement(required: RequiredImport): string {
|
|
368
|
+
return `import { ${required.namedExports.join(', ')} } from '${required.from}';`
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Ensures required imports are present in the file content
|
|
373
|
+
* Only adds missing imports, preserves existing imports and their order
|
|
374
|
+
* Preserves group JSDoc at the top if it exists
|
|
375
|
+
* @internal - exported for testing purposes
|
|
376
|
+
*/
|
|
377
|
+
export function ensureRequiredImports(content: string): string {
|
|
378
|
+
// Parse existing imports
|
|
379
|
+
const parsedImports = parseImports(content)
|
|
380
|
+
|
|
381
|
+
// Check which required imports are missing
|
|
382
|
+
const missingImports: RequiredImport[] = []
|
|
383
|
+
for (const required of REQUIRED_IMPORTS) {
|
|
384
|
+
if (!hasRequiredImport(parsedImports, required)) {
|
|
385
|
+
missingImports.push(required)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// If all imports are present, return content unchanged
|
|
390
|
+
if (missingImports.length === 0) {
|
|
391
|
+
return content
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Generate import statements for missing imports
|
|
395
|
+
const newImportStatements = missingImports.map(generateImportStatement).join('\n') + '\n'
|
|
396
|
+
|
|
397
|
+
// Check if there's a group JSDoc at the top
|
|
398
|
+
const jsdocBounds = extractGroupJSDocBounds(content)
|
|
399
|
+
|
|
400
|
+
if (jsdocBounds) {
|
|
401
|
+
// Preserve JSDoc, add missing imports after it
|
|
402
|
+
const lines = content.split('\n')
|
|
403
|
+
const jsdoc = lines.slice(jsdocBounds.startLine, jsdocBounds.endLine + 1).join('\n')
|
|
404
|
+
const afterJSDoc = lines.slice(jsdocBounds.endLine + 1).join('\n')
|
|
405
|
+
|
|
406
|
+
// Check if there are already imports after JSDoc
|
|
407
|
+
const afterJSDocTrimmed = afterJSDoc.trimStart()
|
|
408
|
+
if (afterJSDocTrimmed.startsWith('import ')) {
|
|
409
|
+
// Imports already exist, add missing ones right after JSDoc (before existing imports)
|
|
410
|
+
// This preserves the existing import order
|
|
411
|
+
return `${jsdoc}\n${newImportStatements}${afterJSDoc}`
|
|
412
|
+
} else {
|
|
413
|
+
// No imports after JSDoc, add them
|
|
414
|
+
return `${jsdoc}\n${newImportStatements}${afterJSDocTrimmed}`
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// No JSDoc, check if there are existing imports
|
|
419
|
+
if (parsedImports.length > 0) {
|
|
420
|
+
// Find the first import line
|
|
421
|
+
const firstImportLine = parsedImports[0].line
|
|
422
|
+
const lines = content.split('\n')
|
|
423
|
+
const beforeImports = lines.slice(0, firstImportLine).join('\n')
|
|
424
|
+
const afterImports = lines.slice(firstImportLine).join('\n')
|
|
425
|
+
// Add missing imports before existing imports
|
|
426
|
+
return `${beforeImports}${beforeImports ? '\n' : ''}${newImportStatements}${afterImports}`
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// No JSDoc, no existing imports, add imports at the beginning
|
|
430
|
+
return newImportStatements + content
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Intelligently adds a new template step to the file
|
|
435
|
+
* Preserves existing content including imports, types, and other code
|
|
436
|
+
*/
|
|
437
|
+
export async function addTemplateStepToFile(
|
|
438
|
+
groupName: string,
|
|
439
|
+
templateStep: TemplateStep,
|
|
440
|
+
type: TemplateStepGroupType | string,
|
|
441
|
+
): Promise<void> {
|
|
442
|
+
try {
|
|
443
|
+
await ensureStepsDirectory()
|
|
444
|
+
const filePath = getFilePath(groupName, type)
|
|
445
|
+
|
|
446
|
+
let existingContent = ''
|
|
447
|
+
try {
|
|
448
|
+
existingContent = await fs.readFile(filePath, 'utf8')
|
|
449
|
+
} catch {
|
|
450
|
+
// File doesn't exist, start with empty content
|
|
451
|
+
existingContent = ''
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Ensure required imports are present
|
|
455
|
+
existingContent = ensureRequiredImports(existingContent)
|
|
456
|
+
|
|
457
|
+
// Check if step with this signature already exists
|
|
458
|
+
const bounds = findStepFunctionBounds(existingContent, templateStep.signature)
|
|
459
|
+
|
|
460
|
+
let newContent: string
|
|
461
|
+
|
|
462
|
+
if (bounds) {
|
|
463
|
+
// Replace existing step - replace the entire function with JSDoc comments
|
|
464
|
+
const lines = existingContent.split('\n')
|
|
465
|
+
const beforeStep = lines.slice(0, bounds.startLine).join('\n')
|
|
466
|
+
const afterStep = lines.slice(bounds.endLine + 1).join('\n')
|
|
467
|
+
|
|
468
|
+
// Wrap the function definition with JSDoc comments
|
|
469
|
+
const wrappedStepDefinition = wrapFunctionWithJSDoc(templateStep.functionDefinition || '', templateStep)
|
|
470
|
+
newContent =
|
|
471
|
+
beforeStep +
|
|
472
|
+
(beforeStep.trim() ? '\n\n' : '') +
|
|
473
|
+
wrappedStepDefinition +
|
|
474
|
+
(afterStep.trim() ? '\n' : '') +
|
|
475
|
+
afterStep
|
|
476
|
+
} else {
|
|
477
|
+
// Add new step at the end - wrap with JSDoc comments
|
|
478
|
+
const wrappedStepDefinition = wrapFunctionWithJSDoc(templateStep.functionDefinition || '', templateStep)
|
|
479
|
+
newContent = existingContent + (existingContent ? '\n\n' : '') + wrappedStepDefinition
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Format and write the file
|
|
483
|
+
const formattedContent = await formatFileContent(newContent)
|
|
484
|
+
await fs.writeFile(filePath, formattedContent, 'utf8')
|
|
485
|
+
|
|
486
|
+
console.log(`Template step added to file: ${filePath}`)
|
|
487
|
+
} catch (error) {
|
|
488
|
+
console.error(`Failed to add template step to file for group "${groupName}":`, error)
|
|
489
|
+
throw new Error(`File update failed: ${error}`)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Intelligently removes a template step from the file
|
|
495
|
+
* Only removes the specific step, preserves everything else including imports and types
|
|
496
|
+
*/
|
|
497
|
+
export async function removeTemplateStepFromFile(
|
|
498
|
+
groupName: string,
|
|
499
|
+
templateStep: TemplateStep,
|
|
500
|
+
type: TemplateStepGroupType | string,
|
|
501
|
+
): Promise<void> {
|
|
502
|
+
try {
|
|
503
|
+
await ensureStepsDirectory()
|
|
504
|
+
const filePath = getFilePath(groupName, type)
|
|
505
|
+
|
|
506
|
+
let existingContent = ''
|
|
507
|
+
try {
|
|
508
|
+
existingContent = await fs.readFile(filePath, 'utf8')
|
|
509
|
+
} catch {
|
|
510
|
+
// File doesn't exist, nothing to remove
|
|
511
|
+
return
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Ensure required imports are present
|
|
515
|
+
existingContent = ensureRequiredImports(existingContent)
|
|
516
|
+
|
|
517
|
+
// Find the step to remove
|
|
518
|
+
const bounds = findStepFunctionBounds(existingContent, templateStep.signature)
|
|
519
|
+
|
|
520
|
+
if (!bounds) {
|
|
521
|
+
// Step not found, nothing to remove
|
|
522
|
+
return
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Remove the entire function block
|
|
526
|
+
const lines = existingContent.split('\n')
|
|
527
|
+
const beforeStep = lines.slice(0, bounds.startLine).join('\n')
|
|
528
|
+
const afterStep = lines.slice(bounds.endLine + 1).join('\n')
|
|
529
|
+
|
|
530
|
+
// Combine content, handling empty sections
|
|
531
|
+
let newContent = ''
|
|
532
|
+
if (beforeStep.trim()) {
|
|
533
|
+
newContent += beforeStep.trim()
|
|
534
|
+
}
|
|
535
|
+
if (afterStep.trim()) {
|
|
536
|
+
if (newContent) newContent += '\n'
|
|
537
|
+
newContent += afterStep.trim()
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Format and write the file
|
|
541
|
+
const formattedContent = await formatFileContent(newContent)
|
|
542
|
+
await fs.writeFile(filePath, formattedContent, 'utf8')
|
|
543
|
+
|
|
544
|
+
console.log(`Template step removed from file: ${filePath}`)
|
|
545
|
+
} catch (error) {
|
|
546
|
+
console.error(`Failed to remove template step from file for group "${groupName}":`, error)
|
|
547
|
+
throw new Error(`File update failed: ${error}`)
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Intelligently updates a template step in the file
|
|
553
|
+
* Only updates the specific step, preserves everything else including imports and types
|
|
554
|
+
*/
|
|
555
|
+
export async function updateTemplateStepInFile(
|
|
556
|
+
groupName: string,
|
|
557
|
+
templateStep: TemplateStep,
|
|
558
|
+
type: TemplateStepGroupType | string,
|
|
559
|
+
oldStep?: TemplateStep,
|
|
560
|
+
): Promise<void> {
|
|
561
|
+
try {
|
|
562
|
+
// If we have the old step data, check if file changes are needed
|
|
563
|
+
if (oldStep && !requiresFileUpdate(oldStep, templateStep)) {
|
|
564
|
+
console.log(`No file changes needed for template step update: ${templateStep.signature}`)
|
|
565
|
+
return
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
await ensureStepsDirectory()
|
|
569
|
+
const filePath = getFilePath(groupName, type)
|
|
570
|
+
|
|
571
|
+
let existingContent = ''
|
|
572
|
+
try {
|
|
573
|
+
existingContent = await fs.readFile(filePath, 'utf8')
|
|
574
|
+
} catch {
|
|
575
|
+
// File doesn't exist, create it with the updated step
|
|
576
|
+
await addTemplateStepToFile(groupName, templateStep, type)
|
|
577
|
+
return
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Ensure required imports are present
|
|
581
|
+
existingContent = ensureRequiredImports(existingContent)
|
|
582
|
+
|
|
583
|
+
// Find the step to update
|
|
584
|
+
const bounds = findStepFunctionBounds(existingContent, oldStep!.signature)
|
|
585
|
+
|
|
586
|
+
if (bounds) {
|
|
587
|
+
// Update existing step - replace the entire function with JSDoc comments
|
|
588
|
+
const lines = existingContent.split('\n')
|
|
589
|
+
const beforeStep = lines.slice(0, bounds.startLine).join('\n')
|
|
590
|
+
const afterStep = lines.slice(bounds.endLine + 1).join('\n')
|
|
591
|
+
|
|
592
|
+
// Wrap the function definition with JSDoc comments
|
|
593
|
+
const wrappedStepDefinition = wrapFunctionWithJSDoc(templateStep.functionDefinition || '', templateStep)
|
|
594
|
+
const newContent =
|
|
595
|
+
beforeStep +
|
|
596
|
+
(beforeStep.trim() ? '\n\n' : '') +
|
|
597
|
+
wrappedStepDefinition +
|
|
598
|
+
(afterStep.trim() ? '\n' : '') +
|
|
599
|
+
afterStep
|
|
600
|
+
|
|
601
|
+
// Format and write the file
|
|
602
|
+
const formattedContent = await formatFileContent(newContent)
|
|
603
|
+
await fs.writeFile(filePath, formattedContent, 'utf8')
|
|
604
|
+
|
|
605
|
+
console.log(`Template step updated in file: ${filePath}`)
|
|
606
|
+
} else {
|
|
607
|
+
// Step not found, add it
|
|
608
|
+
await addTemplateStepToFile(groupName, templateStep, type)
|
|
609
|
+
}
|
|
610
|
+
} catch (error) {
|
|
611
|
+
console.error(`Failed to update template step in file for group "${groupName}":`, error)
|
|
612
|
+
throw new Error(`File update failed: ${error}`)
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Creates a placeholder file for a new template step group
|
|
618
|
+
*/
|
|
619
|
+
export async function createTemplateStepGroupFile(
|
|
620
|
+
groupName: string,
|
|
621
|
+
type: TemplateStepGroupType | string,
|
|
622
|
+
description?: string | null,
|
|
623
|
+
): Promise<void> {
|
|
624
|
+
try {
|
|
625
|
+
await ensureStepsDirectory()
|
|
626
|
+
const filePath = getFilePath(groupName, type)
|
|
627
|
+
|
|
628
|
+
// Generate content with JSDoc at the top, then imports, then placeholder comment
|
|
629
|
+
const groupJSDoc = generateGroupJSDocComment(groupName, description || null, type)
|
|
630
|
+
const requiredImports = `import { When } from '@cucumber/cucumber';
|
|
631
|
+
import { CustomWorld } from '../../config/executor/world.js';
|
|
632
|
+
import { SelectorName } from '@/types/locator/locator.type';
|
|
633
|
+
import { resolveLocator } from '../../utils/locator.util.js';
|
|
634
|
+
|
|
635
|
+
`
|
|
636
|
+
const placeholderComment =
|
|
637
|
+
'// This file is generated automatically. Add template steps to this group to generate content.'
|
|
638
|
+
|
|
639
|
+
// Combine: JSDoc + imports + placeholder comment
|
|
640
|
+
const placeholderContent = `${groupJSDoc}\n${requiredImports}${placeholderComment}`
|
|
641
|
+
|
|
642
|
+
// Format and write the file
|
|
643
|
+
const formattedContent = await formatFileContent(placeholderContent)
|
|
644
|
+
await fs.writeFile(filePath, formattedContent, 'utf8')
|
|
645
|
+
|
|
646
|
+
console.log(`Placeholder file created for template step group: ${groupName}`)
|
|
647
|
+
} catch (error) {
|
|
648
|
+
console.error(`Failed to create placeholder file for group "${groupName}":`, error)
|
|
649
|
+
throw new Error(`File creation failed: ${error}`)
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Deletes the file for a template step group
|
|
655
|
+
*/
|
|
656
|
+
export async function removeTemplateStepGroupFile(
|
|
657
|
+
groupName: string,
|
|
658
|
+
type: TemplateStepGroupType | string,
|
|
659
|
+
): Promise<void> {
|
|
660
|
+
try {
|
|
661
|
+
const filePath = getFilePath(groupName, type)
|
|
662
|
+
|
|
663
|
+
try {
|
|
664
|
+
await fs.access(filePath)
|
|
665
|
+
} catch {
|
|
666
|
+
// File doesn't exist, nothing to delete
|
|
667
|
+
return
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
await fs.unlink(filePath)
|
|
671
|
+
|
|
672
|
+
console.log(`Template step group file deleted: ${filePath}`)
|
|
673
|
+
} catch (error) {
|
|
674
|
+
console.error(`Failed to delete file for group "${groupName}":`, error)
|
|
675
|
+
throw new Error(`File deletion failed: ${error}`)
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Renames a template step group file when the group name or type changes
|
|
681
|
+
* Preserves all existing content and updates JSDoc metadata
|
|
682
|
+
* If type changed, moves file from old folder to new folder
|
|
683
|
+
*/
|
|
684
|
+
export async function renameTemplateStepGroupFile(
|
|
685
|
+
oldGroupName: string,
|
|
686
|
+
newGroupName: string,
|
|
687
|
+
oldType: TemplateStepGroupType | string,
|
|
688
|
+
newType: TemplateStepGroupType | string,
|
|
689
|
+
newDescription?: string | null,
|
|
690
|
+
): Promise<void> {
|
|
691
|
+
try {
|
|
692
|
+
await ensureStepsDirectory()
|
|
693
|
+
const oldFilePath = getFilePath(oldGroupName, oldType)
|
|
694
|
+
const newFilePath = getFilePath(newGroupName, newType)
|
|
695
|
+
|
|
696
|
+
try {
|
|
697
|
+
// Read the existing file content
|
|
698
|
+
let existingContent = await fs.readFile(oldFilePath, 'utf8')
|
|
699
|
+
|
|
700
|
+
// Update the group JSDoc with new metadata
|
|
701
|
+
existingContent = ensureGroupJSDoc(existingContent, newGroupName, newDescription || null, newType)
|
|
702
|
+
|
|
703
|
+
// Format and write the content to the new file
|
|
704
|
+
const formattedContent = await formatFileContent(existingContent)
|
|
705
|
+
await fs.writeFile(newFilePath, formattedContent, 'utf8')
|
|
706
|
+
|
|
707
|
+
// Remove the old file
|
|
708
|
+
await fs.unlink(oldFilePath)
|
|
709
|
+
|
|
710
|
+
console.log(`Template step group file renamed: ${oldFilePath} → ${newFilePath}`)
|
|
711
|
+
} catch (error) {
|
|
712
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
713
|
+
// Old file doesn't exist, which is fine
|
|
714
|
+
console.log(`Old template step group file doesn't exist: ${oldFilePath}`)
|
|
715
|
+
} else {
|
|
716
|
+
throw error
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
} catch (error) {
|
|
720
|
+
console.error(`Failed to rename template step group file from "${oldGroupName}" to "${newGroupName}":`, error)
|
|
721
|
+
throw new Error(`File rename failed: ${error}`)
|
|
722
|
+
}
|
|
723
|
+
}
|