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,316 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises'
|
|
2
|
+
import { StepStatus, StepKeyword } from '@prisma/client'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parsed report structure matching cucumber.json format
|
|
6
|
+
*/
|
|
7
|
+
export interface ParsedReport {
|
|
8
|
+
features: Array<{
|
|
9
|
+
name: string
|
|
10
|
+
description: string
|
|
11
|
+
uri: string
|
|
12
|
+
line: number
|
|
13
|
+
keyword: string
|
|
14
|
+
tags: Array<{ name: string; line: number }>
|
|
15
|
+
scenarios: Array<{
|
|
16
|
+
name: string
|
|
17
|
+
description: string
|
|
18
|
+
line: number
|
|
19
|
+
keyword: string
|
|
20
|
+
type: string
|
|
21
|
+
cucumberId: string
|
|
22
|
+
tags: Array<{ name: string; line: number }>
|
|
23
|
+
steps: Array<{
|
|
24
|
+
keyword: string
|
|
25
|
+
line: number | null
|
|
26
|
+
name: string
|
|
27
|
+
matchLocation?: string
|
|
28
|
+
status: string
|
|
29
|
+
duration: number
|
|
30
|
+
errorMessage?: string
|
|
31
|
+
errorTrace?: string
|
|
32
|
+
hidden: boolean
|
|
33
|
+
order: number
|
|
34
|
+
}>
|
|
35
|
+
hooks: Array<{
|
|
36
|
+
keyword: string
|
|
37
|
+
status: string
|
|
38
|
+
duration: number
|
|
39
|
+
errorMessage?: string
|
|
40
|
+
errorTrace?: string
|
|
41
|
+
hidden: boolean
|
|
42
|
+
}>
|
|
43
|
+
}>
|
|
44
|
+
}>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Cucumber JSON report structure (as received from cucumber-js)
|
|
49
|
+
*/
|
|
50
|
+
interface CucumberJsonFeature {
|
|
51
|
+
description: string
|
|
52
|
+
elements: Array<{
|
|
53
|
+
description: string
|
|
54
|
+
id: string
|
|
55
|
+
keyword: string
|
|
56
|
+
line: number
|
|
57
|
+
name: string
|
|
58
|
+
steps: Array<{
|
|
59
|
+
keyword: string
|
|
60
|
+
line?: number
|
|
61
|
+
name?: string
|
|
62
|
+
hidden?: boolean
|
|
63
|
+
match?: {
|
|
64
|
+
location: string
|
|
65
|
+
}
|
|
66
|
+
result?: {
|
|
67
|
+
status: string
|
|
68
|
+
duration?: number
|
|
69
|
+
error_message?: string
|
|
70
|
+
}
|
|
71
|
+
}>
|
|
72
|
+
tags: Array<{
|
|
73
|
+
name: string
|
|
74
|
+
line: number
|
|
75
|
+
}>
|
|
76
|
+
type: string
|
|
77
|
+
}>
|
|
78
|
+
id: string
|
|
79
|
+
line: number
|
|
80
|
+
keyword: string
|
|
81
|
+
name: string
|
|
82
|
+
tags: Array<{
|
|
83
|
+
name: string
|
|
84
|
+
line: number
|
|
85
|
+
}>
|
|
86
|
+
uri: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Maps cucumber step status to Prisma StepStatus enum
|
|
91
|
+
*/
|
|
92
|
+
function mapStepStatus(status: string): StepStatus {
|
|
93
|
+
const upperStatus = status.toUpperCase()
|
|
94
|
+
switch (upperStatus) {
|
|
95
|
+
case 'PASSED':
|
|
96
|
+
return StepStatus.PASSED
|
|
97
|
+
case 'FAILED':
|
|
98
|
+
return StepStatus.FAILED
|
|
99
|
+
case 'SKIPPED':
|
|
100
|
+
return StepStatus.SKIPPED
|
|
101
|
+
case 'PENDING':
|
|
102
|
+
return StepStatus.PENDING
|
|
103
|
+
case 'UNDEFINED':
|
|
104
|
+
return StepStatus.UNDEFINED
|
|
105
|
+
default:
|
|
106
|
+
return StepStatus.PENDING
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Maps cucumber step keyword to Prisma StepKeyword enum
|
|
112
|
+
*/
|
|
113
|
+
function mapStepKeyword(keyword: string): StepKeyword {
|
|
114
|
+
const trimmedKeyword = keyword.trim()
|
|
115
|
+
const upperKeyword = trimmedKeyword.toUpperCase()
|
|
116
|
+
switch (upperKeyword) {
|
|
117
|
+
case 'GIVEN':
|
|
118
|
+
return StepKeyword.GIVEN
|
|
119
|
+
case 'WHEN':
|
|
120
|
+
return StepKeyword.WHEN
|
|
121
|
+
case 'THEN':
|
|
122
|
+
return StepKeyword.THEN
|
|
123
|
+
case 'AND':
|
|
124
|
+
return StepKeyword.AND
|
|
125
|
+
case 'BUT':
|
|
126
|
+
return StepKeyword.BUT
|
|
127
|
+
case 'BEFORE':
|
|
128
|
+
return StepKeyword.BEFORE
|
|
129
|
+
case 'AFTER':
|
|
130
|
+
return StepKeyword.AFTER
|
|
131
|
+
default:
|
|
132
|
+
// Default to GIVEN for unknown keywords
|
|
133
|
+
return StepKeyword.GIVEN
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Separates error message from stack trace
|
|
139
|
+
* Cucumber's error_message typically contains both the error message and stack trace
|
|
140
|
+
* The stack trace usually starts with "at" or contains file paths
|
|
141
|
+
*
|
|
142
|
+
* @param errorMessage - The full error message from Cucumber
|
|
143
|
+
* @returns Object with separated message and trace
|
|
144
|
+
*/
|
|
145
|
+
function separateErrorMessageAndTrace(errorMessage: string | undefined): {
|
|
146
|
+
message: string | undefined
|
|
147
|
+
trace: string | undefined
|
|
148
|
+
} {
|
|
149
|
+
if (!errorMessage) {
|
|
150
|
+
return { message: undefined, trace: undefined }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Look for common stack trace indicators
|
|
154
|
+
// Stack traces typically start with "at", " at", or contain file paths with line numbers
|
|
155
|
+
const stackTracePatterns = [
|
|
156
|
+
/\n\s*at\s/, // "at" at the start of a line (with optional whitespace)
|
|
157
|
+
/\n\s+at\s/, // "at" with leading whitespace
|
|
158
|
+
/Error:\s*$/m, // Error: at end of line (message ends, trace starts)
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
// Find the first occurrence of a stack trace pattern
|
|
162
|
+
let splitIndex = -1
|
|
163
|
+
for (const pattern of stackTracePatterns) {
|
|
164
|
+
const match = errorMessage.match(pattern)
|
|
165
|
+
if (match && match.index !== undefined) {
|
|
166
|
+
// If we find "at" pattern, split before it (include the newline before "at")
|
|
167
|
+
// If we find "Error:" pattern, split after it
|
|
168
|
+
if (pattern.source.includes('Error:')) {
|
|
169
|
+
splitIndex = match.index + match[0].length
|
|
170
|
+
} else {
|
|
171
|
+
splitIndex = match.index
|
|
172
|
+
}
|
|
173
|
+
break
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// If no stack trace pattern found, check if it looks like just a message (short, no file paths)
|
|
178
|
+
if (splitIndex === -1) {
|
|
179
|
+
// Check if it contains file paths (common in stack traces)
|
|
180
|
+
const hasFilePath = /[\/\\][\w\-\.]+:\d+/.test(errorMessage)
|
|
181
|
+
if (hasFilePath && errorMessage.length > 200) {
|
|
182
|
+
// Likely contains a trace, try to find where message ends
|
|
183
|
+
// Usually the first few lines are the message, rest is trace
|
|
184
|
+
const lines = errorMessage.split('\n')
|
|
185
|
+
// Look for the first line that starts with whitespace and "at" or contains a file path
|
|
186
|
+
for (let i = 0; i < lines.length; i++) {
|
|
187
|
+
if (/^\s*at\s/.test(lines[i]) || /[\/\\][\w\-\.]+:\d+/.test(lines[i])) {
|
|
188
|
+
splitIndex = errorMessage.indexOf(lines[i])
|
|
189
|
+
break
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (splitIndex > 0) {
|
|
196
|
+
const message = errorMessage.substring(0, splitIndex).trim()
|
|
197
|
+
const trace = errorMessage.substring(splitIndex).trim()
|
|
198
|
+
return {
|
|
199
|
+
message: message || undefined,
|
|
200
|
+
trace: trace || undefined,
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// If we can't separate them, treat the whole thing as the message
|
|
205
|
+
// and leave trace as undefined (better than duplicating)
|
|
206
|
+
return {
|
|
207
|
+
message: errorMessage.trim() || undefined,
|
|
208
|
+
trace: undefined,
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Parses a cucumber.json report file into a structured ParsedReport object
|
|
214
|
+
*
|
|
215
|
+
* @param reportPath - Path to the cucumber.json file
|
|
216
|
+
* @returns Parsed report structure
|
|
217
|
+
* @throws Error if file cannot be read or parsed
|
|
218
|
+
*/
|
|
219
|
+
export async function parseCucumberReport(reportPath: string): Promise<ParsedReport> {
|
|
220
|
+
try {
|
|
221
|
+
// Read the report file
|
|
222
|
+
const fileContent = await readFile(reportPath, 'utf-8')
|
|
223
|
+
const cucumberData: CucumberJsonFeature[] = JSON.parse(fileContent)
|
|
224
|
+
|
|
225
|
+
// Parse features
|
|
226
|
+
const features = cucumberData.map(feature => {
|
|
227
|
+
// Separate steps into regular steps and hooks
|
|
228
|
+
const scenarios = feature.elements.map(element => {
|
|
229
|
+
const steps: ParsedReport['features'][0]['scenarios'][0]['steps'] = []
|
|
230
|
+
const hooks: ParsedReport['features'][0]['scenarios'][0]['hooks'] = []
|
|
231
|
+
|
|
232
|
+
element.steps.forEach((step, index) => {
|
|
233
|
+
const isHook = step.keyword === 'Before' || step.keyword === 'After'
|
|
234
|
+
const stepStatus = step.result?.status || 'pending'
|
|
235
|
+
const duration = step.result?.duration || 0
|
|
236
|
+
|
|
237
|
+
// Separate error message from stack trace
|
|
238
|
+
const { message: errorMessage, trace: errorTrace } = separateErrorMessageAndTrace(step.result?.error_message)
|
|
239
|
+
|
|
240
|
+
if (isHook) {
|
|
241
|
+
hooks.push({
|
|
242
|
+
keyword: step.keyword,
|
|
243
|
+
status: stepStatus,
|
|
244
|
+
duration,
|
|
245
|
+
errorMessage,
|
|
246
|
+
errorTrace,
|
|
247
|
+
hidden: step.hidden || false,
|
|
248
|
+
})
|
|
249
|
+
} else {
|
|
250
|
+
steps.push({
|
|
251
|
+
keyword: step.keyword.trim(),
|
|
252
|
+
line: step.line || null,
|
|
253
|
+
name: step.name || '',
|
|
254
|
+
matchLocation: step.match?.location,
|
|
255
|
+
status: stepStatus,
|
|
256
|
+
duration,
|
|
257
|
+
errorMessage,
|
|
258
|
+
errorTrace,
|
|
259
|
+
hidden: step.hidden || false,
|
|
260
|
+
order: index,
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
name: element.name,
|
|
267
|
+
description: element.description || '',
|
|
268
|
+
line: element.line,
|
|
269
|
+
keyword: element.keyword,
|
|
270
|
+
type: element.type,
|
|
271
|
+
cucumberId: element.id,
|
|
272
|
+
tags: element.tags.map(tag => ({
|
|
273
|
+
name: tag.name,
|
|
274
|
+
line: tag.line,
|
|
275
|
+
})),
|
|
276
|
+
steps,
|
|
277
|
+
hooks,
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
name: feature.name,
|
|
283
|
+
description: feature.description || '',
|
|
284
|
+
uri: feature.uri,
|
|
285
|
+
line: feature.line,
|
|
286
|
+
keyword: feature.keyword,
|
|
287
|
+
tags: feature.tags.map(tag => ({
|
|
288
|
+
name: tag.name,
|
|
289
|
+
line: tag.line,
|
|
290
|
+
})),
|
|
291
|
+
scenarios,
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
return { features }
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (error instanceof Error) {
|
|
298
|
+
throw new Error(`Failed to parse cucumber report at ${reportPath}: ${error.message}`)
|
|
299
|
+
}
|
|
300
|
+
throw new Error(`Failed to parse cucumber report at ${reportPath}: Unknown error`)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Helper function to convert parsed step status to Prisma enum
|
|
306
|
+
*/
|
|
307
|
+
export function getStepStatusEnum(status: string): StepStatus {
|
|
308
|
+
return mapStepStatus(status)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Helper function to convert parsed step keyword to Prisma enum
|
|
313
|
+
*/
|
|
314
|
+
export function getStepKeywordEnum(keyword: string): StepKeyword {
|
|
315
|
+
return mapStepKeyword(keyword)
|
|
316
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { BrowserEngine, Environment, Tag } from '@prisma/client'
|
|
2
|
+
import { spawnTask } from '@/tests/utils/spawner.util'
|
|
3
|
+
import { processManager } from './process-manager'
|
|
4
|
+
import type { SpawnedProcess } from '@/tests/utils/spawner.util'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for executing a test run
|
|
9
|
+
*/
|
|
10
|
+
export interface TestRunExecutionConfig {
|
|
11
|
+
testRunId: string
|
|
12
|
+
environment: Environment
|
|
13
|
+
tags: Tag[]
|
|
14
|
+
testWorkersCount: number
|
|
15
|
+
browserEngine: BrowserEngine
|
|
16
|
+
headless?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Maps BrowserEngine enum to browser name string for CLI
|
|
21
|
+
*/
|
|
22
|
+
function mapBrowserEngineToName(browserEngine: BrowserEngine): 'chromium' | 'firefox' | 'webkit' {
|
|
23
|
+
switch (browserEngine) {
|
|
24
|
+
case BrowserEngine.CHROMIUM:
|
|
25
|
+
return 'chromium'
|
|
26
|
+
case BrowserEngine.FIREFOX:
|
|
27
|
+
return 'firefox'
|
|
28
|
+
case BrowserEngine.WEBKIT:
|
|
29
|
+
return 'webkit'
|
|
30
|
+
default:
|
|
31
|
+
return 'chromium'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Combines multiple tag expressions into a single Cucumber tag expression
|
|
37
|
+
* Uses OR logic to combine tags: (@tag1 or @tag2 or @tag3)
|
|
38
|
+
*/
|
|
39
|
+
function combineTagExpressions(tags: Tag[]): string | null {
|
|
40
|
+
if (tags.length === 0) {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (tags.length === 1) {
|
|
45
|
+
return tags[0].tagExpression
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Combine multiple tag expressions with OR
|
|
49
|
+
return tags.map(tag => `(${tag.tagExpression})`).join(' or ')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generates a unique report path for a test run
|
|
54
|
+
* Format: src/tests/reports/cucumber-{testRunId}-{timestamp}.json
|
|
55
|
+
*
|
|
56
|
+
* @param testRunId - The test run ID (runId, not id)
|
|
57
|
+
* @returns The full path to the report file
|
|
58
|
+
*/
|
|
59
|
+
export function generateReportPath(testRunId: string): string {
|
|
60
|
+
const timestamp = Date.now()
|
|
61
|
+
const reportFileName = `cucumber-${testRunId}-${timestamp}.json`
|
|
62
|
+
const reportPath = join(process.cwd(), 'src', 'tests', 'reports', reportFileName)
|
|
63
|
+
return reportPath
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Sets environment variables for the test execution
|
|
68
|
+
*/
|
|
69
|
+
function setEnvironmentVariables(
|
|
70
|
+
environment: Environment,
|
|
71
|
+
headless: boolean,
|
|
72
|
+
browser: string,
|
|
73
|
+
reportPath?: string,
|
|
74
|
+
): void {
|
|
75
|
+
process.env.ENVIRONMENT = environment.name
|
|
76
|
+
process.env.HEADLESS = headless.toString()
|
|
77
|
+
process.env.BROWSER = browser
|
|
78
|
+
if (reportPath) {
|
|
79
|
+
process.env.REPORT_PATH = reportPath
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Executes a test run by spawning a cucumber process
|
|
85
|
+
*
|
|
86
|
+
* @param config - Test run execution configuration
|
|
87
|
+
* @returns Promise that resolves to an object containing the spawned process and report path
|
|
88
|
+
*/
|
|
89
|
+
export async function executeTestRun(config: TestRunExecutionConfig): Promise<{
|
|
90
|
+
process: SpawnedProcess
|
|
91
|
+
reportPath: string
|
|
92
|
+
}> {
|
|
93
|
+
const { testRunId, environment, tags, testWorkersCount, browserEngine, headless = true } = config
|
|
94
|
+
|
|
95
|
+
// Generate unique report path
|
|
96
|
+
const reportPath = generateReportPath(testRunId)
|
|
97
|
+
|
|
98
|
+
// Map browser engine to browser name
|
|
99
|
+
const browserName = mapBrowserEngineToName(browserEngine)
|
|
100
|
+
|
|
101
|
+
// Set environment variables including report path
|
|
102
|
+
setEnvironmentVariables(environment, headless, browserName, reportPath)
|
|
103
|
+
|
|
104
|
+
// Build cucumber command arguments
|
|
105
|
+
const cucumberArgs: string[] = []
|
|
106
|
+
|
|
107
|
+
// Add tag expression if tags are provided
|
|
108
|
+
const tagExpression = combineTagExpressions(tags)
|
|
109
|
+
if (tagExpression) {
|
|
110
|
+
cucumberArgs.push('-t', tagExpression)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Add parallel flag if workers > 1
|
|
114
|
+
if (testWorkersCount > 1) {
|
|
115
|
+
cucumberArgs.push('--parallel', testWorkersCount.toString())
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Spawn the cucumber test process
|
|
119
|
+
// Enable streamLogs to see output in console for debugging
|
|
120
|
+
// Use captureOutput: true to capture logs for streaming
|
|
121
|
+
const process = await spawnTask('npx', ['cucumber-js', ...cucumberArgs], {
|
|
122
|
+
streamLogs: true, // Enable console output to verify logs are being generated
|
|
123
|
+
prefixLogs: true, // Add prefix to identify logs
|
|
124
|
+
logPrefix: `test-run-${testRunId}`,
|
|
125
|
+
captureOutput: true, // Capture output for streaming
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Register the process in ProcessManager
|
|
129
|
+
console.log(`[TestRunExecutor] Registering process for testRunId: ${testRunId}, process name: ${process.name}`)
|
|
130
|
+
processManager.register(testRunId, process)
|
|
131
|
+
console.log(`[TestRunExecutor] Process registered. ProcessManager now has ${processManager.size()} processes`)
|
|
132
|
+
|
|
133
|
+
// Set up exit handler - Immediately unregister process to free up memory
|
|
134
|
+
process.process.on('exit', code => {
|
|
135
|
+
// Immediately unregister the process to free up memory
|
|
136
|
+
// Log storage will be handled in createTestRunAction after process completion
|
|
137
|
+
processManager.unregister(testRunId)
|
|
138
|
+
console.log(
|
|
139
|
+
`[TestRunExecutor] Process exited for testRunId: ${testRunId} with code ${code}, unregistered from ProcessManager`,
|
|
140
|
+
)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
return { process, reportPath }
|
|
144
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import winston from 'winston'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { promises as fs } from 'fs'
|
|
4
|
+
|
|
5
|
+
const LOGS_DIR = path.join(process.cwd(), 'src', 'tests', 'reports', 'logs')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Ensures the logs directory exists, creating it if necessary
|
|
9
|
+
*/
|
|
10
|
+
async function ensureLogsDirectory(): Promise<void> {
|
|
11
|
+
try {
|
|
12
|
+
await fs.access(LOGS_DIR)
|
|
13
|
+
} catch {
|
|
14
|
+
// Directory doesn't exist, create it
|
|
15
|
+
await fs.mkdir(LOGS_DIR, { recursive: true })
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a Winston logger instance for a specific test run
|
|
21
|
+
* @param testRunId - The unique test run ID (runId, not id)
|
|
22
|
+
* @returns A configured Winston logger instance
|
|
23
|
+
*/
|
|
24
|
+
export async function createTestRunLogger(testRunId: string): Promise<winston.Logger> {
|
|
25
|
+
// Ensure logs directory exists
|
|
26
|
+
await ensureLogsDirectory()
|
|
27
|
+
|
|
28
|
+
// Create log file path
|
|
29
|
+
const logFilePath = path.join(LOGS_DIR, `${testRunId}.log`)
|
|
30
|
+
|
|
31
|
+
// Define log format
|
|
32
|
+
const logFormat = winston.format.combine(
|
|
33
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
34
|
+
winston.format.errors({ stack: true }),
|
|
35
|
+
winston.format.printf(({ timestamp, level, message, stack }) => {
|
|
36
|
+
if (stack) {
|
|
37
|
+
return `${timestamp} [${level.toUpperCase()}]: ${message}\n${stack}`
|
|
38
|
+
}
|
|
39
|
+
return `${timestamp} [${level.toUpperCase()}]: ${message}`
|
|
40
|
+
}),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// Create logger with console and file transports
|
|
44
|
+
const logger = winston.createLogger({
|
|
45
|
+
level: 'info',
|
|
46
|
+
format: logFormat,
|
|
47
|
+
transports: [
|
|
48
|
+
// Console transport (stdout)
|
|
49
|
+
new winston.transports.Console({
|
|
50
|
+
format: winston.format.combine(
|
|
51
|
+
winston.format.colorize(),
|
|
52
|
+
winston.format.printf(({ timestamp, level, message, stack }) => {
|
|
53
|
+
if (stack) {
|
|
54
|
+
return `${timestamp} [${level}]: ${message}\n${stack}`
|
|
55
|
+
}
|
|
56
|
+
return `${timestamp} [${level}]: ${message}`
|
|
57
|
+
}),
|
|
58
|
+
),
|
|
59
|
+
}),
|
|
60
|
+
// File transport
|
|
61
|
+
new winston.transports.File({
|
|
62
|
+
filename: logFilePath,
|
|
63
|
+
maxsize: 10 * 1024 * 1024, // 10MB
|
|
64
|
+
maxFiles: 5,
|
|
65
|
+
}),
|
|
66
|
+
],
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
return logger
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Closes a Winston logger instance, ensuring all logs are flushed
|
|
74
|
+
* @param logger - The logger instance to close
|
|
75
|
+
*/
|
|
76
|
+
export async function closeLogger(logger: winston.Logger): Promise<void> {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
logger.end((err: Error | null) => {
|
|
79
|
+
if (err) {
|
|
80
|
+
reject(err)
|
|
81
|
+
} else {
|
|
82
|
+
resolve()
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Gets the log file path for a test run
|
|
90
|
+
* @param testRunId - The unique test run ID (runId, not id)
|
|
91
|
+
* @returns The absolute path to the log file
|
|
92
|
+
*/
|
|
93
|
+
export function getLogFilePath(testRunId: string): string {
|
|
94
|
+
return path.join(LOGS_DIR, `${testRunId}.log`)
|
|
95
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { TemplateStepType } from '@prisma/client'
|
|
2
|
+
|
|
3
|
+
export const generateGherkinStep = (
|
|
4
|
+
type: TemplateStepType,
|
|
5
|
+
signature: string,
|
|
6
|
+
parameters: { value: string; order: number }[],
|
|
7
|
+
) => {
|
|
8
|
+
const signatureParts = signature.split(' ')
|
|
9
|
+
const sortedParameters = parameters.sort((a, b) => a.order - b.order)
|
|
10
|
+
|
|
11
|
+
const gherkinStep = signatureParts
|
|
12
|
+
.map(part => {
|
|
13
|
+
// Check if this part is a parameter placeholder
|
|
14
|
+
if (part.startsWith('{') && part.endsWith('}')) {
|
|
15
|
+
const parameter = sortedParameters.shift()
|
|
16
|
+
|
|
17
|
+
// If no parameter or missing value, return the placeholder to indicate incompleteness
|
|
18
|
+
if (!parameter || !parameter.value || parameter.value.trim() === '') {
|
|
19
|
+
return part
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Format the parameter value based on its type
|
|
23
|
+
switch (part) {
|
|
24
|
+
case '{string}':
|
|
25
|
+
return `"${parameter.value}"`
|
|
26
|
+
case '{int}':
|
|
27
|
+
return parameter.value
|
|
28
|
+
case '{boolean}':
|
|
29
|
+
return parameter.value
|
|
30
|
+
default:
|
|
31
|
+
return parameter.value
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return part
|
|
36
|
+
})
|
|
37
|
+
.join(' ')
|
|
38
|
+
|
|
39
|
+
// Prepend the appropriate Gherkin keyword based on type
|
|
40
|
+
const keyword = type === 'ACTION' ? 'When' : 'Then'
|
|
41
|
+
return `${keyword} ${gherkinStep}`
|
|
42
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { TemplateStepIcon } from '@prisma/client'
|
|
2
|
+
import {
|
|
3
|
+
ALargeSmall,
|
|
4
|
+
BugPlay,
|
|
5
|
+
Database,
|
|
6
|
+
Download,
|
|
7
|
+
Globe,
|
|
8
|
+
Keyboard,
|
|
9
|
+
MousePointerClick,
|
|
10
|
+
Save,
|
|
11
|
+
Server,
|
|
12
|
+
Upload,
|
|
13
|
+
Loader,
|
|
14
|
+
CheckCheck,
|
|
15
|
+
LucideIcon,
|
|
16
|
+
} from 'lucide-react'
|
|
17
|
+
import { ReactElement } from 'react'
|
|
18
|
+
|
|
19
|
+
export const KeyToIconTransformer = (key: TemplateStepIcon, className?: string) => {
|
|
20
|
+
switch (key) {
|
|
21
|
+
case 'MOUSE':
|
|
22
|
+
return <MousePointerClick className={className} />
|
|
23
|
+
case 'NAVIGATION':
|
|
24
|
+
return <Globe className={className} />
|
|
25
|
+
case 'INPUT':
|
|
26
|
+
return <Keyboard className={className} />
|
|
27
|
+
case 'DOWNLOAD':
|
|
28
|
+
return <Download className={className} />
|
|
29
|
+
case 'API':
|
|
30
|
+
return <Server className={className} />
|
|
31
|
+
case 'STORE':
|
|
32
|
+
return <Save className={className} />
|
|
33
|
+
case 'FORMAT':
|
|
34
|
+
return <ALargeSmall className={className} />
|
|
35
|
+
case 'DATA':
|
|
36
|
+
return <Database className={className} />
|
|
37
|
+
case 'UPLOAD':
|
|
38
|
+
return <Upload className={className} />
|
|
39
|
+
case 'WAIT':
|
|
40
|
+
return <Loader className={className} />
|
|
41
|
+
case 'VALIDATION':
|
|
42
|
+
return <CheckCheck className={className} />
|
|
43
|
+
case 'DEBUG':
|
|
44
|
+
return <BugPlay className={className} />
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Map of component types to TemplateStepIcon values
|
|
49
|
+
const componentToIconMap = new Map<LucideIcon, TemplateStepIcon>([
|
|
50
|
+
[MousePointerClick, 'MOUSE'],
|
|
51
|
+
[Globe, 'NAVIGATION'],
|
|
52
|
+
[Keyboard, 'INPUT'],
|
|
53
|
+
[Download, 'DOWNLOAD'],
|
|
54
|
+
[Server, 'API'],
|
|
55
|
+
[Save, 'STORE'],
|
|
56
|
+
[ALargeSmall, 'FORMAT'],
|
|
57
|
+
[Database, 'DATA'],
|
|
58
|
+
[Upload, 'UPLOAD'],
|
|
59
|
+
[Loader, 'WAIT'],
|
|
60
|
+
[CheckCheck, 'VALIDATION'],
|
|
61
|
+
[BugPlay, 'DEBUG'],
|
|
62
|
+
])
|
|
63
|
+
|
|
64
|
+
// Map of string values to TemplateStepIcon values
|
|
65
|
+
const stringToIconMap = new Map<string, TemplateStepIcon>([
|
|
66
|
+
['MOUSE', 'MOUSE'],
|
|
67
|
+
['NAVIGATION', 'NAVIGATION'],
|
|
68
|
+
['INPUT', 'INPUT'],
|
|
69
|
+
['DOWNLOAD', 'DOWNLOAD'],
|
|
70
|
+
['API', 'API'],
|
|
71
|
+
['STORE', 'STORE'],
|
|
72
|
+
['FORMAT', 'FORMAT'],
|
|
73
|
+
['DATA', 'DATA'],
|
|
74
|
+
['UPLOAD', 'UPLOAD'],
|
|
75
|
+
['WAIT', 'WAIT'],
|
|
76
|
+
['VALIDATION', 'VALIDATION'],
|
|
77
|
+
['DEBUG', 'DEBUG'],
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
export const IconToKeyTransformer = (icon: React.ReactNode | string): TemplateStepIcon | undefined => {
|
|
81
|
+
// Handle string input
|
|
82
|
+
if (typeof icon === 'string') {
|
|
83
|
+
return stringToIconMap.get(icon)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Handle React component input
|
|
87
|
+
if (!icon || typeof icon !== 'object') return undefined
|
|
88
|
+
|
|
89
|
+
// Get the component type from the React element
|
|
90
|
+
const componentType = (icon as ReactElement)?.type
|
|
91
|
+
if (!componentType) return undefined
|
|
92
|
+
|
|
93
|
+
// Look up the icon in our map
|
|
94
|
+
return componentToIconMap.get(componentType as LucideIcon)
|
|
95
|
+
}
|