From 07bab81424095b456cb43329d3a5fb0c7b4e9a3d Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Fri, 28 Feb 2025 12:16:53 +1300 Subject: [PATCH 1/7] ci: allow arbitrary loads when building ios for alttester, fix bundle identifier issue --- .github/workflows/ui-tests.yml | 281 ++++++++++-------- sample/Assets/Editor/iOSPostBuildProcessor.cs | 83 ++++++ .../Editor/iOSPostBuildProcessor.cs.meta | 3 + sample/Tests/test/test_ios.py | 37 ++- sample/build_ios.sh | 5 +- 5 files changed, 280 insertions(+), 129 deletions(-) create mode 100644 sample/Assets/Editor/iOSPostBuildProcessor.cs create mode 100644 sample/Assets/Editor/iOSPostBuildProcessor.cs.meta diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 7885d6c8..84027683 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -12,145 +12,174 @@ concurrency: cancel-in-progress: true jobs: - build: - name: Build ${{ matrix.targetPlatform }} for AltTester 🛠️ - runs-on: ubuntu-latest-8-cores - strategy: - fail-fast: false - matrix: - include: - - targetPlatform: StandaloneOSX - buildMethod: MacBuilder.BuildForAltTester - buildPath: sample/Builds/MacOS - - targetPlatform: StandaloneWindows64 - buildMethod: WindowsBuilder.BuildForAltTester - buildPath: sample/Builds/Windows64 - - targetPlatform: Android - buildMethod: MobileBuilder.BuildForAltTester - buildPath: sample/Builds/Android + # build: + # name: Build ${{ matrix.targetPlatform }} for AltTester 🛠️ + # runs-on: ubuntu-latest-8-cores + # strategy: + # fail-fast: false + # matrix: + # include: + # - targetPlatform: StandaloneOSX + # buildMethod: MacBuilder.BuildForAltTester + # buildPath: sample/Builds/MacOS + # - targetPlatform: StandaloneWindows64 + # buildMethod: WindowsBuilder.BuildForAltTester + # buildPath: sample/Builds/Windows64 + # - targetPlatform: Android + # buildMethod: MobileBuilder.BuildForAltTester + # buildPath: sample/Builds/Android + # steps: + # - uses: actions/checkout@v3 + # with: + # lfs: true + # - uses: actions/cache@v3 + # with: + # path: Library + # key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('sample/Assets/**', 'sample/Packages/**', 'sample/ProjectSettings/**') }} + # restore-keys: | + # Library-${{ matrix.targetPlatform }} + # Library- + # - name: Build project + # uses: game-ci/unity-builder@v4 + # env: + # UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + # UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + # UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} + # with: + # targetPlatform: ${{ matrix.targetPlatform }} + # projectPath: sample + # buildMethod: ${{ matrix.buildMethod }} + # customParameters: -logFile logFile.log -quit -batchmode + # artifactsPath: ${{ matrix.buildPath }} + # - name: List build directory + # run: ls -R ./ + # - name: Ensure build path exists + # run: mkdir -p ${{ matrix.buildPath }} + # - name: Upload artifact + # uses: actions/upload-artifact@v4 + # if: always() + # with: + # name: Build-${{ matrix.targetPlatform }} + # path: ${{ matrix.buildPath }} + # test: + # name: Run ${{ matrix.targetPlatform }} UI tests 🧪 + # needs: build + # strategy: + # matrix: + # include: + # - targetPlatform: StandaloneOSX + # runs-on: [self-hosted, macOS] + # 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 + # - 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 }} + # steps: + # - uses: actions/checkout@v3 + # with: + # lfs: true + # - name: Create temporary keychain + # if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} + # run: | + # security list-keychains + # security delete-keychain temporary || true + # security list-keychains + # security create-keychain -p "" temporary + # security default-keychain -s temporary + # security unlock-keychain -p "" temporary + # security set-keychain-settings -lut 600 temporary + # - uses: actions/download-artifact@v4 + # 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" + # - name: Install dependencies (Windows) + # if: ${{ matrix.targetPlatform == 'StandaloneWindows64' }} + # run: pip install -r "sample/Tests/requirements-desktop.txt" + # - name: Install dependencies (Mac) + # if: ${{ matrix.targetPlatform != 'StandaloneWindows64' }} + # run: | + # if [[ "${{ matrix.targetPlatform }}" == "StandaloneOSX" ]]; then + # pip uninstall -y browserstack-sdk || true + # pip install -r "sample/Tests/requirements-desktop.txt" + # else + # pip install -r "sample/Tests/requirements-mobile.txt" + # fi + # - name: Run UI tests + # env: + # UNITY_APP_PATH: SampleApp.app + # UNITY_APP_NAME: SampleApp + # MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} + # BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + # BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + # working-directory: sample/Tests + # run: ${{ matrix.test_script }} + # - name: Remove temporary keychain + # if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} + # run: | + # security list-keychains + # security default-keychain -s ~/Library/Keychains/login.keychain-db + # security delete-keychain temporary + # security list-keychains + test-ios: + name: Run iOS UI tests 🧪 + runs-on: [ self-hosted, macOS ] steps: - uses: actions/checkout@v3 with: lfs: true - - uses: actions/cache@v3 - with: - path: Library - key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('sample/Assets/**', 'sample/Packages/**', 'sample/ProjectSettings/**') }} - restore-keys: | - Library-${{ matrix.targetPlatform }} - Library- - - name: Build project - uses: game-ci/unity-builder@v4 + - name: Build iOS app + working-directory: sample + run: ./build_ios.sh + - name: Install jq (if not installed) + run: | + if ! command -v jq &> /dev/null; then + echo "jq not found, installing..." + brew install jq + else + echo "jq is already installed" + fi + - name: Upload iOS app to BrowserStack + working-directory: sample/Tests env: - UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} - UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} - UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} - with: - targetPlatform: ${{ matrix.targetPlatform }} - projectPath: sample - buildMethod: ${{ matrix.buildMethod }} - customParameters: -logFile logFile.log -quit -batchmode - artifactsPath: ${{ matrix.buildPath }} - - name: List build directory - run: ls -R ./ - - name: Ensure build path exists - run: mkdir -p ${{ matrix.buildPath }} - - name: Upload artifact - uses: actions/upload-artifact@v4 - if: always() - with: - name: Build-${{ matrix.targetPlatform }} - path: ${{ matrix.buildPath }} - test: - name: Run ${{ matrix.targetPlatform }} UI tests 🧪 - needs: build - strategy: - matrix: - include: - - targetPlatform: StandaloneOSX - runs-on: [self-hosted, macOS] - 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 - - 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 }} - steps: - - uses: actions/checkout@v3 - with: - lfs: true - - name: Create temporary keychain - if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} run: | - security list-keychains - security delete-keychain temporary || true - security list-keychains - security create-keychain -p "" temporary - security default-keychain -s temporary - security unlock-keychain -p "" temporary - security set-keychain-settings -lut 600 temporary - - uses: actions/download-artifact@v4 - 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/* + curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ + -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ + -F "file=@Payload.ipa" + + # Extract app_url from the response using jq + app_url=$(echo "$response" | jq -r '.app_url') + + if [ -z "$app_url" ]; then + echo "Error: app_url is not found in the response" + exit 1 # Fail the workflow if app_url is not present + fi + + echo "app_url: $app_url" + + echo "APP_URL=$app_url" >> $GITHUB_ENV - uses: actions/setup-python@v4 with: python-version: "3.10" - - name: Install dependencies (Windows) - if: ${{ matrix.targetPlatform == 'StandaloneWindows64' }} - run: pip install -r "sample/Tests/requirements-desktop.txt" - - name: Install dependencies (Mac) - if: ${{ matrix.targetPlatform != 'StandaloneWindows64' }} - run: | - if [[ "${{ matrix.targetPlatform }}" == "StandaloneOSX" ]]; then - pip uninstall -y browserstack-sdk || true - pip install -r "sample/Tests/requirements-desktop.txt" - else - pip install -r "sample/Tests/requirements-mobile.txt" - fi + - name: Install dependencies + run: pip install -r "sample/Tests/requirements-mobile.txt" - name: Run UI tests env: - UNITY_APP_PATH: SampleApp.app - UNITY_APP_NAME: SampleApp MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} working-directory: sample/Tests - run: ${{ matrix.test_script }} - - name: Remove temporary keychain - if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} - run: | - security list-keychains - security default-keychain -s ~/Library/Keychains/login.keychain-db - security delete-keychain temporary - security list-keychains -# 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-mobile.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" + run: browserstack-sdk pytest -s ./test/test_ios.py --browserstack.config "browserstack.ios.yml" diff --git a/sample/Assets/Editor/iOSPostBuildProcessor.cs b/sample/Assets/Editor/iOSPostBuildProcessor.cs new file mode 100644 index 00000000..82d5e95c --- /dev/null +++ b/sample/Assets/Editor/iOSPostBuildProcessor.cs @@ -0,0 +1,83 @@ +using UnityEngine; +using UnityEditor; +using UnityEditor.Callbacks; +using System.IO; +using System.Linq; +using UnityEditor.iOS.Xcode; + +public class iOSPostBuildProcessor +{ + [PostProcessBuild] + public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) + { + if (target == BuildTarget.iOS && IsCommandLineBuild()) + { + Debug.Log("Command-line iOS build detected. Modifying Info.plist and Xcode project..."); + ModifyInfoPlist(pathToBuiltProject); + ModifyXcodeProject(pathToBuiltProject, GetBundleIdentifierFromArgs()); + } + else + { + Debug.Log("Skipping Info.plist modification (not an iOS command-line build)."); + } + } + + private static bool IsCommandLineBuild() + { + string[] args = System.Environment.GetCommandLineArgs(); + return args.Contains("--ciBuild"); // Check for the --ciBuild flag + } + + private static void ModifyInfoPlist(string pathToBuiltProject) + { + string plistPath = Path.Combine(pathToBuiltProject, "Info.plist"); + + if (!File.Exists(plistPath)) + { + Debug.LogError("Info.plist not found!"); + return; + } + + // Load the Info.plist + PlistDocument plist = new PlistDocument(); + plist.ReadFromFile(plistPath); + + // Get the root dictionary + PlistElementDict rootDict = plist.root; + + // Add App Transport Security Settings + PlistElementDict atsDict = rootDict.CreateDict("NSAppTransportSecurity"); + atsDict.SetBoolean("NSAllowsArbitraryLoads", true); + + // Save the modified Info.plist + plist.WriteToFile(plistPath); + + Debug.Log("Successfully updated Info.plist with NSAllowsArbitraryLoads set to YES."); + } + + private static void ModifyXcodeProject(string pathToBuiltProject, string bundleIdentifier) + { + string pbxprojPath = PBXProject.GetPBXProjectPath(pathToBuiltProject); + PBXProject pbxProject = new PBXProject(); + pbxProject.ReadFromFile(pbxprojPath); + + string targetGuid = pbxProject.GetUnityMainTargetGuid(); // Unity 2019+ + pbxProject.SetBuildProperty(targetGuid, "PRODUCT_BUNDLE_IDENTIFIER", bundleIdentifier); + + pbxProject.WriteToFile(pbxprojPath); + Debug.Log($"Updated Xcode project with bundle identifier: {bundleIdentifier}"); + } + + private static string GetBundleIdentifierFromArgs() + { + string[] args = System.Environment.GetCommandLineArgs(); + for (int i = 0; i < args.Length; i++) + { + if (args[i] == "--bundleIdentifier" && i + 1 < args.Length) + { + return args[i + 1]; + } + } + return "com.immutable.Immutable-Sample"; // Default fallback + } +} \ No newline at end of file diff --git a/sample/Assets/Editor/iOSPostBuildProcessor.cs.meta b/sample/Assets/Editor/iOSPostBuildProcessor.cs.meta new file mode 100644 index 00000000..39c192bd --- /dev/null +++ b/sample/Assets/Editor/iOSPostBuildProcessor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 500da24b224a4d3b846ba1ccc9094b2b +timeCreated: 1740453437 \ No newline at end of file diff --git a/sample/Tests/test/test_ios.py b/sample/Tests/test/test_ios.py index a35f1e9b..d3a5fbe0 100644 --- a/sample/Tests/test/test_ios.py +++ b/sample/Tests/test/test_ios.py @@ -1,7 +1,8 @@ +import os import sys import time -import unittest from pathlib import Path +from datetime import datetime from appium import webdriver from appium.options.ios import XCUITestOptions @@ -14,13 +15,20 @@ from test import TestConfig, UnityTest +from browserstack.local import Local + sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'src')) from fetch_otp import fetch_code +BROWSERSTACK_USERNAME = os.environ.get("BROWSERSTACK_USERNAME") +BROWSERSTACK_ACCESS_KEY = os.environ.get("BROWSERSTACK_ACCESS_KEY") +APP_URL = os.environ.get("APP_URL") + # To run this test on an actual Android device: appium --base-path /wd/hub --allow-insecure chromedriver_autodownload class TestBase(UnityTest): altdriver = None appium_driver = None + browser_stack_local = None @classmethod def setUpClass(cls): @@ -29,16 +37,41 @@ def setUpClass(cls): options.show_xcode_log = True options.auto_accept_alerts = True + browserstack_options = { + "projectName": "Unity Sample App", + "buildName": "Unity Sample App iOS", + "sessionName": f"tests - {datetime.now().strftime('%B %d - %H:%M')}", + "local": "true", + "userName": BROWSERSTACK_USERNAME, + "accessKey": BROWSERSTACK_ACCESS_KEY, + "idleTimeout": 300 + } + + # Set capabilities in the options object + options.set_capability("bstack:options", browserstack_options) + options.set_capability("platformName", "iOS") + options.set_capability("platformVersion", "18") + options.set_capability("deviceName", "iPhone 14") + options.set_capability("app", APP_URL) + + # Start BrowserStack Local + browser_stack_local = Local() + bs_local_args = {"key": BROWSERSTACK_ACCESS_KEY} + browser_stack_local.start(**bs_local_args) + print("BrowserStack Local is running:", browser_stack_local.isRunning()) + cls.appium_driver = webdriver.Remote('https://hub-cloud.browserstack.com/wd/hub/', options=options) time.sleep(10) - cls.altdriver = AltDriver() + + cls.altdriver = AltDriver(timeout=120) @classmethod def tearDownClass(cls): print("\nEnding") cls.appium_driver.quit() cls.altdriver.stop() + cls.browser_stack_local.stop() @classmethod def login(cls): diff --git a/sample/build_ios.sh b/sample/build_ios.sh index 9cdeb517..670f5152 100755 --- a/sample/build_ios.sh +++ b/sample/build_ios.sh @@ -34,7 +34,10 @@ mkdir -p "$BUILD_IPA_PATH" mkdir -p "$DERIVED_DATA_PATH" # Unity build command -UNITY_COMMAND="$PATH_UNITY -projectPath \"$PATH_TO_UNITY_SDK_SAMPLE_APP\" -executeMethod $BUILD_METHOD -logFile logFile.log -quit -batchmode --buildPath \"$BUILD_XCODE_PATH\" --platform iOS --bundleIdentifier com.immutable.Immutable-Sample-GameSDK" +UNITY_COMMAND="$PATH_UNITY -projectPath \"$PATH_TO_UNITY_SDK_SAMPLE_APP\" -executeMethod $BUILD_METHOD \ +-logFile logFile.log -quit -batchmode --buildPath \"$BUILD_XCODE_PATH\" \ +--platform iOS --bundleIdentifier com.immutable.Immutable-Sample-GameSDK \ +--host \"127.0.0.1\" --ciBuild" echo "Running command: $UNITY_COMMAND" # Execute the Unity build command From aed31c9d9c0ab1f1679e6ca2bbf265a11383762d Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Mon, 10 Mar 2025 10:26:10 +1300 Subject: [PATCH 2/7] test: fix ios browserstack appium set up --- .github/workflows/ui-tests.yml | 33 +- .../Passport/ZkEvm/ZkEvmSendTransaction.unity | 14 +- .../ZkEvm/ZkEvmSendTransactionScript.cs | 7 +- sample/ProjectSettings/ProjectSettings.asset | 4 +- sample/Tests/requirements-mobile.txt | 5 +- .../Tests/{ => test/ios}/browserstack.ios.yml | 13 +- sample/Tests/test/ios/conftest.py | 21 ++ sample/Tests/test/ios/test_ios.py | 237 ++++++++++++++ sample/Tests/test/test.py | 2 +- sample/Tests/test/test_ios.py | 291 ------------------ sample/build_ios.sh | 2 +- .../Editor/PassportAndroidProcessor.cs | 4 +- .../ZkEvm/ZkEvmSendTransactionScript.cs | 6 +- 13 files changed, 290 insertions(+), 349 deletions(-) rename sample/Tests/{ => test/ios}/browserstack.ios.yml (91%) create mode 100644 sample/Tests/test/ios/conftest.py create mode 100644 sample/Tests/test/ios/test_ios.py delete mode 100644 sample/Tests/test/test_ios.py diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 84027683..f4697e30 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -141,35 +141,6 @@ jobs: - name: Build iOS app working-directory: sample run: ./build_ios.sh - - name: Install jq (if not installed) - run: | - if ! command -v jq &> /dev/null; then - echo "jq not found, installing..." - brew install jq - else - echo "jq is already installed" - fi - - name: Upload iOS app to BrowserStack - working-directory: sample/Tests - env: - BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} - BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - run: | - curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \ - -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ - -F "file=@Payload.ipa" - - # Extract app_url from the response using jq - app_url=$(echo "$response" | jq -r '.app_url') - - if [ -z "$app_url" ]; then - echo "Error: app_url is not found in the response" - exit 1 # Fail the workflow if app_url is not present - fi - - echo "app_url: $app_url" - - echo "APP_URL=$app_url" >> $GITHUB_ENV - uses: actions/setup-python@v4 with: python-version: "3.10" @@ -180,6 +151,6 @@ jobs: 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" + working-directory: sample/Tests/test/ios + run: browserstack-sdk pytest -xs ./test_ios.py --browserstack.config "browserstack.ios.yml" diff --git a/sample/Assets/Scenes/Passport/ZkEvm/ZkEvmSendTransaction.unity b/sample/Assets/Scenes/Passport/ZkEvm/ZkEvmSendTransaction.unity index 3723f97a..ad07e508 100644 --- a/sample/Assets/Scenes/Passport/ZkEvm/ZkEvmSendTransaction.unity +++ b/sample/Assets/Scenes/Passport/ZkEvm/ZkEvmSendTransaction.unity @@ -1288,7 +1288,7 @@ MonoBehaviour: m_TargetGraphic: {fileID: 1741513414} m_HandleRect: {fileID: 1741513413} m_Direction: 2 - m_Value: 1 + m_Value: 0 m_Size: 1 m_NumberOfSteps: 0 m_OnValueChanged: @@ -1368,7 +1368,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 432.63553, y: 0} + m_AnchoredPosition: {x: 427.93484, y: 0} m_SizeDelta: {x: 0, y: 60} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &885137217 @@ -2046,7 +2046,7 @@ MonoBehaviour: m_EditorClassIdentifier: Output: {fileID: 1253661940} ConfirmToggle: {fileID: 1782090444} - GetTrasactionReceiptToggle: {fileID: 1873101809} + GetTransactionReceiptToggle: {fileID: 1873101809} ToInputField: {fileID: 649013321} ValueInputField: {fileID: 1901422923} DataInputField: {fileID: 1391622483} @@ -3406,8 +3406,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 885.5994, y: 0} - m_SizeDelta: {x: 1771.1991, y: 0} + m_AnchoredPosition: {x: 1153.199, y: 0} + m_SizeDelta: {x: 2306.398, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1833965463 MonoBehaviour: @@ -4341,8 +4341,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 885.59955, y: 0} - m_SizeDelta: {x: 1771.1991, y: 0} + m_AnchoredPosition: {x: 1153.199, y: 0} + m_SizeDelta: {x: 2306.398, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &2131064088 MonoBehaviour: diff --git a/sample/Assets/Scripts/Passport/ZkEvm/ZkEvmSendTransactionScript.cs b/sample/Assets/Scripts/Passport/ZkEvm/ZkEvmSendTransactionScript.cs index b99b30db..560fa43a 100644 --- a/sample/Assets/Scripts/Passport/ZkEvm/ZkEvmSendTransactionScript.cs +++ b/sample/Assets/Scripts/Passport/ZkEvm/ZkEvmSendTransactionScript.cs @@ -6,6 +6,7 @@ using Immutable.Passport; using Immutable.Passport.Model; using Cysharp.Threading.Tasks; +using UnityEngine.Serialization; public class ZkEvmSendTransactionScript : MonoBehaviour { @@ -13,7 +14,7 @@ public class ZkEvmSendTransactionScript : MonoBehaviour [SerializeField] private Text Output; [SerializeField] private Toggle ConfirmToggle; - [SerializeField] private Toggle GetTrasactionReceiptToggle; + [SerializeField] private Toggle GetTransactionReceiptToggle; [SerializeField] private InputField ToInputField; [SerializeField] private InputField ValueInputField; [SerializeField] private InputField DataInputField; @@ -31,7 +32,7 @@ void Start() // Show get transaction receipt option if send transaction with confirmation toggle is off ConfirmToggle.onValueChanged.AddListener(delegate { - GetTrasactionReceiptToggle.gameObject.SetActive(!ConfirmToggle.isOn); + GetTransactionReceiptToggle.gameObject.SetActive(!ConfirmToggle.isOn); }); } else @@ -73,7 +74,7 @@ public async void SendTransaction() string transactionHash = await Passport.ZkEvmSendTransaction(request); // Check if receipt is requested - if (GetTrasactionReceiptToggle.isOn) + if (GetTransactionReceiptToggle.isOn) { // Poll for the receipt and display transaction status string? status = await PollStatus(transactionHash); diff --git a/sample/ProjectSettings/ProjectSettings.asset b/sample/ProjectSettings/ProjectSettings.asset index 0428f12d..a57f5808 100644 --- a/sample/ProjectSettings/ProjectSettings.asset +++ b/sample/ProjectSettings/ProjectSettings.asset @@ -157,13 +157,13 @@ PlayerSettings: applicationIdentifier: Android: com.immutable.ImmutableSample Standalone: com.immutable.Immutable-Sample - iPhone: com.immutable.Immutable-Sample + iPhone: com.immutable.Immutable-Sample-GameSDK buildNumber: Standalone: 0 VisionOS: 0 iPhone: 0 tvOS: 0 - overrideDefaultApplicationIdentifier: 0 + overrideDefaultApplicationIdentifier: 1 AndroidBundleVersionCode: 1 AndroidMinSdkVersion: 26 AndroidTargetSdkVersion: 33 diff --git a/sample/Tests/requirements-mobile.txt b/sample/Tests/requirements-mobile.txt index 3fc2df38..e24ff82c 100644 --- a/sample/Tests/requirements-mobile.txt +++ b/sample/Tests/requirements-mobile.txt @@ -7,4 +7,7 @@ pytest==8.2.2 requests==2.32.3 mailslurp-client==15.19.22 Appium-Python-Client -browserstack-sdk \ No newline at end of file +browserstack-sdk +browserstack-local +pytest-variables +pytest-selenium \ No newline at end of file diff --git a/sample/Tests/browserstack.ios.yml b/sample/Tests/test/ios/browserstack.ios.yml similarity index 91% rename from sample/Tests/browserstack.ios.yml rename to sample/Tests/test/ios/browserstack.ios.yml index 4d39ce54..f8a15b5d 100644 --- a/sample/Tests/browserstack.ios.yml +++ b/sample/Tests/test/ios/browserstack.ios.yml @@ -21,7 +21,7 @@ buildName: iOS build # Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression} framework: pytest -source: pytest-browserstack:sample-sdk:v1.0 +source: pytest-appium-browserstack:sample-sdk:v1.0 # Set `app` to define the app that is to be used for testing. # It can either take the id of any uploaded app or the path of the app directly. @@ -35,9 +35,9 @@ app: ./Payload.ipa #For running local tests # Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate) platforms: - - platformName: ios - deviceName: iPhone 14 Pro Max - platformVersion: 16 + - deviceName: iPhone 14 Pro + osVersion: 16 + platformName: ios # ======================= # Parallels per Platform @@ -57,10 +57,10 @@ parallelsPerPlatform: 1 # Set browserStackLocal to true if your website under test is not accessible publicly over the internet # Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction browserstackLocal: true # (Default false) -browserStackLocalOptions: +# browserStackLocalOptions: #Options to be passed to BrowserStack local in-case of advanced configurations # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local. - forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. + # forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections # =================== @@ -70,4 +70,3 @@ debug: false # # Set to true if you need screenshots for every seleniu networkLogs: false # Set to true to enable HAR logs capturing consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors) # Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) -acceptInsecureCerts: true diff --git a/sample/Tests/test/ios/conftest.py b/sample/Tests/test/ios/conftest.py new file mode 100644 index 00000000..6c343087 --- /dev/null +++ b/sample/Tests/test/ios/conftest.py @@ -0,0 +1,21 @@ +import pytest +from appium import webdriver +from alttester import * + +@pytest.fixture(scope='function') +def setWebdriver(request, session_capabilities): + session_capabilities["autoAcceptAlerts"] = True + remoteURL = "https://hub.browserstack.com/wd/hub" + driver = webdriver.Remote(remoteURL, session_capabilities) + + altdriver = AltDriver(timeout=120) + + request.instance.driver = driver + request.instance.altdriver = altdriver + + request.node._driver = driver + + yield driver + + driver.quit() + altdriver.stop() \ No newline at end of file diff --git a/sample/Tests/test/ios/test_ios.py b/sample/Tests/test/ios/test_ios.py new file mode 100644 index 00000000..afee4b5c --- /dev/null +++ b/sample/Tests/test/ios/test_ios.py @@ -0,0 +1,237 @@ +import os +import sys +import time +from pathlib import Path +import pytest + +from appium.webdriver.common.appiumby import AppiumBy +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.by import By + +from alttester import * + +PROJECT_ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(PROJECT_ROOT / "test")) +sys.path.insert(0, str(PROJECT_ROOT / "src")) +from test import TestConfig, UnityTest +from fetch_otp import fetch_code + +@pytest.mark.usefixtures('setWebdriver') +class TestIos: + + def login(self): + driver = self.driver + + # Wait for the ASWebAuthenticationSession context to appear + WebDriverWait(driver, 30).until(lambda d: len(d.contexts) > 1) + time.sleep(5) # Refresh contexts by waiting before fetching again + print("Available contexts:", driver.contexts) + contexts = driver.contexts + driver.switch_to.context(driver.contexts[-1]) + print("Current context:", driver.current_context) + + target_context = None + + # Since it's unclear which WebView context contains the email field on iOS, + # we need to iterate through each context to identify the correct one. + for context in contexts: + if context == "NATIVE_APP": + continue + + driver.switch_to.context(context) + + try: + # Attempt to find the email input field + email_field = WebDriverWait(driver, 5).until( + EC.presence_of_element_located((AppiumBy.XPATH, "//input[@name='address']")) + ) + # Found email + target_context = context + + email_field.send_keys(TestConfig.EMAIL) + submit_button = driver.find_element(by=AppiumBy.XPATH, value="//form/div/div/div[2]/button") + submit_button.click() + + time.sleep(10) # Wait for OTP + + code = fetch_code() + assert code, "Failed to fetch OTP from MailSlurp" + print(f"Successfully fetched OTP: {code}") + + # Unlike on Android, each digit must be entered into a separate input field on iOS. + for i, digit in enumerate(code): + otp_field = driver.find_element(by=AppiumBy.XPATH, value=f"//div[@id='passwordless_container']/div[{i + 1}]/input") + otp_field.send_keys(digit) + + break + except: + # If the field is not found, continue to the next context + print(f"Email field not found in context: {context}") + + # If target context was not found, raise an error + if not target_context: + raise Exception("Could not find the email field in any webview context.") + + # @classmethod + # def close_and_open_app(cls): + # driver = cls.driver + + # # Close app + # time.sleep(5) + # print("Closing app...") + # driver.terminate_app(TestConfig.IOS_BUNDLE_ID) + # time.sleep(5) + # print("Closed app") + + # # Reopen app + # print("Opening app...") + # driver.activate_app(TestConfig.IOS_BUNDLE_ID) + # time.sleep(10) + # print("Opened app") + + def test_1_pkce_login(self): + # Select use PKCE auth + self.altdriver.find_object(By.NAME, "PKCE").tap() + + # Wait for unauthenticated screen + self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + + # Login + loginBtn = self.altdriver.wait_for_object(By.NAME, "LoginBtn") + loginBtn.tap() + + self.login() + + # Wait for authenticated screen + self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + + # 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_pkce_relogin(self): + # driver = self.appium_driver + + # self.close_and_open_app() + + # # Restart AltTester + # self.altdriver.stop() + # self.altdriver = AltDriver() + # time.sleep(5) + + # # # Select use PKCE auth + # self.altdriver.find_object(By.NAME, "PKCE").tap() + # # Wait for unauthenticated screen + # self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + + # # 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) + + # # Click Connect to IMX button + # self.altdriver.find_object(By.NAME, "ConnectBtn").tap() + # self.assertEqual("Connected to IMX", output.get_text()) + + # self.altdriver.stop() + + # def test_7_pkce_reconnect(self): + # self.close_and_open_app() + + # # Restart AltTester + # self.altdriver.stop() + # self.altdriver = AltDriver() + # time.sleep(5) + + # # Select use PKCE auth + # self.altdriver.find_object(By.NAME, "PKCE").tap() + # # Wait for unauthenticated screen + # self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + + # # 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) + + # # 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) + + # # Wait for authenticated screen + # self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + # time.sleep(5) + # print("Logged out") + + # self.altdriver.stop() + + # def test_8_pkce_connect_imx(self): + # self.close_and_open_app() + + # # Restart AltTester + # self.altdriver.stop() + # self.altdriver = AltDriver() + # time.sleep(5) + + # # Select use PKCE auth + # self.altdriver.find_object(By.NAME, "PKCE").tap() + # # Wait for unauthenticated screen + # self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + + # # Connect IMX + # print("Logging in and connecting to IMX...") + # self.altdriver.wait_for_object(By.NAME, "ConnectBtn").tap() + + # self.login() + + # # Wait for authenticated screen + # self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") + # print("Logged in and connected to IMX") + + # # 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()) + + # # Logout + # print("Logging out...") + # self.altdriver.find_object(By.NAME, "LogoutBtn").tap() + # time.sleep(5) + + # # Wait for authenticated screen + # self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") + # time.sleep(5) + # print("Logged out") \ No newline at end of file diff --git a/sample/Tests/test/test.py b/sample/Tests/test/test.py index 76db716c..a03a214e 100644 --- a/sample/Tests/test/test.py +++ b/sample/Tests/test/test.py @@ -11,7 +11,7 @@ class TestConfig: PASSPORT_ID="email|67480492219c150aceeb1f37" WALLET_ADDRESS = "0x547044ea95f03651139081241c99ffedbefdc5e8" ANDROID_PACKAGE = "com.immutable.ImmutableSample" - IOS_BUNDLE_ID = "com.immutable.Immutable-Sample" + IOS_BUNDLE_ID = "com.immutable.Immutable-Sample-GameSDK" class UnityTest(unittest.TestCase): diff --git a/sample/Tests/test/test_ios.py b/sample/Tests/test/test_ios.py deleted file mode 100644 index d3a5fbe0..00000000 --- a/sample/Tests/test/test_ios.py +++ /dev/null @@ -1,291 +0,0 @@ -import os -import sys -import time -from pathlib import Path -from datetime import datetime - -from appium import webdriver -from appium.options.ios import XCUITestOptions -from appium.webdriver.common.appiumby import AppiumBy -from appium.webdriver.webdriver import WebDriver -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC - -from alttester import * - -from test import TestConfig, UnityTest - -from browserstack.local import Local - -sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'src')) -from fetch_otp import fetch_code - -BROWSERSTACK_USERNAME = os.environ.get("BROWSERSTACK_USERNAME") -BROWSERSTACK_ACCESS_KEY = os.environ.get("BROWSERSTACK_ACCESS_KEY") -APP_URL = os.environ.get("APP_URL") - -# To run this test on an actual Android device: appium --base-path /wd/hub --allow-insecure chromedriver_autodownload -class TestBase(UnityTest): - altdriver = None - appium_driver = None - browser_stack_local = None - - @classmethod - def setUpClass(cls): - # https://appium.github.io/appium-xcuitest-driver/latest/preparation/real-device-config/ - options = XCUITestOptions() - options.show_xcode_log = True - options.auto_accept_alerts = True - - browserstack_options = { - "projectName": "Unity Sample App", - "buildName": "Unity Sample App iOS", - "sessionName": f"tests - {datetime.now().strftime('%B %d - %H:%M')}", - "local": "true", - "userName": BROWSERSTACK_USERNAME, - "accessKey": BROWSERSTACK_ACCESS_KEY, - "idleTimeout": 300 - } - - # Set capabilities in the options object - options.set_capability("bstack:options", browserstack_options) - options.set_capability("platformName", "iOS") - options.set_capability("platformVersion", "18") - options.set_capability("deviceName", "iPhone 14") - options.set_capability("app", APP_URL) - - # Start BrowserStack Local - browser_stack_local = Local() - bs_local_args = {"key": BROWSERSTACK_ACCESS_KEY} - browser_stack_local.start(**bs_local_args) - print("BrowserStack Local is running:", browser_stack_local.isRunning()) - - cls.appium_driver = webdriver.Remote('https://hub-cloud.browserstack.com/wd/hub/', options=options) - - time.sleep(10) - - cls.altdriver = AltDriver(timeout=120) - - @classmethod - def tearDownClass(cls): - print("\nEnding") - cls.appium_driver.quit() - cls.altdriver.stop() - cls.browser_stack_local.stop() - - @classmethod - def login(cls): - driver = cls.appium_driver - - # Wait for the ASWebAuthenticationSession context to appear - WebDriverWait(driver, 30).until(lambda d: len(d.contexts) > 2) - time.sleep(5) # Refresh contexts by waiting before fetching again - print("Available contexts:", driver.contexts) - contexts = driver.contexts - driver.switch_to.context(driver.contexts[-1]) - print("Current context:", driver.current_context) - - target_context = None - - # Since it's unclear which WebView context contains the email field on iOS, - # we need to iterate through each context to identify the correct one. - for context in contexts: - if context == "NATIVE_APP": - continue - - driver.switch_to.context(context) - - try: - # Attempt to find the email input field - email_field = WebDriverWait(driver, 5).until( - EC.presence_of_element_located((AppiumBy.XPATH, "//input[@name='address']")) - ) - # Found email - target_context = context - - email_field.send_keys(TestConfig.EMAIL) - submit_button = driver.find_element(by=AppiumBy.XPATH, value="//form/div/div/div[2]/button") - submit_button.click() - - time.sleep(10) # Wait for OTP - - code = fetch_code() - assert code, "Failed to fetch OTP from MailSlurp" - print(f"Successfully fetched OTP: {code}") - - # Unlike on Android, each digit must be entered into a separate input field on iOS. - for i, digit in enumerate(code): - otp_field = driver.find_element(by=AppiumBy.XPATH, value=f"//div[@id='passwordless_container']/div[{i + 1}]/input") - otp_field.send_keys(digit) - - break - except: - # If the field is not found, continue to the next context - print(f"Email field not found in context: {context}") - - # If target context was not found, raise an error - if not target_context: - raise Exception("Could not find the email field in any webview context.") - - @classmethod - def close_and_open_app(cls): - driver = cls.appium_driver - - # Close app - time.sleep(5) - print("Closing app...") - driver.terminate_app(TestConfig.IOS_BUNDLE_ID) - time.sleep(5) - print("Closed app") - - # Reopen app - print("Opening app...") - driver.activate_app(TestConfig.IOS_BUNDLE_ID) - time.sleep(10) - print("Opened app") - - def test_1_pkce_login(self): - # Select use PKCE auth - self.altdriver.find_object(By.NAME, "PKCE").tap() - - # Wait for unauthenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - - # Login - loginBtn = self.altdriver.wait_for_object(By.NAME, "LoginBtn") - loginBtn.tap() - - self.login() - - # Wait for authenticated screen - self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") - - 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_pkce_relogin(self): - driver = self.appium_driver - - self.close_and_open_app() - - # Restart AltTester - self.altdriver.stop() - self.altdriver = AltDriver() - time.sleep(5) - - # # Select use PKCE auth - self.altdriver.find_object(By.NAME, "PKCE").tap() - # Wait for unauthenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - - # 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) - - # Click Connect to IMX button - self.altdriver.find_object(By.NAME, "ConnectBtn").tap() - self.assertEqual("Connected to IMX", output.get_text()) - - self.altdriver.stop() - - def test_7_pkce_reconnect(self): - self.close_and_open_app() - - # Restart AltTester - self.altdriver.stop() - self.altdriver = AltDriver() - time.sleep(5) - - # Select use PKCE auth - self.altdriver.find_object(By.NAME, "PKCE").tap() - # Wait for unauthenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - - # 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) - - # 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) - - # Wait for authenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - time.sleep(5) - print("Logged out") - - self.altdriver.stop() - - def test_8_pkce_connect_imx(self): - self.close_and_open_app() - - # Restart AltTester - self.altdriver.stop() - self.altdriver = AltDriver() - time.sleep(5) - - # Select use PKCE auth - self.altdriver.find_object(By.NAME, "PKCE").tap() - # Wait for unauthenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - - # Connect IMX - print("Logging in and connecting to IMX...") - self.altdriver.wait_for_object(By.NAME, "ConnectBtn").tap() - - self.login() - - # Wait for authenticated screen - self.altdriver.wait_for_current_scene_to_be("AuthenticatedScene") - print("Logged in and connected to IMX") - - # 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()) - - # Logout - print("Logging out...") - self.altdriver.find_object(By.NAME, "LogoutBtn").tap() - time.sleep(5) - - # Wait for authenticated screen - self.altdriver.wait_for_current_scene_to_be("UnauthenticatedScene") - time.sleep(5) - print("Logged out") \ No newline at end of file diff --git a/sample/build_ios.sh b/sample/build_ios.sh index 670f5152..440e932d 100755 --- a/sample/build_ios.sh +++ b/sample/build_ios.sh @@ -64,4 +64,4 @@ mv "$(pwd)/build/output/iOS/DerivedData/Build/Products/ReleaseForRunning-iphoneo pushd "$(pwd)/build/output/iOS/IPA" && zip -r Payload.zip Payload && popd -mv "$(pwd)/build/output/iOS/IPA/Payload.zip" "$(pwd)/Tests/Payload.ipa" \ No newline at end of file +mv "$(pwd)/build/output/iOS/IPA/Payload.zip" "$(pwd)/Tests/test/ios/Payload.ipa" \ No newline at end of file diff --git a/src/Packages/Passport/Editor/PassportAndroidProcessor.cs b/src/Packages/Passport/Editor/PassportAndroidProcessor.cs index c27195d6..9a84f28f 100644 --- a/src/Packages/Passport/Editor/PassportAndroidProcessor.cs +++ b/src/Packages/Passport/Editor/PassportAndroidProcessor.cs @@ -24,7 +24,7 @@ public void OnPostGenerateGradleAndroidProject(string path) } FileHelpers.CopyDirectory(passportWebFilesDir, $"{path}/src/main/assets/ImmutableSDK/Runtime/Passport"); - Debug.Log($"Sucessfully copied Passport files"); + Debug.Log($"Successfully copied Passport files"); AddUseAndroidX(path); } @@ -46,4 +46,4 @@ private void AddUseAndroidX(string path) } } -#endif \ No newline at end of file +#endif diff --git a/src/Packages/Passport/Samples~/SamplesScenesScripts/Scripts/Passport/ZkEvm/ZkEvmSendTransactionScript.cs b/src/Packages/Passport/Samples~/SamplesScenesScripts/Scripts/Passport/ZkEvm/ZkEvmSendTransactionScript.cs index b99b30db..971cb418 100644 --- a/src/Packages/Passport/Samples~/SamplesScenesScripts/Scripts/Passport/ZkEvm/ZkEvmSendTransactionScript.cs +++ b/src/Packages/Passport/Samples~/SamplesScenesScripts/Scripts/Passport/ZkEvm/ZkEvmSendTransactionScript.cs @@ -13,7 +13,7 @@ public class ZkEvmSendTransactionScript : MonoBehaviour [SerializeField] private Text Output; [SerializeField] private Toggle ConfirmToggle; - [SerializeField] private Toggle GetTrasactionReceiptToggle; + [SerializeField] private Toggle GetTransactionReceiptToggle; [SerializeField] private InputField ToInputField; [SerializeField] private InputField ValueInputField; [SerializeField] private InputField DataInputField; @@ -31,7 +31,7 @@ void Start() // Show get transaction receipt option if send transaction with confirmation toggle is off ConfirmToggle.onValueChanged.AddListener(delegate { - GetTrasactionReceiptToggle.gameObject.SetActive(!ConfirmToggle.isOn); + GetTransactionReceiptToggle.gameObject.SetActive(!ConfirmToggle.isOn); }); } else @@ -73,7 +73,7 @@ public async void SendTransaction() string transactionHash = await Passport.ZkEvmSendTransaction(request); // Check if receipt is requested - if (GetTrasactionReceiptToggle.isOn) + if (GetTransactionReceiptToggle.isOn) { // Poll for the receipt and display transaction status string? status = await PollStatus(transactionHash); From 43634fc5ca1d9e6e6ccd327b051e24099209253a Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Mon, 10 Mar 2025 11:09:40 +1300 Subject: [PATCH 3/7] test: fix ios xcode build path --- sample/.gitignore | 2 ++ sample/build_ios.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 sample/.gitignore diff --git a/sample/.gitignore b/sample/.gitignore new file mode 100644 index 00000000..991944bb --- /dev/null +++ b/sample/.gitignore @@ -0,0 +1,2 @@ +**/Payload.ipa +**/logFile.log \ No newline at end of file diff --git a/sample/build_ios.sh b/sample/build_ios.sh index 440e932d..bba80f62 100755 --- a/sample/build_ios.sh +++ b/sample/build_ios.sh @@ -50,7 +50,7 @@ if [ $? -ne 0 ]; then fi echo "Building app..." -xcodebuild clean build \ +/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild clean build \ -project "$(pwd)/build/output/iOS/Xcode/Unity-iPhone.xcodeproj" \ -scheme Unity-iPhone \ -destination "generic/platform=iOS" \ From b5e67898b828e1c3468c170b015ef8b2b2f7cc16 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Mon, 10 Mar 2025 11:50:08 +1300 Subject: [PATCH 4/7] fix: mobile test requirements --- sample/.gitignore | 3 ++- sample/Tests/requirements-mobile.txt | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sample/.gitignore b/sample/.gitignore index 991944bb..da85648b 100644 --- a/sample/.gitignore +++ b/sample/.gitignore @@ -1,2 +1,3 @@ **/Payload.ipa -**/logFile.log \ No newline at end of file +**/logFile.log +**/local.log \ No newline at end of file diff --git a/sample/Tests/requirements-mobile.txt b/sample/Tests/requirements-mobile.txt index e24ff82c..a4eb2323 100644 --- a/sample/Tests/requirements-mobile.txt +++ b/sample/Tests/requirements-mobile.txt @@ -10,4 +10,8 @@ Appium-Python-Client browserstack-sdk browserstack-local pytest-variables -pytest-selenium \ No newline at end of file +pytest-selenium +jsonmerge +multiprocess +paver +psutil \ No newline at end of file From 219b08542a3ce76e33e2a40a6627404b3bd6b5ae Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Mon, 10 Mar 2025 20:51:32 +1300 Subject: [PATCH 5/7] test: auto accept ios alerts --- sample/Tests/test/ios/browserstack.ios.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sample/Tests/test/ios/browserstack.ios.yml b/sample/Tests/test/ios/browserstack.ios.yml index f8a15b5d..d85ea5de 100644 --- a/sample/Tests/test/ios/browserstack.ios.yml +++ b/sample/Tests/test/ios/browserstack.ios.yml @@ -70,3 +70,5 @@ debug: false # # Set to true if you need screenshots for every seleniu networkLogs: false # Set to true to enable HAR logs capturing consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors) # Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) + +autoAcceptAlerts: true \ No newline at end of file From fa454688691aae5fa891159cd1c9deeb9f1edaa3 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Mon, 10 Mar 2025 22:10:39 +1300 Subject: [PATCH 6/7] ci: uncomment ui tests for windows, ios and android --- .github/workflows/ui-tests.yml | 238 ++++++++++++++++----------------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index f4697e30..6bda5c22 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -12,125 +12,125 @@ concurrency: cancel-in-progress: true jobs: - # build: - # name: Build ${{ matrix.targetPlatform }} for AltTester 🛠️ - # runs-on: ubuntu-latest-8-cores - # strategy: - # fail-fast: false - # matrix: - # include: - # - targetPlatform: StandaloneOSX - # buildMethod: MacBuilder.BuildForAltTester - # buildPath: sample/Builds/MacOS - # - targetPlatform: StandaloneWindows64 - # buildMethod: WindowsBuilder.BuildForAltTester - # buildPath: sample/Builds/Windows64 - # - targetPlatform: Android - # buildMethod: MobileBuilder.BuildForAltTester - # buildPath: sample/Builds/Android - # steps: - # - uses: actions/checkout@v3 - # with: - # lfs: true - # - uses: actions/cache@v3 - # with: - # path: Library - # key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('sample/Assets/**', 'sample/Packages/**', 'sample/ProjectSettings/**') }} - # restore-keys: | - # Library-${{ matrix.targetPlatform }} - # Library- - # - name: Build project - # uses: game-ci/unity-builder@v4 - # env: - # UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} - # UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} - # UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} - # with: - # targetPlatform: ${{ matrix.targetPlatform }} - # projectPath: sample - # buildMethod: ${{ matrix.buildMethod }} - # customParameters: -logFile logFile.log -quit -batchmode - # artifactsPath: ${{ matrix.buildPath }} - # - name: List build directory - # run: ls -R ./ - # - name: Ensure build path exists - # run: mkdir -p ${{ matrix.buildPath }} - # - name: Upload artifact - # uses: actions/upload-artifact@v4 - # if: always() - # with: - # name: Build-${{ matrix.targetPlatform }} - # path: ${{ matrix.buildPath }} - # test: - # name: Run ${{ matrix.targetPlatform }} UI tests 🧪 - # needs: build - # strategy: - # matrix: - # include: - # - targetPlatform: StandaloneOSX - # runs-on: [self-hosted, macOS] - # 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 - # - 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 }} - # steps: - # - uses: actions/checkout@v3 - # with: - # lfs: true - # - name: Create temporary keychain - # if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} - # run: | - # security list-keychains - # security delete-keychain temporary || true - # security list-keychains - # security create-keychain -p "" temporary - # security default-keychain -s temporary - # security unlock-keychain -p "" temporary - # security set-keychain-settings -lut 600 temporary - # - uses: actions/download-artifact@v4 - # 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" - # - name: Install dependencies (Windows) - # if: ${{ matrix.targetPlatform == 'StandaloneWindows64' }} - # run: pip install -r "sample/Tests/requirements-desktop.txt" - # - name: Install dependencies (Mac) - # if: ${{ matrix.targetPlatform != 'StandaloneWindows64' }} - # run: | - # if [[ "${{ matrix.targetPlatform }}" == "StandaloneOSX" ]]; then - # pip uninstall -y browserstack-sdk || true - # pip install -r "sample/Tests/requirements-desktop.txt" - # else - # pip install -r "sample/Tests/requirements-mobile.txt" - # fi - # - name: Run UI tests - # env: - # UNITY_APP_PATH: SampleApp.app - # UNITY_APP_NAME: SampleApp - # MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} - # BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} - # BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - # working-directory: sample/Tests - # run: ${{ matrix.test_script }} - # - name: Remove temporary keychain - # if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} - # run: | - # security list-keychains - # security default-keychain -s ~/Library/Keychains/login.keychain-db - # security delete-keychain temporary - # security list-keychains + build: + name: Build ${{ matrix.targetPlatform }} for AltTester 🛠️ + runs-on: ubuntu-latest-8-cores + strategy: + fail-fast: false + matrix: + include: + - targetPlatform: StandaloneOSX + buildMethod: MacBuilder.BuildForAltTester + buildPath: sample/Builds/MacOS + - targetPlatform: StandaloneWindows64 + buildMethod: WindowsBuilder.BuildForAltTester + buildPath: sample/Builds/Windows64 + - targetPlatform: Android + buildMethod: MobileBuilder.BuildForAltTester + buildPath: sample/Builds/Android + steps: + - uses: actions/checkout@v3 + with: + lfs: true + - uses: actions/cache@v3 + with: + path: Library + key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('sample/Assets/**', 'sample/Packages/**', 'sample/ProjectSettings/**') }} + restore-keys: | + Library-${{ matrix.targetPlatform }} + Library- + - name: Build project + uses: game-ci/unity-builder@v4 + env: + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} + with: + targetPlatform: ${{ matrix.targetPlatform }} + projectPath: sample + buildMethod: ${{ matrix.buildMethod }} + customParameters: -logFile logFile.log -quit -batchmode + artifactsPath: ${{ matrix.buildPath }} + - name: List build directory + run: ls -R ./ + - name: Ensure build path exists + run: mkdir -p ${{ matrix.buildPath }} + - name: Upload artifact + uses: actions/upload-artifact@v4 + if: always() + with: + name: Build-${{ matrix.targetPlatform }} + path: ${{ matrix.buildPath }} + test: + name: Run ${{ matrix.targetPlatform }} UI tests 🧪 + needs: build + strategy: + matrix: + include: + - targetPlatform: StandaloneOSX + runs-on: [self-hosted, macOS] + 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 + - 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 }} + steps: + - uses: actions/checkout@v3 + with: + lfs: true + - name: Create temporary keychain + if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} + run: | + security list-keychains + security delete-keychain temporary || true + security list-keychains + security create-keychain -p "" temporary + security default-keychain -s temporary + security unlock-keychain -p "" temporary + security set-keychain-settings -lut 600 temporary + - uses: actions/download-artifact@v4 + 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" + - name: Install dependencies (Windows) + if: ${{ matrix.targetPlatform == 'StandaloneWindows64' }} + run: pip install -r "sample/Tests/requirements-desktop.txt" + - name: Install dependencies (Mac) + if: ${{ matrix.targetPlatform != 'StandaloneWindows64' }} + run: | + if [[ "${{ matrix.targetPlatform }}" == "StandaloneOSX" ]]; then + pip uninstall -y browserstack-sdk || true + pip install -r "sample/Tests/requirements-desktop.txt" + else + pip install -r "sample/Tests/requirements-mobile.txt" + fi + - name: Run UI tests + env: + UNITY_APP_PATH: SampleApp.app + UNITY_APP_NAME: SampleApp + MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }} + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + working-directory: sample/Tests + run: ${{ matrix.test_script }} + - name: Remove temporary keychain + if: ${{ matrix.targetPlatform == 'StandaloneOSX' }} + run: | + security list-keychains + security default-keychain -s ~/Library/Keychains/login.keychain-db + security delete-keychain temporary + security list-keychains test-ios: name: Run iOS UI tests 🧪 runs-on: [ self-hosted, macOS ] From 6172c8cd0f461cbcf714dd8c6bad630237a4aed1 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Tue, 11 Mar 2025 08:40:11 +1300 Subject: [PATCH 7/7] ci: fix scripting symbols for ios --- sample/Assets/Editor/iOSPostBuildProcessor.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sample/Assets/Editor/iOSPostBuildProcessor.cs b/sample/Assets/Editor/iOSPostBuildProcessor.cs index 82d5e95c..64ebbd75 100644 --- a/sample/Assets/Editor/iOSPostBuildProcessor.cs +++ b/sample/Assets/Editor/iOSPostBuildProcessor.cs @@ -1,3 +1,5 @@ +#if UNITY_IPHONE && UNITY_EDITOR_OSX + using UnityEngine; using UnityEditor; using UnityEditor.Callbacks; @@ -80,4 +82,6 @@ private static string GetBundleIdentifierFromArgs() } return "com.immutable.Immutable-Sample"; // Default fallback } -} \ No newline at end of file +} + +#endif \ No newline at end of file