Skip to content

test(js): stop test-file module mocks leaking across files #227

test(js): stop test-file module mocks leaking across files

test(js): stop test-file module mocks leaking across files #227

Workflow file for this run

name: e2e-ios
on:
pull_request:
branches:
- '**'
paths:
- '.github/workflows/e2e_ios.yml'
- '.gitmodules'
- 'package.json'
- 'bun.lock'
- 'react-native-update.podspec'
- 'react-native.config.js'
- 'expo-module.config.json'
- 'src/**'
- 'scripts/**'
- 'ios/**'
- 'cpp/**'
- 'android/jni/**'
- 'Example/e2etest/**'
push:
branches:
- master
paths:
- '.github/workflows/e2e_ios.yml'
- '.gitmodules'
- 'package.json'
- 'bun.lock'
- 'react-native-update.podspec'
- 'react-native.config.js'
- 'expo-module.config.json'
- 'src/**'
- 'scripts/**'
- 'ios/**'
- 'cpp/**'
- 'android/jni/**'
- 'Example/e2etest/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e-ios:
runs-on: macos-26
# 绿色基线 ~16 分钟;40 分钟覆盖最坏情况:一次 --retries 1 失败重试
# (失败用例会等满各级长超时)
timeout-minutes: 40
steps:
- name: Checkout react-native-update
uses: actions/checkout@v7
with:
submodules: recursive
- name: Checkout react-native-update-cli
uses: actions/checkout@v7
with:
repository: reactnativecn/react-native-update-cli
path: react-native-update-cli
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Cache Bun dependencies
uses: actions/cache@v5.0.4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-e2e-${{ hashFiles('Example/e2etest/bun.lock', 'react-native-update-cli/bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-e2e-
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '26.4'
- name: Boot iOS simulator in background
# 模拟器冷启动要 2-5 分钟,提前异步 boot,让它和依赖安装/xcodebuild
# 并行。型号必须用与 .detoxrc.ts 完全同一份探测代码——此前 shell 里
# 另写一份选中了 detox 不用的设备,预热全部白费。
# 失败无害:detox 测试阶段会自己 boot。
run: |
udid=$(node Example/e2etest/scripts/detect-ios-simulator-type.js --udid)
if [ -n "$udid" ]; then
echo "pre-booting $(node Example/e2etest/scripts/detect-ios-simulator-type.js) ($udid)"
xcrun simctl boot "$udid" || true
else
echo "no matching simulator found, skipping pre-boot"
fi
- name: Cache Homebrew downloads
# bottle 按 URL 内容寻址,restore-keys 前缀命中即省下载(装/链接照常)
uses: actions/cache@v5.0.4
with:
path: ~/Library/Caches/Homebrew
key: ${{ runner.os }}-brew-e2e-${{ github.sha }}
restore-keys: |
${{ runner.os }}-brew-e2e-
- name: Install applesimutils and ccache
run: brew trust wix/brew && HOMEBREW_NO_AUTO_UPDATE=1 brew install wix/brew/applesimutils ccache
- name: Cache ccache
# ccache 按(预处理后)内容寻址,不受 fresh checkout 的 mtime 影响——
# 这是 DerivedData 缓存在 -UseNewBuildSystem=NO 下做不到的。
# 陈旧条目不影响正确性,只影响命中率,所以 restore-keys 用前缀即可。
uses: actions/cache@v5.0.4
with:
path: ~/Library/Caches/ccache
key: ${{ runner.os }}-ccache-e2e-xcode-26.4-${{ github.sha }}
restore-keys: |
${{ runner.os }}-ccache-e2e-xcode-26.4-
- name: Install e2etest dependencies
run: cd Example/e2etest && bun install --frozen-lockfile
- name: Link local react-native-update
run: |
set -euo pipefail
export LOCAL_RNU_VERSION="$(node -p "require('./Example/e2etest/node_modules/react-native-update/package.json').version")"
LOCAL_RNU_DIR="$PWD/Example/e2etest/.e2e-artifacts/local-react-native-update"
rm -rf "$LOCAL_RNU_DIR" Example/e2etest/node_modules/react-native-update
mkdir -p "$LOCAL_RNU_DIR"
cp package.json react-native-update.podspec react-native.config.js expo-module.config.json "$LOCAL_RNU_DIR"/
rsync -a --exclude='.git' src ios android cpp scripts "$LOCAL_RNU_DIR"/
node -e "const fs = require('fs'); const file = 'Example/e2etest/.e2e-artifacts/local-react-native-update/package.json'; const pkg = JSON.parse(fs.readFileSync(file, 'utf8')); pkg.version = process.env.LOCAL_RNU_VERSION; fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + '\n');"
ln -s ../.e2e-artifacts/local-react-native-update Example/e2etest/node_modules/react-native-update
- name: Install react-native-update-cli dependencies
run: cd react-native-update-cli && bun install --frozen-lockfile && bun run build
- name: Cache Metro transform cache
# 内容寻址(等价 ccache 之于 clang):陈旧条目只影响命中率不影响正确性
uses: actions/cache@v5.0.4
with:
path: Example/e2etest/.metro-cache
key: ${{ runner.os }}-metro-e2e-${{ github.sha }}
restore-keys: |
${{ runner.os }}-metro-e2e-
- name: Prepare local update artifacts
env:
E2E_PLATFORM: ios
RNU_CLI_ROOT: ${{ github.workspace }}/react-native-update-cli
run: cd Example/e2etest && bun run prepare:e2e
- name: Cache CocoaPods downloads
uses: actions/cache@v5.0.4
with:
path: ~/Library/Caches/CocoaPods
key: ${{ runner.os }}-cocoapods-e2e-${{ hashFiles('Example/e2etest/ios/Podfile.lock', 'Example/e2etest/ios/Podfile', 'Example/e2etest/bun.lock') }}
restore-keys: |
${{ runner.os }}-cocoapods-e2e-
- name: Cache built e2e app (keyed on native inputs)
# 命中 = native 输入(两个 ios 工程、cpp/jni、podspec、依赖锁、codegen 输入)
# 一字未改,跳过 pod install + xcodebuild,只重新注入 JS bundle(见下)。
# codegen 输入必须参与 key:TurboModule 胶水在 pod install 时由根
# package.json 的 codegenConfig + src/Native*.ts spec 生成,漏了它们
# 会在改 spec 后把新 JS 注入到旧胶水的二进制上。
# 故意不设 restore-keys:部分命中就是错的二进制,宁可全量重建。
id: cache-ios-app
uses: actions/cache@v5.0.4
with:
path: |
Example/e2etest/ios/build/Build/Products/Release-iphonesimulator/AwesomeProject.app
Example/e2etest/ios/build/hermesc
key: ${{ runner.os }}-e2e-app-xcode-26.4-${{ hashFiles('Example/e2etest/ios/**', 'Example/e2etest/package.json', 'Example/e2etest/bun.lock', 'react-native-update.podspec', 'react-native.config.js', 'expo-module.config.json', 'ios/**', 'cpp/**', 'android/jni/**', 'src/Native*.ts', 'package.json') }}
- name: Install CocoaPods dependencies
if: steps.cache-ios-app.outputs.cache-hit != 'true'
# USE_CCACHE=1 让 react_native_post_install 给所有 pod target 接上
# RN 自带的 ccache 编译器包装脚本
run: cd Example/e2etest/ios && USE_CCACHE=1 pod install
- name: Cache Detox iOS framework
id: cache-detox-ios-framework
uses: actions/cache@v5.0.4
with:
path: ~/Library/Detox
key: ${{ runner.os }}-detox-ios-framework-xcode-26.4-${{ hashFiles('Example/e2etest/bun.lock') }}
restore-keys: |
${{ runner.os }}-detox-ios-framework-xcode-26.4-
- name: Build Detox iOS framework cache
if: steps.cache-detox-ios-framework.outputs.cache-hit != 'true'
run: cd Example/e2etest && bunx detox build-framework-cache
- name: Detox build (ios.sim.release)
if: steps.cache-ios-app.outputs.cache-hit != 'true'
env:
RNU_CLI_ROOT: ${{ github.workspace }}/react-native-update-cli
run: |
cd Example/e2etest && E2E_PLATFORM=ios bunx detox build --configuration ios.sim.release
# hermesc 进缓存区:命中 run 没有 Pods,注入脚本需要它编 HBC
cp ios/Pods/hermes-engine/destroot/bin/hermesc ios/build/hermesc
- name: Refresh JS bundle in cached app
if: steps.cache-ios-app.outputs.cache-hit == 'true'
run: cd Example/e2etest && ./scripts/refresh-ios-app-bundle.sh
- name: Detox test (ios.sim.release)
env:
RNU_CLI_ROOT: ${{ github.workspace }}/react-native-update-cli
RNU_E2E_SKIP_PREPARE: 'true'
run: cd Example/e2etest && E2E_PLATFORM=ios bunx detox test --configuration ios.sim.release --retries 1
- name: Upload Detox artifacts
# cancelled() 覆盖 job 超时被杀的场景,否则超时时拿不到现场
if: failure() || cancelled()
uses: actions/upload-artifact@v7.0.1
with:
name: e2e-ios-detox-artifacts
path: Example/e2etest/artifacts
if-no-files-found: ignore
retention-days: 7