diff --git a/CLAUDE.md b/CLAUDE.md index c6bc20fa..654b7843 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -627,7 +627,7 @@ The `automated` field accepts three values: `true` (fully automated), `assisted` (human-in-the-loop), `false` (fully manual) When marking a TC `automated: true`, ensure a non-`[assisted]` integration test exists in `src/__integration-tests__/suite/` on the same branch When marking a TC `automated: assisted`, ensure an `[assisted]`-tagged integration test exists in `src/__integration-tests__/suite/` on the same branch. See TESTING.md § "Assisted mode" for the `[assisted]` tag convention. - Use `automated: false` for scenarios that can't be integration-tested at all (e.g., requires AI assistant interaction, platform-specific behaviour). See TESTING.md § "QuickPick limitation" for what can and cannot be automated. + Use `automated: false` for scenarios that can't be integration-tested at all (e.g., requires AI assistant interaction, platform-specific behaviour). See TESTING.md § "QuickPick and InputBox dismissal" for what can and cannot be automated. Mark `automated: true` or `automated: assisted` based on unit tests alone — the validator only checks integration tests packages/rangelink-vscode-extension/scripts/validate-qa-coverage.sh diff --git a/packages/rangelink-vscode-extension/TESTING.md b/packages/rangelink-vscode-extension/TESTING.md index a0023bae..607a7b76 100644 --- a/packages/rangelink-vscode-extension/TESTING.md +++ b/packages/rangelink-vscode-extension/TESTING.md @@ -75,13 +75,29 @@ Integration tests run inside a real VS Code process via `@vscode/test-cli`. They pnpm test:release ``` -### QuickPick limitation +### QuickPick and InputBox dismissal -VS Code's extension host test runner provides no API to interact with QuickPick UI — tests cannot programmatically select items, dismiss pickers, or read picker contents. A QuickPick that opens during a test **will stall the test indefinitely** because it blocks the command from completing, and there is no way to dismiss it from test code. +VS Code's extension host test runner provides no API to programmatically select QuickPick items or interact with dialogs. However, `workbench.action.closeQuickOpen` can programmatically dismiss QuickPicks and InputBoxes — meaning tests that open a picker, read its logged content, and dismiss it **can now be fully automated**. -**Workaround — command bypass:** Many TCs that involve a QuickPick as a means to an end (e.g., "bind via picker, verify toast") can be automated by calling the underlying command directly (`rangelink.bindToTerminalHere`, `rangelink.bindToTextEditorHere`) to bypass the picker entirely, then asserting the outcome via log-based UI assertions. +**`openAndDismiss` helper:** The pattern for automated picker-open-and-dismiss is encapsulated in `openAndDismiss(command)`: -**What cannot be fully automated:** TCs that verify picker content itself (item ordering, badges, grouping, placeholder text) or dialog interaction (confirmation buttons, cancel behavior) require a human to open/dismiss the picker. Mark these `automated: assisted` in the QA YAML — the test automates setup and validates content via log-based QuickPick assertions while the human performs the mechanical UI action. See [Assisted mode](#assisted-mode-assisted-tests) below. Only TCs that genuinely cannot be tested even with human-in-the-loop assistance should remain `automated: false`. +```typescript +// Fires the command (which opens a QuickPick), waits for render + log emission, +// dismisses with closeQuickOpen, then settles. +await openAndDismiss(CMD_BIND_TO_DESTINATION); +const items = extractQuickPickItemsLogged(logCapture.getLinesSince('before-test')); +// assert on items as usual +``` + +**Workaround — command bypass:** TCs that use a picker as a means to an end (e.g., "bind via picker, verify toast") can be automated by calling the underlying command directly (`rangelink.bindToTerminalHere`, `rangelink.bindToTextEditorHere`) to bypass the picker entirely. + +**What still requires assisted mode:** TCs that need to: + +- Select a specific item from a picker (closeQuickOpen only dismisses, it cannot choose) +- Navigate a multi-picker flow (select item in picker A → picker B opens → verify B's content) +- Verify dialog interactions (confirmation buttons with Yes/No) + +Mark these `automated: assisted` in the QA YAML. See [Assisted mode](#assisted-mode-assisted-tests) below. Only TCs that genuinely cannot be tested even with human-in-the-loop assistance should remain `automated: false`. See https://github.com/couimet/rangeLink/issues/483 for the full triage of automatable vs manual TCs. diff --git a/packages/rangelink-vscode-extension/qa/qa-test-cases-v1.1.0.yaml b/packages/rangelink-vscode-extension/qa/qa-test-cases-v1.1.0.yaml index 4f26218f..1fb15c21 100644 --- a/packages/rangelink-vscode-extension/qa/qa-test-cases-v1.1.0.yaml +++ b/packages/rangelink-vscode-extension/qa/qa-test-cases-v1.1.0.yaml @@ -40,7 +40,7 @@ test_cases: steps: - 'Click the $(link) RangeLink item in the status bar' expected_result: "A QuickPick menu opens titled 'RangeLink Menu'. Menu items are visible." - automated: assisted + automated: true - id: status-bar-menu-003 feature: 'R-M Status Bar Menu' @@ -50,7 +50,7 @@ test_cases: steps: - 'Press Cmd+R Cmd+M' expected_result: 'Same QuickPick menu opens as clicking the status bar item.' - automated: assisted + automated: true - id: status-bar-menu-004 labels: @@ -73,7 +73,7 @@ test_cases: - 'Open the R-M menu (keybinding or status bar click)' - 'Observe the menu items' expected_result: "Menu contains 'Jump to Bound Destination' with the destination name shown. 'Unbind Destination' is also visible." - automated: assisted + automated: true - id: status-bar-menu-006 feature: 'R-M Status Bar Menu' @@ -84,7 +84,7 @@ test_cases: - 'Open the R-M menu' - 'Observe the first item' expected_result: "Menu shows a bind item that opens the destination picker. 'Jump to Bound Destination' is absent." - automated: assisted + automated: true - id: status-bar-menu-007 feature: 'R-M Status Bar Menu' @@ -198,7 +198,7 @@ test_cases: - 'Open R-D picker' - 'Press Escape without selecting anything' expected_result: 'Picker closes. Binding state is unchanged.' - automated: assisted + automated: true - id: bind-to-destination-011 labels: @@ -236,7 +236,7 @@ test_cases: - 'Open R-D destination picker (Cmd+R Cmd+D or Ctrl+R Ctrl+D)' - 'Observe the picker items' expected_result: 'Picker shows inline terminal items up to maxInline limit, "More terminals..." overflow item, inline file items (current-in-group), "More files..." overflow item, and standard command items (Go to Link, Show Version Info).' - automated: assisted + automated: true - id: bind-to-destination-014 feature: 'R-D Bind to Destination' @@ -275,7 +275,7 @@ test_cases: - "Click a terminal tab (e.g., 'Gamma') to make it the active terminal" - 'Open the terminal picker via R-D or R-M' expected_result: "'Gamma' shows an 'active' badge in the picker. Other terminals have no badge." - automated: assisted + automated: true - id: terminal-picker-002 feature: 'Terminal Picker' @@ -286,7 +286,7 @@ test_cases: steps: - 'Open the terminal picker' expected_result: "'Beta' shows a 'bound' badge. 'Gamma' shows an 'active' badge." - automated: assisted + automated: true - id: terminal-picker-003 feature: 'Terminal Picker' @@ -297,7 +297,7 @@ test_cases: - "Click the 'Beta' terminal tab to make it active" - 'Open the terminal picker' expected_result: "'Beta' shows a combined 'bound · active' badge (both indicators present on the same item)." - automated: assisted + automated: true - id: terminal-picker-004 feature: 'Terminal Picker' @@ -308,7 +308,7 @@ test_cases: steps: - 'Open the terminal picker' expected_result: "'Beta' is the first item in the terminal list, regardless of the order terminals were opened." - automated: assisted + automated: true - id: terminal-picker-005 feature: 'Terminal Picker' @@ -319,7 +319,7 @@ test_cases: steps: - 'Open the terminal picker' expected_result: "'Beta' (bound) is first. 'Gamma' (active, not bound) is second. Remaining terminals follow in any order." - automated: assisted + automated: true - id: terminal-picker-006 labels: @@ -332,7 +332,7 @@ test_cases: steps: - 'Open the terminal picker' expected_result: 'Hidden background terminals do not appear in the picker. Only user-visible terminals are listed.' - automated: assisted + automated: true - id: terminal-picker-007 feature: 'Terminal Picker' @@ -343,7 +343,7 @@ test_cases: steps: - 'Open R-D picker' expected_result: "All terminals appear inline. No 'More terminals...' item is visible." - automated: assisted + automated: true - id: terminal-picker-008 feature: 'Terminal Picker' @@ -354,7 +354,7 @@ test_cases: steps: - 'Open R-D picker or terminal sub-section of R-M' expected_result: "Only 3 terminals shown inline. A 'More terminals...' item appears at the bottom of the terminal section." - automated: assisted + automated: true - id: terminal-picker-009 feature: 'Terminal Picker' @@ -385,7 +385,7 @@ test_cases: - 'Set rangelink.terminalPicker.maxInline = 2 in VS Code settings' - 'Open R-D picker' expected_result: "Only 2 terminals shown inline. 'More terminals...' appears. Previously all 4 were inline with higher maxInline." - automated: assisted + automated: true - id: terminal-picker-012 feature: 'Terminal Picker' @@ -397,7 +397,7 @@ test_cases: - 'Open R-M menu' - 'Observe the terminal entries in the menu' expected_result: 'Terminal picker items appear inline within the R-M menu. Selecting one binds that terminal.' - automated: assisted + automated: true - id: terminal-picker-013 feature: 'Terminal Picker' @@ -408,7 +408,7 @@ test_cases: - 'Press Cmd+R Cmd+D' - 'Observe that terminal entries are part of the combined destination picker' expected_result: 'Terminal entries appear in the R-D picker with the same badges and ordering as when accessed from R-M.' - automated: assisted + automated: true # --------------------------------------------------------------------------- # Section 4 — File Picker @@ -423,7 +423,7 @@ test_cases: steps: - 'Open R-D picker' expected_result: "src/utils/helper.ts appears at the top of the file list with a 'bound' badge." - automated: assisted + automated: true - id: file-picker-002 feature: 'File Picker' @@ -436,7 +436,7 @@ test_cases: - 'Click src/index.ts to make it active in tab group 1' - 'Open R-D picker' expected_result: 'src/index.ts appears before other files in tab group 1 within the picker.' - automated: assisted + automated: true - id: file-picker-003 feature: 'File Picker' @@ -447,7 +447,7 @@ test_cases: - 'Open both files in the editor' - 'Open R-D picker (file section)' expected_result: 'Both index.ts files appear with enough path context to distinguish them (e.g., utils/index.ts and types/index.ts).' - automated: assisted + automated: true - id: file-picker-004 feature: 'File Picker' @@ -457,7 +457,7 @@ test_cases: steps: - 'Open R-D picker' expected_result: "The open file entries appear inline in the picker. No 'More files...' item." - automated: assisted + automated: true - id: file-picker-005 feature: 'File Picker' @@ -467,7 +467,7 @@ test_cases: steps: - 'Open R-D picker' expected_result: "Only the inline limit of files is shown. A 'More files...' item appears at the bottom of the file section." - automated: assisted + automated: true - id: file-picker-006 feature: 'File Picker' @@ -510,7 +510,7 @@ test_cases: steps: - 'Open R-M menu' expected_result: 'File entries appear inline within the R-M menu. Selecting one binds the file as the destination.' - automated: assisted + automated: true - id: file-picker-010 feature: 'File Picker' @@ -532,7 +532,7 @@ test_cases: steps: - 'Open R-D destination picker' expected_result: 'All three files appear with unique disambiguating path fragments that distinguish their locations.' - automated: assisted + automated: true - id: file-picker-012 feature: 'File Picker' @@ -542,7 +542,7 @@ test_cases: steps: - 'Open R-D destination picker' expected_result: 'File descriptions contain only badges and tab group labels — no disambiguation path prefix.' - automated: assisted + automated: true # --------------------------------------------------------------------------- # Section 5 — Clipboard Preservation @@ -697,7 +697,7 @@ test_cases: - 'Press Escape to dismiss without selecting anything' - 'Press Cancel — test reads clipboard and asserts it still equals the sentinel' expected_result: 'Clipboard contains the sentinel value. assertClipboardRestored passes — no preserve cycle was triggered because the operation was cancelled before any transport.' - automated: assisted + automated: true - id: clipboard-preservation-010 labels: @@ -1730,7 +1730,7 @@ test_cases: steps: - 'Press Cmd+R Cmd+G' expected_result: 'An input box appears with placeholder text indicating it expects a RangeLink (e.g., path/to/file.ts#L10).' - automated: assisted + automated: true # go-to-link-002 removed — Ctrl+R Ctrl+G on Win/Linux tests VS Code's keybinding # resolution, not RangeLink behavior. Both bindings are declared in package.json; @@ -1911,7 +1911,7 @@ test_cases: - 'Confirm the .png file is NOT listed' - 'Press Escape to dismiss' expected_result: 'The .png file does not appear in the picker despite being open. The .txt file does appear. The isBinaryFile filter in the R-D picker is the upstream defense; runtime rejection in PasteDestinationManager.bindTextEditor is a defense-in-depth layer covered by unit tests.' - automated: assisted + automated: true - id: editor-binding-validation-005 feature: 'Editor Binding Validation' @@ -2242,7 +2242,7 @@ test_cases: - 'Press Cmd+R Cmd+L' - 'Observe — destination picker should appear' expected_result: 'Destination picker opens. Selecting a destination binds it and sends the RangeLink. Escape dismisses silently with no clipboard write.' - automated: assisted + automated: true - id: core-send-commands-r-p-001 labels: @@ -2258,7 +2258,7 @@ test_cases: - "Select 'RangeLink: Send Portable Link'" - 'Observe — destination picker should appear' expected_result: 'Destination picker opens (not a silent clipboard fallback). Selecting a destination binds it and sends the portable link.' - automated: assisted + automated: true - id: core-send-commands-r-v-001 labels: @@ -2273,7 +2273,7 @@ test_cases: - 'Select text' - "Open Command Palette, select 'RangeLink: Send Selected Text'" expected_result: 'Destination picker opens. Selecting a destination sends the selected text to it.' - automated: assisted + automated: true # --------------------------------------------------------------------------- # Section — Text Editor Destination @@ -2820,7 +2820,7 @@ test_cases: - 'Press Cmd+R Cmd+D to open destination picker' - 'Observe the picker items' expected_result: 'The custom AI assistant appears in the picker with its configured extensionName, after the built-in destinations' - automated: assisted + automated: true - id: custom-ai-assistant-004 labels: @@ -2872,7 +2872,7 @@ test_cases: - 'Press Cmd+R Cmd+D to open destination picker' - 'Observe order of custom AI assistants in the picker' expected_result: 'Custom AI assistants appear after built-in destinations, in the same order as defined in settings.json' - automated: assisted + automated: true - id: custom-ai-assistant-008 labels: @@ -3097,15 +3097,15 @@ test_cases: - id: github-copilot-chat-001 feature: 'Built-in AI Assistants' - scenario: 'GitHub Copilot Chat appears in destination picker when extension is active' + scenario: 'GitHub Copilot Chat appears in destination picker when available' preconditions: - - 'GitHub Copilot Chat extension (GitHub.copilot-chat) is installed and active' + - 'VS Code has built-in workbench.action.chat.open command (present in all modern VS Code)' - 'No destination currently bound' steps: - 'Open destination picker via R-D or Command Palette → "Bind to Destination"' - 'Confirm "GitHub Copilot Chat" item appears in the AI Assistants group' expected_result: '"GitHub Copilot Chat" is listed in the AI Assistants group of the destination picker' - automated: false + automated: true - id: github-copilot-chat-002 labels: diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/assistedTestHelper.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/assistedTestHelper.ts index db2f1e9c..50a7ee38 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/assistedTestHelper.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/assistedTestHelper.ts @@ -4,22 +4,8 @@ import * as vscode from 'vscode'; const nodeConsole = new Console(process.stdout, process.stderr); -const ASSISTED_BANNER_WIDTH = 60; -const BANNER_LINE = '═'.repeat(ASSISTED_BANNER_WIDTH); -const SECTION_LINE = '─'.repeat(ASSISTED_BANNER_WIDTH); - -/** - * Prints a visible banner at suite start explaining the assisted mode workflow. - * Call this in suiteSetup() for any suite containing [assisted] tests. - */ -export const printAssistedBanner = (): void => { - nodeConsole.log(`\n${BANNER_LINE}`); - nodeConsole.log('ASSISTED TEST MODE'); - nodeConsole.log('Tests tagged [assisted] will pause for human interaction.'); - nodeConsole.log('Instructions appear as a persistent VS Code notification.'); - nodeConsole.log('Click Cancel on the notification when you have completed the action.'); - nodeConsole.log(BANNER_LINE); -}; +const SECTION_LINE_WIDTH = 60; +const SECTION_LINE = '─'.repeat(SECTION_LINE_WIDTH); /** * Pauses the test until the human completes a UI action. diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/index.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/index.ts index 8759a6a8..9a5ca9ef 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/index.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/index.ts @@ -1,9 +1,4 @@ -export { - type HumanVerdict, - printAssistedBanner, - waitForHuman, - waitForHumanVerdict, -} from './assistedTestHelper'; +export { type HumanVerdict, waitForHuman, waitForHumanVerdict } from './assistedTestHelper'; export { assertTerminalBufferContains, assertTerminalBufferEquals, @@ -49,12 +44,12 @@ export { createLogger } from './logHelpers'; export { navigateViaHandleLinkClick } from './navigationHelpers'; export { loadSettingsProfile, resetRangelinkSettings } from './settingsHelpers'; export { standardSuite } from './standardSuite'; -export type { StandardSuiteOptions } from './standardSuite'; export { createAndBindTerminal, createTerminal, findTerminalItems } from './terminalHelpers'; export { activateExtension, getExtensionVersion, getWorkspaceRoot, + openAndDismiss, POLL_INTERVAL_MS, POLL_TIMEOUT_MS, settle, diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/standardSuite.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/standardSuite.ts index 81a7e954..0c880b51 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/standardSuite.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/standardSuite.ts @@ -1,30 +1,14 @@ -import { printAssistedBanner } from './assistedTestHelper'; import { closeAllEditors } from './fileHelpers'; import { createLogger } from './logHelpers'; import { resetRangelinkSettings } from './settingsHelpers'; import { activateExtension, settle } from './testEnv'; -export interface StandardSuiteOptions { - assisted?: boolean; -} - -export const standardSuite = ( - name: string, - options: StandardSuiteOptions, - fn: (log: (msg: string) => void) => void, -): void => { +export const standardSuite = (name: string, fn: (log: (msg: string) => void) => void): void => { suite(name, () => { const log = createLogger(name); suiteSetup(async () => { await activateExtension(); - const assisted = options.assisted ?? false; - log( - `[DEBUG] standardSuite.assisted=${assisted} (${options.assisted === undefined ? 'default' : 'provided'})`, - ); - if (assisted) { - printAssistedBanner(); - } }); setup(async () => { diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/testEnv.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/testEnv.ts index 14cef8a7..8e1fa53c 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/testEnv.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/helpers/testEnv.ts @@ -29,3 +29,29 @@ export const getExtensionVersion = (): string => { export const settle = (ms: number = SETTLE_MS): Promise => new Promise((resolve) => setTimeout(resolve, ms)); + +/** + * Open a QuickPick or InputBox via command, dismiss it with closeQuickOpen, and return after + * the promise settles. The caller inspects log-captured items after this resolves. + */ +export const openAndDismiss = async (command: string): Promise => { + const promise = vscode.commands.executeCommand(command); + await settle(); + const deadline = Date.now() + POLL_TIMEOUT_MS; + // Retry dismissal until the command resolves — the picker may be slow to render on loaded CI. + for (;;) { + await vscode.commands.executeCommand('workbench.action.closeQuickOpen'); + const done = await Promise.race([ + promise.then(() => true), + settle(POLL_INTERVAL_MS).then(() => false), + ]); + if (done) break; + if (Date.now() >= deadline) { + throw new Error( + `openAndDismiss: "${command}" did not resolve within ${POLL_TIMEOUT_MS}ms deadline`, + ); + } + } + await promise; + await settle(); +}; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/bindToDestination.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/bindToDestination.test.ts index 15372469..45a02384 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/bindToDestination.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/bindToDestination.test.ts @@ -3,6 +3,7 @@ import * as path from 'node:path'; import * as vscode from 'vscode'; +import { CMD_BIND_TO_DESTINATION } from '../../constants/commandIds'; import { assertNoStatusBarMsgLogged, assertStatusBarMsgLogged, @@ -15,6 +16,7 @@ import { findTerminalItems, findTestItemsByPrefix, getLogCapture, + openAndDismiss, parseQuickPickItemsFromLogLine, settle, standardSuite, @@ -22,7 +24,7 @@ import { waitForHumanVerdict, } from '../helpers'; -standardSuite('R-D Bind to Destination', { assisted: true }, (log) => { +standardSuite('R-D Bind to Destination', (log) => { const terminals: vscode.Terminal[] = []; const tmpFileUris: vscode.Uri[] = []; @@ -289,7 +291,7 @@ standardSuite('R-D Bind to Destination', { assisted: true }, (log) => { log('✓ No rebind toast — original binding preserved (human verdict + state invariant)'); }); - test('[assisted] bind-to-destination-010: Escape from destination picker dismisses without changing binding', async () => { + test('bind-to-destination-010: Escape from destination picker dismisses without changing binding', async () => { await createTerminal('rl-btd-010', terminals); await vscode.commands.executeCommand('rangelink.bindToTerminalHere'); await settle(); @@ -297,7 +299,7 @@ standardSuite('R-D Bind to Destination', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-btd-010'); - await waitForHuman('bind-to-destination-010', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-btd-010'); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/builtInAiAssistants.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/builtInAiAssistants.test.ts index 31ce2673..e31dd44c 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/builtInAiAssistants.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/builtInAiAssistants.test.ts @@ -2,17 +2,23 @@ import assert from 'node:assert'; import * as vscode from 'vscode'; -import { CMD_BIND_TO_CLAUDE_CODE, CMD_UNBIND_DESTINATION } from '../../constants/commandIds'; +import { + CMD_BIND_TO_CLAUDE_CODE, + CMD_BIND_TO_DESTINATION, + CMD_UNBIND_DESTINATION, +} from '../../constants/commandIds'; import { activateExtension, cleanupFiles, closeAllEditors, createLogger, createWorkspaceFile, + extractQuickPickItemsLogged, getLogCapture, + openAndDismiss, openEditor, - printAssistedBanner, settle, + standardSuite, waitForHuman, } from '../helpers'; @@ -22,7 +28,6 @@ suite('Built-in AI Assistants', () => { suiteSetup(async () => { await activateExtension(); - printAssistedBanner(); }); teardown(async () => { @@ -138,3 +143,22 @@ suite('Built-in AI Assistants', () => { log('✓ Warm paste: content arrived without cold-start refocus'); }); }); + +standardSuite('Built-in AI Assistants — Destination Picker', (log) => { + test('github-copilot-chat-001: GitHub Copilot Chat appears in destination picker when available', async () => { + const logCapture = getLogCapture(); + logCapture.mark('before-copilot-001'); + + await openAndDismiss(CMD_BIND_TO_DESTINATION); + + const lines = logCapture.getLinesSince('before-copilot-001'); + const items = extractQuickPickItemsLogged(lines); + assert.ok(items, 'Expected showQuickPick log entry — was the picker opened?'); + + const copilotItem = items!.find((item) => item.displayName === 'GitHub Copilot Chat'); + assert.ok(copilotItem, 'Expected "GitHub Copilot Chat" in the destination picker items'); + assert.strictEqual(copilotItem!.itemKind, 'bindable'); + + log('✓ github-copilot-chat-001 — log confirms "GitHub Copilot Chat" appears in R-D picker'); + }); +}); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts index 343524ee..549ad16c 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/clipboardPreservation.test.ts @@ -22,6 +22,7 @@ import { createTerminal, createWorkspaceFile, loadSettingsProfile, + openAndDismiss, openEditor, settle, standardSuite, @@ -30,7 +31,7 @@ import { writeClipboardSentinel, } from '../helpers'; -standardSuite('Clipboard Preservation', {}, (_log) => { +standardSuite('Clipboard Preservation', (_log) => { let testFileUri: vscode.Uri; let editor: vscode.TextEditor; let capturing: CapturingTerminal; @@ -117,7 +118,7 @@ standardSuite('Clipboard Preservation', {}, (_log) => { }); }); -standardSuite('Clipboard Preservation — Assisted', { assisted: true }, (log) => { +standardSuite('Clipboard Preservation — Assisted', (log) => { const tmpFileUris: vscode.Uri[] = []; const tmpTerminals: vscode.Terminal[] = []; @@ -299,28 +300,26 @@ standardSuite('Clipboard Preservation — Assisted', { assisted: true }, (log) = log('✓ Clipboard changed from sentinel and phrase landed in destination file after R-V'); }); - test('[assisted] clipboard-preservation-009: always mode — dismissed picker leaves clipboard unchanged', async () => { + test('clipboard-preservation-009: always mode — dismissed picker leaves clipboard unchanged', async () => { await vscode.commands.executeCommand(CMD_UNBIND_DESTINATION); const lines = Array.from({ length: 5 }, (_, i) => `line ${i + 1}`); const fileUri = createWorkspaceFile('cbp-009', lines.join('\n') + '\n'); tmpFileUris.push(fileUri); - await openEditor(fileUri); + const SELECTION_START_LINE = 0; + const SELECTION_END_LINE = 2; + const SELECTION_COLUMN = 0; + + const editor009 = await openEditor(fileUri); + editor009.selection = new vscode.Selection( + new vscode.Position(SELECTION_START_LINE, SELECTION_COLUMN), + new vscode.Position(SELECTION_END_LINE, SELECTION_COLUMN), + ); await settle(); await writeClipboardSentinel(); - await waitForHuman( - 'clipboard-preservation-009', - `clipboard.preserve="always", no destination bound. Select lines, press Cmd+R Cmd+L (picker opens), press Escape. Sentinel: "${CLIPBOARD_SENTINEL}".`, - [ - '1. Click into the test file (cbp-009-...)', - '2. Select a few lines', - '3. Press Cmd+R Cmd+L — the destination picker opens (no destination is bound)', - '4. Press Escape to dismiss without selecting anything', - '5. Press Cancel to continue (test asserts clipboard still has the sentinel)', - ], - ); + await openAndDismiss(CMD_COPY_LINK_RELATIVE); await assertClipboardRestored('clipboard-preservation-009: always + picker dismissed'); log('✓ Clipboard unchanged after picker dismissed (no operation performed)'); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/commandRegistration.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/commandRegistration.test.ts index 14b41d5a..6a600727 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/commandRegistration.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/commandRegistration.test.ts @@ -60,7 +60,7 @@ const EXPECTED_COMMAND_IDS = [ 'rangelink.handleFilePathClick', ] as const; -standardSuite('Command Registration', {}, (_log) => { +standardSuite('Command Registration', (_log) => { let registeredCommands: string[]; suiteSetup(async () => { diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorContent.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorContent.test.ts index 2b0af177..216b54c3 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorContent.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorContent.test.ts @@ -26,7 +26,7 @@ import { const FILE_CONTENT = 'line 1\nline 2\nline 3\nline 4\n'; const CONTEXT_IS_BOUND_KEY = 'rangelink.isBound'; -standardSuite('Context Menus — Editor Content', { assisted: true }, (log) => { +standardSuite('Context Menus — Editor Content', (log) => { const terminals: vscode.Terminal[] = []; const tmpFileUris: vscode.Uri[] = []; let originalMultiLinePasteWarning: unknown; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorTab.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorTab.test.ts index 5a52bb3d..cfa6d042 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorTab.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuEditorTab.test.ts @@ -23,7 +23,7 @@ import { const FILE_CONTENT = 'editor-tab context-menu test file\n'; const CONTEXT_IS_BOUND_KEY = 'rangelink.isBound'; -standardSuite('Context Menus — Editor Tab', { assisted: true }, (log) => { +standardSuite('Context Menus — Editor Tab', (log) => { const terminals: vscode.Terminal[] = []; const tmpFileUris: vscode.Uri[] = []; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuExplorer.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuExplorer.test.ts index 719420d8..c653d94b 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuExplorer.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuExplorer.test.ts @@ -26,7 +26,7 @@ import { const FILE_CONTENT = 'explorer context-menu test file\n'; const CONTEXT_IS_BOUND_KEY = 'rangelink.isBound'; -standardSuite('Context Menus — Explorer', { assisted: true }, (log) => { +standardSuite('Context Menus — Explorer', (log) => { const terminals: vscode.Terminal[] = []; const tmpFileUris: vscode.Uri[] = []; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuTerminal.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuTerminal.test.ts index d40306e0..a3234148 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuTerminal.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/contextMenuTerminal.test.ts @@ -29,7 +29,7 @@ import { const CONTEXT_IS_BOUND_KEY = 'rangelink.isBound'; -standardSuite('Context Menus — Terminal', { assisted: true }, (log) => { +standardSuite('Context Menus — Terminal', (log) => { const terminals: vscode.Terminal[] = []; const tmpFileUris: vscode.Uri[] = []; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/coreSendCommands.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/coreSendCommands.test.ts index dd26465e..92e43f21 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/coreSendCommands.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/coreSendCommands.test.ts @@ -7,6 +7,8 @@ import { CMD_BIND_TO_TEXT_EDITOR_HERE, CMD_COPY_LINK_ONLY_RELATIVE, CMD_COPY_LINK_RELATIVE, + CMD_COPY_PORTABLE_LINK_RELATIVE, + CMD_PASTE_TO_DESTINATION, CMD_TERMINAL_PASTE_SELECTED_TEXT, CMD_UNBIND_DESTINATION, } from '../../constants/commandIds'; @@ -24,6 +26,7 @@ import { createWorkspaceFile, extractQuickPickItemsLogged, getLogCapture, + openAndDismiss, openEditor, settle, standardSuite, @@ -35,7 +38,7 @@ import { const NO_TERMINAL_SELECTION_MSG = 'RangeLink: No text selected in the terminal. Select text and try again.'; -standardSuite('Core Send Commands', { assisted: true }, (log) => { +standardSuite('Core Send Commands', (log) => { const tmpFileUris: vscode.Uri[] = []; const tmpTerminals: vscode.Terminal[] = []; @@ -339,7 +342,7 @@ standardSuite('Core Send Commands', { assisted: true }, (log) => { log('✓ R-V with no bound destination opens picker (log-based)'); }); - test('[assisted] core-send-commands-r-l-005: R-L with no bound destination opens picker', async () => { + test('core-send-commands-r-l-005: R-L with no bound destination opens picker', async () => { const fileUri = createWorkspaceFile('csc-r-l-005', 'test content\n'); tmpFileUris.push(fileUri); const doc = await vscode.workspace.openTextDocument(fileUri); @@ -350,14 +353,7 @@ standardSuite('Core Send Commands', { assisted: true }, (log) => { const logCaptureRl005 = getLogCapture(); logCaptureRl005.mark('before-r-l-005'); - await waitForHuman( - 'core-send-commands-r-l-005', - 'No destination bound. "test" is already selected.', - [ - 'Press Cmd+R Cmd+L — the RangeLink destination picker opens', - 'Press Escape to dismiss the picker, then click Cancel', - ], - ); + await openAndDismiss(CMD_COPY_LINK_RELATIVE); const itemsRl005 = extractQuickPickItemsLogged(logCaptureRl005.getLinesSince('before-r-l-005')); assert.ok( @@ -368,7 +364,7 @@ standardSuite('Core Send Commands', { assisted: true }, (log) => { log('✓ R-L with no destination opens picker (log-based)'); }); - test('[assisted] core-send-commands-r-p-001: Send Portable Link with no bound destination opens picker', async () => { + test('core-send-commands-r-p-001: Send Portable Link with no bound destination opens picker', async () => { const fileUri = createWorkspaceFile('csc-r-p-001', 'test content\n'); tmpFileUris.push(fileUri); const doc = await vscode.workspace.openTextDocument(fileUri); @@ -379,14 +375,7 @@ standardSuite('Core Send Commands', { assisted: true }, (log) => { const logCaptureRp001 = getLogCapture(); logCaptureRp001.mark('before-r-p-001'); - await waitForHuman( - 'core-send-commands-r-p-001', - 'No destination bound. "test" is already selected.', - [ - 'Press Cmd+Shift+P → "RangeLink: Send Portable Link" — the RangeLink destination picker opens', - 'Press Escape to dismiss the picker, then click Cancel', - ], - ); + await openAndDismiss(CMD_COPY_PORTABLE_LINK_RELATIVE); const itemsRp001 = extractQuickPickItemsLogged(logCaptureRp001.getLinesSince('before-r-p-001')); assert.ok( @@ -397,7 +386,7 @@ standardSuite('Core Send Commands', { assisted: true }, (log) => { log('✓ Send Portable Link with no destination opens picker (log-based)'); }); - test('[assisted] core-send-commands-r-v-001: Send Selected Text with no bound destination opens picker', async () => { + test('core-send-commands-r-v-001: Send Selected Text with no bound destination opens picker', async () => { const fileUri = createWorkspaceFile('csc-r-v-001', 'test content\n'); tmpFileUris.push(fileUri); const doc = await vscode.workspace.openTextDocument(fileUri); @@ -408,14 +397,7 @@ standardSuite('Core Send Commands', { assisted: true }, (log) => { const logCaptureRv001 = getLogCapture(); logCaptureRv001.mark('before-r-v-001'); - await waitForHuman( - 'core-send-commands-r-v-001', - 'No destination bound. "test" is already selected.', - [ - 'Press Cmd+Shift+P → "RangeLink: Send Selected Text" — the RangeLink destination picker opens', - 'Press Escape to dismiss the picker, then click Cancel', - ], - ); + await openAndDismiss(CMD_PASTE_TO_DESTINATION); const itemsRv001 = extractQuickPickItemsLogged(logCaptureRv001.getLinesSince('before-r-v-001')); assert.ok( diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/customAiAssistants.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/customAiAssistants.test.ts index 9e361722..328c897c 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/customAiAssistants.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/customAiAssistants.test.ts @@ -2,6 +2,7 @@ import assert from 'node:assert'; import * as vscode from 'vscode'; +import { CMD_BIND_TO_DESTINATION } from '../../constants/commandIds'; import { assertClipboardChanged, assertClipboardRestored, @@ -11,6 +12,7 @@ import { createAndOpenFile, extractQuickPickItemsLogged, getLogCapture, + openAndDismiss, settle, standardSuite, waitForHuman, @@ -20,7 +22,7 @@ import { const EXPECTED_CUSTOM_ASSISTANTS_COUNT = 6; const EXPECTED_CUSTOM_AI_REGISTRATIONS = 5; -standardSuite('Custom AI Assistants', {}, (_log) => { +standardSuite('Custom AI Assistants', (_log) => { test('custom-ai-assistant-001: three-tier config is parsed and logged at activation', () => { const logCapture = getLogCapture(); const allLines = logCapture.getAllLines(); @@ -204,20 +206,12 @@ standardSuite('Custom AI Assistants', {}, (_log) => { }); }); -standardSuite('Custom AI Assistants — Destination Picker (Assisted)', { assisted: true }, (log) => { - test('[assisted] custom-ai-assistant-003: custom AI assistant appears in R-D destination picker with configured display name', async () => { +standardSuite('Custom AI Assistants — Destination Picker', (log) => { + test('custom-ai-assistant-003: custom AI assistant appears in R-D destination picker with configured display name', async () => { const logCapture = getLogCapture(); logCapture.mark('before-003'); - await waitForHuman( - 'custom-ai-assistant-003', - 'Open R-D picker (Cmd+R Cmd+D), observe that Dummy AI entries appear, then press Escape', - [ - '1. Press Cmd+R Cmd+D to open the destination picker', - '2. Observe the list — confirm "Dummy AI (Tier 1)" appears', - '3. Press Escape to close without selecting', - ], - ); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-003'); const items = extractQuickPickItemsLogged(lines); @@ -230,19 +224,11 @@ standardSuite('Custom AI Assistants — Destination Picker (Assisted)', { assist log('✓ custom-ai-assistant-003 — log confirms "Dummy AI (Tier 1)" appears in R-D picker'); }); - test('[assisted] custom-ai-assistant-007: multiple custom AI assistants listed in user-defined order', async () => { + test('custom-ai-assistant-007: multiple custom AI assistants listed in user-defined order', async () => { const logCapture = getLogCapture(); logCapture.mark('before-007'); - await waitForHuman( - 'custom-ai-assistant-007', - 'Open R-D picker (Cmd+R Cmd+D), observe Dummy AI order (Tier 1 → Tier 2 → Tier 3 → Template → Fallback), then press Escape', - [ - '1. Press Cmd+R Cmd+D to open the destination picker', - '2. Observe custom AI assistants appear in order: Tier 1, Tier 2, Tier 3, Template, Fallback', - '3. Press Escape to close without selecting', - ], - ); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-007'); const items = extractQuickPickItemsLogged(lines); @@ -277,7 +263,7 @@ standardSuite('Custom AI Assistants — Destination Picker (Assisted)', { assist }); }); -standardSuite('Custom AI Assistants — Cold Start', { assisted: true }, (log) => { +standardSuite('Custom AI Assistants — Cold Start', (log) => { const tmpFileUris: vscode.Uri[] = []; teardown(async () => { @@ -333,7 +319,7 @@ standardSuite('Custom AI Assistants — Cold Start', { assisted: true }, (log) = }); }); -standardSuite('Custom AI Assistants — Paste Flow', { assisted: true }, (log) => { +standardSuite('Custom AI Assistants — Paste Flow', (log) => { const tmpFileUris: vscode.Uri[] = []; suiteSetup(async () => { diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/dirtyBufferWarning.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/dirtyBufferWarning.test.ts index 15197748..85a1222d 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/dirtyBufferWarning.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/dirtyBufferWarning.test.ts @@ -27,7 +27,7 @@ import { writeClipboardSentinel, } from '../helpers'; -standardSuite('Dirty Buffer Warning', { assisted: true }, (_log) => { +standardSuite('Dirty Buffer Warning', (_log) => { let testFileUri: vscode.Uri; suiteSetup(async () => { @@ -389,7 +389,7 @@ standardSuite('Dirty Buffer Warning', { assisted: true }, (_log) => { }); }); -standardSuite('Dirty Buffer Warning — Dialog Interaction', { assisted: true }, (log) => { +standardSuite('Dirty Buffer Warning — Dialog Interaction', (log) => { let testFileUri: vscode.Uri; suiteSetup(async () => { diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/editorBindingValidation.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/editorBindingValidation.test.ts index 80e8be31..e67c1151 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/editorBindingValidation.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/editorBindingValidation.test.ts @@ -4,7 +4,7 @@ import * as path from 'node:path'; import * as vscode from 'vscode'; -import { CMD_UNBIND_DESTINATION } from '../../constants/commandIds'; +import { CMD_BIND_TO_DESTINATION, CMD_UNBIND_DESTINATION } from '../../constants/commandIds'; import { cleanupFiles, closeAllEditors, @@ -13,13 +13,13 @@ import { findTestItemsByPrefix, getLogCapture, getWorkspaceRoot, + openAndDismiss, settle, standardSuite, - waitForHuman, waitForHumanVerdict, } from '../helpers'; -standardSuite('Editor Binding Validation', { assisted: true }, (log) => { +standardSuite('Editor Binding Validation', (log) => { const tmpFileUris: vscode.Uri[] = []; teardown(async () => { @@ -92,7 +92,7 @@ standardSuite('Editor Binding Validation', { assisted: true }, (log) => { log('✓ Settings UI hides file path/bind commands (human verdict)'); }); - test('[assisted] editor-binding-validation-004: binary .png file is excluded from R-D destination picker', async () => { + test('editor-binding-validation-004: binary .png file is excluded from R-D destination picker', async () => { // Minimal PNG magic bytes — enough for VSCode to detect as binary const PNG_MAGIC = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); const pngPath = path.join(getWorkspaceRoot(), `__rl-test-ebv-004-${Date.now()}.png`); @@ -116,17 +116,7 @@ standardSuite('Editor Binding Validation', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-ebv-004'); - await waitForHuman( - 'editor-binding-validation-004', - 'A .txt file is open in col 1, a .png in col 2. Press Cmd+R Cmd+D and Escape — .png must not appear in picker.', - [ - '1. The .txt control file is open in column 1, the .png is open in column 2', - '2. Press Cmd+R Cmd+D to open the destination picker', - '3. Confirm the .txt file IS listed (positive control)', - '4. Confirm no .png file appears in the list', - '5. Press Escape to dismiss', - ], - ); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-ebv-004'); const items = extractQuickPickItemsLogged(lines); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filePathNavigation.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filePathNavigation.test.ts index affaf07b..cdaa5f67 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filePathNavigation.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filePathNavigation.test.ts @@ -16,7 +16,7 @@ import { const NON_EXISTENT_PATH_SETTLE_MS = 1000; -standardSuite('File Path Navigation', {}, (_log) => { +standardSuite('File Path Navigation', (_log) => { let testFileUri: vscode.Uri; let anchorFileUri: vscode.Uri; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filePicker.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filePicker.test.ts index ad32b894..a0d43c9e 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filePicker.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filePicker.test.ts @@ -4,6 +4,7 @@ import * as path from 'node:path'; import * as vscode from 'vscode'; +import { CMD_BIND_TO_DESTINATION, CMD_OPEN_STATUS_BAR_MENU } from '../../constants/commandIds'; import { cleanupFiles, closeAllEditors, @@ -12,6 +13,7 @@ import { findTestItemsByPrefix, getLogCapture, getWorkspaceRoot, + openAndDismiss, parseQuickPickItemsFromLogLine, settle, standardSuite, @@ -21,7 +23,7 @@ import { const SEPARATOR_KIND = -1; const FILE_OVERFLOW_THRESHOLD = 5; -standardSuite('File Picker', { assisted: true }, (log) => { +standardSuite('File Picker', (log) => { const tmpFileUris: vscode.Uri[] = []; teardown(async () => { @@ -35,7 +37,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { const findTestFileItems = (items: Record[]): Record[] => findTestItemsByPrefix(items, '__rl-test-fp-'); - test('[assisted] file-picker-001: bound file appears first with bound badge', async () => { + test('file-picker-001: bound file appears first with bound badge', async () => { const uriA = await createAndOpenFile( 'fp-001-a', 'line 1\nline 2\n', @@ -56,7 +58,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-fp-001'); - await waitForHuman('file-picker-001', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-fp-001'); const items = extractQuickPickItemsLogged(lines); @@ -92,7 +94,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { log('✓ Bound file first with full semantic state + description'); }); - test('[assisted] file-picker-002: active file appears before others in its group', async () => { + test('file-picker-002: active file appears before others in its group', async () => { const uriA = await createAndOpenFile( 'fp-002-a', 'file a\n', @@ -111,7 +113,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-fp-002'); - await waitForHuman('file-picker-002', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-fp-002'); const items = extractQuickPickItemsLogged(lines); @@ -147,7 +149,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { log('✓ Files ordered by ViewColumn (Tab Group 1 before Tab Group 2)'); }); - test('[assisted] file-picker-003: same base name shows path disambiguation', async () => { + test('file-picker-003: same base name shows path disambiguation', async () => { const wsRoot = getWorkspaceRoot(); const subDirA = path.join(wsRoot, 'src', 'dirA'); const subDirB = path.join(wsRoot, 'src', 'dirB'); @@ -176,7 +178,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-fp-003'); - await waitForHuman('file-picker-003', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-fp-003'); const items = extractQuickPickItemsLogged(lines); @@ -224,14 +226,14 @@ standardSuite('File Picker', { assisted: true }, (log) => { log('✓ Path disambiguation validated with disambiguator in descriptions'); }); - test('[assisted] file-picker-004: open files appear as inline items in destination picker', async () => { + test('file-picker-004: open files appear as inline items in destination picker', async () => { const uri = await createAndOpenFile('fp-004', 'content\n', undefined, tmpFileUris); const fn = path.basename(uri.fsPath); const logCapture = getLogCapture(); logCapture.mark('before-fp-004'); - await waitForHuman('file-picker-004', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-fp-004'); const items = extractQuickPickItemsLogged(lines); @@ -260,7 +262,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { log('✓ File appears inline in R-D picker — full assertion'); }); - test('[assisted] file-picker-005: overflow shows "More files..." when exceeding inline limit', async () => { + test('file-picker-005: overflow shows "More files..." when exceeding inline limit', async () => { for (let i = 1; i <= FILE_OVERFLOW_THRESHOLD; i++) { await createAndOpenFile(`fp-005-${i}`, `file ${i}\n`, undefined, tmpFileUris); } @@ -268,7 +270,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-fp-005'); - await waitForHuman('file-picker-005', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-fp-005'); const items = extractQuickPickItemsLogged(lines); @@ -537,7 +539,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { log('✓ Secondary picker shows path disambiguation'); }); - test('[assisted] file-picker-011: three files with same name show deeper disambiguation paths', async () => { + test('file-picker-011: three files with same name show deeper disambiguation paths', async () => { const wsRoot = getWorkspaceRoot(); const dirA = path.join(wsRoot, 'src', 'a', 'nested'); const dirB = path.join(wsRoot, 'src', 'b', 'nested'); @@ -577,7 +579,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-fp-011'); - await waitForHuman('file-picker-011', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-fp-011'); const items = extractQuickPickItemsLogged(lines); @@ -632,7 +634,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { log('✓ Three same-name files: same label/displayName, unique disambiguated descriptions'); }); - test('[assisted] file-picker-012: unique file names have no disambiguator in description', async () => { + test('file-picker-012: unique file names have no disambiguator in description', async () => { const uriA = await createAndOpenFile( 'fp-012-alpha', 'file a\n', @@ -651,7 +653,7 @@ standardSuite('File Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-fp-012'); - await waitForHuman('file-picker-012', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-fp-012'); const items = extractQuickPickItemsLogged(lines); @@ -687,14 +689,14 @@ standardSuite('File Picker', { assisted: true }, (log) => { log('✓ Unique file names: no disambiguator, ordered by ViewColumn'); }); - test('[assisted] file-picker-009: file picker appears inline in R-M menu when unbound', async () => { + test('file-picker-009: file picker appears inline in R-M menu when unbound', async () => { const uri = await createAndOpenFile('fp-009', 'content\n', undefined, tmpFileUris); const fn = path.basename(uri.fsPath); const logCapture = getLogCapture(); logCapture.mark('before-fp-009'); - await waitForHuman('file-picker-009', 'Open R-M menu (Cmd+R Cmd+M), then Escape'); + await openAndDismiss(CMD_OPEN_STATUS_BAR_MENU); const lines = logCapture.getLinesSince('before-fp-009'); const items = extractQuickPickItemsLogged(lines); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filenameOnlyNavigation.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filenameOnlyNavigation.test.ts index e3949668..d0bf4f94 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filenameOnlyNavigation.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/filenameOnlyNavigation.test.ts @@ -18,7 +18,7 @@ import { const DUPLICATE_FILE_CONTENT = 'duplicate file content\n'; -standardSuite('Filename-Only Navigation Fallback', {}, (_log) => { +standardSuite('Filename-Only Navigation Fallback', (_log) => { let uniqueFilename: string; let uniqueFilePath: string; let relativeFilePath: string; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/goToRangeLink.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/goToRangeLink.test.ts index 2bd77d2c..95f53192 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/goToRangeLink.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/goToRangeLink.test.ts @@ -3,6 +3,7 @@ import * as path from 'node:path'; import * as vscode from 'vscode'; +import { CMD_GO_TO_RANGELINK } from '../../constants/commandIds'; import { assertInputBoxLogged, assertToastLogged, @@ -11,6 +12,7 @@ import { createAndOpenFile, extractQuickPickItemsLogged, getLogCapture, + openAndDismiss, settle, standardSuite, waitForHuman, @@ -36,7 +38,7 @@ const assertUserCancelledInputLogged = (lines: string[]): void => { assert.ok(found, 'Expected GoToRangeLinkCommand.execute "User cancelled input" debug log'); }; -standardSuite('R-G Go to Link', { assisted: true }, (log) => { +standardSuite('R-G Go to Link', (log) => { const tmpFileUris: vscode.Uri[] = []; teardown(async () => { @@ -46,11 +48,11 @@ standardSuite('R-G Go to Link', { assisted: true }, (log) => { await settle(); }); - test('[assisted] go-to-link-001: Cmd+R Cmd+G opens the Go to Link input box', async () => { + test('go-to-link-001: Cmd+R Cmd+G opens the Go to Link input box', async () => { const logCapture = getLogCapture(); logCapture.mark('before-gtl-001'); - await waitForHuman('go-to-link-001', 'Press Cmd+R Cmd+G, then Escape the input box'); + await openAndDismiss(CMD_GO_TO_RANGELINK); const lines = logCapture.getLinesSince('before-gtl-001'); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/linkGeneration.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/linkGeneration.test.ts index c1e8e6b8..12dc2630 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/linkGeneration.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/linkGeneration.test.ts @@ -17,7 +17,7 @@ import { const LOGGER = new NoOpLogger(); -standardSuite('Link Generation', {}, (_log) => { +standardSuite('Link Generation', (_log) => { const tmpFileUris: vscode.Uri[] = []; suiteTeardown(async () => { @@ -157,7 +157,7 @@ standardSuite('Link Generation', {}, (_log) => { }); }); -standardSuite('Link Generation — Clickable Links (Assisted)', { assisted: true }, (log) => { +standardSuite('Link Generation — Clickable Links (Assisted)', (log) => { const tmpFileUris: vscode.Uri[] = []; teardown(async () => { diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationClamping.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationClamping.test.ts index 00b56ae0..69711c57 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationClamping.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationClamping.test.ts @@ -18,7 +18,7 @@ import { const LINE_COUNT = 10; const LINE_CONTENT = 'abcdefghijklmnopqrst'; -standardSuite('Navigation Clamping', {}, (_log) => { +standardSuite('Navigation Clamping', (_log) => { let testFilename: string; let testFileUri: vscode.Uri; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationPrecision.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationPrecision.test.ts index 38373600..5a5bad96 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationPrecision.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationPrecision.test.ts @@ -15,7 +15,7 @@ import { standardSuite, } from '../helpers'; -standardSuite('Navigation Precision', {}, (_log) => { +standardSuite('Navigation Precision', (_log) => { let testFilename: string; let testFileUri: vscode.Uri; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationToastSettings.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationToastSettings.test.ts index f4953f5f..ae2c3d26 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationToastSettings.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/navigationToastSettings.test.ts @@ -16,7 +16,7 @@ import { standardSuite, } from '../helpers'; -standardSuite('Navigation Toast Settings', {}, (_log) => { +standardSuite('Navigation Toast Settings', (_log) => { let testFilename: string; let testFileUri: vscode.Uri; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/platformKeybindings.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/platformKeybindings.test.ts index 072685e7..b356e987 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/platformKeybindings.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/platformKeybindings.test.ts @@ -10,7 +10,7 @@ import { waitForHumanVerdict, } from '../helpers'; -standardSuite('Platform Keybindings', { assisted: true }, (_log) => { +standardSuite('Platform Keybindings', (_log) => { const tmpFileUris: vscode.Uri[] = []; suiteTeardown(async () => { diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/releaseNotifier.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/releaseNotifier.test.ts index c079e3b2..49bdfcec 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/releaseNotifier.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/releaseNotifier.test.ts @@ -13,7 +13,7 @@ const getReleaseNotifier = () => { return ext.exports.releaseNotifier; }; -standardSuite('Release Notifier', { assisted: true }, (log) => { +standardSuite('Release Notifier', (log) => { test('release-notifier-001: first install stores version silently without notification', async () => { const notifier = getReleaseNotifier(); await notifier.setLastNotifiedVersion(undefined); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/sendFilePath.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/sendFilePath.test.ts index 4dfbf703..2346df0f 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/sendFilePath.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/sendFilePath.test.ts @@ -28,7 +28,7 @@ import { writeClipboardSentinel, } from '../helpers'; -standardSuite('Send File Path', { assisted: true }, (log) => { +standardSuite('Send File Path', (log) => { const tmpFileUris: vscode.Uri[] = []; const tmpTerminals: vscode.Terminal[] = []; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/smartPadding.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/smartPadding.test.ts index 71600b0e..3855ff80 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/smartPadding.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/smartPadding.test.ts @@ -13,7 +13,7 @@ import { waitForActiveEditor, } from '../helpers'; -standardSuite('Smart Padding — Editor-to-Editor R-V', {}, (log) => { +standardSuite('Smart Padding — Editor-to-Editor R-V', (log) => { let sourceFileUri: vscode.Uri; let destFileUri: vscode.Uri; diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/statusBarMenu.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/statusBarMenu.test.ts index a3fc2481..115eb461 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/statusBarMenu.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/statusBarMenu.test.ts @@ -2,13 +2,18 @@ import assert from 'node:assert'; import * as vscode from 'vscode'; -import { CMD_BIND_TO_TERMINAL_HERE, CMD_UNBIND_DESTINATION } from '../../constants/commandIds'; +import { + CMD_BIND_TO_TERMINAL_HERE, + CMD_OPEN_STATUS_BAR_MENU, + CMD_UNBIND_DESTINATION, +} from '../../constants/commandIds'; import { assertQuickPickItemsLogged, cleanupFiles, createWorkspaceFile, extractQuickPickItemsLogged, getLogCapture, + openAndDismiss, settle, standardSuite, TERMINAL_READY_MS, @@ -18,18 +23,18 @@ import { const SEPARATOR_KIND = -1; -standardSuite('R-M Status Bar Menu', { assisted: true }, (log) => { +standardSuite('R-M Status Bar Menu', (log) => { const tmpFileUris: vscode.Uri[] = []; suiteTeardown(async () => { cleanupFiles(tmpFileUris); }); - test('[assisted] status-bar-menu-002: clicking the status bar item opens the R-M menu', async () => { + test('status-bar-menu-002: invoking openStatusBarMenu command opens the R-M menu', async () => { const logCapture = getLogCapture(); logCapture.mark('before-menu-002'); - await waitForHuman('status-bar-menu-002', 'Click the RangeLink status bar item, then Escape'); + await openAndDismiss(CMD_OPEN_STATUS_BAR_MENU); const lines = logCapture.getLinesSince('before-menu-002'); const items = extractQuickPickItemsLogged(lines); @@ -59,7 +64,7 @@ standardSuite('R-M Status Bar Menu', { assisted: true }, (log) => { log('✓ Unbound menu: no Jump item, correct structure'); }); - test('[assisted] status-bar-menu-003: Cmd+R Cmd+M keybinding opens the R-M menu', async () => { + test('status-bar-menu-003: invoking openStatusBarMenu command opens the R-M menu', async () => { const testFileUri = createWorkspaceFile('menu-003', 'line 1\nline 2\n'); tmpFileUris.push(testFileUri); const doc = await vscode.workspace.openTextDocument(testFileUri); @@ -69,7 +74,7 @@ standardSuite('R-M Status Bar Menu', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-menu-003'); - await waitForHuman('status-bar-menu-003', 'Press Cmd+R Cmd+M, then Escape'); + await openAndDismiss(CMD_OPEN_STATUS_BAR_MENU); const lines = logCapture.getLinesSince('before-menu-003'); const items = extractQuickPickItemsLogged(lines); @@ -96,10 +101,10 @@ standardSuite('R-M Status Bar Menu', { assisted: true }, (log) => { ], ); - log('✓ Keybinding menu: no Jump item, correct structure'); + log('✓ Direct command menu: no Jump item, correct structure'); }); - test('[assisted] status-bar-menu-005: R-M menu shows Jump to Bound Destination when bound', async () => { + test('status-bar-menu-005: R-M menu shows Jump to Bound Destination when bound', async () => { const terminal = vscode.window.createTerminal({ name: 'rl-menu-test' }); terminal.show(true); await settle(TERMINAL_READY_MS); @@ -111,7 +116,7 @@ standardSuite('R-M Status Bar Menu', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-menu-005'); - await waitForHuman('status-bar-menu-005', 'Open R-M menu (Cmd+R Cmd+M), then Escape'); + await openAndDismiss(CMD_OPEN_STATUS_BAR_MENU); const lines = logCapture.getLinesSince('before-menu-005'); assertQuickPickItemsLogged(lines, [ @@ -148,23 +153,14 @@ standardSuite('R-M Status Bar Menu', { assisted: true }, (log) => { log('✓ Status bar item shows correct text and tooltip'); }); - test('[assisted] status-bar-menu-006: R-M menu shows destination picker items when no destination is bound', async () => { + test('status-bar-menu-006: R-M menu shows destination picker items when no destination is bound', async () => { await vscode.commands.executeCommand(CMD_UNBIND_DESTINATION); await settle(); const logCapture = getLogCapture(); logCapture.mark('before-006'); - await waitForHuman( - 'status-bar-menu-006', - 'No destination bound. Open the R-M menu (Cmd+R Cmd+M), observe the first item and the absence of "Jump to Bound Destination", then press Escape.', - [ - '1. Press Cmd+R Cmd+M to open the R-M menu', - '2. Confirm the first item says "No bound destination. Choose below to bind:"', - '3. Confirm there is NO "Jump to Bound Destination" item', - '4. Press Escape to dismiss, then click Cancel', - ], - ); + await openAndDismiss(CMD_OPEN_STATUS_BAR_MENU); const lines = logCapture.getLinesSince('before-006'); const items = extractQuickPickItemsLogged(lines); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/terminalPicker.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/terminalPicker.test.ts index f8697895..7323dbc5 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/terminalPicker.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/terminalPicker.test.ts @@ -2,6 +2,7 @@ import assert from 'node:assert'; import * as vscode from 'vscode'; +import { CMD_BIND_TO_DESTINATION, CMD_OPEN_STATUS_BAR_MENU } from '../../constants/commandIds'; import { cleanupFiles, closeAllEditors, @@ -11,6 +12,7 @@ import { findTerminalItems, getLogCapture, loadSettingsProfile, + openAndDismiss, parseQuickPickItemsFromLogLine, settle, standardSuite, @@ -22,7 +24,7 @@ const TERMINAL_OVERFLOW_COUNT = 6; const MAX_INLINE_DEFAULT = 5; const FILE_OVERFLOW_THRESHOLD = 5; -standardSuite('Terminal Picker', { assisted: true }, (log) => { +standardSuite('Terminal Picker', (log) => { const terminals: vscode.Terminal[] = []; teardown(async () => { @@ -35,7 +37,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { await settle(); }); - test('[assisted] terminal-picker-001: active terminal is marked with active badge', async () => { + test('terminal-picker-001: active terminal is marked with active badge', async () => { const t1 = await createTerminal('rl-tp-001-a', terminals); await createTerminal('rl-tp-001-b', terminals); t1.show(true); @@ -44,7 +46,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-tp-001'); - await waitForHuman('terminal-picker-001', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-001'); const items = extractQuickPickItemsLogged(lines); @@ -83,7 +85,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Active terminal: all 6 fields. Non-active: no badge, no isActive'); }); - test('[assisted] terminal-picker-002: bound terminal is marked with bound badge', async () => { + test('terminal-picker-002: bound terminal is marked with bound badge', async () => { await createTerminal('rl-tp-002', terminals); await vscode.commands.executeCommand('rangelink.bindToTerminalHere'); await settle(); @@ -94,7 +96,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-tp-002'); - await waitForHuman('terminal-picker-002', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-002'); const items = extractQuickPickItemsLogged(lines); @@ -133,7 +135,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Bound first (all 6 fields), active other second'); }); - test('[assisted] terminal-picker-003: terminal that is both active and bound shows dual badge', async () => { + test('terminal-picker-003: terminal that is both active and bound shows dual badge', async () => { const t = await createTerminal('rl-tp-003', terminals); await vscode.commands.executeCommand('rangelink.bindToTerminalHere'); t.show(true); @@ -142,7 +144,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-tp-003'); - await waitForHuman('terminal-picker-003', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-003'); const items = extractQuickPickItemsLogged(lines); @@ -173,7 +175,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Dual badge: all 6 fields validated'); }); - test('[assisted] terminal-picker-004: bound terminal always appears first in the list', async () => { + test('terminal-picker-004: bound terminal always appears first in the list', async () => { await createTerminal('rl-tp-004-b', terminals); await vscode.commands.executeCommand('rangelink.bindToTerminalHere'); await settle(); @@ -182,7 +184,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-tp-004'); - await waitForHuman('terminal-picker-004', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-004'); const items = extractQuickPickItemsLogged(lines); @@ -221,7 +223,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Bound terminal first — all 6 fields'); }); - test('[assisted] terminal-picker-005: active non-bound terminal appears second', async () => { + test('terminal-picker-005: active non-bound terminal appears second', async () => { await createTerminal('rl-tp-005-a', terminals); await vscode.commands.executeCommand('rangelink.bindToTerminalHere'); await settle(); @@ -232,7 +234,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-tp-005'); - await waitForHuman('terminal-picker-005', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-005'); const items = extractQuickPickItemsLogged(lines); @@ -271,13 +273,13 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Bound first, active second — all 6 fields'); }); - test('[assisted] terminal-picker-006: hidden IDE terminals are absent from the picker', async () => { + test('terminal-picker-006: hidden IDE terminals are absent from the picker', async () => { await createTerminal('rl-tp-006', terminals); const logCapture = getLogCapture(); logCapture.mark('before-tp-006'); - await waitForHuman('terminal-picker-006', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-006'); const items = extractQuickPickItemsLogged(lines); @@ -308,14 +310,14 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Only the test terminal appears — full field validation'); }); - test('[assisted] terminal-picker-007: all terminals shown inline when within maxInline limit', async () => { + test('terminal-picker-007: all terminals shown inline when within maxInline limit', async () => { await createTerminal('rl-tp-007-a', terminals); await createTerminal('rl-tp-007-b', terminals); const logCapture = getLogCapture(); logCapture.mark('before-tp-007'); - await waitForHuman('terminal-picker-007', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-007'); const items = extractQuickPickItemsLogged(lines); @@ -360,7 +362,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Both terminals inline, full field validation'); }); - test('[assisted] terminal-picker-008: overflow shows "More terminals..." when exceeding maxInline', async () => { + test('terminal-picker-008: overflow shows "More terminals..." when exceeding maxInline', async () => { for (let i = 1; i <= TERMINAL_OVERFLOW_COUNT; i++) { await createTerminal(`rl-tp-008-${i}`, terminals); } @@ -368,7 +370,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-tp-008'); - await waitForHuman('terminal-picker-008', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-008'); const items = extractQuickPickItemsLogged(lines); @@ -525,7 +527,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Parent picker reopened with identical items'); }); - test('[assisted] terminal-picker-011: maxInline setting changes overflow threshold', async () => { + test('terminal-picker-011: maxInline setting changes overflow threshold', async () => { await loadSettingsProfile('terminal-picker-low', log); const LOW_MAX_INLINE = 2; const TC_TERMINAL_COUNT = 3; @@ -537,7 +539,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-tp-011'); - await waitForHuman('terminal-picker-011', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-011'); const items = extractQuickPickItemsLogged(lines); @@ -586,13 +588,13 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ maxInline=2: overflow + active inline terminal fully validated'); }); - test('[assisted] terminal-picker-012: terminal picker appears inline in R-M menu when unbound', async () => { + test('terminal-picker-012: terminal picker appears inline in R-M menu when unbound', async () => { await createTerminal('rl-tp-012', terminals); const logCapture = getLogCapture(); logCapture.mark('before-tp-012'); - await waitForHuman('terminal-picker-012', 'Open R-M menu (Cmd+R Cmd+M), then Escape'); + await openAndDismiss(CMD_OPEN_STATUS_BAR_MENU); const lines = logCapture.getLinesSince('before-tp-012'); const items = extractQuickPickItemsLogged(lines); @@ -628,13 +630,13 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Terminal inline in R-M menu with full description'); }); - test('[assisted] terminal-picker-013: terminal picker appears inline in R-D destination picker', async () => { + test('terminal-picker-013: terminal picker appears inline in R-D destination picker', async () => { await createTerminal('rl-tp-013', terminals); const logCapture = getLogCapture(); logCapture.mark('before-tp-013'); - await waitForHuman('terminal-picker-013', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-tp-013'); const items = extractQuickPickItemsLogged(lines); @@ -665,7 +667,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { log('✓ Terminal inline in R-D picker — full fields'); }); - test('[assisted] bind-to-destination-013: R-D picker shows both overflow items when many terminals and files are open', async () => { + test('bind-to-destination-013: R-D picker shows both overflow items when many terminals and files are open', async () => { for (let i = 1; i <= TERMINAL_OVERFLOW_COUNT; i++) { await createTerminal(`rl-btd-013-${i}`, terminals); } @@ -686,7 +688,7 @@ standardSuite('Terminal Picker', { assisted: true }, (log) => { const logCapture = getLogCapture(); logCapture.mark('before-btd-013'); - await waitForHuman('bind-to-destination-013', 'Press Cmd+R Cmd+D, then Escape'); + await openAndDismiss(CMD_BIND_TO_DESTINATION); const lines = logCapture.getLinesSince('before-btd-013'); const items = extractQuickPickItemsLogged(lines); diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/textEditorDestination.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/textEditorDestination.test.ts index 7c645316..cfbf63fc 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/textEditorDestination.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/textEditorDestination.test.ts @@ -19,7 +19,7 @@ import { waitForHuman, } from '../helpers'; -standardSuite('Text Editor Destination', { assisted: true }, (log) => { +standardSuite('Text Editor Destination', (log) => { const tmpFileUris: vscode.Uri[] = []; teardown(async () => { diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/unbind.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/unbind.test.ts index fcd5beea..2e7038ea 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/unbind.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/unbind.test.ts @@ -12,7 +12,7 @@ import { standardSuite, } from '../helpers'; -standardSuite('Unbind Destination', {}, (_log) => { +standardSuite('Unbind Destination', (_log) => { let testFileUri: vscode.Uri; suiteSetup(async () => { diff --git a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/untitledNavigation.test.ts b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/untitledNavigation.test.ts index faae13b9..2de33bb8 100644 --- a/packages/rangelink-vscode-extension/src/__integration-tests__/suite/untitledNavigation.test.ts +++ b/packages/rangelink-vscode-extension/src/__integration-tests__/suite/untitledNavigation.test.ts @@ -70,7 +70,7 @@ const navigateToUntitledLink = ( }); }; -standardSuite('Untitled File Navigation', {}, (_log) => { +standardSuite('Untitled File Navigation', (_log) => { let untitledDoc: vscode.TextDocument; let untitledDisplayName: string;