Skip to content

test(sidebar): seed detectedWorktreesByRepo in the Sidebar store fixture - #22564

Closed
nwparker wants to merge 1 commit into
mainfrom
fix/sidebar-test-detected-worktrees-fixture
Closed

nwparker wants to merge 1 commit into
mainfrom
fix/sidebar-test-detected-worktrees-fixture

Conversation

@nwparker

@nwparker nwparker commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2
Prod 0 0 0 0

ELI5

A test that checks the project sidebar started crashing on main, which turned the test gate red for every open PR. The test builds a pretend version of the app's data store, and a recently added sidebar banner needed one piece of data the pretend store had never been told to include. This adds that one piece.

What Changed

src/renderer/src/components/sidebar/Sidebar.test.tsx fakes the app store (@/store) with a hand-written partial state object. setSidebarState now also seeds detectedWorktreesByRepo: {}.

Before: npx vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/Sidebar.test.tsx on clean main failed with

FAIL  Sidebar > does not start a full worktree scan while the startup session is hydrating
TypeError: Cannot read properties of undefined (reading 'repo-a')
 ❯ src/renderer/src/components/sidebar/repo-scan-failure.ts:54:50
 Tests  1 failed | 5 passed (6)

After: 6 passed.

The mechanism: #22552 added <LocalGitToolchainScanBanner /> into src/renderer/src/components/sidebar/index.tsx. That banner reads useAppStore((s) => s.detectedWorktreesByRepo) and passes it to findLocalToolchainBlock, which does detectedByRepo[repo.id] for every repo. The fixture never defined that key, so the value was undefined. Five of the six tests survived because they render with repos: [], and the flatMap over an empty array never dereferences it. The sixth — the hydration test — is the only one that sets a non-empty repos, so it was the only one that indexed into undefined.

No assertion, name, or behaviour in the test changed. The "don't scan during hydration" invariant is asserted exactly as before, and the banner still renders for real (it returns null with an empty record) rather than being mocked away.

Why

main is red, which reds the verify gate on every unrelated PR. In CI this shows up as test / tests node 24 1/8.

The defect is in the test fixture, not the product code, and I checked rather than assumed:

  • createWorktreeSlice spreads worktreeSliceInitialState, which sets detectedWorktreesByRepo: {} at store creation (src/renderer/src/store/slices/worktrees.ts:78, .../worktrees/session/worktree-slice-initial-state.ts:25).
  • WorktreeSlice types it as a required Record<string, DetectedWorktreeListResult> — only the values are allowed to be absent, and resolveRepoScanFailure already handles a missing value via its !detected early return.
  • useAppStore has no persist middleware, so there is no rehydration path that can drop the key.
  • No product code calls useAppStore.setState(next, true); the only replacing setState is in a test harness.

So the real sidebar could never have hit this at runtime — no users were affected, and this is not a latent product crash. That is why I did not add a ?? {} guard to findLocalToolchainBlock: with the key guaranteed by the slice's initial state and by the type, a guard would be unreachable code that also hides the next fixture drift instead of surfacing it. Mocking LocalGitToolchainScanBanner out of the suite (the pattern used for WorktreeList, SidebarHeader, etc.) would also have gone green, but it would quietly drop the banner from Sidebar's render coverage — strictly less testing than we have now. Seeding the fixture keeps coverage and matches what sibling suites already do (WorktreeVisibilityDialog.test.tsx, project-added-default-checkout.test.ts both seed this key in their base state).

Root cause class worth noting for reviewers: the fixture is typed Record<string, unknown>, so TypeScript cannot catch a new store read that the fixture does not satisfy. That is a pre-existing shape in this file and I kept the fix scoped rather than retyping it.

Linked Issue

No tracking issue exists for this breakage; main was red and blocking merges. Filed as a direct fix.

Fixes #

Visual Proof

N/A — test-fixture-only change. No product file is touched, so there is no user-visible before/after to capture. As established in Why, the crash was not reachable in the shipped sidebar.

Testing

macOS (arm64). Reproduced on clean origin/main at 7f203ad5aab with zero local changes before fixing.

npx vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/Sidebar.test.tsx
  → Test Files  1 passed (1) / Tests  6 passed (6)

npx vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar
  → Test Files  310 passed (310) / Tests  2631 passed (2631)

pnpm tc
  → exit 0

pnpm run check:code-quality:changed
  → 0 new findings across 1 changed file; gate passed since 7f203ad5aabc

The whole src/renderer/src/components/sidebar directory was run to confirm no sibling suite regressed.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

The change is the test fix; the existing hydration test is the regression test and it now exercises the real banner.

AI Disclosure

Claude Opus 4.5 (Claude Code) investigated and wrote this change.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

No security, cross-platform, SSH/remote, mobile, backwards-compatibility, or performance impact — this is a one-line addition to a renderer test fixture and ships no product code.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

The Sidebar suite fakes `@/store` with a hand-written partial state object.
PR #22552 added `LocalGitToolchainScanBanner` to the sidebar, which reads
`detectedWorktreesByRepo` and indexes it per repo. The fixture never set that
key, so the one test that renders with a non-empty `repos` list crashed in
`findLocalToolchainBlock` with "Cannot read properties of undefined".

The real store seeds the record to `{}` at creation and has no persist
middleware, so the product code cannot see it undefined; only the fixture
could. Seed it in the fixture rather than adding a dead guard to the product
path. No assertion changed.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • Seed detectedWorktreesByRepo in the Sidebar fixture — setSidebarState now sets detectedWorktreesByRepo: {} with a // Why: comment at src/renderer/src/components/sidebar/Sidebar.test.tsx:110-111, restoring the hand-written partial store state that LocalGitToolchainScanBanner (added in #22552) indexes per repo via findLocalToolchainBlock.

I independently verified the claims in the description:

  • The key is load-bearing — without it, the hydration test's non-empty repos reaches detectedByRepo[repo.id] in repo-scan-failure.ts:54 and throws TypeError: Cannot read properties of undefined. The other five tests pass repos: [], so flatMap never dereferences it.
  • The product store always seeds the key: worktreeSliceInitialState sets detectedWorktreesByRepo: {} (worktree-slice-initial-state.ts:25) and the slice types it as a required Record<string, DetectedWorktreeListResult> (worktree-helpers.ts:98), so no runtime consumer can hit this.
  • Sibling suites already seed the same key (WorktreeVisibilityDialog.test.tsx:27, project-added-default-checkout.test.ts:23), and the banner retains direct coverage in LocalGitToolchainScanBanner.test.tsx rather than being mocked out.
  • npx vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/Sidebar.test.tsx → 6 passed locally.

Scoping the fix to the fixture rather than adding a ?? {} guard to findLocalToolchainBlock is the right call: the guard would be unreachable in production and would mask the next fixture drift. The pre-existing Record<string, unknown> fixture typing that let this slip past tsc is worth revisiting separately, but not in this fix.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 901d9792-589b-43b2-a5c3-e24a33cdfb01

📥 Commits

Reviewing files that changed from the base of the PR and between 7f203ad and e8b1628.

📒 Files selected for processing (1)
  • src/renderer/src/components/sidebar/Sidebar.test.tsx

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The sidebar test fixture now initializes detectedWorktreesByRepo as an empty record. A comment notes that the store normally seeds this state and that the toolchain banner indexes it by repository.

Merge Risk: ⚪ Minimal · up to e8b16

This test-only change aligns the sidebar fixture with the real store; it does not change production behavior, and no actionable merge risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the change: adding detectedWorktreesByRepo to the Sidebar test fixture.
Description check ✅ Passed The description explains the problem, change, rationale, and testing in detail. The required Linked Issue section has no issue link; the author states that no tracking issue exists, but the template r…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nwparker

Copy link
Copy Markdown
Contributor Author

Superseded by #22563, which landed the identical fix (detectedWorktreesByRepo: {} in the Sidebar store fixture) while this was in CI. Independently reached the same conclusion — the real store seeds the field unconditionally in worktree-slice-initial-state.ts, so the banner's dereference is safe and the fixture was the gap. Verified Sidebar.test.tsx now passes 6/6 on current main. Closing as a duplicate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant