UNPKG

react-native-integrate

Version:

Automate integration of additional code into React Native projects

735 lines (734 loc) 29.8 kB
"use strict"; /* eslint-disable @typescript-eslint/no-unsafe-call */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const { mockFs } = require('../../mocks/mockAll'); const mockWaitForFile = jest.spyOn(require('../../../utils/waitForFile'), 'waitForFile'); const mockAddPackageUpgradeFile = jest.spyOn(require('../../../utils/packageUpgradeConfig'), 'addPackageUpgradeFile'); const path_1 = __importDefault(require("path")); const constants_1 = require("../../../constants"); const xcodeTask_1 = require("../../../tasks/xcode/xcodeTask"); const getIosProjectPath_1 = require("../../../utils/getIosProjectPath"); const getProjectPath_1 = require("../../../utils/getProjectPath"); const xcode_context_1 = require("../../../utils/xcode.context"); const variables_1 = require("../../../variables"); const mockAll_1 = require("../../mocks/mockAll"); const mockPbxProjTemplate_1 = require("../../mocks/mockPbxProjTemplate"); const xcode_1 = __importDefault(require("xcode")); const mockXCSchemeTemplate_1 = require("../../mocks/mockXCSchemeTemplate"); describe('xcodeTask', () => { beforeEach(() => { mockAll_1.mockPrompter.text.mockImplementationOnce(() => ''); mockWaitForFile.mockImplementationOnce(() => { return Promise.resolve(true); }); mockAddPackageUpgradeFile.mockImplementationOnce(() => { /* empty */ }); }); it('should not change project', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const contentBefore = proj.writeSync(); const task = { task: 'xcode', actions: [ { random: 'value', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toEqual(contentBefore); }); it('should add notification service to project', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); mockAll_1.mockPrompter.text.mockReset().mockImplementation(() => 'test'); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { name: 'notification.service', addTarget: 'test', type: 'notification-service', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/\{.*?\bNotificationService\.m.*?}/s); expect(variables_1.variables.get('notification.service.target')).toEqual('test'); mockAll_1.mockPrompter.log.message.mockReset(); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockAll_1.mockPrompter.log.message).toHaveBeenCalledWith(expect.stringContaining('skipped adding target')); }); it('should add multiple notification services to project', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { name: 'noti', addTarget: 'test', type: 'notification-service', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/\{.*?\bNotificationService\.m.*?}/s); mockAll_1.mockPrompter.log.message.mockReset(); mockAll_1.mockPrompter.text.mockReset().mockImplementationOnce(() => 'test2'); const task2 = { task: 'xcode', actions: [ { name: 'noti', addTarget: 'test2', type: 'notification-service', }, ], }; const children = proj.getPBXGroupByKey(proj.getFirstProject().firstProject.mainGroup).children; children.splice(children.findIndex(x => x.comment == 'Products'), 1); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task2, content: proj, packageName: 'test-package', }); expect(mockAll_1.mockPrompter.log.message).not.toHaveBeenCalledWith(expect.stringContaining('skipped adding target')); }); it('should add notification content to project', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { name: 'noti', addTarget: 'test', type: 'notification-content', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/\{.*?\bNotificationViewController\.m.*?}/s); mockAll_1.mockPrompter.log.message.mockReset(); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockAll_1.mockPrompter.log.message).toHaveBeenCalledWith(expect.stringContaining('skipped adding target')); }); it('should add notification content to project even if products section does not exist', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate.replace(/\/\* Products \*\//g, '/* SomethingElse */')); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { name: 'noti', addTarget: 'test', type: 'notification-content', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/\{.*?\bNotificationViewController\.m.*?}/s); }); it('should add notification content to project', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { name: 'noti', addTarget: 'test', type: 'notification-content', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/\{.*?\bNotificationViewController\.m.*?}/s); mockAll_1.mockPrompter.log.message.mockReset(); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockAll_1.mockPrompter.log.message).toHaveBeenCalledWith(expect.stringContaining('skipped adding target')); }); it('should add capabilities to project', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { addCapability: 'push', target: 'ReactNativeCliTemplates', }, { addCapability: 'groups', target: 'main', groups: ['group.test'], }, { addCapability: 'domains', target: 'main', domains: ['applinks:domain.test'], }, { addCapability: 'keychain-sharing', target: 'main', groups: ['group.test'], }, { addCapability: 'background-mode', target: 'main', modes: ['fetch'], }, { addCapability: 'game-controllers', target: 'main', controllers: ['directional'], }, { addCapability: 'maps', target: 'main', routing: ['car', 'bus'], }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/\{.*?\bReactNativeCliTemplates\.entitlements.*?}/s); const targetName = 'ReactNativeCliTemplates'; const entitlementsContent = mockFs.readFileSync(path_1.default.join((0, getProjectPath_1.getProjectPath)(), 'ios', targetName, targetName + '.entitlements')); const infoContent = mockFs.readFileSync(path_1.default.join((0, getProjectPath_1.getProjectPath)(), 'ios', targetName, 'Info.plist')); expect(entitlementsContent).toContain('aps-environment'); expect(entitlementsContent).toContain('com.apple.security.application-groups'); expect(entitlementsContent).toContain('keychain-access-groups'); expect(infoContent).toContain('UIBackgroundModes'); expect(infoContent).toContain('DirectionalGamepad'); expect(infoContent).toContain('MKDirectionsModeBus'); }); it('should add capabilities to project with path of target', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const targetName = 'ReactNativeCliTemplates'; const mock = jest.spyOn(xcode_1.default.project.prototype, 'getPBXGroupByKey'); mock.mockReturnValueOnce({ name: undefined, path: 'path/' + targetName, children: [], }); const task = { task: 'xcode', actions: [ { addCapability: 'groups', target: 'main', groups: ['group.test'], }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/\{.*?\bReactNativeCliTemplates\.entitlements.*?}/s); const entitlementsContent = mockFs.readFileSync(path_1.default.join((0, getProjectPath_1.getProjectPath)(), 'ios', targetName, targetName + '.entitlements')); expect(entitlementsContent).toContain('com.apple.security.application-groups'); mock.mockRestore(); }); it('should add resource to root', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { addFile: 'GoogleService-Info.plist', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/83CBB9F61A601CBA00E9B192 = \{.*?GoogleService-Info\.plist.*?}/s); mockAll_1.mockPrompter.log.message.mockReset(); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockAll_1.mockPrompter.log.message).toHaveBeenCalledWith(expect.stringContaining('skipped adding resource')); }); it('should set higher deployment version of main', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); xcode_context_1.xcodeContext.set(proj); proj.parseSync(); const task = { task: 'xcode', actions: [ { setDeploymentVersion: '13.0', target: 'root', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(variables_1.variables.get('IOS_DEPLOYMENT_VERSION')).toEqual('13.0'); xcode_context_1.xcodeContext.clear(); }); it('should set higher deployment version of new added extension', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); xcode_context_1.xcodeContext.set(proj); proj.parseSync(); const task = { task: 'xcode', actions: [ { name: 'notification.service', addTarget: 'test', type: 'notification-service', }, { setDeploymentVersion: '13.0', target: 'test', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(proj.getBuildProperty('IPHONEOS_DEPLOYMENT_TARGET', 'Release', '"test"')).toEqual('13.0'); xcode_context_1.xcodeContext.clear(); }); it('should set lower deployment version of main', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); xcode_context_1.xcodeContext.set(proj); proj.parseSync(); const task = { task: 'xcode', actions: [ { setDeploymentVersion: '13.0', target: 'main', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(variables_1.variables.get('IOS_DEPLOYMENT_VERSION')).toEqual('13.0'); xcode_context_1.xcodeContext.clear(); }); it('should not change deployment version when current is higher than minimum', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); xcode_context_1.xcodeContext.set(proj); proj.parseSync(); const task = { task: 'xcode', actions: [ { setDeploymentVersion: { min: '8.0' }, target: 'main', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(variables_1.variables.get('IOS_DEPLOYMENT_VERSION')).toEqual('10.0'); xcode_context_1.xcodeContext.clear(); }); it('should change deployment version to minimum', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); xcode_context_1.xcodeContext.set(proj); proj.parseSync(); const task = { task: 'xcode', actions: [ { setDeploymentVersion: { min: '13.0' }, target: 'main', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(variables_1.variables.get('IOS_DEPLOYMENT_VERSION')).toEqual('13.0'); xcode_context_1.xcodeContext.clear(); }); it('should add configuration', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); let proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { addConfiguration: 'TEST=true', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); let content = proj.writeSync(); const configFilePath = path_1.default.join((0, getProjectPath_1.getProjectPath)(), 'ios', constants_1.Constants.XCConfig_FILE_NAME); expect(mockFs.readFileSync(configFilePath)).toBe('TEST=true\n'); expect(content).toMatch(/baseConfigurationReference = .*? Config\.xcconfig \*\/;/s); mockAll_1.mockPrompter.log.message.mockReset(); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockAll_1.mockPrompter.log.message).toHaveBeenCalledWith(expect.stringContaining('code already exists')); mockFs.writeFileSync(configFilePath, 'TEST=false'); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockFs.readFileSync(configFilePath)).toBe('TEST=false\nTEST=true\n'); content = proj.writeSync(); content = content.replace(/baseConfigurationReference/g, 'random'); mockFs.writeFileSync(pbxFilePath, content); proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); mockAll_1.mockPrompter.log.message.mockReset(); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockAll_1.mockPrompter.log.message).toHaveBeenCalledWith(expect.stringContaining('skipped adding resource')); }); it('should add pre build run script action', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { addPreBuildRunScriptAction: 'TESTSCRIPT();', }, ], }; const projectName = (0, getIosProjectPath_1.getIosProjectName)(); const iosProjectPath = (0, getIosProjectPath_1.getIosProjectPath)(); const schemePath = path_1.default.join(iosProjectPath + '.xcodeproj', 'xcshareddata', 'xcschemes', `${projectName}.xcscheme`); mockFs.writeFileSync(schemePath, mockXCSchemeTemplate_1.mockXCSchemeTemplate); mockAll_1.mockPrompter.log.message.mockReset(); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockFs.readFileSync(schemePath)).toMatch('TESTSCRIPT();'); await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); expect(mockAll_1.mockPrompter.log.message).toHaveBeenCalledWith(expect.stringContaining('code already exists')); }); it('should add resource to main', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { addFile: 'GoogleService-Info.plist', target: 'main', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/ReactNativeCliTemplates \*\/ = \{.*?GoogleService-Info\.plist.*?}/s); }); it('should skip if condition not met', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { when: { test: 'random' }, addFile: 'GoogleService-Info.plist', target: 'main', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).not.toMatch(/ReactNativeCliTemplates \*\/ = \{.*?GoogleService-Info\.plist.*?}/s); }); it('should add resource to custom group', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); const task = { task: 'xcode', actions: [ { addFile: 'GoogleService-Info.plist', target: 'Resources', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/Resources \*\/ = \{.*?GoogleService-Info\.plist.*?}/s); }); it('should add resource to root with no resources group', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); proj.removePbxGroup('Resources'); const task = { task: 'xcode', actions: [ { addFile: 'GoogleService-Info.plist', }, ], }; await (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', }); const content = proj.writeSync(); expect(content).toMatch(/83CBB9F61A601CBA00E9B192 = \{.*?GoogleService-Info\.plist.*?}/s); }); it('should throw on random error', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); mockWaitForFile.mockReset().mockImplementationOnce(() => { throw new Error('some random error'); }); const proj = xcode_1.default.project(pbxFilePath); proj.parseSync(); proj.removePbxGroup('Resources'); const task = { task: 'xcode', actions: [ { addFile: 'GoogleService-Info.plist', }, ], }; await expect(() => (0, xcodeTask_1.xcodeTask)({ configPath: 'path/to/config', task: task, content: proj, packageName: 'test-package', })).rejects.toThrowError('random error'); }); describe('runTask', () => { it('should read and write plist file', async () => { const pbxFilePath = (0, getIosProjectPath_1.getPbxProjectPath)(); mockFs.writeFileSync(pbxFilePath, mockPbxProjTemplate_1.mockPbxProjTemplate); const task = { task: 'xcode', actions: [ { addFile: 'GoogleService-Info.plist', target: 'main', }, ], }; await (0, xcodeTask_1.runTask)({ configPath: 'path/to/config', task: task, packageName: 'test-package', }); const content = mockFs.readFileSync(pbxFilePath); expect(content).toMatch(/ReactNativeCliTemplates \*\/ = \{.*?GoogleService-Info\.plist.*?}/s); }); it('should throw when plist does not exist', async () => { const task = { task: 'xcode', actions: [ { addFile: 'GoogleService-Info.plist', target: 'main', }, ], }; // noinspection SpellCheckingInspection await expect(() => (0, xcodeTask_1.runTask)({ configPath: 'path/to/config', task: task, packageName: 'test-package', })).rejects.toThrowError('project.pbxproj file not found'); }); it('should throw when project does not exist', async () => { const mock = jest.spyOn(mockFs, 'readdirSync').mockImplementation(() => { throw new Error('Directory not found'); }); const task = { task: 'xcode', actions: [ { addFile: 'GoogleService-Info.plist', target: 'main', }, ], }; await expect(() => (0, xcodeTask_1.runTask)({ configPath: 'path/to/config', task: task, packageName: 'test-package', })).rejects.toThrowError('project not found'); mock.mockRestore(); }); }); });