| ... | ... | @@ -3581,6 +3581,98 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 3581 | 3581 | } |
| 3582 | 3582 | }); |
| 3583 | 3583 | |
| 3584 | it("creates a fresh worktree draft from an existing worktree thread when the default mode is worktree", async () => { |
| 3585 | const mounted = await mountChatView({ |
| 3586 | viewport: DEFAULT_VIEWPORT, |
| 3587 | snapshot: { |
| 3588 | ...createSnapshotForTargetUser({ |
| 3589 | targetMessageId: "msg-user-new-thread-worktree-default-test" as MessageId, |
| 3590 | targetText: "new thread worktree default test", |
| 3591 | }), |
| 3592 | threads: createSnapshotForTargetUser({ |
| 3593 | targetMessageId: "msg-user-new-thread-worktree-default-test" as MessageId, |
| 3594 | targetText: "new thread worktree default test", |
| 3595 | }).threads.map((thread) => |
| 3596 | thread.id === THREAD_ID |
| 3597 | ? Object.assign({}, thread, { |
| 3598 | branch: "feature/existing", |
| 3599 | worktreePath: "/repo/.t3/worktrees/existing", |
| 3600 | }) |
| 3601 | : thread, |
| 3602 | ), |
| 3603 | }, |
| 3604 | configureFixture: (nextFixture) => { |
| 3605 | nextFixture.serverConfig = { |
| 3606 | ...nextFixture.serverConfig, |
| 3607 | settings: { |
| 3608 | ...nextFixture.serverConfig.settings, |
| 3609 | defaultThreadEnvMode: "worktree", |
| 3610 | }, |
| 3611 | }; |
| 3612 | }, |
| 3613 | }); |
| 3614 | |
| 3615 | try { |
| 3616 | const newThreadButton = page.getByTestId("new-thread-button"); |
| 3617 | await expect.element(newThreadButton).toBeInTheDocument(); |
| 3618 | |
| 3619 | await newThreadButton.click(); |
| 3620 | |
| 3621 | const newThreadPath = await waitForURL( |
| 3622 | mounted.router, |
| 3623 | (path) => UUID_ROUTE_RE.test(path), |
| 3624 | "Route should change to a new draft thread.", |
| 3625 | ); |
| 3626 | const newDraftId = draftIdFromPath(newThreadPath); |
| 3627 | |
| 3628 | expect(useComposerDraftStore.getState().getDraftSession(newDraftId)).toMatchObject({ |
| 3629 | envMode: "worktree", |
| 3630 | worktreePath: null, |
| 3631 | }); |
| 3632 | } finally { |
| 3633 | await mounted.cleanup(); |
| 3634 | } |
| 3635 | }); |
| 3636 | |
| 3637 | it("creates a new draft instead of reusing a promoting draft thread", async () => { |
| 3638 | const mounted = await mountChatView({ |
| 3639 | viewport: DEFAULT_VIEWPORT, |
| 3640 | snapshot: createSnapshotForTargetUser({ |
| 3641 | targetMessageId: "msg-user-promoting-draft-new-thread-test" as MessageId, |
| 3642 | targetText: "promoting draft new thread test", |
| 3643 | }), |
| 3644 | }); |
| 3645 | |
| 3646 | try { |
| 3647 | const newThreadButton = page.getByTestId("new-thread-button"); |
| 3648 | await expect.element(newThreadButton).toBeInTheDocument(); |
| 3649 | |
| 3650 | await newThreadButton.click(); |
| 3651 | |
| 3652 | const firstDraftPath = await waitForURL( |
| 3653 | mounted.router, |
| 3654 | (path) => UUID_ROUTE_RE.test(path), |
| 3655 | "Route should change to the first draft thread.", |
| 3656 | ); |
| 3657 | const firstDraftId = draftIdFromPath(firstDraftPath); |
| 3658 | const firstThreadId = draftThreadIdFor(firstDraftId); |
| 3659 | |
| 3660 | await materializePromotedDraftThreadViaDomainEvent(firstThreadId); |
| 3661 | expect(mounted.router.state.location.pathname).toBe(firstDraftPath); |
| 3662 | |
| 3663 | await newThreadButton.click(); |
| 3664 | |
| 3665 | const secondDraftPath = await waitForURL( |
| 3666 | mounted.router, |
| 3667 | (path) => UUID_ROUTE_RE.test(path) && path !== firstDraftPath, |
| 3668 | "Route should change to a second draft thread instead of reusing the promoting draft.", |
| 3669 | ); |
| 3670 | expect(draftIdFromPath(secondDraftPath)).not.toBe(firstDraftId); |
| 3671 | } finally { |
| 3672 | await mounted.cleanup(); |
| 3673 | } |
| 3674 | }); |
| 3675 | |
| 3584 | 3676 | it("snapshots sticky codex settings into a new draft thread", async () => { |
| 3585 | 3677 | useComposerDraftStore.setState({ |
| 3586 | 3678 | stickyModelSelectionByProvider: { |