def download_file_and_push(self, bundle_id, file_url, remotepath): '''支持URL上传文件到IOS设备sandbox的指定目录 :param bundle_id: app的bundle id :type bundle_id: str :parameter file_url 文件下载链接 :type file_url: str :param remotepath: sandbox上的目录,例如:/Library/Caches/test/ :type remotepath: str :returns: boolean ''' filename = os.path.basename(file_url) if not os.path.exists(TMP_LOAD_FILE): os.mkdir(TMP_LOAD_FILE) localpath = os.path.join(TMP_LOAD_FILE, filename) flag = False try: r = urlopen(file_url) with open(localpath, "wb") as wd: while True: s = r.read(1024*1024*10) if not s: break wd.write(s) flag = self.push_file(bundle_id, localpath, remotepath) except Exception: logger.get_logger("xctest_%s" % self.udid).exception("download_file_and_push") finally: if os.path.exists(localpath): os.remove(localpath) return flag
def stop_all_agents(cls): '''关闭全部Agent ''' for device_id in cls._agents: cls._agents[device_id].stop() cls._port_manager.del_port('agent', device_id) logger.get_logger("xctest_%s" % device_id).info('stop_agent') cls._agents = {}
def __init__(self, bundle_id, udid=None): self.app_bundle_id = bundle_id self.udid = udid if self.udid: self.logger = get_logger("driverserver_%s" % self.udid) else: self.logger = get_logger() self.seq = 0 # webkit调试命令的序号 self.conn_id = str(uuid.uuid1()).upper() self.sender_key = str(uuid.uuid4()).upper() self.retry = 5 # 初始化消息的重试次数 self.msgbuf = [] self.cached_pages = [] self.app_id = None self.page_id = None self.host_app_ids = []
def __init__(self, udid, listen_port): self.logger = logger.get_logger('driverserver_%s' % udid) self._udid = udid screen_producer = ScreenProducer(udid, self.logger) screen_producer.start() server = ThreadedTCPServer(('localhost', listen_port), ScreenConsumer) server.screen_producer = screen_producer server.serve_forever()
def do_POST(self): '''处理HTTP的POST请求 ''' if not self.is_rpc_path_valid(): self.report_404() return try: max_chunk_size = 10 * 1024 * 1024 size_remaining = int(self.headers["content-length"]) L = [] while size_remaining: chunk_size = min(size_remaining, max_chunk_size) L.append(self.rfile.read(chunk_size)) size_remaining -= len(L[-1]) if PY2: data = ''.join(L) else: data = b''.join(L) response = self.server._marshaled_dispatch( data, getattr(self, '_dispatch', None), self.path) self.send_response(200) except Exception: response = Fault().response() self.send_response(500, response) logger.get_logger().exception("ProtocolError:%s" % response) if response is None: response = '' self.send_header("Content-Type", "application/json-rpc") if self.encode_threshold is not None: if len(response) > self.encode_threshold: q = self.accept_encodings().get("gzip", 0) if q: try: if six.PY3 and isinstance(response, six.string_types): response = response.encode('utf-8') response = xmlrpc_client.gzip_encode(response) self.send_header("Content-Encoding", "gzip") except NotImplementedError: pass self.send_header("Content-length", str(len(response))) self.end_headers() if six.PY3 and isinstance(response, six.string_types): response = response.encode('utf-8') self.wfile.write(response)
def __init__(self, device_id, server_ip=DEFAULT_IP, server_port=DEFAULT_PORT, keep_alive=False, retry=3, timeout=60): self.log_name = "xctest_%s" % device_id self.log = logger.get_logger(self.log_name) self.version = '0.0.0' version_file = os.path.join(os.path.expanduser('~'), 'XCTestAgent', 'version.txt') if os.path.exists(version_file): with open(version_file, 'r') as f: self.version = f.read() # XCTestAgent工程默认路径:~/XCTestAent/XCTestAgent.xcodeproj self.XCTestAgentPath = os.path.join(os.path.expanduser('~'), "XCTestAgent", "XCTestAgent.xcodeproj") if not os.path.exists(self.XCTestAgentPath): msg = 'XCTestAgent does not exist. Please use command line: "python manage.py qt4i.setup" to setup XCTestAgent.xcodeproj' self.log.error(msg) raise Exception(msg) self.udid = device_id self._server_ip = server_ip self._server_port = server_port self._server_url = 'http://%s:%d' % (server_ip, server_port) self.type = EnumDevice.Simulator if dt.DT().is_simulator( device_id) else EnumDevice.Device self._is_remote = True self.session_id = None self.crash_flag = False self.capabilities = {} self.mjpeg_port = 9100 + self._server_port - 8100 self.stub_remote_port = 18123 self.stub_port = 18123 + self._server_port - 8100 self.stub_server_url = 'http://%s:%d' % (DEFAULT_IP, self.stub_port) self.stub_client = RemoteConnection(self.stub_server_url, keep_alive=False, logger_name=self.log_name) self.stub_client.set_timeout(0.01) self.error_handler = ErrorHandler() self._command_executor = RemoteConnection(self._server_url, keep_alive=keep_alive, logger_name=self.log_name) self._is_relay_quit = threading.Event() self.start(retry, timeout)
def install(self, _file_path, _device_udid=None): '''通过udid指定真机安装IPA或模拟器安装APP(注意:真机所用IPA不能在模拟器上安装和使用,IPA与APP相互不兼容) :param _file_path: IPA或APP安装包的路径(注意:当前Mac机必须能访问到该路径) :type _file_path: str :param _device_udid: 设备的udid(当udid为None时,IPA包则安装到第一台连接上的真机上,APP包则安装到默认模拟器上) :type _device_udid: str :returns: bool ''' self.install_error = "" from qt4i.driver.tools import logger log = logger.get_logger("task") if not _file_path: return True pkgcachedir = os.path.join(os.environ['HOME'],'pkg_cache') if not os.path.isdir(pkgcachedir): os.mkdir(pkgcachedir) pkgcachedir = os.path.join(pkgcachedir, str(_device_udid)) file_path = self._prepare_package(_file_path, pkgcachedir) if os.path.exists(file_path) == False : self.install_error = 'file does not exist' return False if file_path.endswith("ipa"): if self.is_simulator(_device_udid): log.warn('Failed to install an ipa to a simulator.') return False ip = InstallationProxy(_device_udid) ret = ip.install(file_path) else: if not self.is_simulator(_device_udid): log.warn('Failed to install an app to a real device.') return False self.start_simulator(_device_udid) if _device_udid is None: _device_udid = "booted" ret = self._install_for_simulator(_device_udid, file_path) if ret[0]: if os.path.exists(pkgcachedir): shutil.rmtree(pkgcachedir) else: log.debug('install error: %s' % str(ret[1])) self.install_error = ret[1] return ret[0]
def __init__(self, bundle_id, udid): self.app_bundle_id = bundle_id self.udid = udid self.logger = get_logger("driverserver_%s" % self.udid) self.seq = 0 # webkit调试命令的序号 self.conn_id = str(uuid.uuid1()).upper() self.sender_key = str(uuid.uuid4()).upper() self.retry = 5 # 初始化消息的重试次数 self.msgbuf = [] self.cached_pages = [] self.app_id = None self.page_id = None self.host_app_ids = [] self.target_id = None self._ios_version = DT().get_device_by_udid(self.udid)['ios'] self._is_target_domain = DT.compare_version(self._ios_version, '12.2') >= 0 self.is_target_wrapped = False
def start_agent(self, device_id, server_ip=DEFAULT_IP, server_port=DEFAULT_PORT, keep_alive=False, retry=3, timeout=60): '''启动Agent并返回 :param device_id: 设备ID :type device_id: str :param server_ip: 设备IP地址 :type server_ip: str :param server_port: 设备端口号 :type server_port: int :param keep_alive: HTTP远程连接是否使用keep-alive,默认为False :type keep_alive: boolean :param retry: 启动尝试次数,默认3次 :type retry: int :param timeout: 单次启动超时 (秒) :type timeout: int :returns: XCUITestAgent ''' self.log = logger.get_logger("xctest_%s" % device_id) if device_id not in self._agents: try: self._port_manager.set_port('agent', device_id, server_port) server_port = self._port_manager.get_port('agent', device_id) self.log.info('start_agent, port: %d' % server_port) with self._lock: self._agents[device_id] = XCUITestAgent( device_id, server_ip, server_port, keep_alive, retry, timeout) except: self.log.exception('start_agent') self._port_manager.del_port('agent', device_id) raise return self._agents[device_id]
# # https://opensource.org/licenses/BSD-3-Clause # # Unless required by applicable law or agreed to in writing, software distributed # under the License is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS # OF ANY KIND, either express or implied. See the License for the specific language # governing permissions and limitations under the License. # '''命令''' # -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- from __future__ import absolute_import, print_function import time, threading from qt4i.driver.tools import logger # -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- log = logger.get_logger("instruments") class __DeviceCommandId__(threading.Thread): Id = 0 Lock = threading.Lock() def __init__(self): threading.Thread.__init__(self) self._id = None def get_id(self): return self._id def run(self):
def _marshaled_dispatch(self, data, dispatch_method=None, path=None): origin_path = path path = path[1:] #remove / if not path.endswith('/'): path += '/' try: request = json.loads(data) except ValueError: fault = Fault(-32700, 'JSON parsing error') return fault.response() if not isinstance(request, dict): fault = Fault(-32600, 'Invalid request data type') return fault.response() rpcid = request.get('id', None) method = request.get('method', None) params = request.get('params', []) if not method or type(method) not in types.StringTypes or \ type(params) not in (types.ListType, types.DictType, types.TupleType): return Fault(-32600, 'Invalid request method or parameters').response() tried_enpoint_clss = [] for url_pattern, method_pattern, endpoint_cls in self._dispatcher_patterns: m = url_pattern.match(path) if m: try: log_suffix = m.group(1) log = logger.get_logger("driverserver_%s" % log_suffix) except: log = logger.get_logger() tried_enpoint_clss.append(endpoint_cls) if (method_pattern is None) or method_pattern.match(method): endpoint_params = m.groupdict() try: endpoint = endpoint_cls(self, **endpoint_params) except: fault = Fault() log.exception(fault.error()) return fault.response() else: break else: if not tried_enpoint_clss: return Fault(-32601, "invalid URL: \"%s\"" % origin_path).response() else: return Fault( -32601, "invalid method: \"%s\", no matched end point, end point: %s tried" % (method, ", ".join( ['"%s"' % it.__name__ for it in tried_enpoint_clss]))).response() try: log.debug('--- --- --- --- --- ---') log.debug('%s <<< %s' % (method, params)) response = endpoint._dispatch(method, params) if method in self.LOG_FILTERED_METHODS: log.debug('%s >>> done' % method) else: log.debug('%s >>> %s' % (method, response)) # wrap response in a singleton tuple response = (response, ) response = json.dumps({ "jsonrpc": "2.0", "result": response, "id": rpcid }) except: fault = Fault() response = fault.response() log.error('%s >>> %s' % (method, fault.error())) log.exception(fault.error()) return response
def __init__(self, rpc_server, device_id): self.udid = device_id self.logger = get_logger("driverserver_%s" % device_id) RPCEndpoint.__init__(self)
import os import signal import sys import time from six import string_types from qt4i.driver.rpc import SimpleJSONRPCServer from qt4i.driver.tools import logger as logging from qt4i.driver.util import Process DEFAULT_IP = '0.0.0.0' DEFAULT_PORT = 12306 DEFAULT_AGENT_PORT = 8100 DEFAULT_PID_FILE = '/tmp/driverserver.pid' logger = logging.get_logger() class ArgumentParser(argparse.ArgumentParser): '''Argument parser, automatically display help while error ''' def error(self, message): sys.stderr.write('error: %s\n' % message) self.print_help() sys.exit(2) CMD_HELP = { "start": "start driver server daemon", "stop": "stop driver server daemon", "restart": "stop driver server daemon and start it again",
def __init__(self, device_id): self.log = logger.get_logger("instruments") self.udid = device_id if self.udid not in self.instances: self.instances[self.udid] = self self.crash_flag = False
def __init__(self, **args): # self.logger = Logger(os.path.join('/tmp', 'instruments.log')) self.logger = logger.get_logger("instruments") self.args = args if self.args.get('device_udid') is None: raise Exception('device_udid is None.') if self.args.get('device_udid') is None: raise Exception('bundle_id is None.') self.device_udid = self.args['device_udid'] self.environment = { 'device_udid': self.args['device_udid'], 'device_simulator': self.args.get('device_simulator', False), 'bundle_id': self.args['bundle_id'], 'trace_template': self.args.get('trace_template'), 'trace_output': self.args.get('trace_output'), 'uia_script': os.path.abspath( self.args.get( 'uia_script', os.path.join(self.CurrentDirectoryPath, '_bootstrap.js'))), 'uia_results_path': os.path.join('/tmp', '_uiaresults'), 'screen_shot_path': os.path.join('/tmp', '_screenshot'), 'cwd': self.CurrentDirectoryPath, 'timeout': self.args.get('timeout', 20 * 60), 'xmlrpc_uri': self.args.get( 'xmlrpc_uri', '/'.join([ 'http://0.0.0.0:12306', 'device', '%s' % self.device_udid ])), 'cmd_fetch_delegate': os.path.join(self.CurrentDirectoryPath, '_cmd_fetch_delegate.py'), 'cmd_fetch_delegate_timeout': self.args.get('cmd_fetch_delegate_timeout', 10), 'ignore_cmd_error': self.args.get('ignore_cmd_error', True), 'python_path': self.PythonPath } self.environment.update( eval(urllib.unquote(self.args.get('environment')))) self.args.update(self.environment) self.args.update({ 'started_callback': self.__started_callback__, 'stdout_line_callback': self.__stdout_line_callback__, 'stderr_line_callback': self.__stderr_line_callback__, 'return_code_callback': self.__return_code_callback__ }) self._clean_instrumentscli_n_trace( self.environment.get('trace_output')) self._renew_output_path() self._update_environment_file() self.out = list() self.err = list() self.rpc = RPCClientProxy(self.environment.get('xmlrpc_uri'), allow_none=True) self.instruments = Xcode().start_instruments(**self.args) self.logger.info(self.instruments.command) self.instruments_uia_result_path = None self.logger.info("Instruments timeout %d" % self.environment.get('timeout')) self.instruments_timeout_daemon = ThreadTimer( timeout=self.environment.get('timeout'), callback=self.__timeout_kill_instruments__) self.instruments_pid = self.instruments.pid self.instruments_trace_complete = False self.instruments.wait()
def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=True, logger_name='RemoteConnection'): # Attempt to resolve the hostname and get an IP address. self.logger = logging.get_logger(logger_name) self.keep_alive = keep_alive parsed_url = parse.urlparse(remote_server_addr) addr = "" if parsed_url.hostname and resolve_ip: try: netloc = socket.gethostbyname(parsed_url.hostname) addr = netloc if parsed_url.port: netloc += ':%d' % parsed_url.port if parsed_url.username: auth = parsed_url.username if parsed_url.password: auth += ':%s' % parsed_url.password netloc = '%s@%s' % (auth, netloc) remote_server_addr = parse.urlunparse( (parsed_url.scheme, netloc, parsed_url.path, parsed_url.params, parsed_url.query, parsed_url.fragment)) except socket.gaierror: self.logger.info('Could not get IP address for host: %s' % parsed_url.hostname) self._url = remote_server_addr if keep_alive: self._conn = httplib.HTTPConnection( str(addr), str(parsed_url.port), timeout=self._timeout) self._commands = { Command.HEALTH: ('GET', '/health'), Command.DEACTIVATE_APP: ('POST', '/session/$sessionId/wda/deactivateApp'), Command.STATUS: ('GET', '/status'), Command.SESSION: ('POST', '/session'), Command.GET_ALL_SESSIONS: ('GET', '/sessions'), Command.QUIT: ('DELETE', '/session/$sessionId'), Command.GET: ('POST', '/session/$sessionId/url'), Command.EXECUTE_SCRIPT: ('POST', '/session/$sessionId/execute'), Command.GET_CURRENT_URL: ('GET', '/session/$sessionId/url'), Command.GET_TITLE: ('GET', '/session/$sessionId/title'), Command.GET_PAGE_SOURCE: ('GET', '/session/$sessionId/source'), Command.SCREENSHOT: ('GET', '/screenshot'), Command.ELEMENT_SCREENSHOT: ('GET', '/session/$sessionId/screenshot/$id'), Command.FIND_ELEMENT: ('POST', '/session/$sessionId/element'), Command.FIND_ELEMENTS: ('POST', '/session/$sessionId/elements'), Command.GET_ACTIVE_ELEMENT: ('POST', '/session/$sessionId/element/active'), Command.FIND_CHILD_ELEMENT: ('POST', '/session/$sessionId/element/$id/element'), Command.FIND_CHILD_ELEMENTS: ('POST', '/session/$sessionId/element/$id/elements'), Command.CLICK_ELEMENT: ('POST', '/session/$sessionId/element/$id/click'), Command.CLEAR_ELEMENT: ('POST', '/session/$sessionId/element/$id/clear'), Command.SUBMIT_ELEMENT: ('POST', '/session/$sessionId/element/$id/submit'), Command.GET_ELEMENT_TREE: ('GET', '/session/$sessionId/tree'), Command.GET_ELEMENT_TEXT: ('GET', '/session/$sessionId/element/$id/text'), Command.SEND_KEYS_TO_ELEMENT: ('POST', '/session/$sessionId/element/$id/value'), Command.SEND_KEYS_TO_ACTIVE_ELEMENT: ('POST', '/session/$sessionId/keys'), Command.GET_ELEMENT_VALUE: ('GET', '/session/$sessionId/element/$id/value'), Command.GET_ELEMENT_TAG_NAME: ('GET', '/session/$sessionId/element/$id/name'), Command.IS_ELEMENT_SELECTED: ('GET', '/session/$sessionId/element/$id/selected'), Command.SET_ELEMENT_SELECTED: ('POST', '/session/$sessionId/element/$id/selected'), Command.IS_ELEMENT_ENABLED: ('GET', '/session/$sessionId/element/$id/enabled'), Command.IS_ELEMENT_DISPLAYED: ('GET', '/session/$sessionId/element/$id/displayed'), Command.GET_ELEMENT_LOCATION: ('GET', '/session/$sessionId/element/$id/location'), Command.GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW: ('GET', '/session/$sessionId/element/$id/location_in_view'), Command.GET_ELEMENT_SIZE: ('GET', '/session/$sessionId/element/$id/size'), Command.GET_ELEMENT_RECT: ('GET', '/session/$sessionId/element/$id/rect'), Command.GET_ELEMENT_ATTRIBUTE: ('GET', '/session/$sessionId/element/$id/attribute/$name'), Command.ELEMENT_EQUALS: ('GET', '/session/$sessionId/element/$id/equals/$other'), Command.SWITCH_TO_FRAME: ('POST', '/session/$sessionId/frame'), Command.SWITCH_TO_PARENT_FRAME: ('POST', '/session/$sessionId/frame/parent'), Command.SWITCH_TO_WINDOW: ('POST', '/session/$sessionId/window'), Command.CLOSE: ('DELETE', '/session/$sessionId/window'), Command.IMPLICIT_WAIT: ('POST', '/session/$sessionId/timeouts/implicit_wait'), Command.EXECUTE_ASYNC_SCRIPT: ('POST', '/session/$sessionId/execute_async'), Command.SET_SCRIPT_TIMEOUT: ('POST', '/session/$sessionId/timeouts/async_script'), Command.SET_TIMEOUTS: ('POST', '/session/$sessionId/timeouts'), Command.DISMISS_ALERT: ('POST', '/session/$sessionId/dismiss_alert'), Command.ACCEPT_ALERT: ('POST', '/session/$sessionId/accept_alert'), Command.SET_ALERT_VALUE: ('POST', '/session/$sessionId/alert_text'), Command.GET_ALERT_TEXT: ('GET', '/session/$sessionId/alert_text'), Command.SET_ALERT_CREDENTIALS: ('POST', '/session/$sessionId/alert/credentials'), Command.CLICK: ('POST', '/session/$sessionId/click'), Command.DOUBLE_CLICK: ('POST', '/session/$sessionId/doubleclick'), Command.MOUSE_DOWN: ('POST', '/session/$sessionId/buttondown'), Command.MOUSE_UP: ('POST', '/session/$sessionId/buttonup'), Command.MOVE_TO: ('POST', '/session/$sessionId/moveto'), Command.GET_WINDOW_SIZE: ('GET', '/session/$sessionId/window/size'), Command.SET_WINDOW_SIZE: ('POST', '/session/$sessionId/window/$windowHandle/size'), Command.GET_WINDOW_POSITION: ('GET', '/session/$sessionId/window/$windowHandle/position'), Command.SET_WINDOW_POSITION: ('POST', '/session/$sessionId/window/$windowHandle/position'), Command.MAXIMIZE_WINDOW: ('POST', '/session/$sessionId/window/$windowHandle/maximize'), Command.SET_SCREEN_ORIENTATION: ('POST', '/session/$sessionId/orientation'), Command.GET_SCREEN_ORIENTATION: ('GET', '/session/$sessionId/orientation'), Command.SINGLE_TAP: ('POST', '/session/$sessionId/touch/click'), Command.TOUCH_DOWN: ('POST', '/session/$sessionId/touch/down'), Command.TOUCH_UP: ('POST', '/session/$sessionId/touch/up'), Command.TOUCH_MOVE: ('POST', '/session/$sessionId/touch/move'), Command.TOUCH_SCROLL: ('POST', '/session/$sessionId/touch/scroll'), Command.DOUBLE_TAP: ('POST', '/session/$sessionId/touch/doubleclick'), Command.LONG_PRESS: ('POST', '/session/$sessionId/touch/longclick'), Command.FLICK: ('POST', '/session/$sessionId/touch/flick'), Command.EXECUTE_SQL: ('POST', '/session/$sessionId/execute_sql'), Command.GET_LOCATION: ('GET', '/session/$sessionId/location'), Command.SET_LOCATION: ('POST', '/session/$sessionId/location'), Command.GET_APP_CACHE: ('GET', '/session/$sessionId/application_cache'), Command.GET_APP_CACHE_STATUS: ('GET', '/session/$sessionId/application_cache/status'), Command.CLEAR_APP_CACHE: ('DELETE', '/session/$sessionId/application_cache/clear'), Command.GET_NETWORK_CONNECTION: ('GET', '/session/$sessionId/network_connection'), Command.SET_NETWORK_CONNECTION: ('POST', '/session/$sessionId/network_connection'), Command.GET_LOCAL_STORAGE_ITEM: ('GET', '/session/$sessionId/local_storage/key/$key'), Command.REMOVE_LOCAL_STORAGE_ITEM: ('DELETE', '/session/$sessionId/local_storage/key/$key'), Command.GET_LOCAL_STORAGE_KEYS: ('GET', '/session/$sessionId/local_storage'), Command.SET_LOCAL_STORAGE_ITEM: ('POST', '/session/$sessionId/local_storage'), Command.CLEAR_LOCAL_STORAGE: ('DELETE', '/session/$sessionId/local_storage'), Command.GET_LOCAL_STORAGE_SIZE: ('GET', '/session/$sessionId/local_storage/size'), Command.GET_SESSION_STORAGE_ITEM: ('GET', '/session/$sessionId/session_storage/key/$key'), Command.REMOVE_SESSION_STORAGE_ITEM: ('DELETE', '/session/$sessionId/session_storage/key/$key'), Command.GET_SESSION_STORAGE_KEYS: ('GET', '/session/$sessionId/session_storage'), Command.SET_SESSION_STORAGE_ITEM: ('POST', '/session/$sessionId/session_storage'), Command.CLEAR_SESSION_STORAGE: ('DELETE', '/session/$sessionId/session_storage'), Command.GET_SESSION_STORAGE_SIZE: ('GET', '/session/$sessionId/session_storage/size'), Command.CURRENT_CONTEXT_HANDLE: ('GET', '/session/$sessionId/context'), Command.CONTEXT_HANDLES: ('GET', '/session/$sessionId/contexts'), Command.SWITCH_TO_CONTEXT: ('POST', '/session/$sessionId/context'), Command.QTA_FIND_ELEMENTS: ('POST', '/session/$sessionId/qta/element/$id/elements'), Command.QTA_DEVICE_CLICK: ('POST', '/session/$sessionId/qta/click'), Command.QTA_ELEMENT_CLICK: ('POST', '/session/$sessionId/qta/element/$id/click'), Command.QTA_DEVICE_DOUBLE_CLICK: ('POST', '/session/$sessionId/qta/doubleclick'), Command.QTA_ELEMENT_DOUBLE_CLICK: ('POST', '/session/$sessionId/wda/element/$id/doubleTap'), Command.QTA_DEVICE_LONG_CLICK: ('POST', '/session/$sessionId/qta/longclick'), Command.QTA_ELEMENT_LONG_CLICK: ('POST', '/session/$sessionId/wda/element/$id/touchAndHold'), Command.QTA_DEVICE_SENDKEYS: ('POST', '/session/$sessionId/qta/sendkeys'), Command.QTA_ELEMENT_SENDKEYS: ('POST', '/session/$sessionId/qta/element/$id/sendkeys'), Command.QTA_DEVICE_DRAG: ('POST', '/session/$sessionId/qta/drag'), Command.QTA_ELEMENT_DRAG: ('POST', '/session/$sessionId/qta/element/$id/drag'), Command.QTA_GET_PARENT_ELEMENT: ('POST', '/session/$sessionId/qta/element/$id/parent'), Command.QTA_GET_CHILDREN_ELEMENTS: ('POST', '/session/$sessionId/qta/element/$id/children'), Command.QTA_SCROLL_TO_VISIBLE: ('POST', '/session/$sessionId/qta/element/$id/scroll'), Command.QTA_DRAG_TO_VALUE: ('POST', '/session/$sessionId/qta/element/$id/slider'), Command.QTA_GET_ELEMENT_ATTRS: ('POST', '/session/$sessionId/qta/element/$id/attrs'), Command.QTA_ALERT_RULES_UPDATE: ('POST', '/qta/alertrules/update'), Command.QTA_ELEMENT_SETVALUE: ('POST', '/session/$sessionId/qta/element/$id/value'), Command.QTA_ELEMENT_TREE: ('POST', '/session/$sessionId/qta/element/$id/tree'), Command.QTA_STOP_AGENT: ('POST', '/qta/stop'), Command.QTA_GET_FOREGROUND_APP_NAME: ('GET', '/qta/appName'), Command.QTA_GET_FOREGROUND_APP_PID: ('GET', '/qta/appPid'), Command.QTA_GET_ELEMENT_WIN_INFO: ('GET', '/session/$sessionId/qta/element/$id/wininfo'), Command.QTA_ALERT_DISMISS: ('POST', '/qta/alert/dismiss'), Command.QTA_DEVICE_LOCK: ('POST', '/qta/device/lock'), Command.QTA_DEVICE_UNLOCK: ('POST', '/qta/device/unlock'), Command.QTA_SANDBOX_LIST: ('POST', '/qta/sandbox/list'), Command.QTA_SANDBOX_REMOVE: ('POST', '/qta/sandbox/remove'), Command.QTA_ALBUM_UPLOAD: ('POST', '/qta/album/upload'), Command.QTA_STUB_CALL: ('POST', '/qta/stub'), Command.QTA_DEVICE_VOLUME: ('POST', '/qta/device/volume'), Command.QTA_DEVICE_SIRI: ('POST', '/qta/device/siri'), Command.QTA_DEVICE_SCREEN_DIRECTION: ('POST', '/qta/device/screenDirection'), Command.QTA_DEVICE_DETAIL_INFO: ('POST', '/qta/device/detailInfo'), Command.QTA_WHEEL_SELECT: ('POST', '/session/$sessionId/qta/element/$id/wheel/select'), }