/
githubmr
/
facebook-react-native
Обзор
Документация
Войти
/
githubmr
/
facebook-react-native
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-native/scripts/swiftpm/__tests__/headers-utils-test.js
1 210 строк
38 KB
Riccardo Cipolleschi
add function hard links for headers from ReactCodegen (#53640)
22 сен 2025, 20:14
22 сен 2025, 20:14
c6395ec
Код
Авторство
О чём код?
/** * 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 * @noflow */ 'use strict'; const { symlinkCodegenHeaders, symlinkHeadersFromPath, symlinkReactAppleHeaders, symlinkReactCommonHeaders, symlinkThirdPartyDependenciesHeaders, } = require('../headers-utils'); // Mock all required modules jest.mock('../utils'); jest.mock('../headers-mappings'); jest.mock('fs'); jest.mock('path'); describe('symlinkHeadersFromPath', () => { let mockUtils; let mockFs; let mockPath; let originalConsoleWarn; let originalConsoleLog; beforeEach(() => { // Setup mocks mockUtils = require('../utils'); mockFs = require('fs'); mockPath = require('path'); // Mock path functions mockPath.relative.mockImplementation((from, to) => { return to.replace(from + '/', ''); }); mockPath.join.mockImplementation((...args) => args.join('/')); mockPath.dirname.mockImplementation(filePath => { const parts = filePath.split('/'); parts.pop(); const result = parts.join('/'); return result === '' ? '.' : result; }); mockPath.basename.mockImplementation(filePath => { return filePath.split('/').pop(); }); // Mock console methods to prevent test output noise originalConsoleWarn = console.warn; originalConsoleLog = console.log; console.warn = jest.fn(); console.log = jest.fn(); // Reset all mocks jest.clearAllMocks(); }); afterEach(() => { // Restore console methods console.warn = originalConsoleWarn; console.log = originalConsoleLog; }); it('should create symlinks for found header files without preserving structure', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; const headerFiles = [ '/source/path/subdir/header1.h', '/source/path/header2.h', ]; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockFs.existsSync.mockImplementation(filePath => { return ( filePath === '/source/path/subdir/header1.h' || filePath === '/source/path/header2.h' ); }); // Execute const result = symlinkHeadersFromPath(sourcePath, outputPath, false, []); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith(sourcePath, []); expect(mockUtils.setupSymlink).toHaveBeenCalledTimes(2); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/source/path/subdir/header1.h', '/output/path/header1.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/source/path/header2.h', '/output/path/header2.h', ); expect(result).toBe(2); }); it('should preserve directory structure when preserveStructure is true', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; const headerFiles = [ '/source/path/subdir/header1.h', '/source/path/another/header2.h', ]; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockFs.existsSync.mockImplementation(filePath => { return ( filePath === '/source/path/subdir/header1.h' || filePath === '/source/path/another/header2.h' ); }); // Execute const result = symlinkHeadersFromPath(sourcePath, outputPath, true, []); // Assert expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/source/path/subdir/header1.h', '/output/path/subdir/header1.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/source/path/another/header2.h', '/output/path/another/header2.h', ); expect(result).toBe(2); }); it('should exclude specified folders from find command', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; const excludeFolders = ['node_modules', 'test']; mockUtils.listHeadersInFolder.mockReturnValue([]); // Execute symlinkHeadersFromPath(sourcePath, outputPath, false, excludeFolders); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( sourcePath, excludeFolders, ); }); it('should create destination directories if they do not exist', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; const headerFiles = ['/source/path/subdir/header1.h']; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockFs.existsSync.mockImplementation(filePath => { return filePath === '/source/path/subdir/header1.h'; }); // Execute symlinkHeadersFromPath(sourcePath, outputPath, true, []); // Assert - setupSymlink should be called, which internally handles directory creation expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/source/path/subdir/header1.h', '/output/path/subdir/header1.h', ); }); it('should remove existing symlink before creating new one', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; const headerFiles = ['/source/path/header1.h']; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockFs.existsSync.mockImplementation(filePath => { return filePath === '/source/path/header1.h'; // Only source file exists }); // Execute symlinkHeadersFromPath(sourcePath, outputPath, false, []); // Assert - setupSymlink should be called, which internally handles removing existing links expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/source/path/header1.h', '/output/path/header1.h', ); }); it('should skip non-existent source files', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; const headerFiles = [ '/source/path/header1.h', '/source/path/nonexistent.h', ]; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockFs.existsSync.mockImplementation(filePath => { return filePath === '/source/path/header1.h'; // Only header1.h exists }); // Execute const result = symlinkHeadersFromPath(sourcePath, outputPath, false, []); // Assert expect(mockUtils.setupSymlink).toHaveBeenCalledTimes(1); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/source/path/header1.h', '/output/path/header1.h', ); expect(result).toBe(1); }); it('should handle empty find command output', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; mockUtils.listHeadersInFolder.mockReturnValue([]); // Execute const result = symlinkHeadersFromPath(sourcePath, outputPath, false, []); // Assert expect(mockUtils.setupSymlink).not.toHaveBeenCalled(); expect(result).toBe(0); }); it('should handle whitespace-only find command output', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; mockUtils.listHeadersInFolder.mockReturnValue([]); // Execute const result = symlinkHeadersFromPath(sourcePath, outputPath, false, []); // Assert expect(mockUtils.setupSymlink).not.toHaveBeenCalled(); expect(result).toBe(0); }); it('should handle listHeadersInFolder throwing an error', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; const error = new Error('Command failed'); mockUtils.listHeadersInFolder.mockImplementation(() => { throw error; }); // Execute const result = symlinkHeadersFromPath(sourcePath, outputPath, false, []); // Assert expect(console.warn).toHaveBeenCalledWith( 'Failed to process headers from /source/path:', 'Command failed', ); expect(result).toBe(0); }); it('should handle setupSymlink throwing an error', () => { // Setup const sourcePath = '/source/path'; const outputPath = '/output/path'; const headerFiles = ['/source/path/header1.h']; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockFs.existsSync.mockImplementation(filePath => { return filePath === '/source/path/header1.h'; }); mockUtils.setupSymlink.mockImplementation(() => { throw new Error('Link failed'); }); // Execute const result = symlinkHeadersFromPath(sourcePath, outputPath, false, []); // Assert expect(console.warn).toHaveBeenCalledWith( 'Failed to process headers from /source/path:', 'Link failed', ); expect(result).toBe(0); }); }); describe('symlinkCodegenHeaders', () => { let mockUtils; let mockFs; let mockPath; let originalConsoleWarn; let originalConsoleLog; beforeEach(() => { // Setup mocks mockUtils = require('../utils'); mockFs = require('fs'); mockPath = require('path'); // Mock path functions mockPath.relative.mockImplementation((from, to) => { return to.replace(from + '/', ''); }); mockPath.join.mockImplementation((...args) => args.join('/')); mockPath.dirname.mockImplementation(filePath => { const parts = filePath.split('/'); parts.pop(); const result = parts.join('/'); return result === '' ? '.' : result; }); mockPath.basename.mockImplementation(filePath => { return filePath.split('/').pop(); }); // Mock console methods to prevent test output noise originalConsoleWarn = console.warn; originalConsoleLog = console.log; console.warn = jest.fn(); console.log = jest.fn(); // Reset all mocks jest.clearAllMocks(); }); afterEach(() => { // Restore console methods console.warn = originalConsoleWarn; console.log = originalConsoleLog; }); it('should create symlinks for codegen headers with conditional directory structure', () => { // Setup const reactNativePath = '/path/to/react-native'; const iosAppPath = '/path/to/ios-app'; const outputFolder = '/output/folder'; const reactCodegenPath = '/path/to/ios-app/build/generated/ios/ReactCodegen'; const headerFiles = [ `${reactCodegenPath}/ComponentDescriptors.h`, `${reactCodegenPath}/ModuleProvider.h`, `${reactCodegenPath}/react/renderer/components/MyComponent/ComponentDescriptors.h`, `${reactCodegenPath}/react/renderer/components/MyComponent/EventEmitter.h`, ]; mockFs.existsSync.mockImplementation(filePath => { if (filePath === reactCodegenPath) return true; if (filePath.includes('ReactCodegen') && filePath.endsWith('.h')) return true; return false; }); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkCodegenHeaders( reactNativePath, iosAppPath, outputFolder, ); // Assert expect(mockFs.existsSync).toHaveBeenCalledWith(reactCodegenPath); expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( reactCodegenPath, ['headers', 'tests'], ); // Files with no subpath go to ReactCodegen folder expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${reactCodegenPath}/ComponentDescriptors.h`, '/output/folder/headers/ReactCodegen/ComponentDescriptors.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${reactCodegenPath}/ModuleProvider.h`, '/output/folder/headers/ReactCodegen/ModuleProvider.h', ); // Files with subpaths preserve structure under headers/ expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${reactCodegenPath}/react/renderer/components/MyComponent/ComponentDescriptors.h`, '/output/folder/headers/react/renderer/components/MyComponent/ComponentDescriptors.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${reactCodegenPath}/react/renderer/components/MyComponent/EventEmitter.h`, '/output/folder/headers/react/renderer/components/MyComponent/EventEmitter.h', ); expect(result).toBe(4); }); it('should warn and return 0 if ReactCodegen path does not exist', () => { // Setup const reactNativePath = '/path/to/react-native'; const iosAppPath = '/path/to/ios-app'; const outputFolder = '/output/folder'; const reactCodegenPath = '/path/to/ios-app/build/generated/ios/ReactCodegen'; mockFs.existsSync.mockImplementation(filePath => { if (filePath === reactCodegenPath) return false; // ReactCodegen path doesn't exist return false; }); // Execute const result = symlinkCodegenHeaders( reactNativePath, iosAppPath, outputFolder, ); // Assert expect(console.warn).toHaveBeenCalledWith( `ReactCodegen path does not exist: ${reactCodegenPath}`, ); expect(mockUtils.listHeadersInFolder).not.toHaveBeenCalled(); expect(result).toBe(0); }); it('should return 0 if no header files exist', () => { // Setup const reactNativePath = '/path/to/react-native'; const iosAppPath = '/path/to/ios-app'; const outputFolder = '/output/folder'; const reactCodegenPath = '/path/to/ios-app/build/generated/ios/ReactCodegen'; mockFs.existsSync.mockImplementation(filePath => { if (filePath === reactCodegenPath) return true; return false; }); mockUtils.listHeadersInFolder.mockReturnValue([]); // Execute const result = symlinkCodegenHeaders( reactNativePath, iosAppPath, outputFolder, ); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( reactCodegenPath, ['headers', 'tests'], ); expect(mockUtils.setupSymlink).not.toHaveBeenCalled(); expect(console.log).toHaveBeenCalledWith( 'Created symlinks for 0 Codegen headers with conditional directory structure', ); expect(result).toBe(0); }); }); describe('symlinkThirdPartyDependenciesHeaders', () => { let mockUtils; let mockFs; let mockPath; let originalConsoleWarn; let originalConsoleLog; beforeEach(() => { // Setup mocks mockUtils = require('../utils'); mockFs = require('fs'); mockPath = require('path'); // Mock path functions mockPath.relative.mockImplementation((from, to) => { return to.replace(from + '/', ''); }); mockPath.join.mockImplementation((...args) => args.join('/')); mockPath.dirname.mockImplementation(filePath => { const parts = filePath.split('/'); parts.pop(); return parts.join('/'); }); mockPath.basename.mockImplementation(filePath => { return filePath.split('/').pop(); }); // Mock console methods to prevent test output noise originalConsoleWarn = console.warn; originalConsoleLog = console.log; console.warn = jest.fn(); console.log = jest.fn(); // Reset all mocks jest.clearAllMocks(); }); afterEach(() => { // Restore console methods console.warn = originalConsoleWarn; console.log = originalConsoleLog; }); it('should create symlinks for third-party dependencies headers with preserved structure', () => { // Setup const reactNativePath = '/path/to/react-native'; const outputFolder = '/output/folder'; const folderName = 'headers'; const thirdPartyHeadersPath = '/path/to/react-native/third-party/ReactNativeDependencies.xcframework/Headers'; const headerFiles = [ `${thirdPartyHeadersPath}/boost/boost.h`, `${thirdPartyHeadersPath}/glog/glog.h`, `${thirdPartyHeadersPath}/fmt/format.h`, `${thirdPartyHeadersPath}/folly/folly.hpp`, ]; mockFs.existsSync.mockImplementation(filePath => { if (filePath === thirdPartyHeadersPath) return true; if ( filePath.includes('third-party') && (filePath.endsWith('.h') || filePath.endsWith('.hpp')) ) return true; return false; }); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkThirdPartyDependenciesHeaders( reactNativePath, outputFolder, folderName, ); // Assert expect(mockFs.existsSync).toHaveBeenCalledWith(thirdPartyHeadersPath); expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( thirdPartyHeadersPath, ['tests'], ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${thirdPartyHeadersPath}/boost/boost.h`, '/output/folder/headers/boost/boost.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${thirdPartyHeadersPath}/glog/glog.h`, '/output/folder/headers/glog/glog.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${thirdPartyHeadersPath}/fmt/format.h`, '/output/folder/headers/fmt/format.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${thirdPartyHeadersPath}/folly/folly.hpp`, '/output/folder/headers/folly/folly.hpp', ); expect(result).toBe(4); }); it('should use default folder name when folderName parameter is not provided', () => { // Setup const reactNativePath = '/path/to/react-native'; const outputFolder = '/output/folder'; const thirdPartyHeadersPath = '/path/to/react-native/third-party/ReactNativeDependencies.xcframework/Headers'; const headerFiles = [`${thirdPartyHeadersPath}/boost/boost.h`]; mockFs.existsSync.mockImplementation(filePath => { if (filePath === thirdPartyHeadersPath) return true; if (filePath.includes('third-party') && filePath.endsWith('.h')) return true; return false; }); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute - without folderName parameter const result = symlinkThirdPartyDependenciesHeaders( reactNativePath, outputFolder, ); // Assert - should use default 'headers' folder expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${thirdPartyHeadersPath}/boost/boost.h`, '/output/folder/headers/boost/boost.h', ); expect(result).toBe(1); }); it('should warn and return 0 if third-party headers path does not exist', () => { // Setup const reactNativePath = '/path/to/react-native'; const outputFolder = '/output/folder'; const folderName = 'headers'; const thirdPartyHeadersPath = '/path/to/react-native/third-party/ReactNativeDependencies.xcframework/Headers'; mockFs.existsSync.mockImplementation(filePath => { if (filePath === thirdPartyHeadersPath) return false; // third-party headers path doesn't exist return false; }); // Execute const result = symlinkThirdPartyDependenciesHeaders( reactNativePath, outputFolder, folderName, ); // Assert expect(console.warn).toHaveBeenCalledWith( `Third-party dependencies headers path does not exist: ${thirdPartyHeadersPath}`, ); expect(mockUtils.listHeadersInFolder).not.toHaveBeenCalled(); expect(result).toBe(0); }); it('should handle setupSymlink throwing an error gracefully', () => { // Setup const reactNativePath = '/path/to/react-native'; const outputFolder = '/output/folder'; const folderName = 'headers'; const thirdPartyHeadersPath = '/path/to/react-native/third-party/ReactNativeDependencies.xcframework/Headers'; const headerFiles = [`${thirdPartyHeadersPath}/boost/boost.h`]; mockFs.existsSync.mockImplementation(filePath => { if (filePath === thirdPartyHeadersPath) return true; if (filePath.includes('third-party') && filePath.endsWith('.h')) return true; return false; }); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => { throw new Error('Link failed'); }); // Execute and Assert - function should throw the error from setupSymlink expect(() => { symlinkThirdPartyDependenciesHeaders( reactNativePath, outputFolder, folderName, ); }).toThrow('Link failed'); }); it('should handle both .h and .hpp files', () => { // Setup const reactNativePath = '/path/to/react-native'; const outputFolder = '/output/folder'; const folderName = 'headers'; const thirdPartyHeadersPath = '/path/to/react-native/third-party/ReactNativeDependencies.xcframework/Headers'; const headerFiles = [ `${thirdPartyHeadersPath}/library1/header.h`, `${thirdPartyHeadersPath}/library2/header.hpp`, ]; mockFs.existsSync.mockImplementation(filePath => { if (filePath === thirdPartyHeadersPath) return true; if ( filePath.includes('third-party') && (filePath.endsWith('.h') || filePath.endsWith('.hpp')) ) return true; return false; }); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkThirdPartyDependenciesHeaders( reactNativePath, outputFolder, folderName, ); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( thirdPartyHeadersPath, ['tests'], ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${thirdPartyHeadersPath}/library1/header.h`, '/output/folder/headers/library1/header.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( `${thirdPartyHeadersPath}/library2/header.hpp`, '/output/folder/headers/library2/header.hpp', ); expect(result).toBe(2); }); }); describe('symlinkReactAppleHeaders', () => { let mockUtils; let mockPath; let originalConsoleWarn; let originalConsoleLog; beforeEach(() => { // Setup mocks mockUtils = require('../utils'); mockPath = require('path'); // Mock path functions mockPath.relative.mockImplementation((from, to) => { return to.replace(from + '/', ''); }); mockPath.join.mockImplementation((...args) => args.join('/')); mockPath.dirname.mockImplementation(filePath => { const parts = filePath.split('/'); parts.pop(); return parts.join('/'); }); mockPath.basename.mockImplementation(filePath => { return filePath.split('/').pop(); }); mockPath.sep = '/'; // Mock console methods to prevent test output noise originalConsoleWarn = console.warn; originalConsoleLog = console.log; console.warn = jest.fn(); console.log = jest.fn(); // Reset all mocks jest.clearAllMocks(); }); afterEach(() => { // Restore console methods console.warn = originalConsoleWarn; console.log = originalConsoleLog; }); it('should create symlinks for ReactApple headers with Exported folder structure', () => { // Setup const reactApplePath = '/path/to/ReactApple'; const headersOutput = '/output/headers'; const headerFiles = [ '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', ]; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactAppleHeaders(reactApplePath, headersOutput); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported', ['tests'], ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', '/output/headers/RCTDeprecation/RCTDeprecation.h', ); expect(result).toBe(1); }); it('should handle empty header files from mapping', () => { // Setup const reactApplePath = '/path/to/ReactApple'; const headersOutput = '/output/headers'; mockUtils.listHeadersInFolder.mockReturnValue([]); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactAppleHeaders(reactApplePath, headersOutput); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported', ['tests'], ); expect(mockUtils.setupSymlink).not.toHaveBeenCalled(); expect(result).toBe(0); }); it('should handle multiple header files in the same mapping', () => { // Setup const reactApplePath = '/path/to/ReactApple'; const headersOutput = '/output/headers'; const headerFiles = [ '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTUtility.h', ]; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactAppleHeaders(reactApplePath, headersOutput); // Assert expect(mockUtils.setupSymlink).toHaveBeenCalledTimes(2); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', '/output/headers/RCTDeprecation/RCTDeprecation.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTUtility.h', '/output/headers/RCTDeprecation/RCTUtility.h', ); expect(result).toBe(2); }); it('should handle setupSymlink throwing an error', () => { // Setup const reactApplePath = '/path/to/ReactApple'; const headersOutput = '/output/headers'; const headerFiles = [ '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', ]; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => { throw new Error('Link failed'); }); // Execute and Assert - function throws the error from setupSymlink expect(() => { symlinkReactAppleHeaders(reactApplePath, headersOutput); }).toThrow('Link failed'); }); it('should work correctly with the symcoded mapping structure', () => { // Setup const reactApplePath = '/path/to/ReactApple'; const headersOutput = '/output/headers'; const headerFiles = [ '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', ]; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactAppleHeaders(reactApplePath, headersOutput); // Assert - setupSymlink handles removing existing files and creating new ones internally expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', '/output/headers/RCTDeprecation/RCTDeprecation.h', ); expect(result).toBe(1); }); it('should handle listHeadersInFolder returning empty array', () => { // Setup const reactApplePath = '/path/to/ReactApple'; const headersOutput = '/output/headers'; mockUtils.listHeadersInFolder.mockReturnValue([]); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactAppleHeaders(reactApplePath, headersOutput); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported', ['tests'], ); expect(mockUtils.setupSymlink).not.toHaveBeenCalled(); expect(result).toBe(0); }); it('should handle listHeadersInFolder throwing an error', () => { // Setup const reactApplePath = '/path/to/ReactApple'; const headersOutput = '/output/headers'; const error = new Error('Command failed'); mockUtils.listHeadersInFolder.mockImplementation(() => { throw error; }); // Execute and Assert - function throws the error from listHeadersInFolder expect(() => { symlinkReactAppleHeaders(reactApplePath, headersOutput); }).toThrow('Command failed'); }); it('should work correctly with single symcoded mapping', () => { // Setup - Test the specific mapping defined in the function const reactApplePath = '/path/to/ReactApple'; const headersOutput = '/output/headers'; const headerFiles = [ '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', ]; mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactAppleHeaders(reactApplePath, headersOutput); // Assert - Test the specific mapping that's symcoded expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported', ['tests'], ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported/RCTDeprecation.h', '/output/headers/RCTDeprecation/RCTDeprecation.h', ); expect(result).toBe(1); }); }); describe('symlinkReactCommonHeaders', () => { let mockUtils; let mockReactCommonMappings; let mockPath; let originalConsoleWarn; let originalConsoleLog; beforeEach(() => { // Setup mocks mockUtils = require('../utils'); mockReactCommonMappings = require('../headers-mappings').reactCommonMappings; mockPath = require('path'); // Mock path functions mockPath.relative.mockImplementation((from, to) => { return to.replace(from + '/', ''); }); mockPath.join.mockImplementation((...args) => args.join('/')); mockPath.dirname.mockImplementation(filePath => { const parts = filePath.split('/'); parts.pop(); return parts.join('/'); }); mockPath.basename.mockImplementation(filePath => { return filePath.split('/').pop(); }); mockPath.sep = '/'; // Mock console methods to prevent test output noise originalConsoleWarn = console.warn; originalConsoleLog = console.log; console.warn = jest.fn(); console.log = jest.fn(); // Reset all mocks jest.clearAllMocks(); }); afterEach(() => { // Restore console methods console.warn = originalConsoleWarn; console.log = originalConsoleLog; }); it('should use reactCommonMappings and create symlinks for mapped directories', () => { // Setup const reactCommonPath = '/path/to/ReactCommon'; const headersOutput = '/output/headers'; const mappings = { '/path/to/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon': { destination: '/output/headers/ReactCommon', excludeFolders: ['tests'], preserveStructure: false, }, }; const headerFiles = [ '/path/to/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/TurboModule.h', ]; mockReactCommonMappings.mockReturnValue(mappings); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactCommonHeaders(reactCommonPath, headersOutput); // Assert expect(mockReactCommonMappings).toHaveBeenCalledWith( reactCommonPath, headersOutput, ); expect(mockUtils.listHeadersInFolder).toHaveBeenCalledWith( '/path/to/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon', ['tests'], ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/TurboModule.h', '/output/headers/ReactCommon/TurboModule.h', ); expect(result).toBe(1); }); it('should handle multiple mappings with different configurations', () => { // Setup const reactCommonPath = '/path/to/ReactCommon'; const headersOutput = '/output/headers'; const mappings = { '/path/to/ReactCommon/react/renderer/core': { destination: '/output/headers/react/renderer/core', excludeFolders: ['tests'], preserveStructure: true, }, '/path/to/ReactCommon/turbomodule/core': { destination: '/output/headers/ReactCommon', excludeFolders: ['tests'], preserveStructure: false, }, }; const headerFiles1 = [ '/path/to/ReactCommon/react/renderer/core/Component.h', ]; const headerFiles2 = [ '/path/to/ReactCommon/turbomodule/core/TurboModule.h', ]; mockReactCommonMappings.mockReturnValue(mappings); mockUtils.listHeadersInFolder .mockReturnValueOnce(headerFiles1) .mockReturnValueOnce(headerFiles2); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactCommonHeaders(reactCommonPath, headersOutput); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledTimes(2); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactCommon/react/renderer/core/Component.h', '/output/headers/react/renderer/core/Component.h', ); expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactCommon/turbomodule/core/TurboModule.h', '/output/headers/ReactCommon/TurboModule.h', ); expect(result).toBe(2); }); it('should handle empty mappings from reactCommonMappings', () => { // Setup const reactCommonPath = '/path/to/ReactCommon'; const headersOutput = '/output/headers'; const mappings = {}; // Empty mappings mockReactCommonMappings.mockReturnValue(mappings); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactCommonHeaders(reactCommonPath, headersOutput); // Assert expect(mockReactCommonMappings).toHaveBeenCalledWith( reactCommonPath, headersOutput, ); expect(mockUtils.listHeadersInFolder).not.toHaveBeenCalled(); expect(mockUtils.setupSymlink).not.toHaveBeenCalled(); expect(result).toBe(0); }); it('should handle mapping with preserveStructure true', () => { // Setup const reactCommonPath = '/path/to/ReactCommon'; const headersOutput = '/output/headers'; const mappings = { '/path/to/ReactCommon/react/renderer/core': { destination: '/output/headers/react/renderer/core', excludeFolders: ['tests'], preserveStructure: true, }, }; const headerFiles = [ '/path/to/ReactCommon/react/renderer/core/subdir/Component.h', ]; mockReactCommonMappings.mockReturnValue(mappings); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactCommonHeaders(reactCommonPath, headersOutput); // Assert expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactCommon/react/renderer/core/subdir/Component.h', '/output/headers/react/renderer/core/subdir/Component.h', ); expect(result).toBe(1); }); it('should handle mapping with preserveStructure false (flattened)', () => { // Setup const reactCommonPath = '/path/to/ReactCommon'; const headersOutput = '/output/headers'; const mappings = { '/path/to/ReactCommon/turbomodule/core': { destination: '/output/headers/ReactCommon', excludeFolders: ['tests'], preserveStructure: false, }, }; const headerFiles = [ '/path/to/ReactCommon/turbomodule/core/subdir/TurboModule.h', ]; mockReactCommonMappings.mockReturnValue(mappings); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactCommonHeaders(reactCommonPath, headersOutput); // Assert expect(mockUtils.setupSymlink).toHaveBeenCalledWith( '/path/to/ReactCommon/turbomodule/core/subdir/TurboModule.h', '/output/headers/ReactCommon/TurboModule.h', ); expect(result).toBe(1); }); it('should handle listHeadersInFolder returning empty arrays for all mappings', () => { // Setup const reactCommonPath = '/path/to/ReactCommon'; const headersOutput = '/output/headers'; const mappings = { '/path/to/ReactCommon/react/renderer/core': { destination: '/output/headers/react/renderer/core', excludeFolders: ['tests'], preserveStructure: true, }, '/path/to/ReactCommon/turbomodule/core': { destination: '/output/headers/ReactCommon', excludeFolders: ['tests'], preserveStructure: false, }, }; mockReactCommonMappings.mockReturnValue(mappings); mockUtils.listHeadersInFolder.mockReturnValue([]); mockUtils.setupSymlink.mockImplementation(() => {}); // Execute const result = symlinkReactCommonHeaders(reactCommonPath, headersOutput); // Assert expect(mockUtils.listHeadersInFolder).toHaveBeenCalledTimes(2); expect(mockUtils.setupSymlink).not.toHaveBeenCalled(); expect(result).toBe(0); }); it('should handle setupSymlink throwing an error', () => { // Setup const reactCommonPath = '/path/to/ReactCommon'; const headersOutput = '/output/headers'; const mappings = { '/path/to/ReactCommon/react/renderer/core': { destination: '/output/headers/react/renderer/core', excludeFolders: ['tests'], preserveStructure: true, }, }; const headerFiles = [ '/path/to/ReactCommon/react/renderer/core/Component.h', ]; mockReactCommonMappings.mockReturnValue(mappings); mockUtils.listHeadersInFolder.mockReturnValue(headerFiles); mockUtils.setupSymlink.mockImplementation(() => { throw new Error('Setup failed'); }); // Execute and Assert - function should throw the error from setupSymlink expect(() => { symlinkReactCommonHeaders(reactCommonPath, headersOutput); }).toThrow('Setup failed'); }); it('should handle listHeadersInFolder throwing an error', () => { // Setup const reactCommonPath = '/path/to/ReactCommon'; const headersOutput = '/output/headers'; const mappings = { '/path/to/ReactCommon/react/renderer/core': { destination: '/output/headers/react/renderer/core', excludeFolders: ['tests'], preserveStructure: true, }, }; mockReactCommonMappings.mockReturnValue(mappings); mockUtils.listHeadersInFolder.mockImplementation(() => { throw new Error('List failed'); }); // Execute and Assert - function should throw the error from listHeadersInFolder expect(() => { symlinkReactCommonHeaders(reactCommonPath, headersOutput); }).toThrow('List failed'); }); });