ScreenshotNeo

BlogHow-to

Simulate a Preconnected Bluetooth Peripheral with Puppeteer

Use Puppeteer’s experimental Bluetooth emulation to simulate a preconnected peripheral, test Web Bluetooth behavior, and isolate tests reliably.

By the ScreenshotNeo team4 October 20267 min read

Use Puppeteer’s experimental page.bluetooth.simulatePreconnectedPeripheral() API to make a virtual Bluetooth device appear already connected to the system. First emulate a powered-on adapter, then describe the peripheral with its address, name, manufacturer data, and known service UUIDs. This tests Web Bluetooth behavior without requiring a physical peripheral.

The API is experimental, so check the API reference that matches your installed Puppeteer version and pinned Chromium build. The current references returned different version labels for the Bluetooth emulation and peripheral interfaces; do not assume a minimum version for this method based on those labels alone. See the Puppeteer BluetoothEmulation reference and PreconnectedPeripheral interface.

Runnable example

This complete Node.js script launches Chromium, opens a page, enables a powered-on emulated adapter, adds a preconnected peripheral, and evaluates Web Bluetooth API access in the page.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();

    // Use an isolated browser context when Bluetooth state must not be shared
    // with other test pages.
    await page.goto('https://example.com');

    await page.bluetooth.emulateAdapter('powered-on');
    await page.bluetooth.simulatePreconnectedPeripheral({
      address: '09:09:09:09:09:09',
      name: 'SOME_NAME',
      manufacturerData: [{
        key: 17,
        data: 'AP8BAX8=',
      }],
      knownServiceUuids: ['12345678-1234-5678-9abc-def123456789'],
    });

    const bluetoothAvailable = await page.evaluate(() => {
      return 'bluetooth' in navigator;
    });
    console.log({ bluetoothAvailable });
  } finally {
    await browser.close();
  }
})();

Install Puppeteer in the project before running the script. Use the Chromium binary associated with the installed Puppeteer package, and keep the package and browser versions pinned in CI so the experimental emulation behavior is reproducible. The example registers the peripheral after navigating; if your app configures Bluetooth immediately during startup, install the emulation before triggering that application flow.

What the peripheral fields mean

simulatePreconnectedPeripheral() accepts a PreconnectedPeripheral object. The documented fields are:

Field Purpose Example and notes
address Address identifying the simulated peripheral. 09:09:09:09:09:09
name Name associated with the peripheral. SOME_NAME
manufacturerData Manufacturer-specific data entries. Each entry has a numeric key and base64-encoded data, such as { key: 17, data: 'AP8BAX8=' }.
knownServiceUuids Service UUIDs known for the peripheral. Use UUIDs matching the services your application expects.

Use realistic values from the application’s expected device profile. A peripheral declaration establishes its identity and known services; it does not, by itself, fully define service characteristics, descriptors, or every GATT operation response.

Test the application against the simulated device

  1. Start with a controlled page. Navigate to the page that exercises Web Bluetooth, or serve a small fixture page that calls the same application logic.
  2. Enable the adapter. Call page.bluetooth.emulateAdapter('powered-on') before the app requests or inspects Bluetooth devices.
  3. Add the preconnected peripheral. Call simulatePreconnectedPeripheral() with the device profile your test needs.
  4. Run the app interaction. Trigger the application path and assert its visible state or resulting behavior. The setup alone does not assert that the page connected to or used the device correctly.
  5. Clean up and isolate state. Close the page or browser context after the test. Use separate contexts for tests that need independent Bluetooth state.

The browser page needs to be allowed to use Web Bluetooth under the browser’s normal security and permission rules. For API support and usage constraints, see Chrome’s guide to communicating with Bluetooth devices over JavaScript.

Preconnected simulation versus chooser automation

Choose the method based on what the test needs to prove:

Test goal Use Tradeoff
Test app behavior with a virtual device that is already connected to the system simulatePreconnectedPeripheral() Does not require physical hardware; supports a virtual peripheral profile.
Test the browser chooser flow that follows navigator.bluetooth.requestDevice() waitForDevicePrompt(), then select a matching device The documented guide uses a real compatible Bluetooth device and Puppeteer v21.4.0 or later.
Test services, GATT operations, advertisements, or disconnection failures Bluetooth DevTools Protocol emulation methods More control, but verify the available methods against the Chromium protocol version in use.

For chooser automation, register waitForDevicePrompt() before clicking the page control that invokes navigator.bluetooth.requestDevice(). Then wait for the appropriate device and call select(). The order matters because the prompt is triggered by the user action. Follow the official Chrome for Developers Puppeteer Web Bluetooth guide for that separate hardware-backed workflow.

Testing GATT operations and failure paths

The Chrome DevTools Protocol Bluetooth emulation domain includes methods for adding and removing services, characteristics, and descriptors; simulating advertisements and disconnections; and controlling responses to GATT and characteristic or descriptor operations. This makes it possible to test beyond device presence, including app behavior when a device disconnects or an operation fails.

Those protocol capabilities are broader than declaring a preconnected peripheral. Consult the BluetoothEmulation protocol reference for the methods supported by the Chromium version under test. Protocol support can vary with the browser version, so avoid building tests around a method without checking that version’s protocol surface.

Isolation and reliability

The Web Bluetooth specification calls for emulated adapters to be isolated per top-level navigable, but Chromium’s current emulation implementation is tied to browser context. Pages sharing a context can therefore interfere with one another’s Bluetooth emulation state.

  • Give tests that require independent adapter or peripheral state separate browser contexts.
  • Avoid running tests that mutate Bluetooth emulation state concurrently inside one shared context.
  • Pin Puppeteer and Chromium versions, and validate the workflow after upgrades because the API is experimental.
  • Keep setup and app interactions in a clear order: adapter, peripheral, then application action.
  • Use a real-device test when the requirement includes actual radio behavior or physical hardware integration.

Troubleshooting

Symptom Likely cause Fix
page.bluetooth is missing or the method is undefined The installed Puppeteer version or its matching Chromium does not expose the documented experimental API. Check the installed-version API reference and use its matching Puppeteer/Chromium pair. Do not rely on a version label from a different API page.
The app reports Bluetooth unavailable The emulated adapter was not enabled, or the page is running in an unsupported or insecure context. Enable the adapter with emulateAdapter('powered-on') before app interaction, and verify the page’s Web Bluetooth support and security context.
The page cannot find the expected device or service The app’s expected name, address, or service UUID does not match the simulated profile. Compare the test fixture with the application’s filters and service UUIDs; use the exact UUID format expected by the API.
Manufacturer data does not match expectations The manufacturer entry uses an incorrect numeric key or malformed base64 data. Check the manufacturer identifier and encode the intended bytes as base64. Keep the structure as an array of { key, data } entries.
Tests pass alone but fail when run together Pages in the same browser context can share or interfere with Bluetooth emulation state. Run those tests in isolated browser contexts or serialize the stateful Bluetooth tests.
The chooser never appears in a chooser automation test The prompt listener was registered after the click, or the test is using preconnected simulation while expecting chooser behavior. Register waitForDevicePrompt() before clicking, and use the real-device chooser workflow when that interaction is the subject of the test.
A GATT test cannot exercise a desired error or disconnection Peripheral declaration alone does not configure all services or operation outcomes. Use the relevant Bluetooth DevTools Protocol methods supported by the pinned Chromium version.

Performance, reliability, and cost

Virtual peripheral simulation avoids the setup and variability of a physical device for tests focused on application logic. It does not establish real-world Bluetooth radio performance, interoperability, or hardware reliability. Keep hardware-in-the-loop coverage for behaviors that depend on actual peripherals.

For repeatable CI runs, pin browser dependencies, isolate state, use deterministic device profiles, and ensure each test resets or discards the context it mutates. The cited documentation does not provide a performance benchmark for this API, so measure your own suite if runtime matters. Costs depend on your CI/browser execution environment; no physical device is required for the simulated-peripheral path.

Or skip the browser setup

If the task is capturing a website for a report or visual reference rather than testing its Bluetooth interactions, ScreenshotNeo provides a website screenshot API and MCP server. It cannot simulate a Bluetooth peripheral or replace this Puppeteer test; it handles website capture.

One GET request returns an image or PDF. See the ScreenshotNeo API documentation for the request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots each month with no card, and paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does a preconnected simulated peripheral require a physical Bluetooth device?

No. The preconnected-peripheral path creates a virtual device profile through emulation. The separate documented chooser workflow uses a real compatible Bluetooth device.

Does adding the peripheral test a complete Bluetooth connection?

It tests against a simulated peripheral, but the declaration alone does not define all GATT services and operation responses. Use protocol emulation for those cases and real hardware for radio-level behavior.

Why should the Puppeteer version be checked for this API?

The API is experimental, and the referenced Puppeteer API pages showed different version labels. Match the documentation to the package and Chromium build actually used by the test.