|
23 | 23 | from pydoll.elements.mixins import FindElementsMixin |
24 | 24 | from pydoll.elements.shadow_root import ShadowRoot |
25 | 25 | from pydoll.exceptions import ( |
| 26 | + CommandExecutionTimeout, |
26 | 27 | ElementNotAFileInput, |
27 | 28 | ElementNotFound, |
28 | 29 | ElementNotInteractable, |
|
32 | 33 | MissingScreenshotPath, |
33 | 34 | ShadowRootNotFound, |
34 | 35 | WaitElementTimeout, |
| 36 | + WebSocketConnectionClosed, |
35 | 37 | ) |
36 | 38 | from pydoll.interactions.iframe import IFrameContext, IFrameContextResolver |
37 | 39 | from pydoll.interactions.keyboard import Keyboard |
@@ -238,7 +240,10 @@ async def iframe_context(self) -> Optional[IFrameContext]: |
238 | 240 | return None |
239 | 241 |
|
240 | 242 | resolver = self._get_iframe_resolver() |
| 243 | + old_context = self._iframe_context |
241 | 244 | self._iframe_context = await resolver.resolve() |
| 245 | + if old_context is not None and old_context is not self._iframe_context: |
| 246 | + await old_context.close() |
242 | 247 | self._apply_routing_from_context() |
243 | 248 | return self._iframe_context |
244 | 249 |
|
@@ -299,14 +304,14 @@ async def get_shadow_root(self, timeout: float = 0) -> ShadowRoot: |
299 | 304 | if not timeout: |
300 | 305 | return await self._get_shadow_root() |
301 | 306 |
|
302 | | - start_time = asyncio.get_event_loop().time() |
| 307 | + start_time = asyncio.get_running_loop().time() |
303 | 308 | while True: |
304 | 309 | try: |
305 | 310 | return await self._get_shadow_root() |
306 | 311 | except ShadowRootNotFound: |
307 | 312 | pass |
308 | 313 |
|
309 | | - if asyncio.get_event_loop().time() - start_time > timeout: |
| 314 | + if asyncio.get_running_loop().time() - start_time > timeout: |
310 | 315 | raise WaitElementTimeout( |
311 | 316 | f'Timed out after {timeout}s waiting for shadow root on element' |
312 | 317 | ) |
@@ -512,7 +517,7 @@ async def wait_until( |
512 | 517 | f'Waiting for element: visible={is_visible}, ' |
513 | 518 | f'interactable={is_interactable}, timeout={timeout}s' |
514 | 519 | ) |
515 | | - loop = asyncio.get_event_loop() |
| 520 | + loop = asyncio.get_running_loop() |
516 | 521 | start_time = loop.time() |
517 | 522 | while True: |
518 | 523 | results = await asyncio.gather(*(check() for check in checks)) |
@@ -683,9 +688,22 @@ async def insert_text(self, text: str): |
683 | 688 | # Keep cached attributes coherent for common cases (e.g., input value) |
684 | 689 | # This avoids forcing a DOM round-trip for simple assertions. |
685 | 690 | if self._attributes.get('tag_name', '').lower() in {'input', 'textarea'}: |
686 | | - # When inserting into an empty field, resulting value equals inserted text. |
687 | | - # For complex cases (non-empty with caret), tests usually check non-empty. |
688 | | - self._attributes['value'] = text |
| 691 | + # Re-read the actual DOM value to keep cache consistent. |
| 692 | + # insertText appends at cursor, so the result may differ from |
| 693 | + # the inserted text when the field already had content. |
| 694 | + try: |
| 695 | + live_result = await self.execute_script('return this.value', return_by_value=True) |
| 696 | + self._attributes['value'] = ( |
| 697 | + live_result.get('result', {}).get('result', {}).get('value', text) |
| 698 | + ) |
| 699 | + except ( |
| 700 | + KeyError, |
| 701 | + TypeError, |
| 702 | + AttributeError, |
| 703 | + CommandExecutionTimeout, |
| 704 | + WebSocketConnectionClosed, |
| 705 | + ): |
| 706 | + self._attributes['value'] = text |
689 | 707 |
|
690 | 708 | async def set_input_files(self, files: str | Path | list[str | Path]): |
691 | 709 | """ |
|
0 commit comments