-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #346 from immutable/feature/dx-3448-ios-browserstack
[SDX-3448] iOS browserstack tests on CICD
- Loading branch information
Showing
5 changed files
with
187 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# ============================= | ||
# Set BrowserStack Credentials | ||
# ============================= | ||
# Add your BrowserStack userName and acccessKey here or set BROWSERSTACK_USERNAME and | ||
# BROWSERSTACK_ACCESS_KEY as env variables | ||
#userName: BROWSERSTACK_USERNAME | ||
#accessKey: BROWSERSTACK_ACCESS_KEY | ||
|
||
# ====================== | ||
# BrowserStack Reporting | ||
# ====================== | ||
# The following capabilities are used to set up reporting on BrowserStack: | ||
# Set 'projectName' to the name of your project. Example, Marketing Website | ||
projectName: Unity Sample App | ||
# Set `buildName` as the name of the job / testsuite being run | ||
buildName: iOS build | ||
# `buildIdentifier` is a unique id to differentiate every execution that gets appended to | ||
# buildName. Choose your buildIdentifier format from the available expressions: | ||
# ${BUILD_NUMBER} (Default): Generates an incremental counter with every execution | ||
# ${DATE_TIME}: Generates a Timestamp with every execution. Eg. 05-Nov-19:30 | ||
# Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests | ||
buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression} | ||
framework: pytest | ||
source: pytest-browserstack:sample-sdk:v1.0 | ||
|
||
# Set `app` to define the app that is to be used for testing. | ||
# It can either take the id of any uploaded app or the path of the app directly. | ||
#app: ./WikipediaSample.apk | ||
app: ./Payload.ipa #For running local tests | ||
|
||
# ======================================= | ||
# Platforms (Browsers / Devices to test) | ||
# ======================================= | ||
# Platforms object contains all the browser / device combinations you want to test on. | ||
# Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate) | ||
|
||
platforms: | ||
- platformName: ios | ||
deviceName: iPhone 14 Pro Max | ||
platformVersion: 16 | ||
|
||
# ======================= | ||
# Parallels per Platform | ||
# ======================= | ||
# The number of parallel threads to be used for each platform set. | ||
# BrowserStack's SDK runner will select the best strategy based on the configured value | ||
# | ||
# Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack | ||
# | ||
# Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack | ||
parallelsPerPlatform: 1 | ||
|
||
# ========================================== | ||
# BrowserStack Local | ||
# (For localhost, staging/private websites) | ||
# ========================================== | ||
# Set browserStackLocal to true if your website under test is not accessible publicly over the internet | ||
# Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction | ||
browserstackLocal: true # <boolean> (Default false) | ||
browserStackLocalOptions: | ||
#Options to be passed to BrowserStack local in-case of advanced configurations | ||
# localIdentifier: # <string> (Default: null) Needed if you need to run multiple instances of local. | ||
forceLocal: true # <boolean> (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. | ||
# Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections | ||
|
||
# =================== | ||
# Debugging features | ||
# =================== | ||
debug: false # <boolean> # Set to true if you need screenshots for every selenium command ran | ||
networkLogs: false # <boolean> Set to true to enable HAR logs capturing | ||
consoleLogs: errors # <string> Remote browser's console debug levels to be printed (Default: errors) | ||
# Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) | ||
acceptInsecureCerts: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import sys | ||
import time | ||
import unittest | ||
from pathlib import Path | ||
|
||
from appium import webdriver | ||
from appium.options.ios import XCUITestOptions | ||
from appium.webdriver.common.appiumby import AppiumBy | ||
from appium.webdriver.webdriver import WebDriver | ||
from selenium.webdriver.support.ui import WebDriverWait | ||
from selenium.webdriver.support import expected_conditions as EC | ||
|
||
from alttester import AltDriver, By | ||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'src')) | ||
from fetch_otp import EMAIL, fetch_code | ||
|
||
# To run this test on an actual Android device: appium --base-path /wd/hub --allow-insecure chromedriver_autodownload | ||
class TestBase(unittest.TestCase): | ||
altdriver = None | ||
appium_driver = None | ||
|
||
@classmethod | ||
def setUpClass(cls): | ||
# https://appium.github.io/appium-xcuitest-driver/latest/preparation/real-device-config/ | ||
options = XCUITestOptions() | ||
options.app = "./Payload.ipa" | ||
options.show_xcode_log = True | ||
options.xcode_org_id = "APPLE_TEAM_ID" # Replace with Apple Team ID | ||
options.auto_accept_alerts = True | ||
|
||
cls.appium_driver = webdriver.Remote('https://hub-cloud.browserstack.com/wd/hub/', options=options) | ||
|
||
time.sleep(10) | ||
cls.altdriver = AltDriver() | ||
|
||
@classmethod | ||
def tearDownClass(cls): | ||
print("\nEnding") | ||
cls.altdriver.stop() | ||
cls.appium_driver.quit() | ||
|
||
def test_1_pkce_login(self): | ||
# Select use PKCE auth | ||
self.altdriver.find_object(By.NAME, "PKCE").tap() | ||
|
||
# Wait for unauthenticated screen | ||
self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") | ||
|
||
# Login | ||
loginBtn = self.altdriver.wait_for_object(By.NAME, "LoginBtn") | ||
loginBtn.tap() | ||
|
||
driver = self.appium_driver | ||
|
||
# Wait for the ASWebAuthenticationSession context to appear | ||
WebDriverWait(driver, 30).until(lambda d: len(d.contexts) > 2) | ||
contexts = driver.contexts | ||
|
||
target_context = None | ||
|
||
# Since it's unclear which WebView context contains the email field on iOS, | ||
# we need to iterate through each context to identify the correct one. | ||
for context in contexts: | ||
if context == "NATIVE_APP": | ||
continue | ||
|
||
driver.switch_to.context(context) | ||
|
||
try: | ||
# Attempt to find the email input field | ||
email_field = WebDriverWait(driver, 5).until( | ||
EC.presence_of_element_located((AppiumBy.XPATH, "//input[@name='address']")) | ||
) | ||
# Found email | ||
target_context = context | ||
|
||
email_field.send_keys("[email protected]") | ||
submit_button = driver.find_element(by=AppiumBy.XPATH, value="//form/div/div/div[2]/button") | ||
submit_button.click() | ||
|
||
time.sleep(10) # Wait for OTP | ||
|
||
code = fetch_gmail_code() | ||
assert code, "Failed to fetch OTP from Gmail" | ||
print(f"Successfully fetched OTP: {code}") | ||
|
||
# Unlike on Android, each digit must be entered into a separate input field on iOS. | ||
for i, digit in enumerate(code): | ||
otp_field = driver.find_element(by=AppiumBy.XPATH, value=f"//div[@id='passwordless_container']/div[{i + 1}]/input") | ||
otp_field.send_keys(digit) | ||
|
||
# Wait for authenticated screen | ||
self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") | ||
|
||
break | ||
except: | ||
# If the field is not found, continue to the next context | ||
print(f"Email field not found in context: {context}") | ||
|
||
# If target context was not found, raise an error | ||
if not target_context: | ||
raise Exception("Could not find the email field in any webview context.") |