Files
react-native/scripts/releases/utils/__tests__/version-utils-test.js
T
Luna Wei d6a440ee8b Remove process.exit calls from publish-npm and throw errors instead (#43039)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/43039

Changelog: [Internal] - `publish-npm.js` is a [script we call in our CI](https://www.internalfb.com/code/fbsource/[c0b8566ac0d66c2c0282eeb597bfb54bedf757c6]/xplat/js/react-native-github/.circleci/configurations/jobs.yml?lines=1243) to publish the react-native package and others.

Currently, the script leverages `exit/process.exit` to terminate early in a couple of places which makes the code hard to test because our tests don't truly early exit when `exit/process.exit` is called.

This change removes any explicit `exit` calls and instead leverages the uncaught error to terminate the process and set the non-zero exit code. This makes our tests more accurate to the real control flow of the script.

I've also updated the tests to better capture what we're actually testing by mocking at a higher level.

Reviewed By: cipolleschi

Differential Revision: D53792754

fbshipit-source-id: 9293bb9a95430c50052db36c0e6f6c1ba348107f
2024-02-15 11:18:05 -08:00

405 lines
12 KiB
JavaScript

/**
* 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.
*
* @flow strict-local
* @format
* @oncall react_native
*/
const {
isNightly,
isReleaseBranch,
parseVersion,
validateBuildType,
} = require('../version-utils');
jest.mock('shelljs', () => ({
exec: () => {
return {
stdout: null,
};
},
echo: message => {
console.log(message);
},
exit: jest.fn(),
}));
describe('version-utils', () => {
describe('isReleaseBranch', () => {
it('should identify as release branch', () => {
expect(isReleaseBranch('v0.66-stable')).toBe(true);
expect(isReleaseBranch('0.66-stable')).toBe(true);
expect(isReleaseBranch('made-up-stuff-stable')).toBe(true);
});
it('should not identify as release branch', () => {
expect(isReleaseBranch('main')).toBe(false);
expect(isReleaseBranch('pull/32659')).toBe(false);
});
});
describe('parseVersion', () => {
it('should throw error if buildType is not `release`, `dry-run`, `prealpha`` or `nightly`', () => {
function testInvalidVersion() {
// $FlowExpectedError[incompatible-call]
parseVersion('v0.10.5', 'invalid_build_type');
}
expect(testInvalidVersion).toThrowErrorMatchingInlineSnapshot(
`"Unsupported build type: invalid_build_type"`,
);
});
it('should throw error if invalid match with release', () => {
function testInvalidVersion() {
parseVersion('<invalid version>', 'release');
}
expect(testInvalidVersion).toThrowErrorMatchingInlineSnapshot(
`"You must pass a correctly formatted version; couldn't parse <invalid version>"`,
);
});
it('should throw error if invalid match with dry-run', () => {
function testInvalidVersion() {
parseVersion('<invalid version>', 'dry-run');
}
expect(testInvalidVersion).toThrowErrorMatchingInlineSnapshot(
`"You must pass a correctly formatted version; couldn't parse <invalid version>"`,
);
});
it('should throw error if invalid match with nightly', () => {
function testInvalidVersion() {
parseVersion('<invalid version>', 'nightly');
}
expect(testInvalidVersion).toThrowErrorMatchingInlineSnapshot(
`"You must pass a correctly formatted version; couldn't parse <invalid version>"`,
);
});
it('should parse pre-release version with release and `.`', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.66.0-rc.4',
'release',
);
expect(version).toBe('0.66.0-rc.4');
expect(major).toBe('0');
expect(minor).toBe('66');
expect(patch).toBe('0');
expect(prerelease).toBe('rc.4');
});
it('should parse pre-release version with release and `-`', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.66.0-rc-4',
'release',
);
expect(version).toBe('0.66.0-rc-4');
expect(major).toBe('0');
expect(minor).toBe('66');
expect(patch).toBe('0');
expect(prerelease).toBe('rc-4');
});
it('should reject pre-release version with random prerelease pattern', () => {
function testInvalidVersion() {
parseVersion('0.66.0-something_invalid', 'release');
}
expect(testInvalidVersion).toThrowErrorMatchingInlineSnapshot(
`"Version 0.66.0-something_invalid is not valid for Release"`,
);
});
it('should parse stable version', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.66.0',
'release',
);
expect(version).toBe('0.66.0');
expect(major).toBe('0');
expect(minor).toBe('66');
expect(patch).toBe('0');
expect(prerelease).toBeUndefined();
});
it('should parse pre-release version from tag', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'v0.66.0-rc.4',
'release',
);
expect(version).toBe('0.66.0-rc.4');
expect(major).toBe('0');
expect(minor).toBe('66');
expect(patch).toBe('0');
expect(prerelease).toBe('rc.4');
});
it('should reject pre-release version from tag with random prerelease pattern', () => {
function testInvalidVersion() {
parseVersion('v0.66.0-something_invalid', 'release');
}
expect(testInvalidVersion).toThrowErrorMatchingInlineSnapshot(
`"Version 0.66.0-something_invalid is not valid for Release"`,
);
});
it('should parse stable version from tag', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'v0.66.0',
'release',
);
expect(version).toBe('0.66.0');
expect(major).toBe('0');
expect(minor).toBe('66');
expect(patch).toBe('0');
expect(prerelease).toBeUndefined();
});
it('should parse nightly with no prerelease', () => {
// this should fail
const {version, major, minor, patch, prerelease} = parseVersion(
'0.0.0',
'nightly',
);
expect(version).toBe('0.0.0');
expect(major).toBe('0');
expect(minor).toBe('0');
expect(patch).toBe('0');
expect(prerelease).toBeUndefined();
});
it('should reject nightly with prerelease but wrong version numbers', () => {
// this should fail
function testInvalidFunction() {
parseVersion('1.2.3-pre-release', 'nightly');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
`"Version 1.2.3-pre-release is not valid for nightlies"`,
);
});
it('should parse nightly with 0.0.0 and a prerelease part', () => {
// this should fail
const {version, major, minor, patch, prerelease} = parseVersion(
'0.0.0-pre-release',
'nightly',
);
expect(version).toBe('0.0.0-pre-release');
expect(major).toBe('0');
expect(minor).toBe('0');
expect(patch).toBe('0');
expect(prerelease).toBe('pre-release');
});
it('should parse dryrun with release version', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.7.3',
'dry-run',
);
expect(version).toBe('0.7.3');
expect(major).toBe('0');
expect(minor).toBe('7');
expect(patch).toBe('3');
expect(prerelease).toBeUndefined();
});
it('should parse dryrun with prerelease . version', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.20.0-rc.0',
'dry-run',
);
expect(version).toBe('0.20.0-rc.0');
expect(major).toBe('0');
expect(minor).toBe('20');
expect(patch).toBe('0');
expect(prerelease).toBe('rc.0');
});
it('should parse dryrun with prerelease - version', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.20.0-rc-0',
'dry-run',
);
expect(version).toBe('0.20.0-rc-0');
expect(major).toBe('0');
expect(minor).toBe('20');
expect(patch).toBe('0');
expect(prerelease).toBe('rc-0');
});
it('should parse dryrun with main version', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'1000.0.0',
'dry-run',
);
expect(version).toBe('1000.0.0');
expect(major).toBe('1000');
expect(minor).toBe('0');
expect(patch).toBe('0');
expect(prerelease).toBeUndefined();
});
it('should fail for dryrun with v1000.0.1 version', () => {
function testInvalidFunction() {
parseVersion('v1000.0.1', 'dry-run');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
`"Version 1000.0.1 is not valid for dry-runs"`,
);
});
it('should parse dryrun with nightly version', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.0.0-something-else',
'dry-run',
);
expect(version).toBe('0.0.0-something-else');
expect(major).toBe('0');
expect(minor).toBe('0');
expect(patch).toBe('0');
expect(prerelease).toBe('something-else');
});
it('should reject dryrun invalid values', () => {
function testInvalidFunction() {
parseVersion('1000.0.4', 'dry-run');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
`"Version 1000.0.4 is not valid for dry-runs"`,
);
});
it('should reject dryrun for invalid prerelease', () => {
function testInvalidFunction() {
parseVersion('0.6.4-something-else', 'dry-run');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
`"Version 0.6.4-something-else is not valid for dry-runs"`,
);
});
it('should parse dryrun for nightlies with no prerelease', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.0.0',
'dry-run',
);
expect(version).toBe('0.0.0');
expect(major).toBe('0');
expect(minor).toBe('0');
expect(patch).toBe('0');
expect(prerelease).toBeUndefined();
});
it('should parse prealpha with valid value', () => {
const {version, major, minor, patch, prerelease} = parseVersion(
'0.0.0-prealpha-2023100416',
'prealpha',
);
expect(version).toBe('0.0.0-prealpha-2023100416');
expect(major).toBe('0');
expect(minor).toBe('0');
expect(patch).toBe('0');
expect(prerelease).toBe('prealpha-2023100416');
});
it('should reject prealpha with 1.0.0 version', () => {
function testInvalidFunction() {
parseVersion('1.0.0-prealpha-2023100416', 'prealpha');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
'"Version 1.0.0-prealpha-2023100416 is not valid for prealphas"',
);
});
it('should reject prealpha with invalid version', () => {
function testInvalidFunction() {
parseVersion('1.2.3-prealpha-2023100416', 'prealpha');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
'"Version 1.2.3-prealpha-2023100416 is not valid for prealphas"',
);
});
it('should reject prealpha with no prerelease', () => {
function testInvalidFunction() {
parseVersion('1.0.0', 'prealpha');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
'"Version 1.0.0 is not valid for prealphas"',
);
});
it('should reject prealpha with invalid prerelease (no timestamp)', () => {
function testInvalidFunction() {
parseVersion('1.0.0-prealpha', 'prealpha');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
'"Version 1.0.0-prealpha is not valid for prealphas"',
);
});
it('should reject prealpha with invalid prerelease (no prealpha)', () => {
function testInvalidFunction() {
parseVersion('1.0.0-2023100416', 'prealpha');
}
expect(testInvalidFunction).toThrowErrorMatchingInlineSnapshot(
'"Version 1.0.0-2023100416 is not valid for prealphas"',
);
});
it('should reject stable releases with major > 0', () => {
expect(() => parseVersion('1.0.1', 'release')).toThrow(
'Version 1.0.1 is not valid for Release',
);
});
});
describe('isNightly', () => {
it('should match old version of nightlies', () => {
expect(
isNightly({
version: '0.0.0-20230420-2108-f84256a92',
major: '0',
minor: '0',
patch: '0',
prerelease: '20230420-2108-f84256a92',
}),
).toBe(true);
});
it('should match nightlies', () => {
expect(
isNightly({
version: '0.81.0-nightly-20230420-f84256a92',
major: '0',
minor: '81',
patch: '0',
prerelease: 'nightly-20230420-f84256a92',
}),
).toBe(true);
});
});
describe('Validate version', () => {
test('should return false if the buildType is unknown', () => {
expect(validateBuildType('wrong_build')).toBe(false);
});
test.each(['release', 'nightly', 'dry-run'])(
'should return true if the buildType is %s',
buildType => {
expect(validateBuildType(buildType)).toBe(true);
},
);
});
});