Skip to content

fix: react input issues#243

Merged
nathanfallet merged 2 commits intomainfrom
fix/react-input-issues
Mar 2, 2026
Merged

fix: react input issues#243
nathanfallet merged 2 commits intomainfrom
fix/react-input-issues

Conversation

@nathanfallet
Copy link
Member

@nathanfallet nathanfallet commented Mar 2, 2026

Description

Port of cdpdriver/kdriver#61 and cdpdriver/kdriver#62 to zendriver.

Problem

When automating React controlled inputs, clear_input() and clear_input_by_deleting() silently fail to notify React, leaving the component state stale. The real-world consequence is a mixed value when re-filling a pre-filled input — e.g. old value "10", new value "25", actual result "025" or "1025".

Root cause

React installs an instance-level value property setter on controlled inputs (its _valueTracker mechanism). Direct n.value = x assignments in JavaScript go through this setter, updating both the DOM value and the tracker's "last known value" simultaneously. When an input event then fires, React compares el.value === tracker.getValue() — they match — so React concludes nothing changed and never calls onChange.

Both methods were affected:

  • clear_input() used element.value = "" with no event dispatch at all — a double failure
  • clear_input_by_deleting() used n.value = n.value.slice(1) on each iteration, which also went through the tracker setter; the subsequent input event was silently ignored by React

A secondary bug in clear_input_by_deleting: the loop used Delete (keyCode 46) with the cursor at position 0. On some VM environments, VK_DELETE is treated as backward-delete, making it a no-op at position 0 and causing an infinite loop.

Fix

  • clear_input() — replaced the direct .value assignment with a call to the native prototype setter (Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set.call(el, '')), which bypasses the tracker entirely. Added an InputEvent dispatch so React detects the mismatch and fires onChange.

  • clear_input_by_deleting() — switched from Delete at position 0 to Backspace (keyCode 8) with the cursor explicitly kept at the end of the remaining text, which is unambiguous on every platform. Replaced the direct n.value = n.value.slice(1) mutation with a native prototype setter call, and added an InputEvent dispatch after each deletion so React correctly fires onChange on every character removed.

Tests

Added tests/core/test_react_controlled_input.py with an accompanying HTML fixture (tests/sample_data/react-controlled-input-test.html) that faithfully reproduces the _valueTracker mechanism in vanilla JS — same instance-level setter, same mismatch comparison, same state tracking. Three tests, each run in both headless and headful mode:

Test What it proves
test_clear_input_does_not_notify_react Before fix: clear_input() leaves React state at "10" instead of ""
test_clear_input_by_deleting_does_not_notify_react Before fix: clear_input_by_deleting() leaves React state at a partial value instead of ""
test_fill_react_controlled_input_produces_mixed_value Before fix: clear + re-render + type produces "025" or "1025" instead of "25"

Pre-merge Checklist

  • I have described my change in the section above.
  • I have ran the ./scripts/format.sh and ./scripts/lint.sh scripts. My code is properly formatted and has no linting errors.
  • I have ran uv run pytest and ensured all tests pass.
  • I have added my change to CHANGELOG.md under the [Unreleased] section.

@nathanfallet nathanfallet requested a review from a team as a code owner March 2, 2026 04:49
@codecov
Copy link

codecov bot commented Mar 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@nathanfallet nathanfallet merged commit b506c8f into main Mar 2, 2026
5 checks passed
@nathanfallet nathanfallet deleted the fix/react-input-issues branch March 2, 2026 05:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant