Skip to content
| Marketplace
Sign in
Visual Studio Code>Testing>Open Test AdapterNew to Visual Studio Code? Get it now.
Open Test Adapter

Open Test Adapter

AppGates

|
1 install
| (0) | Free
Generic VS Code Test Explorer adapter that delegates to an external script
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Open Test Adapter

A generic VS Code Test Explorer extension that delegates test discovery and execution to an external adapter script. The extension is framework-agnostic — any test framework can integrate by implementing a simple JSON protocol.

Features

  • Framework-agnostic — works with any test framework via a simple JSON adapter script
  • Run tests from the Test Explorer sidebar with pass/fail/error results and duration
  • Debug tests with breakpoints — supports both Bun and Node.js debuggers
  • Click-to-navigate — double-click a test to jump to the exact source line
  • Auto-discovery — file watchers re-discover tests when source files change
  • Bun & Node.js — choose your runtime; TypeScript runs natively with Bun, or use Node with a loader
  • Fixture & test filtering — run individual tests, entire fixtures, or all tests at once
  • Browser testing — run and debug tests in Chrome/Edge with zero extra dependencies

Quick Start

  1. Install the extension (npm run deploy or from .vsix)
  2. Create an adapter script that implements the Adapter Protocol
  3. Configure the extension to point to your adapter script
  4. Open the Test Explorer — your tests appear automatically

Architecture

flowchart LR
    subgraph VS Code
        TE[Test Explorer UI]
        EXT[Open Test Adapter]
    end

    subgraph "External Process (bun / node)"
        AS[Your Adapter Script]
        TF[Your Test Framework]
    end

    TE -- "discover / run / debug" --> EXT
    EXT -- "spawns process" --> AS
    AS -- "calls" --> TF
    AS -- "JSON on stdout" --> EXT
    EXT -- "pass / fail / error" --> TE

The extension spawns your adapter script as a child process using the configured runtime (bun or node). It sends commands via CLI arguments and reads structured JSON from stdout. The extension never touches your test framework directly.

sequenceDiagram
    participant UI as Test Explorer
    participant E as Extension
    participant A as Adapter Script

    Note over E,A: Discovery
    UI->>E: Open test panel
    E->>A: spawn: adapter.ts discover
    A-->>E: DiscoverResult JSON (stdout)
    E->>UI: Populate test tree

    Note over E,A: Execution
    UI->>E: Click "Run"
    E->>A: spawn: adapter.ts run --tests id1,id2
    A-->>E: RunResult JSON (stdout)
    E->>UI: Mark pass/fail/error

Adapter Protocol

Type definitions: src/AdapterProtocol.ts

This is the contract your adapter script must fulfill.

Your adapter script must handle two CLI commands and write JSON to stdout.

Command: discover

bun my-adapter.ts discover

Output a JSON array of discovered fixtures with their tests:

interface DiscoveredTest {
    name: string;
    line?: number;       // 0-based line number (enables click-to-navigate)
}

interface DiscoveredFixture {
    name: string;
    tests: DiscoveredTest[];
    filePath: string;    // absolute path to source file
    line?: number;       // 0-based line of the class/suite
}

// stdout: DiscoveredFixture[]

Example output:

[
  {
    "name": "MathTests",
    "filePath": "/project/tests/MathTests.ts",
    "line": 5,
    "tests": [
      { "name": "additionTest", "line": 10 },
      { "name": "divisionByZeroTest", "line": 20 }
    ]
  }
]

Command: run --tests id1,id2,...

bun my-adapter.ts run --tests MathTests.additionTest,MathTests.divisionByZeroTest

Run the specified tests and output a JSON array of results:

interface TestResult {
    testId: string;                              // "FixtureName.testName"
    status: "passed" | "failed" | "errored";
    duration?: number;                           // milliseconds
    error?: string;                              // error message
}

// stdout: TestResult[]

Example output:

[
  { "testId": "MathTests.additionTest", "status": "passed", "duration": 2 },
  { "testId": "MathTests.divisionByZeroTest", "status": "failed", "duration": 1, "error": "Expected Error but got result" }
]

Important Rules

  • Use process.stdout.write() for JSON output, not console.log() — your test framework might redirect console output
  • Test IDs must use the format FixtureName.testName and match between discovery and run
  • Line numbers are optional but enable double-click navigation to the test source
  • The process must exit after writing output — clean up any timers or open handles

Writing a Custom Adapter — Step by Step

Step 1: Create the adapter script

Create a file (e.g. my-adapter.ts) that handles the two commands:

// my-adapter.ts

// --- Discovery ---

async function discover() {
    // TODO: use your test framework's API to find test files/classes/methods
    const fixtures = [
        {
            name: "MathTests",
            filePath: "/absolute/path/to/MathTests.ts",
            line: 5,
            tests: [
                { name: "additionTest", line: 10 },
                { name: "subtractionTest", line: 25 },
            ]
        }
    ];
    process.stdout.write(JSON.stringify(fixtures));
}

// --- Execution ---

async function run(testIds: string[]) {
    const results = [];

    for (const id of testIds) {
        const [fixture, test] = id.split(".");
        const start = performance.now();

        try {
            // TODO: instantiate your fixture class and call the test method
            // await myFramework.runTest(fixture, test);

            results.push({
                testId: id,
                status: "passed",
                duration: Math.round(performance.now() - start)
            });
        } catch (err) {
            results.push({
                testId: id,
                status: "failed",
                duration: Math.round(performance.now() - start),
                error: String(err)
            });
        }
    }

    process.stdout.write(JSON.stringify(results));
}

// --- CLI entry point ---

const command = process.argv[2];

if (command === "discover") {
    await discover();
} else if (command === "run") {
    const testIds = process.argv[process.argv.indexOf("--tests") + 1].split(",");
    await run(testIds);
}

Step 2: Test it from the command line

# Discovery
bun my-adapter.ts discover
# Should output: [{"name":"MathTests","tests":[...],"filePath":"..."}]

# Run a test
bun my-adapter.ts run --tests MathTests.additionTest
# Should output: [{"testId":"MathTests.additionTest","status":"passed","duration":2}]

Step 3: Configure the extension

Add to your .vscode/settings.json or workspace settings:

{
    "openTestAdapter.adapterScript": "my-adapter.ts",
    "openTestAdapter.runtime": "bun"
}

Step 4: Open Test Explorer

Reload VS Code and open the Test Explorer panel. Your tests should appear. Click play to run them.

Configuration

Setting Description Default
openTestAdapter.adapterScript Path to the adapter script (absolute, or relative to the first workspace folder) FixtureTestAdapter.ts
openTestAdapter.runtime Runtime executable: bun, node, or a full path. If empty, auto-detects ./node_modules/bun/bin/bun.exe ""
openTestAdapter.watchPatterns Glob patterns (relative to workspace) to watch — triggers re-discovery on file changes []
openTestAdapter.browserUrl URL pattern for browser test execution. Use {tests} as placeholder for test IDs ""
openTestAdapter.browserDebugPort Chrome DevTools Protocol port for browser communication 9222
openTestAdapter.browserExecutable Path to Chrome or Edge. If empty, auto-detects from common install locations ""

Runtime: Bun vs Node

The extension supports both bun and node as runtimes:

Bun Node
Config "runtime": "bun" or leave empty (auto-detected) "runtime": "node"
TypeScript Native .ts support, no build step Requires tsx, ts-node, or pre-compiled .js
Debugging Uses the Bun VS Code extension Uses the built-in Node.js debugger
Speed Faster startup Wider compatibility

Browser Testing

The extension can run and debug tests directly in a browser (Chrome or Edge) — no Playwright or Puppeteer required. The extension communicates with the browser via the Chrome DevTools Protocol (CDP), which is built into every Chromium-based browser.

How It Works

sequenceDiagram
    participant UI as Test Explorer
    participant E as Extension
    participant B as Browser (Chrome/Edge)
    participant P as Your Test Page

    UI->>E: Click "Run (Browser)"
    E->>B: Launch with --remote-debugging-port
    B->>P: Navigate to browserUrl?tests=id1,id2
    P->>P: Run tests
    P->>P: document.title = "DONE:" + JSON.stringify(results)
    E->>B: Poll GET /json (CDP)
    B-->>E: Tab title contains "DONE:..."
    E->>UI: Parse results, mark pass/fail/error

Run Profiles

The extension provides four run profiles in the Test Explorer dropdown:

Profile Runtime Headless Debugger
Run bun / node N/A None
Debug bun / node N/A bun / Node.js debugger
Run (Browser) Chrome / Edge Yes None
Debug (Browser) Chrome / Edge No Chrome DevTools + VS Code

Browser Result Convention

When tests complete, the test page must signal results by setting document.title:

// In your test page (browser):
const results = [
    { testId: "MathTests.additionTest", status: "passed", duration: 5 },
    { testId: "MathTests.divisionByZeroTest", status: "failed", duration: 3, error: "Expected Error" }
];

document.title = "DONE:" + JSON.stringify(results);

The results array uses the same RunResult format as the process-based adapter. The extension polls the browser's CDP endpoint and detects the title change to collect results.

Setting Up Browser Testing

Step 1: Configure the browser URL in your workspace settings:

{
    "openTestAdapter.browserUrl": "http://localhost:5173/tests?tests={tests}"
}

The {tests} placeholder is replaced with comma-separated test IDs (e.g. MathTests.additionTest,MathTests.divisionByZeroTest).

Step 2: Create a test page that:

  1. Reads test IDs from the URL (e.g. from the tests query parameter)
  2. Runs the requested tests using your framework
  3. Sets document.title = "DONE:" + JSON.stringify(results) when complete

Step 3: Start your dev server (e.g. npm run dev for Vite) so the browser URL is reachable.

Step 4: Select "Run (Browser)" or "Debug (Browser)" from the Test Explorer profile dropdown and click play.

Browser Auto-Detection

The extension auto-detects installed browsers in this order:

  1. Microsoft Edge (always available on Windows 11)
  2. Google Chrome

Override with openTestAdapter.browserExecutable if needed.

Development

Scripts

Script Description
npm run build Compile TypeScript
npm run deploy Build, auto-increment version, package .vsix, and install into VS Code
npm run publish Deploy + publish to VS Code Marketplace (requires VsCodeMarketplaceAccessToken env var)

Publishing

export VsCodeMarketplaceAccessToken=your-pat-here
npm run publish

Troubleshooting

Tests not appearing: Check the adapter script path in settings. Run bun <adapterScript> discover manually to verify it outputs valid JSON.

Tests spin endlessly: The adapter process isn't exiting. Ensure any timers or open handles are cleaned up after execution.

All tests show as errored with "No result received": The testId in the run output doesn't match the discovery ID. The format must be FixtureName.testName.

Debug breakpoints don't bind: Make sure you have the correct debugger extension installed — Bun for VS Code for bun, or the built-in debugger for node.

Browser tests time out: Ensure your dev server is running and the browserUrl is reachable. Verify your test page sets document.title = "DONE:" + JSON.stringify(results) when tests complete.

Browser not found: Set openTestAdapter.browserExecutable to the full path of your Chrome or Edge executable.

CDP port conflict: If port 9222 is already in use, change openTestAdapter.browserDebugPort to another port.

License

MIT

Publish

https://marketplace.visualstudio.com/manage/publishers/AppGates?auth_redirect=True

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft