diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index dc8cab466c..9d9f8b1308 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -38,7 +38,7 @@ interface INpmInstallationManager { install(packageName: string, packageDir: string, options?: INpmInstallOptions): Promise; getLatestVersion(packageName: string): Promise; getNextVersion(packageName: string): Promise; - getLatestCompatibleVersion(packageName: string): Promise; + getLatestCompatibleVersion(packageName: string, referenceVersion?: string): Promise; getInspectorFromCache(inspectorNpmPackageName: string, projectDir: string): Promise; } diff --git a/lib/device-sockets/ios/socket-proxy-factory.ts b/lib/device-sockets/ios/socket-proxy-factory.ts index 33a75b7b03..20e7502237 100644 --- a/lib/device-sockets/ios/socket-proxy-factory.ts +++ b/lib/device-sockets/ios/socket-proxy-factory.ts @@ -74,7 +74,7 @@ export class SocketProxyFactory extends EventEmitter implements ISocketProxyFact public async createWebSocketProxy(factory: () => Promise): Promise { // NOTE: We will try to provide command line options to select ports, at least on the localhost. - const localPort = await this.$net.getFreePort(); + const localPort = await this.$net.getAvailablePortInRange(41000); this.$logger.info("\nSetting up debugger proxy...\nPress Ctrl + C to terminate, or disconnect.\n"); @@ -85,6 +85,7 @@ export class SocketProxyFactory extends EventEmitter implements ISocketProxyFact const server = new ws.Server({ port: localPort, + host: "localhost", verifyClient: async (info: any, callback: Function) => { this.$logger.info("Frontend client connected."); let _socket; @@ -131,12 +132,13 @@ export class SocketProxyFactory extends EventEmitter implements ISocketProxyFact deviceSocket.on("close", () => { this.$logger.info("Backend socket closed!"); if (!this.$options.watch) { - process.exit(0); + webSocket.close(); } }); webSocket.on("close", () => { this.$logger.info('Frontend socket closed!'); + deviceSocket.destroy(); if (!this.$options.watch) { process.exit(0); } diff --git a/lib/npm-installation-manager.ts b/lib/npm-installation-manager.ts index 3c44556873..b9d1879391 100644 --- a/lib/npm-installation-manager.ts +++ b/lib/npm-installation-manager.ts @@ -9,7 +9,8 @@ export class NpmInstallationManager implements INpmInstallationManager { private $options: IOptions, private $settingsService: ISettingsService, private $fs: IFileSystem, - private $staticConfig: IStaticConfig) { + private $staticConfig: IStaticConfig, + private $projectDataService: IProjectDataService) { } public async getLatestVersion(packageName: string): Promise { @@ -20,23 +21,21 @@ export class NpmInstallationManager implements INpmInstallationManager { return await this.getVersion(packageName, constants.PackageVersion.NEXT); } - public async getLatestCompatibleVersion(packageName: string): Promise { - const configVersion = this.$staticConfig.version; - const isPreReleaseVersion = semver.prerelease(configVersion) !== null; - let cliVersionRange = `~${semver.major(configVersion)}.${semver.minor(configVersion)}.0`; - if (isPreReleaseVersion) { - // if the user has some 0-19 pre-release version, include pre-release versions in the search query. - cliVersionRange = `~${configVersion}`; - } - + public async getLatestCompatibleVersion(packageName: string, referenceVersion?: string): Promise { + referenceVersion = referenceVersion || this.$staticConfig.version; + const isPreReleaseVersion = semver.prerelease(referenceVersion) !== null; + // if the user has some v.v.v-prerelease-xx.xx pre-release version, include pre-release versions in the search query. + const compatibleVersionRange = isPreReleaseVersion + ? `~${referenceVersion}` + : `~${semver.major(referenceVersion)}.${semver.minor(referenceVersion)}.0`; const latestVersion = await this.getLatestVersion(packageName); - if (semver.satisfies(latestVersion, cliVersionRange)) { + if (semver.satisfies(latestVersion, compatibleVersionRange)) { return latestVersion; } const data = await this.$npm.view(packageName, { "versions": true }); - const maxSatisfying = semver.maxSatisfying(data, cliVersionRange); + const maxSatisfying = semver.maxSatisfying(data, compatibleVersionRange); return maxSatisfying || latestVersion; } @@ -59,21 +58,32 @@ export class NpmInstallationManager implements INpmInstallationManager { const inspectorPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName); // local installation takes precedence over cache - if (!this.inspectorAlreadyInstalled(inspectorPath)) { - const cachePath = path.join(this.$settingsService.getProfileDir(), constants.INSPECTOR_CACHE_DIRNAME); - this.prepareCacheDir(cachePath); - const pathToPackageInCache = path.join(cachePath, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName); - - if (!this.$fs.exists(pathToPackageInCache)) { - const version = await this.getLatestCompatibleVersion(inspectorNpmPackageName); - await this.$childProcess.exec(`npm install ${inspectorNpmPackageName}@${version} --prefix ${cachePath}`); + if (this.inspectorAlreadyInstalled(inspectorPath)) { + return inspectorPath; + } + + const cachePath = path.join(this.$settingsService.getProfileDir(), constants.INSPECTOR_CACHE_DIRNAME); + this.prepareCacheDir(cachePath); + const pathToPackageInCache = path.join(cachePath, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName); + const iOSFrameworkNSValue = this.$projectDataService.getNSValue(projectDir, constants.TNS_IOS_RUNTIME_NAME); + const version = await this.getLatestCompatibleVersion(inspectorNpmPackageName, iOSFrameworkNSValue.version); + let shouldInstall = !this.$fs.exists(pathToPackageInCache); + + if (!shouldInstall) { + try { + const installedVersion = this.$fs.readJson(path.join(pathToPackageInCache, constants.PACKAGE_JSON_FILE_NAME)).version; + shouldInstall = version !== installedVersion; + } catch (err) { + shouldInstall = true; } + } - this.$logger.out("Using inspector from cache."); - return pathToPackageInCache; + if (shouldInstall) { + await this.$childProcess.exec(`npm install ${inspectorNpmPackageName}@${version} --prefix ${cachePath}`); } - return inspectorPath; + this.$logger.out("Using inspector from cache."); + return pathToPackageInCache; } private prepareCacheDir(cacheDirName: string): void { diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 658bafee3b..ae897458ef 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -64,7 +64,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ const projectRoot = path.join(projectData.platformsDir, this.$devicePlatformsConstants.iOS.toLowerCase()); this._platformData = { - frameworkPackageName: "tns-ios", + frameworkPackageName: constants.TNS_IOS_RUNTIME_NAME, normalizedPlatformName: "iOS", appDestinationDirectoryPath: path.join(projectRoot, projectData.projectName), platformProjectService: this, diff --git a/test/npm-installation-manager.ts b/test/npm-installation-manager.ts index 1c35146e37..63c516b846 100644 --- a/test/npm-installation-manager.ts +++ b/test/npm-installation-manager.ts @@ -10,6 +10,7 @@ import * as StaticConfigLib from "../lib/config"; import * as yok from "../lib/common/yok"; import ChildProcessLib = require("../lib/common/child-process"); import { SettingsService } from "../lib/common/test/unit-tests/stubs"; +import { ProjectDataService } from "../lib/services/project-data-service"; function createTestInjector(): IInjector { const testInjector = new yok.Yok(); @@ -23,6 +24,7 @@ function createTestInjector(): IInjector { testInjector.register("staticConfig", StaticConfigLib.StaticConfig); testInjector.register("childProcess", ChildProcessLib.ChildProcess); testInjector.register("settingsService", SettingsService); + testInjector.register("projectDataService", ProjectDataService); testInjector.register("npmInstallationManager", NpmInstallationManagerLib.NpmInstallationManager); @@ -57,6 +59,13 @@ interface ITestData { */ cliVersion: string; + /** + * Version, based on which the version of the package that will be installed is detected. + * Used when semantically the correct reference version is different than the CLI version. + * (e.g. inspector package version should be determined by the project's ios runtime version) + */ + referenceVersion?: string; + /** * Expected result */ @@ -64,114 +73,123 @@ interface ITestData { } describe("Npm installation manager tests", () => { - const testData: IDictionary = { - "when there's only one available version and it matches CLI's version": { - versions: ["1.4.0"], - packageLatestVersion: "1.4.0", - cliVersion: "1.4.0", - expectedResult: "1.4.0" - }, - - "when there's only one available version and it is higher than match CLI's version": { - versions: ["1.4.0"], - packageLatestVersion: "1.4.0", - cliVersion: "1.2.0", - expectedResult: "1.4.0" - }, - - "when there's only one available version and it is lower than CLI's version": { - versions: ["1.4.0"], - packageLatestVersion: "1.4.0", - cliVersion: "1.6.0", - expectedResult: "1.4.0" - }, - - "when there are multiple package versions and the latest one matches ~": { - versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"], - packageLatestVersion: "1.3.3", - cliVersion: "1.3.0", - expectedResult: "1.3.3" - }, - - "when there are multiple package versions and the latest one matches ~ when there are newer matching versions but they are not under latest tag": { - versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"], - packageLatestVersion: "1.3.2", - cliVersion: "1.3.0", - expectedResult: "1.3.2" - }, - - "when there are multiple package versions and the latest one is lower than ~": { - versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"], - packageLatestVersion: "1.4.0", - cliVersion: "1.5.0", - expectedResult: "1.4.0" - }, - - "when there are multiple package versions and there's beta version matching CLI's semver": { - versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182"], - packageLatestVersion: "1.4.0", - cliVersion: "1.5.0", - expectedResult: "1.4.0" - }, - - "when there are multiple package versions and package's latest version is greater than CLI's version": { - versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0", "1.6.0"], - packageLatestVersion: "1.6.0", - cliVersion: "1.5.0", - expectedResult: "1.5.0" - }, - - "when there are multiple versions latest one does not match CLI's semver and other versions are not matching either": { - versions: ["1.0.0", "1.0.1", "1.2.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0"], - packageLatestVersion: "1.0.0", - cliVersion: "1.1.0", - expectedResult: "1.0.0" - }, - - "when CLI's version is beta (has dash) latest matching beta version is returned": { - versions: ["1.0.0", "1.0.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0-2016-02-26-202"], - packageLatestVersion: "1.4.0", - cliVersion: "1.5.0-182", - expectedResult: "1.5.0-2016-02-26-202" - }, - - "when CLI's version is beta (has dash) latest matching official version is returned when beta versions do not match": { - versions: ["1.0.0", "1.0.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0-2016-02-26-202"], - packageLatestVersion: "1.4.0", - cliVersion: "1.6.0-2016-03-01-182", - expectedResult: "1.4.0" - }, - - "when CLI's version is beta (has dash) latest matching official version is returned when beta versions do not match (when the prerelease of CLI is higher than prerelease version of runtime)": { - versions: ["1.0.0", "1.0.1", "1.4.0", "1.6.0-2016-02-25-182", "1.6.0-2016-02-26-202"], - packageLatestVersion: "1.4.0", - cliVersion: "1.6.0-2016-10-01-182", - expectedResult: "1.4.0" - }, - "When CLI Version has patch version larger than an existing package, should return max compliant package from the same major.minor version": { - versions: ["1.0.0", "1.0.1", "1.4.0", "2.5.0", "2.5.1", "2.5.2", "3.0.0"], - packageLatestVersion: "3.0.0", - cliVersion: "2.5.4", - expectedResult: "2.5.2" - } - }; + describe("getLatestCompatibleVersion", () => { + const testData: IDictionary = { + "when there's only one available version and it matches CLI's version": { + versions: ["1.4.0"], + packageLatestVersion: "1.4.0", + cliVersion: "1.4.0", + expectedResult: "1.4.0" + }, + + "when there's only one available version and it is higher than match CLI's version": { + versions: ["1.4.0"], + packageLatestVersion: "1.4.0", + cliVersion: "1.2.0", + expectedResult: "1.4.0" + }, + + "when there's only one available version and it is lower than CLI's version": { + versions: ["1.4.0"], + packageLatestVersion: "1.4.0", + cliVersion: "1.6.0", + expectedResult: "1.4.0" + }, + + "when there are multiple package versions and the latest one matches ~": { + versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"], + packageLatestVersion: "1.3.3", + cliVersion: "1.3.0", + expectedResult: "1.3.3" + }, + + "when there are multiple package versions and the latest one matches ~ when there are newer matching versions but they are not under latest tag": { + versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"], + packageLatestVersion: "1.3.2", + cliVersion: "1.3.0", + expectedResult: "1.3.2" + }, + + "when there are multiple package versions and the latest one is lower than ~": { + versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"], + packageLatestVersion: "1.4.0", + cliVersion: "1.5.0", + expectedResult: "1.4.0" + }, + + "when there are multiple package versions and there's beta version matching CLI's semver": { + versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182"], + packageLatestVersion: "1.4.0", + cliVersion: "1.5.0", + expectedResult: "1.4.0" + }, + + "when there are multiple package versions and package's latest version is greater than CLI's version": { + versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0", "1.6.0"], + packageLatestVersion: "1.6.0", + cliVersion: "1.5.0", + expectedResult: "1.5.0" + }, + + "when there are multiple versions latest one does not match CLI's semver and other versions are not matching either": { + versions: ["1.0.0", "1.0.1", "1.2.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0"], + packageLatestVersion: "1.0.0", + cliVersion: "1.1.0", + expectedResult: "1.0.0" + }, + + "when CLI's version is beta (has dash) latest matching beta version is returned": { + versions: ["1.0.0", "1.0.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0-2016-02-26-202"], + packageLatestVersion: "1.4.0", + cliVersion: "1.5.0-182", + expectedResult: "1.5.0-2016-02-26-202" + }, + + "when CLI's version is beta (has dash) latest matching official version is returned when beta versions do not match": { + versions: ["1.0.0", "1.0.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0-2016-02-26-202"], + packageLatestVersion: "1.4.0", + cliVersion: "1.6.0-2016-03-01-182", + expectedResult: "1.4.0" + }, + + "when CLI's version is beta (has dash) latest matching official version is returned when beta versions do not match (when the prerelease of CLI is higher than prerelease version of runtime)": { + versions: ["1.0.0", "1.0.1", "1.4.0", "1.6.0-2016-02-25-182", "1.6.0-2016-02-26-202"], + packageLatestVersion: "1.4.0", + cliVersion: "1.6.0-2016-10-01-182", + expectedResult: "1.4.0" + }, + "When CLI Version has patch version larger than an existing package, should return max compliant package from the same major.minor version": { + versions: ["1.0.0", "1.0.1", "1.4.0", "2.5.0", "2.5.1", "2.5.2", "3.0.0"], + packageLatestVersion: "3.0.0", + cliVersion: "2.5.4", + expectedResult: "2.5.2" + }, + "When reference version is specified as argument": { + versions: ["122.0.4", "123.0.0", "123.0.1", "123.1.0", "124.0.0"], + packageLatestVersion: "124.0.0", + cliVersion: "0.0.0", // should not matter + expectedResult: "123.0.1", + referenceVersion: "123.0.5" + } + }; - _.each(testData, (currentTestData: ITestData, testName: string) => { - it(`returns correct latest compatible version, ${testName}`, async () => { - const testInjector = createTestInjector(); + _.each(testData, (currentTestData: ITestData, testName: string) => { + it(`returns correct latest compatible version, ${testName}`, async () => { + const testInjector = createTestInjector(); - mockNpm(testInjector, currentTestData.versions, currentTestData.packageLatestVersion); + mockNpm(testInjector, currentTestData.versions, currentTestData.packageLatestVersion); - // Mock staticConfig.version - const staticConfig = testInjector.resolve("staticConfig"); - staticConfig.version = currentTestData.cliVersion; + // Mock staticConfig.version + const staticConfig = testInjector.resolve("staticConfig"); + staticConfig.version = currentTestData.cliVersion; - // Mock npmInstallationManager.getLatestVersion - const npmInstallationManager = testInjector.resolve("npmInstallationManager"); - npmInstallationManager.getLatestVersion = (packageName: string) => Promise.resolve(currentTestData.packageLatestVersion); + // Mock npmInstallationManager.getLatestVersion + const npmInstallationManager = testInjector.resolve("npmInstallationManager"); + npmInstallationManager.getLatestVersion = (packageName: string) => Promise.resolve(currentTestData.packageLatestVersion); - const actualLatestCompatibleVersion = await npmInstallationManager.getLatestCompatibleVersion(""); - assert.equal(actualLatestCompatibleVersion, currentTestData.expectedResult); + const actualLatestCompatibleVersion = await npmInstallationManager.getLatestCompatibleVersion("", currentTestData.referenceVersion); + assert.equal(actualLatestCompatibleVersion, currentTestData.expectedResult); + }); }); }); }); diff --git a/test/services/ios-debug-service.ts b/test/services/ios-debug-service.ts index 23af05478b..d994ea9cbd 100644 --- a/test/services/ios-debug-service.ts +++ b/test/services/ios-debug-service.ts @@ -48,6 +48,10 @@ const createTestInjector = (): IInjector => { on: (event: string | symbol, listener: Function): any => undefined }); + testInjector.register("net", { + getAvailablePortInRange: async (startPort: number, endPort?: number): Promise => 41000 + }); + return testInjector; };