From 6a117e7a2f492f6d4fb66ffdeea912dc5f848919 Mon Sep 17 00:00:00 2001 From: Panayot Cankov Date: Tue, 11 Apr 2017 11:52:42 +0300 Subject: [PATCH] Fix some issues about the provision switch --- lib/bootstrap.ts | 3 + lib/commands/emulate.ts | 2 +- lib/declarations.d.ts | 2 +- lib/definitions/project-changes.d.ts | 7 +- lib/definitions/project.d.ts | 6 + lib/node/pbxproj-dom-xcode.ts | 7 + lib/node/xcode.ts | 11 ++ lib/services/android-project-service.ts | 4 + lib/services/ios-project-service.ts | 67 +++++-- lib/services/ios-provision-service.ts | 2 +- lib/services/platform-service.ts | 2 +- lib/services/project-changes-service.ts | 23 +-- package.json | 4 +- test/ios-project-service.ts | 241 +++++++++++++++++++++++- test/mocha.opts | 1 + test/npm-support.ts | 3 +- test/platform-service.ts | 6 +- test/project-changes-service.ts | 33 +++- test/stubs.ts | 3 + test/tns-appstore-upload.ts | 23 ++- 20 files changed, 405 insertions(+), 45 deletions(-) create mode 100644 lib/node/pbxproj-dom-xcode.ts create mode 100644 lib/node/xcode.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index eb70d055e6..25ec914ba8 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -124,6 +124,9 @@ $injector.require("projectChangesService", "./services/project-changes-service") $injector.require("emulatorPlatformService", "./services/emulator-platform-service"); +$injector.require("pbxprojDomXcode", "./node/pbxproj-dom-xcode"); +$injector.require("xcode", "./node/xcode"); + $injector.require("staticConfig", "./config"); $injector.require("requireService", "./services/require-service"); diff --git a/lib/commands/emulate.ts b/lib/commands/emulate.ts index dfb25faf2c..51cb3068ef 100644 --- a/lib/commands/emulate.ts +++ b/lib/commands/emulate.ts @@ -27,7 +27,7 @@ export class EmulateCommandBase { keyStorePassword: this.$options.keyStorePassword, keyStorePath: this.$options.keyStorePath }; - return this.$platformService.emulatePlatform(args[0], appFilesUpdaterOptions, emulateOptions, this.$projectData, this.$options.provision); + return this.$platformService.emulatePlatform(args[0], appFilesUpdaterOptions, emulateOptions, this.$projectData, this.$options); } } diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index ccb24e46f6..5fb04bf394 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -281,7 +281,7 @@ interface IClean { } interface IProvision { - provision: any; + provision: string; } interface ITeamIdentifier { diff --git a/lib/definitions/project-changes.d.ts b/lib/definitions/project-changes.d.ts index cd5bae3cfa..ceefe2b371 100644 --- a/lib/definitions/project-changes.d.ts +++ b/lib/definitions/project-changes.d.ts @@ -15,8 +15,11 @@ interface IProjectChangesInfo { configChanged: boolean; packageChanged: boolean; nativeChanged: boolean; - hasChanges: boolean; - changesRequireBuild: boolean; + signingChanged: boolean; + + readonly hasChanges: boolean; + readonly changesRequireBuild: boolean; + readonly changesRequirePrepare: boolean; } interface IProjectChangesOptions extends IAppFilesUpdaterOptions, IProvision {} diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index fd44de57ef..50c2e58acc 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -258,6 +258,12 @@ interface IPlatformProjectService extends NodeJS.EventEmitter { * @returns {void} */ cleanProject(projectRoot: string, projectData: IProjectData): Promise + + /** + * Check the current state of the project, and validate against the options. + * If there are parts in the project that are inconsistent with the desired options, marks them in the changeset flags. + */ + checkForChanges(changeset: IProjectChangesInfo, options: IProjectChangesOptions, projectData: IProjectData): void; } interface IAndroidProjectPropertiesManager { diff --git a/lib/node/pbxproj-dom-xcode.ts b/lib/node/pbxproj-dom-xcode.ts new file mode 100644 index 0000000000..f7bb3a80d7 --- /dev/null +++ b/lib/node/pbxproj-dom-xcode.ts @@ -0,0 +1,7 @@ +import * as pbxprojDomXcodeModule from "pbxproj-dom/xcode"; + +declare global { + type IPbxprojDomXcode = typeof pbxprojDomXcodeModule; +} + +$injector.register("pbxprojDomXcode", pbxprojDomXcodeModule); diff --git a/lib/node/xcode.ts b/lib/node/xcode.ts new file mode 100644 index 0000000000..0e306d8a1a --- /dev/null +++ b/lib/node/xcode.ts @@ -0,0 +1,11 @@ +import * as xcode from "xcode"; + +declare global { + type IXcode = typeof xcode; + export namespace IXcode { + export type project = typeof xcode.project; + export interface Options extends xcode.Options {} // tslint:disable-line + } +} + +$injector.register("xcode", xcode); diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index dd0e3d9dbe..a45f50f2a1 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -438,6 +438,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject await adb.executeShellCommand(["rm", "-rf", deviceRootPath]); } + public checkForChanges(changesInfo: IProjectChangesInfo, options: IProjectChangesOptions, projectData: IProjectData): void { + // Nothing android specific to check yet. + } + private _canUseGradle: boolean; private canUseGradle(projectData: IProjectData, frameworkVersion?: string): boolean { if (!this._canUseGradle) { diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index ab138c670c..c39d6c013d 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -2,7 +2,6 @@ import * as path from "path"; import * as shell from "shelljs"; import * as os from "os"; import * as semver from "semver"; -import * as xcode from "xcode"; import * as constants from "../constants"; import * as helpers from "../common/helpers"; import { attachAwaitDetach } from "../common/helpers"; @@ -11,7 +10,6 @@ import { PlistSession } from "plist-merge-patch"; import { EOL } from "os"; import * as temp from "temp"; import * as plist from "plist"; -import { Xcode } from "pbxproj-dom/xcode"; import { IOSProvisionService } from "./ios-provision-service"; export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService { @@ -42,7 +40,9 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $pluginVariablesService: IPluginVariablesService, private $xcprojService: IXcprojService, private $iOSProvisionService: IOSProvisionService, - private $sysInfo: ISysInfo) { + private $sysInfo: ISysInfo, + private $pbxprojDomXcode: IPbxprojDomXcode, + private $xcode: IXcode) { super($fs, $projectDataService); } @@ -379,10 +379,9 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ await this.createIpa(projectRoot, projectData, buildConfig); } - private async setupSigningFromProvision(projectRoot: string, projectData: IProjectData, provision?: any): Promise { + private async setupSigningFromProvision(projectRoot: string, projectData: IProjectData, provision?: string): Promise { if (provision) { - const pbxprojPath = path.join(projectRoot, projectData.projectName + ".xcodeproj", "project.pbxproj"); - const xcode = Xcode.open(pbxprojPath); + const xcode = this.$pbxprojDomXcode.Xcode.open(this.getPbxProjPath(projectData)); const signing = xcode.getSigning(projectData.projectName); let shouldUpdateXcode = false; @@ -399,8 +398,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ } if (shouldUpdateXcode) { - // This is slow, it read through 260 mobileprovision files on my machine and does quite some checking whether provisioning profiles and devices will match. - // That's why we try to avoid id by checking in the Xcode first. const pickStart = Date.now(); const mobileprovision = await this.$iOSProvisionService.pick(provision, projectData.projectId); const pickEnd = Date.now(); @@ -428,11 +425,16 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ } private async setupSigningForDevice(projectRoot: string, buildConfig: IiOSBuildConfig, projectData: IProjectData): Promise { - const pbxprojPath = path.join(projectRoot, projectData.projectName + ".xcodeproj", "project.pbxproj"); - const xcode = Xcode.open(pbxprojPath); + const xcode = this.$pbxprojDomXcode.Xcode.open(this.getPbxProjPath(projectData)); const signing = xcode.getSigning(projectData.projectName); - if ((this.readXCConfigProvisioningProfile(projectData) || this.readXCConfigProvisioningProfileForIPhoneOs(projectData)) && (!signing || signing.style !== "Manual")) { + const hasProvisioningProfileInXCConfig = + this.readXCConfigProvisioningProfileSpecifierForIPhoneOs(projectData) || + this.readXCConfigProvisioningProfileSpecifier(projectData) || + this.readXCConfigProvisioningProfileForIPhoneOs(projectData) || + this.readXCConfigProvisioningProfile(projectData); + + if (hasProvisioningProfileInXCConfig && (!signing || signing.style !== "Manual")) { xcode.setManualSigningStyle(projectData.projectName); xcode.save(); } else if (!buildConfig.provision && !(signing && signing.style === "Manual" && !buildConfig.teamId)) { @@ -490,7 +492,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ let frameworkBinaryPath = path.join(frameworkPath, frameworkName); let isDynamic = _.includes((await this.$childProcess.spawnFromEvent("otool", ["-Vh", frameworkBinaryPath], "close")).stdout, " DYLIB "); - let frameworkAddOptions: xcode.Options = { customFramework: true }; + let frameworkAddOptions: IXcode.Options = { customFramework: true }; if (isDynamic) { frameworkAddOptions["embed"] = true; @@ -623,7 +625,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f if (provision) { let projectRoot = path.join(projectData.platformsDir, "ios"); - await this.setupSigningFromProvision(projectRoot, provision); + await this.setupSigningFromProvision(projectRoot, projectData, provision); } let project = this.createPbxProj(projectData); @@ -787,7 +789,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private createPbxProj(projectData: IProjectData): any { - let project = new xcode.project(this.getPbxProjPath(projectData)); + let project = new this.$xcode.project(this.getPbxProjPath(projectData)); project.parseSync(); return project; @@ -844,6 +846,35 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return Promise.resolve(); } + public checkForChanges(changesInfo: IProjectChangesInfo, options: IProjectChangesOptions, projectData: IProjectData): void { + const provision = options.provision; + if (provision !== undefined) { + // Check if the native project's signing is set to the provided provision... + const pbxprojPath = this.getPbxProjPath(projectData); + + if (this.$fs.exists(pbxprojPath)) { + const xcode = this.$pbxprojDomXcode.Xcode.open(pbxprojPath); + const signing = xcode.getSigning(projectData.projectName); + if (signing && signing.style === "Manual") { + for (let name in signing.configurations) { + let config = signing.configurations[name]; + if (config.uuid !== provision && config.name !== provision) { + changesInfo.signingChanged = true; + break; + } + } + } else { + // Specifying provisioning profile requires "Manual" signing style. + // If the current signing style was not "Manual" it was probably "Automatic" or, + // it was not uniform for the debug and release build configurations. + changesInfo.signingChanged = true; + } + } else { + changesInfo.signingChanged = true; + } + } + } + private getAllLibsForPluginWithFileExtension(pluginData: IPluginData, fileExtension: string): string[] { let filterCallback = (fileName: string, pluginPlatformsFolderPath: string) => path.extname(fileName) === fileExtension; return this.getAllNativeLibrariesForPlugin(pluginData, IOSProjectService.IOS_PLATFORM_NAME, filterCallback); @@ -1186,6 +1217,14 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return this.readXCConfig("PROVISIONING_PROFILE[sdk=iphoneos*]", projectData); } + private readXCConfigProvisioningProfileSpecifier(projectData: IProjectData): string { + return this.readXCConfig("PROVISIONING_PROFILE_SPECIFIER", projectData); + } + + private readXCConfigProvisioningProfileSpecifierForIPhoneOs(projectData: IProjectData): string { + return this.readXCConfig("PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]", projectData); + } + private async getDevelopmentTeam(projectData: IProjectData, teamId?: string): Promise { teamId = teamId || this.readTeamId(projectData); diff --git a/lib/services/ios-provision-service.ts b/lib/services/ios-provision-service.ts index 3ab4522a53..a8d2db581f 100644 --- a/lib/services/ios-provision-service.ts +++ b/lib/services/ios-provision-service.ts @@ -29,7 +29,7 @@ export class IOSProvisionService { function formatSupportedDeviceCount(prov: mobileprovision.provision.MobileProvision) { if (devices.length > 0 && prov.Type === "Development") { - return prov.ProvisionedDevices.reduce((count, device) => count + (devices.indexOf(device) >= 0 ? 1 : 0), 0) + "/" + devices.length + " targets"; + return prov.ProvisionedDevices.filter(device => devices.indexOf(device) >= 0).length + "/" + devices.length + " targets"; } else { return ""; } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 633bfd55f9..4ed171abb8 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -290,7 +290,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } } - if (!changesInfo || changesInfo.appResourcesChanged) { + if (!changesInfo || changesInfo.changesRequirePrepare) { await this.copyAppFiles(platform, appFilesUpdaterOptions, projectData); this.copyAppResources(platform, projectData); await platformData.platformProjectService.prepareProject(projectData, platformSpecificData); diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index befe87b170..c28b4272b8 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -11,13 +11,15 @@ class ProjectChangesInfo implements IProjectChangesInfo { public configChanged: boolean; public packageChanged: boolean; public nativeChanged: boolean; + public signingChanged: boolean; public get hasChanges(): boolean { return this.packageChanged || this.appFilesChanged || this.appResourcesChanged || this.modulesChanged || - this.configChanged; + this.configChanged || + this.signingChanged; } public get changesRequireBuild(): boolean { @@ -25,6 +27,11 @@ class ProjectChangesInfo implements IProjectChangesInfo { this.appResourcesChanged || this.nativeChanged; } + + public get changesRequirePrepare(): boolean { + return this.appResourcesChanged || + this.signingChanged; + } } export class ProjectChangesService implements IProjectChangesService { @@ -75,16 +82,10 @@ export class ProjectChangesService implements IProjectChangesService { ]); } } - if (platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { - const nextCommandProvisionUUID = projectChangesOptions.provision; - // We should consider reading here the provisioning profile UUID from the xcodeproj and xcconfig. - const prevProvisionUUID = this._prepareInfo.iOSProvisioningProfileUUID; - if (nextCommandProvisionUUID !== prevProvisionUUID) { - this._changesInfo.nativeChanged = true; - this._changesInfo.configChanged = true; - this._prepareInfo.iOSProvisioningProfileUUID = nextCommandProvisionUUID; - } - } + + let projectService = platformData.platformProjectService; + projectService.checkForChanges(this._changesInfo, projectChangesOptions, projectData); + if (projectChangesOptions.bundle !== this._prepareInfo.bundle || projectChangesOptions.release !== this._prepareInfo.release) { this._changesInfo.appFilesChanged = true; this._changesInfo.appResourcesChanged = true; diff --git a/package.json b/package.json index 9b21ed3f19..38a1455b1f 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "mute-stream": "0.0.5", "open": "0.0.5", "osenv": "0.1.3", - "pbxproj-dom": "1.0.9", + "pbxproj-dom": "1.0.11", "plist": "1.1.0", "plist-merge-patch": "0.0.9", "plistlib": "0.2.1", @@ -98,8 +98,8 @@ "grunt-tslint": "4.0.0", "istanbul": "0.4.5", "mocha": "3.1.2", - "mocha-typescript": "^1.0.4", "should": "7.0.2", + "source-map-support": "^0.4.14", "tslint": "4.3.1", "typescript": "2.1.4" }, diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index abf9288b69..7ab5b0252a 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -24,6 +24,7 @@ import { PluginVariablesHelper } from "../lib/common/plugin-variables-helper"; import { Utils } from "../lib/common/utils"; import { CocoaPodsService } from "../lib/services/cocoapods-service"; import { assert } from "chai"; +import { IOSProvisionService } from "../lib/services/ios-provision-service"; import temp = require("temp"); temp.track(); @@ -85,6 +86,14 @@ function createTestInjector(projectPath: string, projectName: string): IInjector testInjector.register("androidProcessService", {}); testInjector.register("processService", {}); testInjector.register("sysInfo", {}); + testInjector.register("pbxprojDomXcode", {}); + testInjector.register("xcode", { + project: class { + constructor() { /* */ } + parseSync() { /* */ } + pbxGroupByName() { /* */ } + } + }); return testInjector; } @@ -416,7 +425,7 @@ describe("Static libraries support", () => { return; } - let projectName = "projectDirectory"; + let projectName = "TNSApp"; let projectPath = temp.mkdirSync(projectName); let libraryName = "testLibrary1"; let headers = ["TestHeader1.h", "TestHeader2.h"]; @@ -486,3 +495,233 @@ describe("Relative paths", () => { assert.equal(result, path.join("..", "..", "sub", "path")); }); }); + +describe("iOS Project Service Signing", () => { + let testInjector: IInjector; + let projectName: string; + let projectDirName: string; + let projectPath: string; + let files: any; + let iOSProjectService: IPlatformProjectService; + let projectData: any; + let pbxproj: string; + let iOSProvisionService: IOSProvisionService; + let pbxprojDomXcode: IPbxprojDomXcode; + + beforeEach(() => { + files = {}; + projectName = "TNSApp" + Math.ceil(Math.random() * 1000); + projectDirName = projectName + "Dir"; + projectPath = temp.mkdirSync(projectDirName); + testInjector = createTestInjector(projectPath, projectDirName); + testInjector.register("fs", { + files: {}, + readJson(path: string): any { + if (this.exists(path)) { + return JSON.stringify(files[path]); + } else { + return null; + } + }, + exists(path: string): boolean { + return path in files; + } + }); + testInjector.register("pbxprojDomXcode", { Xcode: {} }); + pbxproj = path.join(projectPath, `platforms/ios/${projectDirName}.xcodeproj/project.pbxproj`); + iOSProjectService = testInjector.resolve("iOSProjectService"); + iOSProvisionService = testInjector.resolve("iOSProvisionService"); + pbxprojDomXcode = testInjector.resolve("pbxprojDomXcode"); + projectData = testInjector.resolve("projectData"); + iOSProvisionService.pick = async (uuidOrName: string, projId: string) => { + return ({ + "NativeScriptDev": { + Name: "NativeScriptDev", + CreationDate: null, + ExpirationDate: null, + TeamName: "Telerik AD", + TeamIdentifier: ["TKID101"], + ProvisionedDevices: [], + Entitlements: { + "application-identifier": "*", + "com.apple.developer.team-identifier": "ABC" + }, + UUID: "12345", + ProvisionsAllDevices: false, + ApplicationIdentifierPrefix: null, + DeveloperCertificates: null, + Type: "Development" + }, + "NativeScriptDist": { + Name: "NativeScriptDist", + CreationDate: null, + ExpirationDate: null, + TeamName: "Telerik AD", + TeamIdentifier: ["TKID202"], + ProvisionedDevices: [], + Entitlements: { + "application-identifier": "*", + "com.apple.developer.team-identifier": "ABC" + }, + UUID: "6789", + ProvisionsAllDevices: true, + ApplicationIdentifierPrefix: null, + DeveloperCertificates: null, + Type: "Distribution" + }, + "NativeScriptAdHoc": { + Name: "NativeScriptAdHoc", + CreationDate: null, + ExpirationDate: null, + TeamName: "Telerik AD", + TeamIdentifier: ["TKID303"], + ProvisionedDevices: [], + Entitlements: { + "application-identifier": "*", + "com.apple.developer.team-identifier": "ABC" + }, + UUID: "1010", + ProvisionsAllDevices: true, + ApplicationIdentifierPrefix: null, + DeveloperCertificates: null, + Type: "Distribution" + } + })[uuidOrName]; + }; + }); + + describe("Check for Changes", () => { + it("sets signingChanged if no Xcode project exists", () => { + let changes = {}; + iOSProjectService.checkForChanges(changes, { bundle: false, release: false, provision: "NativeScriptDev" }, projectData); + assert.isTrue(!!changes.signingChanged); + }); + it("sets signingChanged if the Xcode projects is configured with Automatic signing, but proivsion is specified", () => { + files[pbxproj] = ""; + pbxprojDomXcode.Xcode.open = function(path: string) { + assert.equal(path, pbxproj); + return { + getSigning(x: string) { + return { style: "Automatic" }; + } + }; + }; + let changes = {}; + iOSProjectService.checkForChanges(changes, { bundle: false, release: false, provision: "NativeScriptDev" }, projectData); + assert.isTrue(!!changes.signingChanged); + }); + it("sets signingChanged if the Xcode projects is configured with Manual signing, but the proivsion specified differs the selected in the pbxproj", () => { + files[pbxproj] = ""; + pbxprojDomXcode.Xcode.open = function(path: string) { + assert.equal(path, pbxproj); + return { + getSigning() { + return { style: "Manual", configurations: { + Debug: { name: "NativeScriptDev2" }, + Release: { name: "NativeScriptDev2" } + }}; + } + }; + }; + let changes = {}; + iOSProjectService.checkForChanges(changes, { bundle: false, release: false, provision: "NativeScriptDev" }, projectData); + assert.isTrue(!!changes.signingChanged); + }); + it("does not set signingChanged if the Xcode projects is configured with Manual signing and proivsion matches", () => { + files[pbxproj] = ""; + pbxprojDomXcode.Xcode.open = function(path: string) { + assert.equal(path, pbxproj); + return { + getSigning() { + return { style: "Manual", configurations: { + Debug: { name: "NativeScriptDev" }, + Release: { name: "NativeScriptDev" } + }}; + } + }; + }; + let changes = {}; + iOSProjectService.checkForChanges(changes, { bundle: false, release: false, provision: "NativeScriptDev" }, projectData); + assert.isFalse(!!changes.signingChanged); + }); + }); + + describe("specifying provision", () => { + describe("from Automatic to provision name", () => { + beforeEach(() => { + files[pbxproj] = ""; + pbxprojDomXcode.Xcode.open = function(path: string) { + return { + getSigning(x: string) { + return { style: "Automatic", teamID: "AutoTeam" }; + } + }; + }; + }); + it("fails with proper error if the provision can not be found", async () => { + try { + await iOSProjectService.prepareProject(projectData, { sdk: undefined, provision: "NativeScriptDev2" }); + } catch (e) { + assert.isTrue(e.toString().indexOf("Failed to find mobile provision with UUID or Name: NativeScriptDev2") >= 0); + } + }); + it("succeeds if the provision name is provided for development cert", async() => { + let stack: any = []; + pbxprojDomXcode.Xcode.open = function(path: string) { + assert.equal(path, pbxproj); + return { + getSigning() { + return { style: "Automatic", teamID: "AutoTeam" }; + }, + save() { + stack.push("save()"); + }, + setManualSigningStyle(targetName: string, manualSigning: any) { + stack.push({ targetName, manualSigning }); + } + }; + }; + await iOSProjectService.prepareProject(projectData, { sdk: undefined, provision: "NativeScriptDev" }); + assert.deepEqual(stack, [{targetName: projectDirName, manualSigning: { team: "TKID101", uuid: "12345", name: "NativeScriptDev", identity: "iPhone Developer" }}, "save()"]); + }); + it("succeds if the provision name is provided for distribution cert", async () => { + let stack: any = []; + pbxprojDomXcode.Xcode.open = function(path: string) { + assert.equal(path, pbxproj); + return { + getSigning() { + return { style: "Automatic", teamID: "AutoTeam" }; + }, + save() { + stack.push("save()"); + }, + setManualSigningStyle(targetName: string, manualSigning: any) { + stack.push({ targetName, manualSigning }); + } + }; + }; + await iOSProjectService.prepareProject(projectData, { sdk: undefined, provision: "NativeScriptDist" }); + assert.deepEqual(stack, [{targetName: projectDirName, manualSigning: { team: "TKID202", uuid: "6789", name: "NativeScriptDist", identity: "iPhone Distribution" }}, "save()"]); + }); + it("succeds if the provision name is provided for adhoc cert", async () => { + let stack: any = []; + pbxprojDomXcode.Xcode.open = function(path: string) { + assert.equal(path, pbxproj); + return { + getSigning() { + return { style: "Automatic", teamID: "AutoTeam" }; + }, + save() { + stack.push("save()"); + }, + setManualSigningStyle(targetName: string, manualSigning: any) { + stack.push({ targetName, manualSigning }); + } + }; + }; + await iOSProjectService.prepareProject(projectData, { sdk: undefined, provision: "NativeScriptAdHoc" }); + assert.deepEqual(stack, [{targetName: projectDirName, manualSigning: { team: "TKID303", uuid: "1010", name: "NativeScriptAdHoc", identity: "iPhone Distribution" }}, "save()"]); + }); + }); + }); +}); diff --git a/test/mocha.opts b/test/mocha.opts index 6541568281..5a8f874618 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,5 +1,6 @@ --recursive --reporter spec +--require source-map-support/register --require test/test-bootstrap.js --timeout 150000 test/ diff --git a/test/npm-support.ts b/test/npm-support.ts index e69d9a4d69..00fe04a5cc 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -159,7 +159,8 @@ async function setupProject(dependencies?: any): Promise { ensureConfigurationFileInAppResources: (): any => null, interpolateConfigurationFile: (): void => undefined, isPlatformPrepared: (projectRoot: string) => false, - validatePlugins: (projectData: IProjectData) => Promise.resolve() + validatePlugins: (projectData: IProjectData) => Promise.resolve(), + checkForChanges: () => { /* */ } } }; }; diff --git a/test/platform-service.ts b/test/platform-service.ts index 54df6f0626..a1d95ee1b3 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -418,7 +418,8 @@ describe('Platform Service Tests', () => { ensureConfigurationFileInAppResources: (): any => null, interpolateConfigurationFile: (): void => undefined, isPlatformPrepared: (projectRoot: string) => false, - prepareAppResources: (appResourcesDirectoryPath: string, projectData: IProjectData): void => undefined + prepareAppResources: (appResourcesDirectoryPath: string, projectData: IProjectData): void => undefined, + checkForChanges: () => { /* */ } } }; }; @@ -842,7 +843,8 @@ describe('Platform Service Tests', () => { processConfigurationFilesFromAppResources: () => Promise.resolve(), ensureConfigurationFileInAppResources: (): any => null, interpolateConfigurationFile: (): void => undefined, - isPlatformPrepared: (projectRoot: string) => false + isPlatformPrepared: (projectRoot: string) => false, + checkForChanges: () => { /* */ } }, frameworkPackageName: "tns-ios" }; diff --git a/test/project-changes-service.ts b/test/project-changes-service.ts index ccd3d9b177..5fab8b9598 100644 --- a/test/project-changes-service.ts +++ b/test/project-changes-service.ts @@ -59,9 +59,27 @@ describe("Project Changes Service Tests", () => { serviceTest.platformsData.getPlatformData = (platform: string) => { - return { - projectRoot: path.join(platformsDir, platform) - }; + if (platform.toLowerCase() === "ios") { + return { + projectRoot: path.join(platformsDir, platform), + get platformProjectService(): any { + return { + checkForChanges(changesInfo: IProjectChangesInfo, options: IProjectChangesOptions, projectData: IProjectData): void { + changesInfo.signingChanged = true; + } + }; + } + }; + } else { + return { + projectRoot: path.join(platformsDir, platform), + get platformProjectService(): any { + return { + checkForChanges(changesInfo: IProjectChangesInfo, options: IProjectChangesOptions, projectData: IProjectData): void { /* no android changes */ } + }; + } + }; + } }; }); @@ -113,4 +131,13 @@ describe("Project Changes Service Tests", () => { } }); }); + + describe("Accumulates Changes From Project Services", () => { + it("accumulates changes from the project service", () => { + let iOSChanges = serviceTest.projectChangesService.checkForChanges("ios", serviceTest.projectData, { bundle: false, release: false, provision: undefined }); + assert.isTrue(!!iOSChanges.signingChanged, "iOS signingChanged expected to be true"); + let androidChanges = serviceTest.projectChangesService.checkForChanges("android", serviceTest.projectData, { bundle: false, release: false, provision: undefined }); + assert.isFalse(!!androidChanges.signingChanged, "Android signingChanged expected to be false"); + }); + }); }); diff --git a/test/stubs.ts b/test/stubs.ts index 00f09ba5cd..3893ba69f1 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -357,6 +357,9 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor async cleanProject(projectRoot: string, projectData: IProjectData): Promise { return Promise.resolve(); } + checkForChanges(changesInfo: IProjectChangesInfo, options: IProjectChangesOptions, projectData: IProjectData): void { + // Nothing yet. + } } export class ProjectDataService implements IProjectDataService { diff --git a/test/tns-appstore-upload.ts b/test/tns-appstore-upload.ts index 09c62c8457..bc930731ce 100644 --- a/test/tns-appstore-upload.ts +++ b/test/tns-appstore-upload.ts @@ -1,10 +1,8 @@ -import { suite, test/*, only*/ } from "mocha-typescript"; import { PublishIOS } from "../lib/commands/appstore-upload"; import { PrompterStub, LoggerStub, ProjectDataStub } from "./stubs"; import * as chai from "chai"; import * as yok from "../lib/common/yok"; -@suite("tns appstore") class AppStore { static itunesconnect = { user: "person@private.com", @@ -142,7 +140,6 @@ class AppStore { }; } - @test("without args, prompts for itunesconnect credentionals, prepares, archives and uploads") async noArgs() { this.expectItunesPrompt(); this.expectPreparePlatform(); @@ -155,7 +152,6 @@ class AppStore { this.assert(); } - @test("with command line itunesconnect credentionals, prepares, archives and uploads") async itunesconnectArgs() { this.expectPreparePlatform(); this.expectArchive(); @@ -167,7 +163,6 @@ class AppStore { this.assert(); } - @test("passes --team-id to xcodebuild exportArchive") async teamIdOption() { this.expectItunesPrompt(); this.expectPreparePlatform(); @@ -182,3 +177,21 @@ class AppStore { this.assert(); } } + +describe("tns appstore", () => { + it("without args, prompts for itunesconnect credentionals, prepares, archives and uploads", async () => { + const instance = new AppStore(); + instance.before(); + await instance.noArgs(); + }); + it("with command line itunesconnect credentionals, prepares, archives and uploads", async () => { + const instance = new AppStore(); + instance.before(); + await instance.itunesconnectArgs(); + }); + it("passes --team-id to xcodebuild exportArchive", async () => { + const instance = new AppStore(); + instance.before(); + await instance.teamIdOption(); + }); +});