| # Copyright 2017 Google Inc. |
| # |
| # 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. |
| |
| """Provides a Step object. |
| """ |
| |
| import collections |
| |
| # Used when the id attribute of an element in known. The first element with the |
| # id attribute value matched will be returned. |
| ID = 'id' |
| |
| # Used to locate nodes in the XML document of the screen. It opens up all sorts |
| # of new possibilities to locate an element such as locating the third checkbox. |
| # However, this should only be used when there is no suitable ID attribute to |
| # locate an element on the screen. |
| XPATH = 'xpath' |
| |
| # Javascript implementation of locating an element using the json structure of |
| # the screen. Locating an element by xpath is a shortcut of xpath since |
| # Appium converts xpath into uiautomation path. uiautomation is not supported |
| # by selenium webdriver. |
| UIAUTOMATION = 'uiautomation' |
| |
| # Used to locate an element by class attribute name. The first element with the |
| # matching class attribute name will be returned. |
| CLASS_NAME = 'class name' |
| |
| # Mobile device types. |
| PHONE = 'phone' |
| TABLET = 'tablet' |
| |
| # Latest iOS major version. |
| IOS_10 = 10 |
| # A major version for Android. |
| ANDROID_6 = 6 |
| |
| # A Point has x and y coordinates each of type float and ranging from 0.0 to 1.0 |
| # for relative horizontal and vertical positions of a point on the screen with |
| # {x:0.0, y:0.0} as the top left and {x:1.0, y:1.0} as the bottom right. |
| class Point(object): |
| def __init__(self, x, y): |
| self.x = x |
| self.y = y |
| |
| def __eq__(self, other): |
| return other and self.x == other.x and self.y == other.y |
| |
| def to_str(self): |
| return '%s,%s' % (self.x, self.y) |
| |
| def __repr__(self): |
| return 'Point(x=%s, y=%s)' % (self.x, self.y) |
| |
| |
| class Step(object): |
| """Provides a test Step. |
| |
| Attributes: |
| description: A string that briefly describes what the test step does in a |
| human friendly language. An example will be: Tap on the Back button to |
| return to the home screen. A test step description is not a required |
| field but very recommended to have. |
| action_type: A string for the category of actions to use based on the way |
| it interacts with the app. tap, scroll, system, etc... are examples of |
| types of actions which are each mapped to a file whose implementation |
| resides in the actions directory. The action type is a required field. |
| action: A string for the name of the method that calls Appium webdriver |
| for its execution. DoubleTap, ScrollUpToElement, Swipe, Type, and Tap |
| are examples of actions and are implemented in their respective action |
| type files. A test step action is a required field. |
| by: A string that represents the first element of the locator tuple. This |
| field may or maynot be required depending on the type of action used. |
| In Appium, elements can be found on the screen by uiautomation, xpath, |
| id, classname. 'type_and_name' is a built-in |by| field used to tap on |
| a hidden element which Appium cannot do. |
| path: A string that represents the second element of the locator tuple. A |
| path could be the accessibility label or accessibility identifier of an |
| element. This field is only required if 'by' field is provided. In |
| Selenium, a locator is a tuple of 'by' and 'path' used to find and match |
| elements on the screen to interact with. |
| value: A string for the step value. It could be the text that populates a |
| text field. A test step value field may or may not be required depending |
| on the type of action used. |
| duration: A string for the time in seconds a test step action needs in order |
| to accomplish a specific task. For example DeactivateAppForDuration |
| uses duration field as the time for the app to remain inactive. This |
| field may or may not be required depending on the type of action used. |
| start_coordinate: A Point for the starting point on the screen. This field |
| may or may not be required depending on the type of action used. |
| end_coordinate: A Point for the ending point on the screen. This field may |
| or may not be required depending on the type of action used. |
| device_type: A string used to disable an entire test case or a set of test |
| steps. If given at the beginning of a test case, the test case will not |
| be executed on a device configuration different from the one specified. |
| If used on a test step, that test step will not be executed. A test case |
| that has a blank device type attribute will run on all devices. |
| device_versions: A list of strings delimited by a comma for all the device |
| versions a test can be executed on. If specified at the beginning of a |
| test case, the test will not be executed on a device whose version is |
| different from the one(s) listed. A test case that has a blank device |
| version attribute will run on all device versions. |
| """ |
| |
| def __init__(self, description=None, action_type=None, action=None, by=None, |
| path=None, value=None, duration=None, start_coordinate=None, |
| end_coordinate=None, device_type=None, device_versions=None): |
| """Step constructor.""" |
| self.description = description |
| self.action_type = action_type |
| self.action = action |
| self.by = by |
| self.path = path |
| self.value = value |
| self.duration = duration |
| self.start_coordinate = start_coordinate |
| self.end_coordinate = end_coordinate |
| self.device_type = device_type |
| self.device_versions = device_versions or set() |
| |
| @classmethod |
| def from_row(cls, description, action_type, action, by, path, value, |
| duration, start_coordinate, end_coordinate, device_type, |
| device_versions): |
| """Create a new Step object from a spreadsheet row (list of strings).""" |
| step = cls() |
| step.description = description or None |
| step.action_type = action_type or None |
| step.action = action or None |
| step.by = by or None |
| step.path = path or None |
| step.value = value or None |
| step.duration = duration or None |
| step.start_coordinate = None |
| if start_coordinate: |
| start_x, start_y = start_coordinate.split(',') |
| step.start_coordinate = Point(float(start_x), float(start_y)) |
| step.end_coordinate = None |
| if end_coordinate: |
| end_x, end_y = end_coordinate.split(',') |
| step.end_coordinate = Point(float(end_x), float(end_y)) |
| step.device_type = device_type or None |
| step.device_versions = set() |
| if device_versions: |
| step.device_versions = set(device_versions.split(',')) |
| return step |
| |
| def to_dict(self): |
| return { |
| 'description': self.description, |
| 'action_type': self.action_type, |
| 'action': self.action, |
| 'by': self.by, |
| 'path': self.path, |
| 'value': self.value, |
| 'duration': self.duration, |
| 'start_coordinate': self.start_coordinate, |
| 'end_coordinate': self.end_coordinate, |
| 'device_type': self.device_type, |
| 'device_versions': self.device_versions, |
| } |
| |
| def __repr__(self): |
| return repr(self.to_dict()) |
| |
| def to_row(self): |
| """Return CSV-compatible row representation""" |
| return [ |
| self.description or '', |
| self.action_type or '', |
| self.action or '', |
| self.by or '', |
| self.path or '', |
| self.value or '', |
| self.duration or '', |
| self.start_coordinate.to_str() if self.start_coordinate else '', |
| self.end_coordinate.to_str() if self.end_coordinate else '', |
| self.device_type or '', |
| ','.join(sorted(self.device_versions)), |
| ] |
| |
| def __eq__(self, other): |
| return self.to_dict() == other.to_dict() |