Skip to content

Add tests for RewindSelector + handleRewindConfirm orchestration #4187

Description

@doudouOUC

Resolution status

Resolved by PR #5044 (test(cli): Cover rewind selection and confirm flow), merged on 2026-06-13 23:07 UTC with merge commit 87dc1a3, and now present on main.

Current status:

  • Required acceptance criteria below are complete.
  • CI/review state at merge: approved, 9 successful checks, 0 failed, 0 pending.
  • No unresolved review threads remain.

Remaining required work: none for this issue.

Optional follow-up: runtime verification noted that the No files needed to be restored. user-facing message branch is the only handleRewindConfirm message not directly asserted. This is non-blocking and outside the original acceptance criteria; track separately only if full message coverage is desired.

Background

PR #4064 added two large rewind-related code paths with no direct test coverage:

  • packages/cli/src/ui/components/RewindSelector.tsx (≈239 LOC) — a 3-phase Ink component:

    1. Pick list — select which user turn to rewind to
    2. Restore options — choose code/conversation/both with async diff stats loading
    3. Legacy confirm — Y/N fallback when fileCheckpointingEnabled is false

    Plus an isRestoring keypress-guard state surfaced as a Spinner while onRewind is awaited.

  • packages/cli/src/ui/AppContainer.tsx#handleRewindConfirm (~140 LOC) — orchestrates the post-selection flow:

    • Pre-validates conversation truncation before touching files for 'both' mode
    • Routes the file-restore call (with proper truncateHistory flag)
    • Surfaces no-client / partial-failure / unrecoverable-snapshot errors via the history manager
    • Skips conversation truncation when file restore partially fails (avoids inconsistent state)
    • Falls through to a clean error path when the whole flow throws

The component / orchestration have been manually verified end-to-end via tmux-driven REPL sessions (see PR #4064 "Automated verification results" section), but there is no automated regression coverage.

Why this is a follow-up, not part of #4064

Both surfaces require similar testing scaffolding the repo does not yet use:

  • An ink-testing-library setup (rendering, keypress simulation, snapshot/text assertion) for RewindSelector
  • A heavier AppContainer mock harness — config / geminiClient / historyManager / fileHistoryService — for handleRewindConfirm

Building both layers together is the right shape (≈350-500 LOC of new test code plus a one-time pattern pass for ink-testing patterns) but bundling it into the rewind PR (already 30+ commits and several review rounds) significantly delays the merge for in-session functionality that has been manually verified.

Acceptance criteria

RewindSelector.test.tsx

  • Phase 1 (turn selection) keypress navigation up/down/enter/cancel
  • Phase 2 (restore options) renders each RestoreOption correctly
  • Phase 2 getDiffStats success populates the (+N -M in K files) detail string
  • Phase 2 getDiffStats rejection falls back to undefined diffStats without crashing
  • isRestoring blocks all keypresses including Escape (per the upstream-parity decision in PR feat(rewind): add file restoration support to /rewind command #4064)
  • Esc from Phase 2 returns to Phase 1
  • Legacy confirm Y/N path when fileCheckpointingEnabled is false
  • The "File restore is unavailable for this turn" hint shows when getRestoreOptions(...) doesn't include code/both

handleRewindConfirm tests (in AppContainer.test.tsx or a focused harness)

  • 'both' mode with file restore failure skips conversation truncation
  • 'both' mode with file restore success still truncates conversation
  • 'code' only mode does NOT truncate history
  • 'conversation' only mode does NOT call getFileHistoryService().rewind
  • No-client fallback messages: 'conversation' shows error and returns; 'both' flips into code-only with the warning history item
  • Outer try/catch surfaces unexpected errors via historyManager.addItem of type: 'error'
  • Compressed-turn error path: apiTruncateIndex < 0 adds the t-wrapped error and bails

Related

🤖 Generated with Qwen Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions