Skip to content

ci(react-native): add lint, build and UI testing with BrowserStack - #151

Merged
Teodor Ciuraru (teodorciuraru) merged 131 commits into
mainfrom
teodor/add-rn-apps-tests-pipeline
Sep 30, 2025
Merged

Teodor Ciuraru (teodorciuraru) merged 131 commits into
mainfrom
teodor/add-rn-apps-tests-pipeline

Conversation

@teodorciuraru

@teodorciuraru Teodor Ciuraru (teodorciuraru) commented Sep 1, 2025 •

Copy link
Copy Markdown
Contributor

Summary

Add React Native E2E testing infrastructure using Maestro and BrowserStack App Automate for automated real device testing on Android and iOS.

Problem Solved

  • No automated testing of React Native apps on real devices
  • Manual verification was time-consuming and error-prone
  • Need validation that Ditto sync works correctly across iOS/Android platforms

What This Adds

E2E Testing Infrastructure

  • Real device testing on Samsung Galaxy S22, Pixel 7, iPhone 14/15 via BrowserStack
  • Automated test execution integrated into GitHub Actions CI pipeline
  • Task visibility verification ensuring Ditto sync works correctly across platforms
  • Maestro test files for both React Native CLI and Expo apps on Android and iOS

BrowserStack Android Workaround

  • Environment variable limitation: BrowserStack confirmed Maestro Android API doesn't support environment variables
  • Timeline: Feature on their roadmap, estimated Oct 2025
  • Current solution: Using sed to replace test variables during CI execution
  • Future plan: Will revert to setEnvVariables when officially supported

React Native App Improvements

  • DittoConfig Migration: Updated both React Native apps from legacy IdentityOnlinePlayground to modern DittoConfig syntax
  • WebSocket URL Configuration: Added transportConfig.connect.websocketURLs for proper transport setup
  • Production Build Support: Enhanced CI workflows to generate proper APK/IPA files for testing

Validation

Automated

  • Tests run on every PR via GitHub Actions
  • BrowserStack results posted as PR comments
  • CI fails if any device tests fail

Manual

# Local testing
maestro test .maestro/flows/01-app-launch-and-seeded-tasks-android.yaml

# Custom task testing  
MAESTRO_TASK_TO_FIND="My Task" maestro test .maestro/flows/01-app-launch-and-seeded-tasks-android.yaml

Copilot AI review requested due to automatic review settings September 1, 2025 11:13

This comment was marked as outdated.

@teodorciuraru
Teodor Ciuraru (teodorciuraru) marked this pull request as draft September 1, 2025 11:16
@teodorciuraru Teodor Ciuraru (teodorciuraru) changed the title feat: add comprehensive Maestro E2E testing with BrowserStack integration ci(react-native): add E2E testing with BrowserStack integration Sep 1, 2025
@teodorciuraru Teodor Ciuraru (teodorciuraru) changed the title ci(react-native): add E2E testing with BrowserStack integration ci(react-native): add E2E testing and BrowserStack integration Sep 1, 2025
@teodorciuraru Teodor Ciuraru (teodorciuraru) added the enhancement New feature or request label Sep 1, 2025

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive end-to-end testing infrastructure for React Native apps using Maestro and BrowserStack integration, providing real device testing across Android, iOS, and macOS platforms.

  • Adds 9 comprehensive Maestro test flows covering complete user journeys for task management functionality
  • Integrates BrowserStack App Automate for real device testing with automated Python execution script
  • Implements GitHub Actions workflows for Android, iOS, and macOS testing with proper environment handling

Reviewed Changes

Copilot reviewed 39 out of 40 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
scripts/test-maestro-locally.sh Interactive local test runner for cross-platform Maestro testing
react-native/.maestro/flows/*.yaml Core test flows for bare React Native app testing
react-native-expo/.maestro/flows/*.yaml Test flows adapted for Expo React Native app
.github/workflows/*-maestro-browserstack.yml CI workflows for automated testing on real devices
.github/scripts/browserstack-maestro-test.py Python automation script for BrowserStack integration
react-native-expo/components/*.tsx Added accessibility test IDs for Maestro automation

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread scripts/test-maestro-locally.sh Outdated
Comment thread react-native-expo/.maestro/flows/09-full-workflow.yaml Outdated
Comment thread .github/scripts/browserstack-maestro-test.py Outdated
Comment thread react-native-expo/android/gradle.properties Outdated
Teodor Ciuraru and others added 19 commits September 9, 2025 16:47
…tion

Add complete end-to-end testing infrastructure for React Native apps using Maestro
and BrowserStack App Automate for real device testing across Android, iOS, and macOS.

## 🧪 Maestro Test Framework
- **9 comprehensive test flows** covering complete user journeys
- **3 failure test scenarios** for validation and debugging
- **Cross-platform support** for Android, iOS, and macOS
- **Proper accessibility IDs** added to React Native components

## ☁️ BrowserStack Integration
- **Real device testing** on Android (Pixel 7, Galaxy S23, OnePlus 11)
- **iOS device support** for iPhone 14, iPhone 15, iPad Pro
- **Automated test execution** with Python script
- **Environment variable handling** for Ditto credentials

## 🚀 GitHub Actions Workflows
- **Android workflow**: Ubuntu runners with BrowserStack integration
- **iOS workflow**: macOS runners with real device testing
- **macOS workflow**: Native app testing on macOS runners
- **Standardized naming** following existing conventions
- **45-minute timeout** and proper caching strategies

## 🛠️ Local Development Support
- **Interactive test runner** script with platform selection
- **Tag-based test execution** (smoke, core, integration)
- **Cross-platform support** (Android emulator, iOS simulator, macOS)
- **Comprehensive error handling** and validation

## 📋 Test Coverage
- App launch and smoke tests
- Task CRUD operations (create, read, update, delete)
- Sync toggle functionality
- Data persistence across app restarts
- External sync verification via Ditto Cloud API
- Full end-to-end user workflows

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix command injection vulnerability in env variable parsing
- Remove hardcoded local path from Python script
- Fix shell variable scoping in test runner using process substitution
- Fix head command syntax causing iOS build failure (head -n-1 -> head -n -1)
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
The head command on macOS/BSD does not support negative line counts.
Replace with sed '$d' which removes the last line and works on both BSD and GNU systems.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Change build commands from 'yarn build:android' to direct Gradle assembleDebug
- BrowserStack App Automate requires APK files, not AAB files
- Use gradle assembleDebug task which generates APK files in android/app/build/outputs/apk/debug/

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix API base URL: api.browserstack.com -> api-cloud.browserstack.com
- Update endpoints to use v2 API:
  - /maestro/upload-suite -> /maestro/v2/test-suite
  - /maestro/build -> /maestro/v2/android/build
  - /maestro/builds/{id} -> /maestro/v2/builds/{id}
- Resolves 404 errors when uploading Maestro test suites

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Try multiple possible response field names (test_url, suite_url, test_suite_url)
- Add debug logging to show full API response
- Resolves issue where suite_url was None causing 422 'Invalid testing suite' error

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…n API

- Change 'suite' to 'testSuite' in execution payload
- Matches BrowserStack Maestro v2 API specification
- Should resolve 422 'Invalid testing suite' error

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Change from objects to simple strings as required by v2 API
- Use format 'Device Name-OS Version' instead of {device: ..., osVersion: ...}
- Should resolve 422 'Invalid value passed for devices parameter' error

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix device names to match BrowserStack catalog exactly:
  - 'Google Pixel 7' → 'Pixel 7'
  - 'Samsung Galaxy S23' → 'Galaxy S23'
  - 'OnePlus 11' → 'OnePlus 11R' (11R is available, 11 is not)
- Should resolve 422 'BROWSERSTACK_INVALID_DEVICE' error

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Change from dash format 'Pixel 7-13.0' to comma format 'Pixel 7, 13.0'
- Matches BrowserStack API specification exactly
- Final fix for device specification format

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Change device names from 'Pixel 7, 13.0' to 'Google Pixel 7-13.0'
- Update all device specifications to match BrowserStack API requirements
- Addresses 422 error: BROWSERSTACK_NO_DEVICE_SPECIFIED

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add fallback parsing for build_id and id fields
- Include debug output to show full response structure
- Handles cases where BrowserStack returns different field names

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Search multiple build paths for .app files (Debug-iphonesimulator, build dirs)
- Add comprehensive debugging output for failed app detection
- Look for Build, DerivedData, and Products directories
- Addresses app verification failures in iOS and macOS workflows

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
iOS/macOS App Generation:
- Replace react-native build commands with proper xcodebuild
- Use xcodebuild with derivedDataPath to generate actual .app files
- Update verification to search in correct xcodebuild output locations

BrowserStack Test Reliability:
- Add simplified basic launch test for BrowserStack compatibility
- Reduce device count to single device for initial testing
- Improve permission handling in test flows

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…rrors

Remove specific iPhone 14 destination which isn't available on all runners.
Use generic iOS Simulator platform destination which works universally.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Use 'DittoReactNativeSampleApp-macOS' instead of 'DittoReactNativeSampleApp'.
The actual scheme includes the -macOS suffix as found in xcschemes directory.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Use 'id=dvtdevice-DVTiOSDeviceSimulatorPlaceholder-iphonesimulator:placeholder'
instead of generic platform destination. This matches the available destination
listed by xcodebuild -list output.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace placeholder ID with actual iPhone 16 simulator ID (75860960-263C-416D-B8B8-60C5169ACF52)
from the available destinations list. The placeholder ID was listed as available but not working.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Teodor Ciuraru and others added 11 commits September 23, 2025 18:30
- Add outputs section to seed-ditto-cloud job in both CI workflows
- Add step ID to document seeding step for proper output reference
- Ensures MAESTRO_TASK_TO_FIND gets the actual task title instead of empty string
- Fixes BrowserStack tests receiving empty task title parameter

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace unique timestamped task IDs with fixed "Basic Test Task"
- Both react-native-ci and react-native-expo-ci now create identical task
- Add scrolling functionality to all Maestro test flows
- Ensures consistent test target across all React Native workflows
- Simplifies BrowserStack testing with predictable task name

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ests

- Add testID props to task components in both React Native and Expo apps
- Update all Maestro flows to use reliable testID selectors instead of text
- Replace fixed scroll loops with scrollUntilVisible for efficient element finding
- Standardize test flows across iOS and Android platforms
- Fix CI workflow task naming for consistency

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Revert CI workflows to use timestamp-based unique task IDs
- This ensures Maestro tests look for the dynamically created task
- Fixes mismatch where tests looked for hardcoded task that doesn't exist

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add sed command to replace ${MAESTRO_TASK_TO_FIND} with actual task name in Android test files
- Remove ineffective env sections from CI jobs (only setEnvVariables in API calls work)
- Keep iOS tests using setEnvVariables (works correctly for iOS)
- Add TODO comments documenting BrowserStack Android API bug

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Keep only the testID on Text element (used by Maestro tests)
- Remove unused testID from View container and TaskDone component
- Clean up TaskDone props to remove unused testID parameter
@teodorciuraru

Copy link
Copy Markdown
Contributor Author

Vincent Ahrend (@pvditto), there are some new developments here:

  • BS was false positively telling us RN tests were working because we were providing an empty "" seeded task title and somehow that worked. To fix this, we now send correct env vars.
  • BS staff told me env vars don't work for Maestro and this feature is expected for October. We kept env vars for BS RN iOS because they actually work, but for Android we hardcode the seeded task title in the CI scripts as a workaround (for now).
  • While debugging the problems, I added scrolling to the e2e tests, thinking the test didn't work because something was blocking it or it was not loaded yet in the list. Although that was a debugging idea, I propose we keep it as it makes tests more robust; consider scenarios where we run multiple CI's and all seed tasks at the same time, occupying the screen space. We should either add scrolling to all other apps or adopt a more effective testing strategy overall (for future reference).

PR should be ready to go.

@teodorciuraru Teodor Ciuraru (teodorciuraru) changed the title ci: add React Native E2E testing with BrowserStack Maestro integration ci(react-native): add lint, build and UI testing with BrowserStack Sep 25, 2025
- Add output declarations for build_id in test jobs
- Add report-results job that posts BrowserStack dashboard links to PR
- Provides direct links to Android and iOS test dashboards
@github-actions

Copy link
Copy Markdown
Contributor

📱 React Native Expo - BrowserStack Maestro Test Results

Android Tests

✅ View Android Dashboard
Build ID: 1484247bef5f6fbfe3a7ad48289142dc4cbdb5b7

iOS Tests

❌ iOS tests did not produce a valid build ID


🔗 Workflow Run

- Add status output to test jobs (success/failure)
- Display pass/fail status in PR comments with ✅/❌ icons
- Add overall test result summary
- Show clear status for each platform (Android/iOS)
@github-actions

Copy link
Copy Markdown
Contributor

📱 React Native - BrowserStack Maestro Test Results

Android Tests

✅ View Android Dashboard
Build ID: 615c3d3d1fdd962a8ee4536885855e5201b0cd14

iOS Tests

✅ View iOS Dashboard
Build ID: de11a5e58ff334a6eb60ae2e2f241333e1218e8c


🔗 Workflow Run

- Add unique HTML comment identifiers to find previous reports
- Update existing comment instead of creating new ones
- Each workflow updates its own dedicated comment
- Reduces PR noise while keeping latest results visible
@github-actions

Copy link
Copy Markdown
Contributor

📱 React Native - BrowserStack Maestro Test Results

Android Tests

❌ Status: Failed (No build created)

iOS Tests

❌ Status: Failed (No build created)


⚠️ Overall Result: Some tests failed
🔗 Workflow Run

@github-actions

Copy link
Copy Markdown
Contributor

📱 React Native Expo - BrowserStack Maestro Test Results

Android Tests

❌ Status: Failed (No build created)

iOS Tests

❌ Status: Failed (No build created)


⚠️ Overall Result: Some tests failed
🔗 Workflow Run

- Replace PR comments with GitHub Step Summary for cleaner PR experience
- Results now appear in Actions tab workflow summary
- Zero PR comment spam while maintaining full visibility
- BrowserStack links and status still easily accessible from workflow run
@github-actions

Copy link
Copy Markdown
Contributor

📱 React Native Expo - BrowserStack Maestro Test Results

Android Tests

❌ Status: Failed (No build created)

iOS Tests

❌ Status: Failed (No build created)


⚠️ Overall Result: Some tests failed
🔗 Workflow Run

@github-actions

Copy link
Copy Markdown
Contributor

📱 React Native - BrowserStack Maestro Test Results

Android Tests

❌ Status: Failed (No build created)

iOS Tests

❌ Status: Failed (No build created)


⚠️ Overall Result: Some tests failed
🔗 Workflow Run

@pvditto Vincent Ahrend (pvditto) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

I think the iOS test doesn't click "Allow" often enough. In the recording I can still see one open dialog at the end of the test. See https://app-automate.browserstack.com/dashboard/v2/builds/6d42de9f7a64c0790e8a6d608974e67e8f72a2e3/sessions/tests/de2b78c744f040d96d1b00f30a25b90553e55c4a525880fe

I prefer the check annotation over a comment as well but now that these integration tests use a mix of both it gets confusing.

Comment thread react-native-expo/App.tsx Outdated
Teodor Ciuraru and others added 2 commits September 30, 2025 11:00
Removes accidental debug configuration that disabled DQL strict mode in
the React Native Expo app. This restores symmetry with the React Native
CLI app and ensures proper production configuration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Added conditional handling for the Bluetooth permission dialog that appears
when the Ditto SDK initializes on iOS. The test now taps "OK" when the
Bluetooth permission alert is visible, preventing test failures.

Applied to both React Native CLI and React Native Expo iOS test flows.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@teodorciuraru

Copy link
Copy Markdown
Contributor Author

I think the iOS test doesn't click "Allow" often enough. In the recording I can still see one open dialog at the end of the test.

Solved.

@teodorciuraru
Teodor Ciuraru (teodorciuraru) merged commit 26dbd16 into main Sep 30, 2025
18 checks passed
@teodorciuraru
Teodor Ciuraru (teodorciuraru) deleted the teodor/add-rn-apps-tests-pipeline branch September 30, 2025 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants