Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/actions/run-integration-tests-with-extensions/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: 'Run Integration Tests (with extensions)'
description: 'Run pnpm test:release:with-extensions --no-assisted under Xvfb — installs marketplace extensions and runs automated tests that depend on them'
runs:
using: 'composite'
steps:
- name: Install Xvfb and VS Code native dependencies
shell: bash
run: |
sudo apt-get update
ALSA_PKG=$(apt-cache show libasound2t64 >/dev/null 2>&1 && echo libasound2t64 || echo libasound2)
sudo apt-get install -y --no-install-recommends xvfb libgtk-3-0 libxss1 libnss3 "$ALSA_PKG"

- name: Cache VS Code test binary
uses: actions/cache@v4
with:
path: ~/.vscode-test
key: vscode-test-${{ runner.os }}-stable
restore-keys: vscode-test-${{ runner.os }}-

- name: Run integration tests under Xvfb
shell: bash
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" pnpm test:release:with-extensions --no-assisted
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,20 @@ jobs:
# For PRs: compare with base branch
# For pushes to main: just count current TODOs
base-ref: ${{ github.event_name == 'pull_request' && github.base_ref || '' }}

test-with-extensions:
name: Integration Tests (with extensions)
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm

- name: Install dependencies
uses: ./.github/actions/install-deps

- name: Run integration tests with extensions
uses: ./.github/actions/run-integration-tests-with-extensions
22 changes: 3 additions & 19 deletions packages/rangelink-vscode-extension/.vscode-test.automated.mjs
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
import * as os from 'node:os';
import * as path from 'node:path';

import { defineConfig } from '@vscode/test-cli';

const MOCHA_TIMEOUT_MS = 20_000;
const ASSISTED_TEST_GREP = '\\[assisted\\]';
const USER_DATA_DIR = path.join(os.tmpdir(), 'rl-vscode-test');
import { CI_TIMEOUT_MS, BASE_CONFIG } from './.vscode-test.base.mjs';

export default defineConfig([
{
files: 'out/__integration-tests__/suite/**/*.test.js',
extensionDevelopmentPath: ['./', './test-fixtures/dummy-ai-extension/'],
workspaceFolder: './',
version: 'stable',
launchArgs: ['--user-data-dir', USER_DATA_DIR],
env: {
RANGELINK_CAPTURE_LOGS: 'true',
},
mocha: {
timeout: MOCHA_TIMEOUT_MS,
grep: ASSISTED_TEST_GREP,
invert: true,
},
...BASE_CONFIG,
mocha: { timeout: CI_TIMEOUT_MS, ...BASE_CONFIG.mocha },
},
]);
29 changes: 29 additions & 0 deletions packages/rangelink-vscode-extension/.vscode-test.base.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as os from 'node:os';
import * as path from 'node:path';

// 10 minutes per test — assisted tests block on human interaction.
export const ASSISTED_TIMEOUT_MS = 600_000;

// No human in CI — automated tests resolve in under 5s.
export const CI_TIMEOUT_MS = 20_000;

export const userDataDir = (suffix = '') => [
'--user-data-dir',
path.join(os.tmpdir(), `rl-vscode-test${suffix}`),
];

// grep and invert are driven by env vars set in test-release-run.sh.
const envMocha = () => ({
...(process.env.MOCHA_GREP ? { grep: process.env.MOCHA_GREP } : {}),
...(process.env.MOCHA_INVERT === 'true' ? { invert: true } : {}),
});

export const BASE_CONFIG = {
files: 'out/__integration-tests__/suite/**/*.test.js',
extensionDevelopmentPath: ['./', './test-fixtures/dummy-ai-extension/'],
workspaceFolder: './',
version: 'stable',
launchArgs: userDataDir(),
env: { RANGELINK_CAPTURE_LOGS: 'true' },
mocha: envMocha(),
};
24 changes: 3 additions & 21 deletions packages/rangelink-vscode-extension/.vscode-test.mjs
Original file line number Diff line number Diff line change
@@ -1,28 +1,10 @@
import * as os from 'node:os';
import * as path from 'node:path';

import { defineConfig } from '@vscode/test-cli';

// 10 minutes per test — assisted tests block on human interaction (modal
// verdict dialogs don't auto-dismiss), so the human needs headroom to read
// instructions, complete UI actions, or step away for a break. Automated tests
// never come close to this threshold.
const MOCHA_TIMEOUT_MS = 600_000;
const USER_DATA_DIR = path.join(os.tmpdir(), 'rl-vscode-test');
import { ASSISTED_TIMEOUT_MS, BASE_CONFIG } from './.vscode-test.base.mjs';

export default defineConfig([
{
files: 'out/__integration-tests__/suite/**/*.test.js',
extensionDevelopmentPath: ['./', './test-fixtures/dummy-ai-extension/'],
workspaceFolder: './',
version: 'stable',
launchArgs: ['--user-data-dir', USER_DATA_DIR],
env: {
RANGELINK_CAPTURE_LOGS: 'true',
},
mocha: {
timeout: MOCHA_TIMEOUT_MS,
...(process.env.MOCHA_GREP ? { grep: process.env.MOCHA_GREP } : {}),
},
...BASE_CONFIG,
mocha: { timeout: ASSISTED_TIMEOUT_MS, ...BASE_CONFIG.mocha },
},
]);
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
import * as os from 'node:os';
import * as path from 'node:path';

import { defineConfig } from '@vscode/test-cli';

// Same timeout as the main config — assisted tests block on human interaction.
const MOCHA_TIMEOUT_MS = 600_000;
const USER_DATA_DIR = path.join(os.tmpdir(), 'rl-vscode-test-with-ext');
import { ASSISTED_TIMEOUT_MS, BASE_CONFIG, userDataDir } from './.vscode-test.base.mjs';

// Extensions installed from the marketplace before tests run.
// With these present, isClaudeCodeAvailable() returns true, enabling [assisted]
// tests that verify real focus + paste behavior.
// Marketplace extensions installed before tests run. With these present,
// isClaudeCodeAvailable() returns true, enabling tests that verify real
// focus + paste behavior.
const MARKETPLACE_EXTENSIONS = ['anthropic.claude-code'];

export default defineConfig([
{
files: 'out/__integration-tests__/suite/**/*.test.js',
extensionDevelopmentPath: ['./', './test-fixtures/dummy-ai-extension/'],
workspaceFolder: './',
version: 'stable',
...BASE_CONFIG,
launchArgs: userDataDir('-with-ext'),
installExtensions: MARKETPLACE_EXTENSIONS,
launchArgs: ['--user-data-dir', USER_DATA_DIR],
env: {
RANGELINK_CAPTURE_LOGS: 'true',
},
mocha: {
timeout: MOCHA_TIMEOUT_MS,
...(process.env.MOCHA_GREP ? { grep: process.env.MOCHA_GREP } : {}),
},
mocha: { timeout: ASSISTED_TIMEOUT_MS, ...BASE_CONFIG.mocha },
},
]);
2 changes: 1 addition & 1 deletion packages/rangelink-vscode-extension/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
| Coverage report | `pnpm test:coverage` (from extension dir) | Before PR / on demand | ✅ (with thresholds) |
| Integration tests | `pnpm test:release` | Before PR, after feature work | — |
| Integration (CI-safe) | `pnpm test:release:automated` | CI / headless environments | ✅ |
| Integration (extensions) | `pnpm test:release:with-extensions` | Tests needing real AI extensions | |
| Integration (extensions) | `pnpm test:release:with-extensions` | Tests needing real AI extensions | |
| Integration (filter) | `pnpm test:release:grep "<pattern>"` | Run specific TCs by ID or suite | — |
| Prepare QA test plan | `pnpm generate:qa-test-plan:vscode-extension` | Start of release cycle | — |
| Generate QA issue | `pnpm generate:qa-issue:vscode-extension` | At the start of each release cycle | — |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');

const SETTINGS_DIR = path.join(os.tmpdir(), 'rl-vscode-test', 'User');
const suffixIndex = process.argv.indexOf('--suffix');
if (suffixIndex !== -1 && !process.argv[suffixIndex + 1]) {
console.error('Error: --suffix requires a value');
process.exit(1);
}
const suffix = suffixIndex !== -1 ? process.argv[suffixIndex + 1] : '';
const DATA_DIR = `rl-vscode-test${suffix}`;
const SETTINGS_DIR = path.join(os.tmpdir(), DATA_DIR, 'User');
const SETTINGS_FILE = path.join(SETTINGS_DIR, 'settings.json');

const settings = {
Expand Down
14 changes: 13 additions & 1 deletion packages/rangelink-vscode-extension/scripts/test-release-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ if [[ -n "$LABEL_FILTER" ]]; then
fi
fi

if [[ -z "$GREP_PATTERN" && ( "$MODE" == "automated" || "$NO_ASSISTED" == true ) ]]; then
export MOCHA_GREP='\[assisted\]'
export MOCHA_INVERT=true
fi

OUTPUT_DIR="$PACKAGE_ROOT/qa/output"
mkdir -p "$OUTPUT_DIR"
TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
Expand Down Expand Up @@ -139,9 +144,16 @@ echo ""

pnpm test:release:prepare

if [[ "$WITH_EXTENSIONS" == "true" ]]; then
node "$SCRIPT_DIR/setup-integration-test-settings.js" --suffix -with-ext
fi

TEST_EXIT=0
# shellcheck disable=SC2086
MOCHA_GREP="$GREP_PATTERN" npx vscode-test $VSCODE_TEST_CONFIG 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | tee -a "$REPORT_FILE" || TEST_EXIT=$?
if [[ -n "$GREP_PATTERN" ]]; then
export MOCHA_GREP="$GREP_PATTERN"
fi
npx vscode-test $VSCODE_TEST_CONFIG 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | tee -a "$REPORT_FILE" || TEST_EXIT=$?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Quote $VSCODE_TEST_CONFIG to prevent word splitting.

The variable should be quoted to avoid potential issues if it contains special characters or spaces, following shell scripting best practices.

🐚 Proposed fix
-npx vscode-test $VSCODE_TEST_CONFIG 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | tee -a "$REPORT_FILE" || TEST_EXIT=$?
+npx vscode-test "$VSCODE_TEST_CONFIG" 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | tee -a "$REPORT_FILE" || TEST_EXIT=$?
📝 Committable suggestion

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

Suggested change
npx vscode-test $VSCODE_TEST_CONFIG 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | tee -a "$REPORT_FILE" || TEST_EXIT=$?
npx vscode-test "$VSCODE_TEST_CONFIG" 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | tee -a "$REPORT_FILE" || TEST_EXIT=$?
🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 156-156: Double quote to prevent globbing and word splitting.

(SC2086)

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

In `@packages/rangelink-vscode-extension/scripts/test-release-run.sh` at line 156,
The test runner command uses an unquoted variable npx vscode-test
$VSCODE_TEST_CONFIG ... which can cause word-splitting; update the invocation to
quote the variable name ("$VSCODE_TEST_CONFIG") so the full config string is
passed as a single argument and keep the rest of the pipeline (sed, tee, and the
TEST_EXIT assignment) unchanged; locate the command invoking npx vscode-test and
replace the unquoted $VSCODE_TEST_CONFIG with the quoted form.


if [[ -n "$GREP_PATTERN" && $TEST_EXIT -eq 0 ]]; then
if ! grep -qE '[1-9][0-9]* passing' "$REPORT_FILE"; then
Expand Down
Loading