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,205 @@
|
|
|
1
|
+
import prisma from '@/config/db-config'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a module path structure
|
|
5
|
+
*/
|
|
6
|
+
export interface ModulePath {
|
|
7
|
+
path: string
|
|
8
|
+
name: string
|
|
9
|
+
parentPath?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates or finds a module by its path
|
|
14
|
+
* @param modulePath - The module path (e.g., "/module1/submodule")
|
|
15
|
+
* @param moduleName - The name of the module
|
|
16
|
+
* @param parentId - Optional parent module ID
|
|
17
|
+
* @returns Promise<string> - The ID of the created or found module
|
|
18
|
+
*/
|
|
19
|
+
export async function createOrFindModule(modulePath: string, moduleName: string, parentId?: string): Promise<string> {
|
|
20
|
+
try {
|
|
21
|
+
// First, try to find existing module by name and parent
|
|
22
|
+
const existingModule = await prisma.module.findFirst({
|
|
23
|
+
where: {
|
|
24
|
+
name: moduleName,
|
|
25
|
+
parentId: parentId || null,
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
if (existingModule) {
|
|
30
|
+
return existingModule.id
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Create new module
|
|
34
|
+
const newModule = await prisma.module.create({
|
|
35
|
+
data: {
|
|
36
|
+
name: moduleName,
|
|
37
|
+
parentId: parentId || null,
|
|
38
|
+
// Add description only if it's required by the schema
|
|
39
|
+
// For now, we'll leave it empty since it's optional
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
console.log(`Created module: ${moduleName} (${modulePath})`)
|
|
44
|
+
return newModule.id
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(`Error creating module ${moduleName}:`, error)
|
|
47
|
+
throw error
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Builds the complete module hierarchy from a module path
|
|
53
|
+
* @param modulePath - The module path (e.g., "/module1/submodule")
|
|
54
|
+
* @returns Promise<string> - The ID of the leaf module
|
|
55
|
+
*/
|
|
56
|
+
export async function buildModuleHierarchy(modulePath: string): Promise<string> {
|
|
57
|
+
try {
|
|
58
|
+
// Parse the module path
|
|
59
|
+
const pathParts = modulePath.split('/').filter(part => part && part !== '')
|
|
60
|
+
|
|
61
|
+
// Handle empty path (root level) - create/find a default root module
|
|
62
|
+
if (pathParts.length === 0) {
|
|
63
|
+
const rootModule = await prisma.module.findFirst({
|
|
64
|
+
where: {
|
|
65
|
+
name: 'root',
|
|
66
|
+
parentId: null,
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
if (rootModule) {
|
|
71
|
+
return rootModule.id
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Create default root module
|
|
75
|
+
const newRootModule = await prisma.module.create({
|
|
76
|
+
data: {
|
|
77
|
+
name: 'root',
|
|
78
|
+
parentId: null,
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
console.log(`Created root module for features without module path`)
|
|
83
|
+
return newRootModule.id
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let currentParentId: string | undefined
|
|
87
|
+
let currentPath = ''
|
|
88
|
+
|
|
89
|
+
// Create or find each module in the hierarchy
|
|
90
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
91
|
+
const moduleName = pathParts[i]
|
|
92
|
+
currentPath += `/${moduleName}`
|
|
93
|
+
|
|
94
|
+
const moduleId = await createOrFindModule(currentPath, moduleName, currentParentId)
|
|
95
|
+
|
|
96
|
+
currentParentId = moduleId
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return currentParentId!
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(`Error building module hierarchy for path ${modulePath}:`, error)
|
|
102
|
+
throw error
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Gets all existing modules as a flat list with their paths
|
|
108
|
+
* @returns Promise<Array<{id: string, name: string, path: string, parentId: string | null}>>
|
|
109
|
+
*/
|
|
110
|
+
export async function getAllModulesWithPaths(): Promise<
|
|
111
|
+
Array<{
|
|
112
|
+
id: string
|
|
113
|
+
name: string
|
|
114
|
+
path: string
|
|
115
|
+
parentId: string | null
|
|
116
|
+
}>
|
|
117
|
+
> {
|
|
118
|
+
try {
|
|
119
|
+
const modules = await prisma.module.findMany({
|
|
120
|
+
orderBy: [{ parentId: 'asc' }, { name: 'asc' }],
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// Build paths for each module
|
|
124
|
+
const modulesWithPaths = modules.map(module => {
|
|
125
|
+
const path = buildModulePath(modules, module)
|
|
126
|
+
return {
|
|
127
|
+
id: module.id,
|
|
128
|
+
name: module.name,
|
|
129
|
+
path,
|
|
130
|
+
parentId: module.parentId,
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
return modulesWithPaths
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error('Error fetching modules with paths:', error)
|
|
137
|
+
throw error
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Builds the full path for a module based on its hierarchy
|
|
143
|
+
* @param allModules - All modules from the database
|
|
144
|
+
* @param targetModule - The module to build the path for
|
|
145
|
+
* @returns string - The full module path
|
|
146
|
+
*/
|
|
147
|
+
function buildModulePath(
|
|
148
|
+
allModules: Array<{ id: string; name: string; parentId: string | null }>,
|
|
149
|
+
targetModule: { id: string; name: string; parentId: string | null },
|
|
150
|
+
): string {
|
|
151
|
+
const pathParts: string[] = []
|
|
152
|
+
let currentModule = targetModule
|
|
153
|
+
|
|
154
|
+
// Traverse up the hierarchy
|
|
155
|
+
while (currentModule) {
|
|
156
|
+
pathParts.unshift(currentModule.name)
|
|
157
|
+
|
|
158
|
+
if (currentModule.parentId) {
|
|
159
|
+
currentModule = allModules.find(m => m.id === currentModule.parentId)!
|
|
160
|
+
if (!currentModule) break
|
|
161
|
+
} else {
|
|
162
|
+
break
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return '/' + pathParts.join('/')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Finds a module by its path
|
|
171
|
+
* @param modulePath - The module path to find
|
|
172
|
+
* @returns Promise<string | null> - The module ID or null if not found
|
|
173
|
+
*/
|
|
174
|
+
export async function findModuleByPath(modulePath: string): Promise<string | null> {
|
|
175
|
+
try {
|
|
176
|
+
const modules = await prisma.module.findMany()
|
|
177
|
+
const pathParts = modulePath.split('/').filter(part => part && part !== '')
|
|
178
|
+
|
|
179
|
+
if (pathParts.length === 0) {
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Find the root module
|
|
184
|
+
let currentModule = modules.find(m => m.name === pathParts[0] && !m.parentId)
|
|
185
|
+
if (!currentModule) {
|
|
186
|
+
return null
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Traverse down the hierarchy
|
|
190
|
+
for (let i = 1; i < pathParts.length; i++) {
|
|
191
|
+
const childModule = modules.find(m => m.name === pathParts[i] && m.parentId === currentModule!.id)
|
|
192
|
+
|
|
193
|
+
if (!childModule) {
|
|
194
|
+
return null
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
currentModule = childModule
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return currentModule.id
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(`Error finding module by path ${modulePath}:`, error)
|
|
203
|
+
return null
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Module } from '@prisma/client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds the hierarchical path for a module based on its parent modules
|
|
5
|
+
* @param modules - Array of all modules
|
|
6
|
+
* @param selectedModule - The module to get the path for
|
|
7
|
+
* @returns The hierarchical path as a string (e.g., "/user/user profile")
|
|
8
|
+
*/
|
|
9
|
+
export function buildModulePath(modules: Module[], selectedModule: Module): string {
|
|
10
|
+
const path: string[] = []
|
|
11
|
+
let currentModule: Module | null = selectedModule
|
|
12
|
+
|
|
13
|
+
// Build path from current module up to root
|
|
14
|
+
while (currentModule) {
|
|
15
|
+
path.unshift(currentModule.name)
|
|
16
|
+
currentModule = modules.find(m => m.id === currentModule?.parentId) || null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Return path with leading slash
|
|
20
|
+
return `/${path.join('/')}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Alternative implementation that can handle modules with parent relationships
|
|
25
|
+
* This version works with the current data structure where we have parent info
|
|
26
|
+
*/
|
|
27
|
+
export function buildModulePathFromParent(
|
|
28
|
+
modules: (Module & { parent: { name: string } })[],
|
|
29
|
+
selectedModule: Module & { parent: { name: string } },
|
|
30
|
+
): string {
|
|
31
|
+
const path: string[] = []
|
|
32
|
+
let currentModule: (Module & { parent: { name: string } }) | null = selectedModule
|
|
33
|
+
|
|
34
|
+
// Build path from current module up to root
|
|
35
|
+
while (currentModule) {
|
|
36
|
+
path.unshift(currentModule.name)
|
|
37
|
+
|
|
38
|
+
if (currentModule.parent?.name) {
|
|
39
|
+
// Find the parent module in the modules array
|
|
40
|
+
const parentModule = modules.find(m => m.name === currentModule?.parent?.name)
|
|
41
|
+
currentModule = parentModule || null
|
|
42
|
+
} else {
|
|
43
|
+
// No parent, we've reached the root
|
|
44
|
+
currentModule = null
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Return path with leading slash
|
|
49
|
+
return `/${path.join('/')}`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Builds paths for all modules in a single pass for better performance
|
|
54
|
+
* @param modules - Array of all modules with parent info
|
|
55
|
+
* @returns Map of module ID to path string
|
|
56
|
+
*/
|
|
57
|
+
export function buildAllModulePaths(modules: (Module & { parent: { name: string } })[]): Map<string, string> {
|
|
58
|
+
const pathMap = new Map<string, string>()
|
|
59
|
+
|
|
60
|
+
// First pass: create a map for quick lookup
|
|
61
|
+
const moduleMap = new Map<string, Module & { parent: { name: string } }>()
|
|
62
|
+
modules.forEach(module => moduleMap.set(module.id, module))
|
|
63
|
+
|
|
64
|
+
// Second pass: build paths for each module
|
|
65
|
+
modules.forEach(module => {
|
|
66
|
+
const path = buildModulePathFromParent(modules, module)
|
|
67
|
+
pathMap.set(module.id, path)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
return pathMap
|
|
71
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log formatting utilities for storing and retrieving test run logs
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface LogEntry {
|
|
6
|
+
type: 'stdout' | 'stderr' | 'status'
|
|
7
|
+
message: string
|
|
8
|
+
timestamp: Date
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Formats log entries into a single text string for storage in the database
|
|
13
|
+
* Format: [timestamp] [TYPE] message
|
|
14
|
+
*
|
|
15
|
+
* @param logs - Array of log entries to format
|
|
16
|
+
* @returns Formatted log string
|
|
17
|
+
*/
|
|
18
|
+
export function formatLogsForStorage(logs: LogEntry[]): string {
|
|
19
|
+
return logs
|
|
20
|
+
.map(log => {
|
|
21
|
+
const timestamp = log.timestamp.toISOString()
|
|
22
|
+
const type = log.type.toUpperCase()
|
|
23
|
+
// Escape newlines in message to preserve them in the formatted string
|
|
24
|
+
const escapedMessage = log.message.replace(/\n/g, '\\n')
|
|
25
|
+
return `[${timestamp}] [${type}] ${escapedMessage}`
|
|
26
|
+
})
|
|
27
|
+
.join('\n')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parses formatted log text back into an array of LogEntry objects
|
|
32
|
+
*
|
|
33
|
+
* @param formattedLogs - Formatted log string from database
|
|
34
|
+
* @returns Array of log entries
|
|
35
|
+
*/
|
|
36
|
+
export function parseLogsFromStorage(formattedLogs: string): LogEntry[] {
|
|
37
|
+
if (!formattedLogs || formattedLogs.trim() === '') {
|
|
38
|
+
return []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const lines = formattedLogs.split('\n')
|
|
42
|
+
const parsedLogs: LogEntry[] = []
|
|
43
|
+
|
|
44
|
+
for (const line of lines) {
|
|
45
|
+
if (!line.trim()) continue
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// Match format: [timestamp] [TYPE] message
|
|
49
|
+
const match = line.match(/^\[([^\]]+)\] \[([^\]]+)\] (.+)$/)
|
|
50
|
+
|
|
51
|
+
if (match) {
|
|
52
|
+
const [, timestampStr, typeStr, message] = match
|
|
53
|
+
const timestamp = new Date(timestampStr)
|
|
54
|
+
const type = typeStr.toLowerCase() as 'stdout' | 'stderr' | 'status'
|
|
55
|
+
|
|
56
|
+
// Unescape newlines in message
|
|
57
|
+
const unescapedMessage = message.replace(/\\n/g, '\n')
|
|
58
|
+
|
|
59
|
+
// Validate type
|
|
60
|
+
if (type === 'stdout' || type === 'stderr' || type === 'status') {
|
|
61
|
+
parsedLogs.push({
|
|
62
|
+
type,
|
|
63
|
+
message: unescapedMessage,
|
|
64
|
+
timestamp: isNaN(timestamp.getTime()) ? new Date() : timestamp,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
// Fallback: treat as stdout if format doesn't match
|
|
69
|
+
parsedLogs.push({
|
|
70
|
+
type: 'stdout',
|
|
71
|
+
message: line,
|
|
72
|
+
timestamp: new Date(),
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
// Skip malformed lines
|
|
77
|
+
console.error('[LogFormatter] Error parsing log line:', line, error)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return parsedLogs
|
|
82
|
+
}
|
|
83
|
+
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { SpawnedProcess } from '@/tests/utils/spawner.util'
|
|
2
|
+
import { EventEmitter } from 'events'
|
|
3
|
+
import { taskSpawner } from '@/tests/utils/spawner.util'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Process Manager - Singleton to track running test processes by test run ID
|
|
7
|
+
*
|
|
8
|
+
* This manager maintains a registry of active test processes, allowing
|
|
9
|
+
* the SSE route handler to look up processes and stream their logs.
|
|
10
|
+
*
|
|
11
|
+
* Uses global variable pattern (like Prisma) to persist across Next.js runtime contexts
|
|
12
|
+
*
|
|
13
|
+
* Security: Currently stores processes by testRunId only.
|
|
14
|
+
* TODO: When user authentication is implemented, consider adding user isolation:
|
|
15
|
+
* - Option 1: Use composite keys like `${userId}:${testRunId}`
|
|
16
|
+
* - Option 2: Store userId in process metadata and filter by userId in get() method
|
|
17
|
+
* - Option 3: Create separate ProcessManager instances per user (more complex)
|
|
18
|
+
*/
|
|
19
|
+
class ProcessManager extends EventEmitter {
|
|
20
|
+
private processes: Map<string, SpawnedProcess> = new Map()
|
|
21
|
+
// Track event listeners for cleanup
|
|
22
|
+
private eventListeners: Map<string, Map<string, () => void>> = new Map()
|
|
23
|
+
|
|
24
|
+
private constructor() {
|
|
25
|
+
super()
|
|
26
|
+
// Private constructor for singleton pattern
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the singleton instance of ProcessManager
|
|
31
|
+
* Uses global variable to persist across Next.js runtime contexts
|
|
32
|
+
*/
|
|
33
|
+
static getInstance(): ProcessManager {
|
|
34
|
+
const globalForProcessManager = global as unknown as {
|
|
35
|
+
processManager: ProcessManager | undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!globalForProcessManager.processManager) {
|
|
39
|
+
globalForProcessManager.processManager = new ProcessManager()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return globalForProcessManager.processManager
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Register a process for a test run
|
|
47
|
+
* Sets up event listeners to parse custom events from stdout/stderr
|
|
48
|
+
* @param testRunId - The test run ID
|
|
49
|
+
* @param process - The spawned process instance
|
|
50
|
+
*/
|
|
51
|
+
register(testRunId: string, process: SpawnedProcess): void {
|
|
52
|
+
this.processes.set(testRunId, process)
|
|
53
|
+
|
|
54
|
+
// Set up listeners to parse custom events from stdout/stderr
|
|
55
|
+
const listeners = new Map<string, () => void>()
|
|
56
|
+
|
|
57
|
+
// Handler for stdout events - parse for custom events
|
|
58
|
+
const onStdout = ({ processName, data }: { processName: string; data: string }) => {
|
|
59
|
+
if (processName === process.name) {
|
|
60
|
+
this.parseAndEmitCustomEvents(testRunId, data)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Handler for stderr events - parse for custom events
|
|
65
|
+
const onStderr = ({ processName, data }: { processName: string; data: string }) => {
|
|
66
|
+
if (processName === process.name) {
|
|
67
|
+
this.parseAndEmitCustomEvents(testRunId, data)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Store cleanup functions
|
|
72
|
+
listeners.set('stdout', () => taskSpawner.removeListener('stdout', onStdout))
|
|
73
|
+
listeners.set('stderr', () => taskSpawner.removeListener('stderr', onStderr))
|
|
74
|
+
|
|
75
|
+
// Register listeners
|
|
76
|
+
taskSpawner.on('stdout', onStdout)
|
|
77
|
+
taskSpawner.on('stderr', onStderr)
|
|
78
|
+
|
|
79
|
+
this.eventListeners.set(testRunId, listeners)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Parse stdout/stderr output for custom event markers and emit them
|
|
84
|
+
* Expects events in JSON format: {"event":"scenario::end","data":{...}}
|
|
85
|
+
*/
|
|
86
|
+
private parseAndEmitCustomEvents(testRunId: string, output: string): void {
|
|
87
|
+
// Split by newlines to handle multi-line output
|
|
88
|
+
const lines = output.split('\n')
|
|
89
|
+
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
if (!line.trim()) continue
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Look for JSON objects in the line that contain an "event" field
|
|
95
|
+
// Matches: {"event":"scenario::end","data":{...}}
|
|
96
|
+
const jsonMatch = line.match(/\{[\s\S]*"event"[\s\S]*\}/)
|
|
97
|
+
if (jsonMatch) {
|
|
98
|
+
const eventData = JSON.parse(jsonMatch[0])
|
|
99
|
+
if (eventData.event === 'scenario::end') {
|
|
100
|
+
console.log(`[ProcessManager] Parsed scenario::end event for testRunId: ${testRunId}`, eventData)
|
|
101
|
+
this.emit('scenario::end', {
|
|
102
|
+
testRunId,
|
|
103
|
+
scenarioName: eventData.data?.scenarioName,
|
|
104
|
+
status: eventData.data?.status,
|
|
105
|
+
tracePath: eventData.data?.tracePath,
|
|
106
|
+
...eventData.data,
|
|
107
|
+
})
|
|
108
|
+
console.log(`[ProcessManager] Emitted scenario::end event for testRunId: ${testRunId}`)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
// Not a custom event, continue parsing other lines
|
|
113
|
+
// This is expected for regular log output
|
|
114
|
+
// Only log if it looks like it might be a JSON parse error
|
|
115
|
+
if (line.trim().startsWith('{')) {
|
|
116
|
+
console.warn(`[ProcessManager] Failed to parse potential event JSON: ${line.substring(0, 100)}`, error)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get a process by test run ID
|
|
124
|
+
* @param testRunId - The test run ID
|
|
125
|
+
* @returns The spawned process or undefined if not found
|
|
126
|
+
*/
|
|
127
|
+
get(testRunId: string): SpawnedProcess | undefined {
|
|
128
|
+
return this.processes.get(testRunId)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Unregister a process for a test run
|
|
133
|
+
* Cleans up event listeners
|
|
134
|
+
* @param testRunId - The test run ID
|
|
135
|
+
* @returns True if the process was found and removed, false otherwise
|
|
136
|
+
*/
|
|
137
|
+
unregister(testRunId: string): boolean {
|
|
138
|
+
// Clean up event listeners
|
|
139
|
+
const listeners = this.eventListeners.get(testRunId)
|
|
140
|
+
if (listeners) {
|
|
141
|
+
listeners.forEach(cleanup => cleanup())
|
|
142
|
+
this.eventListeners.delete(testRunId)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const deleted = this.processes.delete(testRunId)
|
|
146
|
+
console.log(
|
|
147
|
+
`[ProcessManager] Unregistering process for testRunId: ${testRunId}, deleted: ${deleted}, remaining processes: ${this.processes.size}`,
|
|
148
|
+
)
|
|
149
|
+
return deleted
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check if a process exists for a test run
|
|
154
|
+
* @param testRunId - The test run ID
|
|
155
|
+
* @returns True if a process exists, false otherwise
|
|
156
|
+
*/
|
|
157
|
+
has(testRunId: string): boolean {
|
|
158
|
+
return this.processes.has(testRunId)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get all registered test run IDs (for debugging)
|
|
163
|
+
* @returns Array of test run IDs
|
|
164
|
+
*/
|
|
165
|
+
getAllTestRunIds(): string[] {
|
|
166
|
+
return Array.from(this.processes.keys())
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Clear all registered processes (useful for cleanup)
|
|
171
|
+
*/
|
|
172
|
+
clear(): void {
|
|
173
|
+
// Clean up all event listeners
|
|
174
|
+
this.eventListeners.forEach(listeners => {
|
|
175
|
+
listeners.forEach(cleanup => cleanup())
|
|
176
|
+
})
|
|
177
|
+
this.eventListeners.clear()
|
|
178
|
+
this.processes.clear()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get the number of active processes
|
|
183
|
+
* @returns The number of registered processes
|
|
184
|
+
*/
|
|
185
|
+
size(): number {
|
|
186
|
+
return this.processes.size
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Export singleton instance
|
|
191
|
+
export const processManager = ProcessManager.getInstance()
|