Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7b99a2a
feat: add shared jest tests for demo apps
narefyev91 May 6, 2026
7111313
feat: use correct version for yarn
narefyev91 May 6, 2026
d12501f
feat: add change set
alpharius-ck May 7, 2026
ca838d6
Potential fix for pull request finding
alpharius-ck May 8, 2026
643b12c
feat: add required globals
alpharius-ck May 8, 2026
0bd212f
feat: fixes for local build
alpharius-ck May 12, 2026
fde9f2b
Merge branch 'main' into fixes-for-local-build
alpharius-ck May 13, 2026
3f0dd65
feat: add ios e2e tests
alpharius-ck May 22, 2026
5eae0f4
chore: merge main to current branch
alpharius-ck May 22, 2026
4c74e91
feat: add ios e2e tests
alpharius-ck May 26, 2026
e0c6bca
feat: update CI failed
alpharius-ck May 26, 2026
e126da0
feat: add document
alpharius-ck May 26, 2026
b0e8189
Merge branch 'main' into e2e-tests-for-rn-ios
alpharius-ck May 26, 2026
5e70e03
feat: add concurent ci runs
alpharius-ck May 26, 2026
370fee2
chore: add dispatch
alpharius-ck May 26, 2026
7f4890a
chore: retrigger CI
alpharius-ck May 27, 2026
fce7ce1
feat: fix actions for detox
alpharius-ck May 27, 2026
1b8180a
feat: fix actions for RNApp
alpharius-ck May 27, 2026
ac32c18
feat: add correct sim for ci
alpharius-ck May 28, 2026
ba5a4c7
feat: update ci
alpharius-ck May 28, 2026
6079aa0
feat: update ci
alpharius-ck May 28, 2026
26ff08e
feat: update RNApp on CI
alpharius-ck May 29, 2026
9d94277
feat: update Expo55 on CI
alpharius-ck May 29, 2026
a53ac67
feat: add local bash commands
alpharius-ck Jun 1, 2026
ef6d578
feat: add fix for Expo55
alpharius-ck Jun 1, 2026
9ed1725
feat: add fix for Expo55 ci
alpharius-ck Jun 1, 2026
71c8e55
feat: add fix for Expo54 ci
alpharius-ck Jun 2, 2026
07e3525
Merge branch 'main' into e2e-tests-for-rn-ios
alpharius-ck Jun 2, 2026
4eeb404
feat: add fix for Vanilla android
alpharius-ck Jun 3, 2026
db3eb33
feat: add change set
alpharius-ck Jun 3, 2026
f41059b
feat: add changes based on first review
alpharius-ck Jun 8, 2026
994dae2
feat: add more changes
alpharius-ck Jun 8, 2026
699a96d
feat: add E2E tests for AppleApp
alpharius-ck Jun 8, 2026
9b98257
feat: add missing local run
alpharius-ck Jun 8, 2026
0097306
feat: fix e2e test
alpharius-ck Jun 9, 2026
2e6c284
feat: add e2e for expo
alpharius-ck Jun 9, 2026
8cf2499
Merge branch 'main' into e2e-tests-for-rn-ios
alpharius-ck Jun 9, 2026
22022a5
feat: fix e2e for expo
alpharius-ck Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changeset/three-impalas-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

Add Detox E2E coverage for AppleApp brownfield iOS workflow.
118 changes: 115 additions & 3 deletions .github/actions/appleapp-road-test/action.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
name: Apple road test (selected RN app & AppleApp)
description: Package the given RN app as XCFramework, and build the corresponding AppleApp variant
description: Package the given RN app as XCFramework, build the corresponding AppleApp variant, and optionally run Detox E2E

inputs:
variant:
description: 'AppleApp yarn command variant to run (expo or vanilla)'
description: 'AppleApp yarn command variant to run (vanilla, expo54, or expo55)'
required: true

rn-project-path:
description: 'Path to the RN project to build'
required: true

run-e2e:
description: 'Run Detox E2E after packaging (uses Debug build instead of Release road-test build)'
required: false
default: 'false'

e2e-artifact-name:
description: 'Name prefix for Detox artifacts uploaded on failure'
required: false
default: 'detox-appleapp'

runs:
using: composite
steps:
Expand All @@ -34,6 +44,13 @@ runs:
run: brew install ccache
shell: bash

- name: Install applesimutils
if: inputs.run-e2e == 'true'
run: |
brew tap wix/brew
brew install applesimutils
shell: bash

- name: Enable ccache
run: echo "$(brew --prefix)/opt/ccache/libexec" >> $GITHUB_PATH
shell: bash
Expand All @@ -58,8 +75,23 @@ runs:
restore-keys: |
${{ runner.os }}-rnapp-${{ inputs.variant }}-ios-pods-

- name: Brownfield codegen (RN ${{ inputs.variant }} app)
if: inputs.variant == 'vanilla' && inputs.run-e2e == 'true'
run: yarn codegen
working-directory: ${{ inputs.rn-project-path }}
shell: bash

- name: Install pods (RN ${{ inputs.variant }} app, E2E)
if: inputs.variant == 'vanilla' && inputs.run-e2e == 'true'
env:
RCT_USE_PREBUILT_RNCORE: '0'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Q: Does using prebuilt causes error? I believe if we use prebuilts, we might save some time on CI?

run: |
cd ${{ inputs.rn-project-path }}/ios
pod install
shell: bash

- name: Install pods (RN ${{ inputs.variant }} app)
if: inputs.variant == 'vanilla'
if: inputs.variant == 'vanilla' && inputs.run-e2e != 'true'
run: |
cd ${{ inputs.rn-project-path }}/ios
pod install
Expand All @@ -82,10 +114,90 @@ runs:
# == AppleApp ==

- name: Build Brownfield iOS native app (${{ inputs.variant }})
if: inputs.run-e2e != 'true'
run: |
yarn run build:example:ios-consumer:${{ inputs.variant }}
shell: bash

- name: Resolve AppleApp E2E settings
if: inputs.run-e2e == 'true'
run: |
node <<'NODE'
const { getAppleAppDetoxVariant } = require('./apps/brownfield-example-shared-tests/detox-appleapp-variants.cjs');
const variant = getAppleAppDetoxVariant(process.env.APPLEAPP_VARIANT);
const append = (key, value) => {
const fs = require('node:fs');
fs.appendFileSync(process.env.GITHUB_ENV, `${key}=${value}\n`);
};
append('APPLEAPP_XCFRAMEWORK_APP', variant.xcframeworkApp);
append('APPLEAPP_E2E_CONFIGURATION', variant.configuration);
append('APPLEAPP_E2E_BUILD_SCRIPT', variant.e2eBuildScript);
append('APPLEAPP_E2E_TEST_SCRIPT', variant.e2eTestScript);
NODE
env:
APPLEAPP_VARIANT: ${{ inputs.variant }}
shell: bash

- name: Copy XCFrameworks into AppleApp
if: inputs.run-e2e == 'true'
run: node prepareXCFrameworks.js --appName "$APPLEAPP_XCFRAMEWORK_APP"
working-directory: apps/AppleApp
shell: bash

- name: Restore Detox build cache (AppleApp)
if: inputs.run-e2e == 'true'
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
with:
path: apps/AppleApp/build
key: ${{ runner.os }}-e2e-appleapp-${{ inputs.variant }}-build-${{ hashFiles(format('{0}/ios/Podfile.lock', inputs.rn-project-path), 'apps/AppleApp/Brownfield Apple App.xcodeproj/project.pbxproj', 'apps/brownfield-example-shared-tests/e2e/**') }}
restore-keys: |
${{ runner.os }}-e2e-appleapp-${{ inputs.variant }}-build-

- name: Install Detox iOS artifacts
if: inputs.run-e2e == 'true'
run: node node_modules/detox/scripts/postinstall.js
working-directory: apps/AppleApp
shell: bash

- name: Detox build (AppleApp ${{ inputs.variant }})
if: inputs.run-e2e == 'true'
run: yarn "$APPLEAPP_E2E_BUILD_SCRIPT"
working-directory: apps/AppleApp
shell: bash

- name: Verify embedded JS bundle in BrownfieldLib (E2E)
if: inputs.run-e2e == 'true'
run: |
set -euo pipefail
PRODUCTS_DIR="apps/AppleApp/build/Build/Products/${APPLEAPP_E2E_CONFIGURATION}-iphonesimulator"
APP_PATH="$(find "$PRODUCTS_DIR" -maxdepth 1 -name '*.app' -print -quit)"
if [[ -z "$APP_PATH" ]]; then
echo "error: no .app under $PRODUCTS_DIR" >&2
exit 1
fi
BUNDLE_PATH="$APP_PATH/Frameworks/BrownfieldLib.framework/main.jsbundle"
if [[ ! -f "$BUNDLE_PATH" ]]; then
echo "error: $BUNDLE_PATH missing — E2E needs the packaged ${APPLEAPP_XCFRAMEWORK_APP} bundle, not Metro." >&2
echo "Re-run: yarn brownfield:package:ios (${APPLEAPP_XCFRAMEWORK_APP}) && node prepareXCFrameworks.js --appName ${APPLEAPP_XCFRAMEWORK_APP}" >&2
exit 1
fi
echo "Embedded bundle OK: $BUNDLE_PATH ($(wc -c < "$BUNDLE_PATH") bytes)"
shell: bash

- name: Detox test (AppleApp ${{ inputs.variant }})
if: inputs.run-e2e == 'true'
run: yarn "$APPLEAPP_E2E_TEST_SCRIPT"
working-directory: apps/AppleApp
shell: bash

- name: Upload Detox artifacts on failure
if: failure() && inputs.run-e2e == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.e2e-artifact-name }}-${{ inputs.variant }}-ios
path: apps/AppleApp/artifacts
if-no-files-found: ignore

# ==============

- name: Log ccache stats
Expand Down
4 changes: 4 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ runs:
cache: 'yarn'

- name: Install dependencies
env:
# Monorepo has detox in multiple workspaces; parallel postinstalls race on
# $HOME/Library/Detox/ios/framework. E2E jobs run postinstall once later.
DETOX_DISABLE_POSTINSTALL: '1'
run: yarn install
shell: bash

Expand Down
28 changes: 23 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ on:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

concurrency:
group: pr-${{ github.event.pull_request.number }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read
actions: write

jobs:
filter:
name: Detect changed paths
Expand All @@ -35,14 +40,18 @@ jobs:
- 'packages/**'
rnapp:
- 'apps/RNApp/**'
- 'apps/brownfield-example-shared-tests/**'
expo54:
- 'apps/ExpoApp54/**'
- 'apps/brownfield-example-shared-tests/**'
expo55:
- 'apps/ExpoApp55/**'
- 'apps/brownfield-example-shared-tests/**'
androidapp:
- 'apps/AndroidApp/**'
appleapp:
- 'apps/AppleApp/**'
- 'apps/brownfield-example-shared-tests/**'
ci:
- '.github/**'

Expand Down Expand Up @@ -168,8 +177,9 @@ jobs:
rn-project-maven-path: com/rnapp/brownfieldlib

ios-appleapp-vanilla:
name: iOS road test (AppleApp - Vanilla)
name: iOS road test & E2E (AppleApp - Vanilla)
runs-on: macos-26
timeout-minutes: 90
needs: [filter, build-lint]
if: |
always() &&
Expand All @@ -185,15 +195,18 @@ jobs:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6

- name: Run RNApp -> AppleApp road test (Vanilla)
- name: Run RNApp -> AppleApp road test & Detox E2E (Vanilla)
uses: ./.github/actions/appleapp-road-test
with:
variant: vanilla
rn-project-path: apps/RNApp
run-e2e: 'true'
e2e-artifact-name: detox-appleapp-vanilla

ios-appleapp-expo:
name: iOS road test (AppleApp - Expo ${{ matrix.version }})
name: iOS road test${{ matrix.run-e2e == 'true' && ' & E2E' || '' }} (AppleApp - Expo ${{ matrix.version }})
runs-on: macos-26
timeout-minutes: ${{ matrix.run-e2e == 'true' && 90 || 60 }}
needs: [filter, build-lint]
if: |
always() &&
Expand All @@ -209,14 +222,19 @@ jobs:
matrix:
include:
- version: '54'
run-e2e: 'false'
- version: '55'
run-e2e: 'true'

steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6

- name: Run ExpoApp -> AppleApp road test (Expo ${{ matrix.version }})
- name: Run ExpoApp -> AppleApp road test${{ matrix.run-e2e == 'true' && ' & Detox E2E' || '' }} (Expo ${{ matrix.version }})
uses: ./.github/actions/appleapp-road-test
with:
variant: expo${{ matrix.version }}
rn-project-path: apps/ExpoApp${{ matrix.version }}
run-e2e: ${{ matrix.run-e2e }}
e2e-artifact-name: detox-appleapp-expo${{ matrix.version }}

10 changes: 10 additions & 0 deletions apps/AppleApp/.detoxrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const {
createAppleAppIosSimDebugDetoxConfig,
} = require('../brownfield-example-shared-tests/detox-rc-appleapp-ios-sim-debug.cjs');

/** @type {import('detox').DetoxConfig} */
module.exports = createAppleAppIosSimDebugDetoxConfig({
scheme: 'Brownfield Apple App Vanilla',
configuration: 'Debug Vanilla',
appBinaryName: 'Brownfield Apple App (RNApp)',
});
17 changes: 17 additions & 0 deletions apps/AppleApp/.detoxrc.expo55.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const {
createAppleAppIosSimDebugDetoxConfig,
} = require('../brownfield-example-shared-tests/detox-rc-appleapp-ios-sim-debug.cjs');
const {
getAppleAppDetoxVariant,
} = require('../brownfield-example-shared-tests/detox-appleapp-variants.cjs');

const variant = getAppleAppDetoxVariant('expo55');

/** @type {import('detox').DetoxConfig} */
module.exports = createAppleAppIosSimDebugDetoxConfig({
scheme: variant.scheme,
configuration: variant.configuration,
appBinaryName: variant.appBinaryName,
detoxConfiguration: variant.detoxConfiguration,
jestConfigPath: 'e2e/jest.config.expo55.cjs',
});
37 changes: 37 additions & 0 deletions apps/AppleApp/Brownfield Apple App.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,43 @@
79B8BE9B2FB7273600B94C6F /* Brownfield Apple App (ExpoApp55).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Brownfield Apple App (ExpoApp55).app"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
79B8BE822FB7270F00B94C6F /* Exceptions for "Brownfield Apple App" folder in "Brownfield Apple App (ExpoApp54)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Assets.xcassets,
BrownfieldAppleApp.swift,
E2eTestIds.swift,
E2eAccessibility.swift,
components/ContentView.swift,
components/GreetingCard.swift,
components/MaterialCard.swift,
components/MessagesView.swift,
components/ReferralsScreen.swift,
components/SettingsScreen.swift,
components/Toast.swift,
);
target = 79B8BE682FB7270E00B94C6F /* Brownfield Apple App (ExpoApp54) */;
};
79B8BE9C2FB7273600B94C6F /* Exceptions for "Brownfield Apple App" folder in "Brownfield Apple App (ExpoApp55)" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Assets.xcassets,
BrownfieldAppleApp.swift,
E2eTestIds.swift,
E2eAccessibility.swift,
components/ContentView.swift,
components/GreetingCard.swift,
components/MaterialCard.swift,
components/MessagesView.swift,
components/ReferralsScreen.swift,
components/SettingsScreen.swift,
components/Toast.swift,
);
target = 79B8BE832FB7273600B94C6F /* Brownfield Apple App (ExpoApp55) */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */

/* Begin PBXFileSystemSynchronizedRootGroup section */
793C76A92EEBF938008A2A34 /* Brownfield Apple App */ = {
isa = PBXFileSystemSynchronizedRootGroup;
Expand Down
Loading
Loading