1
+ import sys
2
+ import time
3
+ import unittest
4
+ from pathlib import Path
5
+
6
+ from appium import webdriver
7
+ from appium .options .ios import XCUITestOptions
8
+ from appium .webdriver .common .appiumby import AppiumBy
9
+ from appium .webdriver .webdriver import WebDriver
10
+ from selenium .webdriver .support .ui import WebDriverWait
11
+ from selenium .webdriver .support import expected_conditions as EC
12
+
13
+ from alttester import AltDriver , By
14
+
15
+ sys .path .insert (0 , str (Path (__file__ ).resolve ().parent .parent / 'src' ))
16
+ from fetch_otp import EMAIL , fetch_code
17
+
18
+ # To run this test on an actual Android device: appium --base-path /wd/hub --allow-insecure chromedriver_autodownload
19
+ class TestBase (unittest .TestCase ):
20
+ altdriver = None
21
+ appium_driver = None
22
+
23
+ @classmethod
24
+ def setUpClass (cls ):
25
+ # https://appium.github.io/appium-xcuitest-driver/latest/preparation/real-device-config/
26
+ options = XCUITestOptions ()
27
+ options .app = "./Payload.ipa"
28
+ options .show_xcode_log = True
29
+ options .xcode_org_id = "APPLE_TEAM_ID" # Replace with Apple Team ID
30
+ options .auto_accept_alerts = True
31
+
32
+ cls .appium_driver = webdriver .Remote ('https://hub-cloud.browserstack.com/wd/hub/' , options = options )
33
+
34
+ time .sleep (10 )
35
+ cls .altdriver = AltDriver ()
36
+
37
+ @classmethod
38
+ def tearDownClass (cls ):
39
+ print ("\n Ending" )
40
+ cls .altdriver .stop ()
41
+ cls .appium_driver .quit ()
42
+
43
+ def test_1_pkce_login (self ):
44
+ # Select use PKCE auth
45
+ self .altdriver .find_object (By .NAME , "PKCE" ).tap ()
46
+
47
+ # Wait for unauthenticated screen
48
+ self .altdriver .wait_for_current_scene_to_be ("UnauthenticatedScene" )
49
+
50
+ # Login
51
+ loginBtn = self .altdriver .wait_for_object (By .NAME , "LoginBtn" )
52
+ loginBtn .tap ()
53
+
54
+ driver = self .appium_driver
55
+
56
+ # Wait for the ASWebAuthenticationSession context to appear
57
+ WebDriverWait (driver , 30 ).until (lambda d : len (d .contexts ) > 2 )
58
+ contexts = driver .contexts
59
+
60
+ target_context = None
61
+
62
+ # Since it's unclear which WebView context contains the email field on iOS,
63
+ # we need to iterate through each context to identify the correct one.
64
+ for context in contexts :
65
+ if context == "NATIVE_APP" :
66
+ continue
67
+
68
+ driver .switch_to .context (context )
69
+
70
+ try :
71
+ # Attempt to find the email input field
72
+ email_field = WebDriverWait (driver , 5 ).until (
73
+ EC .presence_of_element_located ((AppiumBy .XPATH , "//input[@name='address']" ))
74
+ )
75
+ # Found email
76
+ target_context = context
77
+
78
+ email_field .
send_keys (
"[email protected] " )
79
+ submit_button = driver .find_element (by = AppiumBy .XPATH , value = "//form/div/div/div[2]/button" )
80
+ submit_button .click ()
81
+
82
+ time .sleep (10 ) # Wait for OTP
83
+
84
+ code = fetch_gmail_code ()
85
+ assert code , "Failed to fetch OTP from Gmail"
86
+ print (f"Successfully fetched OTP: { code } " )
87
+
88
+ # Unlike on Android, each digit must be entered into a separate input field on iOS.
89
+ for i , digit in enumerate (code ):
90
+ otp_field = driver .find_element (by = AppiumBy .XPATH , value = f"//div[@id='passwordless_container']/div[{ i + 1 } ]/input" )
91
+ otp_field .send_keys (digit )
92
+
93
+ # Wait for authenticated screen
94
+ self .altdriver .wait_for_current_scene_to_be ("AuthenticatedScene" )
95
+
96
+ break
97
+ except :
98
+ # If the field is not found, continue to the next context
99
+ print (f"Email field not found in context: { context } " )
100
+
101
+ # If target context was not found, raise an error
102
+ if not target_context :
103
+ raise Exception ("Could not find the email field in any webview context." )
0 commit comments