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,617 @@
|
|
|
1
|
+
import { execa, type Options as ExecaOptions } from 'execa'
|
|
2
|
+
import type { ChildProcess } from 'child_process'
|
|
3
|
+
import { EventEmitter } from 'events'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for spawning child processes
|
|
7
|
+
* @extends ExecaOptions - Extends execa Options for additional functionality
|
|
8
|
+
*/
|
|
9
|
+
export interface SpawnerOptions extends ExecaOptions {
|
|
10
|
+
/** Whether to stream logs to console (default: true) */
|
|
11
|
+
streamLogs?: boolean
|
|
12
|
+
/** Whether to prefix logs with process name (default: true) */
|
|
13
|
+
prefixLogs?: boolean
|
|
14
|
+
/** Custom prefix for logs (default: process name) */
|
|
15
|
+
logPrefix?: string
|
|
16
|
+
/** Whether to capture output for later retrieval (default: false) */
|
|
17
|
+
captureOutput?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Represents a spawned child process with metadata and output tracking
|
|
22
|
+
*/
|
|
23
|
+
export interface SpawnedProcess {
|
|
24
|
+
/** The underlying Node.js ChildProcess instance (returned by execa) */
|
|
25
|
+
process: ChildProcess
|
|
26
|
+
/** Process ID (PID) of the spawned process */
|
|
27
|
+
pid: number | undefined
|
|
28
|
+
/** Unique name identifier for the process */
|
|
29
|
+
name: string
|
|
30
|
+
/** Captured output from the process */
|
|
31
|
+
output: {
|
|
32
|
+
/** Standard output lines */
|
|
33
|
+
stdout: string[]
|
|
34
|
+
/** Standard error lines */
|
|
35
|
+
stderr: string[]
|
|
36
|
+
}
|
|
37
|
+
/** Whether the process is currently running */
|
|
38
|
+
isRunning: boolean
|
|
39
|
+
/** Exit code of the process (null if still running) */
|
|
40
|
+
exitCode: number | null
|
|
41
|
+
/** Timestamp when the process was started */
|
|
42
|
+
startTime: Date
|
|
43
|
+
/** Timestamp when the process ended (null if still running) */
|
|
44
|
+
endTime: Date | null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A comprehensive task spawner that manages child processes with logging, output capture,
|
|
49
|
+
* and process lifecycle management. Extends EventEmitter to provide process events.
|
|
50
|
+
*
|
|
51
|
+
* Features:
|
|
52
|
+
* - Concurrent process spawning and management
|
|
53
|
+
* - Real-time log streaming with customizable prefixes
|
|
54
|
+
* - Output capture and buffering
|
|
55
|
+
* - Process lifecycle tracking (start/end times, exit codes)
|
|
56
|
+
* - Event-driven architecture for process monitoring
|
|
57
|
+
* - Graceful process termination
|
|
58
|
+
*/
|
|
59
|
+
export class TaskSpawner extends EventEmitter {
|
|
60
|
+
/** Map of process names to SpawnedProcess instances */
|
|
61
|
+
private processes: Map<string, SpawnedProcess> = new Map()
|
|
62
|
+
/** Counter for generating unique process names */
|
|
63
|
+
private processCounter = 0
|
|
64
|
+
/** Internal buffers for processing output streams line by line */
|
|
65
|
+
private outputBuffers: Map<string, { stdout: string; stderr: string }> = new Map()
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Spawns a child process with comprehensive logging and output management
|
|
69
|
+
*
|
|
70
|
+
* @param command - The command to execute
|
|
71
|
+
* @param args - Array of command line arguments (default: [])
|
|
72
|
+
* @param options - Configuration options for spawning and logging
|
|
73
|
+
* @returns Promise that resolves to a SpawnedProcess instance
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const process = await spawner.spawn('npm', ['test'], {
|
|
78
|
+
* streamLogs: true,
|
|
79
|
+
* captureOutput: true,
|
|
80
|
+
* logPrefix: 'test-runner'
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
async spawn(command: string, args: string[] = [], options: SpawnerOptions = {}): Promise<SpawnedProcess> {
|
|
85
|
+
const { streamLogs = true, prefixLogs = true, logPrefix, captureOutput = false, ...spawnOptions } = options
|
|
86
|
+
|
|
87
|
+
const processName = logPrefix || `${command}_${++this.processCounter}`
|
|
88
|
+
|
|
89
|
+
// Create the spawned process object
|
|
90
|
+
const spawnedProcess: SpawnedProcess = {
|
|
91
|
+
process: null as unknown as ChildProcess, // Will be set below
|
|
92
|
+
pid: undefined, // Will be set below
|
|
93
|
+
name: processName,
|
|
94
|
+
output: {
|
|
95
|
+
stdout: [],
|
|
96
|
+
stderr: [],
|
|
97
|
+
},
|
|
98
|
+
isRunning: false,
|
|
99
|
+
exitCode: null,
|
|
100
|
+
startTime: new Date(),
|
|
101
|
+
endTime: null,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Spawn the child process using execa
|
|
105
|
+
// Always use 'pipe' when captureOutput is true to enable output capture
|
|
106
|
+
// streamLogs just controls whether we also log to console, not whether we can capture
|
|
107
|
+
const stdioConfig = captureOutput ? 'pipe' : streamLogs ? 'inherit' : 'pipe'
|
|
108
|
+
const childProcess = execa(command, args, {
|
|
109
|
+
stdio: stdioConfig,
|
|
110
|
+
...spawnOptions,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
spawnedProcess.process = childProcess
|
|
114
|
+
spawnedProcess.pid = childProcess.pid
|
|
115
|
+
spawnedProcess.isRunning = true
|
|
116
|
+
|
|
117
|
+
// Store the process
|
|
118
|
+
this.processes.set(processName, spawnedProcess)
|
|
119
|
+
|
|
120
|
+
// Initialize output buffers for this process
|
|
121
|
+
this.outputBuffers.set(processName, { stdout: '', stderr: '' })
|
|
122
|
+
|
|
123
|
+
// Set up event listeners
|
|
124
|
+
this.setupProcessListeners(spawnedProcess, {
|
|
125
|
+
streamLogs,
|
|
126
|
+
prefixLogs,
|
|
127
|
+
captureOutput,
|
|
128
|
+
stdioConfig,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Emit spawn event
|
|
132
|
+
this.emit('spawn', spawnedProcess)
|
|
133
|
+
|
|
134
|
+
return spawnedProcess
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Spawns multiple processes concurrently and returns a map of process names to instances
|
|
139
|
+
*
|
|
140
|
+
* @param tasks - Array of task configurations to spawn
|
|
141
|
+
* @returns Promise that resolves to a Map of process names to SpawnedProcess instances
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const processes = await spawner.spawnMultiple([
|
|
146
|
+
* { name: 'server', command: 'npm', args: ['start'] },
|
|
147
|
+
* { name: 'tests', command: 'npm', args: ['test'] }
|
|
148
|
+
* ]);
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
async spawnMultiple(
|
|
152
|
+
tasks: Array<{
|
|
153
|
+
name: string
|
|
154
|
+
command: string
|
|
155
|
+
args?: string[]
|
|
156
|
+
options?: SpawnerOptions
|
|
157
|
+
}>,
|
|
158
|
+
): Promise<Map<string, SpawnedProcess>> {
|
|
159
|
+
const promises = tasks.map(async task => {
|
|
160
|
+
const process = await this.spawn(task.command, task.args || [], {
|
|
161
|
+
logPrefix: task.name,
|
|
162
|
+
...task.options,
|
|
163
|
+
})
|
|
164
|
+
return { name: task.name, process }
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const results = await Promise.all(promises)
|
|
168
|
+
const processMap = new Map<string, SpawnedProcess>()
|
|
169
|
+
|
|
170
|
+
results.forEach(({ name, process }) => {
|
|
171
|
+
processMap.set(name, process)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
return processMap
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Terminates a specific process by name
|
|
179
|
+
*
|
|
180
|
+
* @param processName - The name of the process to kill
|
|
181
|
+
* @param signal - The signal to send to the process (default: 'SIGTERM')
|
|
182
|
+
* @returns True if the process was found and killed, false otherwise
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* const killed = spawner.killProcess('my-process', 'SIGKILL');
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
killProcess(processName: string, signal: NodeJS.Signals = 'SIGTERM'): boolean {
|
|
190
|
+
const spawnedProcess = this.processes.get(processName)
|
|
191
|
+
if (!spawnedProcess || !spawnedProcess.isRunning) {
|
|
192
|
+
return false
|
|
193
|
+
}
|
|
194
|
+
console.log(`[TaskSpawner] Killing process: ${processName}, PID: ${spawnedProcess.pid}, signal: ${signal}`)
|
|
195
|
+
spawnedProcess.process.kill(signal)
|
|
196
|
+
return true
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Terminates all currently running processes managed by this spawner
|
|
201
|
+
*
|
|
202
|
+
* @param signal - The signal to send to all processes (default: 'SIGTERM')
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* spawner.killAll('SIGKILL'); // Force kill all processes
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
killAll(signal: NodeJS.Signals = 'SIGTERM'): void {
|
|
210
|
+
this.processes.forEach(spawnedProcess => {
|
|
211
|
+
if (spawnedProcess.isRunning) {
|
|
212
|
+
spawnedProcess.process.kill(signal)
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Waits for a specific process to complete and returns its exit code
|
|
219
|
+
*
|
|
220
|
+
* @param processName - The name of the process to wait for
|
|
221
|
+
* @returns Promise that resolves to the exit code (null if process not found)
|
|
222
|
+
* @throws Error if the process name is not found
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* const exitCode = await spawner.waitForProcess('my-process');
|
|
227
|
+
* console.log(`Process exited with code: ${exitCode}`);
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
async waitForProcess(processName: string): Promise<number | null> {
|
|
231
|
+
const spawnedProcess = this.processes.get(processName)
|
|
232
|
+
if (!spawnedProcess) {
|
|
233
|
+
throw new Error(`Process '${processName}' not found`)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return new Promise(resolve => {
|
|
237
|
+
if (!spawnedProcess.isRunning) {
|
|
238
|
+
resolve(spawnedProcess.exitCode)
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
spawnedProcess.process.on('exit', (code: number | null) => {
|
|
243
|
+
resolve(code)
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Waits for all currently running processes to complete
|
|
250
|
+
*
|
|
251
|
+
* @returns Promise that resolves to a Map of process names to their exit codes
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* const results = await spawner.waitForAll();
|
|
256
|
+
* results.forEach((exitCode, processName) => {
|
|
257
|
+
* console.log(`${processName} exited with code: ${exitCode}`);
|
|
258
|
+
* });
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
async waitForAll(): Promise<Map<string, number | null>> {
|
|
262
|
+
const results = new Map<string, number | null>()
|
|
263
|
+
const promises: Array<Promise<[string, number | null]>> = []
|
|
264
|
+
|
|
265
|
+
this.processes.forEach((spawnedProcess, name) => {
|
|
266
|
+
if (spawnedProcess.isRunning) {
|
|
267
|
+
promises.push(this.waitForProcess(name).then(code => [name, code] as [string, number | null]))
|
|
268
|
+
} else {
|
|
269
|
+
results.set(name, spawnedProcess.exitCode)
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
const completed = await Promise.all(promises)
|
|
274
|
+
completed.forEach(([name, code]) => {
|
|
275
|
+
results.set(name, code)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
return results
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Retrieves a specific process by name
|
|
283
|
+
*
|
|
284
|
+
* @param processName - The name of the process to retrieve
|
|
285
|
+
* @returns The SpawnedProcess instance or undefined if not found
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* const process = spawner.getProcess('my-process');
|
|
290
|
+
* if (process) {
|
|
291
|
+
* console.log(`Process is running: ${process.isRunning}`);
|
|
292
|
+
* }
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
getProcess(processName: string): SpawnedProcess | undefined {
|
|
296
|
+
return this.processes.get(processName)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Retrieves all processes managed by this spawner (running and completed)
|
|
301
|
+
*
|
|
302
|
+
* @returns A Map of process names to SpawnedProcess instances
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```typescript
|
|
306
|
+
* const allProcesses = spawner.getAllProcesses();
|
|
307
|
+
* console.log(`Total processes: ${allProcesses.size}`);
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
getAllProcesses(): Map<string, SpawnedProcess> {
|
|
311
|
+
return new Map(this.processes)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Retrieves only the currently running processes
|
|
316
|
+
*
|
|
317
|
+
* @returns A Map of running process names to SpawnedProcess instances
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* const running = spawner.getRunningProcesses();
|
|
322
|
+
* console.log(`Running processes: ${running.size}`);
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
325
|
+
getRunningProcesses(): Map<string, SpawnedProcess> {
|
|
326
|
+
const running = new Map<string, SpawnedProcess>()
|
|
327
|
+
this.processes.forEach((process, name) => {
|
|
328
|
+
if (process.isRunning) {
|
|
329
|
+
running.set(name, process)
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
return running
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Retrieves the captured output from a specific process
|
|
337
|
+
*
|
|
338
|
+
* @param processName - The name of the process to get output for
|
|
339
|
+
* @returns Object containing stdout and stderr arrays, or null if process not found
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* const output = spawner.getProcessOutput('my-process');
|
|
344
|
+
* if (output) {
|
|
345
|
+
* console.log('STDOUT:', output.stdout.join('\n'));
|
|
346
|
+
* console.log('STDERR:', output.stderr.join('\n'));
|
|
347
|
+
* }
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
getProcessOutput(processName: string): { stdout: string[]; stderr: string[] } | null {
|
|
351
|
+
const spawnedProcess = this.processes.get(processName)
|
|
352
|
+
return spawnedProcess?.output || null
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Clears all process history and resets the spawner to initial state
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```typescript
|
|
360
|
+
* spawner.clearHistory(); // Removes all tracked processes
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
clearHistory(): void {
|
|
364
|
+
this.processes.clear()
|
|
365
|
+
this.outputBuffers.clear()
|
|
366
|
+
this.processCounter = 0
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Processes buffered output and emits complete lines
|
|
371
|
+
*
|
|
372
|
+
* @private
|
|
373
|
+
* @param processName - The name of the process
|
|
374
|
+
* @param stream - The stream type ('stdout' or 'stderr')
|
|
375
|
+
* @param streamLogs - Whether to stream logs to console
|
|
376
|
+
* @param prefixLogs - Whether to prefix logs with process name
|
|
377
|
+
* @param captureOutput - Whether to capture output for later retrieval
|
|
378
|
+
* @param spawnedProcess - The spawned process instance
|
|
379
|
+
*/
|
|
380
|
+
private processBufferedOutput(
|
|
381
|
+
processName: string,
|
|
382
|
+
stream: 'stdout' | 'stderr',
|
|
383
|
+
streamLogs: boolean,
|
|
384
|
+
prefixLogs: boolean,
|
|
385
|
+
captureOutput: boolean,
|
|
386
|
+
spawnedProcess: SpawnedProcess,
|
|
387
|
+
): void {
|
|
388
|
+
const buffer = this.outputBuffers.get(processName)
|
|
389
|
+
if (!buffer) return
|
|
390
|
+
|
|
391
|
+
const currentBuffer = buffer[stream]
|
|
392
|
+
const lines = currentBuffer.split('\n')
|
|
393
|
+
|
|
394
|
+
// Keep the last line in buffer (might be incomplete)
|
|
395
|
+
buffer[stream] = lines.pop() || ''
|
|
396
|
+
|
|
397
|
+
// Process complete lines
|
|
398
|
+
for (const line of lines) {
|
|
399
|
+
if (captureOutput) {
|
|
400
|
+
spawnedProcess.output[stream].push(line + '\n')
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (streamLogs) {
|
|
404
|
+
const prefix = prefixLogs ? `[${processName}] ` : ''
|
|
405
|
+
if (stream === 'stdout') {
|
|
406
|
+
console.log(`${prefix}${line}`)
|
|
407
|
+
} else {
|
|
408
|
+
console.error(`${prefix}${line}`)
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
this.emit(stream, { processName, data: line + '\n' })
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Sets up event listeners for a spawned process
|
|
418
|
+
*
|
|
419
|
+
* @private
|
|
420
|
+
* @param spawnedProcess - The spawned process instance
|
|
421
|
+
* @param options - Configuration options for the listeners
|
|
422
|
+
*/
|
|
423
|
+
private setupProcessListeners(
|
|
424
|
+
spawnedProcess: SpawnedProcess,
|
|
425
|
+
options: {
|
|
426
|
+
streamLogs: boolean
|
|
427
|
+
prefixLogs: boolean
|
|
428
|
+
captureOutput: boolean
|
|
429
|
+
stdioConfig: string | string[]
|
|
430
|
+
},
|
|
431
|
+
): void {
|
|
432
|
+
const { streamLogs, prefixLogs, captureOutput, stdioConfig } = options
|
|
433
|
+
const { process: childProcess, name } = spawnedProcess
|
|
434
|
+
|
|
435
|
+
// Only set up data listeners when stdio is 'pipe'
|
|
436
|
+
if (stdioConfig === 'pipe') {
|
|
437
|
+
// Handle stdout
|
|
438
|
+
childProcess.stdout?.on('data', (data: Buffer) => {
|
|
439
|
+
const output = data.toString()
|
|
440
|
+
const buffer = this.outputBuffers.get(name)
|
|
441
|
+
|
|
442
|
+
if (buffer) {
|
|
443
|
+
buffer.stdout += output
|
|
444
|
+
this.processBufferedOutput(name, 'stdout', streamLogs, prefixLogs, captureOutput, spawnedProcess)
|
|
445
|
+
} else {
|
|
446
|
+
console.warn(`[TaskSpawner] No output buffer found for process: ${name}`)
|
|
447
|
+
}
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
// Handle stderr
|
|
451
|
+
childProcess.stderr?.on('data', (data: Buffer) => {
|
|
452
|
+
const output = data.toString()
|
|
453
|
+
const buffer = this.outputBuffers.get(name)
|
|
454
|
+
|
|
455
|
+
if (buffer) {
|
|
456
|
+
buffer.stderr += output
|
|
457
|
+
this.processBufferedOutput(name, 'stderr', streamLogs, prefixLogs, captureOutput, spawnedProcess)
|
|
458
|
+
} else {
|
|
459
|
+
console.warn(`[TaskSpawner] No output buffer found for process: ${name}`)
|
|
460
|
+
}
|
|
461
|
+
})
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Handle process exit
|
|
465
|
+
childProcess.on('exit', (code: number | null) => {
|
|
466
|
+
// Only flush buffered output when stdio is 'pipe'
|
|
467
|
+
if (stdioConfig === 'pipe') {
|
|
468
|
+
const buffer = this.outputBuffers.get(name)
|
|
469
|
+
if (buffer) {
|
|
470
|
+
// Process remaining stdout
|
|
471
|
+
if (buffer.stdout) {
|
|
472
|
+
if (captureOutput) {
|
|
473
|
+
spawnedProcess.output.stdout.push(buffer.stdout)
|
|
474
|
+
}
|
|
475
|
+
if (streamLogs) {
|
|
476
|
+
const prefix = prefixLogs ? `[${name}] ` : ''
|
|
477
|
+
console.log(`${prefix}${buffer.stdout}`)
|
|
478
|
+
}
|
|
479
|
+
console.log(
|
|
480
|
+
`[TaskSpawner] Emitting final stdout event for process: ${name}, data length: ${buffer.stdout.length} chars`,
|
|
481
|
+
)
|
|
482
|
+
this.emit('stdout', {
|
|
483
|
+
processName: name,
|
|
484
|
+
data: buffer.stdout,
|
|
485
|
+
})
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Process remaining stderr
|
|
489
|
+
if (buffer.stderr) {
|
|
490
|
+
if (captureOutput) {
|
|
491
|
+
spawnedProcess.output.stderr.push(buffer.stderr)
|
|
492
|
+
}
|
|
493
|
+
if (streamLogs) {
|
|
494
|
+
const prefix = prefixLogs ? `[${name}] ` : ''
|
|
495
|
+
console.error(`${prefix}${buffer.stderr}`)
|
|
496
|
+
}
|
|
497
|
+
console.log(
|
|
498
|
+
`[TaskSpawner] Emitting final stderr event for process: ${name}, data length: ${buffer.stderr.length} chars`,
|
|
499
|
+
)
|
|
500
|
+
this.emit('stderr', {
|
|
501
|
+
processName: name,
|
|
502
|
+
data: buffer.stderr,
|
|
503
|
+
})
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Clear the buffer
|
|
507
|
+
this.outputBuffers.delete(name)
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
spawnedProcess.isRunning = false
|
|
512
|
+
spawnedProcess.exitCode = code
|
|
513
|
+
spawnedProcess.endTime = new Date()
|
|
514
|
+
|
|
515
|
+
console.log(`[TaskSpawner] Process exited: ${name}, code: ${code}, emitting exit event`)
|
|
516
|
+
this.emit('exit', { processName: name, code })
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
// Handle process error
|
|
520
|
+
childProcess.on('error', (error: Error) => {
|
|
521
|
+
spawnedProcess.isRunning = false
|
|
522
|
+
spawnedProcess.endTime = new Date()
|
|
523
|
+
|
|
524
|
+
if (streamLogs) {
|
|
525
|
+
const prefix = prefixLogs ? `[${name}] ` : ''
|
|
526
|
+
console.error(`${prefix}ERROR: ${error.message}`)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
this.emit('error', { processName: name, error })
|
|
530
|
+
})
|
|
531
|
+
|
|
532
|
+
// Handle uncaught exceptions to prevent crashes
|
|
533
|
+
childProcess.on('uncaughtException', (error: Error) => {
|
|
534
|
+
spawnedProcess.isRunning = false
|
|
535
|
+
spawnedProcess.endTime = new Date()
|
|
536
|
+
|
|
537
|
+
if (streamLogs) {
|
|
538
|
+
const prefix = prefixLogs ? `[${name}] ` : ''
|
|
539
|
+
console.error(`${prefix}UNCAUGHT EXCEPTION: ${error.message}`)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
this.emit('error', { processName: name, error })
|
|
543
|
+
})
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Default TaskSpawner instance for convenience
|
|
549
|
+
* Use this when you don't need multiple spawner instances
|
|
550
|
+
* Uses global variable pattern (like Prisma) to persist across Next.js runtime contexts
|
|
551
|
+
*/
|
|
552
|
+
const globalForTaskSpawner = global as unknown as {
|
|
553
|
+
taskSpawner: TaskSpawner | undefined
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export const taskSpawner = globalForTaskSpawner.taskSpawner ?? new TaskSpawner()
|
|
557
|
+
|
|
558
|
+
if (!globalForTaskSpawner.taskSpawner) {
|
|
559
|
+
globalForTaskSpawner.taskSpawner = taskSpawner
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Convenience function to spawn a single task using the default spawner
|
|
564
|
+
*
|
|
565
|
+
* @param command - The command to execute
|
|
566
|
+
* @param args - Array of command line arguments
|
|
567
|
+
* @param options - Configuration options for spawning
|
|
568
|
+
* @returns Promise that resolves to a SpawnedProcess instance
|
|
569
|
+
*/
|
|
570
|
+
export const spawnTask = (command: string, args: string[] = [], options: SpawnerOptions = {}) =>
|
|
571
|
+
taskSpawner.spawn(command, args, options)
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Convenience function to spawn multiple tasks using the default spawner
|
|
575
|
+
*
|
|
576
|
+
* @param tasks - Array of task configurations to spawn
|
|
577
|
+
* @returns Promise that resolves to a Map of process names to SpawnedProcess instances
|
|
578
|
+
*/
|
|
579
|
+
export const spawnMultipleTasks = (
|
|
580
|
+
tasks: Array<{
|
|
581
|
+
name: string
|
|
582
|
+
command: string
|
|
583
|
+
args?: string[]
|
|
584
|
+
options?: SpawnerOptions
|
|
585
|
+
}>,
|
|
586
|
+
) => taskSpawner.spawnMultiple(tasks)
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Convenience function to kill a task using the default spawner
|
|
590
|
+
*
|
|
591
|
+
* @param processName - The name of the process to kill
|
|
592
|
+
* @param signal - The signal to send to the process
|
|
593
|
+
* @returns True if the process was found and killed, false otherwise
|
|
594
|
+
*/
|
|
595
|
+
export const killTask = (processName: string, signal?: NodeJS.Signals) => taskSpawner.killProcess(processName, signal)
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Convenience function to kill all tasks using the default spawner
|
|
599
|
+
*
|
|
600
|
+
* @param signal - The signal to send to all processes
|
|
601
|
+
*/
|
|
602
|
+
export const killAllTasks = (signal?: NodeJS.Signals) => taskSpawner.killAll(signal)
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Convenience function to wait for a task using the default spawner
|
|
606
|
+
*
|
|
607
|
+
* @param processName - The name of the process to wait for
|
|
608
|
+
* @returns Promise that resolves to the exit code
|
|
609
|
+
*/
|
|
610
|
+
export const waitForTask = (processName: string) => taskSpawner.waitForProcess(processName)
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Convenience function to wait for all tasks using the default spawner
|
|
614
|
+
*
|
|
615
|
+
* @returns Promise that resolves to a Map of process names to exit codes
|
|
616
|
+
*/
|
|
617
|
+
export const waitForAllTasks = () => taskSpawner.waitForAll()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { StepParameterType } from '@prisma/client'
|
|
2
|
+
|
|
3
|
+
export type NodeData = {
|
|
4
|
+
order: number
|
|
5
|
+
label: string
|
|
6
|
+
gherkinStep?: string
|
|
7
|
+
isFirstNode?: boolean
|
|
8
|
+
icon?: string
|
|
9
|
+
parameters: {
|
|
10
|
+
name: string
|
|
11
|
+
value: string
|
|
12
|
+
type: StepParameterType
|
|
13
|
+
order: number
|
|
14
|
+
}[]
|
|
15
|
+
templateStepId: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type NodeOrderMap = Record<string, NodeData>
|
|
19
|
+
|
|
20
|
+
export type TemplateTestCaseNodeData = {
|
|
21
|
+
order: number
|
|
22
|
+
label: string
|
|
23
|
+
gherkinStep?: string
|
|
24
|
+
icon?: string
|
|
25
|
+
parameters: {
|
|
26
|
+
name: string
|
|
27
|
+
defaultValue: string
|
|
28
|
+
type: StepParameterType
|
|
29
|
+
order: number
|
|
30
|
+
}[]
|
|
31
|
+
templateStepId: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type TemplateTestCaseNodeOrderMap = Record<string, TemplateTestCaseNodeData>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TemplateStep, TemplateStepGroup } from '@prisma/client'
|
|
2
|
+
|
|
3
|
+
/** Template step with optional group relation (e.g. from getAllTemplateStepsAction). */
|
|
4
|
+
export type TemplateStepWithGroup = TemplateStep & {
|
|
5
|
+
templateStepGroup?: TemplateStepGroup
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Returns the group name in title case for display (e.g. "navigation" -> "Navigation"). */
|
|
9
|
+
export function capitalizeGroupName(name: string): string {
|
|
10
|
+
return name.replace(/\b\w/g, c => c.toUpperCase())
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type BrowserName = 'chromium' | 'firefox' | 'webkit'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type CSSSelector = string
|
|
2
|
+
export type XPathSelector = `/${string}` | `//${string}`
|
|
3
|
+
export type SelectorName = string
|
|
4
|
+
export type Selector = CSSSelector | XPathSelector
|
|
5
|
+
export type Locator = Record<string, Selector>
|
|
6
|
+
export type LocatorMap = {
|
|
7
|
+
name: string
|
|
8
|
+
path: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type LocatorCollection = Record<string, Locator>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Locator = string
|