diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml
index 78f1c669..02e55325 100644
--- a/.github/workflows/ui-tests.yml
+++ b/.github/workflows/ui-tests.yml
@@ -30,8 +30,6 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
- ALTSERVER_HOST: 54.66.58.33
- ALTSERVER_PORT: 13000
with:
targetPlatform: StandaloneOSX
projectPath: sample
@@ -39,13 +37,12 @@ jobs:
customParameters: -logFile logFile.log -quit -batchmode
- name: Upload artifact
uses: actions/upload-artifact@v4
- if: always()
with:
name: Build-StandaloneOSX
path: sample/Builds/MacOS
test:
name: Run UI tests on AltTester 🧪
- runs-on: macos-latest
+ runs-on: self-hosted
needs: build
steps:
- uses: actions/checkout@v3
@@ -54,21 +51,19 @@ jobs:
- uses: actions/download-artifact@v4
with:
name: Build-StandaloneOSX
- - name: Open application
- run: |
- export RUN_IN_BROWSERSTACK="false"
- export ALTSERVER_HOST="54.66.58.33"
- export ALTSERVER_PORT=13000
- chmod -R 755 SampleApp.app
- open SampleApp.app
- 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:
+ UNITY_APP_PATH: ${{ github.workspace }}/SampleApp.app
+ UNITY_APP_NAME: SampleApp
+ MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }}
+ working-directory: sample/Tests
run: |
- export ALTSERVER_HOST="54.66.58.33"
- export ALTSERVER_PORT=13000
- pytest -s -v sample/Tests/test.py
+ chmod -R 755 ${{ github.workspace }}/SampleApp.app
+ chmod +x test_mac.sh
+ ./test_mac.sh
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 587aac0d..efc4daa6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,4 +103,8 @@ sample/AltTester.log
sample/mono_crash*
# Vuplex
-sample/Assets/Vuplex*
\ No newline at end of file
+sample/Assets/Vuplex*
+
+__pycache__/
+*.pyc
+.pytest_cache/
\ No newline at end of file
diff --git a/sample/Tests/requirements.txt b/sample/Tests/requirements.txt
index f6ba7653..2cbdd7de 100644
--- a/sample/Tests/requirements.txt
+++ b/sample/Tests/requirements.txt
@@ -4,4 +4,5 @@ google_auth_oauthlib==1.2.0
protobuf==5.27.2
selenium==4.22.0
pytest==8.2.2
-requests==2.32.3
\ No newline at end of file
+requests==2.32.3
+mailslurp-client==15.19.22
\ No newline at end of file
diff --git a/sample/Tests/src/device_code_login.py b/sample/Tests/src/device_code_login.py
new file mode 100644
index 00000000..da7aa2ef
--- /dev/null
+++ b/sample/Tests/src/device_code_login.py
@@ -0,0 +1,69 @@
+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
+
+# brew install chromedriver
+
+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, 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 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_login_windows.py b/sample/Tests/src/device_code_login_windows.py
new file mode 100644
index 00000000..80a30b2a
--- /dev/null
+++ b/sample/Tests/src/device_code_login_windows.py
@@ -0,0 +1,68 @@
+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 gmail_fetch_otp import fetch_gmail_code
+
+EMAIL = 'user-33f17f82-274b-4269-9ce6-c620e89fcd8d@mailslurp.biz'
+
+# 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_gmail_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.py b/sample/Tests/src/device_code_logout.py
new file mode 100644
index 00000000..e639b2c7
--- /dev/null
+++ b/sample/Tests/src/device_code_logout.py
@@ -0,0 +1,37 @@
+
+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/src/device_code_logout_windows.py b/sample/Tests/src/device_code_logout_windows.py
new file mode 100644
index 00000000..385e56c3
--- /dev/null
+++ b/sample/Tests/src/device_code_logout_windows.py
@@ -0,0 +1,33 @@
+
+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/src/fetch_otp.py b/sample/Tests/src/fetch_otp.py
new file mode 100644
index 00000000..769163bd
--- /dev/null
+++ b/sample/Tests/src/fetch_otp.py
@@ -0,0 +1,30 @@
+import os
+import mailslurp_client
+from mailslurp_client.api import InboxControllerApi, WaitForControllerApi
+import re
+
+INBOX_ID = "33f17f82-274b-4269-9ce6-c620e89fcd8d"
+EMAIL = "user-33f17f82-274b-4269-9ce6-c620e89fcd8d@mailslurp.biz"
+
+def get_mailslurp_client():
+ configuration = mailslurp_client.Configuration()
+ configuration.api_key['x-api-key'] = os.getenv('MAILSLURP_API_KEY')
+ api_client = mailslurp_client.ApiClient(configuration)
+ waitfor_controller = WaitForControllerApi(api_client)
+ return waitfor_controller
+
+def extract_otp_from_email(email_body):
+ # Pattern to match 6-digit code in Passport emails
+ pattern = r'
]*>(\d{6})
'
+ match = re.search(pattern, email_body)
+ if match:
+ return match.group(1)
+ return None
+
+def fetch_code():
+ waitfor_controller = get_mailslurp_client()
+ email = waitfor_controller.wait_for_latest_email(inbox_id=INBOX_ID, timeout=30000, unread_only=True)
+ if email:
+ otp = extract_otp_from_email(email.body)
+ return otp
+ return None
diff --git a/sample/Tests/test.py b/sample/Tests/test.py
deleted file mode 100644
index 7df71c3b..00000000
--- a/sample/Tests/test.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import unittest
-import os
-from alttester import *
-
-class UnityTest(unittest.TestCase):
-
- altdriver = None
-
- @classmethod
- def setUpClass(cls):
- host = os.getenv('ALTSERVER_HOST', '127.0.0.1')
- port = int(os.getenv('ALTSERVER_PORT', '13000'))
- cls.altdriver = AltDriver(host=host, port=port)
-
- @classmethod
- def tearDownClass(cls):
- cls.altdriver.stop()
-
- def test(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")
-
- assert True
\ No newline at end of file
diff --git a/sample/Tests/test/test.py b/sample/Tests/test/test.py
new file mode 100644
index 00000000..2d4f98ed
--- /dev/null
+++ b/sample/Tests/test/test.py
@@ -0,0 +1,219 @@
+import time
+import unittest
+import requests
+import re
+
+from alttester import *
+
+class TestConfig:
+ EMAIL = "user-33f17f82-274b-4269-9ce6-c620e89fcd8d@mailslurp.biz"
+ PASSPORT_ID="email|671ed01e2ab74483c4fb1f42"
+ WALLET_ADDRESS = "0x7dd423aeaccfbdd3a043bb8583085c7d97032de9"
+
+class UnityTest(unittest.TestCase):
+
+ altdriver = None
+
+ @classmethod
+ def setUpClass(cls):
+ cls.altdriver = AltDriver()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.altdriver.stop()
+
+ def test_1_passport_functions(self):
+ output = self.altdriver.find_object(By.NAME, "Output")
+
+ # Get access token
+ self.altdriver.find_object(By.NAME, "GetAccessTokenBtn").tap()
+ self.assertTrue(len(output.get_text()) > 50)
+
+ # Get ID token
+ self.altdriver.find_object(By.NAME, "GetIdTokenBtn").tap()
+ self.assertTrue(len(output.get_text()) > 50)
+
+ # Get email
+ self.altdriver.find_object(By.NAME, "GetEmail").tap()
+ self.assertEqual(TestConfig.EMAIL, output.get_text())
+
+ # Get Passport ID
+ self.altdriver.find_object(By.NAME, "GetPassportId").tap()
+ self.assertEqual(TestConfig.PASSPORT_ID, output.get_text())
+
+ # Get linked addresses
+ self.altdriver.find_object(By.NAME, "GetLinkedAddresses").tap()
+ time.sleep(1)
+ self.assertEqual("No linked addresses", output.get_text())
+
+ def test_2_imx_functions(self):
+ output = self.altdriver.find_object(By.NAME, "Output")
+
+ # Connect to IMX
+ self.altdriver.find_object(By.NAME, "ConnectBtn").tap()
+ self.assertEqual("Connected to IMX", output.get_text())
+
+ # Is registered off-chain
+ self.altdriver.wait_for_object(By.NAME, "IsRegisteredOffchainBtn").tap()
+ time.sleep(1)
+ self.assertEqual("Registered", output.get_text())
+
+ # Register off-chain
+ self.altdriver.find_object(By.NAME, "RegisterOffchainBtn").tap()
+ self.assertEqual("Registering off-chain...", output.get_text())
+ time.sleep(20)
+ self.assertTrue("Passport account already registered" in output.get_text())
+
+ # Get address
+ self.altdriver.find_object(By.NAME, "GetAddressBtn").tap()
+ self.assertEqual(TestConfig.WALLET_ADDRESS, output.get_text())
+
+ # Show NFT transfer scene
+ self.altdriver.find_object(By.NAME, "NftTransferBtn").tap()
+ self.altdriver.wait_for_current_scene_to_be("ImxNftTransfer")
+
+ # Get all NFTs the user owns
+ collection = "0x3765d19d5bc39b60718e43b4b12b30e87d383181"
+ api_url = f"https://api.sandbox.immutable.com/v1/assets?collection={collection}&user={TestConfig.WALLET_ADDRESS}&page_size=3"
+ token_ids = []
+ try:
+ # Make the API request
+ response = requests.get(api_url)
+
+ # Raise an exception if the request was unsuccessful
+ response.raise_for_status()
+
+ # Parse the JSON response
+ data = response.json()
+
+ # Extract the token_ids
+ token_ids = [item['token_id'] for item in data['result']]
+
+ # Check that there's enough NFTs to test transfer
+ if len(token_ids) < 3:
+ raise SystemExit(f"Not enough NFTs to test transfer")
+
+ except requests.exceptions.HTTPError as err:
+ raise SystemExit(f"HTTP error occurred: {err}")
+ except Exception as err:
+ raise SystemExit(f"An error occurred: {err}")
+
+ # Single transfer
+ tokenId = self.altdriver.wait_for_object(By.NAME, "TokenId1")
+ tokenId.set_text(token_ids[0])
+ tokenAddress = self.altdriver.wait_for_object(By.NAME, "TokenAddress1")
+ tokenAddress.set_text(collection)
+ receiver = self.altdriver.wait_for_object(By.NAME, "Receiver1")
+ receiver.set_text("0x0000000000000000000000000000000000000000")
+ self.altdriver.find_object(By.NAME, "TransferButton").tap()
+ time.sleep(10)
+ output = self.altdriver.find_object(By.NAME, "Output")
+ self.assertTrue(output.get_text().startswith("NFT transferred successfully"))
+
+ # Batch transfer
+ tokenId = self.altdriver.wait_for_object(By.NAME, "TokenId1")
+ tokenId.set_text(token_ids[1])
+ tokenAddress = self.altdriver.wait_for_object(By.NAME, "TokenAddress1")
+ tokenAddress.set_text(collection)
+ receiver = self.altdriver.wait_for_object(By.NAME, "Receiver1")
+ receiver.set_text("0x0000000000000000000000000000000000000000")
+ tokenId = self.altdriver.wait_for_object(By.NAME, "TokenId2")
+ tokenId.set_text(token_ids[2])
+ tokenAddress = self.altdriver.wait_for_object(By.NAME, "TokenAddress2")
+ tokenAddress.set_text(collection)
+ receiver = self.altdriver.wait_for_object(By.NAME, "Receiver2")
+ receiver.set_text("0x0000000000000000000000000000000000000000")
+ self.altdriver.find_object(By.NAME, "TransferButton").tap()
+ time.sleep(10)
+ output = self.altdriver.find_object(By.NAME, "Output")
+ self.assertEqual("Successfully transferred 2 NFTs.", output.get_text())
+
+ # Go back to authenticated scene
+ self.altdriver.find_object(By.NAME, "CancelButton").tap()
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ def test_3_zkevm_functions(self):
+ output = self.altdriver.find_object(By.NAME, "Output")
+
+ # Connect to zkEVM
+ self.altdriver.find_object(By.NAME, "ConnectEvmBtn").tap()
+ self.assertEqual("Connected to EVM", output.get_text())
+
+ # Initiliase wallet and get address
+ self.altdriver.wait_for_object(By.NAME, "RequestAccountsBtn").tap()
+ time.sleep(5)
+ self.assertEqual(TestConfig.WALLET_ADDRESS, output.get_text())
+
+ # Show get balance scene
+ self.altdriver.find_object(By.NAME, "GetBalanceBtn").tap()
+ self.altdriver.wait_for_current_scene_to_be("ZkEvmGetBalance")
+
+ # Get balance of account
+ address = self.altdriver.wait_for_object(By.NAME, "AddressInput")
+ address.set_text(TestConfig.WALLET_ADDRESS)
+ self.altdriver.find_object(By.NAME, "GetBalanceButton").tap()
+ time.sleep(2)
+ output = self.altdriver.find_object(By.NAME, "Output")
+ self.assertEqual("Balance:\nHex: 0x0\nDec: 0", output.get_text())
+
+ # Go back to authenticated scene
+ self.altdriver.find_object(By.NAME, "CancelButton").tap()
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ # Show send transaction scene
+ self.altdriver.find_object(By.NAME, "SendTransactionBtn").tap()
+ self.altdriver.wait_for_current_scene_to_be("ZkEvmSendTransaction")
+ output = self.altdriver.find_object(By.NAME, "Output")
+
+ # Send transaction with confirmation
+ to = self.altdriver.wait_for_object(By.NAME, "ToInput")
+ to.set_text("0x912cd5f1cd67F1143b7a5796fd9e5063D755DAbe")
+ amount = self.altdriver.wait_for_object(By.NAME, "ValueInput")
+ amount.set_text("0")
+ data = self.altdriver.wait_for_object(By.NAME, "DataInput")
+ data.set_text("0x1e957f1e")
+ self.altdriver.find_object(By.NAME, "SendButton").tap()
+ time.sleep(15)
+ self.assertTrue(output.get_text().startswith("Transaction hash"))
+ self.assertTrue(output.get_text().endswith("Status: Success"))
+
+ # Send transaction without confirmation and get transaction receipt
+ self.altdriver.wait_for_object(By.NAME, "WithConfirmationToggle").tap()
+ self.altdriver.find_object(By.NAME, "SendButton").tap()
+ time.sleep(20)
+ self.assertTrue(output.get_text().startswith("Transaction hash"))
+ self.assertTrue(output.get_text().endswith("Status: Success"))
+
+ # Send transaction without confirmation and don't get transaction receipt
+ self.altdriver.wait_for_object(By.NAME, "GetTransactionReceiptToggle").tap()
+ self.altdriver.find_object(By.NAME, "SendButton").tap()
+ time.sleep(15)
+ self.assertTrue(output.get_text().startswith("Transaction hash"))
+
+ # Grab the transaction hash
+ match = re.search(r"0x[0-9a-fA-F]+", output.get_text())
+ transactionHash = ""
+ if match:
+ transactionHash = match.group()
+ else:
+ raise SystemExit(f"Could not find transaction hash")
+
+ # Go back to authenticated scene
+ self.altdriver.find_object(By.NAME, "CancelButton").tap()
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ # Show get transaction receipt scene
+ self.altdriver.find_object(By.NAME, "GetTransactionReceiptBtn").tap()
+ self.altdriver.wait_for_current_scene_to_be("ZkEvmGetTransactionReceipt")
+
+ # Get transaction receipt
+ hash = self.altdriver.wait_for_object(By.NAME, "HashInput")
+ hash.set_text(transactionHash)
+ self.altdriver.find_object(By.NAME, "GetReceiptButton").tap()
+ time.sleep(2)
+ output = self.altdriver.find_object(By.NAME, "Output")
+ self.assertEqual("Status: Success", output.get_text())
+
+ # Go back to authenticated scene
+ self.altdriver.find_object(By.NAME, "CancelButton").tap()
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
\ No newline at end of file
diff --git a/sample/Tests/test/test_mac.py b/sample/Tests/test/test_mac.py
new file mode 100644
index 00000000..507df3c1
--- /dev/null
+++ b/sample/Tests/test/test_mac.py
@@ -0,0 +1,101 @@
+import time
+import unittest
+import requests
+import re
+
+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_1_device_code_login(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")
+
+ # Login
+ loginBtn = self.altdriver.wait_for_object(By.NAME, "LoginBtn")
+ loginBtn.tap()
+
+ # Wait for authenticated screen
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ def test_2_device_code_connect_imx(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")
+
+ # Connect IMX
+ connectBtn = self.altdriver.wait_for_object(By.NAME, "ConnectBtn")
+ connectBtn.tap()
+
+ # Wait for authenticated screen
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ # 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("0x9cb14f273de4a8c3d8e9b4c5decbb53519dfa7bd", output.get_text())
+
+ 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()
+
+ # Wait for authenticated screen
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ # 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())
+
+ def test_4_device_code_reconnect(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, "ReconnectBtn")
+ reloginBtn.tap()
+
+ # Wait for authenticated screen
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ # 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("0x9cb14f273de4a8c3d8e9b4c5decbb53519dfa7bd", output.get_text())
\ No newline at end of file
diff --git a/sample/Tests/test/test_mac_device_code_logout.py b/sample/Tests/test/test_mac_device_code_logout.py
new file mode 100644
index 00000000..beb6cb92
--- /dev/null
+++ b/sample/Tests/test/test_mac_device_code_logout.py
@@ -0,0 +1,25 @@
+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_windows.py b/sample/Tests/test/test_windows.py
new file mode 100644
index 00000000..4c66046e
--- /dev/null
+++ b/sample/Tests/test/test_windows.py
@@ -0,0 +1,76 @@
+import time
+import unittest
+import requests
+
+from alttester import *
+
+class WindowsTest(unittest.TestCase):
+
+ altdriver = None
+
+ @classmethod
+ def setUpClass(cls):
+ cls.altdriver = AltDriver()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.altdriver.stop()
+
+ def test_1_device_code_login(self):
+ # Login
+ loginBtn = self.altdriver.wait_for_object(By.NAME, "LoginBtn")
+ loginBtn.tap()
+
+ # Wait for authenticated screen
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ def test_2_device_code_connect_imx(self):
+ # Connect IMX
+ connectBtn = self.altdriver.wait_for_object(By.NAME, "ConnectBtn")
+ connectBtn.tap()
+
+ # Wait for authenticated screen
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ # 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("0x9cb14f273de4a8c3d8e9b4c5decbb53519dfa7bd", output.get_text())
+
+ def test_3_device_code_relogin(self):
+ # Relogin
+ reloginBtn = self.altdriver.wait_for_object(By.NAME, "ReloginBtn")
+ reloginBtn.tap()
+
+ # Wait for authenticated screen
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ # 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())
+
+ def test_4_device_code_reconnect(self):
+ # Relogin
+ reloginBtn = self.altdriver.wait_for_object(By.NAME, "ReconnectBtn")
+ reloginBtn.tap()
+
+ # Wait for authenticated screen
+ self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene")
+
+ # 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("0x9cb14f273de4a8c3d8e9b4c5decbb53519dfa7bd", output.get_text())
diff --git a/sample/Tests/test_mac.sh b/sample/Tests/test_mac.sh
new file mode 100755
index 00000000..eb434081
--- /dev/null
+++ b/sample/Tests/test_mac.sh
@@ -0,0 +1,134 @@
+#!/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)
+
+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_mac_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_mac_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