Skip to content

[side-quest/integration-test-utilities-and-cleanup] Integration test utilities and copy-pasta cleanup#618

Open
couimet wants to merge 12 commits into
mainfrom
improve/integration-test-utilities-and-cleanup
Open

[side-quest/integration-test-utilities-and-cleanup] Integration test utilities and copy-pasta cleanup#618
couimet wants to merge 12 commits into
mainfrom
improve/integration-test-utilities-and-cleanup

Conversation

@couimet
Copy link
Copy Markdown
Owner

@couimet couimet commented Jun 3, 2026

Summary

Adds reusable assertion helpers (withClipboardSentinel, withClipboardChanged, withClipboardRestored, assertClipboardEqualsGeneratedLink, assertTerminalBufferEqualsGeneratedLink, assertTerminalBufferContainsGeneratedLink, assertClipboardPreservedAndTerminalLink, assertLogContains) and converts all call sites across test suite files to use them.

Changes

  • New assertion helpers in clipboardHelpers.ts, capturingPtyHelpers.ts, logBasedUiAssertions.ts, and logHelpers.ts eliminate repeated clipboard/terminal/log assertion boilerplate
  • Converted integration test suites to use the new helpers, reducing copy-pasta and making test intent more readable
  • Fixed broken tests and smart padding issues uncovered during the conversion campaign
  • Updated CLAUDE.md with shell-quoting gotcha and :with-extensions --grep documentation
  • Removed expectContextKeys calls from test suites — those belong to the ContextKeyService extraction (PR Unify jest.config.js files to explicitly call-out some configs #3) and would fail without that infrastructure

Test Plan

  • All 1982 unit tests pass
  • Integration tests pass: pnpm test:release:automated
  • Integration tests pass with extensions: pnpm test:release:with-extensions

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved clipboard preservation and restoration reliability when copying/pasting RangeLinks
    • Added error handling for clipboard operation failures with user-facing warning messages
  • Tests

    • Expanded integration test coverage for clipboard preservation scenarios across multiple destinations
    • Added comprehensive test suites for clipboard service operations and terminal paste functionality
    • Updated QA test cases to verify clipboard behavior with refined preservation settings
  • Chores

    • Refactored clipboard handling architecture for improved reliability
    • Enhanced error code definitions for clipboard-related operations

…utilities and copy-pasta cleanup

Adds reusable assertion helpers (`withClipboardSentinel`, `withClipboardChanged`, `withClipboardRestored`, `assertClipboardEqualsGeneratedLink`, `assertTerminalBufferEqualsGeneratedLink`, `assertTerminalBufferContainsGeneratedLink`, `assertClipboardPreservedAndTerminalLink`, `assertLogContains`) and converts all call sites across test suite files to use them.

## Changes

- New assertion helpers in `clipboardHelpers.ts`, `capturingPtyHelpers.ts`, `logBasedUiAssertions.ts`, and `logHelpers.ts` eliminate repeated clipboard/terminal/log assertion boilerplate
- Converted integration test suites to use the new helpers, reducing copy-pasta and making test intent more readable
- Fixed broken tests and smart padding issues uncovered during the conversion campaign
- Updated CLAUDE.md with shell-quoting gotcha and `:with-extensions --grep` documentation
- Removed `expectContextKeys` calls from test suites — those belong to the ContextKeyService extraction (PR #3) and would fail without that infrastructure

## Test Plan

- [ ] All 1982 unit tests pass
- [ ] Integration tests pass: `pnpm test:release:automated`
- [ ] Integration tests pass with extensions: `pnpm test:release:with-extensions`
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 2026

⚠️ QA Coverage Gap Detected

New error codes related to clipboard operations were introduced, and existing test cases were modified to reflect changes in clipboard behavior. Additional test cases may be needed to cover new error handling.

Suggested test cases:

  • Clipboard Error Handling: Verify that the application handles clipboard errors correctly when clipboard operations fail (manual — Requires verification of error messages displayed to the user, which cannot be automated.)
  • Clipboard Functionality: Ensure clipboard captures and restores content correctly under various conditions (automatable)

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

@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

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

Duration: 1m 6s

QA TC IDs: 2 exercised across 1 features

Report: View run & artifacts

Reproduce locally: ./scripts/run-integration-tests.sh --label needs-override --exclude-label cursor --exclude-assisted

Feature breakdown
Feature TCs IDs
custom-ai-assistants 2 custom-ai-assistant-018, custom-ai-assistant-019

Repository owner deleted a comment from github-actions Bot Jun 8, 2026
Repository owner deleted a comment from github-actions Bot Jun 8, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

✅ CI / Integration Tests (automated) — run summary

Duration: 11m 1s

QA TC IDs: 157 exercised across 0 features

Report: View run & artifacts

Reproduce locally: ./scripts/run-integration-tests.sh --exclude-label requires-extensions --exclude-label cursor --automated

Feature breakdown
Feature TCs IDs

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 8, 2026

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

Duration: 12m 20s

Unit tests: Ran in separate Test & Validate job

QA TC IDs: 189 exercised across 25 features

Report: View run & artifacts

Reproduce locally: ./scripts/run-integration-tests.sh --exclude-label cursor --exclude-label needs-override --exclude-assisted

Feature breakdown
Feature TCs IDs
bind-to-destination 9 bind-to-destination-004, bind-to-destination-005, bind-to-destination-006, bind-to-destination-007, bind-to-destination-010, bind-to-destination-011, bind-to-destination-013, bind-to-destination-014, bind-to-destination-015
built-in-ai-assistants 7 claude-code-001, claude-code-006, claude-code-007, gemini-code-assist-001, gemini-code-assist-005, gemini-code-assist-006, github-copilot-chat-001
clickable-file-paths 12 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
clipboard-preservation 19 clipboard-preservation-001, clipboard-preservation-002, clipboard-preservation-003, clipboard-preservation-004, clipboard-preservation-005, clipboard-preservation-006, clipboard-preservation-007, clipboard-preservation-008, clipboard-preservation-009, clipboard-preservation-010, clipboard-preservation-011, clipboard-preservation-012, clipboard-preservation-015, clipboard-preservation-016, clipboard-preservation-017, clipboard-preservation-019, clipboard-preservation-020, clipboard-preservation-021, clipboard-preservation-022
context-menus 3 context-menus-editor-content-012, context-menus-editor-content-013, context-menus-editor-content-014
core-send-commands 9 core-send-commands-r-c-001, core-send-commands-r-c-002, core-send-commands-r-l-001, core-send-commands-r-l-002, core-send-commands-r-l-003, core-send-commands-r-l-004, core-send-commands-r-l-005, core-send-commands-r-p-001, core-send-commands-r-v-001
custom-ai-assistants 17 custom-ai-assistant-001, custom-ai-assistant-002, custom-ai-assistant-003, 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-010, custom-ai-assistant-011, custom-ai-assistant-012, custom-ai-assistant-013, custom-ai-assistant-014, custom-ai-assistant-015, custom-ai-assistant-016, custom-ai-assistant-017
dirty-buffer-warning 4 dirty-buffer-warning-004, dirty-buffer-warning-006, dirty-buffer-warning-007, dirty-buffer-warning-019
editor-binding-validation 1 editor-binding-validation-004
file-picker 8 file-picker-001, file-picker-002, file-picker-003, file-picker-004, file-picker-005, file-picker-009, file-picker-011, file-picker-012
filename-only-navigation-fallback 4 filename-fallback-navigation-001, filename-fallback-navigation-002, filename-fallback-navigation-003, filename-fallback-navigation-004
go-to-link 5 go-to-link-001, go-to-link-005, go-to-link-006, go-to-link-008, go-to-link-009
link-generation 2 full-line-link-generation-001, full-line-selection-validation-001
link-navigation 10 char-navigation-001, char-navigation-002, full-line-navigation-001, full-line-navigation-002, markdown-link-navigation-001, url-exclusion-001, wrapped-link-navigation-001, wrapped-link-navigation-002, wrapped-link-navigation-003, wrapped-link-navigation-004
navigation-clamping-feedback 4 navigation-clamping-001, navigation-clamping-002, navigation-clamping-003, navigation-clamping-004
navigation-toast-settings 3 navigation-toast-settings-001, navigation-toast-settings-002, navigation-toast-settings-003
release-notifier 2 release-notifier-001, release-notifier-002
send-file-path 15 send-file-path-001, send-file-path-002, send-file-path-003, send-file-path-004, send-file-path-005, send-file-path-006, send-file-path-007, send-file-path-008, send-file-path-009, send-file-path-010, send-file-path-011, send-file-path-012, send-file-path-013, send-file-path-014, send-file-path-015, send-file-path-016
send-terminal-selection 3 send-terminal-selection-002, send-terminal-selection-003, send-terminal-selection-006, send-terminal-selection-007
smart-padding 8 smart-padding-001, smart-padding-003, smart-padding-005, smart-padding-006, smart-padding-007, smart-padding-008, smart-padding-009, smart-padding-011
status-bar-menu 7 status-bar-appearance-001, status-bar-appearance-002, status-bar-menu-002, status-bar-menu-003, status-bar-menu-004, status-bar-menu-005, status-bar-menu-006, status-bar-menu-007
terminal-picker 13 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
text-editor-destination 11 duplicate-tab-group-001, duplicate-tab-group-002, duplicate-tab-group-003, duplicate-tab-group-004, hidden-tab-paste-001, hidden-tab-paste-002, langswitch-binding-001, langswitch-binding-002, stale-viewcolumn-001, text-editor-destination-001, text-editor-destination-002
unbind 3 unbind-001, unbind-002, unbind-003, unbind-004
untitled-file-navigation 6 untitled-navigation-001, untitled-navigation-002, untitled-navigation-003, untitled-navigation-004, untitled-navigation-005, untitled-navigation-006

Repository owner deleted a comment from coderabbitai Bot Jun 8, 2026
@couimet
Copy link
Copy Markdown
Owner Author

couimet commented Jun 8, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 8, 2026

✅ Action performed

Full review finished.

Repository owner deleted a comment from coderabbitai Bot Jun 8, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 8, 2026

Review Change Stack

Walkthrough

This PR replaces clipboard-preserver plumbing with a new ClipboardService, adds terminal paste orchestration, and updates related error codes, messages, wiring, QA cases, and tests to use result-based flows and shared generated-link assertions.

Changes

Clipboard and terminal routing refactor

Layer / File(s) Summary
Contracts and public surface
CLAUDE.md, packages/rangelink-core-ts/src/errors/RangeLinkErrorCodes.ts, packages/rangelink-vscode-extension/src/errors/RangeLinkExtensionErrorCodes.ts, packages/rangelink-vscode-extension/src/feedback/types/PasteSendOutcome.ts, packages/rangelink-vscode-extension/src/types/MessageCode.ts, packages/rangelink-vscode-extension/src/i18n/messages.en.ts, packages/rangelink-vscode-extension/src/ide/TerminalPasteAdapter.ts, packages/rangelink-vscode-extension/src/ide/index.ts, packages/rangelink-vscode-extension/src/clipboard/index.ts, packages/rangelink-vscode-extension/src/services/index.ts, packages/rangelink-vscode-extension/src/utils/index.ts, packages/rangelink-vscode-extension/src/utils/validateTerminalDefined.ts, packages/rangelink-vscode-extension/src/constants/aiAssistantPasteConstants.ts
New clipboard/terminal error codes, message keys, adapter and utility exports, and release-test instructions are added or updated.
Clipboard service and wiring
packages/rangelink-vscode-extension/src/clipboard/ClipboardService.ts, packages/rangelink-vscode-extension/src/clipboard/DefaultClipboardPreserver.ts, packages/rangelink-vscode-extension/src/createWiringServices.ts, packages/rangelink-vscode-extension/src/services/SendRouter.ts, packages/rangelink-vscode-extension/src/services/FilePathPaster.ts, packages/rangelink-vscode-extension/src/services/LinkGenerator.ts, packages/rangelink-vscode-extension/src/services/TerminalSelectionService.ts, packages/rangelink-vscode-extension/src/ide/vscode/VscodeAdapter.ts, packages/rangelink-vscode-extension/src/utils/clipboardPreservation.ts
Clipboard preservation is moved into ClipboardService, old preserver utilities are removed, and send/file-path/link-generation flows use the new clipboard payload behavior.
Terminal flow and destinations
packages/rangelink-vscode-extension/src/services/TerminalPasteService.ts, packages/rangelink-vscode-extension/src/destinations/capabilities/FocusCapabilityFactory.ts, packages/rangelink-vscode-extension/src/destinations/capabilities/TerminalFocusCapability.ts, packages/rangelink-vscode-extension/src/destinations/capabilities/insertFactories/terminalInsertFactory.ts, packages/rangelink-vscode-extension/src/destinations/destinationBuilders.ts, packages/rangelink-vscode-extension/src/ide/vscode/VscodeAdapter.ts
Terminal focusing, destination building, and terminal insertion now use result-returning validation and a dedicated terminal paste service.
Integration helpers
packages/rangelink-vscode-extension/src/__integration-tests__/helpers/clipboardHelpers.ts, packages/rangelink-vscode-extension/src/__integration-tests__/helpers/capturingPtyHelpers.ts, packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logHelpers.ts, packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts, packages/rangelink-vscode-extension/src/__tests__/helpers/*
Shared test helpers and mock factories are added or reshaped for clipboard service, terminal paste, generated-link, and log assertions.
Integration suites
packages/rangelink-vscode-extension/src/__integration-tests__/suite/*, packages/rangelink-vscode-extension/qa/qa-test-cases-unreleased.yaml
Integration suites and QA cases switch to the new helper style, generated-link assertions, clipboard sentinel wrappers, and updated clipboard-preserve expectations.
Service and adapter tests
packages/rangelink-vscode-extension/src/__tests__/*
Unit and integration tests are updated for clipboard service behavior, terminal paste behavior, new error/result contracts, and the new send-router outcome.

Sequence Diagram(s)

sequenceDiagram
  participant SendRouter
  participant ClipboardService
  participant TerminalPasteService
  participant VscodeAdapter
  SendRouter->>ClipboardService: route clipboard work
  TerminalPasteService->>ClipboardService: stage clipboard text
  TerminalPasteService->>VscodeAdapter: showTerminal and executeCommand
  ClipboardService->>VscodeAdapter: read/write/restore clipboard
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Possibly related PRs

  • couimet/rangeLink#499: Touches the same clipboard helper layer in __integration-tests__/helpers/clipboardHelpers.ts.
  • couimet/rangeLink#521: Modifies the same logBasedUiAssertions.ts context-key assertions and related Explorer tests.
  • couimet/rangeLink#386: Directly related clipboard-preservation refactor that this PR builds on.

Poem

🐰 I hopped through logs and terminal light,
New clipboard paths now feel just right.
I nibbled tests and left them neat,
With generated links that land complete.
Hooray, the carrots of code are bright!

✨ 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 improve/integration-test-utilities-and-cleanup

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: 4

🧹 Nitpick comments (3)
packages/rangelink-vscode-extension/src/__tests__/helpers/createMockTerminalPasteService.ts (1)

5-8: ⚡ Quick win

Avoid unknown double-cast in test helper mocks.

as unknown as ... suppresses interface drift checks. Prefer a typed mock object (or satisfies) so added service methods fail at compile time.

Suggested patch
 export const createMockTerminalPasteService = (): jest.Mocked<TerminalPasteService> =>
-  ({
+  ({
     pasteIntoTerminal: jest.fn().mockResolvedValue(Result.ok(undefined)),
-  }) as unknown as jest.Mocked<TerminalPasteService>;
+  }) satisfies jest.Mocked<TerminalPasteService>;
🤖 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__/helpers/createMockTerminalPasteService.ts`
around lines 5 - 8, The helper createMockTerminalPasteService uses an unsafe "as
unknown as jest.Mocked<TerminalPasteService>" double-cast which hides interface
drift; replace it with a properly typed mock object so missing/extra methods
fail at compile time—e.g., construct a const mock:
jest.Mocked<TerminalPasteService> (or use the TypeScript "satisfies" operator)
and assign pasteIntoTerminal: jest.fn().mockResolvedValue(Result.ok(undefined)),
then return that mock from createMockTerminalPasteService; reference
TerminalPasteService, createMockTerminalPasteService, and pasteIntoTerminal to
locate the change.
packages/rangelink-vscode-extension/src/__integration-tests__/suite/builtInAiAssistants.test.ts (1)

1010-1019: ⚡ Quick win

Apply assertLogContains consistently across validation tests.

The claude-code-007 test (lines 901-905) was updated to use the new assertLogContains helper, but this nearly identical validation test for Gemini still uses manual log scanning. Apply the same refactor here for consistency.

♻️ Refactor to match claude-code-007 pattern
-    const lines = logCapture.getLinesSince('before-gc-006');
-    const warningLog = lines.find(
-      (line) =>
-        line.includes('coldStartDelayMs must be greater than coldRefocusIntervalMs') &&
-        line.includes('using defaults'),
-    );
-    assert.ok(
-      warningLog,
-      'Expected validation warning log with "using defaults" when coldStartDelayMs <= coldRefocusIntervalMs',
-    );
+    assertLogContains(
+      'before-gc-006',
+      'coldStartDelayMs must be greater than coldRefocusIntervalMs',
+      'Expected validation warning log when coldStartDelayMs <= coldRefocusIntervalMs',
+    );
🤖 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/__integration-tests__/suite/builtInAiAssistants.test.ts`
around lines 1010 - 1019, Replace the manual log scanning in the Gemini
validation test with the shared helper: find the test block that currently calls
logCapture.getLinesSince('before-gc-006') and assert.ok(...) and use the
assertLogContains helper (same usage as in the claude-code-007 test) to assert
the presence of the warning "coldStartDelayMs must be greater than
coldRefocusIntervalMs" together with "using defaults"; update the test to call
assertLogContains(logCapture, 'before-gc-006', 'coldStartDelayMs must be greater
than coldRefocusIntervalMs', 'using defaults') (or the project's existing helper
signature) so the assertion is consistent and more readable.
packages/rangelink-vscode-extension/src/__integration-tests__/suite/smartPadding.test.ts (1)

373-412: Clarify smart-padding-009 pasteLink setup vs default

  • smart-padding-009 doesn’t explicitly set rangelink.smartPadding.pasteLink, but packages/rangelink-vscode-extension/package.json sets its default to "both", so the test’s pasteLink=both title and getGeneratedLink(..., { smartPad: 'both' }) assertion are consistent.
  • Add an explicit smartPadding.pasteLink = "both" (or rename the test to “default”) to make the intent resilient to future default changes.
🤖 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/__integration-tests__/suite/smartPadding.test.ts`
around lines 373 - 412, The test smart-padding-009 relies on the default
smartPadding.pasteLink value being "both" but doesn't set it explicitly; update
the test (inside the smart-padding-009 block, before calling
CMD_COPY_LINK_RELATIVE) to explicitly set the extension setting
rangelink.smartPadding.pasteLink to "both" (e.g., via the test harness or VS
Code workspace configuration API) so the assertion using
getGeneratedLink('before-pad-009', { smartPad: 'both' }) and the test title
remain correct even if defaults change.
🤖 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-core-ts/src/errors/RangeLinkErrorCodes.ts`:
- Around line 20-28: Within the Clipboard errors block of
RangeLinkErrorCodes.ts, reorder the enum members so they are alphabetically
sorted: move CLIPBOARD_FN_EXECUTION_FAILED to come immediately after
CLIPBOARD_CAPTURE_EXECUTION_FAILED and before CLIPBOARD_READ_FAILED; ensure the
Clipboard section (CLIPBOARD_CAPTURE_EXECUTION_FAILED,
CLIPBOARD_FN_EXECUTION_FAILED, CLIPBOARD_READ_FAILED,
CLIPBOARD_RESTORE_WRITE_FAILED, CLIPBOARD_STAGE_WRITE_FAILED) follows the file's
"alphabetical order within each category" rule.

In
`@packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts`:
- Around line 370-374: Replace the substring-only assertions that look for
"Focus failed, cannot paste link" with structured-context checks: capture the
same logged line(s), call parseLogContext(...) on the captured log entry and
assert the resulting ctx.reason === 'COMMAND_FOCUS_FAILED' and ctx.fn (or
equivalent function identifier emitted by
ComposablePasteDestination.performPaste) matches the paste operation; do this
for the assertions currently around the focus-failure check (the block
exercising ComposablePasteDestination.performPaste) and the second occurrence so
the test verifies the structured context instead of only the message substring.

In
`@packages/rangelink-vscode-extension/src/__tests__/destinations/capabilities/insertFactories/terminalInsertFactory.test.ts`:
- Around line 58-83: The test for TerminalInsertFactory.forTarget currently
verifies pasteIntoTerminal calls but omits logger assertions; update the test to
assert that mockLogger.info was called for each insert with the appropriate
messages/arguments (one for 'content for terminal 1' targeting terminal1 and one
for 'content for terminal 2' targeting terminal2), and keep the existing
mockLogger.error assertion; locate the test using TerminalInsertFactory,
forTarget, insertFn1/insertFn2 and add expectations on mockLogger.info to match
the per-terminal success log contract.

In
`@packages/rangelink-vscode-extension/src/__tests__/helpers/createMockClipboardService.ts`:
- Around line 7-16: The mock ClipboardService functions stage, route, and
capture currently allow rejected callback promises to bubble as thrown
rejections; update each to catch errors and return a Result.err(...) instead of
throwing so the mock matches the real service contract — specifically modify the
stage mock (jest.fn((_text, fn) => ...)), the route mock (jest.fn((fn,
_shouldRestore) => ...)) and the capture mock (jest.fn((producer) => ...)) to
wrap their fn()/producer() calls with .then(...).catch(err => Result.err(err))
and ensure the resolved success path still returns Result.ok(...).

---

Nitpick comments:
In
`@packages/rangelink-vscode-extension/src/__integration-tests__/suite/builtInAiAssistants.test.ts`:
- Around line 1010-1019: Replace the manual log scanning in the Gemini
validation test with the shared helper: find the test block that currently calls
logCapture.getLinesSince('before-gc-006') and assert.ok(...) and use the
assertLogContains helper (same usage as in the claude-code-007 test) to assert
the presence of the warning "coldStartDelayMs must be greater than
coldRefocusIntervalMs" together with "using defaults"; update the test to call
assertLogContains(logCapture, 'before-gc-006', 'coldStartDelayMs must be greater
than coldRefocusIntervalMs', 'using defaults') (or the project's existing helper
signature) so the assertion is consistent and more readable.

In
`@packages/rangelink-vscode-extension/src/__integration-tests__/suite/smartPadding.test.ts`:
- Around line 373-412: The test smart-padding-009 relies on the default
smartPadding.pasteLink value being "both" but doesn't set it explicitly; update
the test (inside the smart-padding-009 block, before calling
CMD_COPY_LINK_RELATIVE) to explicitly set the extension setting
rangelink.smartPadding.pasteLink to "both" (e.g., via the test harness or VS
Code workspace configuration API) so the assertion using
getGeneratedLink('before-pad-009', { smartPad: 'both' }) and the test title
remain correct even if defaults change.

In
`@packages/rangelink-vscode-extension/src/__tests__/helpers/createMockTerminalPasteService.ts`:
- Around line 5-8: The helper createMockTerminalPasteService uses an unsafe "as
unknown as jest.Mocked<TerminalPasteService>" double-cast which hides interface
drift; replace it with a properly typed mock object so missing/extra methods
fail at compile time—e.g., construct a const mock:
jest.Mocked<TerminalPasteService> (or use the TypeScript "satisfies" operator)
and assign pasteIntoTerminal: jest.fn().mockResolvedValue(Result.ok(undefined)),
then return that mock from createMockTerminalPasteService; reference
TerminalPasteService, createMockTerminalPasteService, and pasteIntoTerminal to
locate the change.
🪄 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: 31cf6a5f-cba8-441e-888e-c337c1d30f71

📥 Commits

Reviewing files that changed from the base of the PR and between f81c098 and fd7c2b2.

📒 Files selected for processing (72)
  • CLAUDE.md
  • packages/rangelink-core-ts/src/errors/RangeLinkErrorCodes.ts
  • packages/rangelink-vscode-extension/qa/qa-test-cases-unreleased.yaml
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/capturingPtyHelpers.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/clipboardHelpers.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logHelpers.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/bindToDestination.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/builtInAiAssistants.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorContent.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorTab.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuExplorer.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuTerminal.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/coreSendCommands.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/customAiAssistants.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/dirtyBufferWarning.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/linkGeneration.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/sendFilePath.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/smartPadding.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/textEditorDestination.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/unbind.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/clipboard/ClipboardService.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/clipboard/DefaultClipboardPreserver.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/ComposablePasteDestination.integration.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/capabilities/FocusCapabilityFactory.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/capabilities/TerminalFocusCapability.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/capabilities/insertFactories/terminalInsertFactory.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/destinations/destinationBuilders.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/feedback/OperationFeedbackProvider.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockClipboardPreserver.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockClipboardService.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockClipboardWriter.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockOperationFeedbackProvider.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockPasteDestinationForSendRouter.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/createMockTerminalPasteService.ts
  • packages/rangelink-vscode-extension/src/__tests__/helpers/index.ts
  • packages/rangelink-vscode-extension/src/__tests__/ide/vscode/VscodeAdapter.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/services/FilePathPaster.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/services/LinkGenerator.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/services/SendRouter.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/services/TerminalPasteService.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/services/TerminalSelectionService.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/utils/clipboardPreservation.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/utils/validateTerminalDefined.test.ts
  • packages/rangelink-vscode-extension/src/clipboard/ClipboardPreserver.ts
  • packages/rangelink-vscode-extension/src/clipboard/ClipboardService.ts
  • packages/rangelink-vscode-extension/src/clipboard/DefaultClipboardPreserver.ts
  • packages/rangelink-vscode-extension/src/clipboard/index.ts
  • packages/rangelink-vscode-extension/src/constants/aiAssistantPasteConstants.ts
  • packages/rangelink-vscode-extension/src/createWiringServices.ts
  • packages/rangelink-vscode-extension/src/destinations/capabilities/FocusCapabilityFactory.ts
  • packages/rangelink-vscode-extension/src/destinations/capabilities/TerminalFocusCapability.ts
  • packages/rangelink-vscode-extension/src/destinations/capabilities/insertFactories/terminalInsertFactory.ts
  • packages/rangelink-vscode-extension/src/destinations/destinationBuilders.ts
  • packages/rangelink-vscode-extension/src/errors/RangeLinkExtensionErrorCodes.ts
  • packages/rangelink-vscode-extension/src/feedback/OperationFeedbackProvider.ts
  • packages/rangelink-vscode-extension/src/feedback/types/PasteSendOutcome.ts
  • packages/rangelink-vscode-extension/src/i18n/messages.en.ts
  • packages/rangelink-vscode-extension/src/ide/TerminalPasteAdapter.ts
  • packages/rangelink-vscode-extension/src/ide/index.ts
  • packages/rangelink-vscode-extension/src/ide/vscode/VscodeAdapter.ts
  • packages/rangelink-vscode-extension/src/services/FilePathPaster.ts
  • packages/rangelink-vscode-extension/src/services/LinkGenerator.ts
  • packages/rangelink-vscode-extension/src/services/SendRouter.ts
  • packages/rangelink-vscode-extension/src/services/TerminalPasteService.ts
  • packages/rangelink-vscode-extension/src/services/TerminalSelectionService.ts
  • packages/rangelink-vscode-extension/src/services/index.ts
  • packages/rangelink-vscode-extension/src/types/MessageCode.ts
  • packages/rangelink-vscode-extension/src/utils/clipboardPreservation.ts
  • packages/rangelink-vscode-extension/src/utils/index.ts
  • packages/rangelink-vscode-extension/src/utils/validateTerminalDefined.ts
💤 Files with no reviewable changes (8)
  • packages/rangelink-vscode-extension/src/tests/clipboard/DefaultClipboardPreserver.test.ts
  • packages/rangelink-vscode-extension/src/clipboard/ClipboardPreserver.ts
  • packages/rangelink-vscode-extension/src/tests/utils/clipboardPreservation.test.ts
  • packages/rangelink-vscode-extension/src/clipboard/DefaultClipboardPreserver.ts
  • packages/rangelink-vscode-extension/src/tests/helpers/createMockClipboardPreserver.ts
  • packages/rangelink-vscode-extension/src/integration-tests/suite/contextMenuExplorer.test.ts
  • packages/rangelink-vscode-extension/src/utils/clipboardPreservation.ts
  • packages/rangelink-vscode-extension/src/integration-tests/suite/contextMenuEditorTab.test.ts

Comment on lines +20 to +28
//
// Clipboard errors
//
CLIPBOARD_CAPTURE_EXECUTION_FAILED = 'CLIPBOARD_CAPTURE_EXECUTION_FAILED',
CLIPBOARD_READ_FAILED = 'CLIPBOARD_READ_FAILED',
CLIPBOARD_RESTORE_WRITE_FAILED = 'CLIPBOARD_RESTORE_WRITE_FAILED',
CLIPBOARD_STAGE_WRITE_FAILED = 'CLIPBOARD_STAGE_WRITE_FAILED',
CLIPBOARD_FN_EXECUTION_FAILED = 'CLIPBOARD_FN_EXECUTION_FAILED',

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

Clipboard error codes are not alphabetically sorted.

The file's header documentation (line 17) states "Please keep alphabetical order within each category for ease of maintenance." Within the Clipboard errors category, the correct alphabetical order should be:

  1. CLIPBOARD_CAPTURE_EXECUTION_FAILED
  2. CLIPBOARD_FN_EXECUTION_FAILED ← should be here (not at end)
  3. CLIPBOARD_READ_FAILED
  4. CLIPBOARD_RESTORE_WRITE_FAILED
  5. CLIPBOARD_STAGE_WRITE_FAILED
📋 Proposed fix to restore alphabetical order
   //
   // Clipboard errors
   //
   CLIPBOARD_CAPTURE_EXECUTION_FAILED = 'CLIPBOARD_CAPTURE_EXECUTION_FAILED',
+  CLIPBOARD_FN_EXECUTION_FAILED = 'CLIPBOARD_FN_EXECUTION_FAILED',
   CLIPBOARD_READ_FAILED = 'CLIPBOARD_READ_FAILED',
   CLIPBOARD_RESTORE_WRITE_FAILED = 'CLIPBOARD_RESTORE_WRITE_FAILED',
   CLIPBOARD_STAGE_WRITE_FAILED = 'CLIPBOARD_STAGE_WRITE_FAILED',
-  CLIPBOARD_FN_EXECUTION_FAILED = 'CLIPBOARD_FN_EXECUTION_FAILED',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//
// Clipboard errors
//
CLIPBOARD_CAPTURE_EXECUTION_FAILED = 'CLIPBOARD_CAPTURE_EXECUTION_FAILED',
CLIPBOARD_READ_FAILED = 'CLIPBOARD_READ_FAILED',
CLIPBOARD_RESTORE_WRITE_FAILED = 'CLIPBOARD_RESTORE_WRITE_FAILED',
CLIPBOARD_STAGE_WRITE_FAILED = 'CLIPBOARD_STAGE_WRITE_FAILED',
CLIPBOARD_FN_EXECUTION_FAILED = 'CLIPBOARD_FN_EXECUTION_FAILED',
//
// Clipboard errors
//
CLIPBOARD_CAPTURE_EXECUTION_FAILED = 'CLIPBOARD_CAPTURE_EXECUTION_FAILED',
CLIPBOARD_FN_EXECUTION_FAILED = 'CLIPBOARD_FN_EXECUTION_FAILED',
CLIPBOARD_READ_FAILED = 'CLIPBOARD_READ_FAILED',
CLIPBOARD_RESTORE_WRITE_FAILED = 'CLIPBOARD_RESTORE_WRITE_FAILED',
CLIPBOARD_STAGE_WRITE_FAILED = 'CLIPBOARD_STAGE_WRITE_FAILED',
🤖 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-core-ts/src/errors/RangeLinkErrorCodes.ts` around lines 20
- 28, Within the Clipboard errors block of RangeLinkErrorCodes.ts, reorder the
enum members so they are alphabetically sorted: move
CLIPBOARD_FN_EXECUTION_FAILED to come immediately after
CLIPBOARD_CAPTURE_EXECUTION_FAILED and before CLIPBOARD_READ_FAILED; ensure the
Clipboard section (CLIPBOARD_CAPTURE_EXECUTION_FAILED,
CLIPBOARD_FN_EXECUTION_FAILED, CLIPBOARD_READ_FAILED,
CLIPBOARD_RESTORE_WRITE_FAILED, CLIPBOARD_STAGE_WRITE_FAILED) follows the file's
"alphabetical order within each category" rule.

Comment on lines +370 to 374
assertLogContains(
'before-010',
'Focus failed, cannot paste link',
'Expected "Focus failed, cannot paste link" log — the focus command should have thrown',
);
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if focus failure logs have structured JSON fields that should be asserted

# Search for where "Focus failed" message is logged
rg -nP --type=ts -C3 'Focus failed' packages/rangelink-vscode-extension/src/

# Look for the log call that produces this message
ast-grep --pattern $'logger.$_($$$ "Focus failed" $$$)'

Repository: couimet/rangeLink

Length of output: 5099


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the logger.warn call emitting "Focus failed, cannot paste ..."
sed -n '320,380p' packages/rangelink-vscode-extension/src/destinations/ComposablePasteDestination.ts

# Show the relevant test assertions
sed -n '340,430p' packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts

# Find how assertLogContains / parseLogContext are implemented/used
rg -n "assertLogContains|parseLogContext" packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts packages/rangelink-vscode-extension/src/__integration-tests__/ -S

Repository: couimet/rangeLink

Length of output: 9215


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate any existing usage of parseLogContext in this integration-test suite
rg -n "parseLogContext" packages/rangelink-vscode-extension/src/__integration-tests__/suite -S

# Locate patterns in other tests asserting the same reason (COMMAND_FOCUS_FAILED) with structured fields
rg -n "COMMAND_FOCUS_FAILED|reason: 'COMMAND_FOCUS_FAILED'|reason: focusResult\\.error\\.reason" packages/rangelink-vscode-extension/src -S

Repository: couimet/rangeLink

Length of output: 43


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect how assertLogContains works
sed -n '1,220p' packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logHelpers.ts

# Inspect how parseLogContext works and what structured fields it extracts
sed -n '1,260p' packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts

# Also inspect any structured assertions that already exist (search within the helpers file)
rg -n "assert.*Log|toHave.*Called|parseLogContext\\(|reason|COMMAND_FOCUS_FAILED|Focus failed" packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts

Repository: couimet/rangeLink

Length of output: 12480


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect how logs are captured (to understand line format for parseLogContext)
sed -n '1,200p' packages/rangelink-vscode-extension/src/__integration-tests__/helpers/getLogCapture.ts

# Inspect rest of logBasedUiAssertions for any helper used for substring-vs-structured checks
sed -n '260,520p' packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts

# Inspect clipboardPreservation.test.ts for existing parseLogContext usage patterns
sed -n '1,240p' packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts

# Inspect how other tests assert similar focus-failure logs (COMMAND_FOCUS_FAILED)
rg -n "COMMAND_FOCUS_FAILED|Focus failed, cannot paste link" packages/rangelink-vscode-extension/src/__tests__/destinations packages/rangelink-vscode-extension/src/__integration-tests__/suite -S

# Inspect ComposablePasteDestination around performPaste for the exact context fields passed
sed -n '300,380p' packages/rangelink-vscode-extension/src/destinations/ComposablePasteDestination.ts

# Find parseLogContext usage in clipboardPreservation.test.ts (if any)
rg -n "parseLogContext" packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts -S

Repository: couimet/rangeLink

Length of output: 18780


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find how other integration tests use parseLogContext + getLogCapture for structured assertions
rg -n "getLogCapture\\(\\)\\.getLinesSince\\(|parseLogContext\\(" packages/rangelink-vscode-extension/src/__integration-tests__/suite -S

# Find structured assertions on reason / COMMAND_FOCUS_FAILED in integration tests (not unit tests)
rg -n "reason.*COMMAND_FOCUS_FAILED|COMMAND_FOCUS_FAILED" packages/rangelink-vscode-extension/src/__integration-tests__ -S

Repository: couimet/rangeLink

Length of output: 383


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the LogCapture implementation and how it records logger output
fd -t f "LogCapture" packages/rangelink-vscode-extension/src | head
fd -t f "logCapture" packages/rangelink-vscode-extension/src | head

rg -n "class LogCapture|type LogCapture|export.*logCapture|getLinesSince\\(|getLinesSince\\(" packages/rangelink-vscode-extension/src -S

# Find where barebone-logger is wired up / formatted for captured lines
rg -n "barebone-logger|createLogger\\(|logger\\.warn\\(|logger\\." packages/rangelink-vscode-extension/src -S | head -n 80

Repository: couimet/rangeLink

Length of output: 31127


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' packages/rangelink-vscode-extension/src/LogCapture.ts
sed -n '1,220p' packages/rangelink-vscode-extension/src/__tests__/LogCapture.test.ts

Repository: couimet/rangeLink

Length of output: 9354


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '240,330p' packages/rangelink-vscode-extension/src/__tests__/destinations/ComposablePasteDestination.integration.test.ts
sed -n '360,430p' packages/rangelink-vscode-extension/src/__tests__/destinations/ComposablePasteDestination.integration.test.ts

Repository: couimet/rangeLink

Length of output: 5665


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect structured log assertions for "Focus failed, cannot paste link"
sed -n '240,330p' packages/rangelink-vscode-extension/src/__tests__/destinations/ComposablePasteDestination.integration.test.ts
sed -n '360,430p' packages/rangelink-vscode-extension/src/__tests__/destinations/ComposablePasteDestination.integration.test.ts

Repository: couimet/rangeLink

Length of output: 5665


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "COMMAND_FOCUS_FAILED" packages/rangelink-vscode-extension/src/__integration-tests__ -S
rg -n "parseLogContext\\(.*\\)\\s*\\?\\.?\\.reason|parseLogContext\\(.*\\)\\?.reason|\\.reason\\s*===\\s*'COMMAND_FOCUS_FAILED'" packages/rangelink-vscode-extension/src/__integration-tests__ -S
rg -n "assert.*reason|reason:\" packages/rangelink-vscode-extension/src/__integration-tests__ -S

Repository: couimet/rangeLink

Length of output: 43


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the focus failure error reason code wiring
rg -n "COMMAND_FOCUS_FAILED" packages/rangelink-vscode-extension/src -S
rg -n "focusResult\\.error\\.reason|error\\.reason" packages/rangelink-vscode-extension/src/destinations packages/rangelink-vscode-extension/src/utils packages/rangelink-vscode-extension/src/navigation packages/rangelink-vscode-extension/src -S

Repository: couimet/rangeLink

Length of output: 2537


Use structured log context assertions for focus-failure logs (avoid substring-only checks).

ComposablePasteDestination.performPaste logs the focus-failure warning with structured context ({ ...context, reason: focusResult.error.reason }) plus the human message Focus failed, cannot paste ${contentLabel}. assertLogContains(..., "Focus failed, cannot paste link", ...) only matches the message substring and doesn’t assert the semantic field (reason). Update the assertions to use parseLogContext(...) on captured lines and check ctx.reason === 'COMMAND_FOCUS_FAILED' (and ctx.fn for the paste operation) instead of relying on the substring.

Applies to: 370-374, 409-413

🤖 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/__integration-tests__/suite/clipboardPreservation.test.ts`
around lines 370 - 374, Replace the substring-only assertions that look for
"Focus failed, cannot paste link" with structured-context checks: capture the
same logged line(s), call parseLogContext(...) on the captured log entry and
assert the resulting ctx.reason === 'COMMAND_FOCUS_FAILED' and ctx.fn (or
equivalent function identifier emitted by
ComposablePasteDestination.performPaste) matches the paste operation; do this
for the assertions currently around the focus-failure check (the block
exercising ComposablePasteDestination.performPaste) and the second occurrence so
the test verifies the structured context instead of only the message substring.

Source: Coding guidelines

Comment on lines +58 to 83
it('captures per-terminal name in closure', async () => {
const mockPasteService = createMockTerminalPasteService();
const terminal1 = createMockTerminal({ name: 'Terminal 1' });
const terminal2 = createMockTerminal({ name: 'Terminal 2' });
const pasteIntoTerminalSpy = jest
.spyOn(mockAdapter, 'pasteIntoTerminal')
.mockResolvedValue(undefined);

const factory = new TerminalInsertFactory(mockAdapter, mockLogger);
const factory = new TerminalInsertFactory(mockPasteService, mockLogger);
const insertFn1 = factory.forTarget(terminal1);
const insertFn2 = factory.forTarget(terminal2);

await insertFn1('content for terminal 1');
await insertFn2('content for terminal 2');

expect(pasteIntoTerminalSpy).toHaveBeenNthCalledWith(1, terminal1);
expect(pasteIntoTerminalSpy).toHaveBeenNthCalledWith(2, terminal2);
expect(mockPasteService.pasteIntoTerminal).toHaveBeenCalledTimes(2);
expect(mockPasteService.pasteIntoTerminal).toHaveBeenNthCalledWith(
1,
'content for terminal 1',
terminal1,
);
expect(mockPasteService.pasteIntoTerminal).toHaveBeenNthCalledWith(
2,
'content for terminal 2',
terminal2,
);

expect(mockLogger.error).not.toHaveBeenCalled();
expect(mockAdapter.writeTextToClipboard).not.toHaveBeenCalled();
});
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

Assert success logs in the per-terminal closure test.

This test verifies insert behavior but does not assert the corresponding info logs for each terminal. Add explicit logger assertions so logging remains part of the behavior contract.

As per coding guidelines, “Always include logger assertions in tests that verify method behavior - consolidate logging assertions with behavior tests, not separate tests.”

Suggested patch
   it('captures per-terminal name in closure', async () => {
@@
     expect(mockPasteService.pasteIntoTerminal).toHaveBeenNthCalledWith(
       2,
       'content for terminal 2',
       terminal2,
     );
+    expect(mockLogger.info).toHaveBeenNthCalledWith(
+      1,
+      { fn: 'TerminalInsertFactory.insert', terminalName: 'Terminal 1' },
+      'Terminal paste succeeded',
+    );
+    expect(mockLogger.info).toHaveBeenNthCalledWith(
+      2,
+      { fn: 'TerminalInsertFactory.insert', terminalName: 'Terminal 2' },
+      'Terminal paste succeeded',
+    );
 
     expect(mockLogger.error).not.toHaveBeenCalled();
   });
🤖 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/capabilities/insertFactories/terminalInsertFactory.test.ts`
around lines 58 - 83, The test for TerminalInsertFactory.forTarget currently
verifies pasteIntoTerminal calls but omits logger assertions; update the test to
assert that mockLogger.info was called for each insert with the appropriate
messages/arguments (one for 'content for terminal 1' targeting terminal1 and one
for 'content for terminal 2' targeting terminal2), and keep the existing
mockLogger.error assertion; locate the test using TerminalInsertFactory,
forTarget, insertFn1/insertFn2 and add expectations on mockLogger.info to match
the per-terminal success log contract.

Source: Coding guidelines

Comment on lines +7 to +16
stage: jest.fn((_text: string, fn: () => Promise<unknown>) => fn().then((v) => Result.ok(v))),
route: jest.fn((fn: () => Promise<unknown>, _shouldRestore?: () => boolean) =>
fn().then((v) => Result.ok(v)),
),
read: jest.fn().mockResolvedValue(Result.ok('')),
write: jest.fn().mockResolvedValue(Result.ok(undefined)),
restoreClipboard: jest.fn().mockResolvedValue(undefined),
capture: jest.fn((producer: () => Promise<unknown>) =>
producer().then((produced) => Result.ok({ clipboard: '', produced })),
),
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 | 🟠 Major | ⚡ Quick win

Mock ClipboardService should preserve Result contract on callback failures.

Line 7, Line 9, and Line 15 currently let rejected callbacks bubble as thrown rejections instead of returning Result.err(...). This diverges from the real service contract and can make tests validate the wrong behavior.

Proposed fix
 export const createMockClipboardService = (): jest.Mocked<ClipboardService> =>
   ({
-    stage: jest.fn((_text: string, fn: () => Promise<unknown>) => fn().then((v) => Result.ok(v))),
-    route: jest.fn((fn: () => Promise<unknown>, _shouldRestore?: () => boolean) =>
-      fn().then((v) => Result.ok(v)),
-    ),
+    stage: jest.fn(async (_text: string, fn: () => Promise<unknown>) => {
+      try {
+        return Result.ok(await fn());
+      } catch (error) {
+        return Result.err(error as Error);
+      }
+    }),
+    route: jest.fn(async (fn: () => Promise<unknown>, _shouldRestore?: () => boolean) => {
+      try {
+        return Result.ok(await fn());
+      } catch (error) {
+        return Result.err(error as Error);
+      }
+    }),
     read: jest.fn().mockResolvedValue(Result.ok('')),
     write: jest.fn().mockResolvedValue(Result.ok(undefined)),
     restoreClipboard: jest.fn().mockResolvedValue(undefined),
-    capture: jest.fn((producer: () => Promise<unknown>) =>
-      producer().then((produced) => Result.ok({ clipboard: '', produced })),
-    ),
+    capture: jest.fn(async (producer: () => Promise<unknown>) => {
+      try {
+        const produced = await producer();
+        return Result.ok({ clipboard: '', produced });
+      } catch (error) {
+        return Result.err(error as Error);
+      }
+    }),
   }) as unknown as jest.Mocked<ClipboardService>;
🤖 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__/helpers/createMockClipboardService.ts`
around lines 7 - 16, The mock ClipboardService functions stage, route, and
capture currently allow rejected callback promises to bubble as thrown
rejections; update each to catch errors and return a Result.err(...) instead of
throwing so the mock matches the real service contract — specifically modify the
stage mock (jest.fn((_text, fn) => ...)), the route mock (jest.fn((fn,
_shouldRestore) => ...)) and the capture mock (jest.fn((producer) => ...)) to
wrap their fn()/producer() calls with .then(...).catch(err => Result.err(err))
and ensure the resolved success path still returns Result.ok(...).

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