Fix publish-npm test mocks isolation (#54479)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54479

Fixes failing CI jobs on `0.83-stable`, e.g. https://github.com/facebook/react-native/actions/runs/19234912061/job/54982161510.

**Changes**

- Simplify main package `package.json` file read with existing `getWorkspaceRoot` util.
- Fix test by mocking `getWorkspaceRoot` (previously: read the real filesystem and caused test breakage outside `main`).
- Add additional `RC0 → RC1` test case.

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D86674348

fbshipit-source-id: 0b8feb4cbf7b1ac7cf4cc560f066a0a10aec1b5f
This commit is contained in:
Alex Hunt
2025-11-10 09:46:38 -08:00
committed by meta-codesync[bot]
parent ec7f729cb2
commit ee7575fec2
3 changed files with 83 additions and 44 deletions
@@ -80,21 +80,26 @@ describe('publish-npm', () => {
jest.resetAllMocks();
});
describe('publish-npm.js', () => {
it('should fail when invalid build type is passed', async () => {
// Call actual function
// $FlowExpectedError[underconstrained-implicit-instantiation]
const npmUtils = jest.requireActual('../../releases/utils/npm-utils');
getNpmInfoMock.mockImplementation(npmUtils.getNpmInfo);
it('should fail when invalid build type is passed', async () => {
// Call actual function
// $FlowExpectedError[underconstrained-implicit-instantiation]
const npmUtils = jest.requireActual('../../releases/utils/npm-utils');
getNpmInfoMock.mockImplementation(npmUtils.getNpmInfo);
await expect(async () => {
// $FlowExpectedError[incompatible-type]
await publishNpm('invalid');
}).rejects.toThrow('Unsupported build type: invalid');
});
await expect(async () => {
// $FlowExpectedError[incompatible-type]
await publishNpm('invalid');
}).rejects.toThrow('Unsupported build type: invalid');
});
describe("publishNpm('dry-run')", () => {
describe('dry-run', () => {
beforeEach(() => {
jest.mock('../../shared/monorepoUtils', () => ({
...jest.requireActual('../../shared/monorepoUtils'),
getWorkspaceRoot: jest.fn().mockResolvedValue({version: '1000.0.0'}),
}));
});
it('should set version, hermes version, and not publish', async () => {
const version = '1000.0.0-currentco';
getNpmInfoMock.mockReturnValueOnce({
@@ -122,35 +127,73 @@ describe('publish-npm', () => {
expect(publishPackageMock).not.toHaveBeenCalled();
});
it('should set version, not set hermes version, and not publish', async () => {
const version = '1000.0.0-currentco';
getNpmInfoMock.mockReturnValueOnce({
version,
tag: null,
describe('when on stable branch', () => {
it('main → RC0: should skip Hermes version', async () => {
const version = '1000.0.0-currentco';
getNpmInfoMock.mockReturnValueOnce({
version,
tag: null,
});
getBranchName.mockReturnValueOnce('0.83-stable');
await publishNpm('dry-run');
expect(updateHermesVersionsToNightlyMock).not.toHaveBeenCalled();
expect(setVersionMock).not.toBeCalled();
expect(updateReactNativeArtifactsMock).toBeCalledWith(
version,
'dry-run',
);
// Generate Android artifacts is now delegate to build_android entirely
expect(generateAndroidArtifactsMock).not.toHaveBeenCalled();
expect(consoleLogMock).toHaveBeenCalledWith(
'Skipping `npm publish` because --dry-run is set.',
);
// Expect termination
expect(publishAndroidArtifactsToMavenMock).not.toHaveBeenCalled();
expect(publishExternalArtifactsToMavenMock).not.toHaveBeenCalled();
expect(publishPackageMock).not.toHaveBeenCalled();
});
getBranchName.mockReturnValueOnce('0.83-stable');
await publishNpm('dry-run');
it('RC0 → RC1: should skip Hermes version and artifacts', async () => {
const version = '0.83.0-rc.0';
getNpmInfoMock.mockReturnValueOnce({
version,
tag: null,
});
getBranchName.mockReturnValueOnce('0.83-stable');
jest.mock('../../shared/monorepoUtils', () => ({
...jest.requireActual('../../shared/monorepoUtils'),
getWorkspaceRoot: jest
.fn()
.mockResolvedValue({version: '0.83.0-rc.0'}),
}));
expect(updateHermesVersionsToNightlyMock).not.toHaveBeenCalled();
expect(setVersionMock).not.toBeCalled();
expect(updateReactNativeArtifactsMock).toBeCalledWith(version, 'dry-run');
await publishNpm('dry-run');
// Generate Android artifacts is now delegate to build_android entirely
expect(generateAndroidArtifactsMock).not.toHaveBeenCalled();
expect(updateHermesVersionsToNightlyMock).not.toHaveBeenCalled();
expect(setVersionMock).not.toBeCalled();
expect(updateReactNativeArtifactsMock).not.toBeCalled();
expect(consoleLogMock).toHaveBeenCalledWith(
'Skipping `npm publish` because --dry-run is set.',
);
// Generate Android artifacts is now delegate to build_android entirely
expect(generateAndroidArtifactsMock).not.toHaveBeenCalled();
// Expect termination
expect(publishAndroidArtifactsToMavenMock).not.toHaveBeenCalled();
expect(publishExternalArtifactsToMavenMock).not.toHaveBeenCalled();
expect(publishPackageMock).not.toHaveBeenCalled();
expect(consoleLogMock).toHaveBeenCalledWith(
'Skipping `npm publish` because --dry-run is set.',
);
// Expect termination
expect(publishAndroidArtifactsToMavenMock).not.toHaveBeenCalled();
expect(publishExternalArtifactsToMavenMock).not.toHaveBeenCalled();
expect(publishPackageMock).not.toHaveBeenCalled();
});
});
});
describe("publishNpm('nightly')", () => {
describe('nightly', () => {
beforeAll(() => {
jest.mock('../../shared/monorepoUtils', () => ({
...jest.requireActual('../../shared/monorepoUtils'),
@@ -308,7 +351,7 @@ describe('publish-npm', () => {
});
});
describe("publishNpm('release')", () => {
describe('release', () => {
it('should publish non-latest', async () => {
const expectedVersion = '0.81.1';
getNpmInfoMock.mockImplementation(() => ({
+3 -11
View File
@@ -28,8 +28,7 @@ const {
} = require('../releases/utils/release-utils');
const {getBranchName} = require('../releases/utils/scm-utils');
const {REPO_ROOT} = require('../shared/consts');
const {getPackages} = require('../shared/monorepoUtils');
const fs = require('fs');
const {getPackages, getWorkspaceRoot} = require('../shared/monorepoUtils');
const path = require('path');
const yargs = require('yargs');
@@ -110,16 +109,9 @@ async function publishNpm(buildType /*: BuildType */) /*: Promise<void> */ {
// Before updating React Native artifacts versions for dry-run, we check if the version has already been set.
// If it has, we don't need to update the artifacts at all (at this will revert them back to 1000.0.0)
// If it hasn't, we can update the native artifacts accordingly.
const reactNativePackageJson = path.join(
REPO_ROOT,
'packages',
'react-native',
'package.json',
);
const packageJsonContent = fs.readFileSync(reactNativePackageJson, 'utf8');
const packageJson = JSON.parse(packageJsonContent);
const projectInfo = await getWorkspaceRoot();
if (packageJson.version === '1000.0.0') {
if (projectInfo.version === '1000.0.0') {
// Set hermes versions to latest available if not on a stable branch
if (!/.*-stable/.test(getBranchName())) {
await updateHermesVersionsToNightly();
+4
View File
@@ -45,6 +45,9 @@ export type PackageInfo = {
// The absolute path to the package
path: string,
// The package version
version: string,
// The parsed package.json contents
packageJson: PackageJson,
};
@@ -113,6 +116,7 @@ async function parsePackageInfo(
{
name: packageJson.name,
path: packagePath,
version: packageJson.version,
packageJson,
},
];