test(cua): keep hover regressions independent of keyboard focus

This commit is contained in:
creatixchu committed 2026-10-13 11:11:58 +08:00
1 parent 9781ac0fda
commit 02bbff61b6
3 files changed
+25 -22

No files matched your search

@@ -91,7 +91,6 @@ def main():
parser.add_argument("--helper", type=Path, help="Existing native helper executable for this run")
parser.add_argument("--artifacts", type=Path, help="Save fixture screenshots and private JSON protocol transcript")
parser.add_argument("--panels-only", action="store_true", help="Exercise system file panels without unrelated pointer gestures")
parser.add_argument("--pointer-only", action="store_true", help="Exercise window input without the system file-panel scenario")
parser.add_argument("--hover-only", action="store_true", help="Exercise native hover without keyboard or file-panel scenarios")
args = parser.parse_args()
artifacts = args.artifacts
@@ -193,7 +192,7 @@ def main():
call("click", hover_target)
wait_state(lambda actual: actual["hoverClicks"] == 1, "Native hover control did not activate")
call("hover", {**owner, "windowId": primary["windowId"], "x": 10, "y": 40, "useSystemPointer": True})
state = wait_state(lambda actual: not actual["hoverVisible"], "AppKit tracking area did not exit")
wait_state(lambda actual: not actual["hoverVisible"], "AppKit tracking area did not exit")
if args.hover_only:
check_hover()
@@ -257,15 +256,14 @@ def main():
call("screenshot", {**owner, "windowId": secondary["windowId"]}, "WINDOW_UNAVAILABLE")
call("click", button_target(second, "Increment secondary"), "STALE_SNAPSHOT")
if not args.pointer_only:
check_file_panel(call, owner, primary_params, primary, directory, wait_state, button_target)
check_file_panel(call, owner, primary_params, primary, directory, wait_state, button_target)
state = json.loads(state_path.read_text())
assert {key: state[key] for key in expected_pointer_counts} == expected_pointer_counts, state
stop(fixture)
call("listWindows", owner, "APP_UNAVAILABLE")
call("listWindows", owner, "STALE_APP")
helper.eof()
print("PASS: capability preflight, two-window snapshots, scoped invalidation, AX readback, Unicode input, single scroll/right-click/drag dispatch, image dimensions, native hover, stale app cleanup, EOF" + (" (file panel omitted)" if args.pointer_only else ", system file panel"))
print("PASS: capability preflight, two-window snapshots, scoped invalidation, AX readback, Unicode input, single scroll/right-click/drag dispatch, image dimensions, native hover, stale app cleanup, EOF", system file panel")
finally:
def save_artifacts():
if artifacts is not None:
@@ -21,6 +21,7 @@ def main():
parser.add_argument("--helper", type=Path, help="Existing native helper executable for this run")
parser.add_argument('--electron', type=Path, help='Existing Electron executable; no download is attempted')
parser.add_argument('--artifacts', type=Path, help='Save fixture-only PNG and private protocol transcript')
parser.add_argument("--hover-only", action="store_true", help="Exercise hover independently of text input")
args = parser.parse_args()
repo = ROOT.parents[3]
executable = args.electron or repo / 'apps/desktop/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron'
@@ -95,23 +96,24 @@ def main():
raise AssertionError(description + ': ' + json.dumps(actual))
time.sleep(0.025)
call('click', {**owner, 'snapshotId': state['snapshotId'], 'elementIndex': buttons[0]['index']})
wait_state(lambda actual: actual['clicks'] == 1 and actual['lastClickTrusted'] is True,
'Native AX press did not produce one trusted DOM click')
state = observe()
assert 'Clicks: 1' in state['tree'], state['tree']
_, fields = targets(state)
field = fields[0]['bounds']
window = next(window for window in state['windows'] if window['windowId'] == window_id)
call('click', {**owner, 'windowId': window_id, 'snapshotId': state['snapshotId'],
'x': field[0] + field[2] / 2 - window['bounds']['x'],
'y': field[1] + field[3] / 2 - window['bounds']['y']})
call('typeText', {**owner, 'text': 'Electron 中文 🧪'})
wait_state(lambda actual: actual['text'] == 'Electron 中文 🧪' and actual['lastInputTrusted'] is True,
'Chromium did not receive complete trusted Unicode input')
call('pressKey', {**owner, 'key': 'a', 'modifiers': ['command']})
call('typeText', {**owner, 'text': '第二次 Electron'})
wait_state(lambda actual: actual['text'] == '第二次 Electron', 'Chromium Command+A did not replace the field')
if not args.hover_only:
call('click', {**owner, 'snapshotId': state['snapshotId'], 'elementIndex': buttons[0]['index']})
wait_state(lambda actual: actual['clicks'] == 1 and actual['lastClickTrusted'] is True,
'Native AX press did not produce one trusted DOM click')
state = observe()
assert 'Clicks: 1' in state['tree'], state['tree']
_, fields = targets(state)
field = fields[0]['bounds']
call('click', {**owner, 'windowId': window_id, 'snapshotId': state['snapshotId'],
'x': field[0] + field[2] / 2 - window['bounds']['x'],
'y': field[1] + field[3] / 2 - window['bounds']['y']})
call('typeText', {**owner, 'text': 'Electron 中文 🧪'})
wait_state(lambda actual: actual['text'] == 'Electron 中文 🧪' and actual['lastInputTrusted'] is True,
'Chromium did not receive complete trusted Unicode input')
call('pressKey', {**owner, 'key': 'a', 'modifiers': ['command']})
call('typeText', {**owner, 'text': '第二次 Electron'})
wait_state(lambda actual: actual['text'] == '第二次 Electron', 'Chromium Command+A did not replace the field')
image = call('screenshot', {**owner, 'windowId': window_id})
png = base64.b64decode(image['data'], validate=True)
assert png.startswith(b'\x89PNG\r\n\x1a\n')
@@ -151,7 +153,8 @@ def main():
call('hover', {**owner, 'windowId': window_id, 'x': 10, 'y': 40})
wait_state(lambda actual: not actual['popupOpen'], 'Leaving the hover region did not close it')
helper.eof()
print(f"PASS: Electron {runtime['electron']} native AX readiness, trusted DOM click, Unicode input, Command+A replacement, PNG capture, delayed hover across observations, popup entry/click/exit")
print(f"PASS: Electron {runtime['electron']} delayed hover across observations, popup entry/click/exit, invalid targets"
+ (" (text input omitted)" if args.hover_only else ", trusted DOM click, Unicode input, Command+A replacement, PNG capture"))
except Exception:
diagnostics.flush()
diagnostics.seek(0)
@@ -316,6 +316,7 @@
"maximum": 9007199254740991
},
"useSystemPointer": {
"description": "Move the visible system cursor for native hover tracking; requires the target to be unobscured in the foreground app. Default false sends window-directed movement.",
"type": "boolean"
}
},
@@ -351,6 +352,7 @@
"minimum": 0
},
"useSystemPointer": {
"description": "Move the visible system cursor for native hover tracking; requires the target to be unobscured in the foreground app. Default false sends window-directed movement.",
"type": "boolean"
}
},