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,221 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
import prisma from '@/config/db-config'
|
|
4
|
+
|
|
5
|
+
interface EnvironmentConfig {
|
|
6
|
+
baseUrl: string
|
|
7
|
+
apiBaseUrl: string
|
|
8
|
+
email: string
|
|
9
|
+
password: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Gets the file path for the environments.json file
|
|
14
|
+
*/
|
|
15
|
+
export function getEnvironmentsFilePath(): string {
|
|
16
|
+
return path.join('src', 'tests', 'config', 'environments', 'environments.json')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Ensures the config directory exists
|
|
21
|
+
*/
|
|
22
|
+
export async function ensureConfigDirectoryExists(): Promise<void> {
|
|
23
|
+
const filePath = getEnvironmentsFilePath()
|
|
24
|
+
const dir = path.dirname(filePath)
|
|
25
|
+
try {
|
|
26
|
+
await fs.access(dir)
|
|
27
|
+
} catch {
|
|
28
|
+
await fs.mkdir(dir, { recursive: true })
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generates JSON content for environments from database
|
|
34
|
+
*/
|
|
35
|
+
export async function generateEnvironmentsContent(): Promise<Record<string, EnvironmentConfig>> {
|
|
36
|
+
try {
|
|
37
|
+
const environments = await prisma.environment.findMany({
|
|
38
|
+
orderBy: { createdAt: 'asc' },
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const environmentsConfig: Record<string, EnvironmentConfig> = {}
|
|
42
|
+
|
|
43
|
+
environments.forEach(env => {
|
|
44
|
+
const envKey = env.name.toLowerCase().replace(/\s+/g, '_')
|
|
45
|
+
environmentsConfig[envKey] = {
|
|
46
|
+
baseUrl: env.baseUrl,
|
|
47
|
+
apiBaseUrl: env.apiBaseUrl || '',
|
|
48
|
+
email: env.username || '',
|
|
49
|
+
password: env.password || '',
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
return environmentsConfig
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('Error generating environments content:', error)
|
|
56
|
+
return {}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates or updates the environments.json file
|
|
62
|
+
*/
|
|
63
|
+
export async function createOrUpdateEnvironmentsFile(): Promise<boolean> {
|
|
64
|
+
try {
|
|
65
|
+
const filePath = getEnvironmentsFilePath()
|
|
66
|
+
await ensureConfigDirectoryExists()
|
|
67
|
+
|
|
68
|
+
const content = await generateEnvironmentsContent()
|
|
69
|
+
|
|
70
|
+
// If no environments exist, delete the file
|
|
71
|
+
if (Object.keys(content).length === 0) {
|
|
72
|
+
await deleteEnvironmentsFile()
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await fs.writeFile(filePath, JSON.stringify(content, null, 2))
|
|
77
|
+
return true
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error('Error creating/updating environments file:', error)
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Deletes the environments.json file
|
|
86
|
+
*/
|
|
87
|
+
export async function deleteEnvironmentsFile(): Promise<boolean> {
|
|
88
|
+
try {
|
|
89
|
+
const filePath = getEnvironmentsFilePath()
|
|
90
|
+
|
|
91
|
+
// Check if file exists before trying to delete
|
|
92
|
+
try {
|
|
93
|
+
await fs.access(filePath)
|
|
94
|
+
} catch {
|
|
95
|
+
return true // File doesn't exist, nothing to delete
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await fs.unlink(filePath)
|
|
99
|
+
return true
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error('Error deleting environments file:', error)
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Reads and parses the content of the environments.json file
|
|
108
|
+
*/
|
|
109
|
+
export async function readEnvironmentsFile(): Promise<{
|
|
110
|
+
filePath: string
|
|
111
|
+
content: Record<string, EnvironmentConfig>
|
|
112
|
+
} | null> {
|
|
113
|
+
try {
|
|
114
|
+
const filePath = getEnvironmentsFilePath()
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
await fs.access(filePath)
|
|
118
|
+
} catch {
|
|
119
|
+
return null // File doesn't exist
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const fileContent = await fs.readFile(filePath, 'utf-8')
|
|
123
|
+
const jsonContent = JSON.parse(fileContent)
|
|
124
|
+
|
|
125
|
+
return { filePath, content: jsonContent }
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error('Error reading environments file:', error)
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Updates a specific environment entry in the environments.json file
|
|
134
|
+
*/
|
|
135
|
+
export async function updateEnvironmentEntry(environmentId: string, oldName?: string): Promise<boolean> {
|
|
136
|
+
try {
|
|
137
|
+
// Get the environment from database
|
|
138
|
+
const environment = await prisma.environment.findUnique({
|
|
139
|
+
where: { id: environmentId },
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
if (!environment) {
|
|
143
|
+
console.error(`Environment with ID ${environmentId} not found`)
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const filePath = getEnvironmentsFilePath()
|
|
148
|
+
|
|
149
|
+
// Read existing content
|
|
150
|
+
let environmentsConfig: Record<string, EnvironmentConfig> = {}
|
|
151
|
+
try {
|
|
152
|
+
await fs.access(filePath)
|
|
153
|
+
const fileContent = await fs.readFile(filePath, 'utf-8')
|
|
154
|
+
environmentsConfig = JSON.parse(fileContent)
|
|
155
|
+
} catch {
|
|
156
|
+
// File doesn't exist, start with empty object
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Remove old entry if name changed
|
|
160
|
+
if (oldName) {
|
|
161
|
+
const oldKey = oldName.toLowerCase().replace(/\s+/g, '_')
|
|
162
|
+
delete environmentsConfig[oldKey]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Add/update the environment entry
|
|
166
|
+
const envKey = environment.name.toLowerCase().replace(/\s+/g, '_')
|
|
167
|
+
environmentsConfig[envKey] = {
|
|
168
|
+
baseUrl: environment.baseUrl,
|
|
169
|
+
apiBaseUrl: environment.apiBaseUrl || '',
|
|
170
|
+
email: environment.username || '',
|
|
171
|
+
password: environment.password || '',
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Ensure directory exists
|
|
175
|
+
await ensureConfigDirectoryExists()
|
|
176
|
+
|
|
177
|
+
// Write updated content
|
|
178
|
+
await fs.writeFile(filePath, JSON.stringify(environmentsConfig, null, 2))
|
|
179
|
+
return true
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('Error updating environment entry:', error)
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Removes a specific environment entry from the environments.json file
|
|
188
|
+
*/
|
|
189
|
+
export async function removeEnvironmentEntry(environmentName: string): Promise<boolean> {
|
|
190
|
+
try {
|
|
191
|
+
const filePath = getEnvironmentsFilePath()
|
|
192
|
+
|
|
193
|
+
// Check if file exists
|
|
194
|
+
try {
|
|
195
|
+
await fs.access(filePath)
|
|
196
|
+
} catch {
|
|
197
|
+
return true // File doesn't exist, nothing to remove
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Read existing content
|
|
201
|
+
const fileContent = await fs.readFile(filePath, 'utf-8')
|
|
202
|
+
const environmentsConfig: Record<string, EnvironmentConfig> = JSON.parse(fileContent)
|
|
203
|
+
|
|
204
|
+
// Remove the environment entry
|
|
205
|
+
const envKey = environmentName.toLowerCase().replace(/\s+/g, '_')
|
|
206
|
+
delete environmentsConfig[envKey]
|
|
207
|
+
|
|
208
|
+
// If no environments left, delete the file
|
|
209
|
+
if (Object.keys(environmentsConfig).length === 0) {
|
|
210
|
+
await deleteEnvironmentsFile()
|
|
211
|
+
return true
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Write updated content
|
|
215
|
+
await fs.writeFile(filePath, JSON.stringify(environmentsConfig, null, 2))
|
|
216
|
+
return true
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error('Error removing environment entry:', error)
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import { join, dirname } from 'path'
|
|
3
|
+
import prisma from '@/config/db-config'
|
|
4
|
+
import { buildModulePath } from '@/lib/path-helpers/module-path'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Checks if a directory is empty (no files or subdirectories)
|
|
8
|
+
* @param dirPath - Path to the directory to check
|
|
9
|
+
* @returns Promise<boolean> - True if directory is empty, false otherwise
|
|
10
|
+
*/
|
|
11
|
+
async function isDirectoryEmpty(dirPath: string): Promise<boolean> {
|
|
12
|
+
try {
|
|
13
|
+
const entries = await fs.readdir(dirPath)
|
|
14
|
+
return entries.length === 0
|
|
15
|
+
} catch (error) {
|
|
16
|
+
// If directory doesn't exist or can't be read, consider it empty
|
|
17
|
+
console.warn(`Could not read directory ${dirPath}:`, error)
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Removes empty directories up the hierarchy until a non-empty directory is found
|
|
24
|
+
* @param dirPath - Starting directory path to clean up
|
|
25
|
+
* @param basePath - Base path to stop cleaning (e.g., features directory)
|
|
26
|
+
* @returns Promise<void>
|
|
27
|
+
*/
|
|
28
|
+
async function removeEmptyDirectoriesUp(dirPath: string, basePath: string): Promise<void> {
|
|
29
|
+
let currentPath = dirPath
|
|
30
|
+
|
|
31
|
+
// Keep going up the directory tree until we reach the base path
|
|
32
|
+
while (currentPath !== basePath && currentPath !== dirname(currentPath)) {
|
|
33
|
+
try {
|
|
34
|
+
// Check if current directory is empty
|
|
35
|
+
if (await isDirectoryEmpty(currentPath)) {
|
|
36
|
+
await fs.rmdir(currentPath)
|
|
37
|
+
console.log(`Removed empty directory: ${currentPath}`)
|
|
38
|
+
// Move up one level
|
|
39
|
+
currentPath = dirname(currentPath)
|
|
40
|
+
} else {
|
|
41
|
+
// Directory is not empty, stop cleaning
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
// If we can't remove the directory or it doesn't exist, stop
|
|
46
|
+
console.warn(`Could not remove directory ${currentPath}:`, error)
|
|
47
|
+
break
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generates a Gherkin feature file for a test suite
|
|
54
|
+
* @param testSuiteId - The ID of the test suite
|
|
55
|
+
* @param testSuiteName - The name of the test suite
|
|
56
|
+
* @param testSuiteDescription - The description of the test suite
|
|
57
|
+
* @param moduleName - The name of the module the test suite belongs to
|
|
58
|
+
* @returns Promise<string> - The path to the generated feature file
|
|
59
|
+
*/
|
|
60
|
+
export async function generateFeatureFile(
|
|
61
|
+
testSuiteId: string,
|
|
62
|
+
testSuiteName: string,
|
|
63
|
+
testSuiteDescription?: string,
|
|
64
|
+
): Promise<string> {
|
|
65
|
+
try {
|
|
66
|
+
// Fetch test suite with test cases, steps, tags, and all modules for path building
|
|
67
|
+
const [testSuite, allModules] = await Promise.all([
|
|
68
|
+
prisma.testSuite.findUnique({
|
|
69
|
+
where: { id: testSuiteId },
|
|
70
|
+
include: {
|
|
71
|
+
testCases: {
|
|
72
|
+
include: {
|
|
73
|
+
steps: {
|
|
74
|
+
include: {
|
|
75
|
+
parameters: true,
|
|
76
|
+
},
|
|
77
|
+
orderBy: {
|
|
78
|
+
order: 'asc',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
tags: true,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
module: true,
|
|
85
|
+
tags: true,
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
prisma.module.findMany(), // Get all modules to build hierarchy path
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
if (!testSuite) {
|
|
92
|
+
throw new Error(`Test suite with ID ${testSuiteId} not found`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Build the module path for directory structure
|
|
96
|
+
const modulePath = buildModulePath(allModules, testSuite.module)
|
|
97
|
+
|
|
98
|
+
// Generate feature file content
|
|
99
|
+
const featureContent = generateFeatureContent(
|
|
100
|
+
testSuiteDescription || testSuiteName, // Use description as feature title, fallback to name
|
|
101
|
+
testSuite.testCases,
|
|
102
|
+
testSuite.tags,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
// Create the features directory with module path
|
|
106
|
+
const featuresBaseDir = join(process.cwd(), 'src', 'tests', 'features')
|
|
107
|
+
const moduleDir = join(featuresBaseDir, modulePath.substring(1)) // Remove leading slash
|
|
108
|
+
await fs.mkdir(moduleDir, { recursive: true })
|
|
109
|
+
|
|
110
|
+
// Generate a safe filename from the test suite name only
|
|
111
|
+
const safeFileName = generateSafeFileName(testSuiteName)
|
|
112
|
+
const featureFilePath = join(moduleDir, `${safeFileName}.feature`)
|
|
113
|
+
|
|
114
|
+
// Write the feature file
|
|
115
|
+
await fs.writeFile(featureFilePath, featureContent, 'utf8')
|
|
116
|
+
|
|
117
|
+
return featureFilePath
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('Error generating feature file:', error)
|
|
120
|
+
throw error
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Generates the content for a Gherkin feature file
|
|
126
|
+
*/
|
|
127
|
+
function generateFeatureContent(
|
|
128
|
+
featureTitle: string,
|
|
129
|
+
testCases: Array<{
|
|
130
|
+
title: string
|
|
131
|
+
description: string
|
|
132
|
+
steps: Array<{
|
|
133
|
+
gherkinStep: string
|
|
134
|
+
order: number
|
|
135
|
+
}>
|
|
136
|
+
tags?: Array<{
|
|
137
|
+
tagExpression: string
|
|
138
|
+
}>
|
|
139
|
+
}>,
|
|
140
|
+
testSuiteTags?: Array<{
|
|
141
|
+
tagExpression: string
|
|
142
|
+
}>,
|
|
143
|
+
): string {
|
|
144
|
+
const lines: string[] = []
|
|
145
|
+
|
|
146
|
+
// Warning header
|
|
147
|
+
lines.push('# AUTO-GENERATED FILE - DO NOT EDIT MANUALLY')
|
|
148
|
+
lines.push('# This file is automatically generated from Test Suite data.')
|
|
149
|
+
lines.push('# Any manual changes will be overwritten when the Test Suite is updated.')
|
|
150
|
+
lines.push('# To modify this feature, update the corresponding Test Suite in the application.')
|
|
151
|
+
lines.push('')
|
|
152
|
+
|
|
153
|
+
// Add feature-level tags (one tag per line)
|
|
154
|
+
if (testSuiteTags && testSuiteTags.length > 0) {
|
|
155
|
+
testSuiteTags.forEach(tag => {
|
|
156
|
+
lines.push(tag.tagExpression)
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Feature header - use description as feature title
|
|
161
|
+
lines.push(`Feature: ${featureTitle}`)
|
|
162
|
+
lines.push('')
|
|
163
|
+
|
|
164
|
+
// Get test suite tag expressions for deduplication
|
|
165
|
+
const testSuiteTagExpressions = new Set((testSuiteTags || []).map(tag => tag.tagExpression.toLowerCase()))
|
|
166
|
+
|
|
167
|
+
// Generate scenarios for each test case that has steps
|
|
168
|
+
let scenarioCount = 0
|
|
169
|
+
testCases.forEach(testCase => {
|
|
170
|
+
// Only generate scenario if test case has steps
|
|
171
|
+
if (testCase.steps && testCase.steps.length > 0) {
|
|
172
|
+
// Generate Gherkin steps from test case steps
|
|
173
|
+
const gherkinSteps = generateGherkinStepsFromTestCase(testCase.steps)
|
|
174
|
+
|
|
175
|
+
// Only add scenario if there are actual gherkin steps
|
|
176
|
+
if (gherkinSteps.length > 0) {
|
|
177
|
+
if (scenarioCount > 0) {
|
|
178
|
+
lines.push('') // Add blank line between scenarios
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Add scenario-level tags (skip if already present at feature level)
|
|
182
|
+
if (testCase.tags && testCase.tags.length > 0) {
|
|
183
|
+
testCase.tags.forEach(tag => {
|
|
184
|
+
// Only add tag if it's not already present at feature level
|
|
185
|
+
if (!testSuiteTagExpressions.has(tag.tagExpression.toLowerCase())) {
|
|
186
|
+
lines.push(` ${tag.tagExpression}`)
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
lines.push(` Scenario: [${testCase.title}] ${testCase.description}`)
|
|
192
|
+
|
|
193
|
+
gherkinSteps.forEach(step => {
|
|
194
|
+
lines.push(` ${step}`)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
scenarioCount++
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
return lines.join('\n') + '\n'
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Generates Gherkin steps from test case steps using the same logic as the frontend
|
|
207
|
+
*/
|
|
208
|
+
function generateGherkinStepsFromTestCase(
|
|
209
|
+
steps: Array<{
|
|
210
|
+
gherkinStep: string
|
|
211
|
+
order: number
|
|
212
|
+
}>,
|
|
213
|
+
): string[] {
|
|
214
|
+
if (!steps || steps.length === 0) {
|
|
215
|
+
return []
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Sort steps by order
|
|
219
|
+
const sortedSteps = steps.sort((a, b) => a.order - b.order)
|
|
220
|
+
|
|
221
|
+
let hasThenInPrevious = false
|
|
222
|
+
let hasWhenInPrevious = false
|
|
223
|
+
|
|
224
|
+
return sortedSteps.map((step, index) => {
|
|
225
|
+
const gherkinStep = step.gherkinStep?.trim() || ''
|
|
226
|
+
const firstWord = gherkinStep.split(' ')[0].toLowerCase()
|
|
227
|
+
const hasGherkinKeyword = ['given', 'when', 'then', 'and', 'but'].includes(firstWord)
|
|
228
|
+
const stepWithoutKeyword = hasGherkinKeyword ? gherkinStep.split(' ').slice(1).join(' ') : gherkinStep
|
|
229
|
+
|
|
230
|
+
// First step always starts with Given
|
|
231
|
+
if (index === 0) {
|
|
232
|
+
return `Given ${stepWithoutKeyword}`
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Check if this step should be a Then statement
|
|
236
|
+
const isThenStatement =
|
|
237
|
+
firstWord === 'then' ||
|
|
238
|
+
stepWithoutKeyword.toLowerCase().startsWith('should') ||
|
|
239
|
+
stepWithoutKeyword.toLowerCase().startsWith('must') ||
|
|
240
|
+
stepWithoutKeyword.toLowerCase().startsWith('will')
|
|
241
|
+
|
|
242
|
+
// If we haven't seen a Then yet
|
|
243
|
+
if (!hasThenInPrevious) {
|
|
244
|
+
// If this is a Then statement
|
|
245
|
+
if (isThenStatement) {
|
|
246
|
+
hasThenInPrevious = true
|
|
247
|
+
return `Then ${stepWithoutKeyword}`
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// If we haven't seen a When yet, use When
|
|
251
|
+
if (!hasWhenInPrevious) {
|
|
252
|
+
hasWhenInPrevious = true
|
|
253
|
+
return `When ${stepWithoutKeyword}`
|
|
254
|
+
}
|
|
255
|
+
// After When, use And
|
|
256
|
+
return `And ${stepWithoutKeyword}`
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// After Then
|
|
260
|
+
if (isThenStatement) {
|
|
261
|
+
// If it's another Then statement, use And
|
|
262
|
+
return `And ${stepWithoutKeyword}`
|
|
263
|
+
}
|
|
264
|
+
// After Then, use When for new actions
|
|
265
|
+
hasThenInPrevious = false
|
|
266
|
+
hasWhenInPrevious = true
|
|
267
|
+
return `When ${stepWithoutKeyword}`
|
|
268
|
+
})
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Deletes a feature file for a test suite
|
|
273
|
+
* @param testSuiteId - The ID of the test suite
|
|
274
|
+
* @returns Promise<boolean> - True if file was deleted, false if file didn't exist
|
|
275
|
+
*/
|
|
276
|
+
export async function deleteFeatureFile(testSuiteId: string): Promise<boolean> {
|
|
277
|
+
try {
|
|
278
|
+
// Fetch test suite and all modules to build the correct path
|
|
279
|
+
const [testSuite, allModules] = await Promise.all([
|
|
280
|
+
prisma.testSuite.findUnique({
|
|
281
|
+
where: { id: testSuiteId },
|
|
282
|
+
include: {
|
|
283
|
+
module: true,
|
|
284
|
+
},
|
|
285
|
+
}),
|
|
286
|
+
prisma.module.findMany(),
|
|
287
|
+
])
|
|
288
|
+
|
|
289
|
+
if (!testSuite) {
|
|
290
|
+
console.warn(`Test suite with ID ${testSuiteId} not found for feature file deletion`)
|
|
291
|
+
return false
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Build the module path for directory structure
|
|
295
|
+
const modulePath = buildModulePath(allModules, testSuite.module)
|
|
296
|
+
const safeFileName = generateSafeFileName(testSuite.name)
|
|
297
|
+
|
|
298
|
+
const featuresBaseDir = join(process.cwd(), 'src', 'tests', 'features')
|
|
299
|
+
const moduleDir = join(featuresBaseDir, modulePath.substring(1)) // Remove leading slash
|
|
300
|
+
const featureFilePath = join(moduleDir, `${safeFileName}.feature`)
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
await fs.unlink(featureFilePath)
|
|
304
|
+
console.log(`Feature file deleted: ${featureFilePath}`)
|
|
305
|
+
|
|
306
|
+
// Clean up empty directories up the module hierarchy
|
|
307
|
+
await removeEmptyDirectoriesUp(moduleDir, featuresBaseDir)
|
|
308
|
+
|
|
309
|
+
return true
|
|
310
|
+
} catch (error: unknown) {
|
|
311
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
312
|
+
console.warn(`Feature file not found for deletion: ${featureFilePath}`)
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
throw error
|
|
316
|
+
}
|
|
317
|
+
} catch (error) {
|
|
318
|
+
console.error('Error deleting feature file:', error)
|
|
319
|
+
throw error
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Regenerates all feature files from the current database state
|
|
325
|
+
* This is useful after merging changes or database migrations to ensure sync
|
|
326
|
+
* @returns Promise<string[]> - Array of generated feature file paths
|
|
327
|
+
*/
|
|
328
|
+
export async function regenerateAllFeatureFiles(): Promise<string[]> {
|
|
329
|
+
try {
|
|
330
|
+
console.log('Starting regeneration of all feature files...')
|
|
331
|
+
|
|
332
|
+
// Clear existing feature files directory
|
|
333
|
+
const featuresBaseDir = join(process.cwd(), 'src', 'tests', 'features')
|
|
334
|
+
try {
|
|
335
|
+
await fs.rm(featuresBaseDir, { recursive: true, force: true })
|
|
336
|
+
} catch (error) {
|
|
337
|
+
console.warn('Could not clear features directory:', error)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Fetch all test suites from database
|
|
341
|
+
const testSuites = await prisma.testSuite.findMany({
|
|
342
|
+
include: {
|
|
343
|
+
testCases: {
|
|
344
|
+
include: {
|
|
345
|
+
steps: {
|
|
346
|
+
include: {
|
|
347
|
+
parameters: true,
|
|
348
|
+
},
|
|
349
|
+
orderBy: {
|
|
350
|
+
order: 'asc',
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
tags: true,
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
module: true,
|
|
357
|
+
tags: true,
|
|
358
|
+
},
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
// Fetch all modules for path building
|
|
362
|
+
const allModules = await prisma.module.findMany()
|
|
363
|
+
|
|
364
|
+
const generatedFiles: string[] = []
|
|
365
|
+
|
|
366
|
+
// Generate feature file for each test suite
|
|
367
|
+
for (const testSuite of testSuites) {
|
|
368
|
+
try {
|
|
369
|
+
const modulePath = buildModulePath(allModules, testSuite.module)
|
|
370
|
+
const featureContent = generateFeatureContent(
|
|
371
|
+
testSuite.description || testSuite.name,
|
|
372
|
+
testSuite.testCases,
|
|
373
|
+
testSuite.tags,
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
// Create the features directory with module path
|
|
377
|
+
const moduleDir = join(featuresBaseDir, modulePath.substring(1)) // Remove leading slash
|
|
378
|
+
await fs.mkdir(moduleDir, { recursive: true })
|
|
379
|
+
|
|
380
|
+
// Generate filename and write file
|
|
381
|
+
const safeFileName = generateSafeFileName(testSuite.name)
|
|
382
|
+
const featureFilePath = join(moduleDir, `${safeFileName}.feature`)
|
|
383
|
+
|
|
384
|
+
await fs.writeFile(featureFilePath, featureContent, 'utf8')
|
|
385
|
+
generatedFiles.push(featureFilePath)
|
|
386
|
+
|
|
387
|
+
console.log(`Generated: ${featureFilePath}`)
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error(`Error generating feature file for test suite ${testSuite.name}:`, error)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
console.log(`Regeneration complete. Generated ${generatedFiles.length} feature files.`)
|
|
394
|
+
return generatedFiles
|
|
395
|
+
} catch (error) {
|
|
396
|
+
console.error('Error during feature files regeneration:', error)
|
|
397
|
+
throw error
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Generates a safe filename from test suite name
|
|
403
|
+
*/
|
|
404
|
+
function generateSafeFileName(testSuiteName: string): string {
|
|
405
|
+
// Convert to lowercase and replace spaces and special characters with hyphens
|
|
406
|
+
return testSuiteName
|
|
407
|
+
.toLowerCase()
|
|
408
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
409
|
+
.replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
|
|
410
|
+
.replace(/-+/g, '-') // Replace multiple consecutive hyphens with single hyphen
|
|
411
|
+
}
|