diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index e1f545549f..4681d497ef 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -17,6 +17,7 @@ $injector.require("iOSProvisionService", "./services/ios-provision-service"); $injector.require("xcconfigService", "./services/xcconfig-service"); $injector.require("cocoapodsService", "./services/cocoapods-service"); +$injector.require("cocoaPodsPlatformManager", "./services/cocoapods-platform-manager"); $injector.require("projectTemplatesService", "./services/project-templates-service"); $injector.require("projectNameService", "./services/project-name-service"); diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 4af5382abc..aea94962e5 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -542,7 +542,28 @@ interface ICocoaPodsService { mergePodXcconfigFile(projectData: IProjectData, platformData: IPlatformData, opts: IRelease): Promise; } +interface ICocoaPodsPlatformManager { + addPlatformSection(projectData: IProjectData, podfilePlatformData: IPodfilePlatformData, projectPodfileContent: string): string; + removePlatformSection(moduleName: string, projectPodFileContent: string, podfilePath: string): string; + replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData }; +} + interface IRubyFunction { functionName: string; functionParameters?: string; } + +interface IPodfilePlatformData { + /** + * The content of the whole pod's platform row + */ + content: string; + /** + * The version of the pod's platform + */ + version: string; + /** + * The path to the pod's file + */ + path: string; +} diff --git a/lib/services/cocoapods-platform-manager.ts b/lib/services/cocoapods-platform-manager.ts new file mode 100644 index 0000000000..8f34fd4952 --- /dev/null +++ b/lib/services/cocoapods-platform-manager.ts @@ -0,0 +1,130 @@ +import { EOL } from "os"; +import * as path from "path"; +import * as semver from "semver"; +import { PODFILE_NAME } from "../constants"; + +export class CocoaPodsPlatformManager implements ICocoaPodsPlatformManager { + public addPlatformSection(projectData: IProjectData, podfilePlatformData: IPodfilePlatformData, projectPodfileContent: string): string { + const platformSectionData = this.getPlatformSectionData(projectPodfileContent); + if (platformSectionData && platformSectionData.podfilePlatformData) { + const shouldReplacePlatformSection = this.shouldReplacePlatformSection(projectData, platformSectionData.podfilePlatformData, podfilePlatformData); + if (shouldReplacePlatformSection) { + const newSection = this.buildPlatformSection(podfilePlatformData); + projectPodfileContent = projectPodfileContent.replace(platformSectionData.platformSectionContent, newSection); + } + } else { + projectPodfileContent += this.buildPlatformSection(podfilePlatformData); + } + + return projectPodfileContent; + } + + public removePlatformSection(moduleName: string, projectPodfileContent: string, podfilePath: string): string { + const platformSectionData = this.getPlatformSectionData(projectPodfileContent); + if (platformSectionData && platformSectionData.podfilePlatformData && platformSectionData.podfilePlatformData.path === podfilePath) { + const podfileContentRegExp = new RegExp(`# Begin Podfile - ([\\s\\S]*?)# End Podfile`, "mg"); + const allPodfiles = projectPodfileContent.match(podfileContentRegExp) || []; + const selectedPlatformData = this.selectPlatformDataFromProjectPodfile(allPodfiles); + const newPlatformSection = selectedPlatformData ? this.buildPlatformSection(selectedPlatformData) : ""; + const regExp = new RegExp(`\\r?\\n${platformSectionData.platformSectionContent}\\r?\\n`, "mg"); + projectPodfileContent = projectPodfileContent.replace(regExp, newPlatformSection); + } + + return projectPodfileContent; + } + + public replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData } { + let podfilePlatformData: IPodfilePlatformData = null; + const platformRowRegExp = new RegExp(`^\\s*?(platform\\b\\s*?\\:\\s*?ios\\b(?:,\\s*?['"](.+)['"])?)`, "gm"); + const replacedContent = podfileContent.replace(platformRowRegExp, (substring: string, firstGroup: string, secondGroup: string) => { + podfilePlatformData = { content: firstGroup, version: secondGroup, path: podfilePath }; + return `# ${substring.trim()}`; + }); + + return { replacedContent, podfilePlatformData }; + } + + private getPlatformSectionData(projectPodfileContent: string): { podfilePlatformData: IPodfilePlatformData, platformSectionContent: string } { + const platformSectionRegExp = new RegExp(`${this.getPlatformSectionHeader()} ([\\s\\S]*?)with (.*)[\\s\\S]*?${this.getPlatformSectionFooter()}`, "m"); + const match = platformSectionRegExp.exec(projectPodfileContent); + let result = null; + if (match && match[0]) { + result = { + platformSectionContent: match[0], + podfilePlatformData: { + path: match[1].trim(), + content: "", + version: match[2] + } + }; + } + + return result; + } + + private selectPlatformDataFromProjectPodfile(allPodfiles: string[]): IPodfilePlatformData { + const platformRowRegExp = new RegExp(`^\\s*?#\\s*?(platform\\b\\s*?\\:\\s*?ios\\b(?:,\\s*?['"](.+)['"])?)`, "m"); + const podfilePathRegExp = new RegExp(`# Begin Podfile - ([\\s\\S]*?)${EOL}`); + let selectedPlatformData: IPodfilePlatformData = null; + _.each(allPodfiles, podfileContent => { + const platformMatch = platformRowRegExp.exec(podfileContent); + const podfilePathMatch: any[] = podfilePathRegExp.exec(podfileContent) || []; + // platform without version -> select it with highest priority + if (platformMatch && platformMatch[0] && !platformMatch[2]) { + selectedPlatformData = { + version: null, + content: platformMatch[1], + path: podfilePathMatch[1] + }; + return false; + } + + // platform with version + if (platformMatch && platformMatch[0] && platformMatch[2]) { + if (!selectedPlatformData || semver.gt(semver.coerce(platformMatch[2]), semver.coerce(selectedPlatformData.version))) { + selectedPlatformData = { + version: platformMatch[2], + content: platformMatch[1], + path: podfilePathMatch[1] + }; + } + } + }); + + return selectedPlatformData; + } + + private shouldReplacePlatformSection(projectData: IProjectData, oldPodfilePlatformData: IPodfilePlatformData, currentPodfilePlatformData: IPodfilePlatformData): boolean { + // The selected platform should be replaced in the following cases: + // 1. When the pod file is from App_Resources and the selected platform is not from App_Resources + // 2. When the pod file is from App_Resources, the selected platform is also from App_Resources + // and the pod's version is greater than the selected one + // 3. When the pod file doesn't have platform's version -> `platform :ios` + // 4. When the pod file has a version greater than the selected platform's version + const appResourcesPodfilePath = path.join(projectData.getAppResourcesDirectoryPath(), "iOS", PODFILE_NAME); + const isFromAppResources = oldPodfilePlatformData.path !== appResourcesPodfilePath && currentPodfilePlatformData.path === appResourcesPodfilePath; + const isFromAppResourcesWithGreaterPlatformVersion = oldPodfilePlatformData.path === appResourcesPodfilePath && currentPodfilePlatformData.path === appResourcesPodfilePath && semver.gt(semver.coerce(currentPodfilePlatformData.version), semver.coerce(oldPodfilePlatformData.version)); + const isPodfileWithGreaterPlatformVersion = !currentPodfilePlatformData.version || semver.gt(semver.coerce(currentPodfilePlatformData.version), semver.coerce(oldPodfilePlatformData.version)); + const result = isFromAppResources || isFromAppResourcesWithGreaterPlatformVersion || isPodfileWithGreaterPlatformVersion; + return result; + } + + private buildPlatformSection(podfilePlatformData: IPodfilePlatformData) { + let result = `${this.getPlatformSectionHeader()} ${podfilePlatformData.path} with`; + if (podfilePlatformData.version) { + result += ` ${podfilePlatformData.version}`; + } + + result += `${EOL}${podfilePlatformData.content}${EOL}${this.getPlatformSectionFooter()}`; + return result; + } + + private getPlatformSectionHeader(): string { + return '# NativeScriptPlatformSection'; + } + + private getPlatformSectionFooter(): string { + return '# End NativeScriptPlatformSection'; + } +} +$injector.register("cocoaPodsPlatformManager", CocoaPodsPlatformManager); diff --git a/lib/services/cocoapods-service.ts b/lib/services/cocoapods-service.ts index 042970e5c4..b25a7daa13 100644 --- a/lib/services/cocoapods-service.ts +++ b/lib/services/cocoapods-service.ts @@ -6,7 +6,9 @@ export class CocoaPodsService implements ICocoaPodsService { private static PODFILE_POST_INSTALL_SECTION_NAME = "post_install"; private static INSTALLER_BLOCK_PARAMETER_NAME = "installer"; - constructor(private $fs: IFileSystem, + constructor( + private $cocoaPodsPlatformManager: ICocoaPodsPlatformManager, + private $fs: IFileSystem, private $childProcess: IChildProcess, private $errors: IErrors, private $xcprojService: IXcprojService, @@ -79,7 +81,7 @@ export class CocoaPodsService implements ICocoaPodsService { return; } - const { podfileContent, replacedFunctions } = this.buildPodfileContent(podfilePath, moduleName); + const { podfileContent, replacedFunctions, podfilePlatformData } = this.buildPodfileContent(podfilePath, moduleName); const pathToProjectPodfile = this.getProjectPodfilePath(nativeProjectPath); const projectPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.$fs.readText(pathToProjectPodfile).trim() : ""; @@ -92,19 +94,23 @@ export class CocoaPodsService implements ICocoaPodsService { finalPodfileContent = this.addPostInstallHook(replacedFunctions, finalPodfileContent, podfileContent); } + if (podfilePlatformData) { + finalPodfileContent = this.$cocoaPodsPlatformManager.addPlatformSection(projectData, podfilePlatformData, finalPodfileContent); + } + finalPodfileContent = `${podfileContent}${EOL}${finalPodfileContent}`; this.saveProjectPodfile(projectData, finalPodfileContent, nativeProjectPath); } } public removePodfileFromProject(moduleName: string, podfilePath: string, projectData: IProjectData, projectRoot: string): void { - if (this.$fs.exists(this.getProjectPodfilePath(projectRoot))) { let projectPodFileContent = this.$fs.readText(this.getProjectPodfilePath(projectRoot)); // Remove the data between #Begin Podfile and #EndPodfile const regExpToRemove = new RegExp(`${this.getPluginPodfileHeader(podfilePath)}[\\s\\S]*?${this.getPluginPodfileEnd()}`, "mg"); projectPodFileContent = projectPodFileContent.replace(regExpToRemove, ""); projectPodFileContent = this.removePostInstallHook(moduleName, projectPodFileContent); + projectPodFileContent = this.$cocoaPodsPlatformManager.removePlatformSection(moduleName, projectPodFileContent, podfilePath); const defaultPodfileBeginning = this.getPodfileHeader(projectData.projectName); const defaultContentWithPostInstallHook = `${defaultPodfileBeginning}${EOL}${this.getPostInstallHookHeader()}end${EOL}end`; @@ -226,13 +232,15 @@ export class CocoaPodsService implements ICocoaPodsService { return `${CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME} do |${CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME}|${EOL}`; } - private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[] } { + private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[], podfilePlatformData: IPodfilePlatformData } { const pluginPodfileContent = this.$fs.readText(pluginPodFilePath); - const { replacedContent, newFunctions: replacedFunctions } = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName); + const data = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName); + const { replacedContent, podfilePlatformData } = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath); return { podfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`, - replacedFunctions + replacedFunctions: data.newFunctions, + podfilePlatformData }; } diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index ec80a893b2..950df5bea4 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -985,7 +985,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f const projectPodfilePath = this.$cocoapodsService.getProjectPodfilePath(platformData.projectRoot); if (this.$fs.exists(projectPodfilePath)) { await this.$cocoapodsService.executePodInstall(platformData.projectRoot, this.$xcprojService.getXcodeprojPath(projectData, platformData)); - // The `pod install` command adds a new target to the .pbxproject. This target adds additional build phases to xcodebuild. + // The `pod install` command adds a new target to the .pbxproject. This target adds additional build phases to Xcode project. // Some of these phases relies on env variables (like PODS_PODFILE_DIR_PATH or PODS_ROOT). // These variables are produced from merge of pod's xcconfig file and project's xcconfig file. // So the correct order is `pod install` to be executed before merging pod's xcconfig file. diff --git a/test/cocoapods-service.ts b/test/cocoapods-service.ts index df8a0d64c5..d264c686d5 100644 --- a/test/cocoapods-service.ts +++ b/test/cocoapods-service.ts @@ -4,6 +4,8 @@ import { CocoaPodsService } from "../lib/services/cocoapods-service"; import { EOL } from "os"; import { LoggerStub, ErrorsStub } from "./stubs"; import { XcconfigService } from "../lib/services/xcconfig-service"; +import * as path from "path"; +import { CocoaPodsPlatformManager } from "../lib/services/cocoapods-platform-manager"; interface IMergePodfileHooksTestCase { input: string; @@ -24,6 +26,8 @@ function createTestInjector(): IInjector { testInjector.register("logger", LoggerStub); testInjector.register("config", {}); testInjector.register("xcconfigService", XcconfigService); + testInjector.register("projectData", ({})); + testInjector.register("cocoaPodsPlatformManager", CocoaPodsPlatformManager); return testInjector; } @@ -905,4 +909,232 @@ end` }); }); + + describe("remove duplicated platfoms from project podfile", () => { + const projectRoot = "my/project/platforms/ios"; + const projectPodfilePath = path.join(projectRoot, "testProjectPodfilePath"); + const testCases = [ + { + name: "should not change the Podfile when no platform", + pods: [{ + name: 'plugin1', + path: 'path/to/my/plugin1/platforms/ios/Podfile', + content: `pod 'Firebase', '~> 3.1'` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - path/to/my/plugin1/platforms/ios/Podfile +pod 'Firebase', '~> 3.1' +# End Podfile +end` + }, + { + name: "should not change the Podfile when there is only one platform", + pods: [{ + name: 'plugin2', + path: 'path/to/my/plugin2/platforms/ios/Podfile', + content: `platform :ios, '9.0'` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - path/to/my/plugin2/platforms/ios/Podfile +# platform :ios, '9.0' +# End Podfile + +# NativeScriptPlatformSection path/to/my/plugin2/platforms/ios/Podfile with 9.0 +platform :ios, '9.0' +# End NativeScriptPlatformSection +end` + }, + { + name: "should select the platform from Podfile in App_Resources", + pods: [{ + name: 'plugin1', + path: 'my/full/path/to/plugin1/platforms/ios/Podfile', + content: `platform :ios, '10.0'` + }, { + name: 'plugin2', + path: 'my/full/path/to/plugin2/platforms/ios/Podfile', + content: `pod 'myPod' ~> 0.3.4` + }, { + name: 'App_Resources', + path: 'my/full/path/to/app/App_Resources/iOS/Podfile', + content: `platform :ios, '9.0'` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - my/full/path/to/app/App_Resources/iOS/Podfile +# platform :ios, '9.0' +# End Podfile + +# Begin Podfile - my/full/path/to/plugin2/platforms/ios/Podfile +pod 'myPod' ~> 0.3.4 +# End Podfile + +# Begin Podfile - my/full/path/to/plugin1/platforms/ios/Podfile +# platform :ios, '10.0' +# End Podfile + +# NativeScriptPlatformSection my/full/path/to/app/App_Resources/iOS/Podfile with 9.0 +platform :ios, '9.0' +# End NativeScriptPlatformSection +end` + }, + { + name: "should select the platform with highest version from plugins when no Podfile in App_Resources", + pods: [{ + name: 'pluginWithPlatform', + path: 'node_modules/myFirstPluginWithPlatform/Podfile', + content: `platform :ios, '9.0'` + }, { + name: 'mySecondPluginWithPlatform', + path: 'node_modules/mySecondPluginWithPlatform/Podfile', + content: `platform :ios, '10.0'` + }, { + name: 'myPluginWithoutPlatform', + path: 'node_modules/myPluginWithoutPlatform/Podfile', + content: `pod 'myPod' ~> 0.3.4` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - node_modules/myPluginWithoutPlatform/Podfile +pod 'myPod' ~> 0.3.4 +# End Podfile + +# Begin Podfile - node_modules/mySecondPluginWithPlatform/Podfile +# platform :ios, '10.0' +# End Podfile + +# Begin Podfile - node_modules/myFirstPluginWithPlatform/Podfile +# platform :ios, '9.0' +# End Podfile + +# NativeScriptPlatformSection node_modules/mySecondPluginWithPlatform/Podfile with 10.0 +platform :ios, '10.0' +# End NativeScriptPlatformSection +end` + }, + { + name: "should select the platform without version when no Podfile in App_Resources", + pods: [{ + name: 'myPluginWithoutPlatform', + path: 'node_modules/myPluginWithoutPlatform/Podfile', + content: `pod 'myPod' ~> 0.3.4` + }, { + name: 'mySecondPluginWithPlatform', + path: 'node_modules/mySecondPluginWithPlatform/Podfile', + content: `platform :ios, '10.0'` + }, { + name: 'myFirstPluginWithPlatform', + path: 'node_modules/myFirstPluginWithPlatform/Podfile', + content: `platform :ios` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - node_modules/myFirstPluginWithPlatform/Podfile +# platform :ios +# End Podfile + +# Begin Podfile - node_modules/mySecondPluginWithPlatform/Podfile +# platform :ios, '10.0' +# End Podfile + +# Begin Podfile - node_modules/myPluginWithoutPlatform/Podfile +pod 'myPod' ~> 0.3.4 +# End Podfile# NativeScriptPlatformSection node_modules/myFirstPluginWithPlatform/Podfile with +platform :ios +# End NativeScriptPlatformSection +end` + }, + { + name: "should select platform from plugins when the podfile in App_Resources/iOS/Podfile has no platform", + pods: [{ + name: "mySecondPluginWithPlatform", + path: "node_modules/ mypath with spaces/mySecondPluginWithPlatform/Podfile", + content: `platform :ios, '10.0'` + }, { + name: "myPluginWithoutPlatform", + path: "node_modules/myPluginWithoutPlatform/Podfile", + content: `pod 'myPod' ~> 0.3.4` + }, { + name: "myFirstPluginWithPlatform", + path: "node_modules/myFirstPluginWithPlatform/Podfile", + content: `platform :ios, '11.0'` + }, { + name: "App_Resources", + path: "my/full/path/to/app/App_Resources/iOS/Podfile", + content: `pod: 'mySecondPlatformPod' ~> 2.0.0${EOL}pod: 'platformKit' ~> 1.0` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - my/full/path/to/app/App_Resources/iOS/Podfile +pod: 'mySecondPlatformPod' ~> 2.0.0 +pod: 'platformKit' ~> 1.0 +# End Podfile + +# Begin Podfile - node_modules/myFirstPluginWithPlatform/Podfile +# platform :ios, '11.0' +# End Podfile + +# Begin Podfile - node_modules/myPluginWithoutPlatform/Podfile +pod 'myPod' ~> 0.3.4 +# End Podfile + +# Begin Podfile - node_modules/ mypath with spaces/mySecondPluginWithPlatform/Podfile +# platform :ios, '10.0' +# End Podfile + +# NativeScriptPlatformSection node_modules/myFirstPluginWithPlatform/Podfile with 11.0 +platform :ios, '11.0' +# End NativeScriptPlatformSection +end` + } + ]; + + beforeEach(() => { + cocoapodsService.getProjectPodfilePath = () => projectPodfilePath; + }); + + _.each(testCases, testCase => { + it(testCase.name, async () => { + const podsPaths = testCase.pods.map(p => p.path); + let projectPodfileContent = ""; + + const projectData = testInjector.resolve("projectData"); + projectData.getAppResourcesDirectoryPath = () => "my/full/path/to/app/App_Resources"; + projectData.projectName = "projectName"; + + const fs = testInjector.resolve("fs"); + fs.exists = (filePath: string) => projectPodfilePath === filePath || _.includes(podsPaths, filePath); + fs.readText = (filePath: string) => { + if (filePath === projectPodfilePath) { + return projectPodfileContent; + } + + const pod = _.find(testCase.pods, p => p.path === filePath); + if (pod) { + return pod.content; + } + }; + fs.writeFile = (filePath: string, fileContent: string) => { + if (filePath === projectPodfilePath) { + projectPodfileContent = fileContent; + } + }; + cocoapodsService.removePodfileFromProject = () => ({}); + + for (const pod of testCase.pods) { + await cocoapodsService.applyPodfileToProject(pod.name, pod.path, projectData, projectPodfilePath); + } + + assert.deepEqual(projectPodfileContent, testCase.expectedProjectPodfileContent); + }); + }); + }); }); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index d5ec4e47b1..020b5b95b1 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -37,6 +37,7 @@ import { BUILD_XCCONFIG_FILE_NAME } from "../lib/constants"; import { ProjectDataStub } from "./stubs"; import { xcode } from "../lib/node/xcode"; import temp = require("temp"); +import { CocoaPodsPlatformManager } from "../lib/services/cocoapods-platform-manager"; temp.track(); class IOSSimulatorDiscoveryMock extends DeviceDiscovery { @@ -66,6 +67,7 @@ function createTestInjector(projectPath: string, projectName: string, xcode?: IX testInjector.register("iOSEntitlementsService", IOSEntitlementsService); testInjector.register("logger", LoggerLib.Logger); testInjector.register("options", OptionsLib.Options); + testInjector.register("cocoaPodsPlatformManager", CocoaPodsPlatformManager); const projectData = Object.assign({}, ProjectDataStub, { platformsDir: path.join(projectPath, "platforms"), projectName: projectName, @@ -384,26 +386,32 @@ describe("Cocoapods support", () => { projectData.podfilePath = basePodfilePath; - cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); + await cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); const projectPodfilePath = path.join(platformsFolderPath, "Podfile"); - assert.isTrue(fs.exists(projectPodfilePath)); + assert.isTrue(fs.exists(projectPodfilePath), `File ${projectPodfilePath} must exist as we have already applied Podfile to it.`); const actualProjectPodfileContent = fs.readText(projectPodfilePath); + const expectedPluginPodfileContent = ["source 'https://github.com/CocoaPods/Specs.git'", "# platform :ios, '8.1'", "pod 'GoogleMaps'"].join("\n"); + const expectedPlatformSection = [ + `# NativeScriptPlatformSection ${basePodfilePath} with 8.1`, + "platform :ios, '8.1'", + "# End NativeScriptPlatformSection", + ].join("\n"); const expectedProjectPodfileContent = ["use_frameworks!\n", `target "${projectName}" do`, `# Begin Podfile - ${basePodfilePath}`, - `${pluginPodfileContent}`, - "# End Podfile", + expectedPluginPodfileContent, + "# End Podfile\n", + expectedPlatformSection, "end"] .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); fs.deleteFile(basePodfilePath); - cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); - assert.isFalse(fs.exists(projectPodfilePath)); - + await cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); + assert.isFalse(fs.exists(projectPodfilePath), `The projectPodfilePath (${projectPodfilePath}) must not exist when all Podfiles have been deleted and project is prepared again. (i.e. CLI should delete the project Podfile in this case)`); }); it("adds plugin with Podfile", async () => { @@ -462,11 +470,18 @@ describe("Cocoapods support", () => { assert.isTrue(fs.exists(projectPodfilePath)); const actualProjectPodfileContent = fs.readText(projectPodfilePath); + const expectedPluginPodfileContent = ["source 'https://github.com/CocoaPods/Specs.git'", "# platform :ios, '8.1'", "pod 'GoogleMaps'"].join("\n"); + const expectedPlatformSection = [ + `# NativeScriptPlatformSection ${pluginPodfilePath} with 8.1`, + "platform :ios, '8.1'", + "# End NativeScriptPlatformSection", + ].join("\n"); const expectedProjectPodfileContent = ["use_frameworks!\n", `target "${projectName}" do`, `# Begin Podfile - ${pluginPodfilePath}`, - `${pluginPodfileContent}`, - "# End Podfile", + expectedPluginPodfileContent, + "# End Podfile\n", + expectedPlatformSection, "end"] .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); @@ -537,11 +552,18 @@ describe("Cocoapods support", () => { assert.isTrue(fs.exists(projectPodfilePath)); const actualProjectPodfileContent = fs.readText(projectPodfilePath); + const expectedPluginPodfileContent = ["source 'https://github.com/CocoaPods/Specs.git'", "# platform :ios, '8.1'", "pod 'GoogleMaps'"].join("\n"); + const expectedPlatformSection = [ + `# NativeScriptPlatformSection ${pluginPodfilePath} with 8.1`, + "platform :ios, '8.1'", + "# End NativeScriptPlatformSection", + ].join("\n"); const expectedProjectPodfileContent = ["use_frameworks!\n", `target "${projectName}" do`, `# Begin Podfile - ${pluginPodfilePath}`, - `${pluginPodfileContent}`, - "# End Podfile", + expectedPluginPodfileContent, + "# End Podfile\n", + expectedPlatformSection, "end"] .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); @@ -1386,6 +1408,7 @@ describe("handleNativeDependenciesChange", () => { cocoapodsService.executePodInstall = async () => executedCocoapodsMethods.push("podInstall"); cocoapodsService.mergePodXcconfigFile = async () => executedCocoapodsMethods.push("podMerge"); cocoapodsService.applyPodfileFromAppResources = async () => ({}); + cocoapodsService.removeDuplicatedPlatfomsFromProjectPodFile = async () => ({}); cocoapodsService.getProjectPodfilePath = () => projectPodfilePath; const fs = testInjector.resolve("fs");