class Util(object): log = log.logger(logging.INFO) def sleep(self, sec, info=""): """ Put the program to wait for the specified amount of time Wrapped time.sleep() to have better logging """ if info is not None: self.log.info("Wait :: '" + str(sec) + "' seconds for " + info) try: time.sleep(sec) except InterruptedError: traceback.print_stack() def get_alpha_numeric(self, length, type='letters'): """ Get random string of characters Parameters: length: Length of string, number of characters string should have type: Type of characters string should have. Default is letters Provide lower/upper/digits for different types """ alpha_num = '' if type == 'lower': case = string.ascii_lowercase elif type == 'upper': case = string.ascii_uppercase
class StatusOfTest(AppiumDriver): log = logger(logging.INFO) def __init__(self, driver): super().__init__(driver) self.resultList = [] def setResult(self, result, resultMessage): try: if result is not None: if result: self.resultList.append("PASS") self.log.info("### VERIFICATION SUCCEEDED" + resultMessage) else: self.resultList.append("FAIL") self.log.error("### VERIFICATION FAILED" + resultMessage) self.take_screenshot(resultMessage) else: self.resultList.append("FAIL") self.log.error("### VERIFICATION FAILED - RESULT IS NONE" + resultMessage) self.take_screenshot(resultMessage) except: self.resultList.append("FAIL") self.log.error("### EXCEPTION OCCURRED ###") self.take_screenshot(resultMessage) print_stack() def verifyMark(self, result, resultMessage): """ Mark the result of the verification point in a test case. Will not stop a test if there is a failure, but it will fail the test. """ self.setResult(result, resultMessage) def assertionMark(self, testName, result, resultMessage): """ Mark the final result of the verification point in a test case This needs to be called at least once in a test case This should be final test status of the test case (hard assertion) This will stop a test if it fails. """ self.setResult(result, resultMessage) if "FAIL" in self.resultList: self.log.error(testName + " ### TEST FAILED: ") self.resultList.clear() assert True == False else: self.log.info(testName + " ### TEST SUCCESSFUL: ") self.resultList.clear() assert True == True
class ActivityIndicators(BaseView): log = logger(logging.DEBUG) def __init__(self, driver, platform): super().__init__(driver) self.driver = driver self.platform = platform # Locators # Should I just refactor these to be tuples and unpack them in the 'get' methods? # If I do that, I should clean up the AppiumDriver class to also use and unpack tuples def get_activity_indicator_title_locator(self): platform = self.platform.lower() _activity_indicator_title_xpath = "" try: if platform == "ios": _activity_indicator_title_xpath = "//XCUIElementTypeNavigationBar[@name='Activity Indicators']" elif platform == "android": _activity_indicator_title_xpath = "" return _activity_indicator_title_xpath except: self.log.error( "Platform not provided. Cannot get 'activity indicator title'") def get_gray_spinner_locator(self): platform = self.platform.lower() _gray_spinner_xpath = "" try: if platform == "ios": _gray_spinner_xpath = "(//XCUIElementTypeActivityIndicator[@name='In progress'])[1]" elif platform == "android": _gray_spinner_xpath = "" return _gray_spinner_xpath except: self.log.error("Platform not provided. Cannot get 'gray spinner'") def get_tinted_spinner_locator(self): platform = self.platform.lower() _tinted_spinner_xpath = "" try: if platform == "ios": _tinted_spinner_xpath = "(//XCUIElementTypeActivityIndicator[@name='In progress'])[2]" elif platform == "android": _tinted_spinner_xpath = "" return _tinted_spinner_xpath except: self.log.error("Platform not provided. Cannot get 'gray spinner'") def get_gray_label_locator(self): platform = self.platform.lower() _gray_label_xpath = "" try: if platform == "ios": _gray_label_xpath = "(//XCUIElementTypeOther[@name='GRAY'])[1]" elif platform == "android": _gray_label_xpath = "" return _gray_label_xpath except: self.log.error("Platform not provided. Cannot get 'gray label'") def get_tinted_label_locator(self): platform = self.platform.lower() _tinted_label_xpath = "" try: if platform == "ios": _tinted_label_xpath = "(//XCUIElementTypeOther[@name='TINTED'])[1]" elif platform == "android": _tinted_label_xpath = "" return _tinted_label_xpath except: self.log.error("Platform not provided. Cannot get 'gray label'") # Methods def activity_indicators_title_is_displayed(self): activity_indicators_title = self.get_activity_indicator_title_locator() return self.is_element_present(activity_indicators_title, "xpath") def activity_all_elements_are_displayed(self): # elementsList = [self.get_gray_label_locator(), self.get_gray_spinner_locator(), self.get_tinted_label_locator(), # self.get_tinted_spinner_locator()] elementsList = [ self.get_element(self.get_tinted_spinner_locator(), "xpath"), self.get_element(self.get_tinted_label_locator(), "xpath"), self.get_element(self.get_gray_spinner_locator(), "xpath"), self.get_element(self.get_gray_label_locator(), "xpath") ] if len(elementsList) == 4: return True else: return False
class HomeView(BaseView): log = logger(logging.DEBUG) def __init__(self, driver): super().__init__(driver) self.driver = driver def assert_ui_catalog_title_exists(self): title = self.get_ui_catalog_title() return title.is_displayed() def get_ui_catalog_title(self): return self.wait_for_element_to_appear(locator=self._ui_catalog_xpath, locator_type="xpath") def navigate_to_each_view(self): elementList = self.driver.find_elements_by_xpath(".//XCUIElementTypeButton[@name='More Info']") numOfElements = len(elementList) clickCount = 0 for cell in elementList: self.click_element(element=cell) self.driver.back() self.wait_for_element_to_appear(locator=self._ui_catalog_xpath, locator_type="xpath") clickCount += 1 if clickCount == 10: self.vertical_swipe_iOS("up") if numOfElements == clickCount: self.log.info("Navigated through all of the views. Num of elements: " + str(numOfElements)) else: self.log.error("Whoops, skipped navigating to a few views. Num of elements: " + str(numOfElements) + "Num of clicks: " + str(clickCount)) # Interactions def navigate_to_action_sheets_screen(self): self.click_element(self._action_sheets_id) def navigate_to_activity_indicators_screen(self): self.click_element(self._activity_indicators_id) def navigate_to_alert_views_screen(self): self.click_element(self._alert_views_id) def navigate_to_buttons_screen(self): self.click_element(self._buttons_id) def navigate_to_date_pickers_screen(self): self.click_element(self._date_picker_id) def navigate_to_image_views_screen(self): self.click_element(self._image_view_id) def navigate_to_page_controls_screen(self): self.click_element(self._page_control_id) def navigate_to_picker_view_screen(self): self.click_element(self._picker_view_id) def navigate_to_progress_views_screen(self): self.click_element(self._progress_views_id) def navigate_to_segmented_controls_screen(self): self.click_element(self._segmented_controls_id) def navigate_to_sliders_screen(self): self.click_element(self._sliders_id) def navigate_to_steppers_screen(self): self.click_element(self._sliders_id) def navigate_to_switches_screen(self): self.click_element(self._switches_id) def navigate_to_text_fields_screen(self): self.click_element(self._text_fields_id) def navigate_to_text_views_screen(self): self.click_element(self._text_view_id) def navigate_to_web_view_screen(self): self.click_element(self._web_view_id) def navigate_to_search_bars_screen(self): self.click_element(self._search_bars_id) def navigate_to_tool_bars_screen(self): self.click_element(self._toolbars_id) # Element Paths _ui_catalog_xpath = "//XCUIElementTypeNavigationBar[@name='UICatalog']" _action_sheets_id = "Action Sheets" _activity_indicators_id = "Activity Indicators" _alert_views_id = "Alert Views" _buttons_id = "Buttons" _date_picker_id = "Date Picker" _image_view_id = "Image View" _page_control_id = "Page Control" _picker_view_id = "Picker View" _progress_views_id = "Progress Views" _segmented_controls_id = "Segmented Controls" _sliders_id = "Sliders" _steppers_id = "Steppers" _switches_id = "Switches" _text_fields_id = "Text Fields" _text_view_id = "Text View" _web_view_id = "Web View" _search_bars_id = "Search Bars" _toolbars_id = "Toolbars"
class DatePickerView(BaseView): log = logger(logging.DEBUG) def __init__(self, driver, platform): super().__init__(driver) self.driver = driver self.platform = platform # Locators def get_date_picker_title_locator(self): platform = self.platform.lower() _date_picker_title_xpath = "" try: if platform == "ios": _date_picker_title_xpath = "//XCUIElementTypeNavigationBar[@name='Date Picker']" elif platform == "android": _date_picker_title_xpath = "" return _date_picker_title_xpath except: self.log.error("Platform not provided. Cannot get 'date picker title'") def get_day_wheel_locator(self): platform = self.platform.lower() _day_xpath = "" try: if platform == "ios": _day_xpath = "//XCUIElementTypePickerWheel" elif platform == "android": _day_xpath = "" return _day_xpath except: self.log.error("Platform not provided. Cannot get 'day wheel'") def get_hour_wheel_locator(self): platform = self.platform.lower() _hour_xpath = "" try: if platform == "ios": _hour_xpath = "//XCUIElementTypePickerWheel[2]" elif platform == "android": _hour_xpath = "" return _hour_xpath except: self.log.error("Platform not provided. Cannot get 'day wheel'") def get_minute_wheel_locator(self): platform = self.platform.lower() _min_xpath = "" try: if platform == "ios": _min_xpath = "//XCUIElementTypePickerWheel[3]" elif platform == "android": _min_xpath = "" return _min_xpath except: self.log.error("Platform not provided. Cannot get 'day wheel'") def get_meridium_wheel_locator(self): platform = self.platform.lower() _meridium_xpath = "" try: if platform == "ios": _meridium_xpath = "//XCUIElementTypePickerWheel[4]" elif platform == "android": _meridium_xpath = "" return _meridium_xpath except: self.log.error("Platform not provided. Cannot get 'day wheel'") def choose_tomorrow_for_date_picker(self, strHour="11", strMinute="30", strMeridium="PM"): tomorrow = self.getTomorrowsDate() day_picker = self.get_element(self.get_day_wheel_locator(), "xpath") day_picker.send_keys(tomorrow) hour_picker = self.get_element(self.get_hour_wheel_locator(), "xpath") hour_picker.send_keys(strHour) minute_picker = self.get_element(self.get_minute_wheel_locator(), "xpath") minute_picker.send_keys(strMinute) meridium_picker = self.get_element(self.get_meridium_wheel_locator(), "xpath") meridium_picker.send_keys(strMeridium) def getTomorrowsDate(self): return (datetime.date.today() + datetime.timedelta(days=1)).strftime("%b %d") def verifyDatePickerLabel(self, strHour="11", strMinute="30", strMeridium="PM"): tomorrow = self.getTomorrowsDate() _text_locator = "//XCUIElementTypeStaticText[contains(@name, '{0}, 2020 at {1}:{2} {3}')]" _text_label = _text_locator.format(tomorrow, strHour, strMinute, strMeridium) static_text_label = self.get_element(_text_label, "xpath") # return static_text_label.is_displayed() return self.is_element_displayed(element=static_text_label)
class CapsFactory: log = logger(logging.DEBUG) def __init__(self, device): self.device = device """ Set driver and platform environment based on OS XCUIDriver "/Users/KimEklund/XCUIDriver.exe" os.environ["XCUIDriver.iOS.driver"] = xcuidriver self.driver = webdriver.Remote('http://localhost:4723/wd/hub') Preferred: Set the path on the machine where the tests will be executed """ def get_driver_instance(self): remote_url = 'http://localhost:4723/wd/hub' caps = self.get_capabilities() driver = webdriver.Remote(remote_url, caps) driver.implicitly_wait(3) return driver def get_platform_name(self): platformName = self.get_capabilities()['platformName'] return platformName def get_capabilities(self): try: if self.device == "android-sim": # TODO Add caps for Android sim caps = { 'platformVersion': '9.0', 'platformName': 'Android', 'deviceName': 'emulator-5554', 'automationName': 'Espresso', 'app': '/abs/path/to/my.apk' } # if app is already installed, supply the appPackage and appActivity instead of 'app' # get device name by using $adb devices -- after emulator has been launched elif self.device == "ios-real-device": # TODO Add caps for iOS real device caps = { 'platformVersion': '12.4', 'platformName': 'iOS', 'deviceName': 'iPhone 8 Plus', 'automationName': 'XCUITest', 'bundleId': 'com.example.apple-samplecode.UICatalog', 'udid': '', 'xcodeOrgId': '', 'xcodeSigningId': '' } self.log.info("Running tests on iOS Physical Device") elif self.device == "android-real-device": caps = { 'platformVersion': '9.0', 'platformName': 'Android', 'deviceName': 'My Android Phone', 'automationName': 'Espresso', 'app': '/abs/path/to/my.apk' } # if app is already installed, supply the appPackage and appActivity instead of 'app' self.log.info("Running tests on iOS Physical Device") else: caps = { 'platformVersion': '12.4', 'platformName': 'iOS', 'deviceName': 'iPhone 8 Plus 12.4', 'automationName': 'XCUITest', 'bundleId': 'com.example.apple-samplecode.UICatalog' } self.log.info("Running tests on iOS Simulator") return caps except: self.log.error("Could not return desired capabilities")
class AppiumDriver: log = logger(logging.DEBUG) def __init__(self, driver): self.driver = driver def get_web_title(self): """ Used for web testing, if app opens Safari/Chrome? """ return self.driver.title def take_screenshot(self, resultMessage): """ Takes a screenshot of the current open view Called normally upon test failure """ fileName = resultMessage + "." + str(round( time.time() * 1000)) + ".png" screenShotDirectory = "../screenshots/" relativeFileName = screenShotDirectory + fileName currentDirectory = os.path.dirname( __file__) # gets current file and dir of current file destinationFile = os.path.join(currentDirectory, relativeFileName) destinationDirectory = os.path.join(currentDirectory, screenShotDirectory) try: if not os.path.exists(destinationDirectory): os.makedirs(destinationDirectory) self.driver.save_screenshot(destinationFile) self.log.info("Screenshot saved to directory: " + destinationFile) except: self.log.error("### Exception Occurred") print_stack() def vertical_swipe_iOS(self, direction): """ Swipes the app in the direction passed in. Does not scroll to determined coordinates :param direction: "up" or "down" """ self.driver.execute_script("mobile: swipe", {"direction": direction}) def wait_for_element_to_be_clickable(self, locator, locatorType="accessibilityid", timeout=10, pollFrequency=0.5): """ Waits for an element based on expected conditions (to be clickable) :param locator: Locator string (Ex: "id_name") :param locatorType: See 'getByType' for arguments (Ex: "accessibilityid" :param timeout: Int in seconds :param pollFrequency: Int in seconds :return: returns clickable element once the element is clickable if timeout does not expire """ element = None try: byType = self.get_by_type(locatorType) self.log.info("Waiting for element with locator: '" + locator + "' to be clickable") wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=pollFrequency, ignored_exceptions=[ NoSuchElementException, ElementNotVisibleException, ElementNotSelectableException ]) element = wait.until(EC.element_to_be_clickable((byType, locator))) except: self.log.info( "Timeout expired waiting for element with locator: '" + locator + "' to be clickable") print_stack() return element def wait_for_element_to_appear(self, locator, locator_type="accessibilityid", timeout=10, pollFrequency=0.5): """ Waits for an element based on expected conditions (to be clickable) :param locator: Locator string (Ex: "id_name") :param locator_type: See 'getByType' for arguments (Ex: "accessibilityid" :param timeout: Int in seconds :param pollFrequency: Int in seconds :return: returns clickable element once the element is clickable if timeout does not expire """ element = None try: by_type = self.get_by_type(locator_type) self.log.info("Waiting for element with locator: '" + locator + "' to appear") wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=pollFrequency, ignored_exceptions=[ NoSuchElementException, ElementNotVisibleException, ElementNotSelectableException ]) element = wait.until( EC.visibility_of_element_located((by_type, locator))) except: print_stack() return element def get_by_type(self, locator_type): """ Takes a short-hand string and returns the By.LOCATORTYPE :param locator_type: String :return: By.LOCATORTYPE """ locator_type = locator_type.lower() if locator_type == "accessibilityid": # iOS: accessibility-id # Android: content-desc return MobileBy.ACCESSIBILITY_ID elif locator_type == "classname": # iOS: full name of the XCUI element and begins with XCUIElementType # Android: full name of the UIAutomator2 class (e.g.: android.widget.TextView) return By.CLASS_NAME elif locator_type == "id": # Native element identifier. resource-id for android; name for iOS. return By.ID elif locator_type == "name": return By.NAME elif locator_type == "xpath": return By.XPATH elif locator_type == "image": return MobileBy.IMAGE elif locator_type == "uiautomator": # UIAutomator2 only return MobileBy.ANDROID_UIAUTOMATOR elif locator_type == "viewtag": # Espresso only return MobileBy.ANDROID_VIEWTAG elif locator_type == "datamatcher": # Espresso only return MobileBy.ANDROID_DATA_MATCHER elif locator_type == "classchain": # iOS only return MobileBy.IOS_CLASS_CHAIN elif locator_type == "linktext": return By.LINK_TEXT else: self.log.error( "Locator type not supported - or check the argument you passed in" ) return False def get_element(self, locator, locator_type="accessibilityid"): """ Queries for an element """ element = None try: # byType = self.getByType(locatorType) # element = self.driver.find_element(byType, locator) element = self.wait_for_element_to_appear( locator=locator, locator_type=locator_type) self.log.info("Element found with locator: '" + locator + "'") except: self.log.error("Element not found with locator: '" + locator + "'") return element def get_element_list(self, locator, locator_type="accessibilityid"): """ Queries for a list of elements """ element_list = None try: by_type = self.get_by_type(locator_type) element_list = self.driver.find_elements(by_type, locator) if len(element_list) == 1: self.log.info( "Only one element in list. Consider using the singular `get_element` method instead" ) elif len(element_list) > 0: self.log.info("Element list found, num of elements: " + str(len(element_list))) else: self.log.info("Element list is empty. Used locator: '" + locator + "'") except: self.log.error( "Invalid arguments passed into 'get_element_list' method") return element_list def click_element(self, locator="", locator_type="accessibilityid", element=None): """ Clicks on an element """ try: if locator: element = self.get_element(locator, locator_type) element.click() self.log.info("Clicked on element with locator: '" + locator + "'") except: self.log.error("Cannot click on the element with locator: '" + locator + "'") print_stack() def send_text(self, text, locator="", locator_type="accessibilityid", element=None): """ Sends text to an element """ try: if locator: element = self.get_element(locator, locator_type) element.send_keys(text) self.log.info("Sent keys to element with locator: '" + locator + "'") except: self.log.error("Cannot send keys to element with locator: '" + locator + "'") print_stack() def get_text(self, locator="", locator_type="accessibilityid", element=None, info=""): """ Get 'Text' from an element Either provide element or a combination of locator and locatorType """ try: if locator: self.log.debug("In locator condition") element = self.get_element(locator, locator_type) self.log.debug("Before finding text") text = element.text self.log.debug("After finding element, size is: " + str(len(text))) if len(text) != 0: self.log.info("Getting text on element :: " + info) self.log.info("The text is :: '" + text + "'") text = text.strip() except: self.log.error("Failed to get text on element " + info) print_stack() text = None return text def clear_field(self, locator="", locator_type="accessibilityid", element=None): """ Clear an element field """ if locator: element = self.get_element(locator, locator_type) element.clear() self.log.info("Clear field with locator: '" + locator + "'") def is_element_present(self, locator="", locator_type="accessibilityid", element=None): """ Checks for the presence of an element and returns a bool value. """ try: if locator: element = self.get_element(locator, locator_type) if element is not None: self.log.info("Element with locator: '" + locator + "' is present") return True else: return False except: self.log.info("Element with locator: '" + locator + "' is not present") return False def element_list_presence(self, locator, locator_type="accessibilityid"): # plural elements, returns bool like isElementPresent() try: elementsList = self.get_element_list(locator, locator_type) if len(elementsList) > 0: self.log.info("\nNumber of elements found: " + str(len(elementsList))) return True else: return False except: self.log.info("\nElement(s) are not present") return False def is_element_displayed(self, locator="", locator_type="accessibilityid", element=None): isDisplayed = False try: if locator: element = self.get_element(locator, locator_type) if element is not None: isDisplayed = element.is_displayed() self.log.info("Element is displayed with locator: '" + locator + "'") else: self.log.info("Element is not displayed with locator: '" + locator + "'") return isDisplayed except: self.log.error("Exception on 'is_element_displayed'") return False def is_enabled(self, locator, locator_type="accessibilityid", element=None): enabled = False try: if locator: element = self.get_element(locator, locator_type) enabled = element.is_enabled() if enabled: self.log.info("Element is enabled") else: self.log.info("Element is not enabled") except: self.log.error("Element state could not be found") return enabled def is_disabled(self, locator, locator_type="accessibilityid", element=None): enabled = self.is_enabled(locator, locator_type, element) return not enabled
# Initialize the Replay Buffer obs = env.reset() for _ in range(MIN_REPLAY_SIZE): action = env.act_space_sample() new_obs, rew, done, _ = env.step(action) transition = (obs, action, rew, done, new_obs) replay_buffer.append(transition) obss = new_obs #%% # Main training loop obss = env.reset() log = logger(PROJECT_NAME, PROJECT_ID, ['WB', 'LO']) tq = tqdm() for step in itertools.count(): # tq.update(1) epsilon = np.interp(step, [0, EPSILON_DECAY], [EPSILON_START, EPSILON_END]) act = online_network.act(obs, epsilon) new_obs, rew, done, info = env.step(act) transition = (obs, action, rew, done, new_obs) replay_buffer.append(transition) if done: epinfos_buffer.append(info['episode']) episode_count += 1
class ActionSheetsView(BaseView): log = logger(logging.DEBUG) def __init__(self, driver, platform): super().__init__(driver) self.driver = driver self.platform = platform # Locators def get_action_sheets_title_locator(self): platform = self.platform.lower() _action_sheets_title_xpath = "" try: if platform == "ios": _action_sheets_title_xpath = "//XCUIElementTypeNavigationBar[@name='Action Sheets']" elif platform == "android": _action_sheets_title_xpath = "" return _action_sheets_title_xpath except: self.log.error("Platform not provided. Cannot get 'action sheets title'") def get_ok_cancel_locator(self): platform = self.platform.lower() _ok_cancel_id = "" try: if platform == "ios": _ok_cancel_id = "Okay / Cancel" elif platform == "android": _ok_cancel_id = "" return _ok_cancel_id except: self.log.error("Platform not provided. Cannot get 'Okay/Cancel button'") def get_other_locator(self): platform = self.platform.lower() _other_id = "" try: if platform == "ios": _other_id = "Other" elif platform == "android": _other_id = "" return _other_id except: self.log.error("Platform not provided. Cannot get 'Other button'") def get_ok_locator(self): platform = self.platform.lower() _os_ok_button_id = "" try: if platform == "ios": _os_ok_button_id = "OK" elif platform == "android": _os_ok_button_id = "" return _os_ok_button_id except: self.log.error("Platform not provided. Cannot get 'OS 'ok' button'") def get_cancel_locator(self): platform = self.platform.lower() _os_cancel_button_id = "" try: if platform == "ios": _os_cancel_button_id = "Cancel" elif platform == "android": _os_cancel_button_id = "" return _os_cancel_button_id except: self.log.error("Platform not provided. Cannot get 'OS 'cancel' button'") # Interactions def click_ok_cancel_button(self): self.click_element(self.get_ok_cancel_locator()) def click_other_button(self): self.click_element(self.get_other_locator()) def click_os_ok_button(self): self.click_element(self.get_ok_locator()) def click_os_cancel_button(self): self.click_element(self.get_cancel_locator()) # Verifications/Assertions def verify_buttons_disappear(self): return not self.element_list_presence(".//XCUIElementTypeSheet//XCUIElementTypeButton", locator_type="xpath") def verify_buttons_appear(self): return self.element_list_presence(".//XCUIElementTypeSheet//XCUIElementTypeButton", locator_type="xpath")