From 61ee42965d8a708fc54511182bf9bf913a2c51b3 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 20 Nov 2024 10:55:54 +1300 Subject: [PATCH 1/7] test: refactor mac and windows ui tests --- .github/workflows/ui-tests.yml | 2 +- sample/Tests/src/device_code_login_windows.py | 66 ------ .../Tests/src/device_code_logout_windows.py | 33 --- sample/Tests/test/test.py | 35 ++-- sample/Tests/test/test_device_code_logout.py | 25 --- sample/Tests/test/test_mac.py | 116 +++++++---- .../test_mac_helpers.py} | 48 ++++- sample/Tests/test/test_windows.py | 125 +++++++++--- sample/Tests/test/test_windows_helpers.py | 151 ++++++++++++++ sample/Tests/test_windows.ps1 | 191 ------------------ 10 files changed, 395 insertions(+), 397 deletions(-) delete mode 100644 sample/Tests/src/device_code_login_windows.py delete mode 100644 sample/Tests/src/device_code_logout_windows.py delete mode 100644 sample/Tests/test/test_device_code_logout.py rename sample/Tests/{src/device_code_login.py => test/test_mac_helpers.py} (61%) create mode 100644 sample/Tests/test/test_windows_helpers.py delete mode 100644 sample/Tests/test_windows.ps1 diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index e1986d39..3f97c934 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -68,7 +68,7 @@ jobs: test_script: ./test_mac.sh - targetPlatform: StandaloneWindows64 runs-on: [self-hosted, windows] - test_script: ./test_windows.ps1 + test_script: pytest -xs test/test_windows.py::WindowsTest - targetPlatform: Android runs-on: [ self-hosted, macOS ] test_script: browserstack-sdk pytest -s ./test/test_android.py --browserstack.config "browserstack.android.yml" diff --git a/sample/Tests/src/device_code_login_windows.py b/sample/Tests/src/device_code_login_windows.py deleted file mode 100644 index 74829bd7..00000000 --- a/sample/Tests/src/device_code_login_windows.py +++ /dev/null @@ -1,66 +0,0 @@ -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.chrome.options import Options -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.keys import Keys -import time -from fetch_otp import EMAIL, fetch_code - -# Add chrome.exe to environment variable -# Download chrome driver and add to environment variable - -def main(): - print("Connect to Chrome") - # Set up Chrome options to connect to the existing Chrome instance - chrome_options = Options() - chrome_options.add_experimental_option("debuggerAddress", "localhost:9222") - # Connect to the existing Chrome instance - driver = webdriver.Chrome(options=chrome_options) - - print("Waiting for new window...") - WebDriverWait(driver, 60).until(EC.number_of_windows_to_be(2)) - - # Get all window handles - all_windows = driver.window_handles - - print("Find the new window") - new_window = [window for window in all_windows if window != driver.current_window_handle][0] - - print("Switch to the new window") - driver.switch_to.window(new_window) - - wait = WebDriverWait(driver, 60) - - print("Wait for email input...") - email_field = wait.until(EC.presence_of_element_located((By.ID, ':r1:'))) - print("Enter email") - email_field.send_keys(EMAIL) - email_field.send_keys(Keys.RETURN) - - # Wait for the OTP to arrive and page to load - print("Wait for OTP...") - time.sleep(10) - - print("Get OTP from Gmail...") - code = fetch_code() - if code: - print(f"Successfully fetched OTP: {code}") - else: - print("Failed to fetch OTP from Gmail") - driver.quit() - - print("Find OTP input...") - otp_field = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input[data-testid="passwordless_passcode__TextInput--0__input"]'))) - print("Enter OTP") - otp_field.send_keys(code) - - print("Wait for success page...") - success = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'h1[data-testid="device_success_title"]'))) - print("Connected to Passport!") - - driver.quit() - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/sample/Tests/src/device_code_logout_windows.py b/sample/Tests/src/device_code_logout_windows.py deleted file mode 100644 index 385e56c3..00000000 --- a/sample/Tests/src/device_code_logout_windows.py +++ /dev/null @@ -1,33 +0,0 @@ - -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.chrome.options import Options -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.keys import Keys - -def main(): - print("Connect to Chrome") - # Set up Chrome options to connect to the existing Chrome instance - chrome_options = Options() - chrome_options.add_experimental_option("debuggerAddress", "localhost:9222") - # Connect to the existing Chrome instance - driver = webdriver.Chrome(options=chrome_options) - - print("Waiting for new window...") - WebDriverWait(driver, 60).until(EC.number_of_windows_to_be(2)) - - # Get all window handles - all_windows = driver.window_handles - - print("Find the new window") - new_window = [window for window in all_windows if window != driver.current_window_handle][0] - - print("Switch to the new window") - driver.switch_to.window(new_window) - - driver.quit() - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/sample/Tests/test/test.py b/sample/Tests/test/test.py index cda45fd3..7593d747 100644 --- a/sample/Tests/test/test.py +++ b/sample/Tests/test/test.py @@ -2,13 +2,14 @@ import unittest import requests import re +import pytest from alttester import * class TestConfig: - EMAIL = "a1369a61-9149-4499-a75e-610523e2baa7@mailslurp.net" - PASSPORT_ID="email|673a7cc7219c150ace38cf60" - WALLET_ADDRESS = "0xf629c9f0fee71cce1b21a6e5b0db8df2e8cd7354" + EMAIL = "26b067b8-ef3a-4655-955a-19f157b35b6e@mailslurp.net" + PASSPORT_ID="email|673d0795219c150acebff862" + WALLET_ADDRESS = "0x9af9826a83581ddfa0bdd7754de8a741ce64ebe8" class UnityTest(unittest.TestCase): @@ -22,6 +23,7 @@ def setUpClass(cls): def tearDownClass(cls): cls.altdriver.stop() + @pytest.mark.skip(reason="Base test should not be executed directly") def test_0_other_functions(self): # Show set call timeout scene self.altdriver.find_object(By.NAME, "CallTimeout").tap() @@ -37,6 +39,7 @@ def test_0_other_functions(self): self.altdriver.find_object(By.NAME, "CancelButton").tap() self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + @pytest.mark.skip(reason="Base test should not be executed directly") def test_1_passport_functions(self): output = self.altdriver.find_object(By.NAME, "Output") @@ -61,6 +64,7 @@ def test_1_passport_functions(self): time.sleep(1) self.assertEqual("No linked addresses", output.get_text()) + @pytest.mark.skip(reason="Base test should not be executed directly") def test_2_imx_functions(self): output = self.altdriver.find_object(By.NAME, "Output") @@ -75,20 +79,20 @@ def test_2_imx_functions(self): # Register off-chain # Wait up to 3 times for "Passport account already registered" to appear - #attempts = 0 - #while attempts < 6: - # self.altdriver.find_object(By.NAME, "RegisterOffchainBtn").tap() - # self.assertEqual("Registering off-chain...", output.get_text()) - # time.sleep(20) - # if "Passport account already registered" in output.get_text(): - # break - # attempts += 1 + attempts = 0 + while attempts < 3: + self.altdriver.find_object(By.NAME, "RegisterOffchainBtn").tap() + self.assertEqual("Registering off-chain...", output.get_text()) + time.sleep(20) + if "Passport account already registered" in output.get_text(): + break + attempts += 1 # Assert that the desired text is found after waiting - #self.assertTrue( - # "Passport account already registered" in output.get_text(), - # f"Expected 'Passport account already registered' not found. Actual output: '{output.get_text()}'" - #) + self.assertTrue( + "Passport account already registered" in output.get_text(), + f"Expected 'Passport account already registered' not found. Actual output: '{output.get_text()}'" + ) # Get address self.altdriver.find_object(By.NAME, "GetAddressBtn").tap() @@ -158,6 +162,7 @@ def test_2_imx_functions(self): self.altdriver.find_object(By.NAME, "CancelButton").tap() self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + @pytest.mark.skip(reason="Base test should not be executed directly") def test_3_zkevm_functions(self): output = self.altdriver.find_object(By.NAME, "Output") diff --git a/sample/Tests/test/test_device_code_logout.py b/sample/Tests/test/test_device_code_logout.py deleted file mode 100644 index beb6cb92..00000000 --- a/sample/Tests/test/test_device_code_logout.py +++ /dev/null @@ -1,25 +0,0 @@ -import time -import unittest - -from alttester import * - -class MacTest(unittest.TestCase): - - altdriver = None - - @classmethod - def setUpClass(cls): - cls.altdriver = AltDriver() - - @classmethod - def tearDownClass(cls): - cls.altdriver.stop() - - def test_5_logout(self): - # Logout - self.altdriver.find_object(By.NAME, "LogoutBtn").tap() - - time.sleep(10) - - # Wait for authenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") diff --git a/sample/Tests/test/test_mac.py b/sample/Tests/test/test_mac.py index bd18c6d5..ece728e0 100644 --- a/sample/Tests/test/test_mac.py +++ b/sample/Tests/test/test_mac.py @@ -1,18 +1,19 @@ import time -import unittest -import requests -import re from alttester import * -from test import TestConfig +from test import TestConfig, UnityTest +from test_mac_helpers import login, open_sample_app, bring_sample_app_to_foreground, stop_chrome, stop_sample_app -class MacTest(unittest.TestCase): + +class MacTest(UnityTest): altdriver = None @classmethod def setUpClass(cls): + open_sample_app + cls.altdriver = AltDriver() @classmethod @@ -27,48 +28,48 @@ def test_1_device_code_login(self): self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") # Login - loginBtn = self.altdriver.wait_for_object(By.NAME, "LoginBtn") - loginBtn.tap() - + print("Logging in...") + login() + bring_sample_app_to_foreground() + self.altdriver.wait_for_object(By.NAME, "LoginBtn").tap() # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + print("Logged in") - def test_2_device_code_connect_imx(self): - # Select use device code auth - self.altdriver.find_object(By.NAME, "DeviceCodeAuth").tap() + def test_2_other_functions(self): + self.test_0_other_functions() - # Wait for unauthenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + def test_3_passport_functions(self): + self.test_1_passport_functions() - # Connect IMX - connectBtn = self.altdriver.wait_for_object(By.NAME, "ConnectBtn") - connectBtn.tap() + def test_4_imx_functions(self): + self.test_2_imx_functions() - # Wait for authenticated screen - self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + def test_5_zkevm_functions(self): + self.test_3_zkevm_functions() - # Get access token - self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap() - output = self.altdriver.find_object(By.NAME, "Output") - self.assertTrue(len(output.get_text()) > 50) + def test_6_device_code_relogin(self): + # Close and reopen app + stop_sample_app() + open_sample_app() - # Get address without having to click Connect to IMX button - self.altdriver.find_object(By.NAME, "GetAddressBtn").tap() - self.assertEqual(TestConfig.WALLET_ADDRESS, output.get_text()) + # Restart AltTester + self.altdriver.stop() + self.altdriver = AltDriver() + time.sleep(5) - def test_3_device_code_relogin(self): # Select use device code auth self.altdriver.find_object(By.NAME, "DeviceCodeAuth").tap() - # Wait for unauthenticated screen self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") # Relogin - reloginBtn = self.altdriver.wait_for_object(By.NAME, "ReloginBtn") - reloginBtn.tap() + print("Re-logging in...") + self.altdriver.wait_for_object(By.NAME, "ReloginBtn").tap() # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + print("Re-logged in") # Get access token self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap() @@ -79,19 +80,30 @@ def test_3_device_code_relogin(self): self.altdriver.find_object(By.NAME, "ConnectBtn").tap() self.assertEqual("Connected to IMX", output.get_text()) - def test_4_device_code_reconnect(self): + self.altdriver.stop() + + def test_7_reconnect_device_code_connect_imx(self): + # Close and reopen app + stop_sample_app() + open_sample_app() + + # Restart AltTester + self.altdriver.stop() + self.altdriver = AltDriver() + time.sleep(5) + # Select use device code auth self.altdriver.find_object(By.NAME, "DeviceCodeAuth").tap() - # Wait for unauthenticated screen self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - # Relogin - reloginBtn = self.altdriver.wait_for_object(By.NAME, "ReconnectBtn") - reloginBtn.tap() + # Reconnect + print("Reconnecting...") + self.altdriver.wait_for_object(By.NAME, "ReconnectBtn").tap() # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + print("Reconnected") # Get access token self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap() @@ -102,11 +114,41 @@ def test_4_device_code_reconnect(self): self.altdriver.find_object(By.NAME, "GetAddressBtn").tap() self.assertEqual(TestConfig.WALLET_ADDRESS, output.get_text()) - def test_5_logout(self): # Logout + print("Logging out...") self.altdriver.find_object(By.NAME, "LogoutBtn").tap() + time.sleep(5) + bring_sample_app_to_foreground() + # Wait for authenticated screen + self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + stop_chrome() + print("Logged out") + + # Connect IMX + print("Logging in and connecting to IMX...") + self.altdriver.wait_for_object(By.NAME, "ConnectBtn").tap() + login() + bring_sample_app_to_foreground() + # Wait for authenticated screen + self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + print("Logged in and connected to IMX") + stop_chrome() + + # Get access token + self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap() + output = self.altdriver.find_object(By.NAME, "Output") + self.assertTrue(len(output.get_text()) > 50) - time.sleep(10) + # Get address without having to click Connect to IMX button + self.altdriver.find_object(By.NAME, "GetAddressBtn").tap() + self.assertEqual(TestConfig.WALLET_ADDRESS, output.get_text()) + # Logout + print("Logging out...") + self.altdriver.find_object(By.NAME, "LogoutBtn").tap() + time.sleep(5) + bring_sample_app_to_foreground() # Wait for authenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") \ No newline at end of file + self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + stop_chrome() + print("Logged out") \ No newline at end of file diff --git a/sample/Tests/src/device_code_login.py b/sample/Tests/test/test_mac_helpers.py similarity index 61% rename from sample/Tests/src/device_code_login.py rename to sample/Tests/test/test_mac_helpers.py index da7aa2ef..ab0570e2 100644 --- a/sample/Tests/src/device_code_login.py +++ b/sample/Tests/test/test_mac_helpers.py @@ -1,3 +1,9 @@ +import os +import sys +import subprocess +import time +from pathlib import Path + from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options @@ -6,11 +12,13 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.keys import Keys import time + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'src')) from fetch_otp import EMAIL, fetch_code # brew install chromedriver -def main(): +def login(): print("Connect to Chrome") # Set up Chrome options to connect to the existing Chrome instance chrome_options = Options() @@ -65,5 +73,39 @@ def main(): driver.quit() -if __name__ == "__main__": - main() \ No newline at end of file +def open_sample_app(): + print("Opening Unity sample app...") + subprocess.Popen(["open", "SampleApp.app"], shell=False) + time.sleep(5) + print("Unity sample app opened successfully.") + +def stop_sample_app(): + print("Stopping sample app...") + try: + # Get the PID of the sample app using ps, grep, and awk + cmd = f"ps aux | grep 'Sample.app' | grep -v grep | awk '{{print $2}}'" + pid = subprocess.check_output(cmd, shell=True, text=True).strip() + + if pid: + # Terminate the process using the PID + subprocess.run(["kill", pid]) + print(f"Sample app (PID {pid}) has been terminated.") + else: + print("Sample app is not running.") + except subprocess.CalledProcessError: + print("Failed to find the sample app process.") + + time.sleep(5) + print("Stopped sample app.") + +def bring_sample_app_to_foreground(app_name): + print("Bringing Unity sample app to the foreground...") + subprocess.run( + ['osascript', '-e', f'tell application "{app_name}" to activate'], + check=True + ) + +def stop_chrome(): + print("Stopping Chrome all Chrome instances...") + subprocess.run(["pkill", "-f", "chrome"], check=True) + print("Stopped Chrome.") \ No newline at end of file diff --git a/sample/Tests/test/test_windows.py b/sample/Tests/test/test_windows.py index e5124d67..d07cf3eb 100644 --- a/sample/Tests/test/test_windows.py +++ b/sample/Tests/test/test_windows.py @@ -1,72 +1,133 @@ import time -import unittest -import requests from alttester import * -from test import TestConfig +from test import TestConfig, UnityTest +from test_windows_helpers import login, open_sample_app, launch_chrome, bring_sample_app_to_foreground, stop_chrome, stop_sample_app -class WindowsTest(unittest.TestCase): +class WindowsTest(UnityTest): altdriver = None @classmethod def setUpClass(cls): + open_sample_app() + cls.altdriver = AltDriver() @classmethod def tearDownClass(cls): - cls.altdriver.stop() + cls.altdriver.stop() def test_1_device_code_login(self): - # Login - loginBtn = self.altdriver.wait_for_object(By.NAME, "LoginBtn") - loginBtn.tap() + launch_chrome() + + bring_sample_app_to_foreground() + self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + + # Login + print("Logging in...") + self.altdriver.wait_for_object(By.NAME, "LoginBtn").tap() + login() + bring_sample_app_to_foreground() # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + print("Logged in") - def test_2_device_code_connect_imx(self): - # Connect IMX - connectBtn = self.altdriver.wait_for_object(By.NAME, "ConnectBtn") - connectBtn.tap() + stop_chrome() + + def test_2_other_functions(self): + self.test_0_other_functions() + + def test_3_passport_functions(self): + self.test_1_passport_functions() + + def test_4_imx_functions(self): + self.test_2_imx_functions() + + def test_5_zkevm_functions(self): + self.test_3_zkevm_functions() + + def test_6_relogin(self): + # Close and reopen app + stop_sample_app() + open_sample_app() + + # Restart AltTester + self.altdriver.stop() + self.altdriver = AltDriver() + time.sleep(5) + + # Relogin + print("Re-logging in...") + self.altdriver.wait_for_object(By.NAME, "ReloginBtn").tap() # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + print("Re-logged in") # Get access token self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap() output = self.altdriver.find_object(By.NAME, "Output") self.assertTrue(len(output.get_text()) > 50) - # Get address without having to click Connect to IMX button - self.altdriver.find_object(By.NAME, "GetAddressBtn").tap() - self.assertEqual(TestConfig.WALLET_ADDRESS, output.get_text()) + # Click Connect to IMX button + self.altdriver.find_object(By.NAME, "ConnectBtn").tap() + self.assertEqual("Connected to IMX", output.get_text()) - def test_3_device_code_relogin(self): - # Relogin - reloginBtn = self.altdriver.wait_for_object(By.NAME, "ReloginBtn") - reloginBtn.tap() + self.altdriver.stop() + + def test_7_reconnect_device_code_connect_imx(self): + # Close and reopen app + stop_sample_app() + open_sample_app() + + # Restart AltTester + self.altdriver.stop() + self.altdriver = AltDriver() + time.sleep(5) + + # Reconnect + print("Reconnecting...") + self.altdriver.wait_for_object(By.NAME, "ReconnectBtn").tap() # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + print("Reconnected") # Get access token self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap() output = self.altdriver.find_object(By.NAME, "Output") self.assertTrue(len(output.get_text()) > 50) - # Click Connect to IMX button - self.altdriver.find_object(By.NAME, "ConnectBtn").tap() - self.assertEqual("Connected to IMX", output.get_text()) + # Get address without having to click Connect to IMX button + self.altdriver.find_object(By.NAME, "GetAddressBtn").tap() + self.assertEqual(TestConfig.WALLET_ADDRESS, output.get_text()) - def test_4_device_code_reconnect(self): - # Relogin - reloginBtn = self.altdriver.wait_for_object(By.NAME, "ReconnectBtn") - reloginBtn.tap() + # Logout + print("Logging out...") + launch_chrome() + bring_sample_app_to_foreground() + self.altdriver.find_object(By.NAME, "LogoutBtn").tap() + time.sleep(5) + bring_sample_app_to_foreground() + # Wait for authenticated screen + self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + stop_chrome() + print("Logged out") + # Connect IMX + print("Logging in and connecting to IMX...") + launch_chrome() + bring_sample_app_to_foreground() + self.altdriver.wait_for_object(By.NAME, "ConnectBtn").tap() + login() + bring_sample_app_to_foreground() # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + print("Logged in and connected to IMX") + stop_chrome() # Get access token self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap() @@ -76,3 +137,15 @@ def test_4_device_code_reconnect(self): # Get address without having to click Connect to IMX button self.altdriver.find_object(By.NAME, "GetAddressBtn").tap() self.assertEqual(TestConfig.WALLET_ADDRESS, output.get_text()) + + # Logout + launch_chrome() + bring_sample_app_to_foreground() + print("Logging out...") + self.altdriver.find_object(By.NAME, "LogoutBtn").tap() + time.sleep(5) + bring_sample_app_to_foreground() + # Wait for authenticated screen + self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + stop_chrome() + print("Logged out") diff --git a/sample/Tests/test/test_windows_helpers.py b/sample/Tests/test/test_windows_helpers.py new file mode 100644 index 00000000..269b645e --- /dev/null +++ b/sample/Tests/test/test_windows_helpers.py @@ -0,0 +1,151 @@ +import os +import sys +import subprocess +import time +from pathlib import Path + +from selenium import webdriver +from selenium.webdriver.chrome.service import Service +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.keys import Keys + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'src')) +from fetch_otp import EMAIL, fetch_code + +# Add chrome.exe to environment variable +# Download chrome driver and add to environment variable + +def login(): + print("Connect to Chrome") + # Set up Chrome options to connect to the existing Chrome instance + chrome_options = Options() + chrome_options.add_experimental_option("debuggerAddress", "localhost:9222") + # Connect to the existing Chrome instance + driver = webdriver.Chrome(options=chrome_options) + + print("Open a window on Chrome") + # Get the original window handle + original_window = driver.current_window_handle + + print("Waiting for new window...") + WebDriverWait(driver, 60).until(EC.number_of_windows_to_be(2)) + + # Get all window handles + all_windows = driver.window_handles + + print("Find the new window") + new_window = [window for window in all_windows if window != driver.current_window_handle][0] + + print("Switch to the new window") + driver.switch_to.window(new_window) + + wait = WebDriverWait(driver, 60) + + print("Wait for email input...") + email_field = wait.until(EC.presence_of_element_located((By.ID, ':r1:'))) + print("Enter email") + email_field.send_keys(EMAIL) + email_field.send_keys(Keys.RETURN) + + # Wait for the OTP to arrive and page to load + print("Wait for OTP...") + time.sleep(10) + + print("Get OTP from Gmail...") + code = fetch_code() + if code: + print(f"Successfully fetched OTP: {code}") + else: + print("Failed to fetch OTP from Gmail") + driver.quit() + + print("Find OTP input...") + otp_field = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input[data-testid="passwordless_passcode__TextInput--0__input"]'))) + print("Enter OTP") + otp_field.send_keys(code) + + print("Wait for success page...") + success = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'h1[data-testid="device_success_title"]'))) + print("Connected to Passport!") + + driver.quit() + +def open_sample_app(): + print("Opening Unity sample app...") + subprocess.Popen(["SampleApp.exe"], shell=True) + time.sleep(10) + print("Unity sample app opened successfully.") + +def stop_sample_app(): + print("Stopping sample app...") + powershell_command = """ + $process = Get-Process -Name "SampleApp" -ErrorAction SilentlyContinue + if ($process) { + Stop-Process -Id $process.Id + Write-Output "SampleApp.exe has been closed." + } else { + Write-Output "SampleApp.exe is not running." + } + """ + subprocess.run(["powershell.exe", "-Command", powershell_command], check=True) + time.sleep(5) + print("Stopped sample app.") + +def bring_sample_app_to_foreground(): + powershell_script_path = "./switch-app.ps1" + + print("Bring Unity sample app to the foreground.") + + command = [ + "powershell.exe", + "-Command", + f"Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process; & '{powershell_script_path}' -appName 'Immutable Sample'" + ] + + subprocess.run(command, check=True) + time.sleep(10) + +def launch_chrome(): + print("Starting Chrome...") + chrome_paths = [ + r"C:\Program Files\Google\Chrome\Application\chrome.exe", + r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" + ] + + chrome_path = None + for path in chrome_paths: + if os.path.exists(path): + chrome_path = path + break + + if not chrome_path: + print("Chrome executable not found.") + exit(1) + + subprocess.run([ + "powershell.exe", + "-Command", + f"Start-Process -FilePath '{chrome_path}' -ArgumentList '--remote-debugging-port=9222'" + ], check=True) + + time.sleep(5) + +def stop_chrome(): + print("Stopping Chrome...") + powershell_command = """ + $process = Get-Process -Name "chrome" -ErrorAction SilentlyContinue + if ($process) { + $process | ForEach-Object { + Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue + } + Write-Output "All Chrome processes have been closed." + } else { + Write-Output "Chrome is not running." + } + """ + subprocess.run(["powershell.exe", "-Command", powershell_command], check=True) + time.sleep(5) + print("Stopped Chrome.") \ No newline at end of file diff --git a/sample/Tests/test_windows.ps1 b/sample/Tests/test_windows.ps1 deleted file mode 100644 index a1aa5787..00000000 --- a/sample/Tests/test_windows.ps1 +++ /dev/null @@ -1,191 +0,0 @@ -# Function to stop the Unity sample app if it's running -function Stop-SampleApp { - $process = Get-Process -Name "SampleApp" -ErrorAction SilentlyContinue - if ($process) { - Stop-Process -Id $process.Id - Write-Output "SampleApp.exe has been closed." - } else { - Write-Output "SampleApp.exe is not running." - } - Start-Sleep -Seconds 5 -} - -# Function to start the Unity sample app -function Start-SampleApp { - Write-Output "Starting Unity sample app..." - Start-Process -FilePath "SampleApp.exe" - Start-Sleep -Seconds 10 -} - -# Function to bring the Unity sample app to the foreground -function Bring-SampleAppToForeground { - $POWERSHELL_SCRIPT_PATH = "./switch-app.ps1" - Write-Output "Bringing Unity sample app to the foreground..." - powershell.exe -Command "Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process; & '$POWERSHELL_SCRIPT_PATH' -appName 'Immutable Sample'" -} - -# Function to run pytest tests -function Run-Pytest { - param ( - [string]$testFile - ) - Write-Output "Running pytest for $testFile..." - $process = Start-Process -FilePath "pytest" -ArgumentList "-x", $testFile -NoNewWindow -PassThru -Wait - if ($process.ExitCode -ne 0) { - Write-Output "Test failed for $testFile. Stopping execution." - exit $process.ExitCode - } -} - -# Function to stop Chrome if it's running -function Stop-Chrome { - Write-Output "Stopping Chrome.." - $process = Get-Process -Name "chrome" -ErrorAction SilentlyContinue - if ($process) { - $process | ForEach-Object { - Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue - } - Write-Output "All Chrome processes have been closed." - } else { - Write-Output "Chrome is not running." - } - - Start-Sleep -Seconds 10 -} - -# Login -function Login { - param ( - [string]$testFile - ) - # Start Chrome for remote debugging - Write-Output "Starting Chrome..." - - $chromePath = "C:\Program Files\Google\Chrome\Application\chrome.exe" - - if (-not (Test-Path $chromePath)) { - $chromePath = "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" - } - - if (-not (Test-Path $chromePath)) { - Write-Output "Chrome executable not found." - exit - } - - Start-Process -FilePath $chromePath -ArgumentList "--remote-debugging-port=9222" - - # Run Python script for login - Write-Output "Running python script to login..." - $pythonProcess = Start-Process -FilePath "python" -ArgumentList "src/device_code_login_windows.py" -NoNewWindow -PassThru - Write-Output "Python script running in the background..." - - Start-Sleep -Seconds 5 - - Bring-SampleAppToForeground - - Write-Output "Running login test..." - $pytestProcess = Start-Process -FilePath "pytest" -ArgumentList $testFile -NoNewWindow -PassThru - - $pythonProcess | Wait-Process - - Bring-SampleAppToForeground - - $pytestProcess | Wait-Process - - Stop-Chrome -} - -# Logout -function Logout { - # Start Chrome for remote debugging - Write-Output "Starting Chrome..." - $chromePath = "C:\Program Files\Google\Chrome\Application\chrome.exe" - - if (-not (Test-Path $chromePath)) { - $chromePath = "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" - } - - if (-not (Test-Path $chromePath)) { - Write-Output "Chrome executable not found." - exit - } - - Start-Process -FilePath $chromePath -ArgumentList "--remote-debugging-port=9222" - - Write-Output "Running python script to logout..." - $pythonProcess = Start-Process -FilePath "python" -ArgumentList "src/device_code_logout_windows.py" -NoNewWindow -PassThru - Start-Sleep -Seconds 5 - - Bring-SampleAppToForeground - - Write-Output "Running logout test..." - $pytestProcess = Start-Process -FilePath "pytest" -ArgumentList "test/test_device_code_logout.py" -NoNewWindow -PassThru - - $pythonProcess | Wait-Process - - Bring-SampleAppToForeground - - $pytestProcess | Wait-Process - - Stop-Chrome -} - -# Capture the start time -$startTime = Get-Date - -# Start Unity sample app -Start-SampleApp - -# Login -Login "test/test_windows.py::WindowsTest::test_1_device_code_login" - -# Run IMX and zkEVM tests -Run-Pytest "test/test.py" -if (-not $?) { - Write-Output "Tests failed. Stopping execution." - exit 1 -} - -# Relogin -Stop-SampleApp -Start-SampleApp -Run-Pytest "test/test_windows.py::WindowsTest::test_3_device_code_relogin" -if (-not $?) { - Write-Output "Relogin test failed. Stopping execution." - exit 1 -} - -# Reconnect -Stop-SampleApp -Start-SampleApp -Run-Pytest "test/test_windows.py::WindowsTest::test_4_device_code_reconnect" -if (-not $?) { - Write-Output "Reconnect test failed. Stopping execution." - exit 1 -} - -# Logout -Logout - -# Connect IMX -Stop-SampleApp -Start-SampleApp -Write-Output "Connect to IMX..." -Login "test/test_windows.py::WindowsTest::test_2_device_code_connect_imx" - -# Bring the Unity sample app to the foreground -Bring-SampleAppToForeground - -# Logout -Logout - -# Final stop of Unity sample app -Stop-SampleApp - -# Capture the end time -$endTime = Get-Date - -# Calculate and display the elapsed time -$elapsedTime = $endTime - $startTime -Write-Output "All tests completed." -Write-Output "Elapsed time: $($elapsedTime.TotalMinutes) minutes" \ No newline at end of file From f5ea8baa5b33f1933048ae4a4f833031d7cf7a28 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 20 Nov 2024 11:11:41 +1300 Subject: [PATCH 2/7] test: comment out mobile ui tests, fix otp email --- .github/workflows/ui-tests.yml | 50 +++++++++++++-------------- sample/Tests/src/fetch_otp.py | 4 +-- sample/Tests/test/test_mac_helpers.py | 2 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 3f97c934..feca9d71 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -69,9 +69,9 @@ jobs: - targetPlatform: StandaloneWindows64 runs-on: [self-hosted, windows] test_script: pytest -xs test/test_windows.py::WindowsTest - - targetPlatform: Android - runs-on: [ self-hosted, macOS ] - test_script: browserstack-sdk pytest -s ./test/test_android.py --browserstack.config "browserstack.android.yml" + # - targetPlatform: Android + # runs-on: [ self-hosted, macOS ] + # test_script: browserstack-sdk pytest -s ./test/test_android.py --browserstack.config "browserstack.android.yml" concurrency: group: test-${{ matrix.targetPlatform }} runs-on: ${{ matrix.runs-on }} @@ -107,26 +107,26 @@ jobs: BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} working-directory: sample/Tests run: ${{ matrix.test_script }} - test-ios: - name: Run iOS UI tests 🧪 - runs-on: [ self-hosted, macOS ] - steps: - - uses: actions/checkout@v3 - with: - lfs: true - - name: build iOS app - working-directory: sample - run: ./build_ios.sh - - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - name: Install dependencies - run: pip install -r "sample/Tests/requirements.txt" - - name: Run UI tests - env: - MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} - BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} - BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - working-directory: sample/Tests - run: browserstack-sdk pytest -s ./test/test_ios.py --browserstack.config "browserstack.ios.yml" + # test-ios: + # name: Run iOS UI tests 🧪 + # runs-on: [ self-hosted, macOS ] + # steps: + # - uses: actions/checkout@v3 + # with: + # lfs: true + # - name: build iOS app + # working-directory: sample + # run: ./build_ios.sh + # - uses: actions/setup-python@v4 + # with: + # python-version: "3.10" + # - name: Install dependencies + # run: pip install -r "sample/Tests/requirements.txt" + # - name: Run UI tests + # env: + # MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} + # BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + # BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + # working-directory: sample/Tests + # run: browserstack-sdk pytest -s ./test/test_ios.py --browserstack.config "browserstack.ios.yml" \ No newline at end of file diff --git a/sample/Tests/src/fetch_otp.py b/sample/Tests/src/fetch_otp.py index 2cce2725..1a8459ba 100644 --- a/sample/Tests/src/fetch_otp.py +++ b/sample/Tests/src/fetch_otp.py @@ -3,8 +3,8 @@ from mailslurp_client.api import InboxControllerApi, WaitForControllerApi import re -INBOX_ID = "a1369a61-9149-4499-a75e-610523e2baa7" -EMAIL = "a1369a61-9149-4499-a75e-610523e2baa7@mailslurp.net" +INBOX_ID = "26b067b8-ef3a-4655-955a-19f157b35b6e" +EMAIL = "26b067b8-ef3a-4655-955a-19f157b35b6e@mailslurp.net" def get_mailslurp_client(): configuration = mailslurp_client.Configuration() diff --git a/sample/Tests/test/test_mac_helpers.py b/sample/Tests/test/test_mac_helpers.py index ab0570e2..532edca7 100644 --- a/sample/Tests/test/test_mac_helpers.py +++ b/sample/Tests/test/test_mac_helpers.py @@ -59,7 +59,7 @@ def login(): if code: print(f"Successfully fetched OTP: {code}") else: - print("Failed to fetch OTP from Gmail") + print("Failed to fetch OTP from MailSlurp") driver.quit() print("Find OTP input...") From 5a8ab909c66aeb6346e0721ff7634046aef2ff6a Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 20 Nov 2024 11:23:26 +1300 Subject: [PATCH 3/7] test: close sample app at the end of ui tests --- sample/Tests/test/test_mac.py | 2 +- sample/Tests/test/test_windows.py | 4 ++-- sample/Tests/test/test_windows_helpers.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sample/Tests/test/test_mac.py b/sample/Tests/test/test_mac.py index ece728e0..f0da1806 100644 --- a/sample/Tests/test/test_mac.py +++ b/sample/Tests/test/test_mac.py @@ -13,11 +13,11 @@ class MacTest(UnityTest): @classmethod def setUpClass(cls): open_sample_app - cls.altdriver = AltDriver() @classmethod def tearDownClass(cls): + stop_sample_app() cls.altdriver.stop() def test_1_device_code_login(self): diff --git a/sample/Tests/test/test_windows.py b/sample/Tests/test/test_windows.py index d07cf3eb..93893017 100644 --- a/sample/Tests/test/test_windows.py +++ b/sample/Tests/test/test_windows.py @@ -12,12 +12,12 @@ class WindowsTest(UnityTest): @classmethod def setUpClass(cls): open_sample_app() - cls.altdriver = AltDriver() @classmethod def tearDownClass(cls): - cls.altdriver.stop() + cls.altdriver.stop() + stop_sample_app() def test_1_device_code_login(self): launch_chrome() diff --git a/sample/Tests/test/test_windows_helpers.py b/sample/Tests/test/test_windows_helpers.py index 269b645e..d35e68dd 100644 --- a/sample/Tests/test/test_windows_helpers.py +++ b/sample/Tests/test/test_windows_helpers.py @@ -54,12 +54,12 @@ def login(): print("Wait for OTP...") time.sleep(10) - print("Get OTP from Gmail...") + print("Get OTP from MailSlurp...") code = fetch_code() if code: print(f"Successfully fetched OTP: {code}") else: - print("Failed to fetch OTP from Gmail") + print("Failed to fetch OTP from MailSlurp") driver.quit() print("Find OTP input...") From 2fcb28dc523e850afc6ab099bf04d46c2ea5e0a7 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 20 Nov 2024 13:10:59 +1300 Subject: [PATCH 4/7] test: simplify mac ui tests --- sample/Tests/src/fetch_otp.py | 2 +- sample/Tests/test/test_mac.py | 91 +++++++++++++++++++-- sample/Tests/test/test_mac_helpers.py | 110 +++++--------------------- sample/Tests/test/test_windows.py | 4 + 4 files changed, 106 insertions(+), 101 deletions(-) diff --git a/sample/Tests/src/fetch_otp.py b/sample/Tests/src/fetch_otp.py index 1a8459ba..1b406388 100644 --- a/sample/Tests/src/fetch_otp.py +++ b/sample/Tests/src/fetch_otp.py @@ -1,6 +1,6 @@ import os import mailslurp_client -from mailslurp_client.api import InboxControllerApi, WaitForControllerApi +from mailslurp_client.api import WaitForControllerApi import re INBOX_ID = "26b067b8-ef3a-4655-955a-19f157b35b6e" diff --git a/sample/Tests/test/test_mac.py b/sample/Tests/test/test_mac.py index f0da1806..6415ff1d 100644 --- a/sample/Tests/test/test_mac.py +++ b/sample/Tests/test/test_mac.py @@ -1,18 +1,31 @@ +import sys import time +from pathlib import Path + +from selenium import webdriver +from selenium.webdriver.chrome.service import Service +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.by import By as SeleniumBy +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.keys import Keys from alttester import * from test import TestConfig, UnityTest -from test_mac_helpers import login, open_sample_app, bring_sample_app_to_foreground, stop_chrome, stop_sample_app +from test_mac_helpers import open_sample_app, bring_sample_app_to_foreground, stop_sample_app +sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'src')) +from fetch_otp import fetch_code class MacTest(UnityTest): altdriver = None + seleniumdriver = None @classmethod def setUpClass(cls): - open_sample_app + open_sample_app() cls.altdriver = AltDriver() @classmethod @@ -20,6 +33,57 @@ def tearDownClass(cls): stop_sample_app() cls.altdriver.stop() + @classmethod + def setupChrome(cls): + print("Connect to Chrome") + chrome_options = Options() + chrome_options.add_argument('--remote-debugging-port=9222') + + # Initialise Chrome driver + cls.seleniumdriver = webdriver.Chrome(options=chrome_options) + + print("Open a window on Chrome") + cls.seleniumdriver.current_window_handle + + @classmethod + def login(cls): + print("Waiting for new window...") + WebDriverWait(cls.seleniumdriver, 30).until(EC.number_of_windows_to_be(2)) + + # Switch to the new window + all_windows = cls.seleniumdriver.window_handles + new_window = next(window for window in all_windows if window != cls.seleniumdriver.current_window_handle) + print("Switched to new window") + + # Wait for email input and enter email + email_field = WebDriverWait(cls.seleniumdriver, 60).until(EC.presence_of_element_located((SeleniumBy.ID, ':r1:'))) + print("Entering email...") + email_field.send_keys(TestConfig.EMAIL) + email_field.send_keys(Keys.RETURN) + + # Wait for OTP + print("Waiting for OTP...") + time.sleep(10) + print("Fetching OTP from MailSlurp...") + code = fetch_code() + + if not code: + cls.seleniumdriver.quit() + raise AssertionError("Failed to fetch OTP from MailSlurp") + + print(f"Successfully fetched OTP: {code}") + + # Find OTP input and enter the code + otp_field = WebDriverWait(cls.seleniumdriver, 60).until(EC.presence_of_element_located((SeleniumBy.CSS_SELECTOR, 'input[data-testid="passwordless_passcode__TextInput--0__input"]'))) + print("Entering OTP...") + otp_field.send_keys(code) + + # Wait for success page and confirm + success = WebDriverWait(cls.seleniumdriver, 60).until(EC.presence_of_element_located((SeleniumBy.CSS_SELECTOR, 'h1[data-testid="device_success_title"]'))) + print("Connected to Passport!") + + cls.seleniumdriver.quit() + def test_1_device_code_login(self): # Select use device code auth self.altdriver.find_object(By.NAME, "DeviceCodeAuth").tap() @@ -29,9 +93,12 @@ def test_1_device_code_login(self): # Login print("Logging in...") - login() + self.setupChrome() bring_sample_app_to_foreground() self.altdriver.wait_for_object(By.NAME, "LoginBtn").tap() + self.login() + bring_sample_app_to_foreground() + # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") print("Logged in") @@ -116,23 +183,28 @@ def test_7_reconnect_device_code_connect_imx(self): # Logout print("Logging out...") + self.setupChrome() + bring_sample_app_to_foreground() self.altdriver.find_object(By.NAME, "LogoutBtn").tap() time.sleep(5) bring_sample_app_to_foreground() + # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - stop_chrome() + self.seleniumdriver.quit() print("Logged out") - + # Connect IMX print("Logging in and connecting to IMX...") + self.setupChrome() + bring_sample_app_to_foreground() self.altdriver.wait_for_object(By.NAME, "ConnectBtn").tap() - login() + self.login() bring_sample_app_to_foreground() + # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") print("Logged in and connected to IMX") - stop_chrome() # Get access token self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap() @@ -145,10 +217,13 @@ def test_7_reconnect_device_code_connect_imx(self): # Logout print("Logging out...") + self.setupChrome() + bring_sample_app_to_foreground() self.altdriver.find_object(By.NAME, "LogoutBtn").tap() time.sleep(5) bring_sample_app_to_foreground() + # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - stop_chrome() + self.seleniumdriver.quit() print("Logged out") \ No newline at end of file diff --git a/sample/Tests/test/test_mac_helpers.py b/sample/Tests/test/test_mac_helpers.py index 532edca7..cc9f513a 100644 --- a/sample/Tests/test/test_mac_helpers.py +++ b/sample/Tests/test/test_mac_helpers.py @@ -1,77 +1,5 @@ -import os -import sys import subprocess import time -from pathlib import Path - -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.chrome.options import Options -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.keys import Keys -import time - -sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'src')) -from fetch_otp import EMAIL, fetch_code - -# brew install chromedriver - -def login(): - print("Connect to Chrome") - # Set up Chrome options to connect to the existing Chrome instance - chrome_options = Options() - chrome_options.add_argument('--remote-debugging-port=9222') - # Connect to the existing Chrome instance - driver = webdriver.Chrome(options=chrome_options) - - print("Open a window on Chrome") - # Get the original window handle - original_window = driver.current_window_handle - - print("Waiting for new window...") - WebDriverWait(driver, 30).until(EC.number_of_windows_to_be(2)) - - # Get all window handles - all_windows = driver.window_handles - - print("Find the new window") - new_window = [window for window in all_windows if window != driver.current_window_handle][0] - - print("Switch to the new window") - driver.switch_to.window(new_window) - - wait = WebDriverWait(driver, 60) - - print("Wait for email input...") - email_field = wait.until(EC.presence_of_element_located((By.ID, ':r1:'))) - print("Enter email") - email_field.send_keys(EMAIL) - email_field.send_keys(Keys.RETURN) - - # Wait for the OTP to arrive and page to load - print("Wait for OTP...") - time.sleep(10) - - print("Get OTP from Mailslurp...") - code = fetch_code() - if code: - print(f"Successfully fetched OTP: {code}") - else: - print("Failed to fetch OTP from MailSlurp") - driver.quit() - - print("Find OTP input...") - otp_field = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input[data-testid="passwordless_passcode__TextInput--0__input"]'))) - print("Enter OTP") - otp_field.send_keys(code) - - print("Wait for success page...") - success = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'h1[data-testid="device_success_title"]'))) - print("Connected to Passport!") - - driver.quit() def open_sample_app(): print("Opening Unity sample app...") @@ -81,31 +9,29 @@ def open_sample_app(): def stop_sample_app(): print("Stopping sample app...") - try: - # Get the PID of the sample app using ps, grep, and awk - cmd = f"ps aux | grep 'Sample.app' | grep -v grep | awk '{{print $2}}'" - pid = subprocess.check_output(cmd, shell=True, text=True).strip() - if pid: - # Terminate the process using the PID - subprocess.run(["kill", pid]) - print(f"Sample app (PID {pid}) has been terminated.") - else: - print("Sample app is not running.") - except subprocess.CalledProcessError: - print("Failed to find the sample app process.") + bash_script = """ + app_path="SampleApp.app" + echo "Closing sample app..." + PID=$(ps aux | grep "$app_path" | grep -v grep | awk '{print $2}') + if [ -n "$PID" ]; then + kill $PID + echo "Sample app (PID $PID) has been terminated." + else + echo "Sample app is not running." + fi + echo "Waiting for 5 seconds..." + sleep 5 + """ + + subprocess.run(bash_script, shell=True, check=True, text=True) time.sleep(5) print("Stopped sample app.") -def bring_sample_app_to_foreground(app_name): +def bring_sample_app_to_foreground(): print("Bringing Unity sample app to the foreground...") subprocess.run( - ['osascript', '-e', f'tell application "{app_name}" to activate'], + ['osascript', '-e', f'tell application "SampleApp" to activate'], check=True - ) - -def stop_chrome(): - print("Stopping Chrome all Chrome instances...") - subprocess.run(["pkill", "-f", "chrome"], check=True) - print("Stopped Chrome.") \ No newline at end of file + ) \ No newline at end of file diff --git a/sample/Tests/test/test_windows.py b/sample/Tests/test/test_windows.py index 93893017..de3a1819 100644 --- a/sample/Tests/test/test_windows.py +++ b/sample/Tests/test/test_windows.py @@ -31,6 +31,7 @@ def test_1_device_code_login(self): self.altdriver.wait_for_object(By.NAME, "LoginBtn").tap() login() bring_sample_app_to_foreground() + # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") print("Logged in") @@ -112,6 +113,7 @@ def test_7_reconnect_device_code_connect_imx(self): self.altdriver.find_object(By.NAME, "LogoutBtn").tap() time.sleep(5) bring_sample_app_to_foreground() + # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") stop_chrome() @@ -124,6 +126,7 @@ def test_7_reconnect_device_code_connect_imx(self): self.altdriver.wait_for_object(By.NAME, "ConnectBtn").tap() login() bring_sample_app_to_foreground() + # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") print("Logged in and connected to IMX") @@ -145,6 +148,7 @@ def test_7_reconnect_device_code_connect_imx(self): self.altdriver.find_object(By.NAME, "LogoutBtn").tap() time.sleep(5) bring_sample_app_to_foreground() + # Wait for authenticated screen self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") stop_chrome() From 1a4415701516a4a1c557dfd3c2713b6c876e17e8 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 20 Nov 2024 13:23:07 +1300 Subject: [PATCH 5/7] test: remove unused ui test files --- .github/workflows/ui-tests.yml | 2 +- sample/Tests/src/device_code_logout.py | 37 ------- sample/Tests/test/test_mac.py | 3 +- sample/Tests/test_mac.sh | 137 ------------------------- 4 files changed, 3 insertions(+), 176 deletions(-) delete mode 100644 sample/Tests/src/device_code_logout.py delete mode 100755 sample/Tests/test_mac.sh diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index feca9d71..4635078a 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -65,7 +65,7 @@ jobs: include: - targetPlatform: StandaloneOSX runs-on: [self-hosted, macOS] - test_script: ./test_mac.sh + test_script: pytest -xs test/test_mac.py::MacTest - targetPlatform: StandaloneWindows64 runs-on: [self-hosted, windows] test_script: pytest -xs test/test_windows.py::WindowsTest diff --git a/sample/Tests/src/device_code_logout.py b/sample/Tests/src/device_code_logout.py deleted file mode 100644 index e639b2c7..00000000 --- a/sample/Tests/src/device_code_logout.py +++ /dev/null @@ -1,37 +0,0 @@ - -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.chrome.options import Options -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.keys import Keys - -def main(): - print("Connect to Chrome") - # Set up Chrome options to connect to the existing Chrome instance - chrome_options = Options() - chrome_options.add_argument('--remote-debugging-port=9222') - # Connect to the existing Chrome instance - driver = webdriver.Chrome(options=chrome_options) - - print("Open a window on Chrome") - # Get the original window handle - original_window = driver.current_window_handle - - print("Waiting for new window...") - WebDriverWait(driver, 60).until(EC.number_of_windows_to_be(2)) - - # Get all window handles - all_windows = driver.window_handles - - print("Find the new window") - new_window = [window for window in all_windows if window != driver.current_window_handle][0] - - print("Switch to the new window") - driver.switch_to.window(new_window) - - driver.quit() - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/sample/Tests/test/test_mac.py b/sample/Tests/test/test_mac.py index 6415ff1d..13cd5877 100644 --- a/sample/Tests/test/test_mac.py +++ b/sample/Tests/test/test_mac.py @@ -52,7 +52,8 @@ def login(cls): # Switch to the new window all_windows = cls.seleniumdriver.window_handles - new_window = next(window for window in all_windows if window != cls.seleniumdriver.current_window_handle) + new_window = [window for window in all_windows if window != cls.seleniumdriver.current_window_handle][0] + cls.seleniumdriver.switch_to.window(new_window) print("Switched to new window") # Wait for email input and enter email diff --git a/sample/Tests/test_mac.sh b/sample/Tests/test_mac.sh deleted file mode 100755 index aa00c476..00000000 --- a/sample/Tests/test_mac.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/bin/bash - -# Function to open the sample app -open_sample_app() { - local app_path="$1" - echo "Opening Unity sample app..." - open "$app_path" - echo "Unity sample app launched. Waiting for 5 seconds..." - sleep 5 -} - -# Function to close the sample app -close_sample_app() { - local app_path="$1" - echo "Closing sample app..." - local PID=$(ps aux | grep "$app_path" | grep -v grep | awk '{print $2}') - if [ -n "$PID" ]; then - kill $PID - echo "Sample app (PID $PID) has been terminated." - else - echo "Sample app is not running." - fi - echo "Waiting for 5 seconds..." - sleep 5 -} - -# Function to run Python scripts in the background -run_python_script() { - local script_path="$1" - echo "Running $script_path script..." - python3 "$script_path" & - echo "$script_path script running in the background..." -} - -# Function to bring the sample app to the foreground -activate_sample_app() { - local app_name="$1" - echo "Bringing Unity sample app to the foreground..." - osascript -e "tell application \"$app_name\" to activate" -} - -close_chrome() { - echo "Closing all Chrome instances..." - pkill -f chrome - if [ $? -eq 0 ]; then - echo "Chrome closed successfully." - else - echo "No Chrome instances were running." - fi -} - -# Main script execution -app_path="${UNITY_APP_PATH:-SampleApp.app}" -app_name="${UNITY_APP_NAME:-SampleApp}" - -# Capture the start time -start_time=$(date +%s) - -# Set permissions for the app bundle -chmod -R 755 "$app_path" - -echo "Starting Unity sample app..." -open_sample_app "$app_path" - -# Login -run_python_script "src/device_code_login.py" -sleep 5 -activate_sample_app "$app_name" -echo "Running Mac device code login test..." -pytest test/test_mac.py::MacTest::test_1_device_code_login -wait -close_chrome - -# SDK functions -echo "Running SDK functions tests..." -activate_sample_app "$app_name" -pytest test/test.py -wait - -# Relogin -close_sample_app "$app_path" -open_sample_app "$app_path" -echo "Running Mac relogin test..." -pytest test/test_mac.py::MacTest::test_3_device_code_relogin -wait - -# Reconnect -close_sample_app "$app_path" -open_sample_app "$app_path" -echo "Running Mac reconnect test..." -pytest test/test_mac.py::MacTest::test_4_device_code_reconnect -wait - -# Logout -run_python_script "src/device_code_logout.py" -sleep 5 -activate_sample_app "$app_name" -echo "Running Mac device code logout test..." -pytest test/test_device_code_logout.py -wait -close_chrome - -# Connect IMX -close_sample_app "$app_path" -open_sample_app "$app_path" -run_python_script "src/device_code_login.py" -sleep 5 -activate_sample_app "$app_name" -echo "Running Mac device code connect IMX test..." -pytest test/test_mac.py::MacTest::test_2_device_code_connect_imx -wait -close_chrome - -activate_sample_app "$app_name" - -# Logout -run_python_script "src/device_code_logout.py" -sleep 5 -activate_sample_app "$app_name" -echo "Running Mac device code logout test..." -pytest test/test_device_code_logout.py -wait -close_chrome - -# Final stop of Unity sample app -close_sample_app "$app_path" - -# Capture the end time -end_time=$(date +%s) - -# Calculate the duration -execution_time=$((end_time - start_time)) -minutes=$((execution_time / 60)) -seconds=$((execution_time % 60)) - -echo "All tests completed." -echo "Elapsed time: $minutes minutes and $seconds seconds." \ No newline at end of file From 5552b749effe47b0b52381c3ba53c0341b90d5f2 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 20 Nov 2024 14:50:56 +1300 Subject: [PATCH 6/7] ci: skip cache for mac build --- .github/workflows/ui-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 4635078a..075289b0 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -32,6 +32,7 @@ jobs: with: lfs: true - uses: actions/cache@v3 + if: ${{ matrix.targetPlatform != 'StandaloneOSX' }} with: path: Library key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('sample/Assets/**', 'sample/Packages/**', 'sample/ProjectSettings/**') }} From a351e7eb531719cb47e42f65c8dbec259991b530 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 20 Nov 2024 15:39:46 +1300 Subject: [PATCH 7/7] ci: fix mac executable --- .github/workflows/ui-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 075289b0..7a4adae3 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -32,7 +32,6 @@ jobs: with: lfs: true - uses: actions/cache@v3 - if: ${{ matrix.targetPlatform != 'StandaloneOSX' }} with: path: Library key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('sample/Assets/**', 'sample/Packages/**', 'sample/ProjectSettings/**') }} @@ -84,6 +83,9 @@ jobs: with: name: Build-${{ matrix.targetPlatform }} path: sample/Tests + - name: Make macOS artifact executable + if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} + run: chmod +x sample/Tests/SampleApp.app/Contents/MacOS/* - uses: actions/setup-python@v4 with: python-version: "3.10"