Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Bind the click handler in your Electron renderer, then let Playwright trigger it. Playwright does not register your application’s behavior. Your renderer code attaches a DOM or framework callback; the test launches Electron, obtains a Playwright Page for the renderer window, locates the button, and calls locator.click(). A minimal test is:

const { _electron: electron } = require('playwright');

const electronApp = await electron.launch({ args: ['main.js'] });
const window = await electronApp.firstWindow();

await window.getByRole('button', { name: 'Save' }).click();

await electronApp.close();

Replace Save with the button’s accessible name or use a stable test id. The official Playwright Electron documentation describes Electron automation as experimental, so check the current compatibility notes for the exact Playwright and Electron versions in your project.

Understand the two pieces: binding and driving

There are two separate jobs:

  • Binding: application code in the renderer associates a button with a function.
  • Driving: Playwright controls the already-running renderer and performs the interaction your user would perform.

Do not try to “bind” the application’s event from a Playwright test. A test click invokes a handler that the application has already registered; it does not permanently add one.

Plain DOM renderer

For a renderer that uses the DOM directly, register the listener when the page is initialized:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
LAPGEAR Home Office Pro Lap Desk - Black Carbon, Fits 15.6” Laptops
  • Spacious Design: Measuring 21.1" wide and 14.1" deep, our lap desk comfortably fits most laptops up to 15.6". Extra room for accessories ensures convenience.
  • Enhanced Functionality: Packed with handy features, including a 5x9" precision tracking mouse pad and a built-in phone slot for seamless work or video calls. Plus, enjoy ergonomic support with the integrated cushioned wrist rest.
  • Cool Comfort: Enjoy a stable surface with our lap desk's dual bolster cushion, designed for comfort and airflow, keeping your lap cool during extended use.
  • Durable Surface: Work with confidence on our lap desk's solid surface, featuring a sleek black carbon color, ensuring optimal air circulation to prevent your laptop from overheating.
  • On-the-Go Convenience: With an integrated handle and lightweight design (2.8 lbs), our lap desk is portable for travel or moving around the house, offering flexibility in any space.
const saveButton = document.querySelector('#save');

function onSave() {
  document.querySelector('#status').textContent = 'Saved';
}

saveButton.addEventListener('click', onSave);

The corresponding markup needs a real button and an accessible name:

<button id='save' type='button'>Save</button>
<p id='status'>Not saved</p>

In a UI framework, use that framework’s normal button callback or event syntax instead of adding a second, competing DOM listener. The Playwright side remains the same: find the rendered button and click it.

Install and launch an Electron test

Install Playwright

From the project directory, install Playwright as a development dependency:

npm install --save-dev playwright

Your test must be able to launch the application’s Electron entry point (the example below uses main.js). A minimal CommonJS test file, button.spec.js, is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const { _electron: electron } = require('playwright');

(async () => {
  const electronApp = await electron.launch({ args: ['main.js'] });
  try {
    const window = await electronApp.firstWindow();
    await window.getByRole('button', { name: 'Save' }).click();
    await window.getByText('Saved').waitFor();
  } finally {
    await electronApp.close();
  }
})();

firstWindow() waits for the first application window and returns a Playwright Page. The ElectronApplication API reference documents this lifecycle and the events for later windows.

Locate the button reliably

Prefer an accessible role and name

Use a role locator when the button has a stable, user-facing label:

Rank #2
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
await window.getByRole('button', { name: 'Save' }).click();

This expresses what a user sees and avoids coupling the test to layout or generated CSS classes. If the label changes intentionally, update the test with the product change.

Use a test id when the visible name is not stable

For icon-only controls or labels that vary by locale, expose a deliberate test id:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<button data-testid='save-button' aria-label='Save'>
  <span aria-hidden='true'>✓</span>
</button>
await window.getByTestId('save-button').click();

Keep the accessible name as well; it benefits keyboard and assistive-technology users even when the test uses the id.

CSS and text locators are fallbacks

A CSS locator works when the selector is an intentional contract:

await window.locator('#save').click();

A text locator can be useful for a simple static label, but role or test-id locators generally make the intended target clearer.

Trigger a normal user-like click

locator.click() is the default for an end-to-end test. Playwright performs its normal actionability checks before clicking, so the test exercises conditions such as whether the target can be interacted with rather than merely dispatching an event.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Yilador Webcam Cover 3 Pack, 0.03 inch Ultra Thin Laptop Camera Cover Slide
  • Note: Not suitable for MacBooks released after 2023 or devices with a protruding front camera; Not applicable to full-screen or notch-style tempered glass screen protectors; Do not use on the rear camera of the phone.
  • 💻 Why Do You Need a Webcam Cover Slide? — Safeguard your privacy by covering your webcam with our reliable webcam cover when not in use. Don't let anyone secretly watch you. Stay protected!
  • ✅ Thin & Stylish — Enhance your laptop's functionality and aesthetics with our 0.027" ultra-thin webcam covers. Seamlessly close your laptop while adding a touch of sophistication.
  • ✅ Fits Most Devices — Compatible with laptops, phones, tablets, desktops! Keep your privacy intact on Ap/ple, Mac/Book, iPh/one, iP/ad, H/P, L/novo, De/ll, Ac/er, As/us, Sa/msung devices.
  • ✅ 365 Days Protection — Our upgraded 3.0 adhesive ensures a strong hold that won't damage your equipment. Experience reliable, long-term privacy protection day in and day out.
const save = window.getByRole('button', { name: 'Save' });
await save.click();
await window.getByText('Saved').waitFor();

Assert the visible result or another observable side effect after the click. Avoid asserting only that the click call returned; a successful call does not prove that the application’s save operation completed.

Dispatch a DOM click only when that is the test

dispatchEvent('click') directly dispatches a DOM event and is equivalent to calling element.click(). It can dispatch even when the element is not visible. That makes it appropriate for a test specifically about an event handler, but it can bypass the user-facing conditions that locator.click() is designed to check.

const hiddenButton = window.locator('#save');
await hiddenButton.dispatchEvent('click');

Use this intentionally, for example when you are isolating whether a listener responds to a DOM event. Do not substitute it for a normal interaction test merely to get around a covered, disabled, detached, or otherwise non-actionable control.

Handle the first and subsequent Electron windows

Use the first renderer window

For the window created during startup:

const electronApp = await electron.launch({ args: ['main.js'] });
const mainWindow = await electronApp.firstWindow();
await mainWindow.getByRole('button', { name: 'Save' }).click();

Wait before clicking when a click opens a child window

Set up the event wait before the click so the new-window event cannot be missed:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const childWindowPromise = electronApp.waitForEvent('window');
await mainWindow.getByRole('button', { name: 'Open details' }).click();
const childWindow = await childWindowPromise;

await childWindow.getByRole('heading', { name: 'Details' }).waitFor();

ElectronApplication emits a window event for each newly created and loaded window. You can also inspect currently opened windows with electronApp.windows(), as documented in the ElectronApplication reference.

Keep references tied to the window they belong to

A locator belongs to its renderer Page. After a child window opens, create locators from childWindow rather than continuing to search the main page. This prevents a valid selector from being evaluated in the wrong renderer.

Rank #4
AboveTEK Portable Laptop Lap Desk w/Retractable Left/Right Mouse Pad Tray, Non-Slip Heat Shield Tablet Notebook Computer Stand Table w/Sturdy Stable Work Surface for Bed Sofa Couch or Travel
  • Anti-Slip Surface - Transform your laptop into a mobile workstation with the AboveTEK portable laptop lap desk. The anti-slip surface provides a strong grip for laptops up to 15.6 inches(Diagonal), while the double rubber strip on the bottom ensures a stable display or typing experience on your lap, couch, or bed.
  • Retractable Mouse Pad - Retractable laptop mouse pad extends on both directions for the left/right handed with elevation along the edges for stopping mouse from falling off. The size of laptop tray is 14" X 9.7" and the size of mouse pad is 7.4" X 6.1".
  • Effective Heat Shield - The effective heat shield made of sturdy and thick material protects your laptop from overheating. Prioritizes your comfort and safety, an ideal lap pad or board for working anywhere.
  • EASY to Carry and Store - With an ergonomic and simplistic design, the lap desk is portable to store in a backpack. Only 15" in size, 2.2 lb of weight and with slim 0.6 inch thickness, it is ready to be easily carried around.
  • Widely Applicable - The smooth platform accommodates laptops and tablets up to 15.6 inches(Diagonal), making it a versatile accessory and one of the best gifts for mom, dad, students and professionals. Perfect for use as a laptop bed tray or tablet holder anywhere at home, library, or park.

Complete example: renderer, Electron main process, and test

Electron entry point

This compact main.js creates a window and loads a local renderer file:

const { app, BrowserWindow } = require('electron');
const path = require('path');

function createWindow() {
  const win = new BrowserWindow({ width: 800, height: 600 });
  win.loadFile(path.join(__dirname, 'index.html'));
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

Renderer markup and binding

<!doctype html>
<html>
  <body>
    <button id='save' type='button'>Save</button>
    <output id='status'>Not saved</output>
    <script>
      const button = document.querySelector('#save');
      const status = document.querySelector('#status');
      button.addEventListener('click', () => {
        status.textContent = 'Saved';
      });
    </script>
  </body>
</html>

Test the behavior

const { _electron: electron } = require('playwright');

(async () => {
  const electronApp = await electron.launch({ args: ['main.js'] });
  try {
    const window = await electronApp.firstWindow();
    const save = window.getByRole('button', { name: 'Save' });
    await save.click();
    await window.getByText('Saved').waitFor();
  } finally {
    await electronApp.close();
  }
})();

The test action and the application binding are deliberately separate. You can replace the renderer implementation with React, Vue, another framework, or plain DOM code without changing the interaction contract if the rendered button remains discoverable.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshoot failures by symptom

Electron launch times out

Check the Electron launch prerequisites first. The Playwright Electron documentation calls out the nodeCliInspect Electron fuse, FuseV1Options.EnableNodeCliInspectArguments. If that fuse is disabled, launch under Playwright can time out. This is a startup problem, not a locator fix; verify the fuse and then retry with the exact Playwright and Electron versions used by the project.

“Button not found” or a timeout on the locator

  • Confirm that firstWindow() returned the renderer page that actually contains the control.
  • Check the rendered accessible name; whitespace, localization, and changed labels affect a role-and-name locator.
  • Wait for the application state that creates the button instead of inserting an arbitrary long delay.
  • If the control is in a child window, wait for the window event and use that returned page.
  • Use a deliberate test id only when the accessible role and name are not a stable contract.

Click is blocked by visibility or actionability

That usually means the test is exposing a real user-facing condition: an overlay, disabled state, missing layout, or a control that has not finished rendering. Fix the application state or wait for the relevant UI condition. Use dispatchEvent('click') only if bypassing those conditions is the explicit purpose of the test.

The click succeeds but nothing changes

  • Verify that the renderer registered the listener before the click.
  • Check that the locator targets the intended button rather than a duplicate.
  • Assert the expected visible state or side effect to reveal whether the handler ran.
  • For asynchronous work, wait for a UI signal that represents completion rather than ending the test immediately after the click.

The wrong window receives the action

Do not reuse a main-window locator for a newly opened page. Capture the window event before clicking, await the resulting Page, and create fresh locators from that page.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Reliability, speed, and maintenance practices

Make the UI contract explicit

Accessible role/name locators document the user action and tend to survive visual rearrangement. Test ids are useful for controls whose visible wording is intentionally unstable. Whichever contract you choose, keep it unique within the renderer.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Sale
LAPGEAR Home Office Lap Desk – Pink, Fits 15.6” Laptops
  • Spacious Design: Measuring 21.1" wide and 12" deep, our lap desk comfortably fits most laptops up to 15.6". Extra room for accessories ensures convenience.
  • Enhanced Functionality: Packed with handy features, including a 5x9" precision tracking mouse pad and a built-in phone slot for seamless work or video calls. Plus, enjoy laptop support with the integrated device ledge.
  • Cool Comfort: Enjoy a stable surface with our lap desk's dual bolster cushion, designed for comfort and airflow, keeping your lap cool during extended use.
  • Durable Surface: Work with confidence on our lap desk's solid surface, featuring a blush pink color, ensuring optimal air circulation to prevent your laptop from overheating.
  • On-the-Go Convenience: With an integrated handle and lightweight design (2.14 lbs), our lap desk is portable for travel or moving around the house, offering flexibility in any space.

Wait on state, not elapsed time

Prefer a locator wait, a visible status, or the arrival of a child window over a fixed sleep. State-based waits finish as soon as the application is ready and avoid making fast runs unnecessarily slow.

Close the application in cleanup

Always close the ElectronApplication in a finally block. It prevents an interrupted test from leaving Electron processes behind and keeps later tests independent.

Keep event handlers single-purpose

Bind one clear callback for the button, and make the resulting state observable. A small, deterministic side effect gives the test a stable assertion without coupling it to private renderer implementation details.

Account for experimental support

Playwright labels Electron automation experimental and lists version-dependent support information, including Electron 12.2.0+, 13.4.0+, and 14+. Treat those figures as documentation-era compatibility guidance, not a promise for every future release; verify the current Electron page before upgrading.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

If your goal is a screenshot or PDF rather than an interaction test, ScreenshotNeo provides a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF without launching an Electron browser in your test.

For example, using the documented API pattern (see the ScreenshotNeo documentation):

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

ScreenshotNeo accepts the cookie or consent banner before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

Node.js and Python request examples for ScreenshotNeo

If your automation is written outside the Electron test, the same endpoint can be called directly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.