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,259 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import { join, relative } from 'path'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a parsed feature file with its scenarios and steps
|
|
6
|
+
*/
|
|
7
|
+
export interface ParsedFeature {
|
|
8
|
+
filePath: string
|
|
9
|
+
featureName: string
|
|
10
|
+
featureDescription?: string
|
|
11
|
+
tags: string[]
|
|
12
|
+
scenarios: ParsedScenario[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Represents a parsed scenario from a feature file
|
|
17
|
+
*/
|
|
18
|
+
export interface ParsedScenario {
|
|
19
|
+
name: string
|
|
20
|
+
description?: string
|
|
21
|
+
tags: string[]
|
|
22
|
+
steps: ParsedStep[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Represents a parsed step from a feature file
|
|
27
|
+
*/
|
|
28
|
+
export interface ParsedStep {
|
|
29
|
+
keyword: string
|
|
30
|
+
text: string
|
|
31
|
+
order: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parses a Gherkin feature file and extracts scenarios and steps
|
|
36
|
+
* @param filePath - Path to the feature file
|
|
37
|
+
* @returns Promise<ParsedFeature | null> - Parsed feature data or null if parsing fails
|
|
38
|
+
*/
|
|
39
|
+
export async function parseFeatureFile(filePath: string): Promise<ParsedFeature | null> {
|
|
40
|
+
try {
|
|
41
|
+
const content = await fs.readFile(filePath, 'utf-8')
|
|
42
|
+
|
|
43
|
+
// Simple Gherkin parser implementation
|
|
44
|
+
const lines = content.split('\n').map(line => line.trim())
|
|
45
|
+
const scenarios: ParsedScenario[] = []
|
|
46
|
+
|
|
47
|
+
let featureName = ''
|
|
48
|
+
let featureDescription = ''
|
|
49
|
+
const featureTags: string[] = []
|
|
50
|
+
let currentScenario: ParsedScenario | null = null
|
|
51
|
+
let stepOrder = 1
|
|
52
|
+
|
|
53
|
+
// Find feature line and extract tags before it
|
|
54
|
+
let _featureLineIndex = -1
|
|
55
|
+
for (let i = 0; i < lines.length; i++) {
|
|
56
|
+
if (lines[i].startsWith('Feature:')) {
|
|
57
|
+
_featureLineIndex = i
|
|
58
|
+
// Look backwards for tags (skip comments and empty lines)
|
|
59
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
60
|
+
const prevLine = lines[j]
|
|
61
|
+
if (prevLine === '' || prevLine.startsWith('#')) {
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
if (prevLine.startsWith('@')) {
|
|
65
|
+
featureTags.unshift(prevLine) // Add to beginning to maintain order
|
|
66
|
+
} else {
|
|
67
|
+
break // Stop when we hit a non-tag line
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < lines.length; i++) {
|
|
75
|
+
const line = lines[i]
|
|
76
|
+
|
|
77
|
+
// Skip comments and empty lines
|
|
78
|
+
if (line.startsWith('#') || line === '') {
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Parse Feature line
|
|
83
|
+
if (line.startsWith('Feature:')) {
|
|
84
|
+
featureName = line.replace('Feature:', '').trim()
|
|
85
|
+
// Look for description in next lines
|
|
86
|
+
let j = i + 1
|
|
87
|
+
while (j < lines.length && lines[j] && !lines[j].startsWith('Scenario:') && !lines[j].startsWith('Feature:')) {
|
|
88
|
+
if (lines[j] && !lines[j].startsWith('#')) {
|
|
89
|
+
featureDescription += (featureDescription ? ' ' : '') + lines[j]
|
|
90
|
+
}
|
|
91
|
+
j++
|
|
92
|
+
}
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parse Scenario line
|
|
97
|
+
if (line.startsWith('Scenario:')) {
|
|
98
|
+
// Save previous scenario if exists
|
|
99
|
+
if (currentScenario) {
|
|
100
|
+
scenarios.push(currentScenario)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Extract tags before this scenario
|
|
104
|
+
const scenarioTags: string[] = []
|
|
105
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
106
|
+
const prevLine = lines[j]
|
|
107
|
+
if (prevLine === '' || prevLine.startsWith('#')) {
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
if (prevLine.startsWith('@')) {
|
|
111
|
+
scenarioTags.unshift(prevLine) // Add to beginning to maintain order
|
|
112
|
+
} else {
|
|
113
|
+
break // Stop when we hit a non-tag line
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const scenarioText = line.replace('Scenario:', '').trim()
|
|
118
|
+
const [name, description] =
|
|
119
|
+
scenarioText.split(']').length > 1
|
|
120
|
+
? [scenarioText.split(']')[1].trim(), scenarioText.split(']')[0].replace('[', '').trim()]
|
|
121
|
+
: [scenarioText, '']
|
|
122
|
+
|
|
123
|
+
currentScenario = {
|
|
124
|
+
name: name,
|
|
125
|
+
description: description || undefined,
|
|
126
|
+
tags: scenarioTags,
|
|
127
|
+
steps: [],
|
|
128
|
+
}
|
|
129
|
+
stepOrder = 1
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Parse steps (Given, When, Then, And, But)
|
|
134
|
+
if (
|
|
135
|
+
currentScenario &&
|
|
136
|
+
(line.startsWith('Given ') ||
|
|
137
|
+
line.startsWith('When ') ||
|
|
138
|
+
line.startsWith('Then ') ||
|
|
139
|
+
line.startsWith('And ') ||
|
|
140
|
+
line.startsWith('But '))
|
|
141
|
+
) {
|
|
142
|
+
const keyword = line.split(' ')[0]
|
|
143
|
+
const text = line.substring(keyword.length).trim()
|
|
144
|
+
|
|
145
|
+
currentScenario.steps.push({
|
|
146
|
+
keyword: keyword,
|
|
147
|
+
text: text,
|
|
148
|
+
order: stepOrder++,
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Add the last scenario
|
|
154
|
+
if (currentScenario) {
|
|
155
|
+
scenarios.push(currentScenario)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!featureName) {
|
|
159
|
+
console.warn(`No feature found in file: ${filePath}`)
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
filePath,
|
|
165
|
+
featureName,
|
|
166
|
+
featureDescription: featureDescription || undefined,
|
|
167
|
+
tags: featureTags,
|
|
168
|
+
scenarios,
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error(`Error parsing feature file ${filePath}:`, error)
|
|
172
|
+
return null
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Scans a directory for feature files and parses them
|
|
178
|
+
* @param directoryPath - Path to scan for feature files
|
|
179
|
+
* @returns Promise<ParsedFeature[]> - Array of parsed feature files
|
|
180
|
+
*/
|
|
181
|
+
export async function scanFeatureFiles(directoryPath: string): Promise<ParsedFeature[]> {
|
|
182
|
+
const parsedFeatures: ParsedFeature[] = []
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const entries = await fs.readdir(directoryPath, { withFileTypes: true })
|
|
186
|
+
|
|
187
|
+
for (const entry of entries) {
|
|
188
|
+
const fullPath = join(directoryPath, entry.name)
|
|
189
|
+
|
|
190
|
+
if (entry.isDirectory()) {
|
|
191
|
+
// Recursively scan subdirectories
|
|
192
|
+
const subFeatures = await scanFeatureFiles(fullPath)
|
|
193
|
+
parsedFeatures.push(...subFeatures)
|
|
194
|
+
} else if (entry.isFile() && entry.name.endsWith('.feature')) {
|
|
195
|
+
// Parse feature file
|
|
196
|
+
const parsedFeature = await parseFeatureFile(fullPath)
|
|
197
|
+
if (parsedFeature) {
|
|
198
|
+
parsedFeatures.push(parsedFeature)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error(`Error scanning directory ${directoryPath}:`, error)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return parsedFeatures
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Extracts module path from feature file path
|
|
211
|
+
* Works cross-platform (Windows, Mac, Linux)
|
|
212
|
+
* @param featureFilePath - Full path to the feature file
|
|
213
|
+
* @param featuresBaseDir - Base directory for features
|
|
214
|
+
* @returns string - Module path (e.g., "/module1/submodule")
|
|
215
|
+
*/
|
|
216
|
+
export function extractModulePathFromFilePath(featureFilePath: string, featuresBaseDir: string): string {
|
|
217
|
+
// Use path.relative for cross-platform path handling
|
|
218
|
+
const relativePath = relative(featuresBaseDir, featureFilePath)
|
|
219
|
+
|
|
220
|
+
// Normalize to forward slashes for module path format (database uses /)
|
|
221
|
+
const normalizedPath = relativePath.replace(/\\/g, '/')
|
|
222
|
+
const pathParts = normalizedPath.split('/').filter(part => part && part !== '')
|
|
223
|
+
|
|
224
|
+
// Remove the filename and join the remaining parts
|
|
225
|
+
const moduleParts = pathParts.slice(0, -1)
|
|
226
|
+
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Generates a safe test suite name from feature name
|
|
231
|
+
* @param featureName - Name of the feature
|
|
232
|
+
* @returns string - Safe test suite name
|
|
233
|
+
*/
|
|
234
|
+
export function generateSafeTestSuiteName(featureName: string): string {
|
|
235
|
+
return featureName
|
|
236
|
+
.toLowerCase()
|
|
237
|
+
.replace(/[^a-z0-9\s]+/g, '')
|
|
238
|
+
.replace(/\s+/g, ' ')
|
|
239
|
+
.trim()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Generates a safe test case name from scenario name
|
|
244
|
+
* @param scenarioName - Name of the scenario
|
|
245
|
+
* @returns string - Safe test case name
|
|
246
|
+
*/
|
|
247
|
+
export function generateSafeTestCaseName(scenarioName: string): string {
|
|
248
|
+
// Remove scenario prefix if present and clean up the name
|
|
249
|
+
const cleanName = scenarioName
|
|
250
|
+
.replace(/^Scenario:\s*/i, '')
|
|
251
|
+
.replace(/^\[.*?\]\s*/, '') // Remove [brackets] prefix
|
|
252
|
+
.trim()
|
|
253
|
+
|
|
254
|
+
return cleanName
|
|
255
|
+
.toLowerCase()
|
|
256
|
+
.replace(/[^a-z0-9\s]+/g, '')
|
|
257
|
+
.replace(/\s+/g, ' ')
|
|
258
|
+
.trim()
|
|
259
|
+
}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import prisma from '@/config/db-config'
|
|
4
|
+
import { buildModulePath } from '@/lib/path-helpers/module-path'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Gets the file path for a locator group based on its module hierarchy
|
|
8
|
+
*/
|
|
9
|
+
export async function getLocatorGroupFilePath(locatorGroupId: string): Promise<string | null> {
|
|
10
|
+
try {
|
|
11
|
+
const locatorGroup = await prisma.locatorGroup.findUnique({
|
|
12
|
+
where: { id: locatorGroupId },
|
|
13
|
+
include: { module: true },
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
if (!locatorGroup) return null
|
|
17
|
+
|
|
18
|
+
const allModules = await prisma.module.findMany()
|
|
19
|
+
const modulePath = buildModulePath(allModules, locatorGroup.module)
|
|
20
|
+
|
|
21
|
+
const sanitizedPath = modulePath.replace(/^\//, '').replace(/\//g, path.sep)
|
|
22
|
+
const fileName = `${locatorGroup.name}.json`
|
|
23
|
+
|
|
24
|
+
return path.join('src', 'tests', 'locators', sanitizedPath, fileName)
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Error getting locator group file path:', error)
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generates JSON content for a locator group from its locators
|
|
33
|
+
*/
|
|
34
|
+
export async function generateLocatorGroupContent(locatorGroupId: string): Promise<Record<string, string>> {
|
|
35
|
+
try {
|
|
36
|
+
const locatorGroup = await prisma.locatorGroup.findUnique({
|
|
37
|
+
where: { id: locatorGroupId },
|
|
38
|
+
include: {
|
|
39
|
+
locators: {
|
|
40
|
+
select: { name: true, value: true },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
if (!locatorGroup) return {}
|
|
46
|
+
|
|
47
|
+
return Object.fromEntries(locatorGroup.locators.map(locator => [locator.name, locator.value]))
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Error generating locator group content:', error)
|
|
50
|
+
return {}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Ensures a directory exists, creating it if necessary
|
|
56
|
+
*/
|
|
57
|
+
export async function ensureDirectoryExists(filePath: string): Promise<void> {
|
|
58
|
+
const dir = path.dirname(filePath)
|
|
59
|
+
try {
|
|
60
|
+
await fs.access(dir)
|
|
61
|
+
} catch {
|
|
62
|
+
await fs.mkdir(dir, { recursive: true })
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates or updates a locator group JSON file
|
|
68
|
+
*/
|
|
69
|
+
export async function createOrUpdateLocatorGroupFile(locatorGroupId: string): Promise<boolean> {
|
|
70
|
+
try {
|
|
71
|
+
const filePath = await getLocatorGroupFilePath(locatorGroupId)
|
|
72
|
+
if (!filePath) return false
|
|
73
|
+
|
|
74
|
+
await ensureDirectoryExists(filePath)
|
|
75
|
+
const content = await generateLocatorGroupContent(locatorGroupId)
|
|
76
|
+
|
|
77
|
+
await fs.writeFile(filePath, JSON.stringify(content, null, 2))
|
|
78
|
+
return true
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('Error creating/updating locator group file:', error)
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Deletes a locator group JSON file and cleans up empty directories
|
|
87
|
+
*/
|
|
88
|
+
export async function deleteLocatorGroupFile(locatorGroupId: string): Promise<boolean> {
|
|
89
|
+
try {
|
|
90
|
+
const filePath = await getLocatorGroupFilePath(locatorGroupId)
|
|
91
|
+
if (!filePath) return false
|
|
92
|
+
|
|
93
|
+
// Check if file exists before trying to delete
|
|
94
|
+
try {
|
|
95
|
+
await fs.access(filePath)
|
|
96
|
+
} catch {
|
|
97
|
+
return true // File doesn't exist, nothing to delete
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await fs.unlink(filePath)
|
|
101
|
+
await cleanupEmptyDirectories(filePath)
|
|
102
|
+
return true
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error('Error deleting locator group file:', error)
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Renames a locator group file when the name changes
|
|
111
|
+
*/
|
|
112
|
+
export async function renameLocatorGroupFile(
|
|
113
|
+
oldLocatorGroupId: string,
|
|
114
|
+
newName: string,
|
|
115
|
+
oldName?: string,
|
|
116
|
+
): Promise<boolean> {
|
|
117
|
+
try {
|
|
118
|
+
// Get the current file path (with new name) for the directory
|
|
119
|
+
const currentFilePath = await getLocatorGroupFilePath(oldLocatorGroupId)
|
|
120
|
+
if (!currentFilePath) return false
|
|
121
|
+
|
|
122
|
+
// If oldName is provided, construct the old file path manually
|
|
123
|
+
// Otherwise, try to get it from the current path (fallback)
|
|
124
|
+
let oldFilePath: string
|
|
125
|
+
if (oldName) {
|
|
126
|
+
oldFilePath = path.join(path.dirname(currentFilePath), `${oldName}.json`)
|
|
127
|
+
} else {
|
|
128
|
+
oldFilePath = currentFilePath
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const newFilePath = path.join(path.dirname(currentFilePath), `${newName}.json`)
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
await fs.access(oldFilePath)
|
|
135
|
+
console.log('oldFilePath exists:', oldFilePath)
|
|
136
|
+
await fs.rename(oldFilePath, newFilePath)
|
|
137
|
+
console.log('File renamed successfully from', oldFilePath, 'to', newFilePath)
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.log('File not found at old path, creating new one:', error)
|
|
140
|
+
// File doesn't exist, create new one
|
|
141
|
+
return await createOrUpdateLocatorGroupFile(oldLocatorGroupId)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return true
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('Error renaming locator group file:', error)
|
|
147
|
+
return false
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Moves a locator group file when the module changes
|
|
153
|
+
*/
|
|
154
|
+
export async function moveLocatorGroupFile(locatorGroupId: string): Promise<boolean> {
|
|
155
|
+
try {
|
|
156
|
+
// Delete old file and create new one in correct location
|
|
157
|
+
await deleteLocatorGroupFile(locatorGroupId)
|
|
158
|
+
return await createOrUpdateLocatorGroupFile(locatorGroupId)
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error('Error moving locator group file:', error)
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Cleans up empty directories recursively
|
|
167
|
+
*/
|
|
168
|
+
async function cleanupEmptyDirectories(filePath: string): Promise<void> {
|
|
169
|
+
let currentDir = path.dirname(filePath)
|
|
170
|
+
|
|
171
|
+
while (currentDir !== 'tests' && currentDir !== '.') {
|
|
172
|
+
try {
|
|
173
|
+
const files = await fs.readdir(currentDir)
|
|
174
|
+
if (files.length === 0) {
|
|
175
|
+
await fs.rmdir(currentDir)
|
|
176
|
+
currentDir = path.dirname(currentDir)
|
|
177
|
+
} else {
|
|
178
|
+
break
|
|
179
|
+
}
|
|
180
|
+
} catch {
|
|
181
|
+
break
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Creates an empty JSON file for a new locator group
|
|
188
|
+
*/
|
|
189
|
+
export async function createEmptyLocatorGroupFile(locatorGroupId: string): Promise<boolean> {
|
|
190
|
+
try {
|
|
191
|
+
const filePath = await getLocatorGroupFilePath(locatorGroupId)
|
|
192
|
+
if (!filePath) return false
|
|
193
|
+
|
|
194
|
+
await ensureDirectoryExists(filePath)
|
|
195
|
+
await fs.writeFile(filePath, JSON.stringify({}, null, 2))
|
|
196
|
+
return true
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('Error creating empty locator group file:', error)
|
|
199
|
+
return false
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Reads and parses the content of a locator group file
|
|
205
|
+
*/
|
|
206
|
+
export async function readLocatorGroupFile(
|
|
207
|
+
locatorGroupId: string,
|
|
208
|
+
): Promise<{ filePath: string; content: Record<string, string> } | null> {
|
|
209
|
+
try {
|
|
210
|
+
const filePath = await getLocatorGroupFilePath(locatorGroupId)
|
|
211
|
+
if (!filePath) return null
|
|
212
|
+
|
|
213
|
+
const fileContent = await fs.readFile(filePath, 'utf-8')
|
|
214
|
+
const jsonContent = JSON.parse(fileContent)
|
|
215
|
+
|
|
216
|
+
return { filePath, content: jsonContent }
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error('Error reading locator group file:', error)
|
|
219
|
+
return null
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Updates the locator map file with locator group information
|
|
225
|
+
* Overload for updating existing entries (4 parameters)
|
|
226
|
+
*/
|
|
227
|
+
export async function updateLocatorMapFile(
|
|
228
|
+
currentLocatorGroupRoute: string,
|
|
229
|
+
newLocatorGroupRoute: string,
|
|
230
|
+
currentLocatorGroupName: string,
|
|
231
|
+
newLocatorGroupName: string,
|
|
232
|
+
): Promise<boolean>
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Updates the locator map file with locator group information
|
|
236
|
+
* Overload for adding new entries (2 parameters)
|
|
237
|
+
*/
|
|
238
|
+
export async function updateLocatorMapFile(newLocatorGroupName: string, newLocatorGroupRoute: string): Promise<boolean>
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Implementation of updateLocatorMapFile with proper overload handling
|
|
242
|
+
*/
|
|
243
|
+
export async function updateLocatorMapFile(
|
|
244
|
+
param1: string,
|
|
245
|
+
param2: string,
|
|
246
|
+
param3?: string,
|
|
247
|
+
param4?: string,
|
|
248
|
+
): Promise<boolean> {
|
|
249
|
+
try {
|
|
250
|
+
const locatorMapPath = path.join('src', 'tests', 'mapping', 'locator-map.json')
|
|
251
|
+
|
|
252
|
+
// Ensure the mapping directory exists
|
|
253
|
+
await ensureDirectoryExists(locatorMapPath)
|
|
254
|
+
|
|
255
|
+
let locatorMap: Array<{ name: string; path: string }> = []
|
|
256
|
+
|
|
257
|
+
// Read existing locator map or create empty array
|
|
258
|
+
try {
|
|
259
|
+
const fileContent = await fs.readFile(locatorMapPath, 'utf-8')
|
|
260
|
+
locatorMap = JSON.parse(fileContent)
|
|
261
|
+
} catch {
|
|
262
|
+
// File doesn't exist, start with empty array
|
|
263
|
+
locatorMap = []
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Determine if this is a 2-param call (new entry) or 4-param call (update)
|
|
267
|
+
const isNewEntry = param3 === undefined && param4 === undefined
|
|
268
|
+
|
|
269
|
+
if (isNewEntry) {
|
|
270
|
+
// 2 params: newLocatorGroupName, newLocatorGroupRoute
|
|
271
|
+
const name = param1
|
|
272
|
+
const route = param2
|
|
273
|
+
|
|
274
|
+
// Check for uniqueness
|
|
275
|
+
const existingEntry = locatorMap.find(entry => entry.name === name)
|
|
276
|
+
if (existingEntry) {
|
|
277
|
+
console.error(`Locator group with name "${name}" already exists in locator map`)
|
|
278
|
+
return false
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Add new entry
|
|
282
|
+
locatorMap.push({ name, path: route })
|
|
283
|
+
} else {
|
|
284
|
+
// 4 params: update existing entry
|
|
285
|
+
const currentLocatorGroupRoute = param1
|
|
286
|
+
const newLocatorGroupRoute = param2
|
|
287
|
+
const currentLocatorGroupName = param3!
|
|
288
|
+
const newLocatorGroupName = param4!
|
|
289
|
+
|
|
290
|
+
// Find the entry to update
|
|
291
|
+
const entryIndex = locatorMap.findIndex(entry => entry.name === currentLocatorGroupName)
|
|
292
|
+
if (entryIndex === -1) {
|
|
293
|
+
console.error(`Locator group with name "${currentLocatorGroupName}" not found in locator map`)
|
|
294
|
+
return false
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Check if new name is unique (if name is changing)
|
|
298
|
+
if (currentLocatorGroupName !== newLocatorGroupName) {
|
|
299
|
+
const existingEntry = locatorMap.find(entry => entry.name === newLocatorGroupName)
|
|
300
|
+
if (existingEntry) {
|
|
301
|
+
console.error(`Locator group with name "${newLocatorGroupName}" already exists in locator map`)
|
|
302
|
+
return false
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Update the entry
|
|
307
|
+
const updatedEntry = { ...locatorMap[entryIndex] }
|
|
308
|
+
|
|
309
|
+
// Update name if it changed
|
|
310
|
+
if (currentLocatorGroupName !== newLocatorGroupName) {
|
|
311
|
+
updatedEntry.name = newLocatorGroupName
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Update path if it changed
|
|
315
|
+
if (currentLocatorGroupRoute !== newLocatorGroupRoute) {
|
|
316
|
+
updatedEntry.path = newLocatorGroupRoute
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
locatorMap[entryIndex] = updatedEntry
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Write the updated locator map back to file
|
|
323
|
+
await fs.writeFile(locatorMapPath, JSON.stringify(locatorMap, null, 2))
|
|
324
|
+
return true
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error('Error updating locator map file:', error)
|
|
327
|
+
return false
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Removes locator group entries from the locator map file
|
|
333
|
+
* @param locatorGroupNames - Array of locator group names to remove
|
|
334
|
+
*/
|
|
335
|
+
export async function removeLocatorMapEntry(locatorGroupNames: string[]): Promise<boolean> {
|
|
336
|
+
try {
|
|
337
|
+
const locatorMapPath = path.join('src', 'tests', 'mapping', 'locator-map.json')
|
|
338
|
+
|
|
339
|
+
// Check if file exists
|
|
340
|
+
try {
|
|
341
|
+
await fs.access(locatorMapPath)
|
|
342
|
+
} catch {
|
|
343
|
+
// File doesn't exist, nothing to remove
|
|
344
|
+
return true
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Read existing locator map
|
|
348
|
+
const fileContent = await fs.readFile(locatorMapPath, 'utf-8')
|
|
349
|
+
let locatorMap: Array<{ name: string; path: string }> = JSON.parse(fileContent)
|
|
350
|
+
|
|
351
|
+
// Filter out the entries to be removed
|
|
352
|
+
const originalLength = locatorMap.length
|
|
353
|
+
locatorMap = locatorMap.filter(entry => !locatorGroupNames.includes(entry.name))
|
|
354
|
+
|
|
355
|
+
// Check if any entries were actually removed
|
|
356
|
+
const removedCount = originalLength - locatorMap.length
|
|
357
|
+
if (removedCount === 0) {
|
|
358
|
+
console.log('No matching locator group entries found in locator map')
|
|
359
|
+
return true
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Write the updated locator map back to file
|
|
363
|
+
await fs.writeFile(locatorMapPath, JSON.stringify(locatorMap, null, 2))
|
|
364
|
+
console.log(`Removed ${removedCount} locator group entry(ies) from locator map`)
|
|
365
|
+
return true
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error('Error removing locator map entries:', error)
|
|
368
|
+
return false
|
|
369
|
+
}
|
|
370
|
+
}
|