@camaradesuk/git-worktree-tools 1.10.0 → 1.12.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 +4 -4
- package/dist/lib/config.d.ts +2 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +11 -0
- package/dist/lib/config.js.map +1 -1
- package/package.json +12 -3
- package/schemas/worktreerc.schema.json +1 -1
- package/dist/api/list.test.d.ts +0 -5
- package/dist/api/list.test.d.ts.map +0 -1
- package/dist/api/list.test.js +0 -390
- package/dist/api/list.test.js.map +0 -1
- package/dist/cli/cleanpr.test.d.ts +0 -2
- package/dist/cli/cleanpr.test.d.ts.map +0 -1
- package/dist/cli/cleanpr.test.js +0 -954
- package/dist/cli/cleanpr.test.js.map +0 -1
- package/dist/cli/lswt.test.d.ts +0 -2
- package/dist/cli/lswt.test.d.ts.map +0 -1
- package/dist/cli/lswt.test.js +0 -376
- package/dist/cli/lswt.test.js.map +0 -1
- package/dist/cli/newpr.test.d.ts +0 -2
- package/dist/cli/newpr.test.d.ts.map +0 -1
- package/dist/cli/newpr.test.js +0 -1182
- package/dist/cli/newpr.test.js.map +0 -1
- package/dist/cli/prs.test.d.ts +0 -8
- package/dist/cli/prs.test.d.ts.map +0 -1
- package/dist/cli/prs.test.js +0 -463
- package/dist/cli/prs.test.js.map +0 -1
- package/dist/cli/wt/clean.test.d.ts +0 -8
- package/dist/cli/wt/clean.test.d.ts.map +0 -1
- package/dist/cli/wt/clean.test.js +0 -624
- package/dist/cli/wt/clean.test.js.map +0 -1
- package/dist/cli/wt/completion.test.d.ts +0 -5
- package/dist/cli/wt/completion.test.d.ts.map +0 -1
- package/dist/cli/wt/completion.test.js +0 -275
- package/dist/cli/wt/completion.test.js.map +0 -1
- package/dist/cli/wt/config.test.d.ts +0 -7
- package/dist/cli/wt/config.test.d.ts.map +0 -1
- package/dist/cli/wt/config.test.js +0 -440
- package/dist/cli/wt/config.test.js.map +0 -1
- package/dist/cli/wt/entry.test.d.ts +0 -8
- package/dist/cli/wt/entry.test.d.ts.map +0 -1
- package/dist/cli/wt/entry.test.js +0 -201
- package/dist/cli/wt/entry.test.js.map +0 -1
- package/dist/cli/wt/init.test.d.ts +0 -5
- package/dist/cli/wt/init.test.d.ts.map +0 -1
- package/dist/cli/wt/init.test.js +0 -165
- package/dist/cli/wt/init.test.js.map +0 -1
- package/dist/cli/wt/init.unit.test.d.ts +0 -5
- package/dist/cli/wt/init.unit.test.d.ts.map +0 -1
- package/dist/cli/wt/init.unit.test.js +0 -432
- package/dist/cli/wt/init.unit.test.js.map +0 -1
- package/dist/cli/wt/interactive-menu.test.d.ts +0 -12
- package/dist/cli/wt/interactive-menu.test.d.ts.map +0 -1
- package/dist/cli/wt/interactive-menu.test.js +0 -796
- package/dist/cli/wt/interactive-menu.test.js.map +0 -1
- package/dist/cli/wt/list.test.d.ts +0 -10
- package/dist/cli/wt/list.test.d.ts.map +0 -1
- package/dist/cli/wt/list.test.js +0 -157
- package/dist/cli/wt/list.test.js.map +0 -1
- package/dist/cli/wt/prs.test.d.ts +0 -5
- package/dist/cli/wt/prs.test.d.ts.map +0 -1
- package/dist/cli/wt/prs.test.js +0 -410
- package/dist/cli/wt/prs.test.js.map +0 -1
- package/dist/cli/wt/run-command.test.d.ts +0 -5
- package/dist/cli/wt/run-command.test.d.ts.map +0 -1
- package/dist/cli/wt/run-command.test.js +0 -88
- package/dist/cli/wt/run-command.test.js.map +0 -1
- package/dist/cli/wt/state.test.d.ts +0 -9
- package/dist/cli/wt/state.test.d.ts.map +0 -1
- package/dist/cli/wt/state.test.js +0 -127
- package/dist/cli/wt/state.test.js.map +0 -1
- package/dist/cli/wt/wt.test.d.ts +0 -8
- package/dist/cli/wt/wt.test.d.ts.map +0 -1
- package/dist/cli/wt/wt.test.js +0 -739
- package/dist/cli/wt/wt.test.js.map +0 -1
- package/dist/cli/wt.unit.test.d.ts +0 -7
- package/dist/cli/wt.unit.test.d.ts.map +0 -1
- package/dist/cli/wt.unit.test.js +0 -160
- package/dist/cli/wt.unit.test.js.map +0 -1
- package/dist/cli/wtconfig.test.d.ts +0 -5
- package/dist/cli/wtconfig.test.d.ts.map +0 -1
- package/dist/cli/wtconfig.test.js +0 -1289
- package/dist/cli/wtconfig.test.js.map +0 -1
- package/dist/cli/wtlink.test.d.ts +0 -2
- package/dist/cli/wtlink.test.d.ts.map +0 -1
- package/dist/cli/wtlink.test.js +0 -249
- package/dist/cli/wtlink.test.js.map +0 -1
- package/dist/cli/wtstate.test.d.ts +0 -5
- package/dist/cli/wtstate.test.d.ts.map +0 -1
- package/dist/cli/wtstate.test.js +0 -193
- package/dist/cli/wtstate.test.js.map +0 -1
- package/dist/e2e/cleanpr/cleanpr.e2e.test.d.ts +0 -2
- package/dist/e2e/cleanpr/cleanpr.e2e.test.d.ts.map +0 -1
- package/dist/e2e/cleanpr/cleanpr.e2e.test.js +0 -326
- package/dist/e2e/cleanpr/cleanpr.e2e.test.js.map +0 -1
- package/dist/e2e/cli.e2e.test.d.ts +0 -2
- package/dist/e2e/cli.e2e.test.d.ts.map +0 -1
- package/dist/e2e/cli.e2e.test.js +0 -417
- package/dist/e2e/cli.e2e.test.js.map +0 -1
- package/dist/e2e/lswt/lswt.e2e.test.d.ts +0 -2
- package/dist/e2e/lswt/lswt.e2e.test.d.ts.map +0 -1
- package/dist/e2e/lswt/lswt.e2e.test.js +0 -361
- package/dist/e2e/lswt/lswt.e2e.test.js.map +0 -1
- package/dist/e2e/newpr/newpr.e2e.test.d.ts +0 -2
- package/dist/e2e/newpr/newpr.e2e.test.d.ts.map +0 -1
- package/dist/e2e/newpr/newpr.e2e.test.js +0 -286
- package/dist/e2e/newpr/newpr.e2e.test.js.map +0 -1
- package/dist/e2e/newpr/scenarios.e2e.test.d.ts +0 -2
- package/dist/e2e/newpr/scenarios.e2e.test.d.ts.map +0 -1
- package/dist/e2e/newpr/scenarios.e2e.test.js +0 -426
- package/dist/e2e/newpr/scenarios.e2e.test.js.map +0 -1
- package/dist/e2e/newpr-full-flow.e2e.test.d.ts +0 -2
- package/dist/e2e/newpr-full-flow.e2e.test.d.ts.map +0 -1
- package/dist/e2e/newpr-full-flow.e2e.test.js +0 -280
- package/dist/e2e/newpr-full-flow.e2e.test.js.map +0 -1
- package/dist/e2e/prs/prs.e2e.test.d.ts +0 -7
- package/dist/e2e/prs/prs.e2e.test.d.ts.map +0 -1
- package/dist/e2e/prs/prs.e2e.test.js +0 -606
- package/dist/e2e/prs/prs.e2e.test.js.map +0 -1
- package/dist/e2e/workflows/pr-lifecycle.e2e.test.d.ts +0 -2
- package/dist/e2e/workflows/pr-lifecycle.e2e.test.d.ts.map +0 -1
- package/dist/e2e/workflows/pr-lifecycle.e2e.test.js +0 -298
- package/dist/e2e/workflows/pr-lifecycle.e2e.test.js.map +0 -1
- package/dist/e2e/wt/interactive-menu.e2e.test.d.ts +0 -8
- package/dist/e2e/wt/interactive-menu.e2e.test.d.ts.map +0 -1
- package/dist/e2e/wt/interactive-menu.e2e.test.js +0 -583
- package/dist/e2e/wt/interactive-menu.e2e.test.js.map +0 -1
- package/dist/e2e/wt/wt.e2e.test.d.ts +0 -9
- package/dist/e2e/wt/wt.e2e.test.d.ts.map +0 -1
- package/dist/e2e/wt/wt.e2e.test.js +0 -597
- package/dist/e2e/wt/wt.e2e.test.js.map +0 -1
- package/dist/e2e/wtlink/wtlink.e2e.test.d.ts +0 -2
- package/dist/e2e/wtlink/wtlink.e2e.test.d.ts.map +0 -1
- package/dist/e2e/wtlink/wtlink.e2e.test.js +0 -416
- package/dist/e2e/wtlink/wtlink.e2e.test.js.map +0 -1
- package/dist/integration/git.integration.test.d.ts +0 -2
- package/dist/integration/git.integration.test.d.ts.map +0 -1
- package/dist/integration/git.integration.test.js +0 -336
- package/dist/integration/git.integration.test.js.map +0 -1
- package/dist/integration/lswt-remote-pr.integration.test.d.ts +0 -2
- package/dist/integration/lswt-remote-pr.integration.test.d.ts.map +0 -1
- package/dist/integration/lswt-remote-pr.integration.test.js +0 -222
- package/dist/integration/lswt-remote-pr.integration.test.js.map +0 -1
- package/dist/integration/newpr-branchfrom-head.integration.test.d.ts +0 -2
- package/dist/integration/newpr-branchfrom-head.integration.test.d.ts.map +0 -1
- package/dist/integration/newpr-branchfrom-head.integration.test.js +0 -498
- package/dist/integration/newpr-branchfrom-head.integration.test.js.map +0 -1
- package/dist/integration/newpr.integration.test.d.ts +0 -2
- package/dist/integration/newpr.integration.test.d.ts.map +0 -1
- package/dist/integration/newpr.integration.test.js +0 -460
- package/dist/integration/newpr.integration.test.js.map +0 -1
- package/dist/integration/prs.integration.test.d.ts +0 -8
- package/dist/integration/prs.integration.test.d.ts.map +0 -1
- package/dist/integration/prs.integration.test.js +0 -478
- package/dist/integration/prs.integration.test.js.map +0 -1
- package/dist/lib/ai/base-provider.test.d.ts +0 -7
- package/dist/lib/ai/base-provider.test.d.ts.map +0 -1
- package/dist/lib/ai/base-provider.test.js +0 -319
- package/dist/lib/ai/base-provider.test.js.map +0 -1
- package/dist/lib/ai/cli-provider.test.d.ts +0 -5
- package/dist/lib/ai/cli-provider.test.d.ts.map +0 -1
- package/dist/lib/ai/cli-provider.test.js +0 -460
- package/dist/lib/ai/cli-provider.test.js.map +0 -1
- package/dist/lib/ai/fallback-provider.test.d.ts +0 -7
- package/dist/lib/ai/fallback-provider.test.d.ts.map +0 -1
- package/dist/lib/ai/fallback-provider.test.js +0 -165
- package/dist/lib/ai/fallback-provider.test.js.map +0 -1
- package/dist/lib/ai/generation-service.test.d.ts +0 -7
- package/dist/lib/ai/generation-service.test.d.ts.map +0 -1
- package/dist/lib/ai/generation-service.test.js +0 -213
- package/dist/lib/ai/generation-service.test.js.map +0 -1
- package/dist/lib/ai/provider-manager.test.d.ts +0 -5
- package/dist/lib/ai/provider-manager.test.d.ts.map +0 -1
- package/dist/lib/ai/provider-manager.test.js +0 -312
- package/dist/lib/ai/provider-manager.test.js.map +0 -1
- package/dist/lib/ai/repo-docs.test.d.ts +0 -5
- package/dist/lib/ai/repo-docs.test.d.ts.map +0 -1
- package/dist/lib/ai/repo-docs.test.js +0 -357
- package/dist/lib/ai/repo-docs.test.js.map +0 -1
- package/dist/lib/cleanpr/args.test.d.ts +0 -2
- package/dist/lib/cleanpr/args.test.d.ts.map +0 -1
- package/dist/lib/cleanpr/args.test.js +0 -269
- package/dist/lib/cleanpr/args.test.js.map +0 -1
- package/dist/lib/cleanpr/cleanup.test.d.ts +0 -2
- package/dist/lib/cleanpr/cleanup.test.d.ts.map +0 -1
- package/dist/lib/cleanpr/cleanup.test.js +0 -296
- package/dist/lib/cleanpr/cleanup.test.js.map +0 -1
- package/dist/lib/cleanpr/worktree-info.test.d.ts +0 -2
- package/dist/lib/cleanpr/worktree-info.test.d.ts.map +0 -1
- package/dist/lib/cleanpr/worktree-info.test.js +0 -228
- package/dist/lib/cleanpr/worktree-info.test.js.map +0 -1
- package/dist/lib/colors.test.d.ts +0 -2
- package/dist/lib/colors.test.d.ts.map +0 -1
- package/dist/lib/colors.test.js +0 -142
- package/dist/lib/colors.test.js.map +0 -1
- package/dist/lib/config-editor.test.d.ts +0 -11
- package/dist/lib/config-editor.test.d.ts.map +0 -1
- package/dist/lib/config-editor.test.js +0 -526
- package/dist/lib/config-editor.test.js.map +0 -1
- package/dist/lib/config-migration/detector.test.d.ts +0 -5
- package/dist/lib/config-migration/detector.test.d.ts.map +0 -1
- package/dist/lib/config-migration/detector.test.js +0 -201
- package/dist/lib/config-migration/detector.test.js.map +0 -1
- package/dist/lib/config-migration/reporter.test.d.ts +0 -5
- package/dist/lib/config-migration/reporter.test.d.ts.map +0 -1
- package/dist/lib/config-migration/reporter.test.js +0 -305
- package/dist/lib/config-migration/reporter.test.js.map +0 -1
- package/dist/lib/config-migration/runner.test.d.ts +0 -5
- package/dist/lib/config-migration/runner.test.d.ts.map +0 -1
- package/dist/lib/config-migration/runner.test.js +0 -235
- package/dist/lib/config-migration/runner.test.js.map +0 -1
- package/dist/lib/config-validation.test.d.ts +0 -5
- package/dist/lib/config-validation.test.d.ts.map +0 -1
- package/dist/lib/config-validation.test.js +0 -423
- package/dist/lib/config-validation.test.js.map +0 -1
- package/dist/lib/config.test.d.ts +0 -2
- package/dist/lib/config.test.d.ts.map +0 -1
- package/dist/lib/config.test.js +0 -554
- package/dist/lib/config.test.js.map +0 -1
- package/dist/lib/constants.test.d.ts +0 -5
- package/dist/lib/constants.test.d.ts.map +0 -1
- package/dist/lib/constants.test.js +0 -180
- package/dist/lib/constants.test.js.map +0 -1
- package/dist/lib/deprecation.test.d.ts +0 -2
- package/dist/lib/deprecation.test.d.ts.map +0 -1
- package/dist/lib/deprecation.test.js +0 -71
- package/dist/lib/deprecation.test.js.map +0 -1
- package/dist/lib/errors.test.d.ts +0 -2
- package/dist/lib/errors.test.d.ts.map +0 -1
- package/dist/lib/errors.test.js +0 -117
- package/dist/lib/errors.test.js.map +0 -1
- package/dist/lib/git.test.d.ts +0 -2
- package/dist/lib/git.test.d.ts.map +0 -1
- package/dist/lib/git.test.js +0 -608
- package/dist/lib/git.test.js.map +0 -1
- package/dist/lib/github.test.d.ts +0 -2
- package/dist/lib/github.test.d.ts.map +0 -1
- package/dist/lib/github.test.js +0 -441
- package/dist/lib/github.test.js.map +0 -1
- package/dist/lib/global-check.test.d.ts +0 -5
- package/dist/lib/global-check.test.d.ts.map +0 -1
- package/dist/lib/global-check.test.js +0 -150
- package/dist/lib/global-check.test.js.map +0 -1
- package/dist/lib/global-config.test.d.ts +0 -5
- package/dist/lib/global-config.test.d.ts.map +0 -1
- package/dist/lib/global-config.test.js +0 -282
- package/dist/lib/global-config.test.js.map +0 -1
- package/dist/lib/hooks/confirmation.test.d.ts +0 -7
- package/dist/lib/hooks/confirmation.test.d.ts.map +0 -1
- package/dist/lib/hooks/confirmation.test.js +0 -300
- package/dist/lib/hooks/confirmation.test.js.map +0 -1
- package/dist/lib/hooks/executor.test.d.ts +0 -5
- package/dist/lib/hooks/executor.test.d.ts.map +0 -1
- package/dist/lib/hooks/executor.test.js +0 -648
- package/dist/lib/hooks/executor.test.js.map +0 -1
- package/dist/lib/hooks/templates.test.d.ts +0 -5
- package/dist/lib/hooks/templates.test.d.ts.map +0 -1
- package/dist/lib/hooks/templates.test.js +0 -163
- package/dist/lib/hooks/templates.test.js.map +0 -1
- package/dist/lib/hooks/types.test.d.ts +0 -5
- package/dist/lib/hooks/types.test.d.ts.map +0 -1
- package/dist/lib/hooks/types.test.js +0 -132
- package/dist/lib/hooks/types.test.js.map +0 -1
- package/dist/lib/json-output.test.d.ts +0 -5
- package/dist/lib/json-output.test.d.ts.map +0 -1
- package/dist/lib/json-output.test.js +0 -261
- package/dist/lib/json-output.test.js.map +0 -1
- package/dist/lib/logger.test.d.ts +0 -14
- package/dist/lib/logger.test.d.ts.map +0 -1
- package/dist/lib/logger.test.js +0 -692
- package/dist/lib/logger.test.js.map +0 -1
- package/dist/lib/lswt/action-executors.test.d.ts +0 -2
- package/dist/lib/lswt/action-executors.test.d.ts.map +0 -1
- package/dist/lib/lswt/action-executors.test.js +0 -1127
- package/dist/lib/lswt/action-executors.test.js.map +0 -1
- package/dist/lib/lswt/actions.test.d.ts +0 -2
- package/dist/lib/lswt/actions.test.d.ts.map +0 -1
- package/dist/lib/lswt/actions.test.js +0 -497
- package/dist/lib/lswt/actions.test.js.map +0 -1
- package/dist/lib/lswt/args.test.d.ts +0 -2
- package/dist/lib/lswt/args.test.d.ts.map +0 -1
- package/dist/lib/lswt/args.test.js +0 -195
- package/dist/lib/lswt/args.test.js.map +0 -1
- package/dist/lib/lswt/environment.test.d.ts +0 -2
- package/dist/lib/lswt/environment.test.d.ts.map +0 -1
- package/dist/lib/lswt/environment.test.js +0 -544
- package/dist/lib/lswt/environment.test.js.map +0 -1
- package/dist/lib/lswt/formatters.test.d.ts +0 -2
- package/dist/lib/lswt/formatters.test.d.ts.map +0 -1
- package/dist/lib/lswt/formatters.test.js +0 -323
- package/dist/lib/lswt/formatters.test.js.map +0 -1
- package/dist/lib/lswt/fuzzy-search.test.d.ts +0 -5
- package/dist/lib/lswt/fuzzy-search.test.d.ts.map +0 -1
- package/dist/lib/lswt/fuzzy-search.test.js +0 -207
- package/dist/lib/lswt/fuzzy-search.test.js.map +0 -1
- package/dist/lib/lswt/interactive.test.d.ts +0 -2
- package/dist/lib/lswt/interactive.test.d.ts.map +0 -1
- package/dist/lib/lswt/interactive.test.js +0 -771
- package/dist/lib/lswt/interactive.test.js.map +0 -1
- package/dist/lib/lswt/table.test.d.ts +0 -5
- package/dist/lib/lswt/table.test.d.ts.map +0 -1
- package/dist/lib/lswt/table.test.js +0 -262
- package/dist/lib/lswt/table.test.js.map +0 -1
- package/dist/lib/lswt/worktree-info.test.d.ts +0 -2
- package/dist/lib/lswt/worktree-info.test.d.ts.map +0 -1
- package/dist/lib/lswt/worktree-info.test.js +0 -484
- package/dist/lib/lswt/worktree-info.test.js.map +0 -1
- package/dist/lib/newpr/action-deps.test.d.ts +0 -5
- package/dist/lib/newpr/action-deps.test.d.ts.map +0 -1
- package/dist/lib/newpr/action-deps.test.js +0 -111
- package/dist/lib/newpr/action-deps.test.js.map +0 -1
- package/dist/lib/newpr/actions.test.d.ts +0 -2
- package/dist/lib/newpr/actions.test.d.ts.map +0 -1
- package/dist/lib/newpr/actions.test.js +0 -254
- package/dist/lib/newpr/actions.test.js.map +0 -1
- package/dist/lib/newpr/args.test.d.ts +0 -2
- package/dist/lib/newpr/args.test.d.ts.map +0 -1
- package/dist/lib/newpr/args.test.js +0 -479
- package/dist/lib/newpr/args.test.js.map +0 -1
- package/dist/lib/newpr/hook-runner.test.d.ts +0 -7
- package/dist/lib/newpr/hook-runner.test.d.ts.map +0 -1
- package/dist/lib/newpr/hook-runner.test.js +0 -422
- package/dist/lib/newpr/hook-runner.test.js.map +0 -1
- package/dist/lib/newpr/plan-generator.test.d.ts +0 -7
- package/dist/lib/newpr/plan-generator.test.d.ts.map +0 -1
- package/dist/lib/newpr/plan-generator.test.js +0 -387
- package/dist/lib/newpr/plan-generator.test.js.map +0 -1
- package/dist/lib/newpr/scenario-handler.test.d.ts +0 -2
- package/dist/lib/newpr/scenario-handler.test.d.ts.map +0 -1
- package/dist/lib/newpr/scenario-handler.test.js +0 -256
- package/dist/lib/newpr/scenario-handler.test.js.map +0 -1
- package/dist/lib/prompts.test.d.ts +0 -2
- package/dist/lib/prompts.test.d.ts.map +0 -1
- package/dist/lib/prompts.test.js +0 -807
- package/dist/lib/prompts.test.js.map +0 -1
- package/dist/lib/prs/actions.test.d.ts +0 -5
- package/dist/lib/prs/actions.test.d.ts.map +0 -1
- package/dist/lib/prs/actions.test.js +0 -356
- package/dist/lib/prs/actions.test.js.map +0 -1
- package/dist/lib/prs/command.test.d.ts +0 -11
- package/dist/lib/prs/command.test.d.ts.map +0 -1
- package/dist/lib/prs/command.test.js +0 -409
- package/dist/lib/prs/command.test.js.map +0 -1
- package/dist/lib/prs/data.test.d.ts +0 -5
- package/dist/lib/prs/data.test.d.ts.map +0 -1
- package/dist/lib/prs/data.test.js +0 -417
- package/dist/lib/prs/data.test.js.map +0 -1
- package/dist/lib/prs/details.test.d.ts +0 -5
- package/dist/lib/prs/details.test.d.ts.map +0 -1
- package/dist/lib/prs/details.test.js +0 -325
- package/dist/lib/prs/details.test.js.map +0 -1
- package/dist/lib/prs/filters.test.d.ts +0 -5
- package/dist/lib/prs/filters.test.d.ts.map +0 -1
- package/dist/lib/prs/filters.test.js +0 -312
- package/dist/lib/prs/filters.test.js.map +0 -1
- package/dist/lib/prs/formatters.test.d.ts +0 -2
- package/dist/lib/prs/formatters.test.d.ts.map +0 -1
- package/dist/lib/prs/formatters.test.js +0 -387
- package/dist/lib/prs/formatters.test.js.map +0 -1
- package/dist/lib/prs/interactive.test.d.ts +0 -5
- package/dist/lib/prs/interactive.test.d.ts.map +0 -1
- package/dist/lib/prs/interactive.test.js +0 -517
- package/dist/lib/prs/interactive.test.js.map +0 -1
- package/dist/lib/schema.test.d.ts +0 -10
- package/dist/lib/schema.test.d.ts.map +0 -1
- package/dist/lib/schema.test.js +0 -309
- package/dist/lib/schema.test.js.map +0 -1
- package/dist/lib/state-detection.test.d.ts +0 -2
- package/dist/lib/state-detection.test.d.ts.map +0 -1
- package/dist/lib/state-detection.test.js +0 -451
- package/dist/lib/state-detection.test.js.map +0 -1
- package/dist/lib/ui/error.test.d.ts +0 -2
- package/dist/lib/ui/error.test.d.ts.map +0 -1
- package/dist/lib/ui/error.test.js +0 -143
- package/dist/lib/ui/error.test.js.map +0 -1
- package/dist/lib/ui/output.test.d.ts +0 -2
- package/dist/lib/ui/output.test.d.ts.map +0 -1
- package/dist/lib/ui/output.test.js +0 -59
- package/dist/lib/ui/output.test.js.map +0 -1
- package/dist/lib/ui/status.test.d.ts +0 -2
- package/dist/lib/ui/status.test.d.ts.map +0 -1
- package/dist/lib/ui/status.test.js +0 -158
- package/dist/lib/ui/status.test.js.map +0 -1
- package/dist/lib/ui/table.test.d.ts +0 -2
- package/dist/lib/ui/table.test.d.ts.map +0 -1
- package/dist/lib/ui/table.test.js +0 -115
- package/dist/lib/ui/table.test.js.map +0 -1
- package/dist/lib/ui/theme.test.d.ts +0 -2
- package/dist/lib/ui/theme.test.d.ts.map +0 -1
- package/dist/lib/ui/theme.test.js +0 -76
- package/dist/lib/ui/theme.test.js.map +0 -1
- package/dist/lib/wtconfig/config-manager.test.d.ts +0 -5
- package/dist/lib/wtconfig/config-manager.test.d.ts.map +0 -1
- package/dist/lib/wtconfig/config-manager.test.js +0 -501
- package/dist/lib/wtconfig/config-manager.test.js.map +0 -1
- package/dist/lib/wtconfig/environment.test.d.ts +0 -5
- package/dist/lib/wtconfig/environment.test.d.ts.map +0 -1
- package/dist/lib/wtconfig/environment.test.js +0 -285
- package/dist/lib/wtconfig/environment.test.js.map +0 -1
- package/dist/lib/wtlink/config-manifest.test.d.ts +0 -2
- package/dist/lib/wtlink/config-manifest.test.d.ts.map +0 -1
- package/dist/lib/wtlink/config-manifest.test.js +0 -486
- package/dist/lib/wtlink/config-manifest.test.js.map +0 -1
- package/dist/lib/wtlink/link-configs.test.d.ts +0 -2
- package/dist/lib/wtlink/link-configs.test.d.ts.map +0 -1
- package/dist/lib/wtlink/link-configs.test.js +0 -612
- package/dist/lib/wtlink/link-configs.test.js.map +0 -1
- package/dist/lib/wtlink/main-menu.test.d.ts +0 -5
- package/dist/lib/wtlink/main-menu.test.d.ts.map +0 -1
- package/dist/lib/wtlink/main-menu.test.js +0 -126
- package/dist/lib/wtlink/main-menu.test.js.map +0 -1
- package/dist/lib/wtlink/manage-manifest.test.d.ts +0 -2
- package/dist/lib/wtlink/manage-manifest.test.d.ts.map +0 -1
- package/dist/lib/wtlink/manage-manifest.test.js +0 -714
- package/dist/lib/wtlink/manage-manifest.test.js.map +0 -1
- package/dist/lib/wtlink/validate-manifest.test.d.ts +0 -2
- package/dist/lib/wtlink/validate-manifest.test.d.ts.map +0 -1
- package/dist/lib/wtlink/validate-manifest.test.js +0 -220
- package/dist/lib/wtlink/validate-manifest.test.js.map +0 -1
- package/dist/lib/wtstate/analyze.test.d.ts +0 -5
- package/dist/lib/wtstate/analyze.test.d.ts.map +0 -1
- package/dist/lib/wtstate/analyze.test.js +0 -282
- package/dist/lib/wtstate/analyze.test.js.map +0 -1
- package/dist/lib/wtstate/args.test.d.ts +0 -5
- package/dist/lib/wtstate/args.test.d.ts.map +0 -1
- package/dist/lib/wtstate/args.test.js +0 -120
- package/dist/lib/wtstate/args.test.js.map +0 -1
- package/dist/mcp/server.test.d.ts +0 -9
- package/dist/mcp/server.test.d.ts.map +0 -1
- package/dist/mcp/server.test.js +0 -550
- package/dist/mcp/server.test.js.map +0 -1
|
@@ -1,1127 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { executeAction, createDefaultExecutorDeps, formatBranchAsTitle, } from './action-executors.js';
|
|
3
|
-
// Mock inquirer
|
|
4
|
-
vi.mock('inquirer', () => ({
|
|
5
|
-
default: {
|
|
6
|
-
prompt: vi.fn(),
|
|
7
|
-
},
|
|
8
|
-
}));
|
|
9
|
-
// Mock github
|
|
10
|
-
vi.mock('../github.js', () => ({
|
|
11
|
-
getPr: vi.fn(),
|
|
12
|
-
getPrByBranch: vi.fn(),
|
|
13
|
-
createPr: vi.fn(),
|
|
14
|
-
}));
|
|
15
|
-
// Mock git
|
|
16
|
-
vi.mock('../git.js', () => ({
|
|
17
|
-
getRepoRoot: vi.fn(),
|
|
18
|
-
removeWorktree: vi.fn(),
|
|
19
|
-
getMainWorktreeRoot: vi.fn(),
|
|
20
|
-
addWorktree: vi.fn(),
|
|
21
|
-
deleteBranch: vi.fn(),
|
|
22
|
-
exec: vi.fn(),
|
|
23
|
-
}));
|
|
24
|
-
// Mock child_process for spawn/spawnSync
|
|
25
|
-
vi.mock('child_process', async () => {
|
|
26
|
-
const actual = await vi.importActual('child_process');
|
|
27
|
-
return {
|
|
28
|
-
...actual,
|
|
29
|
-
spawnSync: vi.fn(),
|
|
30
|
-
spawn: actual.spawn,
|
|
31
|
-
};
|
|
32
|
-
});
|
|
33
|
-
import inquirer from 'inquirer';
|
|
34
|
-
import * as github from '../github.js';
|
|
35
|
-
import * as git from '../git.js';
|
|
36
|
-
import { spawnSync } from 'child_process';
|
|
37
|
-
describe('lswt/action-executors', () => {
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
vi.clearAllMocks();
|
|
40
|
-
// Reset spawnSync mock for WSL path conversion
|
|
41
|
-
vi.mocked(spawnSync).mockReturnValue({
|
|
42
|
-
status: 0,
|
|
43
|
-
stdout: '\\\\wsl.localhost\\Ubuntu\\home\\user\\repo',
|
|
44
|
-
stderr: '',
|
|
45
|
-
pid: 0,
|
|
46
|
-
signal: null,
|
|
47
|
-
output: ['', '\\\\wsl.localhost\\Ubuntu\\home\\user\\repo', ''],
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
afterEach(() => {
|
|
51
|
-
vi.restoreAllMocks();
|
|
52
|
-
});
|
|
53
|
-
const makeWorktree = (overrides = {}) => ({
|
|
54
|
-
path: '/home/user/repo',
|
|
55
|
-
name: 'repo',
|
|
56
|
-
branch: 'main',
|
|
57
|
-
commit: 'abc123',
|
|
58
|
-
type: 'main',
|
|
59
|
-
prNumber: null,
|
|
60
|
-
prState: null,
|
|
61
|
-
isDraft: null,
|
|
62
|
-
hasChanges: false,
|
|
63
|
-
...overrides,
|
|
64
|
-
});
|
|
65
|
-
const makeEnv = (overrides = {}) => ({
|
|
66
|
-
hasVscode: true,
|
|
67
|
-
hasCursor: false,
|
|
68
|
-
defaultEditor: 'vscode',
|
|
69
|
-
platform: 'linux',
|
|
70
|
-
isInteractive: true,
|
|
71
|
-
shell: '/bin/bash',
|
|
72
|
-
gitVersion: { major: 2, minor: 39, patch: 0, raw: 'git version 2.39.0' },
|
|
73
|
-
isWSL: false,
|
|
74
|
-
...overrides,
|
|
75
|
-
});
|
|
76
|
-
const makeConfig = (overrides = {}) => ({
|
|
77
|
-
baseBranch: 'main',
|
|
78
|
-
worktreePattern: '{repo}.pr{number}',
|
|
79
|
-
worktreeParent: '..',
|
|
80
|
-
draftPr: false,
|
|
81
|
-
sharedRepos: [],
|
|
82
|
-
branchPrefix: 'feature',
|
|
83
|
-
previewLabel: 'preview',
|
|
84
|
-
syncPatterns: [],
|
|
85
|
-
preferredEditor: 'auto',
|
|
86
|
-
ai: { provider: 'none' },
|
|
87
|
-
hooks: {},
|
|
88
|
-
hookDefaults: { timeout: 30000, maxTimeout: 60000 },
|
|
89
|
-
plugins: [],
|
|
90
|
-
generators: {},
|
|
91
|
-
integrations: {},
|
|
92
|
-
logging: { level: 'info', timestamps: true },
|
|
93
|
-
global: { warnNotGlobal: true },
|
|
94
|
-
...overrides,
|
|
95
|
-
});
|
|
96
|
-
const makeDeps = (overrides = {}) => ({
|
|
97
|
-
execCommand: vi.fn(),
|
|
98
|
-
spawnDetached: vi.fn(),
|
|
99
|
-
copyToClipboard: vi.fn(),
|
|
100
|
-
openUrl: vi.fn(),
|
|
101
|
-
wslPathToWindows: vi.fn().mockReturnValue('\\\\wsl.localhost\\Ubuntu\\home\\user\\repo'),
|
|
102
|
-
...overrides,
|
|
103
|
-
});
|
|
104
|
-
describe('createDefaultExecutorDeps', () => {
|
|
105
|
-
it('returns an object with all required methods', () => {
|
|
106
|
-
const deps = createDefaultExecutorDeps();
|
|
107
|
-
expect(deps).toHaveProperty('execCommand');
|
|
108
|
-
expect(deps).toHaveProperty('spawnDetached');
|
|
109
|
-
expect(deps).toHaveProperty('copyToClipboard');
|
|
110
|
-
expect(deps).toHaveProperty('openUrl');
|
|
111
|
-
expect(deps).toHaveProperty('wslPathToWindows');
|
|
112
|
-
expect(typeof deps.execCommand).toBe('function');
|
|
113
|
-
expect(typeof deps.spawnDetached).toBe('function');
|
|
114
|
-
expect(typeof deps.copyToClipboard).toBe('function');
|
|
115
|
-
expect(typeof deps.openUrl).toBe('function');
|
|
116
|
-
expect(typeof deps.wslPathToWindows).toBe('function');
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
describe('executeAction', () => {
|
|
120
|
-
describe('back action', () => {
|
|
121
|
-
it('returns success with no message', async () => {
|
|
122
|
-
const result = await executeAction('back', makeWorktree(), makeEnv(), makeConfig());
|
|
123
|
-
expect(result).toEqual({ success: true });
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
describe('exit action', () => {
|
|
127
|
-
it('returns success with shouldExit true', async () => {
|
|
128
|
-
const result = await executeAction('exit', makeWorktree(), makeEnv(), makeConfig());
|
|
129
|
-
expect(result).toEqual({ success: true, shouldExit: true });
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
describe('open_editor action', () => {
|
|
133
|
-
it('spawns VSCode when preferredEditor is vscode', async () => {
|
|
134
|
-
const deps = makeDeps();
|
|
135
|
-
const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
|
|
136
|
-
const env = makeEnv({ hasVscode: true, defaultEditor: 'vscode' });
|
|
137
|
-
const config = makeConfig({ preferredEditor: 'vscode' });
|
|
138
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
139
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('code', ['/home/user/repo.pr1']);
|
|
140
|
-
expect(result.success).toBe(true);
|
|
141
|
-
expect(result.message).toContain('VSCode');
|
|
142
|
-
});
|
|
143
|
-
it('spawns Cursor when preferredEditor is cursor', async () => {
|
|
144
|
-
const deps = makeDeps();
|
|
145
|
-
const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
|
|
146
|
-
const env = makeEnv({ hasCursor: true, defaultEditor: 'cursor' });
|
|
147
|
-
const config = makeConfig({ preferredEditor: 'cursor' });
|
|
148
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
149
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('cursor', ['/home/user/repo.pr1']);
|
|
150
|
-
expect(result.success).toBe(true);
|
|
151
|
-
expect(result.message).toContain('Cursor');
|
|
152
|
-
});
|
|
153
|
-
it('uses VSCode when preferredEditor is auto and VSCode available', async () => {
|
|
154
|
-
const deps = makeDeps();
|
|
155
|
-
const worktree = makeWorktree();
|
|
156
|
-
const env = makeEnv({ hasVscode: true, hasCursor: false, defaultEditor: 'vscode' });
|
|
157
|
-
const config = makeConfig({ preferredEditor: 'auto' });
|
|
158
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
159
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('code', expect.any(Array));
|
|
160
|
-
expect(result.success).toBe(true);
|
|
161
|
-
});
|
|
162
|
-
it('uses Cursor when preferredEditor is auto and only Cursor available', async () => {
|
|
163
|
-
const deps = makeDeps();
|
|
164
|
-
const worktree = makeWorktree();
|
|
165
|
-
const env = makeEnv({ hasVscode: false, hasCursor: true, defaultEditor: 'cursor' });
|
|
166
|
-
const config = makeConfig({ preferredEditor: 'auto' });
|
|
167
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
168
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('cursor', expect.any(Array));
|
|
169
|
-
expect(result.success).toBe(true);
|
|
170
|
-
});
|
|
171
|
-
it('returns error when no editor is available', async () => {
|
|
172
|
-
const deps = makeDeps();
|
|
173
|
-
const worktree = makeWorktree();
|
|
174
|
-
const env = makeEnv({ hasVscode: false, hasCursor: false, defaultEditor: null });
|
|
175
|
-
const config = makeConfig({ preferredEditor: 'auto' });
|
|
176
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
177
|
-
expect(result.success).toBe(false);
|
|
178
|
-
expect(result.message).toContain('No editor found');
|
|
179
|
-
expect(deps.spawnDetached).not.toHaveBeenCalled();
|
|
180
|
-
});
|
|
181
|
-
it('handles spawn errors gracefully', async () => {
|
|
182
|
-
const deps = makeDeps({
|
|
183
|
-
spawnDetached: vi.fn().mockImplementation(() => {
|
|
184
|
-
throw new Error('spawn failed');
|
|
185
|
-
}),
|
|
186
|
-
});
|
|
187
|
-
const worktree = makeWorktree();
|
|
188
|
-
const env = makeEnv({ hasVscode: true, defaultEditor: 'vscode' });
|
|
189
|
-
const config = makeConfig();
|
|
190
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
191
|
-
expect(result.success).toBe(false);
|
|
192
|
-
expect(result.message).toContain('Failed to open editor');
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
describe('copy_path action', () => {
|
|
196
|
-
it('copies path to clipboard', async () => {
|
|
197
|
-
const deps = makeDeps();
|
|
198
|
-
const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
|
|
199
|
-
const result = await executeAction('copy_path', worktree, makeEnv(), makeConfig(), deps);
|
|
200
|
-
expect(deps.copyToClipboard).toHaveBeenCalledWith('/home/user/repo.pr1');
|
|
201
|
-
expect(result.success).toBe(true);
|
|
202
|
-
expect(result.message).toContain('Copied');
|
|
203
|
-
expect(result.message).toContain('/home/user/repo.pr1');
|
|
204
|
-
});
|
|
205
|
-
it('handles clipboard errors gracefully', async () => {
|
|
206
|
-
const deps = makeDeps({
|
|
207
|
-
copyToClipboard: vi.fn().mockImplementation(() => {
|
|
208
|
-
throw new Error('clipboard unavailable');
|
|
209
|
-
}),
|
|
210
|
-
});
|
|
211
|
-
const worktree = makeWorktree();
|
|
212
|
-
const result = await executeAction('copy_path', worktree, makeEnv(), makeConfig(), deps);
|
|
213
|
-
expect(result.success).toBe(false);
|
|
214
|
-
expect(result.message).toContain('Failed to copy');
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
describe('show_details action', () => {
|
|
218
|
-
it('returns success (details are printed to console)', async () => {
|
|
219
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
220
|
-
const worktree = makeWorktree({
|
|
221
|
-
type: 'pr',
|
|
222
|
-
prNumber: 42,
|
|
223
|
-
prState: 'OPEN',
|
|
224
|
-
branch: 'feature-42',
|
|
225
|
-
});
|
|
226
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
227
|
-
expect(result.success).toBe(true);
|
|
228
|
-
consoleSpy.mockRestore();
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
describe('open_pr_url action', () => {
|
|
232
|
-
it('returns error when worktree has no PR number', async () => {
|
|
233
|
-
const deps = makeDeps();
|
|
234
|
-
const worktree = makeWorktree({ type: 'branch', prNumber: null });
|
|
235
|
-
const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
|
|
236
|
-
expect(result.success).toBe(false);
|
|
237
|
-
expect(result.message).toContain('No PR associated');
|
|
238
|
-
expect(deps.openUrl).not.toHaveBeenCalled();
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
describe('createDefaultExecutorDeps', () => {
|
|
243
|
-
it('returns object with all required methods', () => {
|
|
244
|
-
const deps = createDefaultExecutorDeps();
|
|
245
|
-
expect(deps).toHaveProperty('execCommand');
|
|
246
|
-
expect(deps).toHaveProperty('spawnDetached');
|
|
247
|
-
expect(deps).toHaveProperty('copyToClipboard');
|
|
248
|
-
expect(deps).toHaveProperty('openUrl');
|
|
249
|
-
expect(deps).toHaveProperty('wslPathToWindows');
|
|
250
|
-
expect(typeof deps.execCommand).toBe('function');
|
|
251
|
-
expect(typeof deps.spawnDetached).toBe('function');
|
|
252
|
-
expect(typeof deps.copyToClipboard).toBe('function');
|
|
253
|
-
expect(typeof deps.openUrl).toBe('function');
|
|
254
|
-
expect(typeof deps.wslPathToWindows).toBe('function');
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
describe('open_terminal action', () => {
|
|
258
|
-
it('spawns terminal on Linux', async () => {
|
|
259
|
-
const deps = makeDeps();
|
|
260
|
-
const worktree = makeWorktree({ path: '/home/user/repo' });
|
|
261
|
-
const env = makeEnv({ platform: 'linux' });
|
|
262
|
-
const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
|
|
263
|
-
// Should attempt to spawn a terminal
|
|
264
|
-
expect(deps.spawnDetached).toHaveBeenCalled();
|
|
265
|
-
expect(result.success).toBe(true);
|
|
266
|
-
expect(result.message).toContain('terminal');
|
|
267
|
-
});
|
|
268
|
-
it('uses Windows Terminal via cmd.exe in WSL', async () => {
|
|
269
|
-
const deps = makeDeps();
|
|
270
|
-
const worktree = makeWorktree({ path: '/home/user/repo' });
|
|
271
|
-
const env = makeEnv({ platform: 'linux', isWSL: true });
|
|
272
|
-
const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
|
|
273
|
-
// Should call deps.wslPathToWindows to convert path
|
|
274
|
-
expect(deps.wslPathToWindows).toHaveBeenCalledWith('/home/user/repo');
|
|
275
|
-
// Should try to use cmd.exe to launch Windows Terminal
|
|
276
|
-
expect(deps.execCommand).toHaveBeenCalledWith(expect.stringContaining('cmd.exe'));
|
|
277
|
-
expect(result.success).toBe(true);
|
|
278
|
-
});
|
|
279
|
-
it('shows cd command fallback when WSL Windows Terminal fails', async () => {
|
|
280
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
281
|
-
const deps = makeDeps({
|
|
282
|
-
execCommand: vi.fn().mockImplementation(() => {
|
|
283
|
-
throw new Error('cmd.exe failed');
|
|
284
|
-
}),
|
|
285
|
-
});
|
|
286
|
-
const worktree = makeWorktree({ path: '/home/user/repo' });
|
|
287
|
-
const env = makeEnv({ platform: 'linux', isWSL: true });
|
|
288
|
-
const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
|
|
289
|
-
// Should fall back to showing cd command
|
|
290
|
-
expect(result.success).toBe(true);
|
|
291
|
-
expect(result.message).toContain('copy the cd command');
|
|
292
|
-
// Should print cd command to console
|
|
293
|
-
const output = consoleSpy.mock.calls.map((c) => String(c[0])).join('\n');
|
|
294
|
-
expect(output).toContain('cd');
|
|
295
|
-
consoleSpy.mockRestore();
|
|
296
|
-
});
|
|
297
|
-
it('uses osascript on macOS', async () => {
|
|
298
|
-
const deps = makeDeps();
|
|
299
|
-
const worktree = makeWorktree({ path: '/Users/user/repo' });
|
|
300
|
-
const env = makeEnv({ platform: 'darwin' });
|
|
301
|
-
const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
|
|
302
|
-
expect(deps.execCommand).toHaveBeenCalledWith(expect.stringContaining('osascript'));
|
|
303
|
-
expect(result.success).toBe(true);
|
|
304
|
-
});
|
|
305
|
-
it('tries Windows Terminal on Windows', async () => {
|
|
306
|
-
const deps = makeDeps();
|
|
307
|
-
const worktree = makeWorktree({ path: 'C:\\Users\\user\\repo' });
|
|
308
|
-
const env = makeEnv({ platform: 'win32' });
|
|
309
|
-
const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
|
|
310
|
-
// Should try wt first
|
|
311
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('wt', expect.any(Array));
|
|
312
|
-
expect(result.success).toBe(true);
|
|
313
|
-
});
|
|
314
|
-
it('falls back to cmd on Windows when wt fails', async () => {
|
|
315
|
-
let callCount = 0;
|
|
316
|
-
const deps = makeDeps({
|
|
317
|
-
spawnDetached: vi.fn().mockImplementation((cmd) => {
|
|
318
|
-
callCount++;
|
|
319
|
-
if (cmd === 'wt') {
|
|
320
|
-
throw new Error('wt not found');
|
|
321
|
-
}
|
|
322
|
-
}),
|
|
323
|
-
});
|
|
324
|
-
const worktree = makeWorktree({ path: 'C:\\Users\\user\\repo' });
|
|
325
|
-
const env = makeEnv({ platform: 'win32' });
|
|
326
|
-
const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
|
|
327
|
-
// Should have tried wt, then cmd
|
|
328
|
-
expect(callCount).toBe(2);
|
|
329
|
-
expect(result.success).toBe(true);
|
|
330
|
-
});
|
|
331
|
-
it('handles no terminal available on Linux', async () => {
|
|
332
|
-
const deps = makeDeps({
|
|
333
|
-
spawnDetached: vi.fn().mockImplementation(() => {
|
|
334
|
-
throw new Error('terminal not found');
|
|
335
|
-
}),
|
|
336
|
-
});
|
|
337
|
-
const worktree = makeWorktree();
|
|
338
|
-
const env = makeEnv({ platform: 'linux' });
|
|
339
|
-
const result = await executeAction('open_terminal', worktree, env, makeConfig(), deps);
|
|
340
|
-
expect(result.success).toBe(false);
|
|
341
|
-
expect(result.message).toContain('No terminal emulator found');
|
|
342
|
-
});
|
|
343
|
-
});
|
|
344
|
-
describe('remove_worktree action', () => {
|
|
345
|
-
it('returns error for main worktree', async () => {
|
|
346
|
-
const worktree = makeWorktree({ type: 'main' });
|
|
347
|
-
const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
348
|
-
expect(result.success).toBe(false);
|
|
349
|
-
expect(result.message).toContain('Cannot remove main worktree');
|
|
350
|
-
});
|
|
351
|
-
it('cancels when user declines confirmation', async () => {
|
|
352
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: false });
|
|
353
|
-
const worktree = makeWorktree({
|
|
354
|
-
type: 'branch',
|
|
355
|
-
branch: 'feature-branch',
|
|
356
|
-
name: 'feature-branch',
|
|
357
|
-
});
|
|
358
|
-
const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
359
|
-
expect(result.success).toBe(true);
|
|
360
|
-
expect(result.message).toBe('Cancelled');
|
|
361
|
-
expect(git.removeWorktree).not.toHaveBeenCalled();
|
|
362
|
-
});
|
|
363
|
-
it('removes worktree when user confirms', async () => {
|
|
364
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
365
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: true });
|
|
366
|
-
vi.mocked(git.removeWorktree).mockImplementation(() => { });
|
|
367
|
-
const worktree = makeWorktree({
|
|
368
|
-
type: 'branch',
|
|
369
|
-
branch: 'feature-branch',
|
|
370
|
-
name: 'my-worktree',
|
|
371
|
-
});
|
|
372
|
-
const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
373
|
-
expect(result.success).toBe(true);
|
|
374
|
-
expect(result.message).toContain('Removed worktree');
|
|
375
|
-
expect(result.shouldRefresh).toBe(true);
|
|
376
|
-
expect(git.removeWorktree).toHaveBeenCalledWith(worktree.path);
|
|
377
|
-
consoleSpy.mockRestore();
|
|
378
|
-
});
|
|
379
|
-
it('warns about uncommitted changes', async () => {
|
|
380
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
381
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: false });
|
|
382
|
-
const worktree = makeWorktree({
|
|
383
|
-
type: 'branch',
|
|
384
|
-
branch: 'dirty-branch',
|
|
385
|
-
hasChanges: true,
|
|
386
|
-
});
|
|
387
|
-
await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
388
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
389
|
-
expect(output).toContain('uncommitted changes');
|
|
390
|
-
consoleSpy.mockRestore();
|
|
391
|
-
});
|
|
392
|
-
it('prompts to delete branch for merged PR worktrees', async () => {
|
|
393
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
394
|
-
vi.mocked(inquirer.prompt)
|
|
395
|
-
.mockResolvedValueOnce({ confirm: true })
|
|
396
|
-
.mockResolvedValueOnce({ shouldDelete: true });
|
|
397
|
-
vi.mocked(git.removeWorktree).mockImplementation(() => { });
|
|
398
|
-
vi.mocked(git.getMainWorktreeRoot).mockReturnValue('/home/user/repo');
|
|
399
|
-
const worktree = makeWorktree({
|
|
400
|
-
type: 'pr',
|
|
401
|
-
prNumber: 42,
|
|
402
|
-
prState: 'MERGED',
|
|
403
|
-
branch: 'feature-42',
|
|
404
|
-
name: 'repo.pr42',
|
|
405
|
-
});
|
|
406
|
-
const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
407
|
-
expect(result.success).toBe(true);
|
|
408
|
-
expect(inquirer.prompt).toHaveBeenCalledTimes(2);
|
|
409
|
-
consoleSpy.mockRestore();
|
|
410
|
-
});
|
|
411
|
-
it('prompts to delete branch for closed PR worktrees', async () => {
|
|
412
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
413
|
-
vi.mocked(inquirer.prompt)
|
|
414
|
-
.mockResolvedValueOnce({ confirm: true })
|
|
415
|
-
.mockResolvedValueOnce({ shouldDelete: false });
|
|
416
|
-
vi.mocked(git.removeWorktree).mockImplementation(() => { });
|
|
417
|
-
const worktree = makeWorktree({
|
|
418
|
-
type: 'pr',
|
|
419
|
-
prNumber: 42,
|
|
420
|
-
prState: 'CLOSED',
|
|
421
|
-
branch: 'feature-42',
|
|
422
|
-
name: 'repo.pr42',
|
|
423
|
-
});
|
|
424
|
-
const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
425
|
-
expect(result.success).toBe(true);
|
|
426
|
-
expect(inquirer.prompt).toHaveBeenCalledTimes(2);
|
|
427
|
-
consoleSpy.mockRestore();
|
|
428
|
-
});
|
|
429
|
-
it('continues successfully when branch deletion fails', async () => {
|
|
430
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
431
|
-
vi.mocked(inquirer.prompt)
|
|
432
|
-
.mockResolvedValueOnce({ confirm: true })
|
|
433
|
-
.mockResolvedValueOnce({ shouldDelete: true });
|
|
434
|
-
vi.mocked(git.removeWorktree).mockImplementation(() => { });
|
|
435
|
-
vi.mocked(git.getMainWorktreeRoot).mockReturnValue('/home/user/repo');
|
|
436
|
-
// Branch deletion fails (branch might not exist locally)
|
|
437
|
-
vi.mocked(git.deleteBranch).mockImplementation(() => {
|
|
438
|
-
throw new Error('Branch not found');
|
|
439
|
-
});
|
|
440
|
-
const worktree = makeWorktree({
|
|
441
|
-
type: 'pr',
|
|
442
|
-
prNumber: 42,
|
|
443
|
-
prState: 'MERGED',
|
|
444
|
-
branch: 'feature-42',
|
|
445
|
-
name: 'repo.pr42',
|
|
446
|
-
});
|
|
447
|
-
const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
448
|
-
// Should still succeed even though branch deletion failed
|
|
449
|
-
expect(result.success).toBe(true);
|
|
450
|
-
expect(result.message).toContain('Removed worktree');
|
|
451
|
-
consoleSpy.mockRestore();
|
|
452
|
-
});
|
|
453
|
-
it('handles removal failure', async () => {
|
|
454
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: true });
|
|
455
|
-
vi.mocked(git.removeWorktree).mockImplementation(() => {
|
|
456
|
-
throw new Error('Worktree has changes');
|
|
457
|
-
});
|
|
458
|
-
const worktree = makeWorktree({
|
|
459
|
-
type: 'branch',
|
|
460
|
-
branch: 'feature-branch',
|
|
461
|
-
name: 'my-worktree',
|
|
462
|
-
});
|
|
463
|
-
const result = await executeAction('remove_worktree', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
464
|
-
expect(result.success).toBe(false);
|
|
465
|
-
expect(result.message).toContain('Failed to remove worktree');
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
describe('create_pr action', () => {
|
|
469
|
-
it('returns error for detached HEAD', async () => {
|
|
470
|
-
const worktree = makeWorktree({ type: 'detached', branch: null });
|
|
471
|
-
const result = await executeAction('create_pr', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
472
|
-
expect(result.success).toBe(false);
|
|
473
|
-
expect(result.message).toContain('detached HEAD');
|
|
474
|
-
});
|
|
475
|
-
it('returns error when PR already exists for branch', async () => {
|
|
476
|
-
vi.mocked(github.getPrByBranch).mockReturnValue({
|
|
477
|
-
number: 42,
|
|
478
|
-
url: 'https://github.com/owner/repo/pull/42',
|
|
479
|
-
state: 'OPEN',
|
|
480
|
-
isDraft: false,
|
|
481
|
-
title: 'Existing PR',
|
|
482
|
-
headBranch: 'feature-branch',
|
|
483
|
-
baseBranch: 'main',
|
|
484
|
-
});
|
|
485
|
-
const worktree = makeWorktree({
|
|
486
|
-
type: 'branch',
|
|
487
|
-
branch: 'feature-branch',
|
|
488
|
-
});
|
|
489
|
-
const result = await executeAction('create_pr', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
490
|
-
expect(result.success).toBe(false);
|
|
491
|
-
expect(result.message).toContain('PR already exists');
|
|
492
|
-
expect(result.message).toContain('#42');
|
|
493
|
-
});
|
|
494
|
-
it('creates PR successfully with configured draftPr', async () => {
|
|
495
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
496
|
-
vi.mocked(github.getPrByBranch).mockReturnValue(null);
|
|
497
|
-
vi.mocked(github.createPr).mockReturnValue({
|
|
498
|
-
number: 123,
|
|
499
|
-
url: 'https://github.com/owner/repo/pull/123',
|
|
500
|
-
state: 'OPEN',
|
|
501
|
-
isDraft: true,
|
|
502
|
-
title: 'New PR',
|
|
503
|
-
headBranch: 'feature-branch',
|
|
504
|
-
baseBranch: 'main',
|
|
505
|
-
});
|
|
506
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ title: 'My New PR' });
|
|
507
|
-
const worktree = makeWorktree({
|
|
508
|
-
type: 'branch',
|
|
509
|
-
branch: 'feature-branch',
|
|
510
|
-
});
|
|
511
|
-
const config = makeConfig({ draftPr: true });
|
|
512
|
-
const result = await executeAction('create_pr', worktree, makeEnv(), config, makeDeps());
|
|
513
|
-
expect(result.success).toBe(true);
|
|
514
|
-
expect(result.message).toContain('Created PR #123');
|
|
515
|
-
expect(result.shouldRefresh).toBe(true);
|
|
516
|
-
expect(github.createPr).toHaveBeenCalledWith(expect.objectContaining({ draft: true }), expect.any(String));
|
|
517
|
-
consoleSpy.mockRestore();
|
|
518
|
-
});
|
|
519
|
-
it('prompts for draft status when not configured', async () => {
|
|
520
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
521
|
-
vi.mocked(github.getPrByBranch).mockReturnValue(null);
|
|
522
|
-
vi.mocked(github.createPr).mockReturnValue({
|
|
523
|
-
number: 123,
|
|
524
|
-
url: 'https://github.com/owner/repo/pull/123',
|
|
525
|
-
state: 'OPEN',
|
|
526
|
-
isDraft: false,
|
|
527
|
-
title: 'New PR',
|
|
528
|
-
headBranch: 'feature-branch',
|
|
529
|
-
baseBranch: 'main',
|
|
530
|
-
});
|
|
531
|
-
vi.mocked(inquirer.prompt)
|
|
532
|
-
.mockResolvedValueOnce({ title: 'My PR Title' })
|
|
533
|
-
.mockResolvedValueOnce({ draft: false });
|
|
534
|
-
const worktree = makeWorktree({
|
|
535
|
-
type: 'branch',
|
|
536
|
-
branch: 'feature-branch',
|
|
537
|
-
});
|
|
538
|
-
const config = makeConfig({ draftPr: undefined });
|
|
539
|
-
const result = await executeAction('create_pr', worktree, makeEnv(), config, makeDeps());
|
|
540
|
-
expect(result.success).toBe(true);
|
|
541
|
-
expect(inquirer.prompt).toHaveBeenCalledTimes(2);
|
|
542
|
-
consoleSpy.mockRestore();
|
|
543
|
-
});
|
|
544
|
-
it('handles PR creation failure', async () => {
|
|
545
|
-
vi.mocked(github.getPrByBranch).mockReturnValue(null);
|
|
546
|
-
vi.mocked(github.createPr).mockImplementation(() => {
|
|
547
|
-
throw new Error('GitHub API error');
|
|
548
|
-
});
|
|
549
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ title: 'My PR' });
|
|
550
|
-
const worktree = makeWorktree({
|
|
551
|
-
type: 'branch',
|
|
552
|
-
branch: 'feature-branch',
|
|
553
|
-
});
|
|
554
|
-
const config = makeConfig({ draftPr: false });
|
|
555
|
-
const result = await executeAction('create_pr', worktree, makeEnv(), config, makeDeps());
|
|
556
|
-
expect(result.success).toBe(false);
|
|
557
|
-
expect(result.message).toContain('Failed to create PR');
|
|
558
|
-
});
|
|
559
|
-
});
|
|
560
|
-
describe('link_configs action', () => {
|
|
561
|
-
it('returns error when repo root not found', async () => {
|
|
562
|
-
vi.mocked(git.getRepoRoot).mockReturnValue(null);
|
|
563
|
-
const worktree = makeWorktree({
|
|
564
|
-
type: 'branch',
|
|
565
|
-
branch: 'feature-branch',
|
|
566
|
-
});
|
|
567
|
-
const result = await executeAction('link_configs', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
568
|
-
expect(result.success).toBe(false);
|
|
569
|
-
expect(result.message).toContain('Could not find repository root');
|
|
570
|
-
});
|
|
571
|
-
it('returns error for main worktree', async () => {
|
|
572
|
-
vi.mocked(git.getRepoRoot).mockReturnValue('/home/user/repo');
|
|
573
|
-
const worktree = makeWorktree({ type: 'main' });
|
|
574
|
-
const result = await executeAction('link_configs', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
575
|
-
expect(result.success).toBe(false);
|
|
576
|
-
expect(result.message).toContain('Cannot link configs to main worktree');
|
|
577
|
-
});
|
|
578
|
-
it('handles link configs failure', async () => {
|
|
579
|
-
vi.mocked(git.getRepoRoot).mockReturnValue('/home/user/repo');
|
|
580
|
-
// Mock the dynamic import to throw
|
|
581
|
-
vi.doMock('../wtlink/link-configs.js', () => ({
|
|
582
|
-
run: vi.fn().mockRejectedValue(new Error('Link failed')),
|
|
583
|
-
}));
|
|
584
|
-
const worktree = makeWorktree({
|
|
585
|
-
type: 'branch',
|
|
586
|
-
branch: 'feature-branch',
|
|
587
|
-
path: '/home/user/feature-branch',
|
|
588
|
-
});
|
|
589
|
-
const result = await executeAction('link_configs', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
590
|
-
// Either succeeds or fails with proper message
|
|
591
|
-
if (!result.success) {
|
|
592
|
-
expect(result.message).toContain('Failed to link configs');
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
it('successfully links configs', async () => {
|
|
596
|
-
vi.mocked(git.getRepoRoot).mockReturnValue('/home/user/repo');
|
|
597
|
-
// Mock the dynamic import to succeed
|
|
598
|
-
vi.doMock('../wtlink/link-configs.js', () => ({
|
|
599
|
-
run: vi.fn().mockResolvedValue(undefined),
|
|
600
|
-
}));
|
|
601
|
-
const worktree = makeWorktree({
|
|
602
|
-
type: 'branch',
|
|
603
|
-
branch: 'feature-branch',
|
|
604
|
-
path: '/home/user/feature-branch',
|
|
605
|
-
});
|
|
606
|
-
const result = await executeAction('link_configs', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
607
|
-
// Should succeed
|
|
608
|
-
expect(result.success).toBe(true);
|
|
609
|
-
expect(result.message).toContain('linked successfully');
|
|
610
|
-
});
|
|
611
|
-
});
|
|
612
|
-
describe('show_details action', () => {
|
|
613
|
-
it('shows draft indicator for draft PRs', async () => {
|
|
614
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
615
|
-
const worktree = makeWorktree({
|
|
616
|
-
type: 'pr',
|
|
617
|
-
prNumber: 42,
|
|
618
|
-
prState: 'OPEN',
|
|
619
|
-
isDraft: true,
|
|
620
|
-
branch: 'feature-42',
|
|
621
|
-
});
|
|
622
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
623
|
-
expect(result.success).toBe(true);
|
|
624
|
-
// Check that draft was mentioned in output
|
|
625
|
-
const calls = consoleSpy.mock.calls.map((call) => String(call[0]));
|
|
626
|
-
expect(calls.some((c) => c.includes('Draft'))).toBe(true);
|
|
627
|
-
consoleSpy.mockRestore();
|
|
628
|
-
});
|
|
629
|
-
it('shows branch worktree details', async () => {
|
|
630
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
631
|
-
const worktree = makeWorktree({
|
|
632
|
-
type: 'branch',
|
|
633
|
-
branch: 'feature-branch',
|
|
634
|
-
hasChanges: true,
|
|
635
|
-
});
|
|
636
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
637
|
-
expect(result.success).toBe(true);
|
|
638
|
-
consoleSpy.mockRestore();
|
|
639
|
-
});
|
|
640
|
-
it('shows detached worktree details', async () => {
|
|
641
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
642
|
-
const worktree = makeWorktree({
|
|
643
|
-
type: 'detached',
|
|
644
|
-
branch: null,
|
|
645
|
-
});
|
|
646
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
647
|
-
expect(result.success).toBe(true);
|
|
648
|
-
consoleSpy.mockRestore();
|
|
649
|
-
});
|
|
650
|
-
it('shows all worktree fields in output', async () => {
|
|
651
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
652
|
-
const worktree = makeWorktree({
|
|
653
|
-
path: '/home/user/repo.pr1',
|
|
654
|
-
name: 'repo.pr1',
|
|
655
|
-
type: 'pr',
|
|
656
|
-
prNumber: 123,
|
|
657
|
-
prState: 'OPEN',
|
|
658
|
-
branch: 'feat/test',
|
|
659
|
-
commit: 'abc123def',
|
|
660
|
-
hasChanges: false,
|
|
661
|
-
});
|
|
662
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
663
|
-
expect(result.success).toBe(true);
|
|
664
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
665
|
-
expect(output).toContain('Path');
|
|
666
|
-
expect(output).toContain('Name');
|
|
667
|
-
expect(output).toContain('Branch');
|
|
668
|
-
expect(output).toContain('Commit');
|
|
669
|
-
expect(output).toContain('Type');
|
|
670
|
-
consoleSpy.mockRestore();
|
|
671
|
-
});
|
|
672
|
-
it('shows clean status for worktree without changes', async () => {
|
|
673
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
674
|
-
const worktree = makeWorktree({
|
|
675
|
-
type: 'branch',
|
|
676
|
-
branch: 'clean-branch',
|
|
677
|
-
hasChanges: false,
|
|
678
|
-
});
|
|
679
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
680
|
-
expect(result.success).toBe(true);
|
|
681
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
682
|
-
expect(output).toContain('Clean');
|
|
683
|
-
consoleSpy.mockRestore();
|
|
684
|
-
});
|
|
685
|
-
it('shows uncommitted changes warning', async () => {
|
|
686
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
687
|
-
const worktree = makeWorktree({
|
|
688
|
-
type: 'branch',
|
|
689
|
-
branch: 'dirty-branch',
|
|
690
|
-
hasChanges: true,
|
|
691
|
-
});
|
|
692
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
693
|
-
expect(result.success).toBe(true);
|
|
694
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
695
|
-
expect(output).toContain('uncommitted');
|
|
696
|
-
consoleSpy.mockRestore();
|
|
697
|
-
});
|
|
698
|
-
it('shows PR URL from github.getPr when prUrl is not stored', async () => {
|
|
699
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
700
|
-
vi.mocked(github.getPr).mockReturnValue({
|
|
701
|
-
number: 42,
|
|
702
|
-
url: 'https://github.com/owner/repo/pull/42',
|
|
703
|
-
state: 'OPEN',
|
|
704
|
-
isDraft: false,
|
|
705
|
-
title: 'Test PR',
|
|
706
|
-
headBranch: 'feature-42',
|
|
707
|
-
baseBranch: 'main',
|
|
708
|
-
});
|
|
709
|
-
const worktree = makeWorktree({
|
|
710
|
-
type: 'pr',
|
|
711
|
-
prNumber: 42,
|
|
712
|
-
prState: 'OPEN',
|
|
713
|
-
branch: 'feature-42',
|
|
714
|
-
// prUrl is not set
|
|
715
|
-
});
|
|
716
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
717
|
-
expect(result.success).toBe(true);
|
|
718
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
719
|
-
expect(output).toContain('https://github.com/owner/repo/pull/42');
|
|
720
|
-
consoleSpy.mockRestore();
|
|
721
|
-
});
|
|
722
|
-
it('handles PR URL fetch failure gracefully', async () => {
|
|
723
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
724
|
-
vi.mocked(github.getPr).mockReturnValue(null);
|
|
725
|
-
const worktree = makeWorktree({
|
|
726
|
-
type: 'pr',
|
|
727
|
-
prNumber: 42,
|
|
728
|
-
prState: 'OPEN',
|
|
729
|
-
branch: 'feature-42',
|
|
730
|
-
// prUrl is not set
|
|
731
|
-
});
|
|
732
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
733
|
-
// Should succeed even if PR URL fetch fails
|
|
734
|
-
expect(result.success).toBe(true);
|
|
735
|
-
consoleSpy.mockRestore();
|
|
736
|
-
});
|
|
737
|
-
it('shows recent commits when git log succeeds', async () => {
|
|
738
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
739
|
-
vi.mocked(git.exec).mockReturnValue('abc1234 First commit\ndef5678 Second commit\nghi9012 Third commit');
|
|
740
|
-
const worktree = makeWorktree({
|
|
741
|
-
type: 'branch',
|
|
742
|
-
branch: 'feature-branch',
|
|
743
|
-
});
|
|
744
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
745
|
-
expect(result.success).toBe(true);
|
|
746
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
747
|
-
expect(output).toContain('Recent commits');
|
|
748
|
-
expect(output).toContain('First commit');
|
|
749
|
-
consoleSpy.mockRestore();
|
|
750
|
-
});
|
|
751
|
-
it('handles git log failure gracefully', async () => {
|
|
752
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
753
|
-
vi.mocked(git.exec).mockImplementation(() => {
|
|
754
|
-
throw new Error('git log failed');
|
|
755
|
-
});
|
|
756
|
-
const worktree = makeWorktree({
|
|
757
|
-
type: 'branch',
|
|
758
|
-
branch: 'feature-branch',
|
|
759
|
-
});
|
|
760
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
761
|
-
// Should succeed even if git log fails
|
|
762
|
-
expect(result.success).toBe(true);
|
|
763
|
-
consoleSpy.mockRestore();
|
|
764
|
-
});
|
|
765
|
-
});
|
|
766
|
-
describe('open_pr_url action with mocked github', () => {
|
|
767
|
-
it('returns error when PR is not found', async () => {
|
|
768
|
-
vi.mocked(github.getPr).mockReturnValue(null);
|
|
769
|
-
const deps = makeDeps();
|
|
770
|
-
const worktree = makeWorktree({
|
|
771
|
-
type: 'pr',
|
|
772
|
-
prNumber: 999,
|
|
773
|
-
prState: 'OPEN',
|
|
774
|
-
});
|
|
775
|
-
const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
|
|
776
|
-
expect(result.success).toBe(false);
|
|
777
|
-
expect(result.message).toContain('Could not find PR');
|
|
778
|
-
});
|
|
779
|
-
it('opens PR URL successfully when PR is found', async () => {
|
|
780
|
-
vi.mocked(github.getPr).mockReturnValue({
|
|
781
|
-
number: 42,
|
|
782
|
-
url: 'https://github.com/owner/repo/pull/42',
|
|
783
|
-
state: 'OPEN',
|
|
784
|
-
isDraft: false,
|
|
785
|
-
title: 'Test PR',
|
|
786
|
-
headBranch: 'feature-42',
|
|
787
|
-
baseBranch: 'main',
|
|
788
|
-
});
|
|
789
|
-
const deps = makeDeps();
|
|
790
|
-
const worktree = makeWorktree({
|
|
791
|
-
type: 'pr',
|
|
792
|
-
prNumber: 42,
|
|
793
|
-
prState: 'OPEN',
|
|
794
|
-
});
|
|
795
|
-
const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
|
|
796
|
-
expect(result.success).toBe(true);
|
|
797
|
-
expect(result.message).toContain('Opened PR');
|
|
798
|
-
expect(deps.openUrl).toHaveBeenCalledWith('https://github.com/owner/repo/pull/42');
|
|
799
|
-
});
|
|
800
|
-
it('handles error when opening URL fails', async () => {
|
|
801
|
-
vi.mocked(github.getPr).mockReturnValue({
|
|
802
|
-
number: 42,
|
|
803
|
-
url: 'https://github.com/owner/repo/pull/42',
|
|
804
|
-
state: 'OPEN',
|
|
805
|
-
isDraft: false,
|
|
806
|
-
title: 'Test PR',
|
|
807
|
-
headBranch: 'feature-42',
|
|
808
|
-
baseBranch: 'main',
|
|
809
|
-
});
|
|
810
|
-
const deps = makeDeps({
|
|
811
|
-
openUrl: vi.fn().mockImplementation(() => {
|
|
812
|
-
throw new Error('Failed to open browser');
|
|
813
|
-
}),
|
|
814
|
-
});
|
|
815
|
-
const worktree = makeWorktree({
|
|
816
|
-
type: 'pr',
|
|
817
|
-
prNumber: 42,
|
|
818
|
-
prState: 'OPEN',
|
|
819
|
-
});
|
|
820
|
-
const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
|
|
821
|
-
expect(result.success).toBe(false);
|
|
822
|
-
expect(result.message).toContain('Failed to open PR');
|
|
823
|
-
});
|
|
824
|
-
});
|
|
825
|
-
describe('open_editor action edge cases', () => {
|
|
826
|
-
it('prefers vscode when preferredEditor is vscode even if cursor available', async () => {
|
|
827
|
-
const deps = makeDeps();
|
|
828
|
-
const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
|
|
829
|
-
const env = makeEnv({ hasVscode: true, hasCursor: true, defaultEditor: 'vscode' });
|
|
830
|
-
const config = makeConfig({ preferredEditor: 'vscode' });
|
|
831
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
832
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('code', ['/home/user/repo.pr1']);
|
|
833
|
-
expect(result.success).toBe(true);
|
|
834
|
-
});
|
|
835
|
-
it('prefers cursor when preferredEditor is cursor even if vscode available', async () => {
|
|
836
|
-
const deps = makeDeps();
|
|
837
|
-
const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
|
|
838
|
-
const env = makeEnv({ hasVscode: true, hasCursor: true, defaultEditor: 'vscode' });
|
|
839
|
-
const config = makeConfig({ preferredEditor: 'cursor' });
|
|
840
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
841
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('cursor', ['/home/user/repo.pr1']);
|
|
842
|
-
expect(result.success).toBe(true);
|
|
843
|
-
});
|
|
844
|
-
it('falls back to cursor when preferredEditor is vscode but vscode not installed', async () => {
|
|
845
|
-
const deps = makeDeps();
|
|
846
|
-
const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
|
|
847
|
-
const env = makeEnv({ hasVscode: false, hasCursor: true, defaultEditor: 'cursor' });
|
|
848
|
-
const config = makeConfig({ preferredEditor: 'vscode' });
|
|
849
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
850
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('cursor', ['/home/user/repo.pr1']);
|
|
851
|
-
expect(result.success).toBe(true);
|
|
852
|
-
expect(result.message).toContain('Cursor');
|
|
853
|
-
});
|
|
854
|
-
it('falls back to vscode when preferredEditor is cursor but cursor not installed', async () => {
|
|
855
|
-
const deps = makeDeps();
|
|
856
|
-
const worktree = makeWorktree({ path: '/home/user/repo.pr1' });
|
|
857
|
-
const env = makeEnv({ hasVscode: true, hasCursor: false, defaultEditor: 'vscode' });
|
|
858
|
-
const config = makeConfig({ preferredEditor: 'cursor' });
|
|
859
|
-
const result = await executeAction('open_editor', worktree, env, config, deps);
|
|
860
|
-
expect(deps.spawnDetached).toHaveBeenCalledWith('code', ['/home/user/repo.pr1']);
|
|
861
|
-
expect(result.success).toBe(true);
|
|
862
|
-
expect(result.message).toContain('VSCode');
|
|
863
|
-
});
|
|
864
|
-
});
|
|
865
|
-
describe('copy_path action', () => {
|
|
866
|
-
it('copies the correct path', async () => {
|
|
867
|
-
const deps = makeDeps();
|
|
868
|
-
const worktree = makeWorktree({ path: '/home/user/my-project' });
|
|
869
|
-
const result = await executeAction('copy_path', worktree, makeEnv(), makeConfig(), deps);
|
|
870
|
-
expect(deps.copyToClipboard).toHaveBeenCalledWith('/home/user/my-project');
|
|
871
|
-
expect(result.success).toBe(true);
|
|
872
|
-
expect(result.message).toContain('/home/user/my-project');
|
|
873
|
-
});
|
|
874
|
-
});
|
|
875
|
-
describe('formatBranchAsTitle', () => {
|
|
876
|
-
it('removes feat/ prefix', () => {
|
|
877
|
-
expect(formatBranchAsTitle('feat/add-new-api')).toBe('Add new api');
|
|
878
|
-
});
|
|
879
|
-
it('removes fix/ prefix', () => {
|
|
880
|
-
expect(formatBranchAsTitle('fix/bad-login')).toBe('Bad login');
|
|
881
|
-
});
|
|
882
|
-
it('removes chore/ prefix', () => {
|
|
883
|
-
expect(formatBranchAsTitle('chore/update-deps')).toBe('Update deps');
|
|
884
|
-
});
|
|
885
|
-
it('removes docs/ prefix', () => {
|
|
886
|
-
expect(formatBranchAsTitle('docs/add-guide')).toBe('Add guide');
|
|
887
|
-
});
|
|
888
|
-
it('removes refactor/ prefix', () => {
|
|
889
|
-
expect(formatBranchAsTitle('refactor/clean-code')).toBe('Clean code');
|
|
890
|
-
});
|
|
891
|
-
it('removes test/ prefix', () => {
|
|
892
|
-
expect(formatBranchAsTitle('test/add-more-tests')).toBe('Add more tests');
|
|
893
|
-
});
|
|
894
|
-
it('removes style/ prefix', () => {
|
|
895
|
-
expect(formatBranchAsTitle('style/fix-lint')).toBe('Fix lint');
|
|
896
|
-
});
|
|
897
|
-
it('removes feature/ prefix', () => {
|
|
898
|
-
expect(formatBranchAsTitle('feature/new-api')).toBe('New api');
|
|
899
|
-
});
|
|
900
|
-
it('removes bugfix/ prefix', () => {
|
|
901
|
-
expect(formatBranchAsTitle('bugfix/fix-null')).toBe('Fix null');
|
|
902
|
-
});
|
|
903
|
-
it('removes trailing random suffixes', () => {
|
|
904
|
-
expect(formatBranchAsTitle('feat/add-login-abc123')).toBe('Add login');
|
|
905
|
-
expect(formatBranchAsTitle('add-feature-xyz789')).toBe('Add feature');
|
|
906
|
-
});
|
|
907
|
-
it('replaces hyphens with spaces', () => {
|
|
908
|
-
// Note: 'branch' is 6 chars so it gets removed as a suffix
|
|
909
|
-
expect(formatBranchAsTitle('my-feature-thing')).toBe('My feature thing');
|
|
910
|
-
});
|
|
911
|
-
it('replaces underscores with spaces', () => {
|
|
912
|
-
expect(formatBranchAsTitle('my_new_api')).toBe('My new api');
|
|
913
|
-
});
|
|
914
|
-
it('capitalizes first letter', () => {
|
|
915
|
-
expect(formatBranchAsTitle('lower')).toBe('Lower');
|
|
916
|
-
});
|
|
917
|
-
it('removes trailing 6+ char random suffix', () => {
|
|
918
|
-
// Words like 'branch' (6 chars) are also removed - intentional behavior
|
|
919
|
-
expect(formatBranchAsTitle('my-feature-branch')).toBe('My feature');
|
|
920
|
-
});
|
|
921
|
-
it('handles already capitalized input', () => {
|
|
922
|
-
expect(formatBranchAsTitle('Already-Capitalized')).toBe('Already Capitalized');
|
|
923
|
-
});
|
|
924
|
-
it('handles simple branch name', () => {
|
|
925
|
-
expect(formatBranchAsTitle('main')).toBe('Main');
|
|
926
|
-
});
|
|
927
|
-
it('handles complex branch names', () => {
|
|
928
|
-
expect(formatBranchAsTitle('feat/make-lswt-more-interactive-b5y1o2')).toBe('Make lswt more interactive');
|
|
929
|
-
});
|
|
930
|
-
it('handles mixed separators', () => {
|
|
931
|
-
expect(formatBranchAsTitle('my_feature-branch_name')).toBe('My feature branch name');
|
|
932
|
-
});
|
|
933
|
-
});
|
|
934
|
-
describe('checkout_pr action', () => {
|
|
935
|
-
it('returns error for non-remote_pr worktree type', async () => {
|
|
936
|
-
const worktree = makeWorktree({
|
|
937
|
-
type: 'pr',
|
|
938
|
-
prNumber: 42,
|
|
939
|
-
prState: 'OPEN',
|
|
940
|
-
});
|
|
941
|
-
const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
942
|
-
expect(result.success).toBe(false);
|
|
943
|
-
expect(result.message).toContain('Can only checkout remote PRs');
|
|
944
|
-
});
|
|
945
|
-
it('returns error when worktree has no PR number', async () => {
|
|
946
|
-
const worktree = makeWorktree({
|
|
947
|
-
type: 'remote_pr',
|
|
948
|
-
prNumber: null,
|
|
949
|
-
prState: 'OPEN',
|
|
950
|
-
});
|
|
951
|
-
const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
952
|
-
expect(result.success).toBe(false);
|
|
953
|
-
expect(result.message).toContain('Can only checkout remote PRs');
|
|
954
|
-
});
|
|
955
|
-
it('returns error when worktree has no branch', async () => {
|
|
956
|
-
const worktree = makeWorktree({
|
|
957
|
-
type: 'remote_pr',
|
|
958
|
-
prNumber: 42,
|
|
959
|
-
prState: 'OPEN',
|
|
960
|
-
branch: null,
|
|
961
|
-
});
|
|
962
|
-
const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
963
|
-
expect(result.success).toBe(false);
|
|
964
|
-
expect(result.message).toContain('no associated branch');
|
|
965
|
-
});
|
|
966
|
-
it('returns error when repo root cannot be found', async () => {
|
|
967
|
-
vi.mocked(git.getMainWorktreeRoot).mockReturnValue(null);
|
|
968
|
-
const worktree = makeWorktree({
|
|
969
|
-
type: 'remote_pr',
|
|
970
|
-
prNumber: 42,
|
|
971
|
-
prState: 'OPEN',
|
|
972
|
-
branch: 'feat/remote-feature',
|
|
973
|
-
});
|
|
974
|
-
const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
975
|
-
expect(result.success).toBe(false);
|
|
976
|
-
expect(result.message).toContain('Could not find repository root');
|
|
977
|
-
});
|
|
978
|
-
it('successfully creates worktree for remote PR', async () => {
|
|
979
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
980
|
-
vi.mocked(git.getMainWorktreeRoot).mockReturnValue('/home/user/repo');
|
|
981
|
-
vi.mocked(git.addWorktree).mockImplementation(() => { });
|
|
982
|
-
// Mock git.exec to return empty string (git fetch succeeds)
|
|
983
|
-
vi.mocked(git.exec).mockReturnValue('');
|
|
984
|
-
const worktree = makeWorktree({
|
|
985
|
-
type: 'remote_pr',
|
|
986
|
-
prNumber: 42,
|
|
987
|
-
prState: 'OPEN',
|
|
988
|
-
branch: 'feat/remote-feature',
|
|
989
|
-
prTitle: 'Add remote feature',
|
|
990
|
-
prUrl: 'https://github.com/owner/repo/pull/42',
|
|
991
|
-
});
|
|
992
|
-
const config = makeConfig({ worktreePattern: '{repo}.pr{number}', worktreeParent: '..' });
|
|
993
|
-
const result = await executeAction('checkout_pr', worktree, makeEnv(), config, makeDeps());
|
|
994
|
-
expect(result.success).toBe(true);
|
|
995
|
-
expect(result.message).toContain('Created worktree for PR #42');
|
|
996
|
-
expect(result.shouldRefresh).toBe(true);
|
|
997
|
-
expect(git.addWorktree).toHaveBeenCalledWith(expect.stringContaining('.pr42'), 'feat/remote-feature', expect.objectContaining({ cwd: '/home/user/repo' }));
|
|
998
|
-
consoleSpy.mockRestore();
|
|
999
|
-
});
|
|
1000
|
-
it('handles git fetch failure', async () => {
|
|
1001
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
1002
|
-
vi.mocked(git.getMainWorktreeRoot).mockReturnValue('/home/user/repo');
|
|
1003
|
-
// Mock git.exec to throw (git fetch fails)
|
|
1004
|
-
vi.mocked(git.exec).mockImplementation(() => {
|
|
1005
|
-
throw new Error('Failed to fetch branch');
|
|
1006
|
-
});
|
|
1007
|
-
const worktree = makeWorktree({
|
|
1008
|
-
type: 'remote_pr',
|
|
1009
|
-
prNumber: 42,
|
|
1010
|
-
prState: 'OPEN',
|
|
1011
|
-
branch: 'feat/remote-feature',
|
|
1012
|
-
});
|
|
1013
|
-
const result = await executeAction('checkout_pr', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
1014
|
-
expect(result.success).toBe(false);
|
|
1015
|
-
expect(result.message).toContain('Failed to checkout PR');
|
|
1016
|
-
consoleSpy.mockRestore();
|
|
1017
|
-
});
|
|
1018
|
-
});
|
|
1019
|
-
describe('show_details action for remote_pr', () => {
|
|
1020
|
-
it('shows PR title for remote PRs', async () => {
|
|
1021
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
1022
|
-
const worktree = makeWorktree({
|
|
1023
|
-
type: 'remote_pr',
|
|
1024
|
-
prNumber: 42,
|
|
1025
|
-
prState: 'OPEN',
|
|
1026
|
-
branch: 'feat/remote-feature',
|
|
1027
|
-
prTitle: 'Add amazing new feature',
|
|
1028
|
-
prUrl: 'https://github.com/owner/repo/pull/42',
|
|
1029
|
-
});
|
|
1030
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
1031
|
-
expect(result.success).toBe(true);
|
|
1032
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
1033
|
-
expect(output).toContain('Add amazing new feature');
|
|
1034
|
-
consoleSpy.mockRestore();
|
|
1035
|
-
});
|
|
1036
|
-
it('shows PR URL for remote PRs', async () => {
|
|
1037
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
1038
|
-
const worktree = makeWorktree({
|
|
1039
|
-
type: 'remote_pr',
|
|
1040
|
-
prNumber: 42,
|
|
1041
|
-
prState: 'OPEN',
|
|
1042
|
-
branch: 'feat/remote-feature',
|
|
1043
|
-
prTitle: 'Add feature',
|
|
1044
|
-
prUrl: 'https://github.com/owner/repo/pull/42',
|
|
1045
|
-
});
|
|
1046
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
1047
|
-
expect(result.success).toBe(true);
|
|
1048
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
1049
|
-
expect(output).toContain('https://github.com/owner/repo/pull/42');
|
|
1050
|
-
consoleSpy.mockRestore();
|
|
1051
|
-
});
|
|
1052
|
-
it('shows message about no local checkout for remote PRs', async () => {
|
|
1053
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
1054
|
-
const worktree = makeWorktree({
|
|
1055
|
-
type: 'remote_pr',
|
|
1056
|
-
prNumber: 42,
|
|
1057
|
-
prState: 'OPEN',
|
|
1058
|
-
branch: 'feat/remote-feature',
|
|
1059
|
-
prTitle: 'Add feature',
|
|
1060
|
-
prUrl: 'https://github.com/owner/repo/pull/42',
|
|
1061
|
-
});
|
|
1062
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
1063
|
-
expect(result.success).toBe(true);
|
|
1064
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
1065
|
-
expect(output).toContain('No local checkout');
|
|
1066
|
-
consoleSpy.mockRestore();
|
|
1067
|
-
});
|
|
1068
|
-
it('does not show "Changes" line for remote PRs', async () => {
|
|
1069
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
1070
|
-
const worktree = makeWorktree({
|
|
1071
|
-
type: 'remote_pr',
|
|
1072
|
-
prNumber: 42,
|
|
1073
|
-
prState: 'OPEN',
|
|
1074
|
-
branch: 'feat/remote-feature',
|
|
1075
|
-
prTitle: 'Add feature',
|
|
1076
|
-
prUrl: 'https://github.com/owner/repo/pull/42',
|
|
1077
|
-
hasChanges: false,
|
|
1078
|
-
});
|
|
1079
|
-
const result = await executeAction('show_details', worktree, makeEnv(), makeConfig(), makeDeps());
|
|
1080
|
-
expect(result.success).toBe(true);
|
|
1081
|
-
const output = consoleSpy.mock.calls.map((call) => String(call[0])).join('\n');
|
|
1082
|
-
// For remote PRs, "Changes:" line should not appear since there's no local path
|
|
1083
|
-
expect(output).not.toMatch(/Changes:.*Clean/);
|
|
1084
|
-
consoleSpy.mockRestore();
|
|
1085
|
-
});
|
|
1086
|
-
});
|
|
1087
|
-
describe('open_pr_url action for remote_pr', () => {
|
|
1088
|
-
it('uses stored prUrl for remote PRs', async () => {
|
|
1089
|
-
const deps = makeDeps();
|
|
1090
|
-
const worktree = makeWorktree({
|
|
1091
|
-
type: 'remote_pr',
|
|
1092
|
-
prNumber: 42,
|
|
1093
|
-
prState: 'OPEN',
|
|
1094
|
-
prUrl: 'https://github.com/owner/repo/pull/42',
|
|
1095
|
-
});
|
|
1096
|
-
const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
|
|
1097
|
-
expect(result.success).toBe(true);
|
|
1098
|
-
expect(result.message).toContain('Opened PR #42');
|
|
1099
|
-
expect(deps.openUrl).toHaveBeenCalledWith('https://github.com/owner/repo/pull/42');
|
|
1100
|
-
// Should not call github.getPr since we have the URL stored
|
|
1101
|
-
expect(github.getPr).not.toHaveBeenCalled();
|
|
1102
|
-
});
|
|
1103
|
-
it('falls back to fetching URL when prUrl is not stored', async () => {
|
|
1104
|
-
vi.mocked(github.getPr).mockReturnValue({
|
|
1105
|
-
number: 42,
|
|
1106
|
-
url: 'https://github.com/owner/repo/pull/42',
|
|
1107
|
-
state: 'OPEN',
|
|
1108
|
-
isDraft: false,
|
|
1109
|
-
title: 'Test PR',
|
|
1110
|
-
headBranch: 'feature-42',
|
|
1111
|
-
baseBranch: 'main',
|
|
1112
|
-
});
|
|
1113
|
-
const deps = makeDeps();
|
|
1114
|
-
const worktree = makeWorktree({
|
|
1115
|
-
type: 'remote_pr',
|
|
1116
|
-
prNumber: 42,
|
|
1117
|
-
prState: 'OPEN',
|
|
1118
|
-
prUrl: undefined, // No stored URL
|
|
1119
|
-
});
|
|
1120
|
-
const result = await executeAction('open_pr_url', worktree, makeEnv(), makeConfig(), deps);
|
|
1121
|
-
expect(result.success).toBe(true);
|
|
1122
|
-
expect(github.getPr).toHaveBeenCalledWith(42);
|
|
1123
|
-
expect(deps.openUrl).toHaveBeenCalledWith('https://github.com/owner/repo/pull/42');
|
|
1124
|
-
});
|
|
1125
|
-
});
|
|
1126
|
-
});
|
|
1127
|
-
//# sourceMappingURL=action-executors.test.js.map
|