Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
79de55f
Harden `window` and `navigator` global property descriptors (#57447)
rubennorte Jul 7, 2026
f21a3be
Release 0.87.0-rc.0
react-native-bot Jul 7, 2026
957eeed
[LOCAL] Revert "Release 0.87.0-rc.0" commit
cipolleschi Jul 8, 2026
d67f279
Release 0.87.0-rc.0
react-native-bot Jul 8, 2026
78e296a
[LOCAL] Revert "Release 0.87.0-rc.0" commit
cipolleschi Jul 8, 2026
2d913c7
Release 0.87.0-rc.0
react-native-bot Jul 8, 2026
ca1c1e1
Update Podfile.lock
zeyap Jul 8, 2026
46df8d7
Revert "Update Podfile.lock"
zeyap Jul 8, 2026
babb002
Update Podfile.lock
zeyap Jul 8, 2026
0565bcd
[LOCAL] Bump Metro to 0.87.0 (#57533)
robhogan Jul 13, 2026
26f9754
[0.87] Fix skipped post-publish jobs in release mode (#57480)
cipolleschi Jul 13, 2026
3ece125
Remove react-native/jest-preset subpath (#57481)
huntie Jul 8, 2026
e730105
Remove react-native/rn-get-polyfills subpath (#57482)
huntie Jul 8, 2026
85d2412
Expose 'react-native/setup-env' to replace InitializeCore (#57475)
huntie Jul 8, 2026
f1cfad0
Remove Touchable (base) from public API (#57476)
huntie Jul 8, 2026
0b70d7c
Remove src/* exports mapping (#57484)
huntie Jul 8, 2026
d4f22ae
Expose react-private-interface subpath (#57495)
huntie Jul 10, 2026
e2d6d32
Add unstable-internals entry point (#57489)
huntie Jul 10, 2026
d6baa43
Enable Strict TS API by default (#57490)
huntie Jul 10, 2026
d7288bd
Fix helloworld Jest preset after react-native/jest-preset subpath rem…
zeyap Jul 14, 2026
6c5b9b1
Simplify react-native/setup-env require in community-cli-plugin (#57498)
huntie Jul 10, 2026
9b76007
Bootstrap Metro from InitializeCore until setup-env is graph-reachabl…
zeyap Jul 15, 2026
3e14593
Fix local release testing: publish with --tag for prerelease versions…
zeyap Jul 15, 2026
4b62c4a
Release 0.87.0-rc.1
react-native-bot Jul 15, 2026
7dcf5de
Keep react-native/setup-env reachable in the module graph (#57492)
cipolleschi Jul 9, 2026
cc77834
Restore react-native/setup-env references on main (#57560)
huntie Jul 16, 2026
17f7849
refactor(ios): remove clang VFS overlay, resolve headers via new Reac…
chrfalch Jul 15, 2026
8959ebd
refactor(iOS): include resources like bundles into the precompiled XC…
chrfalch Jul 15, 2026
e325575
feat(iOS): serve third-party deps from the prebuilt ReactNativeDepend…
chrfalch Jul 15, 2026
fd653cb
fix(iOS): classify CFNetwork as a system framework in the header incl…
cipolleschi Jul 15, 2026
486df8d
[0.87] Pick SwiftPM support chain (#57442, #57332, #57564) (#57578)
cipolleschi Jul 16, 2026
6e2ca18
fix(iOS): prevent UUID collision corrupting Pods project in spm_depen…
chrfalch Jul 16, 2026
a556d97
Fix debug Hermes being embedded in iOS Release builds after hermes-en…
tjzel Jul 16, 2026
31cf29a
Remove SceneTracker module and its use in AppRegistry (#57417)
rubennorte Jul 14, 2026
8a35e69
fix(iOS): exclude C++-guarded foreign-namespace headers from React um…
chrfalch Jul 16, 2026
f07a030
fix(iOS): dedupe SPM package module maps by building spm_dependency p…
chrfalch Jul 20, 2026
896fcb3
[LOCAL] Fix CI for RC.2 (#57614)
cipolleschi Jul 20, 2026
88f43bc
[LOCAL] Fix CI: build rntester dynamic-frameworks lane from source (#…
cipolleschi Jul 20, 2026
d045236
Bump hermes version (#57616)
zeyap Jul 20, 2026
17bf68a
[LOCAL] Set .hermesv1version to hermes-v250829098.0.16 (#57620)
zeyap Jul 20, 2026
9518500
Release 0.87.0-rc.2
react-native-bot Jul 20, 2026
651bbdf
[0.87] Update RNTester Podfile.lock for 0.87.0-rc.2 (#57624)
zeyap Jul 21, 2026
65c8168
Fix parentNode for nested root host views like <Modal> (#57623)
rubennorte Jul 21, 2026
50bad79
Fix post-release workflow failures (#57627)
cipolleschi Jul 21, 2026
514f104
fix(iOS): stamp the React Native version in the prebuild compose job …
chrfalch Jul 21, 2026
330080c
fix(iOS): make jsinspector-modern tracing state types move-only for S…
chrfalch Jul 21, 2026
9a7b821
Fix TS exactOptionalPropertyTypes compatibility for generated types (…
zeyap Jul 27, 2026
b67c5cf
Release 0.87.0-rc.3
react-native-bot Jul 27, 2026
ed8973a
[LOCAL] Bump Podfile.lock
react-native-bot Jul 27, 2026
034245a
fix(iOS): stage Hermes headers in prebuild compose job so ReactNative…
chrfalch Jul 27, 2026
0cacb90
SPM: fix raw Flow type annotations in bare-node scripts (#57660)
chrfalch Jul 27, 2026
d8b52df
SPM: allow overriding the autolinking config command (#57662)
chrfalch Jul 27, 2026
8fffb64
fix(rn-tester): use framework-style import for RCTFabricComponentsPlu…
chrfalch Jul 27, 2026
f3e11c2
Fix compat with extensionless scripts/* imports (#57718)
huntie Jul 28, 2026
4dd4d31
Write the prebuilt module-map flag to OTHER_CPLUSPLUSFLAGS too (#57742)
chrfalch Jul 31, 2026
329f864
fix(swiftpm): persist --config-command so the in-build sync keeps usi…
chrfalch Jul 31, 2026
24353da
fix(iOS): keep prebuilt Headers/ in place on a Debug/Release swap (#5…
chrfalch Aug 4, 2026
01f752d
Release 0.87.0-rc.4
react-native-bot Aug 4, 2026
a1942a7
[LOCAL] Bump Podfile.lock
react-native-bot Aug 4, 2026
4bc2473
Release 0.87.0
react-native-bot Aug 11, 2026
c6ebb06
[LOCAL] Bump Podfile.lock
react-native-bot Aug 11, 2026
b8d50a9
Fix Hermes bytecode version mismatch in SwiftPM Release builds (#57928)
nduaarte Aug 14, 2026
363a116
[0.87] Use macOS 26 runners for iOS E2E tests (#58058)
cipolleschi Aug 21, 2026
861e2d0
[LOCAL] Bump hermes-v1 to 250829098.0.17
cipolleschi Aug 21, 2026
566f469
Fix custom font weight rendering heaviest face on iOS Fabric (#57483)
jensdev Jul 9, 2026
f4ef5bd
Fix ConcurrentModificationException in IntentModule.getInitialURL re-…
nickcernera Jul 27, 2026
8fafd46
fix(swiftpm): actually read the artifacts version pin back (#57762)
chrfalch Aug 10, 2026
1216c1b
feat(swiftpm): let autolinking plugins declare build-time script phas…
chrfalch Aug 10, 2026
6e007c3
fix: make an interrupted React-Core-prebuilt swap recoverable (#57831)
adityasingh2400 Aug 10, 2026
f0800d9
Set SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG when injecting SPM (#…
radoslawrolka Aug 13, 2026
f473b03
fix(swiftpm): fix two ways array build settings were mishandled (#57744)
chrfalch Aug 18, 2026
a1c3cfe
Type Animated.event() as an event handler instead of any (#57991)
huntie Aug 18, 2026
11ef67b
Type Animated.Value.addListener callback (#57992)
huntie Aug 18, 2026
5ad5425
- Keep the quotes on header search paths (#57981)
Kudo Aug 19, 2026
115a9ec
docs(swiftpm): rename __doc__ to __docs__, drop two superseded docs, …
chrfalch Aug 19, 2026
f4cdbc4
docs(ios-prebuild): correct how SwiftPM consumes the prebuilt React h…
chrfalch Aug 19, 2026
cdb180c
Derive the generated SPM manifests from shared name constants (#58035)
chrfalch Aug 21, 2026
40af900
Read both export styles from a library's react-native.config.js (#58034)
chrfalch Aug 24, 2026
5e24a69
Drop the inert publicHeadersPath from spm.modules (#58059)
Aug 24, 2026
617a82f
Fail with clear error message when a dep depends on an autolinking pl…
chrfalch Aug 24, 2026
33624ce
Honor a library's spm.name when scaffolding its Package.swift (#58033)
chrfalch Aug 24, 2026
c951129
Resolve SwiftPM manifest naming collisions (#58044)
chrfalch Aug 25, 2026
e6e404e
Hold an app's spm.modules names to the same rules as a library's (#58…
chrfalch Aug 25, 2026
476887c
Merge remote-tracking branch 'refs/remotes/origin/pr/58052' into 0.87…
cipolleschi Aug 26, 2026
dda5dd3
[0.87] Regenerate ReactNativeApi snapshot
cipolleschi Aug 26, 2026
1a6b526
[0.87] Regenerate ReactNativeApi snapshot with CI toolchain
cipolleschi Aug 26, 2026
a59eff6
Release 0.87.1
react-native-bot Aug 26, 2026
2a41d8c
[LOCAL] Bump Podfile.lock
react-native-bot Aug 26, 2026
edc0667
Merge React Native 0.87 stable into macOS fork
Copilot Jul 19, 2026
efa5e1d
fix(jest): preserve setup-env mapping
Copilot Jul 19, 2026
e22be83
fix(types): canonicalize local package imports
Copilot Jul 19, 2026
b52ecd6
chore(types): regenerate React Native API
Copilot Jul 19, 2026
9db8571
fix(changesets): retain origin fallback
Copilot Jul 19, 2026
7198873
chore(release): seed 0.87 rc prerelease
Copilot Jul 19, 2026
0e31a4f
chore(deps): regenerate prerelease lock
Copilot Jul 19, 2026
86a936d
fix(release): align constrained dependencies
Copilot Jul 19, 2026
a0e27d1
fix(ios-prebuild): copy dependency headers safely
Copilot Jul 19, 2026
63597fa
fix(types): resolve macOS typetest workspaces
Copilot Jul 19, 2026
725fd61
chore(jest): regenerate stable sync snapshots
Copilot Jul 19, 2026
92789a5
chore(deps): refresh validation lock
Copilot Jul 19, 2026
30f02b1
fix(pods): resolve dependency package fallback
Copilot Jul 19, 2026
5964743
fix(pods): preserve facade platforms
Copilot Jul 19, 2026
629b06f
chore(pods): regenerate prerelease lock
Copilot Jul 19, 2026
039bf07
fix(prebuild): fail invalid platform requests
Copilot Jul 19, 2026
0a848dd
fix(prebuild): resolve packaged header paths
Copilot Jul 19, 2026
54104ca
style(prebuild): sort header test imports
Copilot Jul 19, 2026
1b1a21e
chore(deps): correct 0.87 stable lock to remove opportunistic upgrades
Saadnajmi Jul 20, 2026
0648dca
fix(babel-preset): restore upstream-canonical transform snapshots
Saadnajmi Jul 20, 2026
f8d9c1a
chore(0.87): sync upstream React Native 0.87.1
Saadnajmi Sep 16, 2026
3b0469e
fix(ci): apply reviewed release validation repairs
Saadnajmi Sep 22, 2026
fb9fe12
fix(ci): align lock metadata with the public registry
Saadnajmi Sep 22, 2026
6d4fde6
fix(ci): refresh validated snapshots and platform fixtures
Saadnajmi Sep 22, 2026
b62db85
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
6c65ca2
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
9b7d594
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
8ce3c9a
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
8750ba4
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 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
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [["react-native-macos", "@react-native-macos/virtualized-lists"]],
"ignore": ["react-native-macos", "@react-native/tester"],
"ignore": [],
"privatePackages": {
"version": false,
"tag": false
Expand Down
41 changes: 41 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"mode": "pre",
"tag": "rc",
"initialVersions": {
"@react-native/asset-utils": "0.87.0-rc.1",
"@react-native/assets-registry": "0.87.0-rc.1",
"@react-native/babel-plugin-codegen": "0.87.0-rc.1",
"@react-native/community-cli-plugin": "0.87.0-rc.1",
"@react-native/debugger-frontend": "0.87.0-rc.1",
"@react-native/debugger-shell": "0.87.0-rc.1",
"@react-native/dev-middleware": "0.87.0-rc.1",
"@react-native/eslint-config": "0.87.0-rc.1",
"@react-native/eslint-plugin": "0.87.0-rc.1",
"@react-native/eslint-plugin-specs": "0.87.0-rc.1",
"@react-native/gradle-plugin": "0.87.0-rc.1",
"@react-native/jest-preset": "0.87.0-rc.1",
"@react-native/metro-config": "0.87.0-rc.1",
"@react-native/new-app-screen": "0.87.0-rc.1",
"@react-native/normalize-colors": "0.87.0-rc.1",
"@react-native/js-polyfills": "0.87.0-rc.1",
"react-native-macos": "0.87.0-rc.0",
"@react-native/babel-preset": "0.87.0-rc.1",
"@react-native/metro-babel-transformer": "0.87.0-rc.1",
"@react-native/codegen": "0.87.0-rc.1",
"@react-native/compatibility-check": "0.87.0-rc.1",
"react-native-macos-init": "2.1.3",
"@react-native/popup-menu-android": "0.87.0-rc.1",
"@react-native/oss-library-example": "0.81.0-main",
"react-native-test-library-apple": "0.87.0-main",
"react-native-test-library-common": "0.87.0-main",
"@react-native/tester": "0.87.0-rc.1",
"@react-native/typescript-config": "0.87.0-rc.1",
"@react-native-macos/virtualized-lists": "0.87.0-rc.0",
"@react-native/core-cli-utils": "0.0.0",
"@react-native/eslint-plugin-monorepo": "0.0.0",
"@react-native/monorepo-tests": "0.0.0",
"@react-native/codegen-typescript-test": "0.0.0",
"@react-native/fantom": "0.0.0"
},
"changesets": []
}
6 changes: 6 additions & 0 deletions .changeset/stable-sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"react-native-macos": patch
"@react-native-macos/virtualized-lists": patch
---

Sync to upstream React Native 0.87.0-rc.1.
9 changes: 9 additions & 0 deletions .github/actions/build-npm-package/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ runs:
pattern: ReactCore*
path: ./packages/react-native/ReactAndroid/external-artifacts/artifacts
merge-multiple: true
# ReactNativeDependenciesHeaders* is already covered by the
# ReactNativeDependencies* pattern above; ReactNativeHeaders* needs its own.
- name: Download ReactNativeHeaders artifacts
if: ${{ inputs.skip-apple-prebuilts != 'true' }}
uses: actions/download-artifact@v7
with:
pattern: ReactNativeHeaders*
path: ./packages/react-native/ReactAndroid/external-artifacts/artifacts
merge-multiple: true
- name: Print Artifacts Directory
if: ${{ inputs.skip-apple-prebuilts != 'true' }}
shell: bash
Expand Down
18 changes: 18 additions & 0 deletions .github/actions/test-ios-rntester/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ inputs:
config of the 2026-07-03 SocketRocket dual-copy regression).
required: false
default: auto
build-from-source:
description: >-
Force building ReactCore/ReactNativeDependencies from source, disabling the
prebuilt Maven fallback (RCT_USE_PREBUILT_RNCORE / RCT_USE_RN_DEP, which
otherwise default to '1'). Used by the from-source dynamic-frameworks lane.
required: false
default: false

runs:
using: composite
Expand Down Expand Up @@ -100,6 +107,17 @@ runs:
export RCT_USE_LOCAL_RN_DEP="/tmp/third-party/ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz"
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ inputs.flavor }}.xcframework.tar.gz"
fi
if [[ "${{ inputs.build-from-source }}" == "true" ]]; then
# Force building ReactCore/ReactNativeDependencies from source. Both
# RCT_USE_PREBUILT_RNCORE and RCT_USE_RN_DEP default to '1', so on a stable
# branch (where released Maven artifacts exist for the current version) the
# install would otherwise consume those prebuilts instead of building from
# source. That is wrong for the from-source dynamic-frameworks lane and also
# fails when a published artifact predates newer podspec layout changes
# (e.g. a React core tarball without ReactNativeHeaders.xcframework).
export RCT_USE_PREBUILT_RNCORE=0
export RCT_USE_RN_DEP=0
fi

cd packages/rn-tester

Expand Down
28 changes: 20 additions & 8 deletions .github/scripts/__tests__/publishing-contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,13 @@ test('repository Changesets policy follows the actual public and private workspa
assert.ok(corePackage && !corePackage.private, 'Missing public core workspace');
assert.ok(listsPackage, 'Missing lists workspace');
const main = corePackage.version === '1000.0.0';
if (!main) parseVersion(corePackage.version);
const prereleaseReview = !main && semver.prerelease(corePackage.version) !== null
&& releasePolicy.baseBranch === 'origin/main';
assert.deepEqual(releasePolicy.ignore, main ? mainPolicy.ignore : []);
assert.equal(releasePolicy.baseBranch, main ? 'origin/main'
assert.equal(releasePolicy.baseBranch, main || prereleaseReview ? 'origin/main'
: `origin/${semver.major(corePackage.version)}.${semver.minor(corePackage.version)}-stable`);
assert.equal(Boolean(listsPackage.private), main, 'Lists must be public on stable and private on main');
assert.equal(Boolean(listsPackage.private), main, 'Lists must be public on release graphs and private in development');
assert.equal(listsPackage.version, corePackage.version);
const publicPackages = [corePackage, listsPackage];
// Derive expectations from manifests, never from the policy or release-plan output.
Expand Down Expand Up @@ -302,8 +305,9 @@ test('repository Changesets policy couples public stable packages without regist
});

test('real Yarn constraints preserve private upstream versions, align public versions, and preserve workspace fork edges', t => {
for (const main of [true, false]) {
const workspaces = graph(main ? '1000.0.0' : '0.83.2');
for (const [version, upstreamVersion] of [['1000.0.0', '0.83.1'], ['0.83.2', '0.83.1'], ['0.87.0-rc.0', '0.87.1']]) {
const main = version === '1000.0.0';
const workspaces = graph(version);
workspaces[1].private = main;
workspaces[1].version = '0.82.0';
workspaces[2].version = '0.82.7';
Expand All @@ -314,7 +318,7 @@ test('real Yarn constraints preserve private upstream versions, align public ver
if (index !== 1) workspaces[index][field][lists] = '*';
}
}
if (!main) workspaces[0].peerDependencies['react-native'] = '0.83.1';
if (!main) workspaces[0].peerDependencies['react-native'] = upstreamVersion;
const {root} = releaseFixture(t, {workspaces});
writeFileSync(join(root, 'yarn.lock'), '');
writeFileSync(join(root, 'yarn.config.cjs'), `module.exports = require(${JSON.stringify(join(repositoryRoot, 'yarn.config.cjs'))});\n`);
Expand All @@ -327,18 +331,26 @@ test('real Yarn constraints preserve private upstream versions, align public ver
yarn(['constraints']);
const actual = workspaces.map((_, index) => JSON.parse(readFileSync(join(root, `packages/p${index}/package.json`), 'utf8')));
assert.equal(actual[0].version, workspaces[0].version);
assert.equal(actual[1].version, main ? '1000.0.0' : '0.83.2');
assert.equal(actual[1].version, version);
assert.equal(actual[2].private, true);
assert.equal(actual[2].version, '0.82.7');
assert.equal(actual[3].version, '1000.0.0');
assert.equal(actual[5].private, true);
assert.equal(actual[5].version, main ? '0.83.0' : '0.83.1');
assert.equal(actual[5].version, main ? '0.83.0' : upstreamVersion);
for (const index of [0, 1, 3]) {
for (const field of ['dependencies', 'devDependencies', 'peerDependencies']) {
assert.equal(actual[index][field]['@react-native/codegen'], main || index === 3 ? 'workspace:*' : '0.83.1');
assert.equal(actual[index][field]['@react-native/codegen'], main || index === 3 ? 'workspace:*' : upstreamVersion);
if (index !== 1) assert.equal(actual[index][field][lists], 'workspace:*');
}
}
if (!main) {
const archive = join(root, 'core.tgz');
yarn(['workspace', core, 'pack', '--out', archive]);
const packed = JSON.parse(execFileSync('tar', ['-xOf', archive, 'package/package.json'], {encoding: 'utf8'}));
assert.equal(packed.version, version);
assert.equal(packed.dependencies[lists], version);
assert.equal(packed.dependencies['@react-native/codegen'], upstreamVersion);
}
}
});

Expand Down
4 changes: 3 additions & 1 deletion .github/scripts/publishing-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ Pending Changesets come from the declared `@changesets/get-release-plan` API, wi

Stable branches must already have their initial release version and React Native peer configured. The publication script does not turn `1000.0.0` into a release. All public release packages must match the core version and branch. Private runtime workspace links are invalid; explicit registry references to the separately published upstream `@react-native/*` packages are valid. Development-only private workspace links are allowed.

Main and merge-stage branches keep the `1000.0.0` development graph, including private virtualized-lists and upstream workspace links. Their Changesets config defers `react-native-macos` with `ignore`; `@react-native/tester` is also listed for compatibility with older Changesets' dependent validation. This preserves pending core changesets until stable preparation, without changing package privacy or enabling private versions or tags. The independent init package remains versionable. Stable preparation must clear `ignore`, set the stable `baseBranch`, make virtualized-lists public, and configure the release versions and upstream registry dependencies before versioning. The repository-graph test checks these branch-specific source settings.
Development graphs on main and merge-stage branches keep `1000.0.0`, including private virtualized-lists and upstream workspace links. Their Changesets config defers `react-native-macos` with `ignore`; `@react-native/tester` is also listed for compatibility with older Changesets' dependent validation. This preserves pending core changesets until stable preparation, without changing package privacy or enabling private versions or tags. The independent init package remains versionable. Stable preparation must clear `ignore`, set the stable `baseBranch`, make virtualized-lists public, and configure the release versions and upstream registry dependencies before versioning. The repository-graph test checks these branch-specific source settings.

Public prerelease review heads can retain `origin/main` while RNM and virtualized-lists have the same prerelease version and neither is private or ignored. They are distinct from the placeholder development graph; stable publication and version-wrapper branch guards still apply. The fork-local lists dependency uses `workspace:*` in source so Changesets tracks it, and Yarn packs it as the exact public version, including its prerelease suffix. Upstream registry dependencies keep their exact React Native peer version.

## Publication and tags

Expand Down
41 changes: 41 additions & 0 deletions .github/workflow-scripts/__tests__/maestro-ios-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

jest.mock('child_process', () => ({
execSync: jest.fn(),
spawn: jest.fn(),
}));
const childProcess = require('child_process');

const {findAvailableSimulator} = require('../maestro-ios');

describe('Maestro iOS runner', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('selects an iPhone Pro simulator from the latest runtime', () => {
childProcess.execSync.mockReturnValue(
JSON.stringify({
devices: {
'iOS 18.5': [{name: 'iPhone 16 Pro', udid: 'old-pro'}],
'iOS 26.5': [
{name: 'iPhone 17 Pro Max', udid: 'new-pro-max'},
{name: 'iPhone 17 Pro', udid: 'new-pro'},
],
},
}),
);

expect(findAvailableSimulator()).toEqual({
name: 'iPhone 17 Pro',
udid: 'new-pro',
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,47 @@ jest.mock('../utils.js', () => ({
process.exit = mockExit;
global.fetch = mockFetch;

const BASE_URL =
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts';

// The verifier HEAD-checks the POM plus every classifier tarball attached to
// the react-native-artifacts publication (external-artifacts/build.gradle.kts).
const expectedUrls = version => [
`${BASE_URL}/${version}/react-native-artifacts-${version}.pom`,
...[
'reactnative-core-debug',
'reactnative-core-release',
'reactnative-dependencies-debug',
'reactnative-dependencies-release',
'reactnative-headers-debug',
'reactnative-headers-release',
'reactnative-dependencies-headers-debug',
'reactnative-dependencies-headers-release',
].map(
classifier =>
`${BASE_URL}/${version}/react-native-artifacts-${version}-${classifier}.tar.gz`,
),
];

describe('#verifyArtifactsAreOnMaven', () => {
beforeEach(jest.clearAllMocks);

it('waits for the packages to be published on maven when version has no v', async () => {
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
throw new Error('Should not be called again!');
});
// First attempt: the POM is not there yet. Second attempt: every URL is.
mockFetch
.mockReturnValueOnce(Promise.resolve({status: 404}))
.mockReturnValueOnce(Promise.resolve({status: 200}));
.mockReturnValue(Promise.resolve({status: 200}));

const version = '0.78.1';
await verifyArtifactsAreOnMaven(version);

expect(mockSleep).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1/react-native-artifacts-0.78.1.pom',
);
for (const url of expectedUrls('0.78.1')) {
expect(mockFetch).toHaveBeenCalledWith(url, {method: 'HEAD'});
}
});

it('waits for the packages to be published on maven, when version starts with v', async () => {
Expand All @@ -48,27 +71,46 @@ describe('#verifyArtifactsAreOnMaven', () => {
});
mockFetch
.mockReturnValueOnce(Promise.resolve({status: 404}))
.mockReturnValueOnce(Promise.resolve({status: 200}));
.mockReturnValue(Promise.resolve({status: 200}));

const version = 'v0.78.1';
await verifyArtifactsAreOnMaven(version);

expect(mockSleep).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1/react-native-artifacts-0.78.1.pom',
);
for (const url of expectedUrls('0.78.1')) {
expect(mockFetch).toHaveBeenCalledWith(url, {method: 'HEAD'});
}
});

it('passes immediately if packages are already on Maven', async () => {
mockFetch.mockReturnValueOnce(Promise.resolve({status: 200}));
mockFetch.mockReturnValue(Promise.resolve({status: 200}));

const version = '0.78.1';
await verifyArtifactsAreOnMaven(version);

expect(mockSleep).toHaveBeenCalledTimes(0);
expect(mockFetch).toHaveBeenCalledWith(
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1/react-native-artifacts-0.78.1.pom',
);
// All nine URLs (POM + 8 classifier tarballs) are verified in one pass.
expect(mockFetch).toHaveBeenCalledTimes(9);
for (const url of expectedUrls('0.78.1')) {
expect(mockFetch).toHaveBeenCalledWith(url, {method: 'HEAD'});
}
});

it('waits when a classifier artifact is missing even though the POM exists', async () => {
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
throw new Error('Should not be called again!');
});
// First attempt: POM ok, first classifier missing. Second attempt: all ok.
mockFetch
.mockReturnValueOnce(Promise.resolve({status: 200}))
.mockReturnValueOnce(Promise.resolve({status: 404}))
.mockReturnValue(Promise.resolve({status: 200}));

const version = '0.78.1';
await verifyArtifactsAreOnMaven(version);

expect(mockSleep).toHaveBeenCalledTimes(1);
expect(mockExit).not.toHaveBeenCalled();
});

it('tries 90 times and then exits', async () => {
Expand All @@ -82,6 +124,7 @@ describe('#verifyArtifactsAreOnMaven', () => {
expect(mockExit).toHaveBeenCalledWith(1);
expect(mockFetch).toHaveBeenCalledWith(
'https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.78.1/react-native-artifacts-0.78.1.pom',
{method: 'HEAD'},
);
});
});
Loading
Loading