|  | 
| 18 | 18 | import socket | 
| 19 | 19 | import requests | 
| 20 | 20 | import psutil | 
|  | 21 | +import base64, imghdr | 
| 21 | 22 | from pathlib import Path | 
| 22 | 23 | sys.path.append("..") | 
| 23 | 24 | from selenium import webdriver | 
| @@ -555,6 +556,7 @@ def headless(): | 
| 555 | 556 |         browser in ("chrome", "microsoft edge chromium") | 
| 556 | 557 |     ): | 
| 557 | 558 |         set_extension_variables() | 
|  | 559 | +        options.add_argument("--disable-features=DisableLoadExtensionCommandLineSwitch") | 
| 558 | 560 |         options.add_argument(f"load-extension={aiplugin_path},{ai_recorder_path}") | 
| 559 | 561 |         # This is for running extension on a http server to call a https request | 
| 560 | 562 |         options.add_argument("--allow-running-insecure-content") | 
| @@ -1262,7 +1264,63 @@ def Keystroke_For_Element(data_set): | 
| 1262 | 1264 |             } | 
| 1263 | 1265 |             for key in convert: | 
| 1264 | 1266 |                 keystroke_value = keystroke_value.replace(key, convert[key]) | 
| 1265 |  | -            if "+" in keystroke_value: | 
|  | 1267 | +                 | 
|  | 1268 | +            # Special handling for paste command (Ctrl+V / Command+V) | 
|  | 1269 | +            normalized_keystroke = keystroke_value.replace(" ", "").replace("_", "").lower() | 
|  | 1270 | +            if normalized_keystroke in ("ctrl+v", "control+v", "ctrlv", "controlv", "cmd+v", "cmdv", "command+v", "commandv"): | 
|  | 1271 | +                capabilities = selenium_driver.capabilities | 
|  | 1272 | +                platform_name = capabilities.get('platformName', '').lower() | 
|  | 1273 | +                browser_name = capabilities.get('browserName', '').lower() | 
|  | 1274 | +                 | 
|  | 1275 | +                if 'mac' in platform_name or 'os x' in platform_name: | 
|  | 1276 | +                    paste_key = Keys.COMMAND | 
|  | 1277 | +                elif platform.system().lower() in ('darwin', 'macos'): | 
|  | 1278 | +                    paste_key = Keys.COMMAND | 
|  | 1279 | +                else: | 
|  | 1280 | +                    paste_key = Keys.CONTROL | 
|  | 1281 | +                 | 
|  | 1282 | +                if browser_name == 'firefox' and ('linux' in platform_name or platform.system().lower() == 'linux'): | 
|  | 1283 | +                    paste_key = Keys.CONTROL | 
|  | 1284 | + | 
|  | 1285 | +                try: | 
|  | 1286 | +                    if get_element: | 
|  | 1287 | +                        selenium_driver.execute_script("arguments[0].focus();", Element) | 
|  | 1288 | +                        time.sleep(0.1) | 
|  | 1289 | +                         | 
|  | 1290 | +                        # Perform paste using ActionChains | 
|  | 1291 | +                        actions = ActionChains(selenium_driver) | 
|  | 1292 | +                        actions.key_down(paste_key, element=Element) | 
|  | 1293 | +                        actions.send_keys_to_element(Element, 'v') | 
|  | 1294 | +                        actions.key_up(paste_key, element=Element) | 
|  | 1295 | +                        actions.perform() | 
|  | 1296 | +                    else: | 
|  | 1297 | +                        actions = ActionChains(selenium_driver) | 
|  | 1298 | +                        actions.key_down(paste_key) | 
|  | 1299 | +                        actions.send_keys('v') | 
|  | 1300 | +                        actions.key_up(paste_key) | 
|  | 1301 | +                        actions.perform() | 
|  | 1302 | +                         | 
|  | 1303 | +                    CommonUtil.ExecLog(sModuleInfo, "Paste command executed successfully", 1) | 
|  | 1304 | +                    return "passed" | 
|  | 1305 | +                except Exception as e: | 
|  | 1306 | +                    # Fallback to JavaScript if ActionChains fails | 
|  | 1307 | +                    try: | 
|  | 1308 | +                        CommonUtil.ExecLog(sModuleInfo, f"Standard paste failed: {str(e)}. Trying JavaScript fallback...", 2) | 
|  | 1309 | +                        if get_element: | 
|  | 1310 | +                            selenium_driver.execute_script("arguments[0].focus();", Element) | 
|  | 1311 | +                            selenium_driver.execute_script("arguments[0].value = arguments[1];", Element, pyperclip.paste()) | 
|  | 1312 | +                        else: | 
|  | 1313 | +                            selenium_driver.execute_script(f"document.activeElement.value += '{pyperclip.paste()}';") | 
|  | 1314 | +                        CommonUtil.ExecLog(sModuleInfo, "Paste executed via JavaScript fallback", 1) | 
|  | 1315 | +                        return "passed" | 
|  | 1316 | +                    except Exception as js_e: | 
|  | 1317 | +                        return CommonUtil.Exception_Handler( | 
|  | 1318 | +                            sys.exc_info(), | 
|  | 1319 | +                            None, | 
|  | 1320 | +                            f"Both methods failed for paste operation: {str(js_e)}" | 
|  | 1321 | +                        ) | 
|  | 1322 | + | 
|  | 1323 | +            elif "+" in keystroke_value: | 
| 1266 | 1324 |                 hotkey_list = keystroke_value.split("+") | 
| 1267 | 1325 |                 for i in range(len(hotkey_list)): | 
| 1268 | 1326 |                     if hotkey_list[i] in list(dict(Keys.__dict__).keys())[2:-2]: | 
| @@ -3458,3 +3516,146 @@ def if_element_exists(data_set): | 
| 3458 | 3516 |         ) | 
| 3459 | 3517 |         return CommonUtil.Exception_Handler(sys.exc_info(), None, errMsg) | 
| 3460 | 3518 | 
 | 
|  | 3519 | + | 
|  | 3520 | +@logger | 
|  | 3521 | +def copy_image_into_browser(data_set): | 
|  | 3522 | +    """ | 
|  | 3523 | +    This action will copy an image from path or a variable into browser, and later you can paste via ctrl+v or cmd+v. | 
|  | 3524 | +    Supported formats: PNG, SVG | 
|  | 3525 | +
 | 
|  | 3526 | +    Example 1: | 
|  | 3527 | +    Field	                    Sub Field	            Value | 
|  | 3528 | +    image file                  input parameter 	    %| image.png |% | 
|  | 3529 | +    copy image into browser     selenium action 	    copy image into browser | 
|  | 3530 | +
 | 
|  | 3531 | +    Example 2: | 
|  | 3532 | +    Field	                    Sub Field	            Value | 
|  | 3533 | +    image variable              input parameter         %| image_var |% | 
|  | 3534 | +    copy image into browser     selenium action 	    copy image into browser | 
|  | 3535 | +    """ | 
|  | 3536 | +    sModuleInfo = inspect.currentframe().f_code.co_name + " : " + MODULE_NAME | 
|  | 3537 | +    global selenium_driver | 
|  | 3538 | +     | 
|  | 3539 | +    try: | 
|  | 3540 | +        image_data = None | 
|  | 3541 | +        image_path = "" | 
|  | 3542 | +        variable_name = "" | 
|  | 3543 | +        mime_type = "image/png" | 
|  | 3544 | +         | 
|  | 3545 | +        # Parse | 
|  | 3546 | +        for left, mid, right in data_set: | 
|  | 3547 | +            left = left.lower().replace(" ", "") | 
|  | 3548 | +            mid = mid.lower().replace(" ", "") | 
|  | 3549 | +            right = right.strip() | 
|  | 3550 | + | 
|  | 3551 | +            if left == "imagefile": | 
|  | 3552 | +                if os.path.exists(right): | 
|  | 3553 | +                    image_path = right | 
|  | 3554 | +                else: | 
|  | 3555 | +                    image_path = CommonUtil.path_parser(right) | 
|  | 3556 | + | 
|  | 3557 | +            elif left == "imagevariable": | 
|  | 3558 | +                if os.path.exists(right): | 
|  | 3559 | +                    image_path = right | 
|  | 3560 | +                else: | 
|  | 3561 | +                    variable_name = right | 
|  | 3562 | + | 
|  | 3563 | +        if image_path: | 
|  | 3564 | +            if not os.path.exists(image_path): | 
|  | 3565 | +                CommonUtil.ExecLog(sModuleInfo, f"Image file not found: {image_path}", 3) | 
|  | 3566 | +                return "zeuz_failed" | 
|  | 3567 | +        elif variable_name: | 
|  | 3568 | +            image_path = Shared_Resources.Get_Shared_Variables(variable_name) | 
|  | 3569 | +            if not image_path: | 
|  | 3570 | +                CommonUtil.ExecLog(sModuleInfo, f"Image path not found in variable: {variable_name}. Make sure you must be use '%| |%' syntax for any variable or attachment.", 3) | 
|  | 3571 | +                return "zeuz_failed" | 
|  | 3572 | +        else: | 
|  | 3573 | +            CommonUtil.ExecLog(sModuleInfo, "Must provide either 'image file' or 'image variable'", 3) | 
|  | 3574 | +            return "zeuz_failed" | 
|  | 3575 | +         | 
|  | 3576 | +        if image_path.lower().endswith(".svg"): | 
|  | 3577 | +            mime_type = "image/svg+xml" | 
|  | 3578 | +        elif image_path.lower().endswith(".png"): | 
|  | 3579 | +            mime_type = "image/png" | 
|  | 3580 | +        else: | 
|  | 3581 | +            CommonUtil.ExecLog(sModuleInfo, "Unsupported file format. You can copy only PNG or SVG image.", 2) | 
|  | 3582 | +            return "zeuz_failed" | 
|  | 3583 | +         | 
|  | 3584 | +        with open(image_path, "rb") as image_file: | 
|  | 3585 | +            image_data = image_file.read() | 
|  | 3586 | +         | 
|  | 3587 | +        # Convert | 
|  | 3588 | +        image_b64 = base64.b64encode(image_data).decode('utf-8') | 
|  | 3589 | +         | 
|  | 3590 | +        browser_name = selenium_driver.capabilities.get('browserName', '').lower() | 
|  | 3591 | +        if browser_name in ('chrome', 'microsoft edge', 'edge'): | 
|  | 3592 | +            try: | 
|  | 3593 | +                selenium_driver.execute_cdp_cmd('Browser.setClipboard', { | 
|  | 3594 | +                    'data': image_b64, | 
|  | 3595 | +                    'type': mime_type | 
|  | 3596 | +                }) | 
|  | 3597 | +                CommonUtil.ExecLog(sModuleInfo, f"Image copied to clipboard via CDP: {image_path}", 1) | 
|  | 3598 | +                return "passed" | 
|  | 3599 | +            except Exception as e: | 
|  | 3600 | +                CommonUtil.ExecLog(sModuleInfo, f"CDP failed ({str(e)}). Trying fallback method", 2) | 
|  | 3601 | + | 
|  | 3602 | +        try: | 
|  | 3603 | +            # Grant clipboard permissions via CDP if possible | 
|  | 3604 | +            try: | 
|  | 3605 | +                from urllib.parse import urlparse | 
|  | 3606 | +                parsed_uri = urlparse(selenium_driver.current_url) | 
|  | 3607 | +                origin = f'{parsed_uri.scheme}://{parsed_uri.netloc}' | 
|  | 3608 | +                selenium_driver.execute_cdp_cmd('Browser.grantPermissions', { | 
|  | 3609 | +                    'origin': origin, | 
|  | 3610 | +                    'permissions': ['clipboardReadWrite', 'clipboardSanitizedWrite'] | 
|  | 3611 | +                }) | 
|  | 3612 | +            except: | 
|  | 3613 | +                pass | 
|  | 3614 | + | 
|  | 3615 | +            async_script = """ | 
|  | 3616 | +            const [base64Data, mimeType, callback] = arguments; | 
|  | 3617 | +            const byteCharacters = atob(base64Data); | 
|  | 3618 | +            const byteArrays = []; | 
|  | 3619 | +             | 
|  | 3620 | +            for (let offset = 0; offset < byteCharacters.length; offset += 512) { | 
|  | 3621 | +                const slice = byteCharacters.slice(offset, offset + 512); | 
|  | 3622 | +                const byteNumbers = new Array(slice.length); | 
|  | 3623 | +                 | 
|  | 3624 | +                for (let i = 0; i < slice.length; i++) { | 
|  | 3625 | +                    byteNumbers[i] = slice.charCodeAt(i); | 
|  | 3626 | +                } | 
|  | 3627 | +                 | 
|  | 3628 | +                byteArrays.push(new Uint8Array(byteNumbers)); | 
|  | 3629 | +            } | 
|  | 3630 | +             | 
|  | 3631 | +            const blob = new Blob(byteArrays, {type: mimeType}); | 
|  | 3632 | +            const item = new ClipboardItem({ [mimeType]: blob }); | 
|  | 3633 | +             | 
|  | 3634 | +            window.focus(); | 
|  | 3635 | +            navigator.clipboard.write([item]) | 
|  | 3636 | +                .then(() => { | 
|  | 3637 | +                    console.log('Successfully copied image to clipboard.'); | 
|  | 3638 | +                    callback(true); | 
|  | 3639 | +                }) | 
|  | 3640 | +                .catch(err => { | 
|  | 3641 | +                    console.log('Failed to copy image to clipboard', err); | 
|  | 3642 | +                    callback(false); | 
|  | 3643 | +                }); | 
|  | 3644 | +            """ | 
|  | 3645 | + | 
|  | 3646 | +            selenium_driver.switch_to.window(selenium_driver.current_window_handle) | 
|  | 3647 | +            selenium_driver.execute_script("window.focus();") | 
|  | 3648 | + | 
|  | 3649 | +            success = selenium_driver.execute_async_script(async_script, image_b64, mime_type) | 
|  | 3650 | +            if success: | 
|  | 3651 | +                CommonUtil.ExecLog(sModuleInfo, f"Image copied to clipboard: {image_path}", 1) | 
|  | 3652 | +                return "passed" | 
|  | 3653 | +            CommonUtil.ExecLog(sModuleInfo, f"Document is not focused. Failed to copy image to clipboard: {image_path}", 3) | 
|  | 3654 | +            return "zeuz_failed" | 
|  | 3655 | + | 
|  | 3656 | +        except Exception as e: | 
|  | 3657 | +            CommonUtil.ExecLog(sModuleInfo, f"Fallback method failed: {str(e)}", 3) | 
|  | 3658 | +            return "zeuz_failed" | 
|  | 3659 | +             | 
|  | 3660 | +    except Exception: | 
|  | 3661 | +        return CommonUtil.Exception_Handler(sys.exc_info()) | 
0 commit comments