Add snapshot tests for generate-artifacts-executor (#49940)

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

This will just make sure that we don't unintentionally break this script.

Changelog: [Internal]

Reviewed By: alanleedev

Differential Revision: D70919549

fbshipit-source-id: 6d9f2acca46486f1c920afed69609c70f53e6b18
This commit is contained in:
Ramanpreet Nara
2025-03-12 14:55:13 -07:00
committed by Facebook GitHub Bot
parent b899a0013b
commit 8afdbebf9d
5 changed files with 575 additions and 11 deletions
@@ -0,0 +1,39 @@
{
"name": "test-library",
"version": "0.0.0",
"private": true,
"scripts": {
},
"dependencies": {
},
"devDependencies": {
},
"codegenConfig": {
"name": "TestLibraryConfig",
"type": "all",
"android": {
"javaPackageName": "com.testLibrary"
},
"ios": {
"modulesConformingToProtocol": {
"RCTImageURLLoader": [
"RCTTestLibraryImageURLLoader"
],
"RCTURLRequestHandler": [
"RCTTestLibraryURLRequestHandler"
],
"RCTImageDataDecoder": [
"RCTTestLibraryImageDataDecoder"
]
},
"modulesProvider": {
"TestLibraryImageURLLoader": "RCTTestLibraryImageURLLoader",
"TestLibraryURLRequestHandler": "RCTTestLibraryURLRequestHandler",
"TestLibraryImageDataDecoder": "RCTTestLibraryImageDataDecoder"
},
"componentProvider": {
"TestLibraryComponent": "RCTTestLibraryComponentClass"
}
}
}
}
@@ -0,0 +1,40 @@
{
"name": "test-app",
"version": "0.0.0",
"private": true,
"scripts": {
},
"dependencies": {
"test-library": "./lib/test-library"
},
"devDependencies": {
},
"codegenConfig": {
"name": "TestAppConfig",
"type": "all",
"android": {
"javaPackageName": "com.testApp"
},
"ios": {
"modulesConformingToProtocol": {
"RCTImageURLLoader": [
"RCTTestAppImageURLLoader"
],
"RCTURLRequestHandler": [
"RCTTestAppURLRequestHandler"
],
"RCTImageDataDecoder": [
"RCTTestAppImageDataDecoder"
]
},
"modulesProvider": {
"TestAppImageURLLoader": "RCTTestAppImageURLLoader",
"TestAppURLRequestHandler": "RCTTestAppURLRequestHandler",
"TestAppImageDataDecoder": "RCTTestAppImageDataDecoder"
},
"componentProvider": {
"TestAppComponent": "RCTTestAppComponentClass"
}
}
}
}
@@ -0,0 +1,441 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`execute "RCTAppDependencyProvider.h" should match snapshot 1`] = `
"/*
* 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.
*/
#import <Foundation/Foundation.h>
#if __has_include(<React-RCTAppDelegate/RCTDependencyProvider.h>)
#import <React-RCTAppDelegate/RCTDependencyProvider.h>
#elif __has_include(<React_RCTAppDelegate/RCTDependencyProvider.h>)
#import <React_RCTAppDelegate/RCTDependencyProvider.h>
#else
#import \\"RCTDependencyProvider.h\\"
#endif
NS_ASSUME_NONNULL_BEGIN
@interface RCTAppDependencyProvider : NSObject <RCTDependencyProvider>
@end
NS_ASSUME_NONNULL_END
"
`;
exports[`execute "RCTAppDependencyProvider.mm" should match snapshot 1`] = `
"/*
* 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.
*/
#import \\"RCTAppDependencyProvider.h\\"
#import <ReactCodegen/RCTModulesConformingToProtocolsProvider.h>
#import <ReactCodegen/RCTThirdPartyComponentsProvider.h>
#import <ReactCodegen/RCTModuleProviders.h>
@implementation RCTAppDependencyProvider
- (nonnull NSArray<NSString *> *)URLRequestHandlerClassNames {
return RCTModulesConformingToProtocolsProvider.URLRequestHandlerClassNames;
}
- (nonnull NSArray<NSString *> *)imageDataDecoderClassNames {
return RCTModulesConformingToProtocolsProvider.imageDataDecoderClassNames;
}
- (nonnull NSArray<NSString *> *)imageURLLoaderClassNames {
return RCTModulesConformingToProtocolsProvider.imageURLLoaderClassNames;
}
- (nonnull NSDictionary<NSString *,Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents {
return RCTThirdPartyComponentsProvider.thirdPartyFabricComponents;
}
- (nonnull NSDictionary<NSString *, id<RCTModuleProvider>> *)moduleProviders {
return RCTModuleProviders.moduleProviders;
}
@end
"
`;
exports[`execute "RCTModuleProviders.h" should match snapshot 1`] = `
"/*
* 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.
*/
#import <Foundation/Foundation.h>
@protocol RCTModuleProvider;
@interface RCTModuleProviders: NSObject
+ (NSDictionary<NSString *, id<RCTModuleProvider>> *)moduleProviders;
@end
"
`;
exports[`execute "RCTModuleProviders.mm" should match snapshot 1`] = `
"/*
* 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.
*/
#import <Foundation/Foundation.h>
#import \\"RCTModuleProviders.h\\"
#import <ReactCommon/RCTTurboModule.h>
#import <React/RCTLog.h>
@implementation RCTModuleProviders
+ (NSDictionary<NSString *, id<RCTModuleProvider>> *)moduleProviders
{
static NSDictionary<NSString *, id<RCTModuleProvider>> *providers = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSDictionary<NSString *, NSString *> * moduleMapping = @{
@\\"TestAppImageURLLoader\\": @\\"RCTTestAppImageURLLoader\\", // test-app
@\\"TestAppURLRequestHandler\\": @\\"RCTTestAppURLRequestHandler\\", // test-app
@\\"TestAppImageDataDecoder\\": @\\"RCTTestAppImageDataDecoder\\", // test-app
@\\"TestLibraryImageURLLoader\\": @\\"RCTTestLibraryImageURLLoader\\", // test-library
@\\"TestLibraryURLRequestHandler\\": @\\"RCTTestLibraryURLRequestHandler\\", // test-library
@\\"TestLibraryImageDataDecoder\\": @\\"RCTTestLibraryImageDataDecoder\\", // test-library
};
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:moduleMapping.count];
for (NSString *key in moduleMapping) {
NSString * moduleProviderName = moduleMapping[key];
Class klass = NSClassFromString(moduleProviderName);
if (!klass) {
RCTLogError(@\\"Module provider %@ cannot be found in the runtime\\", moduleProviderName);
continue;
}
id instance = [klass new];
if (![instance respondsToSelector:@selector(getTurboModule:)]) {
RCTLogError(@\\"Module provider %@ does not conform to RCTModuleProvider\\", moduleProviderName);
continue;
}
[dict setObject:instance forKey:key];
}
providers = dict;
});
return providers;
}
@end
"
`;
exports[`execute "RCTModulesConformingToProtocolsProvider.h" should match snapshot 1`] = `
"/*
* 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.
*/
#import <Foundation/Foundation.h>
@interface RCTModulesConformingToProtocolsProvider: NSObject
+(NSArray<NSString *> *)imageURLLoaderClassNames;
+(NSArray<NSString *> *)imageDataDecoderClassNames;
+(NSArray<NSString *> *)URLRequestHandlerClassNames;
@end
"
`;
exports[`execute "RCTModulesConformingToProtocolsProvider.mm" should match snapshot 1`] = `
"/*
* 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.
*/
#import \\"RCTModulesConformingToProtocolsProvider.h\\"
@implementation RCTModulesConformingToProtocolsProvider
+(NSArray<NSString *> *)imageURLLoaderClassNames
{
static NSArray<NSString *> *classNames = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
classNames = @[
@\\"RCTTestAppImageURLLoader\\",
@\\"RCTTestLibraryImageURLLoader\\"
];
});
return classNames;
}
+(NSArray<NSString *> *)imageDataDecoderClassNames
{
static NSArray<NSString *> *classNames = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
classNames = @[
@\\"RCTTestAppImageDataDecoder\\",
@\\"RCTTestLibraryImageDataDecoder\\"
];
});
return classNames;
}
+(NSArray<NSString *> *)URLRequestHandlerClassNames
{
static NSArray<NSString *> *classNames = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
classNames = @[
@\\"RCTTestAppURLRequestHandler\\",
@\\"RCTTestLibraryURLRequestHandler\\"
];
});
return classNames;
}
@end
"
`;
exports[`execute "RCTThirdPartyComponentsProvider.h" should match snapshot 1`] = `
"/*
* 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.
*/
#import <Foundation/Foundation.h>
@protocol RCTComponentViewProtocol;
@interface RCTThirdPartyComponentsProvider: NSObject
+ (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents;
@end
"
`;
exports[`execute "RCTThirdPartyComponentsProvider.mm" should match snapshot 1`] = `
"/*
* 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.
*/
#import <Foundation/Foundation.h>
#import \\"RCTThirdPartyComponentsProvider.h\\"
#import <React/RCTComponentViewProtocol.h>
@implementation RCTThirdPartyComponentsProvider
+ (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
{
static NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *thirdPartyComponents = nil;
static dispatch_once_t nativeComponentsToken;
dispatch_once(&nativeComponentsToken, ^{
thirdPartyComponents = @{
@\\"TestAppComponent\\": NSClassFromString(@\\"RCTTestAppComponentClass\\"), // test-app
@\\"TestLibraryComponent\\": NSClassFromString(@\\"RCTTestLibraryComponentClass\\"), // test-library
};
});
return thirdPartyComponents;
}
@end
"
`;
exports[`execute "ReactAppDependencyProvider.podspec" should match snapshot 1`] = `
"# 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.
version = \\"1000.0.0\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = \`git rev-parse HEAD\`.strip if system(\\"git rev-parse --git-dir > /dev/null 2>&1\\")
else
source[:tag] = \\"v#{version}\\"
end
Pod::Spec.new do |s|
s.name = \\"ReactAppDependencyProvider\\"
s.version = version
s.summary = \\"The third party dependency provider for the app\\"
s.homepage = \\"https://reactnative.dev/\\"
s.documentation_url = \\"https://reactnative.dev/\\"
s.license = \\"MIT\\"
s.author = \\"Meta Platforms, Inc. and its affiliates\\"
s.platforms = min_supported_versions
s.source = source
s.source_files = \\"**/RCTAppDependencyProvider.{h,mm}\\"
# This guard prevent to install the dependencies when we run \`pod install\` in the old architecture.
s.pod_target_xcconfig = {
\\"CLANG_CXX_LANGUAGE_STANDARD\\" => rct_cxx_language_standard(),
\\"DEFINES_MODULE\\" => \\"YES\\"
}
s.dependency \\"ReactCodegen\\"
end
"
`;
exports[`execute "ReactCodegen.podspec" should match snapshot 1`] = `
"# 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.
version = \\"1000.0.0\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = \`git rev-parse HEAD\`.strip if system(\\"git rev-parse --git-dir > /dev/null 2>&1\\")
else
source[:tag] = \\"v#{version}\\"
end
use_frameworks = ENV['USE_FRAMEWORKS'] != nil
folly_compiler_flags = Helpers::Constants.folly_config[:compiler_flags]
boost_compiler_flags = Helpers::Constants.boost_config[:compiler_flags]
header_search_paths = [
\\"\\\\\\"$(PODS_ROOT)/boost\\\\\\"\\",
\\"\\\\\\"$(PODS_ROOT)/RCT-Folly\\\\\\"\\",
\\"\\\\\\"$(PODS_ROOT)/DoubleConversion\\\\\\"\\",
\\"\\\\\\"$(PODS_ROOT)/fast_float/include\\\\\\"\\",
\\"\\\\\\"$(PODS_ROOT)/fmt/include\\\\\\"\\",
\\"\\\\\\"\${PODS_ROOT}/Headers/Public/ReactCodegen/react/renderer/components\\\\\\"\\",
\\"\\\\\\"$(PODS_ROOT)/Headers/Private/React-Fabric\\\\\\"\\",
\\"\\\\\\"$(PODS_ROOT)/Headers/Private/React-RCTFabric\\\\\\"\\",
\\"\\\\\\"$(PODS_ROOT)/Headers/Private/Yoga\\\\\\"\\",
\\"\\\\\\"$(PODS_TARGET_SRCROOT)\\\\\\"\\",
]
framework_search_paths = []
if use_frameworks
ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-Fabric\\", \\"React_Fabric\\", [\\"react/renderer/components/view/platform/cxx\\"])
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-FabricImage\\", \\"React_FabricImage\\", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-graphics\\", \\"React_graphics\\", [\\"react/renderer/graphics/platform/ios\\"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"ReactCommon\\", \\"ReactCommon\\", [\\"react/nativemodule/core\\"]))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-NativeModulesApple\\", \\"React_NativeModulesApple\\", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-RCTFabric\\", \\"RCTFabric\\", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-debug\\", \\"React_debug\\", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-rendererdebug\\", \\"React_rendererdebug\\", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-utils\\", \\"React_utils\\", []))
.concat(ReactNativePodsUtils.create_header_search_path_for_frameworks(\\"PODS_CONFIGURATION_BUILD_DIR\\", \\"React-featureflags\\", \\"React_featureflags\\", []))
.each { |search_path|
header_search_paths << \\"\\\\\\"#{search_path}\\\\\\"\\"
}
end
Pod::Spec.new do |s|
s.name = \\"ReactCodegen\\"
s.version = version
s.summary = 'Temp pod for generated files for React Native'
s.homepage = 'https://facebook.com/'
s.license = 'Unlicense'
s.authors = 'Facebook'
s.compiler_flags = \\"#{folly_compiler_flags} #{boost_compiler_flags} -Wno-nullability-completeness -std=c++20\\"
s.source = { :git => '' }
s.header_mappings_dir = './'
s.platforms = min_supported_versions
s.source_files = \\"**/*.{h,mm,cpp}\\"
s.exclude_files = \\"RCTAppDependencyProvider.{h,mm}\\" # these files are generated in the same codegen path but needs to belong to a different pod
s.pod_target_xcconfig = {
\\"HEADER_SEARCH_PATHS\\" => header_search_paths.join(' '),
\\"FRAMEWORK_SEARCH_PATHS\\" => framework_search_paths,
\\"OTHER_CPLUSPLUSFLAGS\\" => \\"$(inherited) #{folly_compiler_flags} #{boost_compiler_flags}\\"
}
s.dependency \\"React-jsiexecutor\\"
s.dependency \\"RCT-Folly\\"
s.dependency \\"RCTRequired\\"
s.dependency \\"RCTTypeSafety\\"
s.dependency \\"React-Core\\"
s.dependency \\"React-jsi\\"
s.dependency \\"ReactCommon/turbomodule/bridging\\"
s.dependency \\"ReactCommon/turbomodule/core\\"
s.dependency \\"React-NativeModulesApple\\"
s.dependency \\"glog\\"
s.dependency \\"DoubleConversion\\"
s.dependency 'React-graphics'
s.dependency 'React-rendererdebug'
s.dependency 'React-Fabric'
s.dependency 'React-FabricImage'
s.dependency 'React-debug'
s.dependency 'React-utils'
s.dependency 'React-featureflags'
s.dependency 'React-RCTAppDelegate'
depend_on_js_engine(s)
s.script_phases = {
'name' => 'Generate Specs',
'execution_position' => :before_compile,
'input_files' => [],
'show_env_vars_in_log' => true,
'output_files' => [\\"\${DERIVED_FILE_DIR}/react-codegen.log\\"],
'script': <<-SCRIPT
pushd \\"$PODS_ROOT/../\\" > /dev/null
RCT_SCRIPT_POD_INSTALLATION_ROOT=$(pwd)
popd >/dev/null
export RCT_SCRIPT_RN_DIR=\\"$RCT_SCRIPT_POD_INSTALLATION_ROOT/../../../../..\\"
export RCT_SCRIPT_APP_PATH=\\"$RCT_SCRIPT_POD_INSTALLATION_ROOT/..\\",
export RCT_SCRIPT_OUTPUT_DIR=\\"$RCT_SCRIPT_POD_INSTALLATION_ROOT\\",
export RCT_SCRIPT_TYPE=\\"withCodegenDiscovery\\",
SCRIPT_PHASES_SCRIPT=\\"$RCT_SCRIPT_RN_DIR/scripts/react_native_pods_utils/script_phases.sh\\"
WITH_ENVIRONMENT=\\"$RCT_SCRIPT_RN_DIR/scripts/xcode/with-environment.sh\\"
/bin/sh -c \\"$WITH_ENVIRONMENT $SCRIPT_PHASES_SCRIPT\\"
SCRIPT
}
end
"
`;
@@ -12,6 +12,7 @@
const fixtures = require('../__test_fixtures__/fixtures');
const underTest = require('../generate-artifacts-executor');
const fs = require('fs');
const path = require('path');
const rootPath = path.join(__dirname, '../../..');
@@ -20,6 +21,39 @@ const packageJson = JSON.stringify({
name: 'react-native',
});
describe('execute', () => {
const appDir = path.join(__dirname, '../__test_fixtures__/test-app');
const outputDir = path.join(appDir, 'temp');
beforeAll(() => {
underTest.execute(appDir, 'ios', outputDir, 'app', false);
});
afterAll(() => {
fs.rmdirSync(outputDir, {recursive: true});
});
[
'RCTAppDependencyProvider.h',
'RCTAppDependencyProvider.mm',
'RCTModuleProviders.h',
'RCTModuleProviders.mm',
'RCTModulesConformingToProtocolsProvider.h',
'RCTModulesConformingToProtocolsProvider.mm',
'RCTThirdPartyComponentsProvider.h',
'RCTThirdPartyComponentsProvider.mm',
'ReactAppDependencyProvider.podspec',
'ReactCodegen.podspec',
].forEach(file => {
it(`"${file}" should match snapshot`, () => {
const generatedFileDir = path.join(outputDir, 'build/generated/ios');
const generatedFile = path.join(generatedFileDir, file);
expect(fs.existsSync(generatedFile)).toBe(true);
expect(fs.readFileSync(generatedFile, 'utf8')).toMatchSnapshot();
});
});
});
describe('extractLibrariesFromJSON', () => {
it('extracts a single dependency when config has no libraries', () => {
let configFile = fixtures.noLibrariesConfigFile;
@@ -1036,7 +1036,13 @@ SCRIPT`;
* @throws If it can't find a CodeGen configuration in the file.
* @throws If it can't find a cli for the CodeGen.
*/
function execute(projectRoot, targetPlatform, baseOutputPath, source) {
function execute(
projectRoot,
targetPlatform,
baseOutputPath,
source,
runReactNativeCodegen = true,
) {
try {
codegenLog(`Analyzing ${path.join(projectRoot, 'package.json')}`);
@@ -1054,7 +1060,9 @@ function execute(projectRoot, targetPlatform, baseOutputPath, source) {
const pkgJson = readPkgJsonInDirectory(projectRoot);
buildCodegenIfNeeded();
if (runReactNativeCodegen) {
buildCodegenIfNeeded();
}
const libraries = findCodegenEnabledLibraries(pkgJson, projectRoot);
@@ -1074,15 +1082,17 @@ function execute(projectRoot, targetPlatform, baseOutputPath, source) {
platform,
);
const schemaInfos = generateSchemaInfos(libraries);
generateNativeCode(
outputPath,
schemaInfos.filter(schemaInfo =>
mustGenerateNativeCode(projectRoot, schemaInfo),
),
pkgJsonIncludesGeneratedCode(pkgJson),
platform,
);
if (runReactNativeCodegen) {
const schemaInfos = generateSchemaInfos(libraries);
generateNativeCode(
outputPath,
schemaInfos.filter(schemaInfo =>
mustGenerateNativeCode(projectRoot, schemaInfo),
),
pkgJsonIncludesGeneratedCode(pkgJson),
platform,
);
}
if (source === 'app') {
// These components are only required by apps, not by libraries