touch_actions.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. # Licensed to the Software Freedom Conservancy (SFC) under one
  2. # or more contributor license agreements. See the NOTICE file
  3. # distributed with this work for additional information
  4. # regarding copyright ownership. The SFC licenses this file
  5. # to you under the Apache License, Version 2.0 (the
  6. # "License"); you may not use this file except in compliance
  7. # with the License. You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing,
  12. # software distributed under the License is distributed on an
  13. # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. # KIND, either express or implied. See the License for the
  15. # specific language governing permissions and limitations
  16. # under the License.
  17. """
  18. The Touch Actions implementation
  19. """
  20. from selenium.webdriver.remote.command import Command
  21. class TouchActions(object):
  22. """
  23. Generate touch actions. Works like ActionChains; actions are stored in the
  24. TouchActions object and are fired with perform().
  25. """
  26. def __init__(self, driver):
  27. """
  28. Creates a new TouchActions object.
  29. :Args:
  30. - driver: The WebDriver instance which performs user actions.
  31. It should be with touchscreen enabled.
  32. """
  33. self._driver = driver
  34. self._actions = []
  35. def perform(self):
  36. """
  37. Performs all stored actions.
  38. """
  39. for action in self._actions:
  40. action()
  41. def tap(self, on_element):
  42. """
  43. Taps on a given element.
  44. :Args:
  45. - on_element: The element to tap.
  46. """
  47. self._actions.append(lambda: self._driver.execute(
  48. Command.SINGLE_TAP, {'element': on_element.id}))
  49. return self
  50. def double_tap(self, on_element):
  51. """
  52. Double taps on a given element.
  53. :Args:
  54. - on_element: The element to tap.
  55. """
  56. self._actions.append(lambda: self._driver.execute(
  57. Command.DOUBLE_TAP, {'element': on_element.id}))
  58. return self
  59. def tap_and_hold(self, xcoord, ycoord):
  60. """
  61. Touch down at given coordinates.
  62. :Args:
  63. - xcoord: X Coordinate to touch down.
  64. - ycoord: Y Coordinate to touch down.
  65. """
  66. self._actions.append(lambda: self._driver.execute(
  67. Command.TOUCH_DOWN, {
  68. 'x': int(xcoord),
  69. 'y': int(ycoord)}))
  70. return self
  71. def move(self, xcoord, ycoord):
  72. """
  73. Move held tap to specified location.
  74. :Args:
  75. - xcoord: X Coordinate to move.
  76. - ycoord: Y Coordinate to move.
  77. """
  78. self._actions.append(lambda: self._driver.execute(
  79. Command.TOUCH_MOVE, {
  80. 'x': int(xcoord),
  81. 'y': int(ycoord)}))
  82. return self
  83. def release(self, xcoord, ycoord):
  84. """
  85. Release previously issued tap 'and hold' command at specified location.
  86. :Args:
  87. - xcoord: X Coordinate to release.
  88. - ycoord: Y Coordinate to release.
  89. """
  90. self._actions.append(lambda: self._driver.execute(
  91. Command.TOUCH_UP, {
  92. 'x': int(xcoord),
  93. 'y': int(ycoord)}))
  94. return self
  95. def scroll(self, xoffset, yoffset):
  96. """
  97. Touch and scroll, moving by xoffset and yoffset.
  98. :Args:
  99. - xoffset: X offset to scroll to.
  100. - yoffset: Y offset to scroll to.
  101. """
  102. self._actions.append(lambda: self._driver.execute(
  103. Command.TOUCH_SCROLL, {
  104. 'xoffset': int(xoffset),
  105. 'yoffset': int(yoffset)}))
  106. return self
  107. def scroll_from_element(self, on_element, xoffset, yoffset):
  108. """
  109. Touch and scroll starting at on_element, moving by xoffset and yoffset.
  110. :Args:
  111. - on_element: The element where scroll starts.
  112. - xoffset: X offset to scroll to.
  113. - yoffset: Y offset to scroll to.
  114. """
  115. self._actions.append(lambda: self._driver.execute(
  116. Command.TOUCH_SCROLL, {
  117. 'element': on_element.id,
  118. 'xoffset': int(xoffset),
  119. 'yoffset': int(yoffset)}))
  120. return self
  121. def long_press(self, on_element):
  122. """
  123. Long press on an element.
  124. :Args:
  125. - on_element: The element to long press.
  126. """
  127. self._actions.append(lambda: self._driver.execute(
  128. Command.LONG_PRESS, {'element': on_element.id}))
  129. return self
  130. def flick(self, xspeed, yspeed):
  131. """
  132. Flicks, starting anywhere on the screen.
  133. :Args:
  134. - xspeed: The X speed in pixels per second.
  135. - yspeed: The Y speed in pixels per second.
  136. """
  137. self._actions.append(lambda: self._driver.execute(
  138. Command.FLICK, {
  139. 'xspeed': int(xspeed),
  140. 'yspeed': int(yspeed)}))
  141. return self
  142. def flick_element(self, on_element, xoffset, yoffset, speed):
  143. """
  144. Flick starting at on_element, and moving by the xoffset and yoffset
  145. with specified speed.
  146. :Args:
  147. - on_element: Flick will start at center of element.
  148. - xoffset: X offset to flick to.
  149. - yoffset: Y offset to flick to.
  150. - speed: Pixels per second to flick.
  151. """
  152. self._actions.append(lambda: self._driver.execute(
  153. Command.FLICK, {
  154. 'element': on_element.id,
  155. 'xoffset': int(xoffset),
  156. 'yoffset': int(yoffset),
  157. 'speed': int(speed)}))
  158. return self
  159. # Context manager so TouchActions can be used in a 'with .. as' statements.
  160. def __enter__(self):
  161. return self # Return created instance of self.
  162. def __exit__(self, _type, _value, _traceback):
  163. pass # Do nothing, does not require additional cleanup.