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.

Most Firefox failures with browser.keys() are caused by sending the right key to the wrong target. First use WebdriverIO’s current Key constants, then verify that the intended element is focused and keyboard-interactable. Only after those checks should you investigate Firefox, geckodriver, and WebdriverIO versions.

This sequence covers special keys, modifier combinations, text entry, focus and frame problems, overlays, driver configuration, and the legacy moz:webdriverClick capability.

1. Use the current WebdriverIO key API

Import Key from webdriverio instead of relying on hand-written Unicode escape sequences or browser-specific mappings. The WebdriverIO API reference documents browser-level key input such as Enter, Control combinations, and arrow navigation. See the current Key and browser.keys documentation.

import { Key } from 'webdriverio'

await browser.keys(Key.Enter)
await browser.keys([Key.Ctrl, 'a'])
await browser.keys([Key.ArrowDown, Key.Enter])

Key.Ctrl is cross-platform: WebdriverIO maps it to Command on macOS and Control on Windows and Linux. The browser-level command sends input to whichever element currently has focus. If focus is on the document body, a hidden control, or a different window, the call can appear to do nothing even though the command was delivered.

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

Choose the right input form

Need Use Reason
Press Enter, Escape, Tab, an arrow, or another special key browser.keys(Key.Enter) Acts on the currently focused element.
Send a modifier chord browser.keys([Key.Ctrl, 'a']) Sends the modifier and printable key as one sequence.
Replace a known input’s contents await input.setValue('text') Targets that element and replaces its value.
Append text to a known input await input.addValue('text') Targets that element and appends to its existing value.

WebdriverIO documents setValue() and addValue() as higher-level element methods. They are usually more reliable than trying to focus an input indirectly with a browser-wide key command.

2. Send text to a specific element

If the problem is text entry rather than keyboard navigation, locate the control and use an element method. This avoids ambiguity about focus.

const search = await $('#search')
await search.waitForDisplayed()
await search.click()
await search.setValue('Firefox WebDriver')
await browser.keys(Key.Enter)

Use setValue() when existing content must be replaced. Use addValue() when the existing value should remain. Use browser.keys() for actions that intentionally belong to the focused control, such as submitting with Enter or moving through a menu with arrow keys. The related WebDriver commands are described in WebdriverIO’s elementSendKeys documentation.

3. Check Firefox focus and interactability

Firefox’s geckodriver checks whether an element is focusable when it receives keys. A failure can therefore reflect page state, not a broken key mapping. Before changing capabilities, check each item below.

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.
  • Correct window: switch to the window or tab that contains the control.
  • Correct frame: switch into the iframe before locating or typing into an element inside it.
  • Visible: the element is displayed and not positioned off-screen.
  • Enabled: the control is not disabled or read-only when the operation requires editing.
  • Focused: click the intended element, or explicitly focus it with a script when appropriate.
  • Uncovered: a modal, cookie banner, loading layer, or other overlay is not intercepting input.
  • Editable: the selector resolves to an input, textarea, contenteditable region, or another keyboard target rather than a wrapper element.

Make focus observable

const field = await $('input[name="email"]')
await field.waitForDisplayed()
await field.waitForEnabled()
await field.click()

const activeTag = await browser.execute(() => document.activeElement?.tagName)
console.log('active element:', activeTag)

await browser.keys('person@example.com')

If the logged active element is not the expected control, fix the selector, frame, window, or overlay before testing another key constant.

Handle iframes and windows

const frame = await $('iframe[data-testid="checkout"]')
await frame.waitForDisplayed()
await browser.switchToFrame(frame)

const card = await $('input[name="cardnumber"]')
await card.click()
await card.setValue('4242424242424242')

await browser.switchToParentFrame()

For a new tab or popup, obtain its window handle and switch to it before locating the target. A correct selector in the wrong browsing context is still the wrong target.

4. Diagnose the exact failure

Capture the complete exception, the command that failed, and the target state. “Keys do not work” can mean different problems.

Symptom Likely cause Next action
element not interactable The target cannot receive keyboard input. Verify visibility, enabled state, focus, overlays, and element type.
No exception, no visible change Focus is elsewhere or the key has no effect in the current state. Click the control, inspect document.activeElement, and test a visible text field.
Only special keys fail Incorrect constants or an invalid key sequence. Use Key.Enter, Key.Ctrl, and other documented constants.
Text goes to the wrong field Browser-level input is using stale or unexpected focus. Use the element’s setValue() or addValue().
Failure occurs only in one test page Page JavaScript, an overlay, or a custom widget intercepts input. Reproduce with a native input and inspect the page state.

5. Verify Firefox, geckodriver, and WebdriverIO versions

Do not assume Firefox itself is defective until the target and focus checks pass. geckodriver is a separate WebDriver-facing proxy between WebdriverIO and Firefox, and Firefox and geckodriver use different version schemes. Record all three versions in the failing run:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npx wdio --version
firefox --version
geckodriver --version

WebdriverIO supports pinning the geckodriver binary separately with wdio:geckodriverOptions.geckoDriverVersion. Use a compatible browser/driver combination, rerun the smallest reproducible test, and keep the recorded versions with any bug report. The driver relationship and binary configuration are covered in Mozilla’s geckodriver overview and WebdriverIO’s driver-binaries guide.

Example capability configuration

export const config = {
  capabilities: [{
    browserName: 'firefox',
    'wdio:geckodriverOptions': {
      geckoDriverVersion: 'YOUR_COMPATIBLE_VERSION'
    }
  }]
}

Replace the version with one appropriate for your Firefox installation; the documentation does not establish one universal pairing for every environment.

6. Treat moz:webdriverClick as a diagnostic only

Mozilla documents moz:webdriverClick as changing interactability checks for clicks and sending keys. Setting it to false temporarily disables conformant checks, but Mozilla also describes the capability as temporary and subject to removal after stabilization. It is therefore legacy and version-sensitive guidance, not a routine fix.

capabilities: [{
  browserName: 'firefox',
  'moz:webdriverClick': false
}]

Use this only to determine whether strict interactability checking explains a reproducible case. Prefer fixing focusability, overlays, frames, and selectors. If a native, visible, enabled control still fails with a current compatible setup, reduce the test and report the exact error and versions rather than leaving this capability enabled permanently. See Mozilla’s Firefox capabilities reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

7. A minimal Firefox regression test

This test separates key mapping from page-specific behavior by using a native input and an explicit focus step.

import { Key } from 'webdriverio'

describe('Firefox keyboard input', () => {
  it('types, selects, and submits', async () => {
    await browser.url('https://example.test/form')

    const input = await $('#query')
    await input.waitForDisplayed()
    await input.waitForEnabled()
    await input.click()
    await input.setValue('first value')

    await browser.keys([Key.Ctrl, 'a'])
    await browser.keys('second value')
    await browser.keys(Key.Enter)

    await expect($('#results')).toBeDisplayed()
  })
})

If this succeeds while the original test fails, compare the original page’s focus, iframe, overlay, custom widget, and event handlers. If it fails too, preserve the minimal test and version output for driver investigation.

8. Performance and reliability practices

  • Wait for the specific control rather than adding arbitrary long sleeps.
  • Use one explicit focus action before a browser-level key sequence.
  • Prefer element methods for deterministic text replacement.
  • Keep keyboard sequences short; split navigation and text entry when a page changes focus.
  • After an action that opens a dialog or tab, wait for that context before sending keys.
  • Log the selector, window/frame state, active element, and software versions when a failure occurs.
  • Run a small Firefox-only reproduction before changing global capabilities.

Or skip the browser setup

If your goal is to obtain a clean screenshot rather than drive interactive keyboard behavior, ScreenshotNeo makes one HTTP request and returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo documentation for authentication and options.

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

cURL

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

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
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}`);

Every plan includes the full feature set, including full-page and lazy-image capture, CSS-selector element shots, dark mode, device presets, retina scale, PDF controls, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user-agent, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, async webhooks, bulk capture for up to 100 URLs per call, usage data, and an OpenAPI specification. The Free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

9. Final diagnostic order

  1. Use documented Key constants.
  2. Decide whether the action belongs to the focused element or a specific input.
  3. Verify window, frame, visibility, enabled state, focus, and overlays.
  4. Capture the exact exception and active-element information.
  5. Record WebdriverIO, Firefox, and geckodriver versions.
  6. Pin a compatible geckodriver and retest a minimal case.
  7. Use moz:webdriverClick: false only as a temporary diagnostic.
  8. Report a reproducible driver issue if a valid, focusable target still fails.

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.