2323from appium .webdriver .common .multi_action import MultiAction
2424
2525from selenium .webdriver .common .by import By
26+ from selenium .webdriver .remote .webelement import WebElement
2627
2728class WebDriver (webdriver .Remote ):
2829 def __init__ (self , command_executor = 'http://127.0.0.1:4444/wd/hub' ,
@@ -41,6 +42,9 @@ def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
4142 By .ANDROID_UIAUTOMATOR = MobileBy .ANDROID_UIAUTOMATOR
4243 By .ACCESSIBILITY_ID = MobileBy .ACCESSIBILITY_ID
4344
45+ # add methods to the WebElement class
46+ WebElement .set_value = set_value
47+
4448 @property
4549 def contexts (self ):
4650 """
@@ -109,7 +113,8 @@ def find_element_by_accessibility_id(self, id):
109113 """Finds an element by accessibility id.
110114
111115 :Args:
112- - id - a string corresponding to a recursive element search using the Id/Name that the native Accessibility options utilize
116+ - id - a string corresponding to a recursive element search using the
117+ Id/Name that the native Accessibility options utilize
113118
4FA7
114119 :Usage:
115120 driver.find_element_by_accessibility_id()
@@ -120,14 +125,14 @@ def find_elements_by_accessibility_id(self, id):
120125 """Finds elements by accessibility id.
121126
122127 :Args:
123- - id - a string corresponding to a recursive element search using the Id/Name that the native Accessibility options utilize
128+ - id - a string corresponding to a recursive element search using the
129+ Id/Name that the native Accessibility options utilize
124130
125131 :Usage:
126132 driver.find_elements_by_accessibility_id()
127133 """
128134 return self .find_elements (by = By .ACCESSIBILITY_ID , value = id )
129135
130-
131136 # convenience method added to Appium (NOT Selenium 3)
132137 def scroll (self , originEl , destinationEl ):
133138 """Scrolls from one element to another
@@ -141,7 +146,6 @@ def scroll(self, originEl, destinationEl):
141146 """
142147 action = TouchAction (self )
143148 action .press (originEl ).move_to (destinationEl ).release ().perform ()
144-
145149 return self
146150
147151 # convenience method added to Appium (NOT Selenium 3)
@@ -154,15 +158,16 @@ def drag_and_drop(self, originEl, destinationEl):
154158 """
155159 action = TouchAction (self )
156160 action .long_press (originEl ).move_to (destinationEl ).release ().perform ()
157-
158161 return self
159162
160163 # convenience method added to Appium (NOT Selenium 3)
161164 def tap (self , positions , duration = None ):
162- """Taps on an particular place with up to five fingers, holding for a certain time
165+ """Taps on an particular place with up to five fingers, holding for a
166+ certain time
163167
164168 :Args:
165- - positions - an array of tuples representing the x/y coordinates of the fingers to tap. Length can be up to five.
169+ - positions - an array of tuples representing the x/y coordinates of
170+ the fingers to tap. Length can be up to five.
166171 - duration - (optional) length of time to tap, in seconds
167172
168173 :Usage:
@@ -181,7 +186,6 @@ def tap(self, positions, duration=None):
181186 ma .add (action )
182187
183188 ma .perform ()
184-
185189 return self
186190
187191 # convenience method added to Appium (NOT Selenium 3)
@@ -207,7 +211,6 @@ def swipe(self, startx, starty, endx, endy, duration=0):
207211 .move_to (x = endx , y = endy ) \
208212 .release ()
209213 action .perform ()
210-
211214 return self
212215
213216 # convenience method added to Appium (NOT Selenium 3)
@@ -234,7 +237,6 @@ def pinch(self, element=None, startx=None, starty=None, endx=None, endy=None, du
234237 'duration' : duration
235238 };
236239 self .execute_script ('mobile: pinchClose' , opts )
237-
238240 return self
239241
240242 # convenience method added to Appium (NOT Selenium 3)
@@ -263,7 +265,185 @@ def zoom(self, element=None, startx=None, starty=None, endx=None, endy=None, dur
263265 'duration' : duration
264266 };
265267 self .execute_script ('mobile: pinchOpen' , opts )
268+ return self
269+
270+ @property
271+ def app_strings (self ):
272+ """Returns the application strings from the device.
273+
274+ :Usage:
275+ strings = driver.app_strings
276+ """
277+ return self .execute (Command .GET_APP_STRINGS )['value' ]
278+
279+ def keyevent (self , keycode , metastate = None ):
280+ """Sends a keycode to the device. Android only. Possible keycodes can be
281+ found in http://developer.android.com/reference/android/view/KeyEvent.html.
282+
283+ :Args:
284+ - keycode - the keycode to be sent to the device
285+ - metastate - meta information about the keycode being sent
286+ """
287+ data = {
288+ 'keycode' : keycode
289+ }
290+ if metastate != None :
291+ data ['metastate' ] = metastate
292+ self .execute (Command .KEY_EVENT , data )
293+ return self
294+
295+ @property
296+ def current_activity (self ):
297+ """Retrieves the current activity on the device.
298+ """
299+ return self .execute (Command .GET_CURRENT_ACTIVITY )['value' ]
300+
301+ def set_value (self , element , value ):
302+ """Set the value on an element in the application.
303+
304+ :Args:
305+ - element - the element whose value will be set
306+ - Value - the value to set on the element
307+ """
308+ data = {
309+ 'elementId' : element .id ,
310+ 'value' : [value ]
311+ }
312+ self .execute (Command .SET_IMMEDIATE_VALUE , data )
313+ return self
314+
315+ def pull_file (self , path ):
316+ """Retrieves the file at `path`. Returns the file's content encoded as
317+ Base64.
318+
319+ :Args:
320+ - path - the path to the file on the device
321+ """
322+ data = {
323+ 'path' : path
324+ }
325+ return self .execute (Command .PULL_FILE , data )['value' ]
326+
327+ def push_file (self , path , base64data ):
328+ """Puts the data, encoded as Base64, in the file specified as `path`.
329+
330+ :Args:
331+ - path - the path on the device
332+ - base64data - data, encoded as Base64, to be written to the file
333+ """
334+ data = {
335+ 'path' : path ,
336+ 'data' : base64data
337+ }
338+ self .execute (Command .PUSH_FILE , data )
339+ return self
340+
341+ def complex_find (self , selector ):
342+ """Performs a find for elements in the current application.
343+
344+ :Args:
345+ - selector - an array of selection criteria
346+ """
347+ data = {
348+ 'selector' : selector
349+ }
350+ return self .execute (Command .COMPLEX_FIND , data )['value' ]
351+
352+ def background_app (self , seconds ):
353+ """Puts the application in the background on the device for a certain
354+ duration. iOS only.
355+
356+ :Args:
357+ - seconds - the duration for the application to remain in the background
358+ """
359+ data = {
360+ 'seconds' : seconds
361+ }
362+ self .execute (Command .BACKGROUND , data )
363+ return self
364+
365+ def is_app_installed (self , bundle_id ):
366+ """Checks whether the application specified by `bundle_id` is installed
367+ on the device.
368+
369+ :Args:
370+ - bundle_id - the id of the application to query
371+ """
372+ data = {
373+ 'bundleId' : bundle_id
374+ }
375+ return self .execute (Command .IS_APP_INSTALLED , data )['value' ]
376+
377+ def install_app (self , app_path ):
378+ """Install the application found at `app_path` on the device.
379+
380+ :Args:
381+ - app_path - the local or remote path to the application to install
382+ """
383+ data = {
384+ 'appPath' : app_path
385+ }
386+ self .execute (Command .INSTALL_APP , data )
387+ return self
388+
389+ def remove_app (self , app_id ):
390+ """Remove the specified application from the device.
391+
392+ :Args:
393+ - app_id - the application id to be removed
394+ """
395+ data = {
396+ 'appId' : app_id
397+ }
398+ self .execute (Command .REMOVE_APP , data )
399+ return self
400+
401+ def launch_app (self ):
402+ """Start on the device the application specified in the desired capabilities.
403+ """
404+ self .execute (Command .LAUNCH_APP )
405+ return self
406+
407+ def close_app (self ):
408+ """Stop the running application, specified in the desired capabilities, on
409+ the device.
410+ """
411+ self .execute (Command .CLOSE_APP )
412+ return self
413+
414+ def end_test_coverage (self , intent , path ):
415+ """Ends the coverage collection and pull the coverage.ec file from the device.
416+ Android only.
266417
418+ See https://github.com/appium/appium/blob/master/docs/en/android_coverage.md
419+
420+ :Args:
421+ - intent - description of operation to be performed
422+ - path - path to coverage.ec file to be pulled from the device
423+ """
424+ data = {
425+ 'intent' : intent ,
426+ 'path' : path
427+ }
428+ self .execute (Command .END_TEST_COVERAGE , data )
429+ return self
430+
431+ def lock (self , seconds ):
432+ """Lock the device for a certain period of time. iOS only.
433+
434+ :Args:
435+ - the duration to lock the device, in seconds
436+ """
437+ data = {
438+ 'seconds' : seconds
439+ }
440+ self .execute (Command .LOCK , data )
441+ return self
442+
443+ def shake (self ):
444+ """Shake the device.
445+ """
446+ self .execute (Command .SHAKE )
267447 return self
268448
269449
@@ -278,3 +458,47 @@ def _addCommands(self):
278458 ('POST' , '/session/$sessionId/touch/perform' )
279459 self .command_executor ._commands [Command .MULTI_ACTION ] = \
280460 ('POST' , '/session/$sessionId/touch/multi/perform' )
461+ self .command_executor ._commands [Command .GET_APP_STRINGS ] = \
462+ ('GET' , '/session/$sessionId/appium/app/strings' )
463+ self .command_executor ._commands [Command .KEY_EVENT ] = \
464+ ('POST' , '/session/$sessionId/appium/device/keyevent' )
465+ self .command_executor ._commands [Command .GET_CURRENT_ACTIVITY ] = \
466+ ('GET' , '/session/$sessionId/appium/device/current_activity' )
467+ self .command_executor ._commands [Command .SET_IMMEDIATE_VALUE ] = \
468+ ('POST' , '/session/$sessionId/appium/element/$elementId/value' )
469+ self .command_executor ._commands [Command .PULL_FILE ] = \
470+ ('POST' , '/session/$sessionId/appium/device/pull_file' )
471+ self .command_executor ._commands [Command .PUSH_FILE ] = \
472+ ('POST' , '/session/$sessionId/appium/device/push_file' )
473+ self .command_executor ._commands [Command .COMPLEX_FIND ] = \
474+ ('POST' , '/session/$sessionId/appium/app/complex_find' )
475+ self .command_executor ._commands [Command .BACKGROUND ] = \
476+ ('POST' , '/session/$sessionId/appium/app/background' )
477+ self .command_executor ._commands [Command .IS_APP_INSTALLED ] = \
478+ ('POST' , '/session/$sessionId/appium/device/app_installed' )
479+ self .command_executor ._commands [Command .INSTALL_APP ] = \
480+ ('POST' , '/session/$sessionId/appium/device/install_app' )
481+ self .command_executor ._commands [Command .REMOVE_APP ] = \
482+ ('POST' , '/session/$sessionId/appium/device/remove_app' )
483+ self .command_executor ._commands [Command .LAUNCH_APP ] = \
484+ ('POST' , '/session/$sessionId/appium/app/launch' )
485+ self .command_executor ._commands [Command .CLOSE_APP ] = \
486+ ('POST' , '/session/$sessionId/appium/app/close' )
487+ self .command_executor ._commands [Command .END_TEST_COVERAGE ] = \
488+ ('POST' , '/session/$sessionId/appium/app/end_test_coverage' )
489+ self .command_executor ._commands [Command .LOCK ] = \
490+ ('POST' , '/session/$sessionId/appium/device/lock' )
491+ self .command_executor ._commands [Command .SHAKE ] = \
492+ ('POST' , '/session/$sessionId/appium/device/shake' )
493+
494+
495+ # monkeypatched method for WebElement
496+ def set_value (self , value ):
497+ """Set the value on this element in the application
498+ """
499+ data = {
500+ 'elementId' : self .id ,
501+ 'value' : [value ]
502+ }
503+ self ._execute (Command .SET_IMMEDIATE_VALUE , data )
504+ return self
0 commit comments