create-appraisejs 0.2.0-alpha.6 ā 0.3.0-alpha.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 +12 -1
- package/package.json +1 -1
- package/templates/default/.appraise-template-meta.json +2 -2
- package/templates/default/.env.example +2 -2
- package/templates/default/README.md +57 -53
- package/templates/default/automation/steps/actions/click.step.ts +58 -58
- package/templates/default/automation/steps/actions/hover.step.ts +27 -27
- package/templates/default/automation/steps/actions/navigation.step.ts +70 -70
- package/templates/default/automation/steps/actions/random_data.step.ts +142 -142
- package/templates/default/automation/steps/actions/store.step.ts +86 -86
- package/templates/default/automation/steps/actions/wait.step.ts +110 -90
- package/templates/default/automation/steps/validations/active_state_assertion.step.ts +30 -30
- package/templates/default/automation/steps/validations/navigation_assertion.step.ts +22 -22
- package/templates/default/automation/steps/validations/text_assertion.step.ts +107 -107
- package/templates/default/automation/steps/validations/visibility_assertion.step.ts +26 -26
- package/templates/default/components.json +24 -24
- package/templates/default/cucumber.mjs +16 -16
- package/templates/default/eslint.config.mjs +20 -16
- package/templates/default/next-env.d.ts +6 -6
- package/templates/default/next.config.ts +20 -11
- package/templates/default/package-lock.json +1775 -74
- package/templates/default/package.json +8 -1
- package/templates/default/packages/cucumber-runtime/package.json +13 -13
- package/templates/default/packages/cucumber-runtime/src/cache.util.ts +93 -93
- package/templates/default/packages/cucumber-runtime/src/cli.ts +68 -68
- package/templates/default/packages/cucumber-runtime/src/environment.util.ts +21 -21
- package/templates/default/packages/cucumber-runtime/src/executor.ts +32 -32
- package/templates/default/packages/cucumber-runtime/src/index.ts +17 -17
- package/templates/default/packages/cucumber-runtime/src/locator.util.ts +234 -234
- package/templates/default/packages/cucumber-runtime/src/parameter-types.ts +7 -7
- package/templates/default/packages/cucumber-runtime/src/random-data.util.ts +35 -35
- package/templates/default/packages/cucumber-runtime/src/types.ts +13 -13
- package/templates/default/packages/cucumber-runtime/src/world.ts +44 -44
- package/templates/default/packages/cucumber-runtime/tsconfig.json +11 -11
- package/templates/default/postcss.config.mjs +8 -8
- package/templates/default/prisma/dev.db +0 -0
- 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/migrations/20260313093000_add_report_step_screenshot_path/migration.sql +1 -1
- package/templates/default/scripts/build-step-registry.ts +33 -0
- package/templates/default/scripts/install-playwright.ts +17 -8
- package/templates/default/scripts/install-template-step.ts +128 -0
- package/templates/default/scripts/lib/filename-utils.test.ts +24 -0
- package/templates/default/scripts/lib/filename-utils.ts +24 -0
- package/templates/default/scripts/lib/jsdoc-parser.test.ts +63 -0
- package/templates/default/scripts/lib/jsdoc-parser.ts +88 -0
- package/templates/default/scripts/lib/step-file-parser.test.ts +71 -0
- package/templates/default/scripts/lib/step-file-parser.ts +315 -0
- package/templates/default/scripts/lib/step-matcher.test.ts +86 -0
- package/templates/default/scripts/lib/step-matcher.ts +120 -0
- package/templates/default/scripts/lib/sync-script-runner.ts +23 -0
- package/templates/default/scripts/lib/sync-summary.ts +29 -0
- package/templates/default/scripts/lib/tag-parsing.test.ts +20 -0
- package/templates/default/scripts/lib/tag-parsing.ts +10 -0
- package/templates/default/scripts/lib/template-step-installer.test.ts +225 -0
- package/templates/default/scripts/lib/template-step-installer.ts +404 -0
- package/templates/default/scripts/lib/template-step-registry.test.ts +118 -0
- package/templates/default/scripts/lib/template-step-registry.ts +137 -0
- package/templates/default/scripts/protect-seeded-files.ts +51 -38
- package/templates/default/scripts/regenerate-features.ts +98 -94
- package/templates/default/scripts/run-vitest.ts +59 -0
- package/templates/default/scripts/setup-env.ts +26 -19
- package/templates/default/scripts/sync-all.ts +44 -54
- package/templates/default/scripts/sync-appraise-base-template.ts +44 -16
- package/templates/default/scripts/sync-environments.ts +22 -66
- package/templates/default/scripts/sync-locator-groups.ts +358 -410
- package/templates/default/scripts/sync-locators.ts +348 -398
- package/templates/default/scripts/sync-modules.ts +302 -341
- package/templates/default/scripts/sync-tags.ts +29 -65
- package/templates/default/scripts/sync-template-step-groups.ts +24 -182
- package/templates/default/scripts/sync-template-steps.ts +36 -493
- package/templates/default/scripts/sync-test-cases.ts +296 -539
- package/templates/default/scripts/sync-test-suites.ts +32 -79
- package/templates/default/src/actions/dashboard/dashboard-actions.ts +70 -241
- package/templates/default/src/actions/environments/environment-actions.ts +102 -188
- package/templates/default/src/actions/locator/locator-actions.ts +77 -490
- package/templates/default/src/actions/locator-groups/locator-group-actions.ts +34 -212
- package/templates/default/src/actions/locator-picker/locator-picker-actions.test.ts +81 -0
- package/templates/default/src/actions/locator-picker/locator-picker-actions.ts +20 -161
- package/templates/default/src/actions/modules/module-actions.ts +99 -135
- package/templates/default/src/actions/reports/report-actions.ts +28 -565
- package/templates/default/src/actions/settings/sync-actions.test.ts +58 -0
- package/templates/default/src/actions/tags/tag-actions.ts +99 -107
- package/templates/default/src/actions/template-step/template-step-actions.ts +33 -194
- package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +35 -92
- package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +98 -238
- package/templates/default/src/actions/test-case/test-case-actions.ts +108 -356
- package/templates/default/src/actions/test-run/test-run-actions.ts +74 -1081
- package/templates/default/src/actions/test-suite/test-suite-actions.ts +35 -202
- package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
- package/templates/default/src/app/(base)/environments/environment-form.test.tsx +92 -0
- package/templates/default/src/app/(base)/environments/environment-form.tsx +228 -219
- package/templates/default/src/app/(base)/environments/environment-helpers.ts +58 -0
- package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
- package/templates/default/src/app/(base)/environments/environment-table.tsx +25 -24
- package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +49 -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)/locators/create/create-locator-workspace-helpers.test.ts +71 -0
- package/templates/default/src/app/(base)/locators/create/create-locator-workspace-helpers.ts +333 -0
- package/templates/default/src/app/(base)/locators/create/create-locator-workspace.test.tsx +125 -0
- package/templates/default/src/app/(base)/locators/create/create-locator-workspace.tsx +56 -274
- package/templates/default/src/app/(base)/locators/create/page.tsx +8 -4
- package/templates/default/src/app/(base)/locators/create/use-locator-workspace.ts +183 -0
- package/templates/default/src/app/(base)/locators/locator-helpers.test.ts +28 -0
- package/templates/default/src/app/(base)/locators/locator-helpers.ts +59 -0
- package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +74 -73
- package/templates/default/src/app/(base)/locators/locator-table.tsx +30 -28
- package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +20 -8
- package/templates/default/src/app/(base)/locators/page.tsx +3 -6
- package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +67 -66
- package/templates/default/src/app/(base)/modules/create/page.tsx +33 -34
- package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +43 -46
- package/templates/default/src/app/(base)/modules/module-form.test.tsx +84 -0
- package/templates/default/src/app/(base)/modules/module-form.tsx +159 -126
- package/templates/default/src/app/(base)/modules/module-helpers.ts +64 -0
- package/templates/default/src/app/(base)/modules/module-table-columns.tsx +81 -85
- package/templates/default/src/app/(base)/modules/module-table.tsx +25 -24
- package/templates/default/src/app/(base)/modules/page.tsx +59 -59
- package/templates/default/src/app/(base)/reports/[id]/page.tsx +20 -260
- package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
- package/templates/default/src/app/(base)/reports/feature-chart.tsx +79 -78
- package/templates/default/src/app/(base)/reports/overview-chart.tsx +49 -49
- package/templates/default/src/app/(base)/reports/page.tsx +98 -98
- package/templates/default/src/app/(base)/reports/report-detail-helpers.test.ts +109 -0
- package/templates/default/src/app/(base)/reports/report-detail-helpers.ts +247 -0
- package/templates/default/src/app/(base)/reports/report-metric-card.tsx +78 -78
- 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/test-cases/page.tsx +40 -40
- 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 +58 -58
- package/templates/default/src/app/(base)/settings/settings-sync-panel-helpers.test.tsx +40 -0
- package/templates/default/src/app/(base)/settings/settings-sync-panel-helpers.tsx +110 -0
- package/templates/default/src/app/(base)/settings/settings-sync-panel.test.tsx +127 -0
- package/templates/default/src/app/(base)/settings/settings-sync-panel.tsx +19 -134
- package/templates/default/src/app/(base)/settings/use-settings-sync.ts +66 -0
- 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-helpers.ts +13 -0
- package/templates/default/src/app/(base)/tags/tag-form.test.tsx +83 -0
- package/templates/default/src/app/(base)/tags/tag-form.tsx +143 -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 +43 -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.test.tsx +82 -0
- package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +181 -167
- package/templates/default/src/app/(base)/template-step-groups/template-step-group-helpers.ts +54 -0
- 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 +34 -32
- package/templates/default/src/app/(base)/template-steps/create/page.tsx +40 -37
- package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +54 -49
- package/templates/default/src/app/(base)/template-steps/page.tsx +59 -58
- package/templates/default/src/app/(base)/template-steps/paramChip.tsx +233 -213
- package/templates/default/src/app/(base)/template-steps/template-step-form.test.tsx +132 -0
- package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +342 -384
- package/templates/default/src/app/(base)/template-steps/template-step-helpers.test.ts +99 -0
- package/templates/default/src/app/(base)/template-steps/template-step-helpers.ts +176 -0
- package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +153 -158
- package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +26 -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.test.tsx +109 -0
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +45 -84
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.test.tsx +140 -0
- package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +154 -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 +90 -76
- package/templates/default/src/app/(base)/test-cases/create-from-template/create-from-template-helpers.test.ts +94 -0
- package/templates/default/src/app/(base)/test-cases/create-from-template/create-from-template-helpers.ts +171 -0
- package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +105 -96
- package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +40 -38
- package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.test.tsx +87 -0
- package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +83 -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 +3 -2
- package/templates/default/src/app/(base)/test-cases/test-case-flow.test.tsx +108 -0
- package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +43 -82
- package/templates/default/src/app/(base)/test-cases/test-case-form.test.tsx +202 -0
- package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +263 -395
- package/templates/default/src/app/(base)/test-cases/test-case-route-helpers.test.ts +95 -0
- package/templates/default/src/app/(base)/test-cases/test-case-route-helpers.ts +147 -0
- package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +4 -2
- package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +11 -10
- package/templates/default/src/app/(base)/test-runs/create/page.tsx +4 -5
- package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
- package/templates/default/src/app/(base)/test-runs/test-run-form-helpers.test.ts +50 -0
- package/templates/default/src/app/(base)/test-runs/test-run-form-helpers.ts +168 -0
- package/templates/default/src/app/(base)/test-runs/test-run-form.test.tsx +138 -0
- package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +111 -256
- 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-runs/use-test-run-name-validation.ts +74 -0
- package/templates/default/src/app/(base)/test-suites/create/page.tsx +17 -12
- package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +22 -19
- package/templates/default/src/app/(base)/test-suites/page.tsx +14 -56
- package/templates/default/src/app/(base)/test-suites/test-suite-form.test.tsx +127 -0
- package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +45 -64
- package/templates/default/src/app/(base)/test-suites/test-suite-helpers.test.ts +67 -0
- package/templates/default/src/app/(base)/test-suites/test-suite-helpers.ts +215 -0
- package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +32 -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/reports/steps/[stepId]/screenshot/route.test.ts +83 -0
- package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.ts +52 -52
- package/templates/default/src/app/api/test-runs/[runId]/download/route.test.ts +169 -0
- package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +1 -1
- package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.test.ts +135 -0
- 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/page.tsx +1 -1
- 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-helpers.test.ts +32 -0
- package/templates/default/src/components/data-extraction/locator-inspector-helpers.ts +183 -0
- package/templates/default/src/components/data-extraction/locator-inspector.tsx +349 -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/diagram/button-edge.tsx +54 -54
- package/templates/default/src/components/diagram/dynamic-parameters-helpers.test.ts +83 -0
- package/templates/default/src/components/diagram/dynamic-parameters-helpers.ts +158 -0
- package/templates/default/src/components/diagram/dynamic-parameters.tsx +350 -474
- package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
- package/templates/default/src/components/diagram/flow-diagram-helpers.test.ts +117 -0
- package/templates/default/src/components/diagram/flow-diagram-helpers.ts +251 -0
- package/templates/default/src/components/diagram/flow-diagram.tsx +247 -470
- package/templates/default/src/components/diagram/flow-host-helpers.test.ts +74 -0
- package/templates/default/src/components/diagram/flow-host-helpers.ts +51 -0
- package/templates/default/src/components/diagram/node-form-helpers.test.ts +92 -0
- package/templates/default/src/components/diagram/node-form-helpers.ts +100 -0
- package/templates/default/src/components/diagram/node-form.test.tsx +168 -0
- package/templates/default/src/components/diagram/node-form.tsx +199 -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/diagram/use-flow-node-order.ts +49 -0
- 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/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 +118 -116
- package/templates/default/src/components/navigation/nav-card.tsx +31 -31
- package/templates/default/src/components/navigation/nav-command-helpers.ts +122 -0
- package/templates/default/src/components/navigation/nav-command-search.tsx +125 -0
- package/templates/default/src/components/navigation/nav-command.test.tsx +106 -0
- package/templates/default/src/components/navigation/nav-command.tsx +49 -472
- 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/navigation/use-nav-command.ts +58 -0
- package/templates/default/src/components/node-header.tsx +159 -159
- package/templates/default/src/components/reports/test-case-logs-modal.tsx +310 -310
- package/templates/default/src/components/table/table-actions.tsx +174 -172
- package/templates/default/src/components/test-case/test-case-form-helpers.test.ts +100 -0
- package/templates/default/src/components/test-case/test-case-form-helpers.ts +140 -0
- package/templates/default/src/components/test-case/test-case-picker-helpers.test.ts +40 -0
- package/templates/default/src/components/test-case/test-case-picker-helpers.ts +41 -0
- package/templates/default/src/components/test-case/test-case-picker.test.tsx +44 -0
- package/templates/default/src/components/test-case/test-case-picker.tsx +16 -35
- package/templates/default/src/components/test-case/test-scenario-preview.tsx +34 -0
- package/templates/default/src/components/test-run/download-logs-button.tsx +92 -92
- package/templates/default/src/components/test-run/log-viewer-helpers.test.ts +37 -0
- package/templates/default/src/components/test-run/log-viewer-helpers.ts +80 -0
- package/templates/default/src/components/test-run/log-viewer.test.tsx +118 -0
- package/templates/default/src/components/test-run/log-viewer.tsx +51 -350
- package/templates/default/src/components/test-run/test-run-details-helpers.test.ts +31 -0
- package/templates/default/src/components/test-run/test-run-details-helpers.ts +208 -0
- package/templates/default/src/components/test-run/test-run-details.test.tsx +174 -0
- package/templates/default/src/components/test-run/test-run-details.tsx +155 -457
- package/templates/default/src/components/test-run/test-run-header-helpers.test.ts +31 -0
- package/templates/default/src/components/test-run/test-run-header-helpers.ts +23 -0
- package/templates/default/src/components/test-run/test-run-header.test.tsx +103 -0
- package/templates/default/src/components/test-run/test-run-header.tsx +27 -149
- package/templates/default/src/components/test-run/use-log-viewer.ts +213 -0
- package/templates/default/src/components/test-run/use-test-run-details.ts +184 -0
- package/templates/default/src/components/test-run/use-test-run-header.ts +89 -0
- package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
- package/templates/default/src/components/test-suite/test-suite-picker-helpers.test.ts +68 -0
- package/templates/default/src/components/test-suite/test-suite-picker-helpers.ts +76 -0
- package/templates/default/src/components/test-suite/test-suite-picker.test.tsx +65 -0
- package/templates/default/src/components/test-suite/test-suite-picker.tsx +4 -72
- 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.test.tsx +122 -0
- package/templates/default/src/components/ui/data-table.tsx +298 -261
- 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.test.tsx +45 -0
- package/templates/default/src/components/ui/multi-select.tsx +158 -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 -151
- 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.test.tsx +60 -0
- package/templates/default/src/components/user-prompt/delete-prompt.tsx +118 -87
- 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-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/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-suite-form-opts.ts +24 -24
- package/templates/default/src/hooks/use-toast.ts +187 -187
- package/templates/default/src/lib/automation/automation-path-roots.ts +95 -0
- package/templates/default/src/lib/automation/automation-workspace.ts +147 -0
- package/templates/default/src/lib/automation/paths.ts +6 -211
- package/templates/default/src/lib/bidirectional-sync.ts +432 -432
- package/templates/default/src/lib/environment-file-utils.ts +2 -1
- package/templates/default/src/lib/executor/local-executor-adapter.ts +2 -5
- package/templates/default/src/lib/feature-file-generator.ts +2 -1
- package/templates/default/src/lib/gherkin-parser.ts +0 -2
- package/templates/default/src/lib/locator-group-file-utils.ts +304 -307
- package/templates/default/src/lib/locator-picker/session-manager.ts +0 -21
- package/templates/default/src/lib/locator-picker/suggestions.ts +13 -11
- package/templates/default/src/lib/metrics/metric-calculator.ts +2 -6
- 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/sync/sync-executor.test.ts +76 -0
- package/templates/default/src/lib/sync/sync-pending-counts.test.ts +227 -226
- package/templates/default/src/lib/sync/sync-pending-counts.ts +2 -5
- package/templates/default/src/lib/template-sync-utils.d.ts +6 -6
- package/templates/default/src/lib/template-sync-utils.js +46 -46
- package/templates/default/src/lib/template-sync-utils.ts +63 -63
- 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/report-parser.ts +352 -352
- package/templates/default/src/lib/test-run/test-run-executor.ts +13 -13
- package/templates/default/src/lib/test-run/winston-logger.ts +65 -64
- 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 +2 -2
- 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/services/dashboard/dashboard-service.test.ts +106 -0
- package/templates/default/src/services/dashboard/dashboard-service.ts +173 -0
- package/templates/default/src/services/environment/environment-service.test.ts +137 -0
- package/templates/default/src/services/environment/environment-service.ts +96 -0
- package/templates/default/src/services/locator/locator-path-utils.test.ts +14 -0
- package/templates/default/src/services/locator/locator-path-utils.ts +14 -0
- package/templates/default/src/services/locator/locator-service.test.ts +63 -0
- package/templates/default/src/services/locator/locator-service.ts +479 -0
- package/templates/default/src/services/locator/locator-sync-utils.test.ts +19 -0
- package/templates/default/src/services/locator/locator-sync-utils.ts +21 -0
- package/templates/default/src/services/locator-group/locator-group-service.test.ts +123 -0
- package/templates/default/src/services/locator-group/locator-group-service.ts +180 -0
- package/templates/default/src/services/module/module-service.test.ts +89 -0
- package/templates/default/src/services/module/module-service.ts +66 -0
- package/templates/default/src/services/report/report-service.test.ts +244 -0
- package/templates/default/src/services/report/report-service.ts +438 -0
- package/templates/default/src/services/shared/constants.ts +2 -0
- package/templates/default/src/services/shared/errors.test.ts +38 -0
- package/templates/default/src/services/shared/errors.ts +44 -0
- package/templates/default/src/services/shared/index.ts +7 -0
- package/templates/default/src/services/tag/tag-service.test.ts +22 -0
- package/templates/default/src/services/tag/tag-service.ts +41 -0
- package/templates/default/src/services/template-step/template-step-service.test.ts +22 -0
- package/templates/default/src/services/template-step/template-step-service.ts +171 -0
- package/templates/default/src/services/template-step-group/template-step-group-service.test.ts +22 -0
- package/templates/default/src/services/template-step-group/template-step-group-service.ts +81 -0
- package/templates/default/src/services/template-test-case/template-test-case-service.test.ts +22 -0
- package/templates/default/src/services/template-test-case/template-test-case-service.ts +128 -0
- package/templates/default/src/services/test-case/test-case-service.test.ts +175 -0
- package/templates/default/src/services/test-case/test-case-service.ts +298 -0
- package/templates/default/src/services/test-run/test-run-helpers.ts +61 -0
- package/templates/default/src/services/test-run/test-run-service.test.ts +647 -0
- package/templates/default/src/services/test-run/test-run-service.ts +917 -0
- package/templates/default/src/services/test-suite/test-suite-service.test.ts +127 -0
- package/templates/default/src/services/test-suite/test-suite-service.ts +197 -0
- 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 +19 -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/tsconfig.json +1 -1
- package/dist/cli.e2e.test.d.ts +0 -2
- package/dist/cli.e2e.test.d.ts.map +0 -1
- package/dist/cli.e2e.test.js +0 -73
- package/dist/cli.e2e.test.js.map +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -65
- package/dist/config.test.js.map +0 -1
- package/dist/copy-template.test.d.ts +0 -2
- package/dist/copy-template.test.d.ts.map +0 -1
- package/dist/copy-template.test.js +0 -71
- package/dist/copy-template.test.js.map +0 -1
- package/dist/download-repo.test.d.ts +0 -2
- package/dist/download-repo.test.d.ts.map +0 -1
- package/dist/download-repo.test.js +0 -14
- package/dist/download-repo.test.js.map +0 -1
- package/dist/install.test.d.ts +0 -2
- package/dist/install.test.d.ts.map +0 -1
- package/dist/install.test.js +0 -119
- package/dist/install.test.js.map +0 -1
- package/dist/prompts.test.d.ts +0 -2
- package/dist/prompts.test.d.ts.map +0 -1
- package/dist/prompts.test.js +0 -58
- package/dist/prompts.test.js.map +0 -1
- package/templates/default/src/actions/conflict/conflict.action.ts +0 -33
- package/templates/default/src/actions/review/review-actions.ts +0 -147
- package/templates/default/src/actions/user/user-actions.ts +0 -13
- package/templates/default/src/app/(base)/locators/locator-form.tsx +0 -163
- package/templates/default/src/app/(base)/reviews/create/page.tsx +0 -26
- package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +0 -15
- package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +0 -26
- package/templates/default/src/app/(base)/reviews/page.tsx +0 -26
- package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +0 -26
- package/templates/default/src/app/(base)/reviews/review-form.tsx +0 -11
- package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +0 -9
- package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +0 -9
- package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +0 -15
- package/templates/default/src/constants/form-opts/locator-form-opts.ts +0 -20
- package/templates/default/src/constants/form-opts/review-form-opts.ts +0 -23
|
@@ -16,13 +16,22 @@ import {
|
|
|
16
16
|
ParsedStep,
|
|
17
17
|
} from '../src/lib/gherkin-parser'
|
|
18
18
|
import { buildModuleHierarchy, findModuleByPath } from '../src/lib/module-hierarchy-builder'
|
|
19
|
-
import {
|
|
19
|
+
import { StepParameterType, TagType } from '@prisma/client'
|
|
20
20
|
import { ensureAutomationWorkspaceReady, getAutomationFeaturesDir } from '../src/lib/automation/paths'
|
|
21
21
|
import {
|
|
22
22
|
determineProjectedStepIcon,
|
|
23
23
|
getTestSuiteFilesystemKey,
|
|
24
24
|
normalizeProjectedDbTestCaseSteps,
|
|
25
25
|
} from '../src/lib/sync/projected-feature-utils'
|
|
26
|
+
import { extractTestSuiteNameFromFilename } from './lib/filename-utils'
|
|
27
|
+
import { splitTagLine } from './lib/tag-parsing'
|
|
28
|
+
import {
|
|
29
|
+
determineStepTypeAndIcon,
|
|
30
|
+
findMatchingTemplateStep,
|
|
31
|
+
sameResolvedParameters,
|
|
32
|
+
} from './lib/step-matcher'
|
|
33
|
+
import { printSyncSummary } from './lib/sync-summary'
|
|
34
|
+
import { runSyncScript } from './lib/sync-script-runner'
|
|
26
35
|
|
|
27
36
|
interface TestCaseFromFS {
|
|
28
37
|
identifierTag: string // @tc_... tag
|
|
@@ -35,19 +44,6 @@ interface TestCaseFromFS {
|
|
|
35
44
|
filePath: string // Feature file path
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
interface ParameterMatch {
|
|
39
|
-
name: string
|
|
40
|
-
value: string
|
|
41
|
-
order: number
|
|
42
|
-
type: StepParameterType
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface TemplateStepMatch {
|
|
46
|
-
templateStepId: string
|
|
47
|
-
signature: string
|
|
48
|
-
parameters: ParameterMatch[]
|
|
49
|
-
}
|
|
50
|
-
|
|
51
47
|
interface SyncResult {
|
|
52
48
|
testCasesScanned: number
|
|
53
49
|
testCasesExisting: number
|
|
@@ -61,26 +57,6 @@ interface SyncResult {
|
|
|
61
57
|
deletedTestCases: Array<{ identifierTag: string; title: string }>
|
|
62
58
|
}
|
|
63
59
|
|
|
64
|
-
/**
|
|
65
|
-
* Extracts test suite name from filename
|
|
66
|
-
* Example: "login-validation.feature" -> "login-validation"
|
|
67
|
-
*/
|
|
68
|
-
function extractTestSuiteNameFromFilename(filePath: string): string {
|
|
69
|
-
const fileName = filePath.split(/[/\\]/).pop() || ''
|
|
70
|
-
return fileName.replace(/\.feature$/, '')
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Splits a tag line that may contain multiple tags separated by spaces
|
|
75
|
-
* Example: "@smoke @demo" -> ["@smoke", "@demo"]
|
|
76
|
-
*/
|
|
77
|
-
function splitTagLine(tagLine: string): string[] {
|
|
78
|
-
return tagLine
|
|
79
|
-
.split(/\s+/)
|
|
80
|
-
.filter(tag => tag.trim().startsWith('@'))
|
|
81
|
-
.map(tag => tag.trim())
|
|
82
|
-
}
|
|
83
|
-
|
|
84
60
|
/**
|
|
85
61
|
* Normalizes a tag expression to ensure it has the @ prefix
|
|
86
62
|
* Example: "tc_123" -> "@tc_123", "@tc_123" -> "@tc_123"
|
|
@@ -171,149 +147,6 @@ async function scanTestCasesFromFilesystem(featuresDir: string): Promise<TestCas
|
|
|
171
147
|
return testCases
|
|
172
148
|
}
|
|
173
149
|
|
|
174
|
-
/**
|
|
175
|
-
* Converts template step signature to regex pattern
|
|
176
|
-
* Replaces placeholders like {string}, {int}, {boolean} with regex patterns
|
|
177
|
-
*/
|
|
178
|
-
function signatureToRegex(signature: string): RegExp {
|
|
179
|
-
// Escape special regex characters except placeholders
|
|
180
|
-
let pattern = signature.replace(/[.*+?^${}()|[\]\\]/g, match => {
|
|
181
|
-
// Don't escape { and } as they're our placeholders
|
|
182
|
-
if (match === '{' || match === '}') return match
|
|
183
|
-
return '\\' + match
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
// Replace placeholders with regex patterns
|
|
187
|
-
pattern = pattern.replace(/\{string\}/g, '"([^"]+)"') // Matches quoted strings
|
|
188
|
-
pattern = pattern.replace(/\{int\}/g, '(\\d+)') // Matches integers
|
|
189
|
-
pattern = pattern.replace(/\{boolean\}/g, '(true|false)') // Matches booleans
|
|
190
|
-
pattern = pattern.replace(/\{number\}/g, '(\\d+(?:\\.\\d+)?)') // Matches numbers (int or float)
|
|
191
|
-
|
|
192
|
-
// Create regex with case-insensitive matching and word boundaries
|
|
193
|
-
return new RegExp(`^${pattern}$`, 'i')
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Extracts parameters from gherkin step text based on template step signature
|
|
198
|
-
*/
|
|
199
|
-
function extractParametersFromGherkinStep(
|
|
200
|
-
gherkinText: string,
|
|
201
|
-
signature: string,
|
|
202
|
-
templateStepParameters: Array<{ name: string; order: number; type: StepParameterType }>,
|
|
203
|
-
): ParameterMatch[] | null {
|
|
204
|
-
const regex = signatureToRegex(signature)
|
|
205
|
-
const match = gherkinText.match(regex)
|
|
206
|
-
|
|
207
|
-
if (!match) {
|
|
208
|
-
return null
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Extract captured groups (skip index 0 which is the full match)
|
|
212
|
-
const capturedValues = match.slice(1)
|
|
213
|
-
const parameters: ParameterMatch[] = []
|
|
214
|
-
|
|
215
|
-
// Map captured values to template step parameters by order
|
|
216
|
-
for (let i = 0; i < capturedValues.length && i < templateStepParameters.length; i++) {
|
|
217
|
-
const param = templateStepParameters[i]
|
|
218
|
-
const value = capturedValues[i]
|
|
219
|
-
|
|
220
|
-
if (value !== undefined) {
|
|
221
|
-
parameters.push({
|
|
222
|
-
name: param.name,
|
|
223
|
-
value: value,
|
|
224
|
-
order: param.order,
|
|
225
|
-
type: param.type,
|
|
226
|
-
})
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return parameters
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Matches a gherkin step to a template step by pattern matching
|
|
235
|
-
* Returns the template step ID and extracted parameters, or null if no match found
|
|
236
|
-
* Note: Template step signatures don't include the keyword, so we match against step.text only
|
|
237
|
-
*/
|
|
238
|
-
async function matchGherkinStepToTemplateStep(gherkinStep: ParsedStep): Promise<TemplateStepMatch | null> {
|
|
239
|
-
try {
|
|
240
|
-
// Get all template steps from database
|
|
241
|
-
const templateSteps = await prisma.templateStep.findMany({
|
|
242
|
-
include: {
|
|
243
|
-
parameters: {
|
|
244
|
-
orderBy: {
|
|
245
|
-
order: 'asc',
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
// Try to match against each template step signature
|
|
252
|
-
// Template step signatures don't include the keyword, so match against step.text
|
|
253
|
-
for (const templateStep of templateSteps) {
|
|
254
|
-
// Try to match the gherkin step text (without keyword) against the signature
|
|
255
|
-
const parameters = extractParametersFromGherkinStep(
|
|
256
|
-
gherkinStep.text,
|
|
257
|
-
templateStep.signature,
|
|
258
|
-
templateStep.parameters.map(p => ({
|
|
259
|
-
name: p.name,
|
|
260
|
-
order: p.order,
|
|
261
|
-
type: p.type,
|
|
262
|
-
})),
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
if (parameters !== null) {
|
|
266
|
-
return {
|
|
267
|
-
templateStepId: templateStep.id,
|
|
268
|
-
signature: templateStep.signature,
|
|
269
|
-
parameters,
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return null
|
|
275
|
-
} catch (error) {
|
|
276
|
-
console.error(`Error matching gherkin step to template step:`, error)
|
|
277
|
-
return null
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Determines the step type and icon based on the Gherkin keyword
|
|
283
|
-
*/
|
|
284
|
-
function determineStepTypeAndIcon(keyword: string): { type: TemplateStepType; icon: TemplateStepIcon } {
|
|
285
|
-
const lowerKeyword = keyword.toLowerCase().trim()
|
|
286
|
-
|
|
287
|
-
if (lowerKeyword === 'given') {
|
|
288
|
-
return { type: 'ACTION', icon: 'NAVIGATION' }
|
|
289
|
-
} else if (lowerKeyword === 'when') {
|
|
290
|
-
return { type: 'ACTION', icon: 'MOUSE' }
|
|
291
|
-
} else if (lowerKeyword === 'then') {
|
|
292
|
-
return { type: 'ASSERTION', icon: 'VALIDATION' }
|
|
293
|
-
} else if (lowerKeyword === 'and' || lowerKeyword === 'but') {
|
|
294
|
-
return { type: 'ACTION', icon: 'MOUSE' }
|
|
295
|
-
} else {
|
|
296
|
-
// Default fallback
|
|
297
|
-
return { type: 'ACTION', icon: 'MOUSE' }
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function sameResolvedParameters(left: ParameterMatch[], right: ParameterMatch[]): boolean {
|
|
302
|
-
if (left.length !== right.length) {
|
|
303
|
-
return false
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return left.every((parameter, index) => {
|
|
307
|
-
const other = right[index]
|
|
308
|
-
return (
|
|
309
|
-
parameter.name === other?.name &&
|
|
310
|
-
parameter.value === other?.value &&
|
|
311
|
-
parameter.order === other?.order &&
|
|
312
|
-
parameter.type === other?.type
|
|
313
|
-
)
|
|
314
|
-
})
|
|
315
|
-
}
|
|
316
|
-
|
|
317
150
|
/**
|
|
318
151
|
* Finds or creates a tag by tag expression
|
|
319
152
|
* If the tag exists but has a different type, updates it to the correct type
|
|
@@ -354,12 +187,67 @@ async function findOrCreateTag(tagExpression: string, type: TagType): Promise<st
|
|
|
354
187
|
}
|
|
355
188
|
}
|
|
356
189
|
|
|
190
|
+
async function deleteTestCaseWithCascade(
|
|
191
|
+
testCaseId: string,
|
|
192
|
+
identifierTagId?: string,
|
|
193
|
+
): Promise<void> {
|
|
194
|
+
// Keep deletes in dependency order to satisfy RESTRICT constraints and
|
|
195
|
+
// mirror domain-level delete behavior in a single transactional boundary.
|
|
196
|
+
await prisma.$transaction(async tx => {
|
|
197
|
+
await tx.testRunTestCase.deleteMany({
|
|
198
|
+
where: { testCaseId },
|
|
199
|
+
})
|
|
200
|
+
await tx.review.deleteMany({
|
|
201
|
+
where: { testCaseId },
|
|
202
|
+
})
|
|
203
|
+
await tx.linkedJiraTicket.deleteMany({
|
|
204
|
+
where: { testCaseId },
|
|
205
|
+
})
|
|
206
|
+
await tx.testCaseStepParameter.deleteMany({
|
|
207
|
+
where: {
|
|
208
|
+
testCaseStep: { testCaseId },
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
await tx.testCaseStep.deleteMany({
|
|
212
|
+
where: { testCaseId },
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
if (identifierTagId) {
|
|
216
|
+
const otherTestCasesWithTag = await tx.testCase.findMany({
|
|
217
|
+
where: {
|
|
218
|
+
tags: { some: { id: identifierTagId } },
|
|
219
|
+
id: { not: testCaseId },
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
if (otherTestCasesWithTag.length === 0) {
|
|
223
|
+
await tx.tag.delete({
|
|
224
|
+
where: { id: identifierTagId },
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await tx.testCase.delete({
|
|
230
|
+
where: { id: testCaseId },
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
|
|
357
235
|
/**
|
|
358
236
|
* Syncs test case steps to database
|
|
359
237
|
*/
|
|
360
|
-
async function syncTestCaseSteps(
|
|
238
|
+
async function syncTestCaseSteps(
|
|
239
|
+
testCaseId: string,
|
|
240
|
+
steps: ParsedStep[],
|
|
241
|
+
templateSteps: Array<{
|
|
242
|
+
id: string
|
|
243
|
+
signature: string
|
|
244
|
+
parameters: Array<{ name: string; order: number; type: StepParameterType }>
|
|
245
|
+
}>,
|
|
246
|
+
result: SyncResult,
|
|
247
|
+
): Promise<void> {
|
|
361
248
|
try {
|
|
362
|
-
//
|
|
249
|
+
// Load current persisted step state once so we can diff by order and apply
|
|
250
|
+
// minimal mutations for idempotent sync runs.
|
|
363
251
|
const existingSteps = await prisma.testCaseStep.findMany({
|
|
364
252
|
where: { testCaseId },
|
|
365
253
|
orderBy: { order: 'asc' },
|
|
@@ -373,13 +261,13 @@ async function syncTestCaseSteps(testCaseId: string, steps: ParsedStep[], result
|
|
|
373
261
|
},
|
|
374
262
|
})
|
|
375
263
|
|
|
376
|
-
//
|
|
264
|
+
// Order is the stable identity within a scenario for synchronization.
|
|
377
265
|
const existingStepsMap = new Map(existingSteps.map(step => [step.order, step]))
|
|
378
266
|
const projectedExistingStepsMap = new Map(normalizeProjectedDbTestCaseSteps(existingSteps).map(step => [step.order, step]))
|
|
379
267
|
|
|
380
268
|
// Process each step from filesystem
|
|
381
269
|
for (const step of steps) {
|
|
382
|
-
const match =
|
|
270
|
+
const match = findMatchingTemplateStep(step, templateSteps)
|
|
383
271
|
|
|
384
272
|
if (!match) {
|
|
385
273
|
result.warnings.push(
|
|
@@ -403,7 +291,8 @@ async function syncTestCaseSteps(testCaseId: string, steps: ParsedStep[], result
|
|
|
403
291
|
projectedExistingStep.templateStepSignature === match.signature &&
|
|
404
292
|
sameResolvedParameters(projectedExistingStep.parameters, match.parameters)
|
|
405
293
|
|
|
406
|
-
//
|
|
294
|
+
// First compare with projected state (normalizes icon/signature/params) to
|
|
295
|
+
// avoid redundant writes from representational differences.
|
|
407
296
|
const needsUpdate =
|
|
408
297
|
!matchesProjectedState &&
|
|
409
298
|
(existingStep.gherkinStep !== gherkinStep ||
|
|
@@ -483,207 +372,195 @@ async function syncTestCaseSteps(testCaseId: string, steps: ParsedStep[], result
|
|
|
483
372
|
}
|
|
484
373
|
}
|
|
485
374
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
375
|
+
type TemplateStepForMatch = Array<{
|
|
376
|
+
id: string
|
|
377
|
+
signature: string
|
|
378
|
+
parameters: Array<{ name: string; order: number; type: StepParameterType }>
|
|
379
|
+
}>
|
|
380
|
+
|
|
381
|
+
async function upsertTestCase(
|
|
382
|
+
testCase: TestCaseFromFS,
|
|
383
|
+
templateSteps: TemplateStepForMatch,
|
|
384
|
+
suitesByModuleId: Map<string, Array<{ id: string; name: string }>>,
|
|
385
|
+
result: SyncResult,
|
|
386
|
+
): Promise<void> {
|
|
387
|
+
// Resolve module + suite first so create/update paths share the same identity anchor.
|
|
388
|
+
// Ensure module exists
|
|
389
|
+
let moduleId = await findModuleByPath(testCase.modulePath)
|
|
390
|
+
|
|
391
|
+
if (!moduleId) {
|
|
392
|
+
console.log(` š¦ Creating module hierarchy for path: ${testCase.modulePath}`)
|
|
393
|
+
moduleId = await buildModuleHierarchy(testCase.modulePath)
|
|
394
|
+
}
|
|
491
395
|
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
396
|
+
// Find test suite
|
|
397
|
+
let moduleSuites = suitesByModuleId.get(moduleId)
|
|
398
|
+
if (!moduleSuites) {
|
|
399
|
+
moduleSuites = await prisma.testSuite.findMany({
|
|
400
|
+
where: {
|
|
401
|
+
moduleId: moduleId,
|
|
402
|
+
},
|
|
403
|
+
select: {
|
|
404
|
+
id: true,
|
|
405
|
+
name: true,
|
|
406
|
+
},
|
|
407
|
+
})
|
|
408
|
+
suitesByModuleId.set(moduleId, moduleSuites)
|
|
409
|
+
}
|
|
495
410
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
411
|
+
const testSuite = moduleSuites.find(
|
|
412
|
+
suite => getTestSuiteFilesystemKey(suite.name) === getTestSuiteFilesystemKey(testCase.testSuiteName),
|
|
413
|
+
)
|
|
499
414
|
|
|
500
|
-
|
|
501
|
-
|
|
415
|
+
if (!testSuite) {
|
|
416
|
+
result.errors.push(`Test suite '${testCase.testSuiteName}' not found in module '${testCase.modulePath}'`)
|
|
417
|
+
console.error(` ā Test suite '${testCase.testSuiteName}' not found in module '${testCase.modulePath}'`)
|
|
418
|
+
return
|
|
419
|
+
}
|
|
502
420
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}
|
|
421
|
+
const identifierTagName = testCase.identifierTag.startsWith('@')
|
|
422
|
+
? testCase.identifierTag.substring(1)
|
|
423
|
+
: testCase.identifierTag
|
|
507
424
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
425
|
+
const identifierTag = await prisma.tag.findFirst({
|
|
426
|
+
where: {
|
|
427
|
+
name: identifierTagName,
|
|
428
|
+
type: TagType.IDENTIFIER,
|
|
429
|
+
},
|
|
430
|
+
include: {
|
|
431
|
+
testCases: {
|
|
432
|
+
include: {
|
|
433
|
+
TestSuite: true,
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
})
|
|
522
438
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
439
|
+
const filterTagIds: string[] = []
|
|
440
|
+
// FILTER tags are opportunistically created during test-case sync; identifier tags
|
|
441
|
+
// are handled separately because they define test-case identity.
|
|
442
|
+
for (const filterTagExpr of testCase.filterTags) {
|
|
443
|
+
const tagId = await findOrCreateTag(filterTagExpr, TagType.FILTER)
|
|
444
|
+
if (tagId) {
|
|
445
|
+
filterTagIds.push(tagId)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
526
448
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
449
|
+
if (identifierTag && identifierTag.testCases.length > 0) {
|
|
450
|
+
// Prefer a suite-matching case when tags are reused; fallback preserves legacy data.
|
|
451
|
+
const matchedExistingTestCaseSummary =
|
|
452
|
+
identifierTag.testCases.find(existingCase => existingCase.TestSuite.some(suite => suite.id === testSuite.id)) ??
|
|
453
|
+
identifierTag.testCases[0]
|
|
454
|
+
const existingTestCase = await prisma.testCase.findUnique({
|
|
455
|
+
where: { id: matchedExistingTestCaseSummary.id },
|
|
456
|
+
include: {
|
|
457
|
+
tags: true,
|
|
458
|
+
TestSuite: true,
|
|
459
|
+
},
|
|
460
|
+
})
|
|
532
461
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
462
|
+
if (!existingTestCase) {
|
|
463
|
+
result.errors.push(`Test case with identifier tag '${testCase.identifierTag}' not found`)
|
|
464
|
+
return
|
|
465
|
+
}
|
|
537
466
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
467
|
+
const currentFilterTagIds =
|
|
468
|
+
existingTestCase.tags
|
|
469
|
+
.filter(t => t.type === TagType.FILTER)
|
|
470
|
+
.map(t => t.id)
|
|
471
|
+
.sort() || []
|
|
472
|
+
|
|
473
|
+
const newFilterTagIds = filterTagIds.sort()
|
|
474
|
+
const tagsChanged = JSON.stringify(currentFilterTagIds) !== JSON.stringify(newFilterTagIds)
|
|
475
|
+
const isAssociated = existingTestCase.TestSuite.some(ts => ts.id === testSuite.id)
|
|
476
|
+
|
|
477
|
+
const needsUpdate =
|
|
478
|
+
existingTestCase.title !== testCase.title ||
|
|
479
|
+
existingTestCase.description !== testCase.description ||
|
|
480
|
+
tagsChanged ||
|
|
481
|
+
!isAssociated
|
|
482
|
+
|
|
483
|
+
if (needsUpdate) {
|
|
484
|
+
await prisma.testCase.update({
|
|
485
|
+
where: { id: existingTestCase.id },
|
|
486
|
+
data: {
|
|
487
|
+
title: testCase.title,
|
|
488
|
+
description: testCase.description,
|
|
489
|
+
tags: {
|
|
490
|
+
set: [identifierTag.id, ...filterTagIds].map(id => ({ id })),
|
|
549
491
|
},
|
|
492
|
+
TestSuite: isAssociated
|
|
493
|
+
? undefined
|
|
494
|
+
: {
|
|
495
|
+
connect: [{ id: testSuite.id }],
|
|
496
|
+
},
|
|
550
497
|
},
|
|
551
498
|
})
|
|
552
499
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
// Test case exists - update it
|
|
564
|
-
const matchedExistingTestCaseSummary =
|
|
565
|
-
identifierTag.testCases.find(existingCase => existingCase.TestSuite.some(suite => suite.id === testSuite.id)) ??
|
|
566
|
-
identifierTag.testCases[0]
|
|
567
|
-
const existingTestCase = await prisma.testCase.findUnique({
|
|
568
|
-
where: { id: matchedExistingTestCaseSummary.id },
|
|
569
|
-
include: {
|
|
570
|
-
tags: true,
|
|
571
|
-
TestSuite: true,
|
|
572
|
-
},
|
|
573
|
-
})
|
|
574
|
-
|
|
575
|
-
if (!existingTestCase) {
|
|
576
|
-
result.errors.push(`Test case with identifier tag '${testCase.identifierTag}' not found`)
|
|
577
|
-
continue
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Check if update is needed
|
|
581
|
-
const currentFilterTagIds =
|
|
582
|
-
existingTestCase.tags
|
|
583
|
-
.filter(t => t.type === TagType.FILTER)
|
|
584
|
-
.map(t => t.id)
|
|
585
|
-
.sort() || []
|
|
586
|
-
|
|
587
|
-
const newFilterTagIds = filterTagIds.sort()
|
|
588
|
-
const tagsChanged = JSON.stringify(currentFilterTagIds) !== JSON.stringify(newFilterTagIds)
|
|
589
|
-
const isAssociated = existingTestCase.TestSuite.some(ts => ts.id === testSuite.id)
|
|
590
|
-
|
|
591
|
-
const needsUpdate =
|
|
592
|
-
existingTestCase.title !== testCase.title ||
|
|
593
|
-
existingTestCase.description !== testCase.description ||
|
|
594
|
-
tagsChanged ||
|
|
595
|
-
!isAssociated
|
|
596
|
-
|
|
597
|
-
if (needsUpdate) {
|
|
598
|
-
await prisma.testCase.update({
|
|
599
|
-
where: { id: existingTestCase.id },
|
|
600
|
-
data: {
|
|
601
|
-
title: testCase.title,
|
|
602
|
-
description: testCase.description,
|
|
603
|
-
tags: {
|
|
604
|
-
set: [identifierTag.id, ...filterTagIds].map(id => ({ id })),
|
|
605
|
-
},
|
|
606
|
-
TestSuite: isAssociated
|
|
607
|
-
? undefined // Don't change associations if already connected
|
|
608
|
-
: {
|
|
609
|
-
connect: [{ id: testSuite.id }], // Add test suite if not already connected
|
|
610
|
-
},
|
|
611
|
-
},
|
|
612
|
-
})
|
|
500
|
+
result.testCasesUpdated++
|
|
501
|
+
result.updatedTestCases.push({
|
|
502
|
+
identifierTag: testCase.identifierTag,
|
|
503
|
+
title: testCase.title,
|
|
504
|
+
})
|
|
505
|
+
console.log(` š Updated test case '${testCase.title}' (${testCase.identifierTag})`)
|
|
506
|
+
} else {
|
|
507
|
+
result.testCasesExisting++
|
|
508
|
+
console.log(` ā Test case '${testCase.title}' (${testCase.identifierTag}) already up to date`)
|
|
509
|
+
}
|
|
613
510
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
title: testCase.title,
|
|
618
|
-
})
|
|
619
|
-
console.log(` š Updated test case '${testCase.title}' (${testCase.identifierTag})`)
|
|
620
|
-
} else {
|
|
621
|
-
result.testCasesExisting++
|
|
622
|
-
console.log(` ā Test case '${testCase.title}' (${testCase.identifierTag}) already up to date`)
|
|
623
|
-
}
|
|
511
|
+
await syncTestCaseSteps(existingTestCase.id, testCase.steps, templateSteps, result)
|
|
512
|
+
return
|
|
513
|
+
}
|
|
624
514
|
|
|
625
|
-
|
|
626
|
-
await syncTestCaseSteps(existingTestCase.id, testCase.steps, result)
|
|
627
|
-
} else {
|
|
628
|
-
// Test case doesn't exist - create it
|
|
629
|
-
// First ensure identifier tag exists
|
|
630
|
-
const identifierTagId = await findOrCreateTag(testCase.identifierTag, TagType.IDENTIFIER)
|
|
515
|
+
const identifierTagId = await findOrCreateTag(testCase.identifierTag, TagType.IDENTIFIER)
|
|
631
516
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
517
|
+
if (!identifierTagId) {
|
|
518
|
+
result.errors.push(`Failed to create identifier tag '${testCase.identifierTag}'`)
|
|
519
|
+
console.error(` ā Failed to create identifier tag '${testCase.identifierTag}'`)
|
|
520
|
+
return
|
|
521
|
+
}
|
|
637
522
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
523
|
+
const newTestCase = await prisma.testCase.create({
|
|
524
|
+
data: {
|
|
525
|
+
title: testCase.title,
|
|
526
|
+
description: testCase.description,
|
|
527
|
+
tags: {
|
|
528
|
+
connect: [identifierTagId, ...filterTagIds].map(id => ({ id })),
|
|
529
|
+
},
|
|
530
|
+
TestSuite: {
|
|
531
|
+
connect: [{ id: testSuite.id }],
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
include: {
|
|
535
|
+
tags: true,
|
|
536
|
+
},
|
|
537
|
+
})
|
|
653
538
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
}
|
|
539
|
+
const hasIdentifierTag = newTestCase.tags.some(t => t.type === TagType.IDENTIFIER)
|
|
540
|
+
if (!hasIdentifierTag) {
|
|
541
|
+
result.errors.push(
|
|
542
|
+
`Test case '${testCase.title}' was created but identifier tag '${testCase.identifierTag}' was not associated`,
|
|
543
|
+
)
|
|
544
|
+
console.error(
|
|
545
|
+
` ā Test case '${testCase.title}' was created but identifier tag '${testCase.identifierTag}' was not associated`,
|
|
546
|
+
)
|
|
547
|
+
}
|
|
664
548
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
549
|
+
result.testCasesCreated++
|
|
550
|
+
result.createdTestCases.push({
|
|
551
|
+
identifierTag: testCase.identifierTag,
|
|
552
|
+
title: testCase.title,
|
|
553
|
+
})
|
|
554
|
+
console.log(` ā Created test case '${testCase.title}' (${testCase.identifierTag})`)
|
|
671
555
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
} catch (error) {
|
|
676
|
-
const errorMsg = `Error processing test case '${testCase.title}' from ${testCase.filePath}: ${error}`
|
|
677
|
-
result.errors.push(errorMsg)
|
|
678
|
-
console.error(` ā ${errorMsg}`)
|
|
679
|
-
}
|
|
680
|
-
}
|
|
556
|
+
await syncTestCaseSteps(newTestCase.id, testCase.steps, templateSteps, result)
|
|
557
|
+
}
|
|
681
558
|
|
|
682
|
-
|
|
559
|
+
async function deleteOrphanedTestCases(fsTestCaseTags: Set<string>, result: SyncResult): Promise<void> {
|
|
683
560
|
console.log('\nš Checking for orphaned test cases (not in filesystem)...')
|
|
684
561
|
const allDbTestCases = await prisma.testCase.findMany({
|
|
685
562
|
include: {
|
|
686
|
-
tags: true,
|
|
563
|
+
tags: true,
|
|
687
564
|
},
|
|
688
565
|
})
|
|
689
566
|
|
|
@@ -691,56 +568,9 @@ async function syncTestCasesToDatabase(testCasesFromFS: TestCaseFromFS[], result
|
|
|
691
568
|
try {
|
|
692
569
|
const identifierTag = dbTestCase.tags.find(t => t.type === TagType.IDENTIFIER)
|
|
693
570
|
|
|
694
|
-
// Test cases without identifier tags cannot be synced from filesystem
|
|
695
|
-
// and should be deleted as orphaned
|
|
696
571
|
if (!identifierTag) {
|
|
697
572
|
console.log(` ā ļø Test case '${dbTestCase.title}' has no identifier tag - will be deleted as orphaned`)
|
|
698
|
-
|
|
699
|
-
// Delete the test case and all related records in a transaction
|
|
700
|
-
await prisma.$transaction(async tx => {
|
|
701
|
-
// Delete all test run test cases (has RESTRICT constraint, must be deleted first)
|
|
702
|
-
await tx.testRunTestCase.deleteMany({
|
|
703
|
-
where: {
|
|
704
|
-
testCaseId: dbTestCase.id,
|
|
705
|
-
},
|
|
706
|
-
})
|
|
707
|
-
|
|
708
|
-
// Delete all reviews
|
|
709
|
-
await tx.review.deleteMany({
|
|
710
|
-
where: {
|
|
711
|
-
testCaseId: dbTestCase.id,
|
|
712
|
-
},
|
|
713
|
-
})
|
|
714
|
-
|
|
715
|
-
// Delete all linked Jira tickets
|
|
716
|
-
await tx.linkedJiraTicket.deleteMany({
|
|
717
|
-
where: {
|
|
718
|
-
testCaseId: dbTestCase.id,
|
|
719
|
-
},
|
|
720
|
-
})
|
|
721
|
-
|
|
722
|
-
// Delete all step parameters
|
|
723
|
-
await tx.testCaseStepParameter.deleteMany({
|
|
724
|
-
where: {
|
|
725
|
-
testCaseStep: {
|
|
726
|
-
testCaseId: dbTestCase.id,
|
|
727
|
-
},
|
|
728
|
-
},
|
|
729
|
-
})
|
|
730
|
-
|
|
731
|
-
// Delete all test case steps
|
|
732
|
-
await tx.testCaseStep.deleteMany({
|
|
733
|
-
where: {
|
|
734
|
-
testCaseId: dbTestCase.id,
|
|
735
|
-
},
|
|
736
|
-
})
|
|
737
|
-
|
|
738
|
-
// Delete the test case
|
|
739
|
-
await tx.testCase.delete({
|
|
740
|
-
where: { id: dbTestCase.id },
|
|
741
|
-
})
|
|
742
|
-
})
|
|
743
|
-
|
|
573
|
+
await deleteTestCaseWithCascade(dbTestCase.id)
|
|
744
574
|
result.testCasesDeleted++
|
|
745
575
|
result.deletedTestCases.push({
|
|
746
576
|
identifierTag: '(no identifier tag)',
|
|
@@ -750,12 +580,10 @@ async function syncTestCasesToDatabase(testCasesFromFS: TestCaseFromFS[], result
|
|
|
750
580
|
continue
|
|
751
581
|
}
|
|
752
582
|
|
|
753
|
-
// Normalize tagExpression to ensure consistent format comparison
|
|
754
|
-
// fsTestCaseTags contains normalized tags (with @ prefix), so we need to normalize
|
|
755
|
-
// the database tagExpression before comparing
|
|
756
583
|
const identifierTagExpr = normalizeTagExpression(identifierTag.tagExpression)
|
|
584
|
+
// Compare normalized expressions because filesystem tags are always normalized
|
|
585
|
+
// with '@', while historical DB entries may not be.
|
|
757
586
|
if (!fsTestCaseTags.has(identifierTagExpr)) {
|
|
758
|
-
// Check if test case has test runs (for logging)
|
|
759
587
|
const testRunTestCases = await prisma.testRunTestCase.findMany({
|
|
760
588
|
where: { testCaseId: dbTestCase.id },
|
|
761
589
|
})
|
|
@@ -766,72 +594,7 @@ async function syncTestCasesToDatabase(testCasesFromFS: TestCaseFromFS[], result
|
|
|
766
594
|
)
|
|
767
595
|
}
|
|
768
596
|
|
|
769
|
-
|
|
770
|
-
// Following the same pattern as deleteTestCaseAction
|
|
771
|
-
await prisma.$transaction(async tx => {
|
|
772
|
-
// Delete all test run test cases (has RESTRICT constraint, must be deleted first)
|
|
773
|
-
await tx.testRunTestCase.deleteMany({
|
|
774
|
-
where: {
|
|
775
|
-
testCaseId: dbTestCase.id,
|
|
776
|
-
},
|
|
777
|
-
})
|
|
778
|
-
|
|
779
|
-
// Delete all reviews
|
|
780
|
-
await tx.review.deleteMany({
|
|
781
|
-
where: {
|
|
782
|
-
testCaseId: dbTestCase.id,
|
|
783
|
-
},
|
|
784
|
-
})
|
|
785
|
-
|
|
786
|
-
// Delete all linked Jira tickets
|
|
787
|
-
await tx.linkedJiraTicket.deleteMany({
|
|
788
|
-
where: {
|
|
789
|
-
testCaseId: dbTestCase.id,
|
|
790
|
-
},
|
|
791
|
-
})
|
|
792
|
-
|
|
793
|
-
// Delete all step parameters
|
|
794
|
-
await tx.testCaseStepParameter.deleteMany({
|
|
795
|
-
where: {
|
|
796
|
-
testCaseStep: {
|
|
797
|
-
testCaseId: dbTestCase.id,
|
|
798
|
-
},
|
|
799
|
-
},
|
|
800
|
-
})
|
|
801
|
-
|
|
802
|
-
// Delete all test case steps
|
|
803
|
-
await tx.testCaseStep.deleteMany({
|
|
804
|
-
where: {
|
|
805
|
-
testCaseId: dbTestCase.id,
|
|
806
|
-
},
|
|
807
|
-
})
|
|
808
|
-
|
|
809
|
-
// Delete the identifier tag (only if it's not used by other test cases)
|
|
810
|
-
// Check if any other test case uses this tag
|
|
811
|
-
const otherTestCasesWithTag = await tx.testCase.findMany({
|
|
812
|
-
where: {
|
|
813
|
-
tags: {
|
|
814
|
-
some: {
|
|
815
|
-
id: identifierTag.id,
|
|
816
|
-
},
|
|
817
|
-
},
|
|
818
|
-
id: {
|
|
819
|
-
not: dbTestCase.id,
|
|
820
|
-
},
|
|
821
|
-
},
|
|
822
|
-
})
|
|
823
|
-
|
|
824
|
-
if (otherTestCasesWithTag.length === 0) {
|
|
825
|
-
await tx.tag.delete({
|
|
826
|
-
where: { id: identifierTag.id },
|
|
827
|
-
})
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// Delete the test case
|
|
831
|
-
await tx.testCase.delete({
|
|
832
|
-
where: { id: dbTestCase.id },
|
|
833
|
-
})
|
|
834
|
-
})
|
|
597
|
+
await deleteTestCaseWithCascade(dbTestCase.id, identifierTag.id)
|
|
835
598
|
|
|
836
599
|
result.testCasesDeleted++
|
|
837
600
|
result.deletedTestCases.push({
|
|
@@ -849,59 +612,41 @@ async function syncTestCasesToDatabase(testCasesFromFS: TestCaseFromFS[], result
|
|
|
849
612
|
}
|
|
850
613
|
|
|
851
614
|
/**
|
|
852
|
-
*
|
|
615
|
+
* Syncs test cases from filesystem to database
|
|
853
616
|
*/
|
|
854
|
-
function
|
|
855
|
-
console.log('\n
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
console.log('\n Created test cases:')
|
|
866
|
-
result.createdTestCases.forEach((tc, index) => {
|
|
867
|
-
console.log(` ${index + 1}. ${tc.title} (${tc.identifierTag})`)
|
|
868
|
-
})
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
if (result.updatedTestCases.length > 0) {
|
|
872
|
-
console.log('\n Updated test cases:')
|
|
873
|
-
result.updatedTestCases.forEach((tc, index) => {
|
|
874
|
-
console.log(` ${index + 1}. ${tc.title} (${tc.identifierTag})`)
|
|
875
|
-
})
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
if (result.deletedTestCases.length > 0) {
|
|
879
|
-
console.log('\n Deleted test cases:')
|
|
880
|
-
result.deletedTestCases.forEach((tc, index) => {
|
|
881
|
-
console.log(` ${index + 1}. ${tc.title} (${tc.identifierTag})`)
|
|
882
|
-
})
|
|
883
|
-
}
|
|
617
|
+
async function syncTestCasesToDatabase(testCasesFromFS: TestCaseFromFS[], result: SyncResult): Promise<void> {
|
|
618
|
+
console.log('\nā
Syncing test cases to database...')
|
|
619
|
+
// Fixes prior N+1 behavior: template steps are loaded once and reused for
|
|
620
|
+
// every gherkin step match in this sync run.
|
|
621
|
+
const templateSteps = await prisma.templateStep.findMany({
|
|
622
|
+
include: {
|
|
623
|
+
parameters: {
|
|
624
|
+
orderBy: { order: 'asc' },
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
})
|
|
884
628
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
console.log(` ${index + 1}. ${warning}`)
|
|
889
|
-
})
|
|
890
|
-
}
|
|
629
|
+
// Track test cases from filesystem (by identifier tag)
|
|
630
|
+
const fsTestCaseTags = new Set<string>()
|
|
631
|
+
const suitesByModuleId = new Map<string, Array<{ id: string; name: string }>>()
|
|
891
632
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
})
|
|
633
|
+
for (const testCase of testCasesFromFS) {
|
|
634
|
+
try {
|
|
635
|
+
fsTestCaseTags.add(testCase.identifierTag)
|
|
636
|
+
await upsertTestCase(testCase, templateSteps, suitesByModuleId, result)
|
|
637
|
+
} catch (error) {
|
|
638
|
+
const errorMsg = `Error processing test case '${testCase.title}' from ${testCase.filePath}: ${error}`
|
|
639
|
+
result.errors.push(errorMsg)
|
|
640
|
+
console.error(` ā ${errorMsg}`)
|
|
641
|
+
}
|
|
897
642
|
}
|
|
643
|
+
await deleteOrphanedTestCases(fsTestCaseTags, result)
|
|
898
644
|
}
|
|
899
645
|
|
|
900
646
|
/**
|
|
901
|
-
*
|
|
647
|
+
* Generates and displays sync summary
|
|
902
648
|
*/
|
|
903
|
-
async function main() {
|
|
904
|
-
try {
|
|
649
|
+
async function main(): Promise<SyncResult | void> {
|
|
905
650
|
console.log('š Starting test cases sync...')
|
|
906
651
|
console.log('This will scan feature files and sync test cases to database.')
|
|
907
652
|
console.log('Filesystem is the source of truth - test cases in DB but not in FS will be deleted.\n')
|
|
@@ -939,22 +684,34 @@ async function main() {
|
|
|
939
684
|
// Sync to database
|
|
940
685
|
await syncTestCasesToDatabase(testCasesFromFS, result)
|
|
941
686
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
687
|
+
printSyncSummary(
|
|
688
|
+
[
|
|
689
|
+
{ label: 'š Test cases scanned', value: result.testCasesScanned },
|
|
690
|
+
{ label: 'ā
Test cases existing', value: result.testCasesExisting },
|
|
691
|
+
{ label: 'ā Test cases created', value: result.testCasesCreated },
|
|
692
|
+
{ label: 'š Test cases updated', value: result.testCasesUpdated },
|
|
693
|
+
{ label: 'šļø Test cases deleted', value: result.testCasesDeleted },
|
|
694
|
+
{ label: 'ā ļø Warnings', value: result.warnings.length },
|
|
695
|
+
{ label: 'ā Errors', value: result.errors.length },
|
|
696
|
+
],
|
|
697
|
+
[
|
|
698
|
+
{
|
|
699
|
+
title: 'Created test cases',
|
|
700
|
+
items: result.createdTestCases.map(tc => `${tc.title} (${tc.identifierTag})`),
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
title: 'Updated test cases',
|
|
704
|
+
items: result.updatedTestCases.map(tc => `${tc.title} (${tc.identifierTag})`),
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
title: 'Deleted test cases',
|
|
708
|
+
items: result.deletedTestCases.map(tc => `${tc.title} (${tc.identifierTag})`),
|
|
709
|
+
},
|
|
710
|
+
{ title: 'Warnings', items: result.warnings },
|
|
711
|
+
{ title: 'Errors', items: result.errors },
|
|
712
|
+
],
|
|
713
|
+
)
|
|
714
|
+
return result
|
|
957
715
|
}
|
|
958
716
|
|
|
959
|
-
main
|
|
960
|
-
|
|
717
|
+
runSyncScript(main)
|