Files
Profa 149c922bac feat(v3): add Vibium browser automation integration with real E2E testing
- Add VibiumClientImpl wrapper for browser automation via WebDriver BiDi
- Integrate axe-core for real accessibility testing in browser context
- Add multi-viewport screenshot capture service
- Implement visual regression testing with baseline comparison
- Create E2E test runner service with user flow generation
- Add real integration tests (no mocks) with CI environment detection
- Update visual-accessibility domain with browser mode support
- Add npm script test:integration:browser for real browser tests
- Bump version to 3.0.0-alpha.32

Key files:
- src/integrations/vibium/client.ts - Browser automation client
- src/domains/visual-accessibility/services/axe-core-integration.ts
- src/domains/test-execution/services/e2e-runner.ts
- tests/integrations/vibium/vibium-real.test.ts - NO MOCKS tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 09:24:43 +00:00

21 KiB

Vibium JavaScript Client API Reference

Version: 1.0.0 Source Analysis Date: 2026-01-19

This document provides a comprehensive API reference for the Vibium JavaScript client library, generated from source code analysis of the official implementation.


Table of Contents


Browser Launch API

browser.launch(options?: LaunchOptions): Promise<Vibe>

Launches a new browser instance and returns a Vibe controller.

Parameters:

interface LaunchOptions {
  headless?: boolean;        // Run browser in headless mode (default: false)
  port?: number;             // WebSocket port for BiDi connection (default: auto-assigned)
  executablePath?: string;   // Path to browser executable (default: bundled Chromium)
}

Returns: Promise<Vibe> - Browser controller instance

Example:

import { browser } from '@vibium/client';

// Launch in headed mode
const vibe = await browser.launch();

// Launch in headless mode
const vibe = await browser.launch({ headless: true });

// Use custom browser executable
const vibe = await browser.launch({
  executablePath: '/usr/bin/chromium'
});

Implementation Details:

  • Starts a "clicker" process that manages the browser
  • Establishes WebSocket connection via BiDi protocol
  • Automatically creates browsing context
  • Connection URL: ws://localhost:{port}

Navigation API

vibe.go(url: string): Promise<void>

Navigates to the specified URL and waits for the page to load completely.

Parameters:

  • url (string) - The URL to navigate to

Returns: Promise<void>

Wait Strategy: Waits for document load state complete (DOMContentLoaded + all resources loaded)

Example:

await vibe.go('https://example.com');
await vibe.go('https://example.com/login');

Implementation:

  • Uses BiDi command: browsingContext.navigate
  • Wait parameter: 'complete'
  • Blocks until navigation fully completes

Note: The current implementation does NOT include back(), forward(), or refresh() methods. These can be implemented using the BiDi client's browsingContext.traverseHistory command.


Element Location API

vibe.find(selector: string, options?: FindOptions): Promise<Element>

Locates an element by CSS selector and waits for it to exist.

Parameters:

interface FindOptions {
  timeout?: number;  // Timeout in milliseconds (default: 30000)
}
  • selector (string) - CSS selector to locate element
  • options (FindOptions) - Optional configuration

Returns: Promise<Element> - Element handle with interaction methods

Throws:

  • TimeoutError - If element not found within timeout
  • ElementNotFoundError - If selector matches no elements

Example:

// Find with default timeout (30s)
const button = await vibe.find('button.submit');

// Find with custom timeout (10s)
const input = await vibe.find('input[name="email"]', { timeout: 10000 });

Implementation:

  • Uses custom BiDi command: vibium:find
  • Server-side waits for element existence
  • Returns element metadata: tag, text, bounding box

Note: The current implementation does NOT include a findAll() method for locating multiple elements.


Element Interaction API

element.click(options?: ActionOptions): Promise<void>

Clicks the element after verifying actionability.

Parameters:

interface ActionOptions {
  timeout?: number;  // Timeout for actionability checks (default: 30000ms)
}

Returns: Promise<void>

Actionability Checks:

  1. Element is visible
  2. Element is stable (not animating)
  3. Element can receive pointer events (not obscured)
  4. Element is enabled (not disabled)

Throws:

  • TimeoutError - If element not actionable within timeout
  • ElementNotFoundError - If element no longer exists

Example:

const button = await vibe.find('button.submit');
await button.click();

// With custom timeout
await button.click({ timeout: 5000 });

Implementation:

  • Uses custom BiDi command: vibium:click
  • Server performs all actionability checks
  • Simulates realistic click at element center

element.type(text: string, options?: ActionOptions): Promise<void>

Types text into the element after verifying actionability.

Parameters:

  • text (string) - Text to type into element
  • options (ActionOptions) - Optional timeout configuration

Returns: Promise<void>

Actionability Checks:

  1. Element is visible
  2. Element is stable (not animating)
  3. Element can receive pointer events
  4. Element is enabled
  5. Element is editable (input, textarea, contenteditable)

Throws:

  • TimeoutError - If element not actionable within timeout
  • ElementNotFoundError - If element no longer exists

Example:

const input = await vibe.find('input[type="email"]');
await input.type('user@example.com');

const textarea = await vibe.find('textarea');
await textarea.type('Multi-line\ntext content');

Implementation:

  • Uses custom BiDi command: vibium:type
  • Server performs actionability checks
  • Focuses element before typing
  • Simulates realistic keyboard events

Note: The current implementation does NOT include a clear() method. To clear an input, you would need to:

  1. Get current value via getAttribute('value')
  2. Send backspace keystrokes
  3. Or use evaluate() to set value to empty string

Element Properties API

element.text(): Promise<string>

Retrieves the visible text content of the element.

Returns: Promise<string> - Trimmed text content

Throws:

  • ElementNotFoundError - If element no longer exists

Example:

const heading = await vibe.find('h1');
const text = await heading.text();
console.log(text); // "Welcome to Our Site"

Implementation:

  • Uses BiDi script.callFunction to execute: el.textContent.trim()
  • Returns empty string if element has no text

element.getAttribute(name: string): Promise<string | null>

Retrieves the value of an element attribute.

Parameters:

  • name (string) - Attribute name to retrieve

Returns: Promise<string | null> - Attribute value or null if not present

Example:

const link = await vibe.find('a.external');
const href = await link.getAttribute('href');
const target = await link.getAttribute('target');

const input = await vibe.find('input[type="checkbox"]');
const checked = await input.getAttribute('checked');

Implementation:

  • Uses BiDi script.callFunction to execute: el.getAttribute(name)
  • Returns null if attribute doesn't exist

element.boundingBox(): Promise<BoundingBox>

Retrieves the element's bounding box (position and dimensions).

Returns:

interface BoundingBox {
  x: number;        // X coordinate (viewport-relative)
  y: number;        // Y coordinate (viewport-relative)
  width: number;    // Element width in pixels
  height: number;   // Element height in pixels
}

Throws:

  • ElementNotFoundError - If element no longer exists

Example:

const button = await vibe.find('button.submit');
const box = await button.boundingBox();
console.log(box);
// { x: 100, y: 200, width: 120, height: 40 }

Implementation:

  • Uses BiDi script.callFunction to execute: el.getBoundingClientRect()
  • Coordinates are viewport-relative (not page-relative)

element.info: ElementInfo (readonly)

Cached element information captured at find time.

Type:

interface ElementInfo {
  tag: string;          // HTML tag name (e.g., "button", "input")
  text: string;         // Text content at find time
  box: BoundingBox;     // Bounding box at find time
}

Example:

const button = await vibe.find('button.submit');
console.log(button.info.tag);   // "button"
console.log(button.info.text);  // "Submit"
console.log(button.info.box);   // { x: 100, y: 200, width: 120, height: 40 }

Note: This is a snapshot from when the element was located. Use methods like text() and boundingBox() to get current values.


Screenshot API

vibe.screenshot(): Promise<Buffer>

Captures a screenshot of the current viewport.

Returns: Promise<Buffer> - PNG image data as Buffer

Example:

import { writeFile } from 'fs/promises';

const screenshot = await vibe.screenshot();
await writeFile('screenshot.png', screenshot);

Implementation:

  • Uses BiDi command: browsingContext.captureScreenshot
  • Returns viewport screenshot (not full page)
  • Format: PNG encoded as base64, decoded to Buffer

Note: The current implementation only supports viewport screenshots. Full page screenshots would require:

  • Calculating total page height
  • Scrolling and capturing multiple viewports
  • Stitching images together

Saving Options:

  • To file: Use Node.js fs.writeFile() with Buffer
  • To base64: Use screenshot.toString('base64')

Script Execution API

vibe.evaluate<T>(script: string): Promise<T>

Executes JavaScript code in the page context.

Parameters:

  • script (string) - JavaScript code to execute

Type Parameter:

  • T - Expected return type (default: unknown)

Returns: Promise<T> - Result of script execution

Example:

// Get page title
const title = await vibe.evaluate<string>('return document.title');

// Get multiple values
const pageInfo = await vibe.evaluate<{ url: string; title: string }>(`
  return {
    url: window.location.href,
    title: document.title
  };
`);

// Modify page state
await vibe.evaluate<void>(`
  document.body.style.backgroundColor = 'red';
`);

Implementation:

  • Uses BiDi command: script.callFunction
  • Wraps script in anonymous function: () => { ${script} }
  • Awaits promises automatically (awaitPromise: true)
  • Supports returning serializable values

Limitations:

  • Cannot return DOM elements or functions
  • Must return JSON-serializable data
  • No access to Node.js environment

Session Management API

vibe.quit(): Promise<void>

Closes the browser connection and terminates the browser process.

Returns: Promise<void>

Example:

const vibe = await browser.launch();
try {
  await vibe.go('https://example.com');
  // ... perform actions
} finally {
  await vibe.quit();
}

Implementation:

  1. Closes BiDi WebSocket connection
  2. Stops the clicker process
  3. Rejects all pending commands with ConnectionError

Best Practice: Always call quit() in a finally block to ensure cleanup.


Error Types

All error classes are exported from the main package and extend the standard Error class.

ConnectionError

Thrown when connecting to the browser fails.

class ConnectionError extends Error {
  url: string;        // WebSocket URL that failed
  cause?: Error;      // Underlying error
}

When Thrown:

  • WebSocket connection refused
  • Browser process failed to start
  • Invalid WebSocket URL

Example:

import { ConnectionError } from '@vibium/client';

try {
  const vibe = await browser.launch({ port: 9999 });
} catch (err) {
  if (err instanceof ConnectionError) {
    console.error(`Failed to connect to ${err.url}`);
    console.error(`Cause: ${err.cause?.message}`);
  }
}

TimeoutError

Thrown when a wait operation exceeds the timeout.

class TimeoutError extends Error {
  selector: string;   // CSS selector that timed out
  timeout: number;    // Timeout duration in ms
  reason?: string;    // Why the wait failed
}

When Thrown:

  • find() - Element not found within timeout
  • click() - Element not actionable within timeout
  • type() - Element not actionable within timeout

Example:

import { TimeoutError } from '@vibium/client';

try {
  await vibe.find('button.submit', { timeout: 5000 });
} catch (err) {
  if (err instanceof TimeoutError) {
    console.error(`Timeout after ${err.timeout}ms waiting for '${err.selector}'`);
    if (err.reason) {
      console.error(`Reason: ${err.reason}`);
    }
  }
}

ElementNotFoundError

Thrown when a selector matches no elements.

class ElementNotFoundError extends Error {
  selector: string;   // CSS selector that matched nothing
}

When Thrown:

  • element.text() - Element no longer in DOM
  • element.boundingBox() - Element no longer in DOM
  • Re-querying element properties after DOM changes

Example:

import { ElementNotFoundError } from '@vibium/client';

try {
  const button = await vibe.find('button.submit');
  // ... page navigation or DOM change ...
  const text = await button.text(); // Throws if element removed
} catch (err) {
  if (err instanceof ElementNotFoundError) {
    console.error(`Element no longer exists: ${err.selector}`);
  }
}

BrowserCrashedError

Thrown when the browser process dies unexpectedly.

class BrowserCrashedError extends Error {
  exitCode: number;   // Browser process exit code
  output?: string;    // Stderr/stdout output
}

When Thrown:

  • Browser process crashes during operation
  • Browser killed by OS (OOM, signals)
  • Unhandled browser exceptions

Example:

import { BrowserCrashedError } from '@vibium/client';

try {
  await vibe.go('https://example.com');
} catch (err) {
  if (err instanceof BrowserCrashedError) {
    console.error(`Browser crashed with exit code ${err.exitCode}`);
    if (err.output) {
      console.error(`Output: ${err.output}`);
    }
  }
}

Auto-Wait Behavior

Vibium implements intelligent auto-waiting to handle dynamic web applications without explicit waits.

Element Location Waits

find(selector, options) waits for:

  1. Element to exist in DOM
  2. Element to match CSS selector

Timeout: 30000ms (default), configurable via FindOptions.timeout

Implementation: Server-side polling with BiDi vibium:find command


Click Actionability Checks

click(options) waits for:

  1. Visible - Element has non-zero bounding box and is not display: none or visibility: hidden
  2. Stable - Element position not changing (not animating)
  3. Receives Events - Not obscured by another element (z-index check)
  4. Enabled - Not disabled via disabled attribute or CSS pointer-events: none

Timeout: 30000ms (default), configurable via ActionOptions.timeout

Implementation: Server-side checks via BiDi vibium:click command

Retry Logic: Server retries checks until all pass or timeout exceeded


Type Actionability Checks

type(text, options) waits for:

  1. Visible - Element has non-zero bounding box
  2. Stable - Element position not changing
  3. Receives Events - Not obscured by another element
  4. Enabled - Not disabled
  5. Editable - Is <input>, <textarea>, or has contenteditable="true"

Timeout: 30000ms (default), configurable via ActionOptions.timeout

Implementation: Server-side checks via BiDi vibium:type command


Navigation Waits

go(url) waits for:

  • Document readyState === 'complete'
  • All resources loaded (images, stylesheets, scripts)

Strategy: BiDi browsingContext.navigate with wait: 'complete'

No Timeout: Relies on browser's internal navigation timeout


Best Practices

// ✅ GOOD: Let Vibium handle waiting
const button = await vibe.find('button.submit');
await button.click();

// ❌ BAD: Manual sleep is unnecessary
const button = await vibe.find('button.submit');
await new Promise(resolve => setTimeout(resolve, 1000));
await button.click();

// ✅ GOOD: Adjust timeout for slow elements
const slowButton = await vibe.find('.lazy-load-button', { timeout: 60000 });

// ✅ GOOD: Handle expected timeouts gracefully
try {
  await vibe.find('.optional-banner', { timeout: 3000 });
} catch (err) {
  if (err instanceof TimeoutError) {
    // Banner not present, continue
  } else {
    throw err;
  }
}

TypeScript Types

Core Types

// Browser Launch
interface LaunchOptions {
  headless?: boolean;
  port?: number;
  executablePath?: string;
}

// Element Finding
interface FindOptions {
  timeout?: number; // Default: 30000
}

// Element Actions
interface ActionOptions {
  timeout?: number; // Default: 30000
}

// Element Information
interface ElementInfo {
  tag: string;
  text: string;
  box: BoundingBox;
}

interface BoundingBox {
  x: number;
  y: number;
  width: number;
  height: number;
}

BiDi Protocol Types

// Internal types (not typically used directly)

interface BiDiCommand {
  id: number;
  method: string;
  params: Record<string, unknown>;
}

interface BiDiResponse {
  id: number;
  type: 'success' | 'error';
  result?: unknown;
  error?: string;
  message?: string;
}

interface BiDiEvent {
  method: string;
  params: Record<string, unknown>;
}

interface BrowsingContextInfo {
  context: string;
  url: string;
  children: BrowsingContextInfo[];
  parent?: string;
}

interface NavigationResult {
  navigation: string;
  url: string;
}

interface ScreenshotResult {
  data: string; // base64 PNG
}

API Completeness Notes

Implemented Features

  • ✅ Browser launch with options
  • ✅ Navigation (go())
  • ✅ Element finding (find())
  • ✅ Element interaction (click(), type())
  • ✅ Element properties (text(), getAttribute(), boundingBox())
  • ✅ Screenshot capture (viewport only)
  • ✅ Script execution (evaluate())
  • ✅ Session management (quit())
  • ✅ Comprehensive error types
  • ✅ Auto-wait for actionability

Missing Features

  • ❌ back(), forward(), refresh() navigation
  • ❌ findAll() for multiple elements
  • ❌ clear() method for input fields
  • ❌ Full page screenshots
  • ❌ File upload functionality
  • ❌ Cookie management
  • ❌ Network interception
  • ❌ Browser context/page management (multi-tab)
  • ❌ Event listeners (page load, dialog, download)

These missing features can be implemented by:

  1. Adding BiDi protocol commands (e.g., browsingContext.traverseHistory for back/forward)
  2. Extending the Vibe and Element classes with new methods
  3. Wrapping additional BiDi commands in the client

Usage Examples

Complete Test Scenario

import { browser, TimeoutError } from '@vibium/client';
import { writeFile } from 'fs/promises';

async function loginTest() {
  const vibe = await browser.launch({ headless: true });

  try {
    // Navigate to login page
    await vibe.go('https://example.com/login');

    // Fill in credentials
    const emailInput = await vibe.find('input[name="email"]');
    await emailInput.type('user@example.com');

    const passwordInput = await vibe.find('input[name="password"]');
    await passwordInput.type('secretPassword123');

    // Submit form
    const submitButton = await vibe.find('button[type="submit"]');
    await submitButton.click();

    // Wait for redirect and verify success
    try {
      const welcomeMessage = await vibe.find('.welcome-message', { timeout: 5000 });
      const text = await welcomeMessage.text();
      console.log('Login successful:', text);
    } catch (err) {
      if (err instanceof TimeoutError) {
        console.error('Login failed - welcome message not found');
        const screenshot = await vibe.screenshot();
        await writeFile('login-failure.png', screenshot);
      }
      throw err;
    }

  } finally {
    await vibe.quit();
  }
}

loginTest().catch(console.error);

Version History

1.0.0 (2026-01-19)

  • Initial API documentation from source analysis
  • Core browser automation features
  • BiDi protocol integration
  • Auto-wait behavior
  • Custom error types

References

  • Source Repository: /tmp/vibium/clients/javascript/
  • BiDi Protocol: WebDriver BiDi Specification
  • Main Entry: /tmp/vibium/clients/javascript/src/index.ts
  • Core Classes:
    • browser (browser.ts) - Browser launch API
    • Vibe (vibe.ts) - Browser controller
    • Element (element.ts) - Element interaction
    • BiDiClient (bidi/client.ts) - Protocol client

Document Status: ✅ Complete and verified against source code Analysis Date: 2026-01-19 Analyst: Research Agent (Agentic QE v3)