create-appraisejs 0.1.6 ā 0.1.8-alpha
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 +45 -45
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/copy-template.d.ts +2 -1
- package/dist/copy-template.d.ts.map +1 -1
- package/dist/copy-template.js +10 -7
- package/dist/copy-template.js.map +1 -1
- package/dist/copy-template.test.js +19 -0
- package/dist/copy-template.test.js.map +1 -1
- package/package.json +69 -67
- package/templates/default/.vscode/settings.json +5 -0
- package/templates/default/appraisejs.config.json +1 -1
- package/templates/default/components.json +24 -24
- package/templates/default/cucumber.mjs +16 -0
- package/templates/default/eslint.config.mjs +15 -15
- package/templates/default/next.config.ts +13 -7
- package/templates/default/package-lock.json +13732 -14321
- package/templates/default/package.json +11 -9
- package/templates/default/postcss.config.mjs +8 -8
- package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
- package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
- package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
- package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
- package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
- package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
- package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
- package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
- package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
- package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
- package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
- package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
- package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
- package/templates/default/prisma/schema.prisma +554 -554
- package/templates/default/scripts/regenerate-features.ts +94 -94
- package/templates/default/scripts/setup-env.ts +19 -19
- package/templates/default/scripts/sync-all.ts +341 -341
- package/templates/default/scripts/sync-appraise-base-template.ts +52 -2
- package/templates/default/scripts/sync-environments.ts +323 -323
- package/templates/default/scripts/sync-locator-groups.ts +413 -413
- package/templates/default/scripts/sync-locators.ts +402 -402
- package/templates/default/scripts/sync-modules.ts +349 -349
- package/templates/default/scripts/sync-tags.ts +292 -292
- package/templates/default/scripts/sync-template-step-groups.ts +399 -399
- package/templates/default/scripts/sync-template-steps.ts +806 -806
- package/templates/default/scripts/sync-test-cases.ts +905 -905
- package/templates/default/scripts/sync-test-suites.ts +411 -411
- package/templates/default/src/actions/conflict/conflict.action.ts +33 -33
- package/templates/default/src/actions/dashboard/dashboard-actions.ts +240 -240
- package/templates/default/src/actions/environments/environment-actions.ts +205 -205
- package/templates/default/src/actions/locator/locator-actions.ts +547 -547
- package/templates/default/src/actions/locator-groups/locator-group-actions.ts +344 -344
- package/templates/default/src/actions/modules/module-actions.ts +133 -133
- package/templates/default/src/actions/reports/report-actions.ts +613 -613
- package/templates/default/src/actions/review/review-actions.ts +147 -147
- package/templates/default/src/actions/tags/tag-actions.ts +104 -104
- package/templates/default/src/actions/template-step/template-step-actions.ts +332 -332
- package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +278 -278
- package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +238 -238
- package/templates/default/src/actions/test-case/test-case-actions.ts +419 -419
- package/templates/default/src/actions/test-run/test-run-actions.ts +1185 -1185
- package/templates/default/src/actions/test-suite/test-suite-actions.ts +253 -253
- package/templates/default/src/actions/user/user-actions.ts +13 -13
- package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
- package/templates/default/src/app/(base)/environments/environment-form.tsx +219 -219
- package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
- package/templates/default/src/app/(base)/environments/environment-table.tsx +24 -24
- package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +46 -46
- package/templates/default/src/app/(base)/environments/page.tsx +59 -59
- package/templates/default/src/app/(base)/layout.tsx +10 -10
- package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -44
- package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -215
- package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -77
- package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -28
- package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -46
- package/templates/default/src/app/(base)/locator-groups/page.tsx +61 -61
- package/templates/default/src/app/(base)/locators/create/page.tsx +38 -38
- package/templates/default/src/app/(base)/locators/locator-form.tsx +163 -163
- package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +73 -90
- package/templates/default/src/app/(base)/locators/locator-table.tsx +28 -28
- package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +45 -45
- package/templates/default/src/app/(base)/locators/page.tsx +65 -65
- package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +66 -66
- package/templates/default/src/app/(base)/modules/create/page.tsx +34 -34
- package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +46 -46
- package/templates/default/src/app/(base)/modules/module-form.tsx +126 -126
- package/templates/default/src/app/(base)/modules/module-table-columns.tsx +85 -85
- package/templates/default/src/app/(base)/modules/module-table.tsx +24 -24
- package/templates/default/src/app/(base)/modules/page.tsx +59 -59
- package/templates/default/src/app/(base)/reports/[id]/page.tsx +517 -517
- package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
- package/templates/default/src/app/(base)/reports/feature-chart.tsx +78 -78
- package/templates/default/src/app/(base)/reports/overview-chart.tsx +46 -46
- package/templates/default/src/app/(base)/reports/page.tsx +98 -98
- package/templates/default/src/app/(base)/reports/report-metric-card.tsx +16 -16
- package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -189
- package/templates/default/src/app/(base)/reports/report-table.tsx +72 -72
- package/templates/default/src/app/(base)/reports/report-view-table-columns.tsx +131 -131
- package/templates/default/src/app/(base)/reports/report-view-table.tsx +82 -82
- package/templates/default/src/app/(base)/reports/test-cases/page.tsx +42 -42
- package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -115
- package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -27
- package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -42
- package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -79
- package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -27
- package/templates/default/src/app/(base)/reports/view-logs-button.tsx +60 -60
- package/templates/default/src/app/(base)/reviews/create/page.tsx +26 -26
- package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +15 -15
- package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +26 -26
- package/templates/default/src/app/(base)/reviews/page.tsx +26 -26
- package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +26 -26
- package/templates/default/src/app/(base)/reviews/review-form.tsx +11 -11
- package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +9 -9
- package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +9 -9
- package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +15 -15
- package/templates/default/src/app/(base)/tags/create/page.tsx +39 -39
- package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -50
- package/templates/default/src/app/(base)/tags/page.tsx +58 -58
- package/templates/default/src/app/(base)/tags/tag-form.tsx +147 -147
- package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -63
- package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -29
- package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -28
- package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +45 -45
- package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -60
- package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +167 -167
- package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -89
- package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +32 -32
- package/templates/default/src/app/(base)/template-steps/create/page.tsx +37 -37
- package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +49 -49
- package/templates/default/src/app/(base)/template-steps/page.tsx +59 -59
- package/templates/default/src/app/(base)/template-steps/paramChip.tsx +213 -213
- package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +384 -384
- package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +158 -158
- package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +24 -24
- package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -56
- package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -89
- package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -58
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +84 -84
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +262 -262
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -76
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -32
- package/templates/default/src/app/(base)/test-cases/create/page.tsx +76 -76
- package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +96 -96
- package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +38 -38
- package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +73 -73
- package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -106
- package/templates/default/src/app/(base)/test-cases/page.tsx +60 -60
- package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +82 -82
- package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +395 -395
- package/templates/default/src/app/(base)/test-cases/test-case-table-columns.tsx +90 -90
- package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +35 -35
- package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +56 -56
- package/templates/default/src/app/(base)/test-runs/create/page.tsx +47 -47
- package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
- package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +508 -512
- package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -229
- package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -127
- package/templates/default/src/app/(base)/test-suites/create/page.tsx +45 -45
- package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +55 -55
- package/templates/default/src/app/(base)/test-suites/page.tsx +82 -82
- package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +269 -269
- package/templates/default/src/app/(base)/test-suites/test-suite-table-columns.tsx +97 -97
- package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +29 -29
- package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -187
- package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +12 -12
- package/templates/default/src/app/(dashboard-components)/data-card.tsx +26 -26
- package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +56 -56
- package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -87
- package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +44 -44
- package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +133 -133
- package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +420 -420
- package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -146
- package/templates/default/src/app/globals.css +147 -147
- package/templates/default/src/app/layout.tsx +171 -171
- package/templates/default/src/app/page.tsx +64 -64
- package/templates/default/src/assets/icons/empty-tube.tsx +23 -23
- package/templates/default/src/assets/icons/tube-plus.tsx +29 -29
- package/templates/default/src/components/base-node.tsx +21 -21
- package/templates/default/src/components/chart/pie-chart.tsx +73 -73
- package/templates/default/src/components/data-extraction/locator-inspector.tsx +460 -460
- package/templates/default/src/components/data-state/empty-state.tsx +40 -40
- package/templates/default/src/components/data-visualization/info-card.tsx +70 -70
- package/templates/default/src/components/data-visualization/info-grid.tsx +22 -22
- package/templates/default/src/components/devtools/providers.tsx +19 -13
- package/templates/default/src/components/diagram/button-edge.tsx +54 -54
- package/templates/default/src/components/diagram/dynamic-parameters.tsx +438 -438
- package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
- package/templates/default/src/components/diagram/flow-diagram.tsx +470 -470
- package/templates/default/src/components/diagram/node-form.tsx +262 -262
- package/templates/default/src/components/diagram/options-header-node.tsx +57 -57
- package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -155
- package/templates/default/src/components/form/error-message.tsx +7 -7
- package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -453
- package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -30
- package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -8
- package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -8
- package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -8
- package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -14
- package/templates/default/src/components/logo.tsx +15 -15
- package/templates/default/src/components/navigation/command-badge.tsx +34 -34
- package/templates/default/src/components/navigation/command-chain-input.tsx +51 -51
- package/templates/default/src/components/navigation/entity-search-command.tsx +116 -116
- package/templates/default/src/components/navigation/nav-card.tsx +31 -31
- package/templates/default/src/components/navigation/nav-command.tsx +508 -508
- package/templates/default/src/components/navigation/nav-link.tsx +60 -60
- package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -112
- package/templates/default/src/components/node-header.tsx +159 -159
- package/templates/default/src/components/reports/test-case-logs-modal.tsx +253 -253
- package/templates/default/src/components/table/table-actions.tsx +172 -172
- package/templates/default/src/components/test-run/download-logs-button.tsx +99 -99
- package/templates/default/src/components/test-run/log-viewer.tsx +445 -445
- package/templates/default/src/components/test-run/test-run-details.tsx +611 -611
- package/templates/default/src/components/test-run/test-run-header.tsx +149 -149
- package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
- package/templates/default/src/components/theme/mode-toggle.tsx +54 -54
- package/templates/default/src/components/theme/theme-provider.tsx +8 -8
- package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -7
- package/templates/default/src/components/typography/page-header.tsx +7 -7
- package/templates/default/src/components/ui/alert-dialog.tsx +106 -106
- package/templates/default/src/components/ui/alert.tsx +43 -43
- package/templates/default/src/components/ui/avatar.tsx +40 -40
- package/templates/default/src/components/ui/badge.tsx +29 -29
- package/templates/default/src/components/ui/button.tsx +47 -47
- package/templates/default/src/components/ui/calendar.tsx +158 -158
- package/templates/default/src/components/ui/card.tsx +43 -43
- package/templates/default/src/components/ui/checkbox.tsx +28 -28
- package/templates/default/src/components/ui/command.tsx +135 -135
- package/templates/default/src/components/ui/data-table-column-header.tsx +61 -61
- package/templates/default/src/components/ui/data-table-pagination.tsx +87 -87
- package/templates/default/src/components/ui/data-table-view-options.tsx +50 -50
- package/templates/default/src/components/ui/data-table.tsx +267 -267
- package/templates/default/src/components/ui/dialog.tsx +97 -97
- package/templates/default/src/components/ui/dropdown-menu.tsx +182 -182
- package/templates/default/src/components/ui/input.tsx +22 -22
- package/templates/default/src/components/ui/kbd.tsx +28 -28
- package/templates/default/src/components/ui/label.tsx +19 -19
- package/templates/default/src/components/ui/loading.tsx +12 -12
- package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -116
- package/templates/default/src/components/ui/multi-select.tsx +142 -142
- package/templates/default/src/components/ui/navigation-menu.tsx +120 -120
- package/templates/default/src/components/ui/popover.tsx +33 -33
- package/templates/default/src/components/ui/progress.tsx +25 -25
- package/templates/default/src/components/ui/radio-group.tsx +44 -44
- package/templates/default/src/components/ui/scroll-area.tsx +40 -40
- package/templates/default/src/components/ui/select.tsx +151 -144
- package/templates/default/src/components/ui/separator.tsx +22 -22
- package/templates/default/src/components/ui/skeleton.tsx +7 -7
- package/templates/default/src/components/ui/table.tsx +76 -76
- package/templates/default/src/components/ui/tabs.tsx +55 -55
- package/templates/default/src/components/ui/textarea.tsx +21 -21
- package/templates/default/src/components/ui/toast.tsx +113 -113
- package/templates/default/src/components/ui/toaster.tsx +26 -26
- package/templates/default/src/components/user-prompt/delete-prompt.tsx +87 -87
- package/templates/default/src/config/db-config.ts +10 -10
- package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -30
- package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -24
- package/templates/default/src/constants/form-opts/locator-form-opts.ts +20 -20
- package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -28
- package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -21
- package/templates/default/src/constants/form-opts/review-form-opts.ts +23 -23
- package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -42
- package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -16
- package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -24
- package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -39
- package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -36
- package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -43
- package/templates/default/src/constants/form-opts/test-run-form-opts.ts +31 -31
- package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -24
- package/templates/default/src/hooks/use-toast.ts +187 -187
- package/templates/default/src/lib/bidirectional-sync.ts +432 -432
- package/templates/default/src/lib/database-sync.ts +531 -531
- package/templates/default/src/lib/environment-file-utils.ts +221 -221
- package/templates/default/src/lib/feature-file-generator.ts +411 -411
- package/templates/default/src/lib/gherkin-parser.ts +259 -259
- package/templates/default/src/lib/locator-group-file-utils.ts +370 -370
- package/templates/default/src/lib/metrics/metric-calculator.ts +613 -613
- package/templates/default/src/lib/module-hierarchy-builder.ts +205 -205
- package/templates/default/src/lib/path-helpers/module-path.ts +71 -71
- package/templates/default/src/lib/test-case-utils.ts +6 -6
- package/templates/default/src/lib/test-run/log-formatter.ts +83 -83
- package/templates/default/src/lib/test-run/process-manager.ts +191 -191
- package/templates/default/src/lib/test-run/report-parser.ts +316 -316
- package/templates/default/src/lib/test-run/test-run-executor.ts +144 -144
- package/templates/default/src/lib/test-run/winston-logger.ts +95 -95
- package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -42
- package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -95
- package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -160
- package/templates/default/src/lib/utils/node-param-validation.ts +81 -81
- package/templates/default/src/lib/utils/template-step-file-generator.ts +167 -167
- package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +723 -723
- package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -166
- package/templates/default/src/lib/utils.ts +31 -31
- package/templates/default/src/tests/config/executor/world.ts +41 -41
- package/templates/default/src/tests/executor.ts +80 -80
- package/templates/default/src/tests/hooks/hooks.ts +99 -99
- package/templates/default/src/tests/mapping/locator-map.json +1 -1
- package/templates/default/src/tests/steps/actions/click.step.ts +62 -62
- package/templates/default/src/tests/steps/actions/navigation.step.ts +73 -72
- package/templates/default/src/tests/steps/validations/active_state_assertion.step.ts +34 -34
- package/templates/default/src/tests/steps/validations/navigation_assertion.step.ts +24 -23
- package/templates/default/src/tests/steps/validations/text_assertion.step.ts +111 -111
- package/templates/default/src/tests/steps/validations/visibility_assertion.step.ts +30 -30
- package/templates/default/src/tests/support/parameter-types.ts +12 -12
- package/templates/default/src/tests/utils/cache.util.ts +260 -260
- package/templates/default/src/tests/utils/cli.util.ts +177 -177
- package/templates/default/src/tests/utils/environment.util.ts +65 -65
- package/templates/default/src/tests/utils/locator.util.ts +248 -248
- package/templates/default/src/tests/utils/random-data.util.ts +44 -44
- package/templates/default/src/tests/utils/spawner.util.ts +617 -617
- package/templates/default/src/types/diagram/diagram.ts +34 -34
- package/templates/default/src/types/diagram/template-step.ts +11 -11
- package/templates/default/src/types/executor/browser.type.ts +1 -1
- package/templates/default/src/types/form/actionHandler.ts +6 -6
- package/templates/default/src/types/locator/locator.type.ts +11 -11
- package/templates/default/src/types/step/step.type.ts +1 -1
- package/templates/default/src/types/table/data-table.ts +6 -6
- package/templates/default/tailwind.config.ts +62 -62
- package/templates/default/.env +0 -2
- package/templates/default/next-env.d.ts +0 -6
- package/templates/default/prisma/prisma/dev.db +0 -0
- package/templates/default/src/tests/config/environments/environments.json +0 -14
|
@@ -1,349 +1,349 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Script to synchronize module hierarchy from filesystem to database
|
|
5
|
-
* Scans locators and features directories to ensure all modules exist in DB
|
|
6
|
-
* Filesystem is the source of truth - modules in DB but not in FS will be deleted
|
|
7
|
-
* Run this after merging changes to ensure module sync
|
|
8
|
-
*
|
|
9
|
-
* Usage: npx tsx scripts/sync-modules.ts
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { buildModuleHierarchy, findModuleByPath, getAllModulesWithPaths } from '../src/lib/module-hierarchy-builder'
|
|
13
|
-
import { join } from 'path'
|
|
14
|
-
import { glob } from 'glob'
|
|
15
|
-
import prisma from '../src/config/db-config'
|
|
16
|
-
|
|
17
|
-
interface SyncResult {
|
|
18
|
-
modulesScanned: number
|
|
19
|
-
modulesExisting: number
|
|
20
|
-
modulesCreated: number
|
|
21
|
-
modulesDeleted: number
|
|
22
|
-
errors: string[]
|
|
23
|
-
createdModules: string[]
|
|
24
|
-
existingModules: string[]
|
|
25
|
-
deletedModules: string[]
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Scans locator directories and extracts module paths
|
|
30
|
-
*/
|
|
31
|
-
async function scanLocatorDirectories(baseDir: string): Promise<string[]> {
|
|
32
|
-
const modulePaths = new Set<string>()
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
// Get all JSON files in locators directory
|
|
36
|
-
const pattern = 'src/tests/locators/**/*.json'
|
|
37
|
-
const files = await glob(pattern, {
|
|
38
|
-
cwd: baseDir,
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
for (const file of files) {
|
|
42
|
-
const filePath = join(baseDir, file)
|
|
43
|
-
const modulePath = extractModulePathFromLocatorFile(filePath, baseDir)
|
|
44
|
-
if (modulePath) {
|
|
45
|
-
modulePaths.add(modulePath)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} catch (error) {
|
|
49
|
-
console.error('Error scanning locator directories:', error)
|
|
50
|
-
throw error
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return Array.from(modulePaths)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Scans feature directories and extracts module paths
|
|
58
|
-
*/
|
|
59
|
-
async function scanFeatureDirectories(baseDir: string): Promise<string[]> {
|
|
60
|
-
const modulePaths = new Set<string>()
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
// Get all feature files
|
|
64
|
-
const pattern = 'src/tests/features/**/*.feature'
|
|
65
|
-
const files = await glob(pattern, {
|
|
66
|
-
cwd: baseDir,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
for (const file of files) {
|
|
70
|
-
const filePath = join(baseDir, file)
|
|
71
|
-
const modulePath = extractModulePathFromFeatureFile(filePath, baseDir)
|
|
72
|
-
if (modulePath) {
|
|
73
|
-
modulePaths.add(modulePath)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
} catch (error) {
|
|
77
|
-
console.error('Error scanning feature directories:', error)
|
|
78
|
-
throw error
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return Array.from(modulePaths)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Extracts module path from locator file path
|
|
86
|
-
* Example: src/tests/locators/home/home.json -> /home
|
|
87
|
-
*/
|
|
88
|
-
function extractModulePathFromLocatorFile(filePath: string, baseDir: string): string {
|
|
89
|
-
const testsDir = join(baseDir, 'src', 'tests')
|
|
90
|
-
const relativePath = filePath.replace(testsDir, '').replace(/\\/g, '/')
|
|
91
|
-
const pathParts = relativePath.split('/').filter(p => p && p !== 'locators')
|
|
92
|
-
const moduleParts = pathParts.slice(0, -1) // Remove filename
|
|
93
|
-
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Extracts module path from feature file path
|
|
98
|
-
* Example: src/tests/features/login/demo.feature -> /login
|
|
99
|
-
*/
|
|
100
|
-
function extractModulePathFromFeatureFile(filePath: string, baseDir: string): string {
|
|
101
|
-
const featuresBaseDir = join(baseDir, 'src', 'tests', 'features')
|
|
102
|
-
const relativePath = filePath.replace(featuresBaseDir, '').replace(/\\/g, '/')
|
|
103
|
-
const pathParts = relativePath.split('/').filter(part => part && part !== '')
|
|
104
|
-
const moduleParts = pathParts.slice(0, -1) // Remove filename
|
|
105
|
-
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Builds a module tree from discovered paths
|
|
110
|
-
* Returns a map of module paths to their parent paths
|
|
111
|
-
*/
|
|
112
|
-
function buildModuleTree(modulePaths: string[]): Map<string, string | null> {
|
|
113
|
-
const tree = new Map<string, string | null>()
|
|
114
|
-
|
|
115
|
-
// Add all paths and their parent paths
|
|
116
|
-
for (const modulePath of modulePaths) {
|
|
117
|
-
if (modulePath === '/') {
|
|
118
|
-
tree.set('/', null)
|
|
119
|
-
continue
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const pathParts = modulePath.split('/').filter(p => p)
|
|
123
|
-
let currentPath = ''
|
|
124
|
-
|
|
125
|
-
for (let i = 0; i < pathParts.length; i++) {
|
|
126
|
-
currentPath += '/' + pathParts[i]
|
|
127
|
-
|
|
128
|
-
if (i === 0) {
|
|
129
|
-
tree.set(currentPath, null)
|
|
130
|
-
} else {
|
|
131
|
-
const parentPath = '/' + pathParts.slice(0, i).join('/')
|
|
132
|
-
tree.set(currentPath, parentPath)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return tree
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Syncs modules to database (creates missing modules)
|
|
142
|
-
*/
|
|
143
|
-
async function syncModulesToDatabase(moduleTree: Map<string, string | null>): Promise<SyncResult> {
|
|
144
|
-
const result: SyncResult = {
|
|
145
|
-
modulesScanned: moduleTree.size,
|
|
146
|
-
modulesExisting: 0,
|
|
147
|
-
modulesCreated: 0,
|
|
148
|
-
modulesDeleted: 0,
|
|
149
|
-
errors: [],
|
|
150
|
-
createdModules: [],
|
|
151
|
-
existingModules: [],
|
|
152
|
-
deletedModules: [],
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Sort paths by depth (shallowest first) to ensure parents are created before children
|
|
156
|
-
const sortedPaths = Array.from(moduleTree.keys()).sort((a, b) => {
|
|
157
|
-
const depthA = a.split('/').filter(p => p).length
|
|
158
|
-
const depthB = b.split('/').filter(p => p).length
|
|
159
|
-
return depthA - depthB
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
for (const modulePath of sortedPaths) {
|
|
163
|
-
try {
|
|
164
|
-
// Check if module already exists
|
|
165
|
-
const existingModuleId = await findModuleByPath(modulePath)
|
|
166
|
-
|
|
167
|
-
if (existingModuleId) {
|
|
168
|
-
result.modulesExisting++
|
|
169
|
-
result.existingModules.push(modulePath)
|
|
170
|
-
console.log(` ā Module '${modulePath}' already exists`)
|
|
171
|
-
} else {
|
|
172
|
-
// Build the hierarchy for this path (which will create it if needed)
|
|
173
|
-
await buildModuleHierarchy(modulePath)
|
|
174
|
-
result.modulesCreated++
|
|
175
|
-
result.createdModules.push(modulePath)
|
|
176
|
-
console.log(` ā Created module '${modulePath}'`)
|
|
177
|
-
}
|
|
178
|
-
} catch (error) {
|
|
179
|
-
const errorMsg = `Error syncing module '${modulePath}': ${error}`
|
|
180
|
-
result.errors.push(errorMsg)
|
|
181
|
-
console.error(` ā ${errorMsg}`)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return result
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Deletes orphaned modules (modules in DB but not in FS)
|
|
190
|
-
*/
|
|
191
|
-
async function deleteOrphanedModules(fsModulePaths: Set<string>, result: SyncResult): Promise<void> {
|
|
192
|
-
console.log('\nš Checking for orphaned modules (not in filesystem)...')
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
// Get all modules from database with their paths
|
|
196
|
-
const dbModules = await getAllModulesWithPaths()
|
|
197
|
-
|
|
198
|
-
// Sort modules by reverse depth (deepest first) to avoid foreign key constraint issues
|
|
199
|
-
const sortedDbModules = dbModules.sort((a, b) => {
|
|
200
|
-
const depthA = a.path.split('/').filter(p => p).length
|
|
201
|
-
const depthB = b.path.split('/').filter(p => p).length
|
|
202
|
-
return depthB - depthA // Reverse order
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
for (const dbModule of sortedDbModules) {
|
|
206
|
-
// Skip the default root module (preserve it)
|
|
207
|
-
if (dbModule.name === 'root' && dbModule.parentId === null) {
|
|
208
|
-
continue
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Check if module path exists in FS
|
|
212
|
-
if (!fsModulePaths.has(dbModule.path)) {
|
|
213
|
-
try {
|
|
214
|
-
// Check if module has dependencies (for logging)
|
|
215
|
-
const moduleWithDeps = await prisma.module.findUnique({
|
|
216
|
-
where: { id: dbModule.id },
|
|
217
|
-
include: {
|
|
218
|
-
locatorGroups: { select: { id: true } },
|
|
219
|
-
testSuites: { select: { id: true } },
|
|
220
|
-
},
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
if (moduleWithDeps) {
|
|
224
|
-
const hasLocatorGroups = moduleWithDeps.locatorGroups.length > 0
|
|
225
|
-
const hasTestSuites = moduleWithDeps.testSuites.length > 0
|
|
226
|
-
|
|
227
|
-
if (hasLocatorGroups || hasTestSuites) {
|
|
228
|
-
const deps = []
|
|
229
|
-
if (hasLocatorGroups) deps.push(`${moduleWithDeps.locatorGroups.length} locator group(s)`)
|
|
230
|
-
if (hasTestSuites) deps.push(`${moduleWithDeps.testSuites.length} test suite(s)`)
|
|
231
|
-
console.log(
|
|
232
|
-
` ā ļø Module '${dbModule.path}' has dependencies (${deps.join(', ')}) - will be cascade deleted`,
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Delete the module (Prisma cascade will handle children, locatorGroups, and testSuites)
|
|
237
|
-
await prisma.module.delete({
|
|
238
|
-
where: { id: dbModule.id },
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
result.modulesDeleted++
|
|
242
|
-
result.deletedModules.push(dbModule.path)
|
|
243
|
-
console.log(` šļø Deleted module '${dbModule.path}' (not in filesystem)`)
|
|
244
|
-
}
|
|
245
|
-
} catch (error) {
|
|
246
|
-
const errorMsg = `Error deleting module '${dbModule.path}': ${error}`
|
|
247
|
-
result.errors.push(errorMsg)
|
|
248
|
-
console.error(` ā ${errorMsg}`)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} catch (error) {
|
|
253
|
-
const errorMsg = `Error checking for orphaned modules: ${error}`
|
|
254
|
-
result.errors.push(errorMsg)
|
|
255
|
-
console.error(` ā ${errorMsg}`)
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Generates and displays sync summary
|
|
261
|
-
*/
|
|
262
|
-
function generateSummary(result: SyncResult): void {
|
|
263
|
-
console.log('\nš Sync Summary:')
|
|
264
|
-
console.log(` š Modules scanned: ${result.modulesScanned}`)
|
|
265
|
-
console.log(` ā
Modules existing: ${result.modulesExisting}`)
|
|
266
|
-
console.log(` ā Modules created: ${result.modulesCreated}`)
|
|
267
|
-
console.log(` šļø Modules deleted: ${result.modulesDeleted}`)
|
|
268
|
-
console.log(` ā Errors: ${result.errors.length}`)
|
|
269
|
-
|
|
270
|
-
if (result.createdModules.length > 0) {
|
|
271
|
-
console.log('\n Created modules:')
|
|
272
|
-
result.createdModules.forEach((path, index) => {
|
|
273
|
-
console.log(` ${index + 1}. ${path}`)
|
|
274
|
-
})
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (result.deletedModules.length > 0) {
|
|
278
|
-
console.log('\n Deleted modules:')
|
|
279
|
-
result.deletedModules.forEach((path, index) => {
|
|
280
|
-
console.log(` ${index + 1}. ${path}`)
|
|
281
|
-
})
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (result.errors.length > 0) {
|
|
285
|
-
console.log('\n Errors:')
|
|
286
|
-
result.errors.forEach((error, index) => {
|
|
287
|
-
console.log(` ${index + 1}. ${error}`)
|
|
288
|
-
})
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Main function
|
|
294
|
-
*/
|
|
295
|
-
async function main() {
|
|
296
|
-
try {
|
|
297
|
-
console.log('š Starting modules sync...')
|
|
298
|
-
console.log('This will scan filesystem directories and sync module hierarchy to database.')
|
|
299
|
-
console.log('Filesystem is the source of truth - modules in DB but not in FS will be deleted.\n')
|
|
300
|
-
|
|
301
|
-
const baseDir = process.cwd()
|
|
302
|
-
|
|
303
|
-
// Scan directories
|
|
304
|
-
console.log('š Scanning src/tests/locators...')
|
|
305
|
-
const locatorModulePaths = await scanLocatorDirectories(baseDir)
|
|
306
|
-
console.log(` Found ${locatorModulePaths.length} module path(s): ${locatorModulePaths.join(', ') || 'none'}`)
|
|
307
|
-
|
|
308
|
-
console.log('\nš Scanning src/tests/features...')
|
|
309
|
-
const featureModulePaths = await scanFeatureDirectories(baseDir)
|
|
310
|
-
console.log(` Found ${featureModulePaths.length} module path(s): ${featureModulePaths.join(', ') || 'none'}`)
|
|
311
|
-
|
|
312
|
-
// Combine and deduplicate
|
|
313
|
-
const allModulePaths = Array.from(new Set([...locatorModulePaths, ...featureModulePaths]))
|
|
314
|
-
console.log(`\nš Building module hierarchy from ${allModulePaths.length} unique module path(s)...`)
|
|
315
|
-
|
|
316
|
-
// Build module tree
|
|
317
|
-
const moduleTree = buildModuleTree(allModulePaths)
|
|
318
|
-
|
|
319
|
-
// Sync to database (create missing modules)
|
|
320
|
-
console.log('\nā
Syncing modules to database...')
|
|
321
|
-
const result = await syncModulesToDatabase(moduleTree)
|
|
322
|
-
|
|
323
|
-
// Delete orphaned modules (modules in DB but not in FS)
|
|
324
|
-
const fsModulePathsSet = new Set(allModulePaths)
|
|
325
|
-
// Also add all parent paths from the tree
|
|
326
|
-
for (const path of moduleTree.keys()) {
|
|
327
|
-
fsModulePathsSet.add(path)
|
|
328
|
-
}
|
|
329
|
-
await deleteOrphanedModules(fsModulePathsSet, result)
|
|
330
|
-
|
|
331
|
-
// Generate summary
|
|
332
|
-
generateSummary(result)
|
|
333
|
-
|
|
334
|
-
if (result.errors.length === 0) {
|
|
335
|
-
console.log('\nā
Sync completed successfully!')
|
|
336
|
-
} else {
|
|
337
|
-
console.log('\nā ļø Sync completed with errors. Please review the errors above.')
|
|
338
|
-
process.exit(1)
|
|
339
|
-
}
|
|
340
|
-
} catch (error) {
|
|
341
|
-
console.error('\nā Error during sync:', error)
|
|
342
|
-
process.exit(1)
|
|
343
|
-
} finally {
|
|
344
|
-
await prisma.$disconnect()
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
main()
|
|
349
|
-
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Script to synchronize module hierarchy from filesystem to database
|
|
5
|
+
* Scans locators and features directories to ensure all modules exist in DB
|
|
6
|
+
* Filesystem is the source of truth - modules in DB but not in FS will be deleted
|
|
7
|
+
* Run this after merging changes to ensure module sync
|
|
8
|
+
*
|
|
9
|
+
* Usage: npx tsx scripts/sync-modules.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { buildModuleHierarchy, findModuleByPath, getAllModulesWithPaths } from '../src/lib/module-hierarchy-builder'
|
|
13
|
+
import { join } from 'path'
|
|
14
|
+
import { glob } from 'glob'
|
|
15
|
+
import prisma from '../src/config/db-config'
|
|
16
|
+
|
|
17
|
+
interface SyncResult {
|
|
18
|
+
modulesScanned: number
|
|
19
|
+
modulesExisting: number
|
|
20
|
+
modulesCreated: number
|
|
21
|
+
modulesDeleted: number
|
|
22
|
+
errors: string[]
|
|
23
|
+
createdModules: string[]
|
|
24
|
+
existingModules: string[]
|
|
25
|
+
deletedModules: string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Scans locator directories and extracts module paths
|
|
30
|
+
*/
|
|
31
|
+
async function scanLocatorDirectories(baseDir: string): Promise<string[]> {
|
|
32
|
+
const modulePaths = new Set<string>()
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// Get all JSON files in locators directory
|
|
36
|
+
const pattern = 'src/tests/locators/**/*.json'
|
|
37
|
+
const files = await glob(pattern, {
|
|
38
|
+
cwd: baseDir,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
const filePath = join(baseDir, file)
|
|
43
|
+
const modulePath = extractModulePathFromLocatorFile(filePath, baseDir)
|
|
44
|
+
if (modulePath) {
|
|
45
|
+
modulePaths.add(modulePath)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Error scanning locator directories:', error)
|
|
50
|
+
throw error
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return Array.from(modulePaths)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Scans feature directories and extracts module paths
|
|
58
|
+
*/
|
|
59
|
+
async function scanFeatureDirectories(baseDir: string): Promise<string[]> {
|
|
60
|
+
const modulePaths = new Set<string>()
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Get all feature files
|
|
64
|
+
const pattern = 'src/tests/features/**/*.feature'
|
|
65
|
+
const files = await glob(pattern, {
|
|
66
|
+
cwd: baseDir,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
for (const file of files) {
|
|
70
|
+
const filePath = join(baseDir, file)
|
|
71
|
+
const modulePath = extractModulePathFromFeatureFile(filePath, baseDir)
|
|
72
|
+
if (modulePath) {
|
|
73
|
+
modulePaths.add(modulePath)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Error scanning feature directories:', error)
|
|
78
|
+
throw error
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return Array.from(modulePaths)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Extracts module path from locator file path
|
|
86
|
+
* Example: src/tests/locators/home/home.json -> /home
|
|
87
|
+
*/
|
|
88
|
+
function extractModulePathFromLocatorFile(filePath: string, baseDir: string): string {
|
|
89
|
+
const testsDir = join(baseDir, 'src', 'tests')
|
|
90
|
+
const relativePath = filePath.replace(testsDir, '').replace(/\\/g, '/')
|
|
91
|
+
const pathParts = relativePath.split('/').filter(p => p && p !== 'locators')
|
|
92
|
+
const moduleParts = pathParts.slice(0, -1) // Remove filename
|
|
93
|
+
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Extracts module path from feature file path
|
|
98
|
+
* Example: src/tests/features/login/demo.feature -> /login
|
|
99
|
+
*/
|
|
100
|
+
function extractModulePathFromFeatureFile(filePath: string, baseDir: string): string {
|
|
101
|
+
const featuresBaseDir = join(baseDir, 'src', 'tests', 'features')
|
|
102
|
+
const relativePath = filePath.replace(featuresBaseDir, '').replace(/\\/g, '/')
|
|
103
|
+
const pathParts = relativePath.split('/').filter(part => part && part !== '')
|
|
104
|
+
const moduleParts = pathParts.slice(0, -1) // Remove filename
|
|
105
|
+
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Builds a module tree from discovered paths
|
|
110
|
+
* Returns a map of module paths to their parent paths
|
|
111
|
+
*/
|
|
112
|
+
function buildModuleTree(modulePaths: string[]): Map<string, string | null> {
|
|
113
|
+
const tree = new Map<string, string | null>()
|
|
114
|
+
|
|
115
|
+
// Add all paths and their parent paths
|
|
116
|
+
for (const modulePath of modulePaths) {
|
|
117
|
+
if (modulePath === '/') {
|
|
118
|
+
tree.set('/', null)
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const pathParts = modulePath.split('/').filter(p => p)
|
|
123
|
+
let currentPath = ''
|
|
124
|
+
|
|
125
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
126
|
+
currentPath += '/' + pathParts[i]
|
|
127
|
+
|
|
128
|
+
if (i === 0) {
|
|
129
|
+
tree.set(currentPath, null)
|
|
130
|
+
} else {
|
|
131
|
+
const parentPath = '/' + pathParts.slice(0, i).join('/')
|
|
132
|
+
tree.set(currentPath, parentPath)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return tree
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Syncs modules to database (creates missing modules)
|
|
142
|
+
*/
|
|
143
|
+
async function syncModulesToDatabase(moduleTree: Map<string, string | null>): Promise<SyncResult> {
|
|
144
|
+
const result: SyncResult = {
|
|
145
|
+
modulesScanned: moduleTree.size,
|
|
146
|
+
modulesExisting: 0,
|
|
147
|
+
modulesCreated: 0,
|
|
148
|
+
modulesDeleted: 0,
|
|
149
|
+
errors: [],
|
|
150
|
+
createdModules: [],
|
|
151
|
+
existingModules: [],
|
|
152
|
+
deletedModules: [],
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Sort paths by depth (shallowest first) to ensure parents are created before children
|
|
156
|
+
const sortedPaths = Array.from(moduleTree.keys()).sort((a, b) => {
|
|
157
|
+
const depthA = a.split('/').filter(p => p).length
|
|
158
|
+
const depthB = b.split('/').filter(p => p).length
|
|
159
|
+
return depthA - depthB
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
for (const modulePath of sortedPaths) {
|
|
163
|
+
try {
|
|
164
|
+
// Check if module already exists
|
|
165
|
+
const existingModuleId = await findModuleByPath(modulePath)
|
|
166
|
+
|
|
167
|
+
if (existingModuleId) {
|
|
168
|
+
result.modulesExisting++
|
|
169
|
+
result.existingModules.push(modulePath)
|
|
170
|
+
console.log(` ā Module '${modulePath}' already exists`)
|
|
171
|
+
} else {
|
|
172
|
+
// Build the hierarchy for this path (which will create it if needed)
|
|
173
|
+
await buildModuleHierarchy(modulePath)
|
|
174
|
+
result.modulesCreated++
|
|
175
|
+
result.createdModules.push(modulePath)
|
|
176
|
+
console.log(` ā Created module '${modulePath}'`)
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
const errorMsg = `Error syncing module '${modulePath}': ${error}`
|
|
180
|
+
result.errors.push(errorMsg)
|
|
181
|
+
console.error(` ā ${errorMsg}`)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return result
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Deletes orphaned modules (modules in DB but not in FS)
|
|
190
|
+
*/
|
|
191
|
+
async function deleteOrphanedModules(fsModulePaths: Set<string>, result: SyncResult): Promise<void> {
|
|
192
|
+
console.log('\nš Checking for orphaned modules (not in filesystem)...')
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// Get all modules from database with their paths
|
|
196
|
+
const dbModules = await getAllModulesWithPaths()
|
|
197
|
+
|
|
198
|
+
// Sort modules by reverse depth (deepest first) to avoid foreign key constraint issues
|
|
199
|
+
const sortedDbModules = dbModules.sort((a, b) => {
|
|
200
|
+
const depthA = a.path.split('/').filter(p => p).length
|
|
201
|
+
const depthB = b.path.split('/').filter(p => p).length
|
|
202
|
+
return depthB - depthA // Reverse order
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
for (const dbModule of sortedDbModules) {
|
|
206
|
+
// Skip the default root module (preserve it)
|
|
207
|
+
if (dbModule.name === 'root' && dbModule.parentId === null) {
|
|
208
|
+
continue
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check if module path exists in FS
|
|
212
|
+
if (!fsModulePaths.has(dbModule.path)) {
|
|
213
|
+
try {
|
|
214
|
+
// Check if module has dependencies (for logging)
|
|
215
|
+
const moduleWithDeps = await prisma.module.findUnique({
|
|
216
|
+
where: { id: dbModule.id },
|
|
217
|
+
include: {
|
|
218
|
+
locatorGroups: { select: { id: true } },
|
|
219
|
+
testSuites: { select: { id: true } },
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
if (moduleWithDeps) {
|
|
224
|
+
const hasLocatorGroups = moduleWithDeps.locatorGroups.length > 0
|
|
225
|
+
const hasTestSuites = moduleWithDeps.testSuites.length > 0
|
|
226
|
+
|
|
227
|
+
if (hasLocatorGroups || hasTestSuites) {
|
|
228
|
+
const deps = []
|
|
229
|
+
if (hasLocatorGroups) deps.push(`${moduleWithDeps.locatorGroups.length} locator group(s)`)
|
|
230
|
+
if (hasTestSuites) deps.push(`${moduleWithDeps.testSuites.length} test suite(s)`)
|
|
231
|
+
console.log(
|
|
232
|
+
` ā ļø Module '${dbModule.path}' has dependencies (${deps.join(', ')}) - will be cascade deleted`,
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Delete the module (Prisma cascade will handle children, locatorGroups, and testSuites)
|
|
237
|
+
await prisma.module.delete({
|
|
238
|
+
where: { id: dbModule.id },
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
result.modulesDeleted++
|
|
242
|
+
result.deletedModules.push(dbModule.path)
|
|
243
|
+
console.log(` šļø Deleted module '${dbModule.path}' (not in filesystem)`)
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
const errorMsg = `Error deleting module '${dbModule.path}': ${error}`
|
|
247
|
+
result.errors.push(errorMsg)
|
|
248
|
+
console.error(` ā ${errorMsg}`)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} catch (error) {
|
|
253
|
+
const errorMsg = `Error checking for orphaned modules: ${error}`
|
|
254
|
+
result.errors.push(errorMsg)
|
|
255
|
+
console.error(` ā ${errorMsg}`)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Generates and displays sync summary
|
|
261
|
+
*/
|
|
262
|
+
function generateSummary(result: SyncResult): void {
|
|
263
|
+
console.log('\nš Sync Summary:')
|
|
264
|
+
console.log(` š Modules scanned: ${result.modulesScanned}`)
|
|
265
|
+
console.log(` ā
Modules existing: ${result.modulesExisting}`)
|
|
266
|
+
console.log(` ā Modules created: ${result.modulesCreated}`)
|
|
267
|
+
console.log(` šļø Modules deleted: ${result.modulesDeleted}`)
|
|
268
|
+
console.log(` ā Errors: ${result.errors.length}`)
|
|
269
|
+
|
|
270
|
+
if (result.createdModules.length > 0) {
|
|
271
|
+
console.log('\n Created modules:')
|
|
272
|
+
result.createdModules.forEach((path, index) => {
|
|
273
|
+
console.log(` ${index + 1}. ${path}`)
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (result.deletedModules.length > 0) {
|
|
278
|
+
console.log('\n Deleted modules:')
|
|
279
|
+
result.deletedModules.forEach((path, index) => {
|
|
280
|
+
console.log(` ${index + 1}. ${path}`)
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (result.errors.length > 0) {
|
|
285
|
+
console.log('\n Errors:')
|
|
286
|
+
result.errors.forEach((error, index) => {
|
|
287
|
+
console.log(` ${index + 1}. ${error}`)
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Main function
|
|
294
|
+
*/
|
|
295
|
+
async function main() {
|
|
296
|
+
try {
|
|
297
|
+
console.log('š Starting modules sync...')
|
|
298
|
+
console.log('This will scan filesystem directories and sync module hierarchy to database.')
|
|
299
|
+
console.log('Filesystem is the source of truth - modules in DB but not in FS will be deleted.\n')
|
|
300
|
+
|
|
301
|
+
const baseDir = process.cwd()
|
|
302
|
+
|
|
303
|
+
// Scan directories
|
|
304
|
+
console.log('š Scanning src/tests/locators...')
|
|
305
|
+
const locatorModulePaths = await scanLocatorDirectories(baseDir)
|
|
306
|
+
console.log(` Found ${locatorModulePaths.length} module path(s): ${locatorModulePaths.join(', ') || 'none'}`)
|
|
307
|
+
|
|
308
|
+
console.log('\nš Scanning src/tests/features...')
|
|
309
|
+
const featureModulePaths = await scanFeatureDirectories(baseDir)
|
|
310
|
+
console.log(` Found ${featureModulePaths.length} module path(s): ${featureModulePaths.join(', ') || 'none'}`)
|
|
311
|
+
|
|
312
|
+
// Combine and deduplicate
|
|
313
|
+
const allModulePaths = Array.from(new Set([...locatorModulePaths, ...featureModulePaths]))
|
|
314
|
+
console.log(`\nš Building module hierarchy from ${allModulePaths.length} unique module path(s)...`)
|
|
315
|
+
|
|
316
|
+
// Build module tree
|
|
317
|
+
const moduleTree = buildModuleTree(allModulePaths)
|
|
318
|
+
|
|
319
|
+
// Sync to database (create missing modules)
|
|
320
|
+
console.log('\nā
Syncing modules to database...')
|
|
321
|
+
const result = await syncModulesToDatabase(moduleTree)
|
|
322
|
+
|
|
323
|
+
// Delete orphaned modules (modules in DB but not in FS)
|
|
324
|
+
const fsModulePathsSet = new Set(allModulePaths)
|
|
325
|
+
// Also add all parent paths from the tree
|
|
326
|
+
for (const path of moduleTree.keys()) {
|
|
327
|
+
fsModulePathsSet.add(path)
|
|
328
|
+
}
|
|
329
|
+
await deleteOrphanedModules(fsModulePathsSet, result)
|
|
330
|
+
|
|
331
|
+
// Generate summary
|
|
332
|
+
generateSummary(result)
|
|
333
|
+
|
|
334
|
+
if (result.errors.length === 0) {
|
|
335
|
+
console.log('\nā
Sync completed successfully!')
|
|
336
|
+
} else {
|
|
337
|
+
console.log('\nā ļø Sync completed with errors. Please review the errors above.')
|
|
338
|
+
process.exit(1)
|
|
339
|
+
}
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error('\nā Error during sync:', error)
|
|
342
|
+
process.exit(1)
|
|
343
|
+
} finally {
|
|
344
|
+
await prisma.$disconnect()
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
main()
|
|
349
|
+
|