Skip to content

[issues/594] Refactor QuickPick item shape: single source of truth via nested info objects#596

Merged
couimet merged 2 commits into
mainfrom
issues/594
May 21, 2026
Merged

[issues/594] Refactor QuickPick item shape: single source of truth via nested info objects#596
couimet merged 2 commits into
mainfrom
issues/594

Conversation

@couimet
Copy link
Copy Markdown
Owner

@couimet couimet commented May 21, 2026

Summary

Consolidates the EligibleTerminal / EligibleFile domain types so the live vscode.Terminal / vscode.Uri references live exclusively on bindOptions, and drops the top-level isActive / boundState duplication from TerminalBindableQuickPickItem / FileBindableQuickPickItem. The log enrichment that used to require those top-level fields moves behind a RANGELINK_CAPTURE_LOGS=true env gate, matching the existing LogCapture / markRangeLinkTestFixture pattern. Pure refactor: no behavior change, integration-test log contract preserved.

Changes

  • EligibleTerminal.terminal: vscode.TerminalEligibleTerminal.bindOptions: TerminalBindOptions. Same shape for EligibleFile: uri: vscode.UribindOptions: TextEditorBindOptions. The live VS Code reference is now encapsulated as a bind intent from the moment a candidate is identified.
  • BindableQuickPickItem base no longer carries readonly isActive?. TerminalBindableQuickPickItem no longer carries readonly boundState?. FileBindableQuickPickItem no longer carries readonly boundState?. All three fields had zero production consumers — they existed solely to feed VscodeAdapter.showQuickPick's log projection.
  • VscodeAdapter.showQuickPick items projection split into prod path (base fields only: label, description, detail, kind, itemKind, displayName, remainingCount) and a process.env.RANGELINK_CAPTURE_LOGS === 'true' gate that additionally projects isActive / boundState flat by digging into terminalInfo / fileInfo. Integration tests that destructure { isActive, boundState } from the log line need no changes.
  • New projectTestStatusFields helper in VscodeAdapter.ts, exported for direct unit testing. Five new unit tests cover its terminalInfo / fileInfo / absent / both-present cases. Two additional showQuickPick log-projection tests cover both env-gated branches end-to-end: one for the unset/prod path (base fields only, even when terminalInfo is present on the item), and one for RANGELINK_CAPTURE_LOGS='true' using jest.resetModules() + await import() + try/finally env restoration to reload the adapter with the new env value. The env var name stays as a literal in the test per T003 — it's an external contract.
  • Call sites that used to construct bindOptions inline now read eligible.bindOptions — see DestinationAvailabilityService.buildTerminalItem, buildFileItem, DestinationPicker.showSecondaryFilePicker, showSecondaryTerminalPicker, BindToTerminalCommand.execute, BindToTextEditorCommand.executeWithPicker, RangeLinkStatusBar.showSecondaryTerminalPicker, and the file overflow picker. The picker item's top-level bindOptions is now the SAME object reference as terminalInfo.bindOptions — single source.
  • Both TODO(#594) comments removed: one in QuickPickTypes.ts, one in VscodeAdapter.ts.
  • New src/constants/envVarNames.ts centralizes the two production env-var names (ENV_RANGELINK_CAPTURE_LOGS, ENV_RANGELINK_TEST_FIXTURES_ENABLED). Production readers (LogCapture.ts, VscodeAdapter.ts, testFixtureRegistry.ts) import from the constant; error messages template-interpolate the name so a future rename stays in one place. The classifyTerminalForBinding test keeps the literal env-var name per T003 — the test acts as a contract tripwire: a rename in the constants file doesn't silently propagate to the test, so the prod-vs-test mismatch surfaces loudly. .vscode-test.base.mjs also keeps its literal (it runs at vscode-test config-load time, outside the TS module graph; touching it would entangle build order).
  • test:release:automated in packages/rangelink-vscode-extension/package.json now bakes in --exclude-label requires-extensions --exclude-label cursor, matching CI. The redundant flags removed from .github/actions/run-integration-tests/action.yml. Local devs no longer wheel-spin on tests CI was already skipping.
  • Removed 20 unnecessary as const assertions across files this PR touched: getEligibleTerminals.ts, getEligibleFiles.ts, buildTerminalPickerItems.ts, markBoundFile.ts, markBoundTerminal.ts, DestinationPicker.ts, RangeLinkStatusBar.ts. The annotated return types and surrounding contextual types already narrow the literals — the assertions were noise. Out-of-scope as const sites (ListBookmarksCommand.ts, PasteDestinationManager.ts, FocusCapability.ts) left for a follow-up.

Key Discoveries

  • EligibleFile.viewColumn is intentionally kept even though it duplicates bindOptions.viewColumn. It's plain numeric metadata read by sortEligibleFiles, buildFilePickerItems (grouping), and buildDestinationQuickPickItems (group label) — forcing those three call sites through bindOptions.viewColumn for one number is uglier than the redundancy. The smell that motivates removing terminal / uri (live VS Code refs in domain types) does not apply to a plain integer.
  • The issue body listed nonBindableReason as a top-level field on TerminalBindableQuickPickItem to drop. It was never there — that field only lives in classifyTerminalForBinding's discriminated-union return type. The "drop top-level nonBindableReason" cleanup was a no-op.
  • The two clipboard-preservation tests for Cursor AI (clipboard-preservation-013, clipboard-preservation-014) are pre-existing failures on main — verified by stashing and re-running. The CI workflow was already excluding them via --exclude-label cursor. This PR pulls that exclusion into the script entry so local runs match CI without manual flag-passing.

Test Plan

  • All 1947 unit tests pass (pnpm test:fast)
  • All 142 automated integration tests pass (pnpm test:release:automated, ~7 minutes)
  • pnpm compile clean
  • pnpm fix clean
  • Sweep grep confirmed zero stragglers — no remaining .terminal reads on a typed EligibleTerminal, no remaining .uri reads on a typed EligibleFile, no remaining item.isActive / item.boundState reads on a typed BindableQuickPickItem.
  • Integration-test log contract preserved: the prod log no longer emits flat isActive / boundState, but the env-gated test mode does, sourced from terminalInfo / fileInfo. Integration tests destructuring { isActive, boundState } from VscodeAdapter.showQuickPick log lines pass unchanged.

Related

Summary by CodeRabbit

  • Refactor

    • Unified binding payloads across pickers, commands, and the status bar for more consistent bind/unbind behavior in menus.
    • Centralized environment variable names for test/log toggles.
  • Tests

    • Updated numerous tests and test helpers to reflect the new item shapes and logging projections.
  • Chores

    • Adjusted automated test scripts and CI test invocation for the release pipeline.

Review Change Stack

…a nested info objects

## Summary

Consolidates the `EligibleTerminal` / `EligibleFile` domain types so the live `vscode.Terminal` / `vscode.Uri` references live exclusively on `bindOptions`, and drops the top-level `isActive` / `boundState` duplication from `TerminalBindableQuickPickItem` / `FileBindableQuickPickItem`. The log enrichment that used to require those top-level fields moves behind a `RANGELINK_CAPTURE_LOGS=true` env gate, matching the existing `LogCapture` / `markRangeLinkTestFixture` pattern. Pure refactor: no behavior change, integration-test log contract preserved.

## Changes

- `EligibleTerminal.terminal: vscode.Terminal` → `EligibleTerminal.bindOptions: TerminalBindOptions`. Same shape for `EligibleFile`: `uri: vscode.Uri` → `bindOptions: TextEditorBindOptions`. The live VS Code reference is now encapsulated as a bind intent from the moment a candidate is identified.
- `BindableQuickPickItem` base no longer carries `readonly isActive?`. `TerminalBindableQuickPickItem` no longer carries `readonly boundState?`. `FileBindableQuickPickItem` no longer carries `readonly boundState?`. All three fields had zero production consumers — they existed solely to feed `VscodeAdapter.showQuickPick`'s log projection.
- `VscodeAdapter.showQuickPick` items projection split into prod path (base fields only: `label`, `description`, `detail`, `kind`, `itemKind`, `displayName`, `remainingCount`) and a `process.env.RANGELINK_CAPTURE_LOGS === 'true'` gate that additionally projects `isActive` / `boundState` flat by digging into `terminalInfo` / `fileInfo`. Integration tests that destructure `{ isActive, boundState }` from the log line need no changes.
- New `projectTestStatusFields` helper in `VscodeAdapter.ts`, exported for direct unit testing. Five new unit tests cover its terminalInfo / fileInfo / absent / both-present cases. Two additional `showQuickPick` log-projection tests cover both env-gated branches end-to-end: one for the unset/prod path (base fields only, even when `terminalInfo` is present on the item), and one for `RANGELINK_CAPTURE_LOGS='true'` using `jest.resetModules()` + `await import()` + `try/finally` env restoration to reload the adapter with the new env value. The env var name stays as a literal in the test per T003 — it's an external contract.
- Call sites that used to construct `bindOptions` inline now read `eligible.bindOptions` — see `DestinationAvailabilityService.buildTerminalItem`, `buildFileItem`, `DestinationPicker.showSecondaryFilePicker`, `showSecondaryTerminalPicker`, `BindToTerminalCommand.execute`, `BindToTextEditorCommand.executeWithPicker`, `RangeLinkStatusBar.showSecondaryTerminalPicker`, and the file overflow picker. The picker item's top-level `bindOptions` is now the SAME object reference as `terminalInfo.bindOptions` — single source.
- Both `TODO(#594)` comments removed: one in `QuickPickTypes.ts`, one in `VscodeAdapter.ts`.
- New `src/constants/envVarNames.ts` centralizes the two production env-var names (`ENV_RANGELINK_CAPTURE_LOGS`, `ENV_RANGELINK_TEST_FIXTURES_ENABLED`). Production readers (`LogCapture.ts`, `VscodeAdapter.ts`, `testFixtureRegistry.ts`) import from the constant; error messages template-interpolate the name so a future rename stays in one place. The `classifyTerminalForBinding` test keeps the literal env-var name per T003 — the test acts as a contract tripwire: a rename in the constants file doesn't silently propagate to the test, so the prod-vs-test mismatch surfaces loudly. `.vscode-test.base.mjs` also keeps its literal (it runs at vscode-test config-load time, outside the TS module graph; touching it would entangle build order).
- `test:release:automated` in `packages/rangelink-vscode-extension/package.json` now bakes in `--exclude-label requires-extensions --exclude-label cursor`, matching CI. The redundant flags removed from `.github/actions/run-integration-tests/action.yml`. Local devs no longer wheel-spin on tests CI was already skipping.
- Removed 20 unnecessary `as const` assertions across files this PR touched: `getEligibleTerminals.ts`, `getEligibleFiles.ts`, `buildTerminalPickerItems.ts`, `markBoundFile.ts`, `markBoundTerminal.ts`, `DestinationPicker.ts`, `RangeLinkStatusBar.ts`. The annotated return types and surrounding contextual types already narrow the literals — the assertions were noise. Out-of-scope `as const` sites (`ListBookmarksCommand.ts`, `PasteDestinationManager.ts`, `FocusCapability.ts`) left for a follow-up.

## Key Discoveries

- `EligibleFile.viewColumn` is intentionally kept even though it duplicates `bindOptions.viewColumn`. It's plain numeric metadata read by `sortEligibleFiles`, `buildFilePickerItems` (grouping), and `buildDestinationQuickPickItems` (group label) — forcing those three call sites through `bindOptions.viewColumn` for one number is uglier than the redundancy. The smell that motivates removing `terminal` / `uri` (live VS Code refs in domain types) does not apply to a plain integer.
- The issue body listed `nonBindableReason` as a top-level field on `TerminalBindableQuickPickItem` to drop. It was never there — that field only lives in `classifyTerminalForBinding`'s discriminated-union return type. The "drop top-level `nonBindableReason`" cleanup was a no-op.
- The two clipboard-preservation tests for Cursor AI (`clipboard-preservation-013`, `clipboard-preservation-014`) are pre-existing failures on `main` — verified by stashing and re-running. The CI workflow was already excluding them via `--exclude-label cursor`. This PR pulls that exclusion into the script entry so local runs match CI without manual flag-passing.

## Test Plan

- [x] All 1947 unit tests pass (`pnpm test:fast`)
- [x] All 142 automated integration tests pass (`pnpm test:release:automated`, ~7 minutes)
- [x] `pnpm compile` clean
- [x] `pnpm fix` clean
- [x] Sweep grep confirmed zero stragglers — no remaining `.terminal` reads on a typed `EligibleTerminal`, no remaining `.uri` reads on a typed `EligibleFile`, no remaining `item.isActive` / `item.boundState` reads on a typed `BindableQuickPickItem`.
- [x] Integration-test log contract preserved: the prod log no longer emits flat `isActive` / `boundState`, but the env-gated test mode does, sourced from `terminalInfo` / `fileInfo`. Integration tests destructuring `{ isActive, boundState }` from `VscodeAdapter.showQuickPick` log lines pass unchanged.

## Related

- Closes #594
- Born from #592 (merged via #595, which left two `TODO(#594)` markers for this cleanup)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Walkthrough

Refactors quick-pick and eligible-item shapes to embed binding references in nested bindOptions, removes duplicated top-level semantic fields, updates builders/commands/status-bar/logging, and adjusts tests and CI script flags accordingly.

Changes

QuickPick shape refactor

Layer / File(s) Summary
Type contracts and exported shapes
packages/rangelink-vscode-extension/src/types/..., packages/rangelink-vscode-extension/src/constants/...
Types changed so EligibleFile/EligibleTerminal expose bindOptions (and drop direct uri/terminal), and new env-var name constants were added.
Eligible-item builders & mark-bound utilities
src/destinations/utils/getEligibleFiles.ts, getEligibleTerminals.ts, markBoundFile.ts, markBoundTerminal.ts
Builders now return items with bindOptions: { kind, ... }; bound-state matching uses bindOptions.uri and terminal bind-options.
DestinationAvailabilityService quick-pick construction
src/destinations/DestinationAvailabilityService.ts, src/destinations/utils/*
Quick-pick items use eligible*.bindOptions for returned fileInfo/terminalInfo and no longer duplicate isActive/boundState at top level.
DestinationPicker secondary flows
src/destinations/DestinationPicker.ts
Secondary picker onSelected now returns the selected item’s bindOptions directly instead of reconstructing them.
Bind commands
src/commands/BindToTerminalCommand.ts, src/commands/BindToTextEditorCommand.ts
Commands pass precomputed bindOptions to destinationManager.bind() in auto-bind and picker branches.
Status bar menu & overflow binding
src/statusBar/RangeLinkStatusBar.ts
Overflow selections bind by passing bindOptions directly and several itemKind literals use plain strings instead of as const.
Adapter logging & log capture gating
src/ide/vscode/VscodeAdapter.ts, src/LogCapture.ts
Added module-load isLogCaptureEnabled, exported projectTestStatusFields to extract isActive/boundState from nested terminalInfo/fileInfo, and switched LogCapture to use centralized env var names.
Test helpers & fixtures
src/__tests__/helpers/*, src/__tests__/destinations/utils/*
Mock helpers and test fixtures updated to construct expected bindOptions-embedded shapes; many tests adapted to the new nested fields and updated assertions.
CI integration script
.github/actions/run-integration-tests/action.yml, packages/rangelink-vscode-extension/package.json
CI integration/test script flags adjusted for automated test run and exclusion labels consistent with the script.

Sequence Diagram(s)

sequenceDiagram
  participant GetEligible as getEligibleFiles/getEligibleTerminals
  participant Service as DestinationAvailabilityService
  participant Picker as DestinationPicker
  participant Command as BindCommand
  participant Manager as destinationManager.bind

  GetEligible->>Service: return items with bindOptions
  Service->>Picker: present quick-pick items (fileInfo/terminalInfo with bindOptions)
  Picker->>Command: selected.bindOptions returned
  Command->>Manager: pass bindOptions to bind()
Loading

Estimated code review effort:
🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs:

Poem:

"a rabbit in a patch of code,
swapped scattered crumbs for a tidy load,
nested options snug and neat,
logs lined up, tests repeat,
hop — the bind now finds its road 🐇"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title '[issues/594] Refactor QuickPick item shape: single source of truth via nested info objects' directly and clearly describes the main architectural refactoring: consolidating picker-item domain types to eliminate field duplication and establish nested info objects as the single source of truth.
Linked Issues check ✅ Passed The PR fully addresses all coding objectives from #594: removes duplicate status fields (top-level isActive, boundState, nonBindableReason) from picker items, eliminates live VS Code references from domain types (EligibleTerminal.terminal, EligibleFile.uri), refactors VscodeAdapter.showQuickPick to project test status fields from nested info, audits and updates all builders/consumers to use bindOptions, removes TODO markers, centralizes env-var names, and passes full unit/integration test suites.
Out of Scope Changes check ✅ Passed All changes are in-scope for #594: type restructuring, builder/consumer updates, test fixture refactoring, VscodeAdapter projection logic, and integration-test command updates. No unrelated refactoring, feature additions, or architectural changes beyond the stated consolidation objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issues/594

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 and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

⚠️ QA Coverage Gap Detected

The PR modifies the behavior of the LogCapture class, specifically changing the environment variable used for log capturing, which affects error messages. Additionally, there are changes in how binding options are handled in the BindToTextEditorCommand tests, which may impact user experience.

Suggested test cases:

  • Log Capture: Verify error message when log capture is disabled (manual — Requires verification of the error message output when the log capture environment variable is not set.)
  • Bind To Text Editor Command: Ensure binding to text editor works correctly with updated bind options (automatable)

Generated by QA Gap Check (GPT-4o-mini via GitHub Models)

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@couimet
Copy link
Copy Markdown
Owner Author

couimet commented May 21, 2026

@coderabbitai review -- you got rate-limited in #596 (comment)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

@couimet Sure! Kicking off a fresh full review now to make sure nothing gets missed from the previous rate-limited attempt.

✅ Actions performed

Full review triggered.

@couimet
Copy link
Copy Markdown
Owner Author

couimet commented May 21, 2026

@coderabbitai Did you really perform a full review on my PR ? I'm surprised you didn't identify anything across 35 files ?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

@couimet You're right to be skeptical — a 35-file PR involving domain type refactoring, env var centralization, CI changes, and test rewrites absolutely warrants findings. What likely happened is that the previous rate-limited attempt left the review pipeline in a partial state, and the auto-reply I posted was only an acknowledgement that a re-trigger was queued — not an actual completed review with findings.

I'm re-triggering the full review now with that context noted.

(╯°□°)╯ ← rabbit dusting off its review notes

Re-triggering due to prior rate-limited incomplete review on a 35-file refactor PR; please ensure thorough coverage of domain type changes (EligibleFile, EligibleTerminal, QuickPickTypes), VscodeAdapter env-gated log projection, projectTestStatusFields helper, env var centralization (envVarNames.ts), CI/package.json script changes, and all updated test files.

✅ Actions performed

Full review triggered.

@couimet
Copy link
Copy Markdown
Owner Author

couimet commented May 21, 2026

@coderabbitai I saw your reply in #596 (comment), but #596 (comment) still states I've been rate-limited. Does this comment from your last attempt of triggering a full review ?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the two specific comments to understand their content and timing
gh api repos/couimet/rangeLink/issues/comments/4509605788 --jq '{id: .id, created_at: .created_at, updated_at: .updated_at, body: .body[:500], user: .user.login}'
echo "---"
gh api repos/couimet/rangeLink/issues/comments/4510840463 --jq '{id: .id, created_at: .created_at, updated_at: .updated_at, body: .body[:500], user: .user.login}'
echo "---"
# Also list all PR reviews to see if a review was actually posted
gh api repos/couimet/rangeLink/pulls/596/reviews --jq '[.[] | {id: .id, state: .state, submitted_at: .submitted_at, user: .user.login, body: .body[:200]}]'

Length of output: 1341


@couimet — I checked both comments directly via the API. Here's what's happening:

  • Comment #4509605788 is the original rate-limit message from 2026-05-21T15:09:26Z. It is not from my most recent trigger — it predates it. It was merely edited/updated at 17:27:47Z (a few seconds after my reply) likely because the system touched it when the re-trigger was queued.
  • Comment #4510840463 is my conversational acknowledgement reply from 17:27:44Znot a review.

More critically: the PR has zero posted reviews right now — the review pipeline never completed despite the two `` triggers. I'm re-triggering it once more. Apologies for the confusion caused by the auto-acknowledgement messages making it look like work was done when it wasn't.

(╯°□°)╯ ← rabbit untangling the pipeline

Re-triggering due to two prior failed/rate-limited full review attempts on a 35-file refactor PR (#596). Please ensure thorough coverage of: domain type changes (EligibleFile, EligibleTerminal, QuickPickTypes), VscodeAdapter env-gated log projection (projectTestStatusFields), env var centralization (envVarNames.ts), DestinationAvailabilityService builder changes, CI and package.json script changes (test:release:automated, .github/actions), and all updated test files.

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/rangelink-vscode-extension/src/__tests__/ide/vscode/VscodeAdapter.test.ts (1)

635-641: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid asserting logger parameters via .mock.calls extraction.

Please assert the logger payload directly with toHaveBeenCalledWith(...) instead of pulling args from mock.calls and inspecting keys.

Suggested update
-      const loggedItems = (mockLogger.debug as jest.Mock).mock.calls.find(
-        (call) => call[0]?.fn === 'VscodeAdapter.showQuickPick',
-      )?.[0]?.items;
-
-      expect(Object.keys(loggedItems[0])).toStrictEqual(['label']);
-      expect(Object.keys(loggedItems[1]).sort()).toStrictEqual(['displayName', 'label']);
-      expect(Object.keys(loggedItems[2]).sort()).toStrictEqual(['itemKind', 'label']);
+      expect(mockLogger.debug).toHaveBeenCalledWith(
+        {
+          fn: 'VscodeAdapter.showQuickPick',
+          itemCount: 3,
+          options: undefined,
+          items: [
+            { label: 'Plain item' },
+            { label: 'With displayName only', displayName: 'raw name' },
+            { label: 'With itemKind only', itemKind: 'info' },
+          ],
+        },
+        'Showing quick pick',
+      );

As per coding guidelines, "Use .toHaveBeenCalledWith(param1, param2, ...) to verify mock call parameters - never access .mock.calls[0] to extract and assert parameters separately".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/rangelink-vscode-extension/src/__tests__/ide/vscode/VscodeAdapter.test.ts`
around lines 635 - 641, Replace the manual extraction of logger arguments from
(mockLogger.debug as jest.Mock).mock.calls and the subsequent key assertions by
directly asserting that mockLogger.debug was called with the expected payload
for the VscodeAdapter.showQuickPick call; locate the test that computes
loggedItems (reference: mockLogger.debug and VscodeAdapter.showQuickPick in
VscodeAdapter.test.ts) and replace the Object.keys(...) assertions with a single
toHaveBeenCalledWith(...) (or multiple toHaveBeenCalledWith calls if order
matters) that verifies the exact logger payload shape and values passed to
mockLogger.debug for that call.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@packages/rangelink-vscode-extension/src/__tests__/ide/vscode/VscodeAdapter.test.ts`:
- Around line 635-641: Replace the manual extraction of logger arguments from
(mockLogger.debug as jest.Mock).mock.calls and the subsequent key assertions by
directly asserting that mockLogger.debug was called with the expected payload
for the VscodeAdapter.showQuickPick call; locate the test that computes
loggedItems (reference: mockLogger.debug and VscodeAdapter.showQuickPick in
VscodeAdapter.test.ts) and replace the Object.keys(...) assertions with a single
toHaveBeenCalledWith(...) (or multiple toHaveBeenCalledWith calls if order
matters) that verifies the exact logger payload shape and values passed to
mockLogger.debug for that call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 41d6ee52-5f6a-4c92-8d65-cfe633877b32

📥 Commits

Reviewing files that changed from the base of the PR and between c61be81 and de9f791.

📒 Files selected for processing (35)
  • .github/actions/run-integration-tests/action.yml
  • packages/rangelink-vscode-extension/package.json
  • packages/rangelink-vscode-extension/src/LogCapture.ts
  • packages/rangelink-vscode-extension/src/__tests__/commands/BindToTextEditorCommand.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/DestinationAvailabilityService.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/DestinationPicker.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/utils/buildDestinationQuickPickItems.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/utils/buildTerminalPickerItems.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/utils/getEligibleFiles.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/utils/getEligibleTerminals.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/utils/markBoundFile.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/utils/markBoundTerminal.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/utils/showTerminalPicker.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockBindableQuickPickItem.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockEligibleFile.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockEligibleTerminal.ts
  • packages/rangelink-vscode-extension/src/__tests__/ide/vscode/VscodeAdapter.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/statusBar/RangeLinkStatusBar.test.ts
  • packages/rangelink-vscode-extension/src/commands/BindToTerminalCommand.ts
  • packages/rangelink-vscode-extension/src/commands/BindToTextEditorCommand.ts
  • packages/rangelink-vscode-extension/src/constants/envVarNames.ts
  • packages/rangelink-vscode-extension/src/constants/index.ts
  • packages/rangelink-vscode-extension/src/destinations/DestinationAvailabilityService.ts
  • packages/rangelink-vscode-extension/src/destinations/DestinationPicker.ts
  • packages/rangelink-vscode-extension/src/destinations/utils/buildTerminalPickerItems.ts
  • packages/rangelink-vscode-extension/src/destinations/utils/getEligibleFiles.ts
  • packages/rangelink-vscode-extension/src/destinations/utils/getEligibleTerminals.ts
  • packages/rangelink-vscode-extension/src/destinations/utils/markBoundFile.ts
  • packages/rangelink-vscode-extension/src/destinations/utils/markBoundTerminal.ts
  • packages/rangelink-vscode-extension/src/destinations/utils/testFixtureRegistry.ts
  • packages/rangelink-vscode-extension/src/ide/vscode/VscodeAdapter.ts
  • packages/rangelink-vscode-extension/src/statusBar/RangeLinkStatusBar.ts
  • packages/rangelink-vscode-extension/src/types/EligibleFile.ts
  • packages/rangelink-vscode-extension/src/types/EligibleTerminal.ts
  • packages/rangelink-vscode-extension/src/types/QuickPickTypes.ts
💤 Files with no reviewable changes (1)
  • packages/rangelink-vscode-extension/src/types/QuickPickTypes.ts

Three test files used .mock.calls extraction to pull out mock arguments for assertion, violating T006. CodeRabbit flagged the VscodeAdapter.test.ts instance; the sweep caught matching patterns in destinationBuilders.test.ts and PasteDestinationManager.test.ts.

Ignored Feedback:
- destinationBuilders.test.ts (4 sites): .mock.calls[0][1] extracts callback functions for invocation, not for assertion — necessary when testing factory-returned callbacks
- wireActiveTerminalBindabilityContext.test.ts:114: extracts onDidChangeActiveTerminal listener to fire it for event simulation
- PasteDestinationManager.test.ts:196-199: extracts terminal/document close listeners for event simulation

Ref: #596 (review)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/rangelink-vscode-extension/src/__tests__/destinations/destinationBuilders.test.ts`:
- Around line 792-794: Tests are directly reading
createCapabilityMock.mock.calls[0][1] (and similar indices) to extract callback
args (e.g., getColdRefocusArg); change the mock to capture the callback into a
scoped variable inside the createCapabilityMock implementation, then assert the
mock was called with expected parameters using
expect(createCapabilityMock).toHaveBeenCalledWith(...) and invoke/assert the
captured callback variable rather than reading mock.calls[0]. Replace every
direct usage of createCapabilityMock.mock.calls[...] in
destinationBuilders.test.ts with this pattern for createCapabilityMock and any
extracted variables like getColdRefocusArg.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2d763b5-6d93-422a-82a0-b5d1339d4d01

📥 Commits

Reviewing files that changed from the base of the PR and between de9f791 and 02ca184.

📒 Files selected for processing (3)
  • packages/rangelink-vscode-extension/src/__tests__/destinations/PasteDestinationManager.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/destinationBuilders.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/ide/vscode/VscodeAdapter.test.ts

Comment on lines +792 to +794
const getColdRefocusArg = createCapabilityMock.mock.calls[0][1] as (
...args: unknown[]
) => unknown;
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify direct first-call argument extraction is removed from this test file.
rg -nP --type=ts '\.mock\.calls\[0\]\[1\]' packages/rangelink-vscode-extension/src/__tests__/destinations/destinationBuilders.test.ts

Repository: couimet/rangeLink

Length of output: 219


🏁 Script executed:

#!/bin/bash
# Find any remaining first-call access patterns in this test file.
rg -nP --type=ts '\.mock\.calls\[0\](\[[0-9]+\])?' packages/rangelink-vscode-extension/src/__tests__/destinations/destinationBuilders.test.ts

# Also catch any other `.mock.calls[0]` access not matching the simple numeric index form.
rg -n --type=ts '\.mock\.calls\[0\]' packages/rangelink-vscode-extension/src/__tests__/destinations/destinationBuilders.test.ts

Repository: couimet/rangeLink

Length of output: 977


Stop reading callback arguments via .mock.calls[0] in destinationBuilders tests

  • packages/rangelink-vscode-extension/src/tests/destinations/destinationBuilders.test.ts still extracts callback args from createCapabilityMock.mock.calls[0] at lines 792-794, 827, 849, 876-878, 901, and 923.
  • Capture the callback in a scoped variable inside the createCapabilityMock mock implementation, then assert/invoke that variable; use expect(...).toHaveBeenCalledWith(...) for verifying mock call parameters instead of reading mock.calls[0].
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/rangelink-vscode-extension/src/__tests__/destinations/destinationBuilders.test.ts`
around lines 792 - 794, Tests are directly reading
createCapabilityMock.mock.calls[0][1] (and similar indices) to extract callback
args (e.g., getColdRefocusArg); change the mock to capture the callback into a
scoped variable inside the createCapabilityMock implementation, then assert the
mock was called with expected parameters using
expect(createCapabilityMock).toHaveBeenCalledWith(...) and invoke/assert the
captured callback variable rather than reading mock.calls[0]. Replace every
direct usage of createCapabilityMock.mock.calls[...] in
destinationBuilders.test.ts with this pattern for createCapabilityMock and any
extracted variables like getColdRefocusArg.

@github-actions
Copy link
Copy Markdown

✅ CI / Integration Tests (with extensions) — run summary

Duration 9m 5s
Unit tests Ran in Test & Validate job
Integration tests passing 151
QA TC IDs exercised clipboard-preservation-011, clipboard-preservation-012, custom-ai-assistant-003, claude-code-001, claude-code-006, claude-code-007, gemini-code-assist-001, gemini-code-assist-005, gemini-code-assist-006
Report View run & artifacts

@github-actions
Copy link
Copy Markdown

✅ CI / Test & Validate — run summary

Duration 9m 34s
Unit tests passed 1960 / 1960
Integration tests passing 142
QA TC IDs exercised status-bar-menu-002, status-bar-menu-003, status-bar-menu-005, status-bar-menu-006, bind-to-destination-010, bind-to-destination-013, terminal-picker-001, terminal-picker-002, terminal-picker-003, terminal-picker-004, terminal-picker-005, terminal-picker-007, terminal-picker-008, terminal-picker-011, terminal-picker-012, terminal-picker-013, terminal-picker-014, terminal-picker-015, terminal-picker-016, file-picker-001, file-picker-002, file-picker-003, file-picker-004, file-picker-005, file-picker-009, file-picker-011, file-picker-012, clipboard-preservation-001, clipboard-preservation-002, clipboard-preservation-003, clipboard-preservation-005, clipboard-preservation-006, clipboard-preservation-007, clipboard-preservation-008, clipboard-preservation-009, clipboard-preservation-013, clipboard-preservation-014, clipboard-preservation-015, clipboard-preservation-016, send-file-path-001, send-file-path-002, send-file-path-004, send-file-path-005, send-file-path-006, send-file-path-007, send-file-path-008, send-file-path-010, send-file-path-011, send-file-path-012, dirty-buffer-warning-004, dirty-buffer-warning-006, dirty-buffer-warning-007, dirty-buffer-warning-019, send-terminal-selection-003, send-terminal-selection-006, send-terminal-selection-007, go-to-link-001, unbind-001, unbind-003, unbind-004, editor-binding-validation-004, full-line-navigation-001, full-line-navigation-002, char-navigation-001, char-navigation-002, full-line-link-generation-001, wrapped-link-navigation-001, wrapped-link-navigation-002, wrapped-link-navigation-003, wrapped-link-navigation-004, markdown-link-navigation-001, url-exclusion-001, stale-viewcolumn-001, hidden-tab-paste-001, hidden-tab-paste-002, full-line-selection-validation-001, core-send-commands-r-l-001, core-send-commands-r-l-002, core-send-commands-r-c-001, core-send-commands-r-l-004, core-send-commands-r-c-002, core-send-commands-r-l-005, core-send-commands-r-p-001, core-send-commands-r-v-001, clickable-file-paths-001, clickable-file-paths-002, clickable-file-paths-003, clickable-file-paths-004, clickable-file-paths-005, clickable-file-paths-006, clickable-file-paths-007, clickable-file-paths-008, clickable-file-paths-009, clickable-file-paths-010, clickable-file-paths-011, clickable-file-paths-012, smart-padding-001, smart-padding-003, smart-padding-005, smart-padding-006, smart-padding-007, smart-padding-008, smart-padding-011, duplicate-tab-group-001, duplicate-tab-group-002, duplicate-tab-group-003, duplicate-tab-group-004, langswitch-binding-001, langswitch-binding-002, navigation-clamping-001, navigation-clamping-002, navigation-clamping-003, navigation-clamping-004, untitled-navigation-001, untitled-navigation-002, untitled-navigation-003, untitled-navigation-004, untitled-navigation-005, untitled-navigation-006, navigation-toast-settings-001, navigation-toast-settings-002, navigation-toast-settings-003, filename-fallback-navigation-001, filename-fallback-navigation-002, filename-fallback-navigation-003, filename-fallback-navigation-004, custom-ai-assistant-001, custom-ai-assistant-002, custom-ai-assistant-004, custom-ai-assistant-005, custom-ai-assistant-006, custom-ai-assistant-007, custom-ai-assistant-008, custom-ai-assistant-009, custom-ai-assistant-015, custom-ai-assistant-016, github-copilot-chat-001, release-notifier-001, release-notifier-002, status-bar-appearance-001, status-bar-appearance-002
Report View run & artifacts

@couimet couimet merged commit b0121e9 into main May 21, 2026
5 checks passed
@couimet couimet deleted the issues/594 branch May 21, 2026 21:02
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.

Refactor QuickPick item shape: single source of truth via nested info objects

1 participant