Repository navigation
fix(executor): humanized click reads borders without Playwright's private _expect - #141
Merged
Merged
Conversation
…vate _expect
Every human-input click raised KeyError: 'received' on Playwright >= 1.63.
_border_width read computed CSS through Locator._impl_obj._expect('to.have.css')
and took result['received'], but from 1.63 a passing expectation returns only
{"matches": ...}; 'received' is filled in on the failure path. The '.*' regex
always passes, so every normally sized click failed.
The left/top border widths now come from the _browserwright_scroll selector
engine that already plans wheel scrolling. It runs in Playwright's utility
world and answers through a detached transport node, so the read is still
never visible to the page's main world, and it no longer depends on a private
result shape. Both widths come from one read, parsed with parseInt like
Playwright's own padding-relative offset.
uv.lock moves playwright 1.60.0 -> 1.64.0 so the dev env and CI resolve the
same Playwright a fresh install does (pyproject keeps >=1.60.0). The existing
tests/daemon/e2e/test_human_input.py fails with this KeyError on 1.63 against
the old code, so the lock bump makes it the regression guard.
Fixes #140
Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #140
Symptom
With Playwright >= 1.63, every humanized
locator.click()on a normally sized visible element raisesKeyError: 'received'inrepl/human_input.py_border_width. Humanizedfill()clicks first, so it fails too. A plain<button>is enough to trigger it.Root cause
_border_widthread computed CSS through Playwright's privateLocator._impl_obj._expect('to.have.css', {regex '.*'})and tookresult['received']. In Playwright 1.63,Frame._expectreturns only{"matches": ...}when the expectation passes.receivedis filled in only on the failure path, and the.*regex always passes. CI and the dev env never hit this becauseuv.lockpinned 1.60.0, whilepyprojectsays>=1.60.0, so a fresh install resolves to a newer version.Fix
human_input.py: the left/top border widths now come from the existing_browserwright_scrollselector engine (acontentScriptengine, so it runs in Playwright's utility world and answers through a detached transport node). The no-main-world-JS guarantee stays intact, and nothing depends on a private result shape anymore._border_width(locator, prop)(two_expectround trips) is replaced by_borders(locator), which gets both widths from one read. They are parsed withparseIntto match Playwright's own padding-relative offset, as before.uv.lock: playwright 1.60.0 -> 1.64.0, so the dev env and CI resolve the same Playwright a fresh install does.pyprojectis unchanged. No new test: the existingtests/daemon/e2e/test_human_input.pyalready fails with this exactKeyErroron 1.63 against the old code (shown below), so the lock bump turns it into the regression guard.I did not add a fallback to native click on a failed read. The read now goes through the same transport that
_wheel_into_viewalready depends on a moment earlier in the same click, so a fallback would only hide real faults.Evidence
Issue repro (the exact two scripts from #140, 3 rounds each,
--backend=cdp --create, run through this checkout on an isolated daemon: own facade/relay/CDP ports,XDG_RUNTIME_DIR,BS_HOME,BD_CONFIG="",uv run --with playwright==<v>):main (4b54676), playwright 1.63.0:
this branch:
It also passed on 1.60.0, run before the lock bump.
Real-Chrome E2E, human input (
tests/daemon/e2e/run.sh tests/daemon/e2e/test_human_input.py tests/daemon/e2e/test_human_input_no_focus.py -v):test_human_input.py, playwright 1.63.0KeyError: 'received'(viafill->click->_border_width)2 passed in 69.56s2 passed2 passedtest_human_input.pycovers bordered/positioned targets and asserts the real event coordinates, including the border offset, so the new border read is checked against the page's owngetComputedStyle.Note:
test_human_input_no_focus.pyis headful, and on a busy desktop it sometimes fails because another macOS app takes focus. Its focus log then showswindows.onFocusChanged [-1](WINDOW_ID_NONE), never the session window. This happened on both main and this branch, and the test passes on rerun.Fast gate (
mise run test, on the 1.64.0 lock):mise run lint:All checks passed!Not run: the full real-Chrome E2E suite (only the human-input files), and the extension backend against a daily Chrome (the human-input E2E uses the extension backend in Chrome for Testing).
🤖 Generated with Claude Code