blob: 7f72336a1bc1fcef742566759e565497cacd78a4 [file] [log] [blame]
# 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()