@@ -1503,11 +1503,58 @@ class CdpDriver implements AppDriver {
15031503
15041504 /// Type text character by character (more realistic than enterText).
15051505 Future <void > typeText (String text) async {
1506+ // Snapshot focused element's value before typing
1507+ final beforeResult = await _evalJs ('''
1508+ (() => {
1509+ const el = document.activeElement;
1510+ if (!el) return JSON.stringify({tag: null});
1511+ return JSON.stringify({tag: el.tagName, val: el.value || '', len: (el.value || '').length});
1512+ })()
1513+ ''' );
1514+ final beforeParsed = _parseJsonEval (beforeResult);
1515+ final beforeLen = (beforeParsed? ['len' ] as num ? )? .toInt () ?? 0 ;
1516+
1517+ // Primary: keyDown(text) + keyUp per character
15061518 for (final char in text.split ('' )) {
1519+ final code = char.codeUnitAt (0 );
1520+ final keyCode = code >= 97 && code <= 122 ? code - 32 : code;
1521+ final codeStr = code >= 65 && code <= 90 || code >= 97 && code <= 122
1522+ ? 'Key${char .toUpperCase ()}'
1523+ : '' ;
15071524 await _call ('Input.dispatchKeyEvent' , {
1508- 'type' : 'char ' ,
1525+ 'type' : 'keyDown ' ,
15091526 'text' : char,
1527+ 'key' : char,
1528+ 'unmodifiedText' : char,
1529+ if (codeStr.isNotEmpty) 'code' : codeStr,
1530+ 'windowsVirtualKeyCode' : keyCode,
1531+ 'nativeVirtualKeyCode' : keyCode,
15101532 });
1533+ await _call ('Input.dispatchKeyEvent' , {
1534+ 'type' : 'keyUp' ,
1535+ 'key' : char,
1536+ if (codeStr.isNotEmpty) 'code' : codeStr,
1537+ 'windowsVirtualKeyCode' : keyCode,
1538+ 'nativeVirtualKeyCode' : keyCode,
1539+ });
1540+ }
1541+
1542+ // Verify text was inserted; fallback to execCommand if not
1543+ final afterResult = await _evalJs ('''
1544+ (() => {
1545+ const el = document.activeElement;
1546+ if (!el) return JSON.stringify({len: 0});
1547+ return JSON.stringify({len: (el.value || el.textContent || '').length});
1548+ })()
1549+ ''' );
1550+ final afterParsed = _parseJsonEval (afterResult);
1551+ final afterLen = (afterParsed? ['len' ] as num ? )? .toInt () ?? 0 ;
1552+
1553+ if (afterLen <= beforeLen) {
1554+ // dispatchKeyEvent didn't insert text — use execCommand fallback
1555+ final escaped = text.replaceAll ('\\ ' , '\\\\ ' ).replaceAll ("'" , "\\ '" );
1556+ await _evalJs (
1557+ "document.execCommand('insertText', false, '$escaped ')" );
15111558 }
15121559 }
15131560
@@ -2425,14 +2472,44 @@ function _dqAll(sel, root) {
24252472 $deepQ
24262473 let el = _dq('$selector ');
24272474 if (el) return el;
2475+ // Visibility check helper
2476+ function _vis(e) {
2477+ const s = window.getComputedStyle(e);
2478+ if (s.display === 'none' || s.visibility === 'hidden' || s.opacity === '0') return false;
2479+ const r = e.getBoundingClientRect();
2480+ return r.width > 0 && r.height > 0;
2481+ }
2482+ // Interactive tags get priority
2483+ const interactive = new Set(['A','BUTTON','INPUT','SELECT','TEXTAREA','LABEL']);
2484+ function _score(e) {
2485+ let s = 0;
2486+ if (_vis(e)) s += 1000;
2487+ if (interactive.has(e.tagName) || e.getAttribute('role') === 'button' || e.getAttribute('role') === 'link' || e.getAttribute('role') === 'tab') s += 500;
2488+ // Prefer smallest textContent (most specific match)
2489+ s -= Math.min((e.textContent || '').length, 999);
2490+ return s;
2491+ }
24282492 const all = _dqAll('a, button, input, select, textarea, label, span, p, h1, h2, h3, h4, h5, h6, div, li, td, th, [role]');
2493+ // Exact match — pick best scored
2494+ let best = null, bestScore = -Infinity;
24292495 for (const e of all) {
2430- if (e.textContent && e.textContent.trim() === '$escaped ') return e;
2496+ const t = (e.textContent || '').trim();
2497+ if (t === '$escaped ') {
2498+ const sc = _score(e);
2499+ if (sc > bestScore) { best = e; bestScore = sc; }
2500+ }
24312501 }
2502+ if (best) return best;
2503+ // Contains match — pick best scored
2504+ best = null; bestScore = -Infinity;
24322505 for (const e of all) {
2433- if (e.textContent && e.textContent.trim().includes('$escaped ')) return e;
2506+ const t = (e.textContent || '').trim();
2507+ if (t.includes('$escaped ')) {
2508+ const sc = _score(e);
2509+ if (sc > bestScore) { best = e; bestScore = sc; }
2510+ }
24342511 }
2435- return null ;
2512+ return best ;
24362513 })()''' ;
24372514 }
24382515 if (ref != null ) {
0 commit comments