Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions appium/webdriver/extensions/action_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#!/usr/bin/env python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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 selenium import webdriver

from appium.webdriver.common.multi_action import MultiAction
from appium.webdriver.common.touch_action import TouchAction


class ActionHelpers(webdriver.Remote):

def scroll(self, origin_el, destination_el, duration=None):
"""Scrolls from one element to another

:Args:
- originalEl - the element from which to being scrolling
- destinationEl - the element to scroll to
- duration - a duration after pressing originalEl and move the element to destinationEl.
Default is 600 ms for W3C spec. Zero for MJSONWP.

:Usage:
driver.scroll(el1, el2)
"""

# XCUITest x W3C spec has no duration by default in server side
if self.w3c and duration is None:
duration = 600

action = TouchAction(self)
if duration is None:
action.press(origin_el).move_to(destination_el).release().perform()
else:
action.press(origin_el).wait(duration).move_to(destination_el).release().perform()
return self

def drag_and_drop(self, origin_el, destination_el):
"""Drag the origin element to the destination element

:Args:
- originEl - the element to drag
- destinationEl - the element to drag to
"""
action = TouchAction(self)
action.long_press(origin_el).move_to(destination_el).release().perform()
return self

def tap(self, positions, duration=None):
"""Taps on an particular place with up to five fingers, holding for a
certain time

:Args:
- positions - an array of tuples representing the x/y coordinates of
the fingers to tap. Length can be up to five.
- duration - (optional) length of time to tap, in ms

:Usage:
driver.tap([(100, 20), (100, 60), (100, 100)], 500)
"""
if len(positions) == 1:
action = TouchAction(self)
x = positions[0][0]
y = positions[0][1]
if duration:
action.long_press(x=x, y=y, duration=duration).release()
else:
action.tap(x=x, y=y)
action.perform()
else:
ma = MultiAction(self)
for position in positions:
x = position[0]
y = position[1]
action = TouchAction(self)
if duration:
action.long_press(x=x, y=y, duration=duration).release()
else:
action.press(x=x, y=y).release()
ma.add(action)

ma.perform()
return self

def swipe(self, start_x, start_y, end_x, end_y, duration=None):
"""Swipe from one point to another point, for an optional duration.

:Args:
- start_x - x-coordinate at which to start
- start_y - y-coordinate at which to start
- end_x - x-coordinate at which to stop
- end_y - y-coordinate at which to stop
- duration - (optional) time to take the swipe, in ms.

:Usage:
driver.swipe(100, 100, 100, 400)
"""
# `swipe` is something like press-wait-move_to-release, which the server
# will translate into the correct action
action = TouchAction(self)
action \
.press(x=start_x, y=start_y) \
.wait(ms=duration) \
.move_to(x=end_x, y=end_y) \
.release()
action.perform()
return self

def flick(self, start_x, start_y, end_x, end_y):
"""Flick from one point to another point.

:Args:
- start_x - x-coordinate at which to start
- start_y - y-coordinate at which to start
- end_x - x-coordinate at which to stop
- end_y - y-coordinate at which to stop

:Usage:
driver.flick(100, 100, 100, 400)
"""
action = TouchAction(self)
action \
.press(x=start_x, y=start_y) \
.move_to(x=end_x, y=end_y) \
.release()
action.perform()
return self

def pinch(self, element=None, percent=200, steps=50):
"""Pinch on an element a certain amount

:Args:
- element - the element to pinch
- percent - (optional) amount to pinch. Defaults to 200%
- steps - (optional) number of steps in the pinch action

:Usage:
driver.pinch(element)
"""
if element:
element = element.id

opts = {
'element': element,
'percent': percent,
'steps': steps,
}
self.execute_script('mobile: pinchClose', opts)
return self

def zoom(self, element=None, percent=200, steps=50):
"""Zooms in on an element a certain amount

:Args:
- element - the element to zoom
- percent - (optional) amount to zoom. Defaults to 200%
- steps - (optional) number of steps in the zoom action

:Usage:
driver.zoom(element)
"""
if element:
element = element.id

opts = {
'element': element,
'percent': percent,
'steps': steps,
}
self.execute_script('mobile: pinchOpen', opts)
return self
110 changes: 110 additions & 0 deletions appium/webdriver/extensions/keyboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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 selenium import webdriver
from ..mobilecommand import MobileCommand as Command


class Keyboard(webdriver.Remote):

def hide_keyboard(self, key_name=None, key=None, strategy=None):
"""Hides the software keyboard on the device. In iOS, use `key_name` to press
a particular key, or `strategy`. In Android, no parameters are used.

:Args:
- key_name - key to press
- strategy - strategy for closing the keyboard (e.g., `tapOutside`)
"""
data = {}
if key_name is not None:
data['keyName'] = key_name
elif key is not None:
data['key'] = key
elif strategy is None:
strategy = 'tapOutside'
data['strategy'] = strategy
self.execute(Command.HIDE_KEYBOARD, data)
return self

def is_keyboard_shown(self):
"""Attempts to detect whether a software keyboard is present"""
return self.execute(Command.IS_KEYBOARD_SHOWN)['value']

def keyevent(self, keycode, metastate=None):
"""Sends a keycode to the device. Android only. Possible keycodes can be
found in http://developer.android.com/reference/android/view/KeyEvent.html.

:Args:
- keycode - the keycode to be sent to the device
- metastate - meta information about the keycode being sent
"""
data = {
'keycode': keycode,
}
if metastate is not None:
data['metastate'] = metastate
self.execute(Command.KEY_EVENT, data)
return self

def press_keycode(self, keycode, metastate=None, flags=None):
"""Sends a keycode to the device. Android only. Possible keycodes can be
found in http://developer.android.com/reference/android/view/KeyEvent.html.

:Args:
- keycode - the keycode to be sent to the device
- metastate - meta information about the keycode being sent
- flags - the set of key event flags
"""
data = {
'keycode': keycode,
}
if metastate is not None:
data['metastate'] = metastate
if flags is not None:
data['flags'] = flags
self.execute(Command.PRESS_KEYCODE, data)
return self

def long_press_keycode(self, keycode, metastate=None, flags=None):
"""Sends a long press of keycode to the device. Android only. Possible keycodes can be
found in http://developer.android.com/reference/android/view/KeyEvent.html.

:Args:
- keycode - the keycode to be sent to the device
- metastate - meta information about the keycode being sent
- flags - the set of key event flags
"""
data = {
'keycode': keycode
}
if metastate is not None:
data['metastate'] = metastate
if flags is not None:
data['flags'] = flags
self.execute(Command.LONG_PRESS_KEYCODE, data)
return self

# pylint: disable=protected-access

def _addCommands(self):
self.command_executor._commands[Command.HIDE_KEYBOARD] = \
('POST', '/session/$sessionId/appium/device/hide_keyboard')
self.command_executor._commands[Command.IS_KEYBOARD_SHOWN] = \
('GET', '/session/$sessionId/appium/device/is_keyboard_shown')
self.command_executor._commands[Command.KEY_EVENT] = \
('POST', '/session/$sessionId/appium/device/keyevent')
self.command_executor._commands[Command.PRESS_KEYCODE] = \
('POST', '/session/$sessionId/appium/device/press_keycode')
self.command_executor._commands[Command.LONG_PRESS_KEYCODE] = \
('POST', '/session/$sessionId/appium/device/long_press_keycode')
Loading