├── README.md ├── autoitdriverserver_python ├── __init__.py ├── autoit_options.cfg ├── favicon.ico ├── requirements.txt └── server.py └── sample-code ├── CalculatorTest.java ├── SeleniumIntegrationTest.java ├── SeleniumIntegrationWithAutoItDriver.py ├── calculator.py ├── demo.au3 ├── notepad.py └── run_au3_script_demo.py /README.md: -------------------------------------------------------------------------------- 1 | AutoItDriverServer 2 | ========= 3 | 4 | AutoItDriverServer is a server interface wrapper to [AutoIt](https://www.autoitscript.com) that provides a Selenium WebDriver API via the webdriver JSON wire protocol to drive AutoIt (using AutoItX COM/DLL API). 5 | 6 | There are 3 benefits to testing with AutoItDriverServer: 7 | 8 | 1: you are able to write your test in your choice of programming language, using the Selenium WebDriver API and language-specific client libraries. 9 | 10 | 2: is remote execution enabled, and perhaps Selenium Grid compatible in the future. See [Why use AutoItDriverServer](https://github.com/daluu/AutoItDriverServer/wiki/Why-use-AutoItDriverServer) for more details. 11 | 12 | 3: uses the well known AutoIt tool for Windows GUI manipulation. If you already use AutoIt, this will be a nice benefit. 13 | 14 | Quick Start & server/implementation notes 15 | ------------------------------------------ 16 | 17 | The server is currently implemented in Python, calling AutoItX over COM (or optionally/alternatively DLL). There is a plan to do a .NET/C# version that is more standalone than Python in the future. Both implementations have the goal of working with all off the shelf Selenium client libraries. 18 | 19 | Python implementation is adapted from the [old Appium server Python implementation](https://github.com/hugs/appium-old), and uses the [Bottle micro web-framework](http://www.bottlepy.org). The .NET/C# implementation will be adapted from [Strontium server](https://github.com/jimevans/strontium). 20 | 21 | To get started, clone the repo:
22 | `git clone git://github.com/daluu/AutoItDriverServer` 23 | 24 | Next, change into the 'autoitdriverserver_python' directory, and install dependencies:
25 | `pip install -r partial_requirements.txt` 26 | 27 | The partial_requirements file doesn't necessarily specify all requirements. You'll need to have the Python win32com.client module (or Python for Windows extensions, win32 extensions for Python, etc.). That may already be installed with your Python installation. If not, you can install from here for example: http://sourceforge.net/projects/pywin32. You can test first yourself to see if executing "import win32com.client" will return an exception or not in Python, with no errors meaning it's already installed. Alternatively, the server code was initially written to also work with https://github.com/jacexh/pyautoit as well (if you swap out the commented code with the current code). 28 | 29 | Additionally, you'll need to have AutoIt installed, or register the appropriate version of AutoItX DLL (x86 vs x64). When installing AutoIt or registering DLL, the version to register/use depends on the platform you're using with. For Python it would depend on whether you run the 32 or 64 bit version of Python not whether your OS is 32 or 64 bit. 30 | 31 | To launch the webdriver-compatible AutoItDriverServer to use AutoIt via WebDriver client API, run (from 'autoitdriverserver_python' directory):
32 | `python server.py`
33 | 34 | For additional parameter info, append the `--help` parameter 35 | 36 | Example WebDriver API/client test usage against this server tool can be found in `sample-code` folder. To run the test, startup the server (with customized parameters as needed, recommend set address to 127.0.0.1) and review the example files' code before executing those scripts. Examples use Python WebDriver binding, but any language binding will actually do. 37 | 38 | NOTES/Caveats 39 | ------------- 40 | 41 | AutoItDriverServer is simply a WebDriver server interface to AutoIt. Issues you experience may usually be the result of an issue with AutoIt rather than AutoItDriverServer itself. It would be wise to test your issue/scenario using AutoIt (via native AutoIt script compiled to executable or not) or AutoItX to confirm whether the issue is with AutoIt or AutoItDriverServer. The source code of AutoItDriverServer will point you to the appropriate AutoItX API or see the wiki documentation [here](https://github.com/daluu/AutoItDriverServer/wiki/WebDriver-API-command-support-and-mapping-to-AutoItX-API) and [here](https://github.com/daluu/AutoItDriverServer/wiki/Debugging-tests-and-issues-regarding-AutoIt-and-AutoItDriverServer). 42 | 43 | WebDriver API/command support and mapping to AutoItX API 44 | ------------------------------------------------------- 45 | 46 | See [WebDriver API command support and mapping to AutoItX API](https://github.com/daluu/AutoItDriverServer/wiki/WebDriver-API-command-support-and-mapping-to-AutoItX-API) 47 | 48 | Contributing 49 | ------------ 50 | 51 | Fork the project, make a change, and send a pull request! 52 | 53 | Or as a user, try it out, provide your feedback, submit bugs/enhancement requests. 54 | -------------------------------------------------------------------------------- /autoitdriverserver_python/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Appium Committers 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | -------------------------------------------------------------------------------- /autoitdriverserver_python/autoit_options.cfg: -------------------------------------------------------------------------------- 1 | [AutoIt Options] 2 | CaretCoordMode=1 3 | ExpandEnvStrings=0 4 | MouseClickDelay=10 5 | MouseClickDownDelay=10 6 | MouseClickDragDelay=250 7 | MouseCoordinateMode=1 8 | SendAttachMode=0 9 | SendCapslockMode=1 10 | SendKeyDelay=5 11 | SendKeyDownDelay=5 12 | WinDetectHiddenText=0 13 | WinSearchChildren=0 14 | WinTextMatchMode=1 15 | WinTitleMatchMode=1 16 | WinWaitDelay=250 17 | AutoItScriptExecuteScriptAsCompiledBinary=False 18 | AutoIt32BitExecutablePath="C:\Program Files\AutoIt3\AutoIt3.exe" 19 | AutoIt64BitOSOnInstallUse32Bit=True 20 | AutoIt64BitOS32BitExecutablePath="C:\Program Files (x86)\AutoIt3\AutoIt3.exe" 21 | AutoIt64BitOS64BitExecutablePath="C:\Program Files (x86)\AutoIt3\AutoIt3_x64.exe" -------------------------------------------------------------------------------- /autoitdriverserver_python/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daluu/AutoItDriverServer/331ef82d824d15da4a171004ef6bd740829851de/autoitdriverserver_python/favicon.ico -------------------------------------------------------------------------------- /autoitdriverserver_python/requirements.txt: -------------------------------------------------------------------------------- 1 | bottle>=0.10.11 2 | -------------------------------------------------------------------------------- /autoitdriverserver_python/server.py: -------------------------------------------------------------------------------- 1 | # Source code is modified from and based off of 2 | # old/original Appium Python implementation at 3 | # 4 | # https://github.com/hugs/appium-old 5 | # 6 | # Licensed to the Apache Software Foundation (ASF) under one 7 | # or more contributor license agreements. See the NOTICE file 8 | # distributed with this work for additional information 9 | # regarding copyright ownership. The ASF licenses this file 10 | # to you under the Apache License, Version 2.0 (the 11 | # "License"); you may not use this file except in compliance 12 | # with the License. You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, 17 | # software distributed under the License is distributed on an 18 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | # KIND, either express or implied. See the License for the 20 | # specific language governing permissions and limitations 21 | # under the License. 22 | 23 | from __future__ import print_function 24 | from bottle import Bottle, request, response, redirect 25 | from bottle import run, static_file 26 | import json 27 | import socket 28 | import sys 29 | import platform 30 | import os 31 | import subprocess 32 | import tempfile 33 | import base64 34 | import urllib 35 | #import autoit 36 | import win32com.client 37 | from time import time 38 | from time import sleep 39 | try: 40 | import configparser 41 | except ImportError: 42 | import ConfigParser as configparser 43 | 44 | app = Bottle() 45 | 46 | @app.get('/favicon.ico') 47 | def get_favicon(): 48 | return static_file('favicon.ico', root='.') 49 | 50 | def get_platform(): 51 | if sys.platform == "win32": 52 | if platform.release() == "Vista": 53 | wd_platform = "VISTA" 54 | elif platform.release() == "XP": #? 55 | wd_platform = "XP" 56 | else: 57 | wd_platform = "WINDOWS" 58 | else: 59 | wd_platform = "WINDOWS" 60 | return wd_platform 61 | 62 | @app.route('/wd/hub/status', method='GET') 63 | def status(): 64 | status = {'sessionId': app.SESSION_ID if app.started else None, 65 | 'status': 0, 66 | 'value': {'build': {'version': 'AutoItDriverServer 0.1'}, 67 | 'os': {'arch':platform.machine(),'name':'windows','version':platform.release()}}} 68 | return status 69 | 70 | @app.route('/wd/hub/session', method='POST') 71 | def create_session(): 72 | #app.oAutoItX = win32com.client.Dispatch("AutoItX3.Control") 73 | 74 | #process desired capabilities 75 | request_data = request.body.read() 76 | dc = json.loads(request_data.decode()).get('desiredCapabilities') 77 | if dc is not None: 78 | app.caretCoordMode = dc.get('caretCoordMode') if dc.get('caretCoordMode') is not None else app.caretCoordMode 79 | app.expandEnvStrings = dc.get('expandEnvStrings') if dc.get('expandEnvStrings') is not None else app.expandEnvStrings 80 | app.mouseClickDelay = dc.get('mouseClickDelay') if dc.get('mouseClickDelay') is not None else app.mouseClickDelay 81 | app.mouseClickDownDelay = dc.get('mouseClickDownDelay') if dc.get('mouseClickDownDelay') is not None else app.mouseClickDownDelay 82 | app.mouseClickDragDelay = dc.get('mouseClickDragDelay') if dc.get('mouseClickDragDelay') is not None else app.mouseClickDragDelay 83 | app.mouseCoordinateMode = dc.get('mouseCoordinateMode') if dc.get('mouseCoordinateMode') is not None else app.mouseCoordinateMode 84 | app.sendAttachMode = dc.get('sendAttachMode') if dc.get('sendAttachMode') is not None else app.sendAttachMode 85 | app.sendCapslockMode = dc.get('sendCapslockMode') if dc.get('sendCapslockMode') is not None else app.sendCapslockMode 86 | app.sendKeyDelay = dc.get('sendKeyDelay') if dc.get('sendKeyDelay') is not None else app.sendKeyDelay 87 | app.sendKeyDownDelay = dc.get('sendKeyDownDelay') if dc.get('sendKeyDownDelay') is not None else app.sendKeyDownDelay 88 | app.winDetectHiddenText = dc.get('winDetectHiddenText') if dc.get('winDetectHiddenText') is not None else app.winDetectHiddenText 89 | app.winSearchChildren = dc.get('winSearchChildren') if dc.get('winSearchChildren') is not None else app.winSearchChildren 90 | app.winTextMatchMode = dc.get('winTextMatchMode') if dc.get('winTextMatchMode') is not None else app.winTextMatchMode 91 | app.winTitleMatchMode = dc.get('winTitleMatchMode') if dc.get('winTitleMatchMode') is not None else app.winTitleMatchMode 92 | app.winWaitDelay = dc.get('winWaitDelay') if dc.get('winWaitDelay') is not None else app.winWaitDelay 93 | 94 | #autoit.option.caret_coord_mode = app.caretCoordMode 95 | app.oAutoItX.Opt("CaretCoordMode", app.caretCoordMode) 96 | #autoit.option.expand_env_strings = app.expandEnvStrings 97 | app.oAutoItX.Opt("ExpandEnvStrings", app.expandEnvStrings) 98 | #autoit.option.mouse_click_delay = app.mouseClickDelay 99 | app.oAutoItX.Opt("MouseClickDelay", app.mouseClickDelay) 100 | #autoit.option.mouse_click_down_delay = app.mouseClickDownDelay 101 | app.oAutoItX.Opt("MouseClickDownDelay", app.mouseClickDownDelay) 102 | #autoit.option.mouse_click_drag_delay = app.mouseClickDragDelay 103 | app.oAutoItX.Opt("MouseClickDragDelay", app.mouseClickDragDelay) 104 | #autoit.option.mouse_coordinate_mode = app.mouseCoordinateMode 105 | app.oAutoItX.Opt("MouseCoordinateMode", app.mouseCoordinateMode) 106 | #autoit.option.send_attach_mode = app.sendAttachMode 107 | app.oAutoItX.Opt("SendAttachMode", app.sendAttachMode) 108 | #autoit.option.send_capslock_mode = app.sendCapslockMode 109 | app.oAutoItX.Opt("SendCapslockMode", app.sendCapslockMode) 110 | #autoit.option.send_key_delay = app.sendKeyDelay 111 | app.oAutoItX.Opt("SendKeyDelay", app.sendKeyDelay) 112 | #autoit.option.send_key_down_delay = app.sendKeyDownDelay 113 | app.oAutoItX.Opt("SendKeyDownDelay", app.sendKeyDownDelay) 114 | #autoit.option.win_detect_hidden_text = app.winDetectHiddenText 115 | app.oAutoItX.Opt("WinDetectHiddenText", app.winDetectHiddenText) 116 | #autoit.option.win_search_children = app.winSearchChildren 117 | app.oAutoItX.Opt("WinSearchChildren", app.winSearchChildren) 118 | #autoit.option.win_text_match_mode = app.winTextMatchMode 119 | app.oAutoItX.Opt("WinTextMatchMode", app.winTextMatchMode) 120 | #autoit.option.win_title_match_mode = app.winTitleMatchMode 121 | app.oAutoItX.Opt("WinTitleMatchMode", app.winTitleMatchMode) 122 | #autoit.option.win_wait_delay = app.winWaitDelay 123 | app.oAutoItX.Opt("WinWaitDelay", app.winWaitDelay) 124 | 125 | #setup session 126 | app.started = True 127 | redirect('/wd/hub/session/%s' % app.SESSION_ID) 128 | 129 | @app.route('/wd/hub/session/', method='GET') 130 | def get_session(session_id=''): 131 | wd_platform = get_platform() 132 | app_response = {'sessionId': session_id, 133 | 'status': 0, 134 | 'value': {"version":"0.1", 135 | "browserName":"AutoIt", 136 | "platform":wd_platform, 137 | "takesScreenshot":False, 138 | "caretCoordMode":app.caretCoordMode, 139 | "expandEnvStrings":app.expandEnvStrings, 140 | "mouseClickDelay":app.mouseClickDelay, 141 | "mouseClickDownDelay":app.mouseClickDownDelay, 142 | "mouseClickDragDelay":app.mouseClickDragDelay, 143 | "mouseCoordinateMode":app.mouseCoordinateMode, 144 | "sendAttachMode":app.sendAttachMode, 145 | "sendCapslockMode":app.sendCapslockMode, 146 | "sendKeyDelay":app.sendKeyDelay, 147 | "sendKeyDownDelay":app.sendKeyDownDelay, 148 | "winDetectHiddenText":app.winDetectHiddenText, 149 | "winSearchChildren":app.winSearchChildren, 150 | "winTextMatchMode":app.winTextMatchMode, 151 | "winTitleMatchMode":app.winTitleMatchMode, 152 | "winWaitDelay":app.winWaitDelay}} 153 | return app_response 154 | 155 | @app.route('/wd/hub/session/', method='DELETE') 156 | def delete_session(session_id=''): 157 | app.started = False 158 | # any need to dispose of/clean up COM connection to AutoIt for win32com? 159 | # if we instantiated from create session (rather than at server startup) 160 | #app.oAutoItX = None # for example 161 | app_response = {'sessionId': session_id, 162 | 'status': 0, 163 | 'value': {}} 164 | return app_response 165 | 166 | @app.route('/wd/hub/session//execute', method='POST') 167 | def execute_script(session_id=''): 168 | request_data = request.body.read() 169 | try: 170 | script = json.loads(request_data.decode()).get('script') 171 | args = json.loads(request_data.decode()).get('args') 172 | 173 | if config.get("AutoIt Options",'AutoItScriptExecuteScriptAsCompiledBinary') == "False": 174 | if platform.machine() == "AMD64": 175 | if config.get("AutoIt Options",'AutoIt64BitOSOnInstallUse32Bit') == "True": 176 | au3Runner = config.get("AutoIt Options",'AutoIt64BitOS32BitExecutablePath') 177 | else: 178 | au3Runner = config.get("AutoIt Options",'AutoIt64BitOS64BitExecutablePath') 179 | else: # platform.machine() == "i386" 180 | au3Runner = config.get("AutoIt Options",'AutoIt32BitExecutablePath') 181 | script_call = "%s %s" % (au3Runner,script) 182 | else: 183 | script_call = script 184 | if args is not None: 185 | for arg in args: 186 | script_call = "%s %s" % (script_call,arg) 187 | print("script2exec: ",script_call) 188 | os.system(script_call) 189 | except: 190 | response.status = 400 191 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 192 | 193 | app_response = {'sessionId': session_id, 194 | 'status': 0, 195 | 'value': {}} 196 | return app_response 197 | 198 | @app.route('/wd/hub/session//element//click', method='POST') 199 | def element_click(session_id='', element_id=''): 200 | try: 201 | element = decode_value_from_wire(element_id) 202 | #result = autoit.control_click("[active]", element) 203 | result = app.oAutoItX.ControlClick("[active]", "", element) 204 | if result == 0: 205 | raise Exception("AutoIt failed to click element %s." % element) 206 | except: 207 | response.status = 400 208 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 209 | 210 | app_response = {'sessionId': session_id, 211 | 'status': 0, 212 | 'value': {}} 213 | return app_response 214 | 215 | @app.route('/wd/hub/session//click', method='POST') 216 | def mouse_click(session_id=''): 217 | request_data = request.body.read() 218 | if request_data == None or request_data == '' or request_data == "{}": 219 | button = 0 220 | else: 221 | button = json.loads(request_data.decode()).get('button') 222 | try: 223 | if button == 1: 224 | btn_type = "middle" 225 | elif button == 2: 226 | btn_type = "right" 227 | else: 228 | btn_type = "left" 229 | #autoit.mouse_click(btn_type) 230 | app.oAutoItX.MouseClick(btn_type) 231 | except: 232 | response.status = 400 233 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[0])} 234 | 235 | app_response = {'sessionId': session_id, 236 | 'status': 0, 237 | 'value': {}} 238 | return app_response 239 | 240 | @app.route('/wd/hub/session//doubleclick', method='POST') 241 | def double_click(session_id=''): 242 | try: 243 | #src = autoit.mouse_get_pos() 244 | #autoit.mouse_click("left",src.x,src.y,2) 245 | x = app.oAutoItX.MouseGetPosX() 246 | y = app.oAutoItX.MouseGetPosY() 247 | app.oAutoItX.MouseClick("left",x,y,2) 248 | except: 249 | response.status = 400 250 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[0])} 251 | 252 | app_response = {'sessionId': session_id, 253 | 'status': 0, 254 | 'value': {}} 255 | return app_response 256 | 257 | @app.route('/wd/hub/session//buttonup', method='POST') 258 | def mouse_up(session_id=''): 259 | request_data = request.body.read() 260 | if request_data == None or request_data == '' or request_data == "{}": 261 | button = 0 262 | else: 263 | button = json.loads(request_data.decode()).get('button') 264 | try: 265 | if button == 1: 266 | btn_type = "middle" 267 | elif button == 2: 268 | btn_type = "right" 269 | else: 270 | btn_type = "left" 271 | #autoit.mouse_up(btn_type) 272 | app.oAutoItX.MouseUp(btn_type) 273 | except: 274 | response.status = 400 275 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[0])} 276 | 277 | app_response = {'sessionId': session_id, 278 | 'status': 0, 279 | 'value': {}} 280 | return app_response 281 | 282 | @app.route('/wd/hub/session//buttondown', method='POST') 283 | def mouse_down(session_id=''): 284 | request_data = request.body.read() 285 | if request_data == None or request_data == '' or request_data == "{}": 286 | button = 0 287 | else: 288 | button = json.loads(request_data.decode()).get('button') 289 | try: 290 | if button == 1: 291 | btn_type = "middle" 292 | elif button == 2: 293 | btn_type = "right" 294 | else: 295 | btn_type = "left" 296 | #autoit.mouse_down(btn_type) 297 | app.oAutoItX.MouseDown(btn_type) 298 | except: 299 | response.status = 400 300 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[0])} 301 | 302 | app_response = {'sessionId': session_id, 303 | 'status': 0, 304 | 'value': {}} 305 | return app_response 306 | 307 | @app.route('/wd/hub/session//moveto', method='POST') 308 | def move_to(session_id=''): 309 | request_data = request.body.read() 310 | if request_data == None or request_data == '' or request_data == "{}": 311 | element_id = None 312 | xoffset = None 313 | yoffset = None 314 | else: 315 | element_id = json.loads(request_data.decode()).get('element') 316 | xoffset = json.loads(request_data.decode()).get('xoffset') 317 | yoffset = json.loads(request_data.decode()).get('yoffset') 318 | try: 319 | if element_id == None and (xoffset != None or yoffset != None): 320 | #src = autoit.mouse_get_pos() 321 | #autoit.mouse_move(src.x+xoffset,src.y+yoffset) 322 | x = app.oAutoItX.MouseGetPosX() 323 | y = app.oAutoItX.MouseGetPosY() 324 | app.oAutoItX.MouseMove(x+xoffset,y+yoffset) 325 | else: 326 | if xoffset != None or yoffset != None: 327 | control_id = decode_value_from_wire(element_id) 328 | #pos = autoit.control_get_pos("[active]",control_id) 329 | x = app.oAutoItX.ControlGetPosX("[active]","",control_id) 330 | y = app.oAutoItX.ControlGetPosY("[active]","",control_id) 331 | #if autoit._has_error(): 332 | if app.oAutoItX.error == 1: 333 | raise Exception("AutoIt failed to get element %s location to move to." % control_id) 334 | #autoit.mouse_move(pos.left+xoffset,pos.top+yoffset) 335 | app.oAutoItX.MouseMove(x+xoffset,y+yoffset) 336 | else: # just go to center of element 337 | control_id = decode_value_from_wire(element_id) 338 | #pos = autoit.control_get_pos("[active]",control_id) 339 | x = app.oAutoItX.ControlGetPosX("[active]","",control_id) 340 | y = app.oAutoItX.ControlGetPosY("[active]","",control_id) 341 | width = app.oAutoItX.ControlGetPosWidth("[active]","",control_id) 342 | height = app.oAutoItX.ControlGetPosHeight("[active]","",control_id) 343 | #if autoit._has_error(): 344 | if app.oAutoItX.error == 1: 345 | raise Exception("AutoIt failed to get element %s location to move to." % control_id) 346 | #autoit.mouse_move(pos.left+(pos.right/2),pos.top+(pos.bottom/2)) 347 | app.oAutoItX.MouseMove(x+(width/2),y+(height/2)) 348 | except: 349 | response.status = 400 350 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 351 | 352 | app_response = {'sessionId': session_id, 353 | 'status': 0, 354 | 'value': {}} 355 | return app_response 356 | 357 | @app.route('/wd/hub/session//element//value', method='POST') 358 | def set_value(session_id='', element_id=''): 359 | request_data = request.body.read() 360 | try: 361 | value_to_set = json.loads(request_data.decode()).get('value') 362 | value_to_set = ''.join(value_to_set) 363 | control_id = decode_value_from_wire(element_id) 364 | #result = autoit.control_set_text("[active]",control_id,value_to_set) 365 | result = app.oAutoItX.ControlSetText("[active]","",control_id,value_to_set) 366 | if result == 0: 367 | raise Exception("AutoIt failed to set text of element %s, element or window not found." % control_id) 368 | except: 369 | response.status = 400 370 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 371 | 372 | app_response = {'sessionId': session_id, 373 | 'status': 0, 374 | 'value': {}} 375 | return app_response 376 | 377 | @app.route('/wd/hub/session//element//clear', method='POST') 378 | def clear(session_id='', element_id=''): 379 | try: 380 | control_id = decode_value_from_wire(element_id) 381 | #result = autoit.control_set_text("[active]",control_id,"") 382 | result = app.oAutoItX.ControlSetText("[active]","",control_id,"") 383 | if result == 0: 384 | raise Exception("AutoIt failed to clear text of element %s, element or window not found." % control_id) 385 | except: 386 | response.status = 400 387 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 388 | 389 | app_response = {'sessionId': session_id, 390 | 'status': 0, 391 | 'value': {}} 392 | return app_response 393 | 394 | @app.route('/wd/hub/session//element//element', method='POST') 395 | def element_find_element(session_id='', element_id=''): 396 | return _find_element(session_id, element_id) 397 | 398 | @app.route('/wd/hub/session//element', method='POST') 399 | def find_element(session_id=''): 400 | return _find_element(session_id, "root") 401 | 402 | def _find_element(session_id, context, many=False): 403 | try: 404 | json_request_data = json.loads(request.body.read().decode()) 405 | locator_strategy = json_request_data.get('using') 406 | value = json_request_data.get('value') 407 | 408 | if locator_strategy == "id": 409 | control_id = "[ID:%s]" % value 410 | elif locator_strategy == "link text": 411 | control_id = "[TEXT:%s]" % value 412 | elif locator_strategy == "tag name": 413 | control_id = "[CLASS:%s]" % value 414 | elif locator_strategy == "class name": 415 | control_id = "[CLASSNN:%s]" % value 416 | elif locator_strategy == "name": 417 | control_id = "[NAME:%s]" % value 418 | elif locator_strategy == "xpath": 419 | control_id = "[REGEXPCLASS:%s]" % value 420 | elif locator_strategy == "css selector": 421 | control_id = value 422 | else: 423 | control_id = value 424 | 425 | # AutoIt has no concept of finding elements/controls and checking if it 426 | # exists or not. Therefore, we just pass back the location strategy value 427 | # and let the individual WebElement methods fail when control/element not found 428 | found_elements = {'ELEMENT':encode_value_4_wire(control_id)} 429 | return {'sessionId': session_id, 'status': 0, 'value': found_elements} 430 | except: 431 | response.status = 400 432 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[0])} 433 | 434 | @app.route('/wd/hub/session//keys', method='POST') 435 | def keys(session_id=''): 436 | try: 437 | request_data = request.body.read() 438 | wired_keys = json.loads(request_data.decode()).get('value') 439 | keys = "".join(wired_keys) 440 | #autoit.send(keys) 441 | app.oAutoItX.Send(keys) 442 | return {'sessionId': session_id, 'status': 0} 443 | except: 444 | response.status = 400 445 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[0])} 446 | 447 | @app.route('/wd/hub/session//element//location', method='GET') 448 | def element_location(session_id='', element_id=''): 449 | try: 450 | control_id = decode_value_from_wire(element_id) 451 | #pos = autoit.control_get_pos("[active]",control_id) 452 | #location = {'x': pos.left, 'y': pos.top} 453 | x = app.oAutoItX.ControlGetPosX("[active]","",control_id) 454 | y = app.oAutoItX.ControlGetPosY("[active]","",control_id) 455 | location = {'x': x, 'y': y} 456 | #if autoit._has_error(): 457 | if app.oAutoItX.error == 1: 458 | raise Exception("AutoIt failed to get element %s location coordinates." % control_id) 459 | return {'sessionId': session_id, 'status': 0, 'value': location} 460 | except: 461 | response.status = 400 462 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 463 | 464 | @app.route('/wd/hub/session//element//size', method='GET') 465 | def element_size(session_id='', element_id=''): 466 | try: 467 | control_id = decode_value_from_wire(element_id) 468 | #pos = autoit.control_get_pos("[active]",control_id) 469 | #size = {'width': pos.right, 'height': pos.bottom} 470 | width = app.oAutoItX.ControlGetPosWidth("[active]","",control_id) 471 | height = app.oAutoItX.ControlGetPosWidth("[active]","",control_id) 472 | size = {'width': width, 'height': height} 473 | #if autoit._has_error(): 474 | if app.oAutoItX.error == 1: 475 | raise Exception("AutoIt failed to get element %s width & height size." % control_id) 476 | return {'sessionId': session_id, 'status': 0, 'value': size} 477 | except: 478 | response.status = 400 479 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 480 | 481 | @app.route('/wd/hub/session//element//displayed', method='GET') 482 | def element_displayed(session_id='', element_id=''): 483 | try: 484 | control_id = decode_value_from_wire(element_id) 485 | #result = autoit.control_command("[active]",control_id,"IsVisible") 486 | result = app.oAutoItX.ControlCommand("[active]","",control_id,"IsVisible") 487 | displayed = True if result == 1 else False 488 | #if autoit._has_error(): 489 | if app.oAutoItX.error == 1: 490 | raise Exception("AutoIt failed to find element %s." % control_id) 491 | return {'sessionId': session_id, 'status': 0, 'value': displayed} 492 | except: 493 | response.status = 400 494 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 495 | 496 | @app.route('/wd/hub/session//element//enabled', method='GET') 497 | def element_enabled(session_id='', element_id=''): 498 | try: 499 | control_id = decode_value_from_wire(element_id) 500 | #result = autoit.control_command("[active]",control_id,"IsEnabled") 501 | result = app.oAutoItX.ControlCommand("[active]","",control_id,"IsEnabled") 502 | enabled = True if result == 1 else False 503 | #if autoit._has_error(): 504 | if app.oAutoItX.error == 1: 505 | raise Exception("AutoIt failed to find element %s." % control_id) 506 | return {'sessionId': session_id, 'status': 0, 'value': enabled} 507 | except: 508 | response.status = 400 509 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 510 | 511 | @app.route('/wd/hub/session//element//selected', method='GET') 512 | def element_selected(session_id='', element_id=''): 513 | try: 514 | control_id = decode_value_from_wire(element_id) 515 | #result = autoit.control_command("[active]",control_id,"IsChecked") 516 | result = app.oAutoItX.ControlCommand("[active]","",control_id,"IsChecked") 517 | selected = True if result == 1 else False 518 | #if autoit._has_error(): 519 | if app.oAutoItX.error == 1: 520 | raise Exception("AutoIt failed to find element %s." % control_id) 521 | return {'sessionId': session_id, 'status': 0, 'value': selected} 522 | except: 523 | response.status = 400 524 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 525 | 526 | @app.route('/wd/hub/session//element//text', method='GET') 527 | def get_text(session_id='', element_id=''): 528 | try: 529 | control_id = decode_value_from_wire(element_id) 530 | #text = autoit.control_get_text("[active]",control_id) 531 | text = app.oAutoItX.ControlGetText("[active]","",control_id) 532 | #if autoit._has_error() and text == "": 533 | if app.oAutoItX.error == 1 and text == "": 534 | raise Exception("AutoIt failed to find element %s, and get it's text" % control_id) 535 | except: 536 | response.status = 400 537 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 538 | 539 | app_response = {'sessionId': session_id, 540 | 'status': 0, 541 | 'value': text} 542 | return app_response 543 | 544 | @app.route('/wd/hub/session//element//attribute/', method='GET') 545 | def get_attribute(session_id='', element_id='', attribute=''): 546 | try: 547 | control_id = decode_value_from_wire(element_id) 548 | #result = autoit.control_command("[active]",control_id,attribute) 549 | result = app.oAutoItX.ControlCommand("[active]","",control_id,attribute) 550 | #if autoit._has_error(): 551 | if app.oAutoItX.error == 1: 552 | raise Exception("AutoIt failed to find element %s or extract the specified attribute/command '%s'." % (control_id,attribute)) 553 | except: 554 | response.status = 400 555 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 556 | 557 | app_response = {'sessionId': session_id, 558 | 'status': 0, 559 | 'value': str(result)} 560 | return app_response 561 | 562 | @app.route('/wd/hub/session//element//css/', method='GET') 563 | def get_property(session_id='', element_id='', property_name=''): 564 | try: 565 | control_id = decode_value_from_wire(element_id) 566 | #result = autoit.control_command("[active]",control_id,property_name) 567 | result = app.oAutoItX.ControlCommand("[active]","",control_id,property_name) 568 | #if autoit._has_error(): 569 | if app.oAutoItX.error == 1: 570 | raise Exception("AutoIt failed to find element %s or extract the specified property/command '%s'." % (control_id,property_name)) 571 | except: 572 | response.status = 400 573 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 574 | 575 | app_response = {'sessionId': session_id, 576 | 'status': 0, 577 | 'value': str(result)} 578 | return app_response 579 | 580 | @app.route('/wd/hub/session//title', method='GET') 581 | def get_window_title(session_id=''): 582 | try: 583 | #text = autoit.win_get_title("[active]") 584 | text = app.oAutoItX.WinGetTitle("[active]") 585 | if text == 0: 586 | raise Exception("AutoIt failed to get window title of current window.") 587 | except: 588 | response.status = 400 589 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[1])} 590 | 591 | app_response = {'sessionId': session_id, 592 | 'status': 0, 593 | 'value': text} 594 | return app_response 595 | 596 | @app.route('/wd/hub/session//window_handle', method='GET') 597 | def get_current_window_handle(session_id=''): 598 | try: 599 | #handle = autoit.win_get_handle("[active]") 600 | handle = app.oAutoItX.WinGetHandle("[active]") 601 | #if autoit._has_error() and handle == "": 602 | if app.oAutoItX.error == 1 and handle == "": 603 | raise Exception("AutoIt failed to get current window handle.") 604 | except: 605 | response.status = 400 606 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[1])} 607 | 608 | app_response = {'sessionId': session_id, 609 | 'status': 0, 610 | 'value': handle} 611 | return app_response 612 | 613 | @app.route('/wd/hub/session//window', method='POST') 614 | def select_window(session_id=''): 615 | request_data = request.body.read() 616 | try: 617 | win_name_or_handle = json.loads(request_data.decode()).get('name') 618 | #try: 619 | #autoit.win_activate_by_handle(win_name_or_handle) 620 | #except: 621 | #autoit.win_activate(win_name_or_handle) 622 | app.oAutoItX.WinActivate(win_name_or_handle) 623 | except: 624 | response.status = 400 625 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[0])} 626 | 627 | app_response = {'sessionId': session_id, 628 | 'status': 0, 629 | 'value': {}} 630 | return app_response 631 | 632 | @app.route('/wd/hub/session//window', method='DELETE') 633 | def close_window(session_id=''): 634 | try: 635 | #autoit.win_close("[active]") 636 | app.oAutoItX.WinClose("[active]") 637 | except: 638 | response.status = 400 639 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[0])} 640 | 641 | app_response = {'sessionId': session_id, 642 | 'status': 0, 643 | 'value': {}} 644 | return app_response 645 | 646 | @app.route('/wd/hub/session//window//size', method='POST') 647 | def resize_window(session_id='', window_handle=''): 648 | try: 649 | request_data = request.body.read() 650 | width = json.loads(request_data.decode()).get('width') 651 | height = json.loads(request_data.decode()).get('height') 652 | 653 | if window_handle == "current": 654 | window = "[active]" 655 | #pos = autoit.win_get_pos(window) 656 | #if autoit._has_error(): 657 | #raise Exception("Window handle %s not found for resizing." % window_handle) 658 | #autoit.win_move(window,pos.left,pos.top,width,height) 659 | else: 660 | window = window_handle 661 | #pos = autoit.win_get_pos_by_handle(window) 662 | #if autoit._has_error(): 663 | #raise Exception("Window handle %s not found for resizing." % window_handle) 664 | #autoit.win_move_by_handle(window,pos.left,pos.top,width,height) 665 | 666 | x = app.oAutoItX.WinGetPosX(window) 667 | y = app.oAutoItX.WinGetPosX(window) 668 | if app.oAutoItX.error == 1: 669 | raise Exception("Window handle %s not found for resizing." % window_handle) 670 | app.oAutoItX.WinMove(window,x,y,width,height) 671 | 672 | except: 673 | response.status = 400 674 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[1])} 675 | 676 | app_response = {'sessionId': session_id, 677 | 'status': 0, 678 | 'value': {}} 679 | return app_response 680 | 681 | @app.route('/wd/hub/session//window//size', method='GET') 682 | def get_window_size(session_id='', window_handle=''): 683 | try: 684 | if window_handle == "current": 685 | window = "[active]" 686 | #pos = autoit.win_get_pos(window) 687 | else: 688 | window = window_handle 689 | #pos = autoit.win_get_pos_by_handle(window) 690 | 691 | #size = {'width': pos.right, 'height': pos.bottom} 692 | width = app.oAutoItX.WinGetPosWidth(window) 693 | height = app.oAutoItX.WinGetPosHeight(window) 694 | size = {'width': width, 'height': height} 695 | #if autoit._has_error(): 696 | if app.oAutoItX.error == 1: 697 | raise Exception("Window handle %s not found for getting window size." % window_handle) 698 | except: 699 | response.status = 400 700 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[1])} 701 | 702 | app_response = {'sessionId': session_id, 703 | 'status': 0, 704 | 'value': size} 705 | return app_response 706 | 707 | @app.route('/wd/hub/session//window//position', method='POST') 708 | def move_window(session_id='', window_handle=''): 709 | try: 710 | request_data = request.body.read() 711 | x = json.loads(request_data.decode()).get('x') 712 | y = json.loads(request_data.decode()).get('y') 713 | 714 | if window_handle == "current": 715 | window = "[active]" 716 | #autoit.win_move(window,x,y) 717 | else: 718 | window = window_handle 719 | #autoit.win_move_by_handle(window,x,y) 720 | 721 | app.oAutoItX.WinMove(window,x,y) 722 | except: 723 | response.status = 400 724 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[0])} 725 | 726 | app_response = {'sessionId': session_id, 727 | 'status': 0, 728 | 'value': {}} 729 | return app_response 730 | 731 | @app.route('/wd/hub/session//window//position', method='GET') 732 | def get_window_position(session_id='', window_handle=''): 733 | try: 734 | if window_handle == "current": 735 | window = "[active]" 736 | #pos = autoit.win_get_pos(window) 737 | else: 738 | window = window_handle 739 | #pos = autoit.win_get_pos_by_handle(window) 740 | 741 | #size = {'x': pos.left, 'y': pos.top} 742 | x = app.oAutoItX.WinGetPosX(window) 743 | y = app.oAutoItX.WinGetPosY(window) 744 | size = {'x': x, 'y': y} 745 | #if autoit._has_error(): 746 | if app.oAutoItX.error == 1: 747 | raise Exception("Window handle %s not found for getting window position." % window_handle) 748 | except: 749 | response.status = 400 750 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[1])} 751 | 752 | app_response = {'sessionId': session_id, 753 | 'status': 0, 754 | 'value': size} 755 | return app_response 756 | 757 | @app.route('/wd/hub/session//window//maximize', method='POST') 758 | def max_window(session_id='', window_handle=''): 759 | try: 760 | if window_handle == "current": 761 | window = "[active]" 762 | #pos = autoit.win_set_state(window,autoit.properties.SW_MAXIMIZE) 763 | else: 764 | window = window_handle 765 | #pos = autoit.win_set_state_by_handle(window,autoit.properties.SW_MAXIMIZE) 766 | 767 | # FYI, @SW_MAXIMIZE = 3, per https://github.com/jacexh/pyautoit/blob/master/autoit/autoit.py#L111 768 | app.oAutoItX.WinSetState(window,app.oAutoItX.SW_MAXIMIZE) 769 | except: 770 | response.status = 400 771 | return {'sessionId': session_id, 'status': 23, 'value': str(sys.exc_info()[0])} 772 | 773 | app_response = {'sessionId': session_id, 774 | 'status': 0, 775 | 'value': {}} 776 | return app_response 777 | 778 | @app.route('/wd/hub/session//url', method='POST') 779 | def run_autoit_app(session_id=''): 780 | try: 781 | request_data = request.body.read() 782 | app2run = json.loads(request_data.decode()).get('url') 783 | #autoit.run(app2run) 784 | app.oAutoItX.Run(app2run) 785 | #if autoit._has_error(): 786 | if app.oAutoItX.error == 1: 787 | raise Exception("Failed to run the application/executable: %s." % app2run) 788 | except: 789 | response.status = 400 790 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[1])} 791 | 792 | app_response = {'sessionId': session_id, 793 | 'status': 0, 794 | 'value': {}} 795 | return app_response 796 | 797 | @app.route('/wd/hub/session//file', method='POST') 798 | def upload_file(session_id=''): 799 | try: 800 | request_data = request.body.read() 801 | b64data = json.loads(request_data.decode()).get('file') 802 | byteContent = base64.b64decode(b64data) 803 | path = "" 804 | with tempfile.NamedTemporaryFile(delete=False) as f: 805 | f.write(byteContent) 806 | path = f.name 807 | extracted_files = unzip(path,os.path.dirname(path)) 808 | except: 809 | response.status = 400 810 | return {'sessionId': session_id, 'status': 13, 'value': str(sys.exc_info()[0])} 811 | 812 | # For (remote) file uploads - well currently AutoItDriverServer will always be "remote" 813 | # we can't formally/technically support multiple file uploads yet, due to Selenium issue 2239 814 | # as the WebDriver/JSONWireProtocol spec doesn't define how to handle request/response 815 | # of multiple files uploaded. Therefore, we assume user upload single file for now 816 | result = "".join(extracted_files) 817 | app_response = {'sessionId': session_id, 818 | 'status': 0, 819 | 'value': result} 820 | return app_response 821 | 822 | def unzip(source_filename, dest_dir): 823 | import zipfile,os.path 824 | files_in_zip = [] 825 | with zipfile.ZipFile(source_filename) as zf: 826 | for member in zf.infolist(): 827 | words = member.filename.split('/') 828 | path = dest_dir 829 | for word in words[:-1]: 830 | drive, word = os.path.splitdrive(word) 831 | head, word = os.path.split(word) 832 | if word in (os.curdir, os.pardir, ''): continue 833 | path = os.path.join(path, word) 834 | zf.extract(member, path) 835 | unzipped_file = os.path.join(dest_dir,member.filename) 836 | print("Unzipped a file: ",unzipped_file) 837 | files_in_zip.append(unzipped_file) 838 | return files_in_zip 839 | 840 | @app.error(404) 841 | def unsupported_command(error): 842 | response.content_type = 'text/plain' 843 | return 'Unrecognized command, or AutoItDriverServer does not support/implement this: %s %s' % (request.method, request.path) 844 | 845 | def encode_value_4_wire(value): 846 | try: 847 | return urllib.parse.quote(base64.b64encode(value.encode("utf-8"))) 848 | except: 849 | return urllib.quote(base64.b64encode(value.encode("utf-8"))) 850 | 851 | def decode_value_from_wire(value): 852 | try: 853 | return base64.b64decode(urllib.parse.unquote(value)).decode("utf-8") 854 | except: 855 | return base64.b64decode(urllib.unquote(value)).decode("utf-8") 856 | 857 | if __name__ == '__main__': 858 | import argparse 859 | 860 | parser = argparse.ArgumentParser(description='AutoItDriverServer - a webdriver-compatible server for use with desktop GUI automation via AutoIt COM/DLL interface.') 861 | #parser.add_argument('-v', dest='verbose', action="store_true", default=False, help='verbose mode') 862 | parser.add_argument('-a', '--address', type=str, default=None, help='ip address to listen on') 863 | parser.add_argument('-p', '--port', type=int, default=4723, help='port to listen on') 864 | parser.add_argument('-c', '--autoit_options_file', type=str, default=None, help='config file defining the AutoIt options to use, see default sample config file in the app/server directory') 865 | 866 | args = parser.parse_args() 867 | 868 | if args.address is None: 869 | try: 870 | args.address = socket.gethostbyname(socket.gethostname()) 871 | except: 872 | args.address = '127.0.0.1' 873 | 874 | if args.autoit_options_file is not None: 875 | options_file = args.autoit_options_file 876 | else: 877 | options_file = os.path.join(os.path.curdir,'autoit_options.cfg') 878 | config = configparser.RawConfigParser() 879 | config.read(options_file) 880 | app.caretCoordMode = config.get("AutoIt Options",'CaretCoordMode') 881 | app.expandEnvStrings = config.get("AutoIt Options",'ExpandEnvStrings') 882 | app.mouseClickDelay = config.get("AutoIt Options",'MouseClickDelay') 883 | app.mouseClickDownDelay = config.get("AutoIt Options",'MouseClickDownDelay') 884 | app.mouseClickDragDelay = config.get("AutoIt Options",'MouseClickDragDelay') 885 | app.mouseCoordinateMode = config.get("AutoIt Options",'MouseCoordinateMode') 886 | app.sendAttachMode = config.get("AutoIt Options",'SendAttachMode') 887 | app.sendCapslockMode = config.get("AutoIt Options",'SendCapslockMode') 888 | app.sendKeyDelay = config.get("AutoIt Options",'SendKeyDelay') 889 | app.sendKeyDownDelay = config.get("AutoIt Options",'SendKeyDownDelay') 890 | app.winDetectHiddenText = config.get("AutoIt Options",'WinDetectHiddenText') 891 | app.winSearchChildren = config.get("AutoIt Options",'WinSearchChildren') 892 | app.winTextMatchMode = config.get("AutoIt Options",'WinTextMatchMode') 893 | app.winTitleMatchMode = config.get("AutoIt Options",'WinTitleMatchMode') 894 | app.winWaitDelay = config.get("AutoIt Options",'WinWaitDelay') 895 | 896 | # for win32com, if we choose to instantiate AutoIt COM connection here 897 | # instead of per session basis 898 | app.oAutoItX = win32com.client.Dispatch("AutoItX3.Control") 899 | 900 | app.SESSION_ID = "%s:%d" % (args.address, args.port) 901 | app.started = False 902 | run(app, host=args.address, port=args.port) -------------------------------------------------------------------------------- /sample-code/CalculatorTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.*; 2 | import org.openqa.selenium.*; 3 | import org.openqa.selenium.remote.DesiredCapabilities; 4 | import org.openqa.selenium.remote.RemoteWebDriver; 5 | import java.net.URL; 6 | 7 | public class CalculatorTest { 8 | 9 | public WebDriver driver; 10 | public DesiredCapabilities capabilities; 11 | 12 | @Before 13 | public void setUp() throws Exception { 14 | capabilities = new DesiredCapabilities(); 15 | capabilities.setCapability("browserName", "AutoIt"); 16 | driver = new RemoteWebDriver(new URL("http://localhost:4723/wd/hub" ), capabilities); 17 | } 18 | 19 | @After 20 | public void tearDown() throws Exception { 21 | driver.quit(); 22 | } 23 | 24 | @Test 25 | public void test() throws Exception{ 26 | // demo adapted from 27 | // http://www.joecolantonio.com/2014/07/02/selenium-autoit-how-to-automate-non-browser-based-functionality/ 28 | driver.get("calc.exe"); 29 | driver.switchTo().window("Calculator"); 30 | Thread.sleep(1000); 31 | driver.findElement(By.id("133")).click(); // 3 32 | Thread.sleep(1000); 33 | driver.findElement(By.id("93")).click(); // + 34 | Thread.sleep(1000); 35 | driver.findElement(By.id("133")).click(); // 3 36 | Thread.sleep(1000); 37 | driver.findElement(By.id("121")).click(); // = 38 | Thread.sleep(1000); 39 | Assert.assertEquals("3 + 3 did not produce 6 as expected.", "6", driver.findElement(By.id("150")).getText()); 40 | driver.findElement(By.id("81")).click(); // Clear "C" button 41 | Thread.sleep(1000); 42 | driver.close(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /sample-code/SeleniumIntegrationTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.*; 2 | import org.openqa.selenium.*; 3 | import org.openqa.selenium.firefox.*; 4 | import org.openqa.selenium.remote.*; 5 | import org.openqa.selenium.interactions.*; 6 | import java.net.URL; 7 | 8 | public class SeleniumIntegrationTest { 9 | 10 | public WebDriver webDriver, autoitDriver; 11 | public DesiredCapabilities autoItCapabilities; 12 | 13 | @Before 14 | public void setUp() throws Exception { 15 | webDriver = new FirefoxDriver(); 16 | autoItCapabilities = new DesiredCapabilities(); 17 | autoItCapabilities.setCapability("browserName", "AutoIt"); 18 | autoitDriver = new RemoteWebDriver(new URL("http://localhost:4723/wd/hub" ), autoItCapabilities); 19 | } 20 | 21 | @After 22 | public void tearDown() throws Exception { 23 | webDriver.quit(); 24 | autoitDriver.quit(); 25 | } 26 | 27 | @Test 28 | public void test() throws Exception { 29 | // ### HTTP authentication dialog popup demo ### 30 | webDriver.get("http://www.httpwatch.com/httpgallery/authentication/"); 31 | Thread.sleep(1000); 32 | // check state that img is "unauthenticated" at start 33 | String imgSrc = webDriver.findElement(By.id("downloadImg")).getAttribute("src"); 34 | Assert.assertTrue("HTTP demo test fail because test site not started with correct default unauthenticated state.",imgSrc.contains("/images/spacer.gif")); 35 | 36 | // now test authentication 37 | webDriver.findElement(By.id("displayImage")).click(); // trigger the popup 38 | Thread.sleep(5000); // wait for popup to appear 39 | autoitDriver.switchTo().window("Authentication Required"); 40 | new Actions(autoitDriver).sendKeys("httpwatch{TAB}AutoItDriverServerAndSeleniumIntegrationDemo{TAB}{ENTER}").build().perform(); 41 | Thread.sleep(5000); 42 | 43 | // now check img is authenticated or changed 44 | imgSrc = webDriver.findElement(By.id("downloadImg")).getAttribute("src"); 45 | Assert.assertFalse("HTTP demo failed, image didn't authenticate/change after logging in.",imgSrc.contains("/images/spacer.gif")); 46 | 47 | // ### file upload demo, also adapted from sample code of the test/target site ### 48 | webDriver.get("http://www.toolsqa.com/automation-practice-form"); 49 | // webDriver.findElement(By.id("photo")).click(); // this doesn't seem to trigger file upload to popup 50 | WebElement elem = webDriver.findElement(By.id("photo")); 51 | ((JavascriptExecutor) webDriver).executeScript("arguments[0].click();",elem); 52 | Thread.sleep(5000); // wait for file upload dialog to appear 53 | autoitDriver.switchTo().window("File Upload"); 54 | 55 | /* FYI, on FF, Opera, (Windows Safari) filename field control ID may be 1152 56 | * but on IE, Chrome, and Windows in general, should be control ID should be 1148 57 | */ 58 | //uncomment line below if want to transfer file from Selenium code execution machine A to remote node B before typing in actual file path at target node B 59 | //((RemoteWebDriver) autoitDriver).setFileDetector(new LocalFileDetector()); 60 | autoitDriver.findElement(By.className("Edit1")).sendKeys("C:\\ReplaceWith\\PathTo\\AnActualFileOnRemoteNode.txt"); 61 | Thread.sleep(2000); 62 | autoitDriver.findElement(By.className("Button1")).click(); 63 | //new Actions(autoitDriver).sendKeys("{ENTER}").perform(); // another option to "click" Open/Upload 64 | //new Actions(autoitDriver).sendKeys("!o").perform(); // another option to invoke Open/Upload via keyboard shortcut ALT + O 65 | Thread.sleep(5000); 66 | 67 | // execute an AutoIt script from WebDriver 68 | ((JavascriptExecutor) autoitDriver).executeScript("C:\\PathOnAutoItDriverServerHostMachineTo\\demo.au3","Hello","World"); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /sample-code/SeleniumIntegrationWithAutoItDriver.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from selenium import webdriver 3 | from selenium.webdriver import ActionChains 4 | import time 5 | 6 | wd = webdriver.Firefox() 7 | ad = webdriver.Remote( command_executor='http://127.0.0.1:4723/wd/hub', desired_capabilities={'browserName':'AutoIt'}) 8 | print("Desired Capabilities returned by server:\n") 9 | print(ad.desired_capabilities) 10 | print("") 11 | 12 | action1 = ActionChains(ad) 13 | 14 | ### HTTP authentication dialog popup demo ### 15 | wd.get("http://www.httpwatch.com/httpgallery/authentication/") 16 | time.sleep(1) 17 | # check state that img is "unauthenticated" at start 18 | img_src = wd.find_element_by_id("downloadImg").get_attribute("src") 19 | if not img_src.endswith("/images/spacer.gif"): 20 | print("HTTP demo test fail because test site not started with correct default unauthenticated state.") 21 | 22 | # now test authentication 23 | wd.find_element_by_id("displayImage").click() # trigger the popup 24 | time.sleep(5) # wait for popup to appear 25 | ad.switch_to_window("Authentication Required") 26 | action1.send_keys("httpwatch{TAB}AutoItDriverServerAndSeleniumIntegrationDemo{TAB}{ENTER}").perform() 27 | time.sleep(5) 28 | 29 | # now check img is authenticated or changed 30 | img_src = wd.find_element_by_id("downloadImg").get_attribute("src") 31 | if img_src.endswith("/images/spacer.gif"): 32 | print("HTTP demo failed, image didn't authenticate/change after logging in.") 33 | 34 | ### file upload demo, also adapted from sample code of the test/target site ### 35 | wd.get("http://www.toolsqa.com/automation-practice-form") 36 | # wd.find_element_by_id("photo").click() # this doesn't seem to trigger file upload to popup 37 | elem = wd.find_element_by_id("photo") 38 | wd.execute_script("arguments[0].click();",elem) 39 | time.sleep(10) # wait for file upload dialog to appear 40 | ad.switch_to_window("File Upload") 41 | 42 | # FYI, on FF, Opera, (Windows Safari) filename field control ID may be 1152 43 | # but on IE, Chrome, and Windows in general, should be control ID should be 1148 44 | 45 | ad.find_element_by_class_name("Edit1").send_keys("C:\\ReplaceWith\\PathTo\\AnActualFile.txt") 46 | # due to remote file upload (via local file detector), you may notice actual path typed 47 | # may differ from what you fill in above, but that path is still valid for the original file 48 | # it's just a copy in a temp directory. This is the nature of file uploads over RemoteWebDriver 49 | # instead of a local driver. 50 | time.sleep(2) 51 | ad.find_element_by_class_name("Button1").click() 52 | #actions.send_keys("{ENTER}").perform() # another option to "click" Open/Upload 53 | #actions.send_keys("!o").perform() # another option to invoke Open/Upload via keyboard shortcut ALT + O 54 | time.sleep(5) 55 | 56 | ### handle browser's print dialog popup demo? to come... ### 57 | # Print dialog popup may hang Selenium code, 58 | # so may want to fire off a separate thread for AutoIt(DriverServer) 59 | # to monitor for popup when it shows up (i.e. Selenium then hung) 60 | # and have AutoIt print or cancel the popup to then resume Selenium main thread code 61 | 62 | # file download popup handling demo? to come... 63 | 64 | # handle Adobe Acrobat PDF loaded inside browser demo? e.g. click save, print, etc. to come... 65 | 66 | # handle Flash or embedded streaming video controls demo? e.g. play, stop, etc. to come... 67 | 68 | wd.quit() 69 | ad.quit() -------------------------------------------------------------------------------- /sample-code/calculator.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from selenium import webdriver 3 | from selenium.webdriver import ActionChains 4 | import time 5 | 6 | driver = webdriver.Remote( command_executor='http://127.0.0.1:4723/wd/hub', desired_capabilities={'browserName':'AutoIt'}) 7 | print("Desired Capabilities returned by server:\n") 8 | print(driver.desired_capabilities) 9 | print("") 10 | 11 | # demo adapted from 12 | # http://www.joecolantonio.com/2014/07/02/selenium-autoit-how-to-automate-non-browser-based-functionality/ 13 | driver.get("calc.exe") 14 | driver.switch_to_window("Calculator") 15 | time.sleep(1) 16 | driver.find_element_by_id("133").click() # 3 17 | time.sleep(1) 18 | driver.find_element_by_id("93").click() # + 19 | time.sleep(1) 20 | driver.find_element_by_id("133").click() # 3 21 | time.sleep(1) 22 | driver.find_element_by_id("121").click() # = 23 | time.sleep(1) 24 | if driver.find_element_by_id("150").text != "6": 25 | print("3 + 3 did not produce 6 as expected.") 26 | driver.find_element_by_id("81").click() # Clear "C" button 27 | time.sleep(1) 28 | 29 | # demo adapted from AutoItX VBScript example that comes with AutoIt installation 30 | action1 = ActionChains(driver) 31 | action2 = ActionChains(driver) 32 | action3 = ActionChains(driver) 33 | action1.send_keys("2*2=").perform() 34 | time.sleep(1) 35 | if driver.find_element_by_id("150").text != "4": 36 | print("2 x 2 did not produce 4 as expected.") 37 | driver.find_element_by_id("81").click() # Clear "C" button 38 | time.sleep(1) 39 | action2.send_keys("4*4=").perform() 40 | time.sleep(1) 41 | if driver.find_element_by_id("150").text != "16": 42 | print("4 x 4 did not produce 16 as expected.") 43 | driver.find_element_by_id("81").click() # Clear "C" button 44 | time.sleep(1) 45 | action3.send_keys("8*8=").perform() 46 | time.sleep(1) 47 | if driver.find_element_by_id("150").text != "64": 48 | print("8 x 8 did not produce 64 as expected.") 49 | driver.find_element_by_id("81").click() # Clear "C" button 50 | time.sleep(1) 51 | driver.close() 52 | driver.quit() -------------------------------------------------------------------------------- /sample-code/demo.au3: -------------------------------------------------------------------------------- 1 | Local $myArgs = "" 2 | If $CmdLine[0] > 0 Then 3 | For $i = 1 To $CmdLine[0] Step 1 4 | $myArgs &= " " & $CmdLine[$i] 5 | Next 6 | EndIf 7 | MsgBox(64, "Demo", "An AutoIt au3 script was ran, with arguments (if any) of: " & $myArgs) -------------------------------------------------------------------------------- /sample-code/notepad.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from selenium import webdriver 3 | from selenium.webdriver import ActionChains 4 | import time 5 | 6 | driver = webdriver.Remote( command_executor='http://127.0.0.1:4723/wd/hub', desired_capabilities={'browserName':'AutoIt'}) 7 | print("Desired Capabilities returned by server:\n") 8 | print(driver.desired_capabilities) 9 | print("") 10 | 11 | # demo adapted from AutoItX VBScript example that comes with AutoIt installation 12 | driver.get("notepad.exe") 13 | driver.switch_to_window("Untitled - Notepad") 14 | time.sleep(1) 15 | action1 = ActionChains(driver) 16 | action2 = ActionChains(driver) 17 | action3 = ActionChains(driver) 18 | action4 = ActionChains(driver) 19 | action1.send_keys("Hello, this is line 1{ENTER}").perform() 20 | time.sleep(1) 21 | action2.send_keys("This is line 2{ENTER}This is line 3").perform() 22 | time.sleep(1) 23 | action3.send_keys("!{F4}").perform() 24 | time.sleep(1) 25 | action4.send_keys("!n").perform() 26 | driver.quit() -------------------------------------------------------------------------------- /sample-code/run_au3_script_demo.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from selenium import webdriver 3 | import os 4 | 5 | driver = webdriver.Remote( command_executor='http://127.0.0.1:4723/wd/hub', desired_capabilities={'browserName':'AutoIt'}) 6 | print("Desired Capabilities returned by server:\n") 7 | print(driver.desired_capabilities) 8 | print("") 9 | 10 | # execute an AutoIt script file (rather than call specific AutoItX API commands) 11 | # supply path to AutoIt script file followed by optional arguments 12 | driver.execute_script("C:\\PathOnAutoItDriverServerHostMachineTo\\demo.au3","Hello","World") 13 | 14 | # or if using compiled binary option 15 | # (but need to set AutoItScriptExecuteScriptAsCompiledBinary to True in autoit_options.cfg first 16 | # before starting the server). 17 | #driver.execute_script("C:\\PathOnAutoItDriverServerHostMachineTo\\demo.exe","Hello","World") 18 | 19 | driver.quit() --------------------------------------------------------------------------------