diff --git a/.github/workflows/browserstack-e2e-android.yml b/.github/workflows/browserstack-e2e-android-core.yml similarity index 68% rename from .github/workflows/browserstack-e2e-android.yml rename to .github/workflows/browserstack-e2e-android-core.yml index 8e2723daa..56952adc4 100644 --- a/.github/workflows/browserstack-e2e-android.yml +++ b/.github/workflows/browserstack-e2e-android-core.yml @@ -4,7 +4,7 @@ # This software may be modified and distributed under the terms # of the MIT license. See the LICENSE file for details. # -name: BrowserStack E2E — Android +name: BrowserStack E2E — Android Foundation Modules on: workflow_call: @@ -18,7 +18,7 @@ on: description: BrowserStack session name used by Detox required: false type: string - default: android-detox-e2e + default: android-detox-e2e-core secrets: BROWSERSTACK_USERNAME: description: BrowserStack account username @@ -61,23 +61,18 @@ permissions: contents: read jobs: - e2e-android-browserstack: - name: E2E — Android BrowserStack + e2e-android-core: + name: E2E — Android BrowserStack (foundation) runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 45 defaults: run: working-directory: PingTestRunner shell: bash env: - # APK paths — defined once, referenced in upload steps - APP_APK_PATH: android/app/build/outputs/apk/release/app-release.apk - TEST_APK_PATH: android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk - # BrowserStack credentials available to all steps BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - # Ping server configuration available to all steps PING_SERVER_URL: ${{ secrets.PING_SERVER_URL }} PING_REALM_PATH: ${{ secrets.PING_REALM_PATH }} PING_COOKIE_NAME: ${{ secrets.PING_COOKIE_NAME }} @@ -101,17 +96,23 @@ jobs: npm install -g jest npm install -g detox-cli - - name: Set up Java - uses: actions/setup-java@v4 + - name: Download app APK artifact + uses: actions/download-artifact@v4 with: - distribution: temurin - java-version: 17 + name: pingtestrunner-android-bs-app + path: PingTestRunner/android/app/build/outputs/apk/release + + - name: Download test APK artifact + uses: actions/download-artifact@v4 + with: + name: pingtestrunner-android-bs-test-suite + path: PingTestRunner/android/app/build/outputs/apk/androidTest/release - name: Compute BrowserStack identifiers # Naming convention: # Project : rn--test → rn-sdk-test - # Build : --- - # Session : -- + # Build : ----core + # Session : --core run: | set -euo pipefail SHORT_SHA="${GITHUB_SHA:0:7}" @@ -126,49 +127,17 @@ jobs: BUILD_PREFIX="run" ;; esac - echo "BROWSERSTACK_BUILD_ID=${BUILD_PREFIX}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${SHORT_SHA}" >> "$GITHUB_ENV" + echo "BROWSERSTACK_BUILD_ID=${BUILD_PREFIX}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${SHORT_SHA}-core" >> "$GITHUB_ENV" echo "BROWSERSTACK_PROJECT_NAME=${{ inputs.browserstack_project_name }}" >> "$GITHUB_ENV" echo "BROWSERSTACK_SESSION_NAME=${{ inputs.browserstack_session_name }}" >> "$GITHUB_ENV" - - name: Build SDK packages - # Metro bundles the JS at release build time and resolves the Ping SDK - # packages via their compiled lib/ output. Build all packages first so - # lib/module/index.js exists for every dependency. - run: yarn packages:build - working-directory: ${{ github.workspace }} - - - name: Generate codegen artifacts for all SDK modules - # React Native New Architecture requires each library's codegen JNI - # directories to exist before CMake configures the app. Run codegen - # across all included modules so the directories are present. - run: | - cd android && ./gradlew \ - :ping-identity_rn-binding:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-browser:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-device-client:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-device-id:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-device-profile:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-external-idp:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-fido:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-journey:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-logger:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-oath:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-oidc:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-push:generateCodegenArtifactsFromSchema \ - :ping-identity_rn-storage:generateCodegenArtifactsFromSchema \ - :react-native-async-storage_async-storage:generateCodegenArtifactsFromSchema - - - name: Build Release APK and androidTest APK - run: yarn build:runner:bs:android - working-directory: ${{ github.workspace }} - - name: Upload app to BrowserStack id: upload-app run: | response=$(curl --silent --fail-with-body \ -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ -X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/app" \ - -F "file=@$APP_APK_PATH") + -F "file=@android/app/build/outputs/apk/release/app-release.apk") app_url=$(echo "$response" | jq -r '.app_url') if [[ "$app_url" == "null" || -z "$app_url" ]]; then echo "::error::Failed to get app_url from BrowserStack response: $response" @@ -182,7 +151,7 @@ jobs: response=$(curl --silent --fail-with-body \ -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ -X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/app-client" \ - -F "file=@$TEST_APK_PATH") + -F "file=@android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk") test_url=$(echo "$response" | jq -r '.app_client_url') if [[ "$test_url" == "null" || -z "$test_url" ]]; then echo "::error::Failed to get app_client_url from BrowserStack response: $response" @@ -190,14 +159,32 @@ jobs: fi echo "test_url=$test_url" >> "$GITHUB_OUTPUT" - - name: Run E2E tests on BrowserStack real devices + - name: Run E2E core tests on BrowserStack real devices id: run-tests - run: yarn test:runner:bs:android 2>&1 | tee /tmp/detox-bs.log; exit "${PIPESTATUS[0]}" + run: yarn test:runner:bs:android:core 2>&1 | tee /tmp/detox-bs.log; exit "${PIPESTATUS[0]}" working-directory: ${{ github.workspace }} env: BROWSERSTACK_APP_URL: ${{ steps.upload-app.outputs.app_url }} BROWSERSTACK_TEST_URL: ${{ steps.upload-test.outputs.test_url }} + - name: View BrowserStack test results + if: always() && steps.run-tests.outcome != 'skipped' + shell: bash + run: | + set -euo pipefail + + session_id="$(grep -oP '(?<=session_id=)[^[:space:]]+' /tmp/detox-bs.log | head -1 || true)" + + if [ -z "$session_id" ]; then + echo "Session ID not found in Detox logs — visit https://app-automate.browserstack.com/dashboard/v2 to view results." + exit 0 + fi + + echo "Fetching BrowserStack session details for session: $session_id" + curl -s \ + -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ + -X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/sessions/${session_id}" + - name: Prepare test summary report if: always() shell: bash @@ -213,7 +200,6 @@ jobs: result_label="❌ Failed" fi - # Extract Jest-style test counts written by Detox to stdout test_detail="" if [ -f /tmp/detox-bs.log ]; then suites_line="$(grep -E "^Test Suites:" /tmp/detox-bs.log | tail -1 || true)" @@ -226,11 +212,11 @@ jobs: fi { - echo "## BrowserStack Android Detox Summary" + echo "## BrowserStack Android Detox Summary — Core" echo echo "- Build ID: \`${build_id}\`" echo "- Overall status: ${overall_status}" echo "- Result: ${result_label}" - echo "- BrowserStack dashboard: https://app-automate.browserstack.com/dashboard/v2" + echo "- [BrowserStack dashboard](https://app-automate.browserstack.com/dashboard/v2/builds/${build_id})" echo -e "$test_detail" } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/browserstack-e2e-android-journey.yml b/.github/workflows/browserstack-e2e-android-journey.yml new file mode 100644 index 000000000..b07ac5bcf --- /dev/null +++ b/.github/workflows/browserstack-e2e-android-journey.yml @@ -0,0 +1,222 @@ +# +# Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +# +# This software may be modified and distributed under the terms +# of the MIT license. See the LICENSE file for details. +# +name: BrowserStack E2E — Android Journey + +on: + workflow_call: + inputs: + browserstack_project_name: + description: BrowserStack project name used for Android E2E runs + required: false + type: string + default: rn-sdk-test + browserstack_session_name: + description: BrowserStack session name used by Detox + required: false + type: string + default: android-detox-e2e-journey + secrets: + BROWSERSTACK_USERNAME: + description: BrowserStack account username + required: true + BROWSERSTACK_ACCESS_KEY: + description: BrowserStack account access key + required: true + PING_SERVER_URL: + description: Ping server base URL for Tier 2 E2E coverage + required: false + PING_REALM_PATH: + description: Ping realm path for journey and OIDC E2E coverage + required: false + PING_COOKIE_NAME: + description: Ping cookie name used by the test environment + required: false + PING_JOURNEY_NAME: + description: Ping journey name exercised by BrowserStack E2E tests + required: false + PING_TEST_USERNAME: + description: Test account username for server-backed BrowserStack scenarios + required: false + PING_TEST_PASSWORD: + description: Test account password for server-backed BrowserStack scenarios + required: false + PING_DISCOVERY_ENDPOINT: + description: OIDC discovery endpoint used by BrowserStack E2E tests + required: false + PING_CLIENT_ID: + description: OIDC client identifier used by BrowserStack E2E tests + required: false + PING_REDIRECT_URI: + description: OIDC redirect URI used by BrowserStack E2E tests + required: false + PING_CALLBACK_TREES_ENABLED: + description: Flag enabling callback tree coverage in BrowserStack E2E tests + required: false + +permissions: + contents: read + +jobs: + e2e-android-journey: + name: E2E — Android BrowserStack (journey) + runs-on: ubuntu-latest + timeout-minutes: 45 + defaults: + run: + working-directory: PingTestRunner + shell: bash + + env: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + PING_SERVER_URL: ${{ secrets.PING_SERVER_URL }} + PING_REALM_PATH: ${{ secrets.PING_REALM_PATH }} + PING_COOKIE_NAME: ${{ secrets.PING_COOKIE_NAME }} + PING_JOURNEY_NAME: ${{ secrets.PING_JOURNEY_NAME }} + PING_TEST_USERNAME: ${{ secrets.PING_TEST_USERNAME }} + PING_TEST_PASSWORD: ${{ secrets.PING_TEST_PASSWORD }} + PING_DISCOVERY_ENDPOINT: ${{ secrets.PING_DISCOVERY_ENDPOINT }} + PING_CLIENT_ID: ${{ secrets.PING_CLIENT_ID }} + PING_REDIRECT_URI: ${{ secrets.PING_REDIRECT_URI }} + PING_CALLBACK_TREES_ENABLED: ${{ secrets.PING_CALLBACK_TREES_ENABLED }} + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Node.js and install dependencies + uses: ./.github/actions/setup-proj + + - name: Install global Jest and Detox CLI + run: | + npm install -g jest + npm install -g detox-cli + + - name: Download app APK artifact + uses: actions/download-artifact@v4 + with: + name: pingtestrunner-android-bs-app + path: PingTestRunner/android/app/build/outputs/apk/release + + - name: Download test APK artifact + uses: actions/download-artifact@v4 + with: + name: pingtestrunner-android-bs-test-suite + path: PingTestRunner/android/app/build/outputs/apk/androidTest/release + + - name: Compute BrowserStack identifiers + # Naming convention: + # Project : rn--test → rn-sdk-test + # Build : ----journey + # Session : --journey + run: | + set -euo pipefail + SHORT_SHA="${GITHUB_SHA:0:7}" + case "${{ github.event_name }}" in + pull_request) + BUILD_PREFIX="pr" + ;; + push) + BUILD_PREFIX="push" + ;; + *) + BUILD_PREFIX="run" + ;; + esac + echo "BROWSERSTACK_BUILD_ID=${BUILD_PREFIX}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${SHORT_SHA}-journey" >> "$GITHUB_ENV" + echo "BROWSERSTACK_PROJECT_NAME=${{ inputs.browserstack_project_name }}" >> "$GITHUB_ENV" + echo "BROWSERSTACK_SESSION_NAME=${{ inputs.browserstack_session_name }}" >> "$GITHUB_ENV" + + - name: Upload app to BrowserStack + id: upload-app + run: | + response=$(curl --silent --fail-with-body \ + -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ + -X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/app" \ + -F "file=@android/app/build/outputs/apk/release/app-release.apk") + app_url=$(echo "$response" | jq -r '.app_url') + if [[ "$app_url" == "null" || -z "$app_url" ]]; then + echo "::error::Failed to get app_url from BrowserStack response: $response" + exit 1 + fi + echo "app_url=$app_url" >> "$GITHUB_OUTPUT" + + - name: Upload app client to BrowserStack + id: upload-test + run: | + response=$(curl --silent --fail-with-body \ + -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ + -X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/app-client" \ + -F "file=@android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk") + test_url=$(echo "$response" | jq -r '.app_client_url') + if [[ "$test_url" == "null" || -z "$test_url" ]]; then + echo "::error::Failed to get app_client_url from BrowserStack response: $response" + exit 1 + fi + echo "test_url=$test_url" >> "$GITHUB_OUTPUT" + + - name: Run E2E journey tests on BrowserStack real devices + id: run-tests + run: yarn test:runner:bs:android:journey 2>&1 | tee /tmp/detox-bs.log; exit "${PIPESTATUS[0]}" + working-directory: ${{ github.workspace }} + env: + BROWSERSTACK_APP_URL: ${{ steps.upload-app.outputs.app_url }} + BROWSERSTACK_TEST_URL: ${{ steps.upload-test.outputs.test_url }} + + - name: View BrowserStack test results + if: always() && steps.run-tests.outcome != 'skipped' + shell: bash + run: | + set -euo pipefail + + session_id="$(grep -oP '(?<=session_id=)[^[:space:]]+' /tmp/detox-bs.log | head -1 || true)" + + if [ -z "$session_id" ]; then + echo "Session ID not found in Detox logs — visit https://app-automate.browserstack.com/dashboard/v2 to view results." + exit 0 + fi + + echo "Fetching BrowserStack session details for session: $session_id" + curl -s \ + -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ + -X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/sessions/${session_id}" + + - name: Prepare test summary report + if: always() + shell: bash + run: | + set -euo pipefail + + overall_status="${{ steps.run-tests.outcome }}" + build_id="${BROWSERSTACK_BUILD_ID}" + + if [ "$overall_status" = "success" ]; then + result_label="✅ Passed" + else + result_label="❌ Failed" + fi + + test_detail="" + if [ -f /tmp/detox-bs.log ]; then + suites_line="$(grep -E "^Test Suites:" /tmp/detox-bs.log | tail -1 || true)" + tests_line="$(grep -E "^Tests:" /tmp/detox-bs.log | tail -1 || true)" + if [ -n "$suites_line" ] || [ -n "$tests_line" ]; then + test_detail=$'\n### Test Results\n' + [ -n "$suites_line" ] && test_detail+=$'\n'"- ${suites_line}" + [ -n "$tests_line" ] && test_detail+=$'\n'"- ${tests_line}" + fi + fi + + { + echo "## BrowserStack Android Detox Summary — Journey" + echo + echo "- Build ID: \`${build_id}\`" + echo "- Overall status: ${overall_status}" + echo "- Result: ${result_label}" + echo "- [BrowserStack dashboard](https://app-automate.browserstack.com/dashboard/v2/builds/${build_id})" + echo -e "$test_detail" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/browserstack-parse-results-ios.yml b/.github/workflows/browserstack-parse-results-ios.yml index 42abfa6cc..60a5be1b7 100644 --- a/.github/workflows/browserstack-parse-results-ios.yml +++ b/.github/workflows/browserstack-parse-results-ios.yml @@ -113,7 +113,7 @@ jobs: echo "- Build ID: \`${build_id}\`" echo "- Overall status: ${overall_status}" echo "- Result: ${result_label}" - echo "- BrowserStack dashboard: ${build_url}" + echo "- [BrowserStack dashboard](${build_url})" echo echo "### Device Results" echo diff --git a/.github/workflows/browserstack-prep-android-artifacts.yml b/.github/workflows/browserstack-prep-android-artifacts.yml new file mode 100644 index 000000000..deed3620c --- /dev/null +++ b/.github/workflows/browserstack-prep-android-artifacts.yml @@ -0,0 +1,84 @@ +# +# Copyright (c) 2026 Ping Identity Corporation. All rights reserved. +# +# This software may be modified and distributed under the terms +# of the MIT license. See the LICENSE file for details. +# +name: Prepare Android BrowserStack artifacts + +on: + workflow_call: + +permissions: + contents: read + +jobs: + prepare-android-bs-artifacts: + name: Prepare Android BrowserStack artifacts + runs-on: ubuntu-latest + timeout-minutes: 45 + defaults: + run: + working-directory: PingTestRunner + shell: bash + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Node.js and install dependencies + uses: ./.github/actions/setup-proj + + - name: Install global Jest and Detox CLI + run: | + npm install -g jest + npm install -g detox-cli + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Build SDK packages + run: yarn packages:build + working-directory: ${{ github.workspace }} + + - name: Generate codegen artifacts for all SDK modules + # React Native New Architecture requires each library's codegen JNI + # directories to exist before CMake configures the app. Run codegen + # across all included modules so the directories are present. + run: | + cd android && ./gradlew \ + :ping-identity_rn-binding:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-browser:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-device-client:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-device-id:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-device-profile:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-external-idp:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-fido:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-journey:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-logger:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-oath:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-oidc:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-push:generateCodegenArtifactsFromSchema \ + :ping-identity_rn-storage:generateCodegenArtifactsFromSchema \ + :react-native-async-storage_async-storage:generateCodegenArtifactsFromSchema + + - name: Build Release APK and androidTest APK + run: yarn build:runner:bs:android + working-directory: ${{ github.workspace }} + + - name: Upload app APK artifact + uses: actions/upload-artifact@v4 + with: + name: pingtestrunner-android-bs-app + path: PingTestRunner/android/app/build/outputs/apk/release/app-release.apk + if-no-files-found: error + + - name: Upload test APK artifact + uses: actions/upload-artifact@v4 + with: + name: pingtestrunner-android-bs-test-suite + path: PingTestRunner/android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk + if-no-files-found: error diff --git a/.github/workflows/browserstack-prep-ios-artifacts.yml b/.github/workflows/browserstack-prep-ios-artifacts.yml index 03b68ae4d..b37637990 100644 --- a/.github/workflows/browserstack-prep-ios-artifacts.yml +++ b/.github/workflows/browserstack-prep-ios-artifacts.yml @@ -40,7 +40,7 @@ jobs: env: XCODE_VERSION: ${{ vars.XCODE_VERSION || '26.2' }} - RUBY_VERSION: '2.6.10' + RUBY_VERSION: '3.3.6' BUNDLE_GEMFILE: ${{ github.workspace }}/PingTestRunner/Gemfile PING_TEST_RUNNER_DIR: PingTestRunner PING_TEST_RUNNER_IOS_DIR: PingTestRunner/ios diff --git a/.github/workflows/build-and-test-ios.yml b/.github/workflows/build-and-test-ios.yml index f619dd488..d59c03cfa 100644 --- a/.github/workflows/build-and-test-ios.yml +++ b/.github/workflows/build-and-test-ios.yml @@ -17,7 +17,7 @@ env: # DESTINATION is resolved dynamically at runtime by the "Resolve simulator" step. DERIVED_DATA_PATH: DerivedData XCODE_VERSION: '26.4.1' - RUBY_VERSION: '2.6.10' + RUBY_VERSION: '3.3.6' BUNDLE_GEMFILE: ${{ github.workspace }}/PingTestRunner/Gemfile IOS_DIRECTORY: PingTestRunner/ios jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 775bfbc88..1cf9033bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,12 +103,45 @@ jobs: uses: ./.github/workflows/build-and-test-ios.yml secrets: inherit - # Run Android E2E tests on BrowserStack real devices after Android unit tests pass. - browserstack-android: + # Build Android APKs and upload as artifacts for BrowserStack E2E runs. + browserstack-android-prep: needs: android-unit-tests permissions: contents: read - uses: ./.github/workflows/browserstack-e2e-android.yml + uses: ./.github/workflows/browserstack-prep-android-artifacts.yml + + # Run Foundation Modules E2E tests on BrowserStack real devices. + # Intentionally sequenced after core (not parallel): each Android workflow runs 3 matrix + # legs and iOS runs 3 devices concurrently. Running core + journey in parallel would peak + # at 6 Android + 3 iOS = 9 sessions, exceeding the limit of 6. + # Serialising core → journey keeps Android at 3 active sessions at any time, leaving 3 + # slots for iOS running in parallel — exactly at the limit. + browserstack-android-journey: + needs: browserstack-android-core + if: ${{ always() }} + permissions: + contents: read + uses: ./.github/workflows/browserstack-e2e-android-journey.yml + secrets: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + PING_SERVER_URL: ${{ secrets.PING_SERVER_URL }} + PING_REALM_PATH: ${{ secrets.PING_REALM_PATH }} + PING_COOKIE_NAME: ${{ secrets.PING_COOKIE_NAME }} + PING_JOURNEY_NAME: ${{ secrets.PING_JOURNEY_NAME }} + PING_TEST_USERNAME: ${{ secrets.PING_TEST_USERNAME }} + PING_TEST_PASSWORD: ${{ secrets.PING_TEST_PASSWORD }} + PING_DISCOVERY_ENDPOINT: ${{ secrets.PING_DISCOVERY_ENDPOINT }} + PING_CLIENT_ID: ${{ secrets.PING_CLIENT_ID }} + PING_REDIRECT_URI: ${{ secrets.PING_REDIRECT_URI }} + PING_CALLBACK_TREES_ENABLED: ${{ secrets.PING_CALLBACK_TREES_ENABLED }} + + # Run Foundation Modules E2E tests on BrowserStack real devices. + browserstack-android-core: + needs: browserstack-android-prep + permissions: + contents: read + uses: ./.github/workflows/browserstack-e2e-android-core.yml secrets: BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index dbd543687..51136b725 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -38,7 +38,7 @@ jobs: env: XCODE_VERSION: ${{ vars.XCODE_VERSION || '26.4.1' }} - RUBY_VERSION: '2.6.10' + RUBY_VERSION: '3.3.6' BUNDLE_GEMFILE: ${{ github.workspace }}/PingTestRunner/Gemfile steps: diff --git a/.mendsastcli-config.json b/.mendsastcli-config.json index 96c6e9367..ea863691f 100644 --- a/.mendsastcli-config.json +++ b/.mendsastcli-config.json @@ -1,11 +1,12 @@ { - "scans": { - "pathExclusions": [ - "PingSampleApp/**", - "packages/**/src/__tests__/**", - "packages/**/__tests__/**", - "packages/**/android/src/test/**", - "packages/**/ios/Tests/**" - ] - } + "scans": { + "pathExclusions": [ + "PingSampleApp/**", + "PingTestRunner/**", + "packages/**/src/__tests__/**", + "packages/**/__tests__/**", + "packages/**/android/src/test/**", + "packages/**/ios/Tests/**" + ] + } } diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 0acf0dec8..a7f497681 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/PingSampleApp/Gemfile b/PingSampleApp/Gemfile index 0affdbd94..eece267bf 100644 --- a/PingSampleApp/Gemfile +++ b/PingSampleApp/Gemfile @@ -6,7 +6,7 @@ source 'https://rubygems.org' # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version -ruby ">= 2.6.10" +ruby ">= 3.3.6" # Exclude problematic versions of cocoapods and activesupport that causes build failures. gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' diff --git a/PingSampleApp/Gemfile.lock b/PingSampleApp/Gemfile.lock index 59db36e3a..f2b14c97c 100644 --- a/PingSampleApp/Gemfile.lock +++ b/PingSampleApp/Gemfile.lock @@ -111,7 +111,7 @@ DEPENDENCIES xcodeproj (< 1.26.0) RUBY VERSION - ruby 2.6.10p210 + ruby 3.3.6p108 BUNDLED WITH - 2.2.33 + 4.0.13 diff --git a/PingSampleApp/ios/Config.debug.xcconfig b/PingSampleApp/ios/Config.debug.xcconfig index 7702c1195..24177afea 100644 --- a/PingSampleApp/ios/Config.debug.xcconfig +++ b/PingSampleApp/ios/Config.debug.xcconfig @@ -1,9 +1,2 @@ #include "Pods/Target Support Files/Pods-PingSampleApp/Pods-PingSampleApp.debug.xcconfig" - -GOOGLE_IOS_CLIENT_ID=1043559224226-93t50js7chfvihrgtvnb77lac0ojmvbs.apps.googleusercontent.com -GOOGLE_IOS_REVERSED_CLIENT_ID=com.googleusercontent.apps.1043559224226-93t50js7chfvihrgtvnb77lac0ojmvbs -FACEBOOK_APP_ID=your-facebook-app-id -FACEBOOK_CLIENT_TOKEN=your-facebook-client-token -FACEBOOK_LOGIN_PROTOCOL_SCHEME=fbyour-facebook-app-id - #include? "Config.local.xcconfig" diff --git a/PingSampleApp/ios/Config.release.xcconfig b/PingSampleApp/ios/Config.release.xcconfig index 683852f99..5bbb61115 100644 --- a/PingSampleApp/ios/Config.release.xcconfig +++ b/PingSampleApp/ios/Config.release.xcconfig @@ -1,9 +1,2 @@ #include "Pods/Target Support Files/Pods-PingSampleApp/Pods-PingSampleApp.release.xcconfig" - -GOOGLE_IOS_CLIENT_ID=1043559224226-93t50js7chfvihrgtvnb77lac0ojmvbs.apps.googleusercontent.com -GOOGLE_IOS_REVERSED_CLIENT_ID=com.googleusercontent.apps.1043559224226-93t50js7chfvihrgtvnb77lac0ojmvbs -FACEBOOK_APP_ID=your-facebook-app-id -FACEBOOK_CLIENT_TOKEN=your-facebook-client-token -FACEBOOK_LOGIN_PROTOCOL_SCHEME=fbyour-facebook-app-id - #include? "Config.local.xcconfig" diff --git a/PingSampleApp/ios/PingSampleApp.xcodeproj/project.pbxproj b/PingSampleApp/ios/PingSampleApp.xcodeproj/project.pbxproj index 4aa9d9d65..220492db6 100644 --- a/PingSampleApp/ios/PingSampleApp.xcodeproj/project.pbxproj +++ b/PingSampleApp/ios/PingSampleApp.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ 344692DB2F0EC3FC001EF2C5 /* libRNPingStorage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRNPingStorage.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3466ED922F5B937B00E6A758 /* RNPackagesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNPackagesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B4392A12AC88292D35C810B /* Pods-PingSampleApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PingSampleApp.debug.xcconfig"; path = "Target Support Files/Pods-PingSampleApp/Pods-PingSampleApp.debug.xcconfig"; sourceTree = ""; }; + A3F36CA814824280872729DD /* Config.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Config.debug.xcconfig"; path = "Config.debug.xcconfig"; sourceTree = SOURCE_ROOT; }; + 7B2BF181299A412AB9207203 /* Config.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Config.release.xcconfig"; path = "Config.release.xcconfig"; sourceTree = SOURCE_ROOT; }; 533C67601567314E010C1D7B /* Pods-RNPackagesTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNPackagesTests.debug.xcconfig"; path = "Target Support Files/Pods-RNPackagesTests/Pods-RNPackagesTests.debug.xcconfig"; sourceTree = ""; }; 5709B34CF0A7D63546082F79 /* Pods-PingSampleApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PingSampleApp.release.xcconfig"; path = "Target Support Files/Pods-PingSampleApp/Pods-PingSampleApp.release.xcconfig"; sourceTree = ""; }; 58D1E2432F9AA3D900A9C001 /* RNPackagesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNPackagesTests.swift; sourceTree = ""; }; @@ -140,6 +142,8 @@ children = ( 3B4392A12AC88292D35C810B /* Pods-PingSampleApp.debug.xcconfig */, 5709B34CF0A7D63546082F79 /* Pods-PingSampleApp.release.xcconfig */, + A3F36CA814824280872729DD /* Config.debug.xcconfig */, + 7B2BF181299A412AB9207203 /* Config.release.xcconfig */, 8772F6B807DAA5A3C61E05F0 /* Pods-RNStorageTests.debug.xcconfig */, 0B341BD4880DD91608CC85CA /* Pods-RNStorageTests.release.xcconfig */, 533C67601567314E010C1D7B /* Pods-RNPackagesTests.debug.xcconfig */, @@ -354,7 +358,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-PingSampleApp.debug.xcconfig */; + baseConfigurationReference = A3F36CA814824280872729DD /* Config.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -384,7 +388,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-PingSampleApp.release.xcconfig */; + baseConfigurationReference = 7B2BF181299A412AB9207203 /* Config.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/PingSampleApp/ios/PingSampleApp/PingSampleAppDebug.entitlements b/PingSampleApp/ios/PingSampleApp/PingSampleAppDebug.entitlements index 903def2af..80b5221de 100644 --- a/PingSampleApp/ios/PingSampleApp/PingSampleAppDebug.entitlements +++ b/PingSampleApp/ios/PingSampleApp/PingSampleAppDebug.entitlements @@ -4,5 +4,9 @@ aps-environment development + com.apple.developer.applesignin + + Default + diff --git a/PingSampleApp/ios/PingSampleApp/PingSampleAppRelease.entitlements b/PingSampleApp/ios/PingSampleApp/PingSampleAppRelease.entitlements index 903def2af..80b5221de 100644 --- a/PingSampleApp/ios/PingSampleApp/PingSampleAppRelease.entitlements +++ b/PingSampleApp/ios/PingSampleApp/PingSampleAppRelease.entitlements @@ -4,5 +4,9 @@ aps-environment development + com.apple.developer.applesignin + + Default + diff --git a/PingSampleApp/ios/Podfile.lock b/PingSampleApp/ios/Podfile.lock index e86d4a9f6..3aa1a9cde 100644 --- a/PingSampleApp/ios/Podfile.lock +++ b/PingSampleApp/ios/Podfile.lock @@ -2504,6 +2504,7 @@ PODS: - glog - hermes-engine - PingBrowser (= 2.0.0) + - PingLogger (= 2.0.0) - RCT-Folly - RCT-Folly/Fabric - RCTRequired @@ -3590,7 +3591,7 @@ SPEC CHECKSUMS: ReactCommon: 96684b90b235d6ae340d126141edd4563b7a446a RNCAsyncStorage: 767abb068db6ad28b5f59a129fbc9fab18b377e2 RNPingBinding: 10541621edbb8b6885f069ee6316843498d6cc93 - RNPingBrowser: 63741144ed5088a03a765a4d7c42be27f18fdd53 + RNPingBrowser: 885e16bcc7d628ea833becef1ce3ef8ec9320bfa RNPingCore: fe4e99005afb728904b634a2b29bd9f7df0ed7a2 RNPingDeviceClient: b62cdc274eff19c09fce49b7d7b7e1166a81a044 RNPingDeviceId: f37f85e0807a5b0e284631c83e28e0dfd4a78712 @@ -3609,7 +3610,7 @@ SPEC CHECKSUMS: SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 VisionCamera: 3914b29387fc40c64f7022830cfdc604f94eb966 VisionCameraBarcodeScanner: 0b6fe2e3c52ca68a0dbd5d25719c7523c68d807f - Yoga: 703055a9f39562521cdb8657162dfd80f8c174c3 + Yoga: daa1e4de4b971b977b23bc842aaa3e135324f1f3 PODFILE CHECKSUM: 83df8013c84a3febb48d5c5092b148ef9136e104 diff --git a/PingTestRunner/.detoxrc.js b/PingTestRunner/.detoxrc.js index a8ae83e9f..a94ccc317 100644 --- a/PingTestRunner/.detoxrc.js +++ b/PingTestRunner/.detoxrc.js @@ -21,11 +21,12 @@ module.exports = { }, testRunner: { args: { - $0: 'jest', config: 'e2e/jest.config.js', }, + retries: process.env.CI ? 4 : 0, jest: { setupTimeout: 300000, + retries: process.env.CI ? 4 : 0, }, }, apps: { @@ -40,11 +41,11 @@ module.exports = { type: 'android.apk', binaryPath: 'android/app/build/outputs/apk/release/app-release.apk', testBinaryPath: - 'android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk', + 'android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk', build: 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release', }, - // BrowserStack cloud app — URLs are resolved after uploading binaries to BrowserStack. + // BrowserStack cloud app — IDs are resolved after uploading binaries to BrowserStack. 'android.cloud': { type: 'android.cloud', app: process.env.BROWSERSTACK_APP_URL, @@ -61,14 +62,14 @@ module.exports = { emulator: { type: 'android.emulator', device: { - avdName: 'Pixel_9', + avdName: process.env.DETOX_AVD_NAME ?? 'Pixel_10', }, }, // BrowserStack real device — override via env vars to target a different device. browserstack: { type: 'android.cloud', device: { - name: process.env.BROWSERSTACK_DEVICE ?? 'Google Pixel 9', + name: process.env.BROWSERSTACK_DEVICE ?? 'Google Pixel 9 Pro', osVersion: process.env.BROWSERSTACK_OS_VERSION ?? '15.0', }, }, @@ -106,6 +107,9 @@ module.exports = { build: process.env.BROWSERSTACK_BUILD_ID, project: process.env.BROWSERSTACK_PROJECT_NAME, local: false, + // Cold-session launchApp on BrowserStack needs extra time for device-side + // instrumentation to initialise before the first call succeeds. + commandTimeout: 120, }, }, }, diff --git a/PingTestRunner/Gemfile b/PingTestRunner/Gemfile index 4e96de646..5c9c7c13b 100644 --- a/PingTestRunner/Gemfile +++ b/PingTestRunner/Gemfile @@ -5,7 +5,7 @@ source 'https://rubygems.org' -ruby ">= 2.6.10" +ruby ">= 3.3.6" gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' diff --git a/PingTestRunner/Gemfile.lock b/PingTestRunner/Gemfile.lock index 597a79bd5..f8726a644 100644 --- a/PingTestRunner/Gemfile.lock +++ b/PingTestRunner/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.9) + CFPropertyList (3.0.8) activesupport (6.1.7.10) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) @@ -107,7 +107,7 @@ DEPENDENCIES xcodeproj (~> 1.27.0) RUBY VERSION - ruby 2.6.10p210 + ruby 3.3.6p108 BUNDLED WITH - 2.2.33 + 4.0.13 diff --git a/PingTestRunner/android/app/src/main/res/xml/network_security_config.xml b/PingTestRunner/android/app/src/main/res/xml/network_security_config.xml index 3cac0a15d..9e6b11f47 100644 --- a/PingTestRunner/android/app/src/main/res/xml/network_security_config.xml +++ b/PingTestRunner/android/app/src/main/res/xml/network_security_config.xml @@ -12,10 +12,17 @@ See: https://wix.github.io/Detox/docs/introduction/android#6-create-a-network-security-configuration-file --> - - localhost - 10.0.2.2 - 10.0.3.2 - 127.0.0.1 - + + diff --git a/PingTestRunner/android/build.gradle b/PingTestRunner/android/build.gradle index 283459eeb..44ff611a0 100644 --- a/PingTestRunner/android/build.gradle +++ b/PingTestRunner/android/build.gradle @@ -31,6 +31,20 @@ allprojects { configurations.all { resolutionStrategy { force "androidx.test.core:core:1.6.1" + // espresso-core ≤3.6.x calls getDeclaredMethod("getInstance") on + // android.hardware.input.InputManager via reflection inside + // InputManagerEventInjectionStrategy.initialize(). That no-arg static + // method was removed from the Android framework in newer API levels, + // causing a NoSuchMethodException crash at DetoxMain.initEspresso() before + // any test can run — seen on both the local Pixel_10 (API 37) emulator and + // on BrowserStack real devices. espresso-core 3.7.0 replaced the reflection + // call with Context.getSystemService(InputManager.class) and is compatible + // with all API levels we target. The @browserstack/detox package declares + // espresso 3.6.1 transitively, so we force 3.7.0 here to override it. + force "androidx.test.espresso:espresso-core:3.7.0" + force "androidx.test.espresso:espresso-web:3.7.0" + force "androidx.test.espresso:espresso-contrib:3.7.0" + force "androidx.test:runner:1.6.2" } } // Provide a default appRedirectUriScheme for library modules that reference diff --git a/PingTestRunner/ios/Podfile.lock b/PingTestRunner/ios/Podfile.lock index e40c3c0c2..1a4fd7830 100644 --- a/PingTestRunner/ios/Podfile.lock +++ b/PingTestRunner/ios/Podfile.lock @@ -2205,7 +2205,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNPingBinding (0.1.0): + - RNPingBinding (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2236,7 +2236,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingBinding/Tests (0.1.0): + - RNPingBinding/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2267,7 +2267,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingBrowser (0.1.0): + - RNPingBrowser (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2299,7 +2299,7 @@ PODS: - RNPingLogger - SocketRocket - Yoga - - RNPingBrowser/Tests (0.1.0): + - RNPingBrowser/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2331,7 +2331,7 @@ PODS: - RNPingLogger - SocketRocket - Yoga - - RNPingCore (0.1.0): + - RNPingCore (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2360,7 +2360,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNPingCore/Tests (0.1.0): + - RNPingCore/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2389,7 +2389,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNPingDeviceClient (0.1.0): + - RNPingDeviceClient (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2420,7 +2420,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingDeviceClient/Tests (0.1.0): + - RNPingDeviceClient/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2451,7 +2451,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingDeviceId (0.1.0): + - RNPingDeviceId (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2482,7 +2482,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingDeviceId/Tests (0.1.0): + - RNPingDeviceId/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2513,7 +2513,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingDeviceProfile (0.1.0): + - RNPingDeviceProfile (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2544,7 +2544,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingDeviceProfile/Tests (0.1.0): + - RNPingDeviceProfile/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2575,7 +2575,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingExternalIdp (0.1.0): + - RNPingExternalIdp (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2606,7 +2606,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingExternalIdp/Tests (0.1.0): + - RNPingExternalIdp/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2637,7 +2637,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingFido (0.1.0): + - RNPingFido (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2668,7 +2668,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingFido/Tests (0.1.0): + - RNPingFido/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2699,7 +2699,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingJourney (0.1.0): + - RNPingJourney (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2737,7 +2737,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingJourney/Tests (0.1.0): + - RNPingJourney/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2775,7 +2775,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingLogger (0.1.0): + - RNPingLogger (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2806,7 +2806,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingLogger/Tests (0.1.0): + - RNPingLogger/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2837,7 +2837,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingOath (0.1.0): + - RNPingOath (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2868,7 +2868,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingOath/Tests (0.1.0): + - RNPingOath/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2899,7 +2899,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingOidc (0.1.0): + - RNPingOidc (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2934,7 +2934,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingOidc/Tests (0.1.0): + - RNPingOidc/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -2969,7 +2969,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingPush (0.1.0): + - RNPingPush (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -3002,7 +3002,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingPush/Tests (0.1.0): + - RNPingPush/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -3035,7 +3035,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingStorage (0.1.0): + - RNPingStorage (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -3066,7 +3066,7 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingStorage/Tests (0.1.0): + - RNPingStorage/Tests (1.0.0-beta.3): - boost - DoubleConversion - fast_float @@ -3411,11 +3411,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb - fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6 + fast_float: 914e00b8b67cb8ba184a778c7bbc2e9fe98d37b3 FBLazyVector: 09f03e4b6f42f955734b64a118f86509cc719427 - fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd - glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 - hermes-engine: 4f07404533b808de66cf48ac4200463068d0e95a + fmt: b85d977e8fe789fd71c77123f9f4920d88c4d170 + glog: 682871fb30f4a65f657bf357581110656ea90b08 + hermes-engine: c8443ebca6460d15100b5b29ae584a1a6c9dc18b PingBinding: 46036fcbcd4e3cb6a453f08822493ba451d4c8ab PingBrowser: d1526bdcc6197781d0d54fe7ac16ba09a55a1faa PingCommons: 49f2244bda1c41913fa37d9481e969702d658ade @@ -3435,7 +3435,7 @@ SPEC CHECKSUMS: PingPush: 71aed837ffd7bed5a75f9f08a05431c232094957 PingStorage: 7e3bc6966fbf7587523e56a8a7b0f5d85ea3bd94 PingTamperDetector: b5e4b7aa325a11f2de1f3df9bdd8cc3391cddf1c - RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f + RCT-Folly: fb64616e25cfac1c3d10bfd784d68926325ae67f RCTDeprecation: efa5010912100e944a7ac9a93a157e1def1988fe RCTRequired: bbc4cf999ddc4a4b076e076c74dd1d39d0254630 RCTTypeSafety: d877728097547d0a37786cc9130c43ad71739ac3 @@ -3501,22 +3501,22 @@ SPEC CHECKSUMS: ReactCodegen: 1e9f3e8a3f56fa25fbf39ecd37b708a4838d9032 ReactCommon: 96684b90b235d6ae340d126141edd4563b7a446a RNCAsyncStorage: 767abb068db6ad28b5f59a129fbc9fab18b377e2 - RNPingBinding: 64be383c23d1fb0c0899eb650ffb557355111b36 - RNPingBrowser: 68c92341be9aed38dd5a11ecee37b96bf96a6332 - RNPingCore: f41b28de7c94d2e2af7fae663531d0d0341d2775 - RNPingDeviceClient: 6b89f63032ed087e0af22b73312e73a5800cb0ce - RNPingDeviceId: e0d02655613f8e6151496538ce46040f44c414cb - RNPingDeviceProfile: d41184083542e19e85e20ec9938c98f6511931b5 - RNPingExternalIdp: 664978f9d40fdcd80af22fb2da1e2b0ec0059371 - RNPingFido: 22c9c9f44842fb4e42b8c9c193583314ddd8e61f - RNPingJourney: b85d5fef5498aedda4019108230a39a6135935cb - RNPingLogger: 85893024c3d861ac0e01d35f91860e5bdd3cd904 - RNPingOath: f800f54371fa0ee2a849d29fcefdb54eb01eac90 - RNPingOidc: d17f951f9ca06652fd97caaaf7aaced99e4ad9e8 - RNPingPush: 214ca695aa3779c722fb12ef64cd18fba6fd415b - RNPingStorage: e4f9709832992edd2c49e7885d300aca7cacd68e + RNPingBinding: 10541621edbb8b6885f069ee6316843498d6cc93 + RNPingBrowser: 63741144ed5088a03a765a4d7c42be27f18fdd53 + RNPingCore: fe4e99005afb728904b634a2b29bd9f7df0ed7a2 + RNPingDeviceClient: b62cdc274eff19c09fce49b7d7b7e1166a81a044 + RNPingDeviceId: f37f85e0807a5b0e284631c83e28e0dfd4a78712 + RNPingDeviceProfile: 90e4220f1ec456f8dfaeb9a4214214100302292c + RNPingExternalIdp: 7954d281fa3ea15681b5b1c6ce6f8813559e9b48 + RNPingFido: 350763404cdd05b9fe3dbf73eba926e041d4bf63 + RNPingJourney: f3823780322f741fceed6eb6c3c20013d85a44a2 + RNPingLogger: 3bb60c3bc67fe1078a4a4535f98129da4845eebf + RNPingOath: f67ef2fedb4ad9727349be6eac5da988196871eb + RNPingOidc: 889badee38ae3a2a592edb723d2047f3eea603c7 + RNPingPush: 1ac8fd7f91b9489ec088e502bb8a7b96fabacc80 + RNPingStorage: 61024de883275173ac09f95d985f1bb9bef62670 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 703055a9f39562521cdb8657162dfd80f8c174c3 + Yoga: daa1e4de4b971b977b23bc842aaa3e135324f1f3 PODFILE CHECKSUM: aa39790ed4f30ff93815069baba79c18d49c70b3 diff --git a/PingTestRunner/package.json b/PingTestRunner/package.json index add5ffe5c..6c1092d0c 100644 --- a/PingTestRunner/package.json +++ b/PingTestRunner/package.json @@ -19,6 +19,8 @@ "e2e:android:kill-emulators": "sh -c 'command -v adb >/dev/null 2>&1 || exit 0; adb devices | awk \"/^emulator-/{print \\$1}\" | while read -r id; do [ -n \"$id\" ] && adb -s \"$id\" emu kill || true; done'", "test:e2e:android": "sh -c 'export DETOX_SERVER_PORT=${DETOX_SERVER_PORT:-8100}; export NODE_NO_WARNINGS=1; yarn e2e:android:free-detox-port && yarn e2e:android:kill-emulators && detox test --configuration android.emu --cleanup \"$@\"' --", "test:bs:android": "NODE_NO_WARNINGS=1 detox test --configuration android.bs --loglevel trace", + "test:bs:android:journey": "NODE_NO_WARNINGS=1 detox test --configuration android.bs --loglevel trace --testPathPattern journey", + "test:bs:android:core": "NODE_NO_WARNINGS=1 detox test --configuration android.bs --loglevel trace --testPathPattern 'e2e/\\(app-launch\\|app-open-close\\|browser\\|device-id\\|device-profile\\|logger\\|oidc\\|storage\\|use-oidc\\)'", "test:e2e:ios": "sh -c 'export DETOX_SERVER_PORT=${DETOX_SERVER_PORT:-8099}; export NODE_NO_WARNINGS=1; yarn e2e:android:free-detox-port && detox test --configuration ios.sim \"$@\"' --", "build:e2e:android": "detox build --configuration android.emu", "build:bs:android": "cd android && ./gradlew :app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release", diff --git a/README.md b/README.md index 3f743232e..5a413d6ee 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ of the MIT license. See the LICENSE file for details. --> [![Ping Identity][ping-image]][repo-url] +[![Build Status][build-image]][build-url] +[![NPM version][npm-image]][npm-url] [![License][license-image]][license-url] [![Yarn][yarn-image]][yarn-url] [![codecov][codecov-image]][codecov-url] @@ -138,3 +140,7 @@ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) fi [yarn-url]: https://yarnpkg.com/ [codecov-image]: https://codecov.io/gh/ForgeRock/ping-react-native-sdk/graph/badge.svg [codecov-url]: https://codecov.io/gh/ForgeRock/ping-react-native-sdk +[npm-image]: https://img.shields.io/npm/v/@ping-identity/rn-journey.svg?style=flat +[npm-url]: https://www.npmjs.com/package/@ping-identity/rn-journey +[build-image]: https://github.com/ForgeRock/ping-react-native-sdk/actions/workflows/build-packages.yml/badge.svg +[build-url]: https://github.com/ForgeRock/ping-react-native-sdk/actions/workflows/build-packages.yml diff --git a/package.json b/package.json index 7c411a94e..4ed74e26f 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "build:runner:bs:ios:ipa": "yarn workspace rn-test-runner build:bs:ios:ipa", "build:runner:bs:ios:test-suite": "yarn workspace rn-test-runner build:bs:ios:test-suite", "test:runner:bs:android": "yarn workspace rn-test-runner test:bs:android", + "test:runner:bs:android:journey": "yarn workspace rn-test-runner test:bs:android:journey", + "test:runner:bs:android:core": "yarn workspace rn-test-runner test:bs:android:core", "test:runner:e2e:ios": "yarn workspace rn-test-runner test:e2e:ios", "test:runner:e2e:android": "yarn workspace rn-test-runner test:e2e:android", "runner:clean-install": "yarn workspace rn-test-runner run clean-install", diff --git a/packages/device-profile/android/src/main/java/com/pingidentity/rndeviceprofile/RNPingDeviceProfileCommon.kt b/packages/device-profile/android/src/main/java/com/pingidentity/rndeviceprofile/RNPingDeviceProfileCommon.kt index 95cc9c2f4..66dac29a0 100644 --- a/packages/device-profile/android/src/main/java/com/pingidentity/rndeviceprofile/RNPingDeviceProfileCommon.kt +++ b/packages/device-profile/android/src/main/java/com/pingidentity/rndeviceprofile/RNPingDeviceProfileCommon.kt @@ -167,6 +167,12 @@ object RNPingDeviceProfileCommon { val result = callback.collect { logger = resolvedLogger ?: Logger.NONE collectors { + // This was adding all default collectors including + // LocationCollector. addAll() alone would only append — clear() first so + // the bridge-supplied list is the complete set, preventing LocationCollector + // from running (and requesting the location permission) when the caller did + // not include "location" in the collectors array. + clear() addAll(metadataCollectors) } } diff --git a/packages/device-profile/ios/Tests/RNPingDeviceProfileCommonTests.swift b/packages/device-profile/ios/Tests/RNPingDeviceProfileCommonTests.swift index 6a70ebe9e..66a09deab 100644 --- a/packages/device-profile/ios/Tests/RNPingDeviceProfileCommonTests.swift +++ b/packages/device-profile/ios/Tests/RNPingDeviceProfileCommonTests.swift @@ -8,6 +8,10 @@ import RNPingCore @testable import RNPingDeviceProfile /// XCTest coverage for the shared iOS device profile bridge logic. +/// +/// - Note: `testCollectDeviceProfileWithAllStableKnownCollectors` was removed because it was +/// consistently flaky on CI simulators — five sequential real collectors (platform, hardware, +/// network, telephony, browser) regularly exceeded the 10s timeout under parallel simulator load. final class RNPingDeviceProfileCommonTests: XCTestCase { override func tearDown() { @@ -179,37 +183,7 @@ final class RNPingDeviceProfileCommonTests: XCTestCase { } ) - // CI elapsed ~6.1s under parallel simulator load; use 10s to match the ceiling - // applied to testCollectDeviceProfileWithAllStableKnownCollectors. - wait(for: [expectation], timeout: 10) - } - - func testCollectDeviceProfileWithAllStableKnownCollectors() { - let expectation = expectation(description: "resolver called") - - // Bluetooth and Location are intentionally excluded because CoreBluetooth can remain in - // a transient state on simulators long enough to make this unit test - // non-deterministic in CI. - RNPingDeviceProfileCommon.collectDeviceProfile( - ["platform", "hardware", "network", "telephony", "browser"], - resolver: { payload in - XCTAssertNotNil(payload["platform"]) - XCTAssertNotNil(payload["hardware"]) - XCTAssertNotNil(payload["network"]) - XCTAssertNotNil(payload["telephony"]) - XCTAssertNotNil(payload["browser"]) - XCTAssertNil(payload["bluetooth"]) - XCTAssertNil(payload["location"]) - XCTAssertEqual(payload.count, 5) - expectation.fulfill() - }, - rejecter: { _, _, _ in - XCTFail("rejecter should not be called") - } - ) - - // Five sequential real collectors can exceed 5s under parallel simulator - // load in CI — matches the reasoning on testCollectDeviceProfileWithMultipleCollectors. + // CI elapsed ~6.1s under parallel simulator load. wait(for: [expectation], timeout: 10) } diff --git a/yarn.lock b/yarn.lock index 849000043..0380a9632 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2315,22 +2315,6 @@ __metadata: languageName: node linkType: hard -"@isaacs/balanced-match@npm:^4.0.1": - version: 4.0.1 - resolution: "@isaacs/balanced-match@npm:4.0.1" - checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 - languageName: node - linkType: hard - -"@isaacs/brace-expansion@npm:^5.0.0": - version: 5.0.0 - resolution: "@isaacs/brace-expansion@npm:5.0.0" - dependencies: - "@isaacs/balanced-match": "npm:^4.0.1" - checksum: 10c0/b4d4812f4be53afc2c5b6c545001ff7a4659af68d4484804e9d514e183d20269bb81def8682c01a22b17c4d6aed14292c8494f7d2ac664e547101c1a905aa977 - languageName: node - linkType: hard - "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -5479,30 +5463,30 @@ __metadata: linkType: hard "brace-expansion@npm:^1.1.7": - version: 1.1.12 - resolution: "brace-expansion@npm:1.1.12" + version: 1.1.15 + resolution: "brace-expansion@npm:1.1.15" dependencies: balanced-match: "npm:^1.0.0" concat-map: "npm:0.0.1" - checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73 + checksum: 10c0/648e273f57cfa9ed67d8a77bdb15b408205465d33da9331808ee3c188d8b55674c9cdbf1f320b65bc562e485e1263360ae62ad355e128e0435891f6430e795d7 languageName: node linkType: hard "brace-expansion@npm:^2.0.1": - version: 2.0.2 - resolution: "brace-expansion@npm:2.0.2" + version: 2.1.1 + resolution: "brace-expansion@npm:2.1.1" dependencies: balanced-match: "npm:^1.0.0" - checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf + checksum: 10c0/63b5ddce608b70b50a76817c0526faf8ea67a9180073d88bb402f6bbc22a22da6b1dfac4f65efc53e5faa80222fb7d44bbf2fc638c3f55365975573f671d0ccb languageName: node linkType: hard -"brace-expansion@npm:^5.0.2": - version: 5.0.2 - resolution: "brace-expansion@npm:5.0.2" +"brace-expansion@npm:^5.0.2, brace-expansion@npm:^5.0.5": + version: 5.0.6 + resolution: "brace-expansion@npm:5.0.6" dependencies: balanced-match: "npm:^4.0.2" - checksum: 10c0/60c765e5df6fc0ceca3d5703202ae6779db61f28ea3bf93a04dbf0d50c22ef8e4644e09d0459c827077cd2d09ba8f199a04d92c36419fcf874601a5565013174 + checksum: 10c0/8c919869b90f61d533b341d3340be5ee4413232ea89b8246cbc2f38eb014f1d8182785c98a006eaf6111d02dc9eeffefdc240d5ac158625b2ed084dccd4bbf9b languageName: node linkType: hard @@ -7911,18 +7895,18 @@ __metadata: linkType: hard "glob@npm:^11.0.3": - version: 11.0.3 - resolution: "glob@npm:11.0.3" + version: 11.1.0 + resolution: "glob@npm:11.1.0" dependencies: foreground-child: "npm:^3.3.1" jackspeak: "npm:^4.1.1" - minimatch: "npm:^10.0.3" + minimatch: "npm:^10.1.1" minipass: "npm:^7.1.2" package-json-from-dist: "npm:^1.0.0" path-scurry: "npm:^2.0.0" bin: glob: dist/esm/bin.mjs - checksum: 10c0/7d24457549ec2903920dfa3d8e76850e7c02aa709122f0164b240c712f5455c0b457e6f2a1eee39344c6148e39895be8094ae8cfef7ccc3296ed30bce250c661 + checksum: 10c0/1ceae07f23e316a6fa74581d9a74be6e8c2e590d2f7205034dd5c0435c53f5f7b712c2be00c3b65bf0a49294a1c6f4b98cd84c7637e29453b5aa13b79f1763a2 languageName: node linkType: hard @@ -8386,9 +8370,9 @@ __metadata: linkType: hard "ip-address@npm:^10.0.1": - version: 10.1.0 - resolution: "ip-address@npm:10.1.0" - checksum: 10c0/0103516cfa93f6433b3bd7333fa876eb21263912329bfa47010af5e16934eeeff86f3d2ae700a3744a137839ddfad62b900c7a445607884a49b5d1e32a3d7566 + version: 10.2.0 + resolution: "ip-address@npm:10.2.0" + checksum: 10c0/5a00aada6e922c9c69dfc800ed5d0fa3348675ebdeed0e1575f503f27ca385b5f534363c9af7ad1daf64c1f1409388cdd3cc2e9b9b0fe1c924a431378d55075a languageName: node linkType: hard @@ -9884,19 +9868,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^3.13.1": - version: 3.14.1 - resolution: "js-yaml@npm:3.14.1" - dependencies: - argparse: "npm:^1.0.7" - esprima: "npm:^4.0.0" - bin: - js-yaml: bin/js-yaml.js - checksum: 10c0/6746baaaeac312c4db8e75fa22331d9a04cccb7792d126ed8ce6a0bbcfef0cedaddd0c5098fade53db067c09fe00aa1c957674b4765610a8b06a5a189e46433b - languageName: node - linkType: hard - -"js-yaml@npm:^3.6.1": +"js-yaml@npm:^3.13.1, js-yaml@npm:^3.6.1": version: 3.14.2 resolution: "js-yaml@npm:3.14.2" dependencies: @@ -9909,13 +9881,13 @@ __metadata: linkType: hard "js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1": - version: 4.1.1 - resolution: "js-yaml@npm:4.1.1" + version: 4.2.0 + resolution: "js-yaml@npm:4.2.0" dependencies: argparse: "npm:^2.0.1" bin: js-yaml: bin/js-yaml.js - checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 + checksum: 10c0/1916456c118746603b067d74bbcbb0445d9a1d5e474ad4ae775e7b20525bed902e01d9d97dd0c81fcd8d4f596162309d0eb057f4aa38f3e9647f14075e9dea45 languageName: node linkType: hard @@ -10793,12 +10765,12 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^10.0.3": - version: 10.1.1 - resolution: "minimatch@npm:10.1.1" +"minimatch@npm:^10.1.1": + version: 10.2.5 + resolution: "minimatch@npm:10.2.5" dependencies: - "@isaacs/brace-expansion": "npm:^5.0.0" - checksum: 10c0/c85d44821c71973d636091fddbfbffe62370f5ee3caf0241c5b60c18cd289e916200acb2361b7e987558cd06896d153e25d505db9fc1e43e6b4b6752e2702902 + brace-expansion: "npm:^5.0.5" + checksum: 10c0/6bb058bd6324104b9ec2f763476a35386d05079c1f5fe4fbf1f324a25237cd4534d6813ecd71f48208f4e635c1221899bef94c3c89f7df55698fe373aaae20fd languageName: node linkType: hard @@ -12812,9 +12784,9 @@ __metadata: linkType: hard "shell-quote@npm:^1.6.1, shell-quote@npm:^1.7.2, shell-quote@npm:^1.8.3": - version: 1.8.3 - resolution: "shell-quote@npm:1.8.3" - checksum: 10c0/bee87c34e1e986cfb4c30846b8e6327d18874f10b535699866f368ade11ea4ee45433d97bf5eada22c4320c27df79c3a6a7eb1bf3ecfc47f2c997d9e5e2672fd + version: 1.8.4 + resolution: "shell-quote@npm:1.8.4" + checksum: 10c0/86c93678bc394cb81f5ddcdc87df9c95d279ef9652775cd1cd1eed361404169a8d8cbaacaeed232ab09919e36ee1e5363863570390d78571f8c22b7f6312fb40 languageName: node linkType: hard @@ -13338,15 +13310,15 @@ __metadata: linkType: hard "tar@npm:^7.5.2": - version: 7.5.2 - resolution: "tar@npm:7.5.2" + version: 7.5.16 + resolution: "tar@npm:7.5.16" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10c0/a7d8b801139b52f93a7e34830db0de54c5aa45487c7cb551f6f3d44a112c67f1cb8ffdae856b05fd4f17b1749911f1c26f1e3a23bbe0279e17fd96077f13f467 + checksum: 10c0/4f37f3c4bd2ca2755fd736a5df1d573c1a868ec1b1e893346aeafa95ac510f9e2fd1469420bd866cc7904799e5bd4ac62b5d4f03fe27747d6e1e373b44505c5c languageName: node linkType: hard