Skip to content

[issues/481] Add log-based UI validation for integration tests#484

Merged
couimet merged 7 commits into
mainfrom
issues/481
Mar 21, 2026
Merged

[issues/481] Add log-based UI validation for integration tests#484
couimet merged 7 commits into
mainfrom
issues/481

Conversation

@couimet
Copy link
Copy Markdown
Owner

@couimet couimet commented Mar 21, 2026

Summary

VS Code's OutputChannel API is write-only — integration tests couldn't verify which toasts or status bar messages were shown after executing commands. This PR adds a LogCapture infrastructure that captures log lines in memory (gated by RANGELINK_CAPTURE_LOGS env var), along with assertion helpers that parse the VscodeAdapter log JSON to match message type and text. Using this, 6 previously-manual QA test cases are now automated, 4 existing navigation-clamping tests are enhanced with toast content verification, and 7 new integration tests are added.

Changes

  • LogCapture (src/LogCapture.ts) — wraps OutputChannel, proxies appendLine() to both the real channel and an in-memory array when RANGELINK_CAPTURE_LOGS=true. Test-only methods (mark, getLinesSince, getAllLines, clear) throw LOG_CAPTURE_DISABLED in production. Exposed via ext.exports.logCapture through RangeLinkExtensionApi.
  • LogSink interface (src/VSCodeLogger.ts) — decouples VSCodeLogger from vscode.OutputChannel; now accepts any { appendLine(value: string): void }.
  • Log-based UI assertion helpers (__integration-tests__/helpers/logBasedUiAssertions.ts) — assertToastLogged (info/warning/error), assertStatusBarMsgLogged, and their negative counterparts. Toasts and status bar messages are separate assertion functions. Barrel-exported with getLogCapture().
  • Navigation clamping assertions — resolved 4 TODOs in navigationClamping.test.ts that were blocked on Can release tests use logs to validate toast operations given the current setup doesn't allow the UI interaction/validation ? #481. Tests now verify the exact toast type (warning for clamped, info for within-bounds) and message content (including clamping summary).
  • Log-Based UI Assertions suite (logBasedUiValidation.test.ts) — 7 new integration tests: unbind no-op status bar, terminal-focus R-L/R-C error guidance, send link to bound terminal, send file path, dirty buffer clean path, full-line selection validation.
  • QA YAML — 6 TCs flipped to automated: true (core-send-commands-r-l-001, dirty-buffer-warning-007, full-line-selection-validation-001, send-file-path-001, send-terminal-selection-006, send-terminal-selection-007). Validator: 45/45 PASSED.
  • Test runner config (.vscode-test.mjs) — env: { RANGELINK_CAPTURE_LOGS: 'true' } enables capture for all integration tests.

Test Plan

  • All unit tests pass (1683 tests, 91 suites)
  • All integration tests pass (100 tests, 0 failures)
  • QA validator: 45 automated entries ↔ 45 integration test IDs, PASSED
  • LogCapture: 14 unit tests (8 capture-enabled, 6 capture-disabled with LOG_CAPTURE_DISABLED throws)
  • Log-based UI assertions: 15 unit tests (info/warning/error matching, status bar matching, negative assertions, multi-line search)
  • Navigation clamping: 4 tests now include toast content verification (warning type + clamping summary for 001/002/004, info type for 003)
  • Log-Based UI Assertions suite: 7 new integration tests

Related

Summary by CodeRabbit

  • Tests

    • Introduced log-capture infrastructure for integration tests and helpers to assert toast/status-bar output
    • Added new integration and unit test suites validating UI messages and log-based assertions
    • Marked multiple QA scenarios as automated (no longer manual)
  • Chores

    • Updated logging to support injectable sinks and exposed log-capture to test runners
    • Test runner now enables log capture via environment injection during integration runs

Charles Ouimet added 3 commits March 20, 2026 09:59
Integration tests can't access VS Code's OutputChannel content (write-only API). LogCapture wraps the output channel, proxying all appendLine() calls while optionally storing them in an in-memory array. Capture is gated by the RANGELINK_CAPTURE_LOGS environment variable — the integration test runner sets it to 'true'; production never sets it, so the in-memory array stays empty with zero overhead.

The LogSink interface decouples VSCodeLogger from vscode.OutputChannel — it now accepts any object with appendLine(). activate() returns { logCapture } via ext.exports so integration tests access it via vscode.extensions.getExtension().exports.logCapture. RangeLinkExtensionApi interface lives in types/ per project conventions.

Benefits:
- Enables automated toast validation for ~47 manual QA TCs (34% of manual tests) (#481)
- Zero production overhead — capture is disabled by default; appendLine only proxies to the real channel
- Tests use mark()/getLinesSince() to scope assertions to a specific command execution
Integration tests need to verify that specific toasts were shown after executing commands. These helpers parse the captured log lines (from LogCapture) and match against the VscodeAdapter function name (toast type) and exact message text from the JSON context.

assertToastLogged / assertNoToastLogged cover all four toast types: info, warning, error, and statusBar. getLogCapture retrieves the LogCapture instance from ext.exports for use in test setup.

Benefits:
- Enables writing integration tests that assert toast behavior without UI interaction (#481)
- Type-safe toast type mapping (info → VscodeAdapter.showInformationMessage, etc.)
- 11 unit tests verify parsing, matching, type discrimination, and negative assertions
…te 6 manual QA TCs

Resolved 4 navigation-clamping TODOs blocked on #481 by adding toast type and message assertions to existing tests (clamping-001 through -004 now verify warning vs info toast with exact clamped/unclamped message content). Wrote 7 new integration tests in toastValidation.test.ts covering unbind no-op, terminal-focus guidance, send link, send file path, dirty buffer clean path, and full-line selection. Flipped 6 QA TCs from automated: false to automated: true.

The assertToastLogged/assertStatusBarMsgLogged helpers parse VscodeAdapter log JSON to match toast type (info/warning/error) and exact message text. Status bar messages are asserted separately from toasts.

Benefits:
- 11 integration tests now verify toast/status-bar content via log capture (4 enhanced + 7 new)
- 6 QA TCs newly automated: core-send-commands-r-l-001, dirty-buffer-warning-007, full-line-selection-validation-001, send-file-path-001, send-terminal-selection-006, send-terminal-selection-007
- QA automated coverage: 39 → 45 TCs (validator 45/45 PASSED)
- RANGELINK_CAPTURE_LOGS=true set in .vscode-test.mjs — all integration tests benefit from log capture
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 21, 2026

⚠️ QA Coverage Gap Detected

New LogCapture class introduced for capturing logs, which may affect logging behavior and output. Some existing test cases were updated to be automated, but coverage for the new logging functionality is lacking.

Suggested test cases:

  • Log Capture: Capture logs when RANGELINK_CAPTURE_LOGS is set to true (manual — Requires verification of log output in the output channel, which cannot be automated.)
  • Log Capture: Ensure that log capture is disabled when RANGELINK_CAPTURE_LOGS is not set (manual — Requires verification of log output in the output channel, which cannot be automated.)

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b297e92a-707a-4fac-ae72-2aeb2d0f2b82

📥 Commits

Reviewing files that changed from the base of the PR and between a23061b and 75a3a2c.

📒 Files selected for processing (5)
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/getLogCapture.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/logBasedUiValidation.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/integration-helpers/logBasedUiAssertions.test.ts
  • packages/rangelink-vscode-extension/src/errors/RangeLinkExtensionErrorCodes.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/rangelink-vscode-extension/src/errors/RangeLinkExtensionErrorCodes.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/rangelink-vscode-extension/src/integration-tests/helpers/getLogCapture.ts
  • packages/rangelink-vscode-extension/src/integration-tests/suite/logBasedUiValidation.test.ts

Walkthrough

A log-capture system for VS Code integration tests was added. When RANGELINK_CAPTURE_LOGS='true', extension output is stored in-memory via a new LogCapture and exposed from activate(); tests use helpers to assert toast/status-bar events by inspecting captured logs instead of interacting with the UI.

Changes

Cohort / File(s) Summary
Test runner & QA config
packages/rangelink-vscode-extension/.vscode-test.mjs, packages/rangelink-vscode-extension/qa/qa-test-cases-v1.1.0-2026-03-18.yaml
Test runner now injects RANGELINK_CAPTURE_LOGS='true' into VS Code tests. Several QA test cases toggled from automated: falsetrue.
Log capture core & logger
packages/rangelink-vscode-extension/src/LogCapture.ts, packages/rangelink-vscode-extension/src/VSCodeLogger.ts, packages/rangelink-vscode-extension/src/errors/RangeLinkExtensionErrorCodes.ts
Added LogCapture that conditionally stores lines when env var is 'true' (marker API, clear/getAll). Introduced LogSink interface; VSCodeLogger now depends on LogSink. Added LOG_CAPTURE_DISABLED error code.
Extension activation & API types
packages/rangelink-vscode-extension/src/extension.ts, packages/rangelink-vscode-extension/src/types/RangeLinkExtensionApi.ts, packages/rangelink-vscode-extension/src/types/index.ts
activate() now returns RangeLinkExtensionApi exposing logCapture. LogCapture instantiated and injected into VSCodeLogger. New API type and re-export added.
Integration test helpers
packages/rangelink-vscode-extension/src/__integration-tests__/helpers/getLogCapture.ts, packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts, packages/rangelink-vscode-extension/src/__integration-tests__/helpers/index.ts
Added getLogCapture() to fetch exported logCapture from the running extension. New log-based UI assertion helpers (assertToastLogged, assertNoToastLogged, assertStatusBarMsgLogged, assertNoStatusBarMsgLogged) and barrel export.
Integration tests updates & additions
packages/rangelink-vscode-extension/src/__integration-tests__/suite/logBasedUiValidation.test.ts, packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationClamping.test.ts
New logBasedUiValidation.test.ts added to exercise commands and validate UI via log assertions. navigationClamping.test.ts updated to use getLogCapture + toast assertions.
Unit / helper tests
packages/rangelink-vscode-extension/src/__tests__/LogCapture.test.ts, packages/rangelink-vscode-extension/src/__tests__/integration-helpers/logBasedUiAssertions.test.ts
Jest suites added to validate LogCapture behavior (capture on/off, markers, errors) and the correctness/error messages of log-based UI assertion helpers.

Sequence Diagram

sequenceDiagram
    participant TestRunner as Test Runner
    participant VSCodeExt as Extension.activate()
    participant OutputChan as VSCode OutputChannel
    participant LogCapture as LogCapture (LogSink)
    participant Logger as VSCodeLogger
    participant IntegTest as Integration Test
    participant Helpers as Log Assertion Helpers

    TestRunner->>VSCodeExt: set RANGELINK_CAPTURE_LOGS='true' and launch
    VSCodeExt->>OutputChan: create OutputChannel
    VSCodeExt->>LogCapture: new LogCapture(OutputChannel)
    VSCodeExt->>Logger: new VSCodeLogger(LogCapture)
    VSCodeExt-->>TestRunner: return { logCapture }

    IntegTest->>Helpers: getLogCapture()
    Helpers->>VSCodeExt: read ext.exports.logCapture
    Helpers-->>IntegTest: LogCapture instance

    IntegTest->>LogCapture: mark("checkpoint")
    IntegTest->>VSCodeExt: invoke command (e.g., send-range)
    Logger->>OutputChan: appendLine(log message)
    Logger->>LogCapture: appendLine(log message) (stores when enabled)
    IntegTest->>LogCapture: getLinesSince("checkpoint")
    IntegTest->>Helpers: assertToastLogged(lines, {type,message})
    Helpers-->>IntegTest: assertion result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 I nibble logs beneath moonlight's glow,

Marking checkpoints where the soft lines flow.
Toasts and status whispers caught in my lair,
Tests now listen — I keep them aware.
Hooray — automation hops through the air!

🚥 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 title clearly and specifically summarizes the main change: adding log-based UI validation infrastructure for integration tests.
Linked Issues check ✅ Passed The PR fully implements the objective from issue #481 by adding LogCapture infrastructure, log-based assertion helpers, and integration tests that validate toast/status bar operations through captured logs without UI interaction.
Out of Scope Changes check ✅ Passed All changes directly support the PR objectives: test infrastructure (LogCapture, LogSink, helpers), integration tests, unit tests, QA updates, and configuration to enable log capture. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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/481

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.

Tip

CodeRabbit can enforce grammar and style rules using `languagetool`.

Configure the reviews.tools.languagetool setting to enable/disable rules and categories. Refer to the LanguageTool Community to learn more.

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

🧹 Nitpick comments (2)
packages/rangelink-vscode-extension/src/__integration-tests__/helpers/getLogCapture.ts (1)

13-20: Add an explicit guard for missing ext.exports.logCapture.

A direct guard here gives a clearer failure than propagating undefined into later assertions.

🛡️ Suggested defensive check
 export const getLogCapture = (): LogCapture => {
   const ext = vscode.extensions.getExtension<RangeLinkExtensionApi>(EXTENSION_ID);
   if (!ext) {
     throw new Error(`Extension ${EXTENSION_ID} not found`);
   }
   if (!ext.isActive) {
     throw new Error(`Extension ${EXTENSION_ID} is not active — call ext.activate() first`);
   }
-  return ext.exports.logCapture;
+  const logCapture = ext.exports?.logCapture;
+  if (!logCapture) {
+    throw new Error(`Extension ${EXTENSION_ID} is active but did not export logCapture`);
+  }
+  return logCapture;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/rangelink-vscode-extension/src/__integration-tests__/helpers/getLogCapture.ts`
around lines 13 - 20, The function currently assumes ext.exports.logCapture
exists and can return undefined later; add an explicit guard after retrieving
ext.exports.logCapture in getLogCapture (checking ext.exports and
ext.exports.logCapture) and throw a clear Error (including EXTENSION_ID in the
message) if it's missing so callers get an immediate, descriptive failure
instead of a later undefined propagation; reference the ext variable,
EXTENSION_ID, RangeLinkExtensionApi and the returned logCapture export when
updating the check.
packages/rangelink-vscode-extension/src/__integration-tests__/suite/logBasedUiValidation.test.ts (1)

59-66: Consider extracting the sleep pattern to a helper.

The await new Promise<void>((resolve) => setTimeout(resolve, SETTLE_MS)) pattern is repeated 15+ times throughout the test file. Extracting it to a const settle = () => ... helper would reduce noise and make tests more readable.

🔧 Optional refactor to reduce repetition
 const SETTLE_MS = 500;
+const settle = () => new Promise<void>((resolve) => setTimeout(resolve, SETTLE_MS));
 
 // Then in tests:
-await new Promise<void>((resolve) => setTimeout(resolve, SETTLE_MS));
+await settle();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/rangelink-vscode-extension/src/__integration-tests__/suite/logBasedUiValidation.test.ts`
around lines 59 - 66, The test file repeats the pattern await new
Promise<void>((resolve) => setTimeout(resolve, SETTLE_MS)) — extract that into a
small helper (e.g. const settle = () => new Promise<void>(resolve =>
setTimeout(resolve, SETTLE_MS))) at the top of the test file and replace all
occurrences (including inside bindTerminal and other helpers) with await
settle(); keep SETTLE_MS as the same constant and ensure bindTerminal still
returns the terminal after awaiting settle().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts`:
- Around line 29-44: The parseLogContext function currently slices JSON using
line.indexOf('}') which stops at the first closing brace and breaks when the
JSON contains nested objects/arrays; update parseLogContext to locate the
correct JSON end (e.g., use line.lastIndexOf('}') or implement a simple
brace-balancing loop) to capture the full JSON substring before calling
JSON.parse, keeping the existing validation of ctx.fn and ctx.message in the
returned object.

In
`@packages/rangelink-vscode-extension/src/errors/RangeLinkExtensionErrorCodes.ts`:
- Around line 29-32: The enum entries in RangeLinkExtensionErrorCodes must be
alphabetically ordered; move LOG_CAPTURE_DISABLED so all GENERATE_* entries are
grouped and sorted first: ensure the order is GENERATE_LINK_FORMAT_FAILED,
GENERATE_LINK_NO_SELECTION, GENERATE_LINK_SELECTION_CONVERSION_FAILED, then
LOG_CAPTURE_DISABLED to restore the documented alphabetical ordering and avoid
noisy future merges.

---

Nitpick comments:
In
`@packages/rangelink-vscode-extension/src/__integration-tests__/helpers/getLogCapture.ts`:
- Around line 13-20: The function currently assumes ext.exports.logCapture
exists and can return undefined later; add an explicit guard after retrieving
ext.exports.logCapture in getLogCapture (checking ext.exports and
ext.exports.logCapture) and throw a clear Error (including EXTENSION_ID in the
message) if it's missing so callers get an immediate, descriptive failure
instead of a later undefined propagation; reference the ext variable,
EXTENSION_ID, RangeLinkExtensionApi and the returned logCapture export when
updating the check.

In
`@packages/rangelink-vscode-extension/src/__integration-tests__/suite/logBasedUiValidation.test.ts`:
- Around line 59-66: The test file repeats the pattern await new
Promise<void>((resolve) => setTimeout(resolve, SETTLE_MS)) — extract that into a
small helper (e.g. const settle = () => new Promise<void>(resolve =>
setTimeout(resolve, SETTLE_MS))) at the top of the test file and replace all
occurrences (including inside bindTerminal and other helpers) with await
settle(); keep SETTLE_MS as the same constant and ensure bindTerminal still
returns the terminal after awaiting settle().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 88626bd1-0160-46c4-96c5-176a4fce1f2c

📥 Commits

Reviewing files that changed from the base of the PR and between e7599a4 and a23061b.

📒 Files selected for processing (15)
  • packages/rangelink-vscode-extension/.vscode-test.mjs
  • packages/rangelink-vscode-extension/qa/qa-test-cases-v1.1.0-2026-03-18.yaml
  • packages/rangelink-vscode-extension/src/LogCapture.ts
  • packages/rangelink-vscode-extension/src/VSCodeLogger.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/getLogCapture.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/index.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/helpers/logBasedUiAssertions.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/logBasedUiValidation.test.ts
  • packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationClamping.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/LogCapture.test.ts
  • packages/rangelink-vscode-extension/src/__tests__/integration-helpers/logBasedUiAssertions.test.ts
  • packages/rangelink-vscode-extension/src/errors/RangeLinkExtensionErrorCodes.ts
  • packages/rangelink-vscode-extension/src/extension.ts
  • packages/rangelink-vscode-extension/src/types/RangeLinkExtensionApi.ts
  • packages/rangelink-vscode-extension/src/types/index.ts

Charles Ouimet added 4 commits March 21, 2026 12:47
`parseLogContext` used `indexOf('}')` to find the end of the JSON context block, which stops at the first closing brace. This breaks when the JSON contains nested objects (e.g. `{"fn":"x","details":{"key":"val"}}`), truncating the substring before `JSON.parse`.

Switched to `lastIndexOf('}')` which correctly captures the full JSON object for single-JSON-per-line log format.

Benefits:
- Prevents silent assertion failures if log entries ever include nested JSON context
- Unit test with nested objects confirms the fix

Ref: #484 (review)
… codes enum

`LOG_CAPTURE_DISABLED` was inserted between `GENERATE_LINK_FORMAT_FAILED` and `GENERATE_LINK_NO_SELECTION`, breaking the documented alphabetical ordering convention. Moved it to its correct position after all `GENERATE_LINK_*` entries.

Benefits:
- Restores the alphabetical invariant documented in the enum header comments
- Prevents noisy diffs in future merges that add entries near the G/L boundary

Ref: #484 (review)
…ture

`getLogCapture` returned `ext.exports.logCapture` without checking if it was defined. When `RANGELINK_CAPTURE_LOGS` is not set, `logCapture` is undefined and callers get silent propagation instead of a clear failure.

Added an explicit null guard with a descriptive error message, following the same pattern as the existing `ext` and `ext.isActive` guards.

Benefits:
- Fail-fast with a descriptive message instead of silent undefined propagation
- Consistent guard pattern across all precondition checks in the function

Ref: #484 (review)
The `await new Promise<void>((resolve) => setTimeout(resolve, SETTLE_MS))` pattern was repeated 15 times across the test file. Extracted it to a `settle()` helper to reduce noise and communicate intent more clearly.

Benefits:
- Reduces repetition from 15 inline promise constructions to `await settle()` calls
- Named helper makes the purpose (waiting for UI to settle) immediately obvious

Ref: #484 (review)
@couimet couimet merged commit bc8b59b into main Mar 21, 2026
3 checks passed
@couimet couimet deleted the issues/481 branch March 21, 2026 16:59
couimet pushed a commit that referenced this pull request Mar 21, 2026
PR #484 added LogCapture infrastructure for verifying toasts and status bar messages in integration tests. The unbind-001 test previously only verified unbind via clipboard side-effects (R-C after unbind). Now it also asserts the "✓ RangeLink unbound from Terminal" status bar confirmation message, making the test more precise about the user-visible feedback.
couimet added a commit that referenced this pull request Mar 21, 2026
…ns (#489)

* [test] Enhance unbind-001 with log-based status bar assertion

PR #484 added LogCapture infrastructure for verifying toasts and status bar messages in integration tests. The unbind-001 test previously only verified unbind via clipboard side-effects (R-C after unbind). Now it also asserts the "✓ RangeLink unbound from Terminal" status bar confirmation message, making the test more precise about the user-visible feedback.

* [test] Add unbind-004 integration test for Command Palette unbind

unbind-004 QA TC verifies that "RangeLink: Unbind Destination" works from the Command Palette. Since executeCommand uses the same code path as palette selection, the integration test binds a terminal, unbinds via executeCommand, and asserts the "✓ RangeLink unbound" status bar confirmation via LogCapture. Combined with the contract test verifying `enablement: rangelink.isBound`, this covers the TC.

* [test] Update QA YAML unbind TCs to reflect enablement guard and new automation

unbind-003 expected_result was outdated — the keybinding no longer fires when unbound (gated by rangelink.isBound), rather than being a "safe no-op" with an info message. unbind-004 flipped to automated: true now that an integration test exercises the same executeCommand code path as Command Palette invocation, and contract tests verify the enablement clause.

* [docs] Add CLAUDE.md rule requiring pnpm test:release for QA/integration changes

The QA validator script only checks ID matching between YAML and test files — it doesn't execute the tests. Without running `pnpm test:release`, log-based assertions and command behavior are never verified in a real VS Code host, so broken tests slip through unnoticed.

* [docs] Document release test requirement and QuickPick test limitation

Two documentation gaps that kept causing repeated investigation:

1. CLAUDE.md: added rule requiring `pnpm test:release` for any QA/integration test changes — the QA validator only checks ID matching, not that tests actually pass in a VS Code host.

2. TESTING.md: documented the QuickPick limitation — VS Code's extension host test runner provides no API to interact with pickers, so a QuickPick opening during a test stalls it indefinitely. Documented the command-bypass workaround and what must remain manual. Updated CLAUDE.md QA005 to cross-reference this section.
couimet pushed a commit that referenced this pull request Mar 21, 2026
…e navigation

Added 5 integration tests that verify end-to-end navigation to open untitled files in a real VS Code host — creating an untitled document, generating a RangeLink referencing it, invoking handleDocumentLinkClick, and asserting cursor position + toast messages via log-based assertions (leveraging LogCapture from PR #484).

Coverage: full-line (001), line-range (002), character-precision (004), line clamping with warning (005), and file-not-found for closed untitled files (003). All 5 QA TCs are automated.

Benefits:
- Full integration test coverage for untitled file navigation (happy + clamping + error paths)
- Log-based assertions verify info/warning toast messages without UI interaction
- QA validator passes with 51 automated TCs matching integration tests
couimet added a commit that referenced this pull request Mar 22, 2026
…dent fallback (#490)

* [issues/101] Make untitled file navigation locale-independent

The untitled file fallback was gated behind a regex that only matched English "Untitled-N" names. In non-English VS Code locales (e.g., French "Sans titre-1", German "Unbenannt-1"), the fallback never triggered and users saw a generic "Cannot find file" error instead of being navigated to the open untitled document.

Removed the English-only regex gate so `findOpenUntitledFile()` is always tried when a path doesn't resolve on disk. The search uses the `untitled:` URI scheme which is locale-independent. If no matching open untitled document is found, the generic "Cannot find file" warning is shown — the same message for all unresolved paths regardless of naming pattern.

Benefits:
- Untitled file navigation works in all VS Code locales
- Simpler control flow — one fallback path instead of regex-branching
- Removed the untitled-specific warning message (dead path after this change)

* [issues/101] Make findOpenUntitledFile use case-insensitive matching

The display name comparison in `findOpenUntitledFile` used strict equality, which could miss matches when the link path case differs from the document's display name (e.g., searching for "untitled-1" when the document reports "Untitled-1").

Switched to `.toLowerCase()` comparison on both sides to handle cross-platform case differences.

Benefits:
- Handles case mismatches between link references and VS Code's internal naming
- More robust on case-insensitive filesystems (Windows/macOS)

* [issues/101] Remove unused WARN_NAVIGATION_UNTITLED_FILE message

The untitled-specific warning message was made unreachable by S001's control flow simplification — all unresolved paths now show the generic "Cannot find file" warning regardless of naming pattern.

Removed the `WARN_NAVIGATION_UNTITLED_FILE` enum entry from MessageCode and its string template from messages.en.ts.

Benefits:
- No dead code in the message system
- One fewer message to maintain and translate

* [issues/101] Add QA test cases and integration tests for untitled file navigation

Added 5 integration tests that verify end-to-end navigation to open untitled files in a real VS Code host — creating an untitled document, generating a RangeLink referencing it, invoking handleDocumentLinkClick, and asserting cursor position + toast messages via log-based assertions (leveraging LogCapture from PR #484).

Coverage: full-line (001), line-range (002), character-precision (004), line clamping with warning (005), and file-not-found for closed untitled files (003). All 5 QA TCs are automated.

Benefits:
- Full integration test coverage for untitled file navigation (happy + clamping + error paths)
- Log-based assertions verify info/warning toast messages without UI interaction
- QA validator passes with 51 automated TCs matching integration tests

* CHANGELOG entry

* [PR feedback] Add case-insensitive matching TC and use getUntitledDisplayName in tests

Added `untitled-navigation-006` integration test and QA TC that verifies lowercase input (`untitled-1#L5`) matches an open `Untitled-1` document. This directly tests the case-insensitive matching behavior from the `findOpenUntitledFile` change.

Replaced the inline display name derivation logic in the test with an import of `getUntitledDisplayName` from production code, eliminating duplication.

Benefits:
- Case-insensitive matching has dedicated regression coverage (52 automated TCs total)
- Test uses the same derivation logic as production code instead of reimplementing it

Ignored Feedback:
- Multiple untitled match disambiguation via quick-pick: VS Code guarantees unique untitled names per window and `workspace.textDocuments` only returns current-window documents, making multiple matches impossible
- Parameterize English names in QA YAML with `{{placeholders}}`: QA YAML files are human-readable test plans, not executable templates; parameterizing would reduce readability without adding value

Ref: #490 (review)
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.

Can release tests use logs to validate toast operations given the current setup doesn't allow the UI interaction/validation ?

1 participant