From 6ba68be6e03942757d35ea080af5a503aaddba71 Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Fri, 19 Oct 2018 12:41:37 +0300 Subject: [PATCH 01/26] fix(preview): skip check if local plugin version is invalid If the local version of the plugin cannot be parsed by `semver`, the check should be skipped and no warning shown. fixes #4043 --- .../playground/preview-app-plugins-service.ts | 25 +++++++++++-------- .../playground/preview-app-plugins-service.ts | 10 ++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/services/livesync/playground/preview-app-plugins-service.ts b/lib/services/livesync/playground/preview-app-plugins-service.ts index 41a2480cfd..ecf7fc0b57 100644 --- a/lib/services/livesync/playground/preview-app-plugins-service.ts +++ b/lib/services/livesync/playground/preview-app-plugins-service.ts @@ -74,20 +74,25 @@ export class PreviewAppPluginsService implements IPreviewAppPluginsService { private getWarningForPluginCore(localPlugin: string, localPluginVersion: string, devicePluginVersion: string, deviceId: string): string { this.$logger.trace(`Comparing plugin ${localPlugin} with localPluginVersion ${localPluginVersion} and devicePluginVersion ${devicePluginVersion}`); - if (devicePluginVersion) { - const localPluginVersionData = semver.coerce(localPluginVersion); - const devicePluginVersionData = semver.coerce(devicePluginVersion); - - if (localPluginVersionData.major !== devicePluginVersionData.major) { - return util.format(PluginComparisonMessages.LOCAL_PLUGIN_WITH_DIFFERENCE_IN_MAJOR_VERSION, localPlugin, localPluginVersion, devicePluginVersion); - } else if (localPluginVersionData.minor > devicePluginVersionData.minor) { - return util.format(PluginComparisonMessages.LOCAL_PLUGIN_WITH_GREATHER_MINOR_VERSION, localPlugin, localPluginVersion, devicePluginVersion); - } + if (!devicePluginVersion) { + return util.format(PluginComparisonMessages.PLUGIN_NOT_INCLUDED_IN_PREVIEW_APP, localPlugin, deviceId); + } + const shouldSkipCheck = !semver.valid(localPluginVersion) && !semver.validRange(localPluginVersion); + if (shouldSkipCheck) { return null; } - return util.format(PluginComparisonMessages.PLUGIN_NOT_INCLUDED_IN_PREVIEW_APP, localPlugin, deviceId); + const localPluginVersionData = semver.coerce(localPluginVersion); + const devicePluginVersionData = semver.coerce(devicePluginVersion); + + if (localPluginVersionData.major !== devicePluginVersionData.major) { + return util.format(PluginComparisonMessages.LOCAL_PLUGIN_WITH_DIFFERENCE_IN_MAJOR_VERSION, localPlugin, localPluginVersion, devicePluginVersion); + } else if (localPluginVersionData.minor > devicePluginVersionData.minor) { + return util.format(PluginComparisonMessages.LOCAL_PLUGIN_WITH_GREATHER_MINOR_VERSION, localPlugin, localPluginVersion, devicePluginVersion); + } + + return null; } private hasNativeCode(localPlugin: string, platform: string, projectDir: string): boolean { diff --git a/test/services/playground/preview-app-plugins-service.ts b/test/services/playground/preview-app-plugins-service.ts index 1b21282909..13087a0a7f 100644 --- a/test/services/playground/preview-app-plugins-service.ts +++ b/test/services/playground/preview-app-plugins-service.ts @@ -243,6 +243,16 @@ describe("previewAppPluginsService", () => { "nativescript-theme-core": "3.5.0" }, expectedWarnings: [] + }, + { + name: "should not show warning when the local plugin version is tag", + localPlugins: { + "tns-core-modules": "rc" + }, + previewAppPlugins: { + "tns-core-modules": "5.0.0" + }, + expectedWarnings: [] } ]; From 6fd316a6d690208fabb28d479eabf326b3f90385 Mon Sep 17 00:00:00 2001 From: fatme Date: Wed, 24 Oct 2018 12:24:42 +0300 Subject: [PATCH 02/26] fix: remove persisted emulator's data on deviceLost event --- lib/common/definitions/mobile.d.ts | 11 +++++++++++ lib/common/mobile/android/android-device.ts | 6 ++++++ .../mobile/android/android-emulator-services.ts | 4 ++++ .../mobile/android/android-virtual-device-service.ts | 6 ++++++ 4 files changed, 27 insertions(+) diff --git a/lib/common/definitions/mobile.d.ts b/lib/common/definitions/mobile.d.ts index 3fa23012fa..532c3aee2d 100644 --- a/lib/common/definitions/mobile.d.ts +++ b/lib/common/definitions/mobile.d.ts @@ -730,6 +730,12 @@ declare module Mobile { * @returns {Promise} Starts the emulator and returns the errors if some error occurs. */ startEmulator(options: Mobile.IStartEmulatorOptions): Promise; + + /** + * Called when emulator is lost. Its purpose is to clean any resources used by the instance. + * @returns {void} + */ + detach?(deviceInfo: Mobile.IDeviceInfo): void; } interface IStartEmulatorOutput { @@ -772,6 +778,11 @@ declare module Mobile { * @param imageIdentifier - The imagerIdentifier of the emulator. */ startEmulatorArgs(imageIdentifier: string): string[]; + /** + * Called when emulator is lost. Its purpose is to clean any resources used by the instance. + * @returns {void} + */ + detach?(deviceInfo: Mobile.IDeviceInfo): void; } interface IVirtualBoxService { diff --git a/lib/common/mobile/android/android-device.ts b/lib/common/mobile/android/android-device.ts index 1a2b87cc1d..33fa17a2d5 100644 --- a/lib/common/mobile/android/android-device.ts +++ b/lib/common/mobile/android/android-device.ts @@ -121,6 +121,12 @@ export class AndroidDevice implements Mobile.IAndroidDevice { } } + public detach(): void { + if (this.isEmulator) { + this.$androidEmulatorServices.detach(this.deviceInfo); + } + } + private async getDeviceDetails(shellCommandArgs: string[]): Promise { const parsedDetails: any = {}; diff --git a/lib/common/mobile/android/android-emulator-services.ts b/lib/common/mobile/android/android-emulator-services.ts index 48648c50be..0b193cd135 100644 --- a/lib/common/mobile/android/android-emulator-services.ts +++ b/lib/common/mobile/android/android-emulator-services.ts @@ -59,6 +59,10 @@ export class AndroidEmulatorServices implements Mobile.IEmulatorPlatformService }; } + public detach(deviceInfo: Mobile.IDeviceInfo) { + this.$androidVirtualDeviceService.detach(deviceInfo); + } + private async startEmulatorCore(options: Mobile.IAndroidStartEmulatorOptions): Promise<{runningEmulator: Mobile.IDeviceInfo, errors: string[], endTimeEpoch: number}> { const timeout = options.timeout || AndroidVirtualDevice.TIMEOUT_SECONDS; const endTimeEpoch = getCurrentEpochTime() + this.$utils.getMilliSecondsTimeout(timeout); diff --git a/lib/common/mobile/android/android-virtual-device-service.ts b/lib/common/mobile/android/android-virtual-device-service.ts index a81a391c6c..c98b859131 100644 --- a/lib/common/mobile/android/android-virtual-device-service.ts +++ b/lib/common/mobile/android/android-virtual-device-service.ts @@ -142,6 +142,12 @@ export class AndroidVirtualDeviceService implements Mobile.IAndroidVirtualDevice }); } + public detach(deviceInfo: Mobile.IDeviceInfo) { + if (this.mapEmulatorIdToImageIdentifier[deviceInfo.identifier]) { + delete this.mapEmulatorIdToImageIdentifier[deviceInfo.identifier]; + } + } + private async getEmulatorImagesCore(): Promise { let result: ISpawnResult = null; let devices: Mobile.IDeviceInfo[] = []; From 1d12b64f5a357e1008a9e7cf814f85198cd7dec2 Mon Sep 17 00:00:00 2001 From: fatme Date: Fri, 19 Oct 2018 16:40:37 +0300 Subject: [PATCH 03/26] fix: doesn't await for AppLaunching notification when CLI retries to attach to debugger --- lib/declarations.d.ts | 2 +- lib/definitions/debug.d.ts | 4 +++ .../ios/socket-request-executor.ts | 28 +++++++++++-------- lib/services/ios-debug-service.ts | 2 +- lib/services/livesync/livesync-service.ts | 5 +++- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index ef54b193b8..67ecb3381f 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -709,7 +709,7 @@ interface IiOSNotification extends NodeJS.EventEmitter { } interface IiOSSocketRequestExecutor { - executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise; + executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, debugOptions: IDebugOptions): Promise; executeAttachRequest(device: Mobile.IiOSDevice, timeout: number, projectId: string): Promise; } diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index 034f3403b6..350094fa27 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -95,6 +95,10 @@ interface IDebugOptions { * The sdk version of the emulator. */ sdk?: string; + /** + * Defines if the handshake(AppLaunching notification) between CLI and runtime should be executed. The handshake is not needed when CLI retries to attach to the debugger. + */ + skipHandshake?: boolean; } /** diff --git a/lib/device-sockets/ios/socket-request-executor.ts b/lib/device-sockets/ios/socket-request-executor.ts index 88bb8e5bc4..76db790060 100644 --- a/lib/device-sockets/ios/socket-request-executor.ts +++ b/lib/device-sockets/ios/socket-request-executor.ts @@ -48,24 +48,19 @@ export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor { } } - public async executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise { + public async executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, debugOptions: IDebugOptions): Promise { try { - const appLaunchingSocket = await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAppLaunching(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE); - await this.$iOSNotificationService.awaitNotification(deviceIdentifier, +appLaunchingSocket, timeout); + if (!debugOptions.skipHandshake) { + await this.executeHandshake(deviceIdentifier, projectId, timeout); + } - if (shouldBreak) { + if (debugOptions.debugBrk) { await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getWaitForDebug(projectId)); } - // We need to send the ObserveNotification ReadyForAttach before we post the AttachRequest. - const readyForAttachSocket = await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getReadyForAttach(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE); - const readyForAttachPromise = this.$iOSNotificationService.awaitNotification(deviceIdentifier, +readyForAttachSocket, readyForAttachTimeout); - - await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachRequest(projectId, deviceIdentifier)); - await readyForAttachPromise; + await this.executeAttachAvailable(deviceIdentifier, projectId, readyForAttachTimeout); } catch (e) { - this.$logger.trace("Launch request error:"); - this.$logger.trace(e); + this.$logger.trace("Launch request error: ", e); this.$errors.failWithoutHelp("Error while waiting for response from NativeScript runtime."); } } @@ -79,9 +74,18 @@ export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor { await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachRequest(projectId, deviceIdentifier)); await readyForAttachPromise; } catch (e) { + this.$logger.trace("Attach available error: ", e); this.$errors.failWithoutHelp(`The application ${projectId} timed out when performing the socket handshake.`); } } + + private async executeHandshake(deviceIdentifier: string, projectId: string, timeout: number): Promise { + // This notification will be send only once by the runtime during application start. + // In case app is already running, we'll fail here as we'll not receive it. + const appLaunchingNotification = this.$iOSNotification.getAppLaunching(projectId); + const appLaunchingSocket = await this.$iOSNotificationService.postNotification(deviceIdentifier, appLaunchingNotification, constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE); + await this.$iOSNotificationService.awaitNotification(deviceIdentifier, +appLaunchingSocket, timeout); + } } $injector.register("iOSSocketRequestExecutor", IOSSocketRequestExecutor); diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 94648ecc54..2fa6749ad6 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -182,7 +182,7 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS } private async debugBrkCore(device: Mobile.IiOSDevice, debugData: IDebugData, debugOptions: IDebugOptions): Promise { - await this.$iOSSocketRequestExecutor.executeLaunchRequest(device.deviceInfo.identifier, AWAIT_NOTIFICATION_TIMEOUT_SECONDS, AWAIT_NOTIFICATION_TIMEOUT_SECONDS, debugData.applicationIdentifier, debugOptions.debugBrk); + await this.$iOSSocketRequestExecutor.executeLaunchRequest(device.deviceInfo.identifier, AWAIT_NOTIFICATION_TIMEOUT_SECONDS, AWAIT_NOTIFICATION_TIMEOUT_SECONDS, debugData.applicationIdentifier, debugOptions); return this.wireDebuggerClient(debugData, debugOptions, device); } diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 56566b8d9e..89eb8329ef 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -238,7 +238,9 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi }; debugData.pathToAppPackage = this.$platformService.lastOutputPath(settings.platform, buildConfig, projectData, settings.outputPath); - return this.printDebugInformation(await this.$debugService.debug(debugData, settings.debugOptions)); + const debugInfo = await this.$debugService.debug(debugData, settings.debugOptions); + const result = this.printDebugInformation(debugInfo); + return result; } public printDebugInformation(debugInformation: IDebugInformation): IDebugInformation { @@ -284,6 +286,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } catch (err) { this.$logger.trace("Couldn't attach debugger, will modify options and try again.", err); attachDebuggerOptions.debugOptions.start = false; + attachDebuggerOptions.debugOptions.skipHandshake = true; try { debugInformation = await this.attachDebugger(attachDebuggerOptions); } catch (innerErr) { From 45654049687e39c099ee48556bb906721b15ed0b Mon Sep 17 00:00:00 2001 From: fatme Date: Wed, 24 Oct 2018 13:56:18 +0300 Subject: [PATCH 04/26] fix: don't show "TypeError: Invalid Version: null" error in sidekick when emulator is stoped immediately after start --- lib/common/mobile/android/logcat-helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/mobile/android/logcat-helper.ts b/lib/common/mobile/android/logcat-helper.ts index 127c2c7799..a9374758b4 100644 --- a/lib/common/mobile/android/logcat-helper.ts +++ b/lib/common/mobile/android/logcat-helper.ts @@ -61,7 +61,7 @@ export class LogcatHelper implements Mobile.ILogcatHelper { private async getLogcatStream(deviceIdentifier: string, pid?: string) { const device = await this.$devicesService.getDevice(deviceIdentifier); const minAndroidWithLogcatPidSupport = "7.0.0"; - const isLogcatPidSupported = semver.gte(semver.coerce(device.deviceInfo.version), minAndroidWithLogcatPidSupport); + const isLogcatPidSupported = !!device.deviceInfo.version && semver.gte(semver.coerce(device.deviceInfo.version), minAndroidWithLogcatPidSupport); const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier }); const logcatCommand = ["logcat"]; From ef91626633c77592b184b26bb63a2977418a208a Mon Sep 17 00:00:00 2001 From: fatme Date: Wed, 24 Oct 2018 13:57:09 +0300 Subject: [PATCH 05/26] refactor: move the private method after the public methods --- lib/common/mobile/android/logcat-helper.ts | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/common/mobile/android/logcat-helper.ts b/lib/common/mobile/android/logcat-helper.ts index a9374758b4..d662733afe 100644 --- a/lib/common/mobile/android/logcat-helper.ts +++ b/lib/common/mobile/android/logcat-helper.ts @@ -58,20 +58,6 @@ export class LogcatHelper implements Mobile.ILogcatHelper { } } - private async getLogcatStream(deviceIdentifier: string, pid?: string) { - const device = await this.$devicesService.getDevice(deviceIdentifier); - const minAndroidWithLogcatPidSupport = "7.0.0"; - const isLogcatPidSupported = !!device.deviceInfo.version && semver.gte(semver.coerce(device.deviceInfo.version), minAndroidWithLogcatPidSupport); - const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier }); - const logcatCommand = ["logcat"]; - - if (pid && isLogcatPidSupported) { - logcatCommand.push(`--pid=${pid}`); - } - const logcatStream = await adb.executeCommand(logcatCommand, { returnChildProcess: true }); - return logcatStream; - } - public async dump(deviceIdentifier: string): Promise { const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier }); const logcatDumpStream = await adb.executeCommand(["logcat", "-d"], { returnChildProcess: true }); @@ -101,6 +87,20 @@ export class LogcatHelper implements Mobile.ILogcatHelper { delete this.mapDevicesLoggingData[deviceIdentifier]; } } + + private async getLogcatStream(deviceIdentifier: string, pid?: string) { + const device = await this.$devicesService.getDevice(deviceIdentifier); + const minAndroidWithLogcatPidSupport = "7.0.0"; + const isLogcatPidSupported = !!device.deviceInfo.version && semver.gte(semver.coerce(device.deviceInfo.version), minAndroidWithLogcatPidSupport); + const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier }); + const logcatCommand = ["logcat"]; + + if (pid && isLogcatPidSupported) { + logcatCommand.push(`--pid=${pid}`); + } + const logcatStream = await adb.executeCommand(logcatCommand, { returnChildProcess: true }); + return logcatStream; + } } $injector.register("logcatHelper", LogcatHelper); From b332669b0c7bcb4f8dc904b89b5824be84a93ef4 Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Wed, 24 Oct 2018 19:04:27 +0300 Subject: [PATCH 06/26] fix: stop making a full rebuild on livesync when the --clean flag is set --- lib/services/livesync/livesync-service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 89eb8329ef..3a6cf0b68a 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -703,7 +703,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi rebuiltInformation, projectData, deviceBuildInfoDescriptor, - liveSyncData, + // the clean option should be respected only during initial sync + liveSyncData: _.assign({}, liveSyncData, { clean: false }), settings: latestAppPackageInstalledSettings, modifiedFiles: allModifiedFiles, filesToRemove: currentFilesToRemove, From 1ab8e0bf30147783202326aa983c18de35c7a1d6 Mon Sep 17 00:00:00 2001 From: fatme Date: Wed, 24 Oct 2018 14:18:13 +0300 Subject: [PATCH 07/26] fix: validate params passed to preview command --- lib/commands/preview.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/commands/preview.ts b/lib/commands/preview.ts index 45d6639ff8..1926fcf8bf 100644 --- a/lib/commands/preview.ts +++ b/lib/commands/preview.ts @@ -3,6 +3,7 @@ export class PreviewCommand implements ICommand { private static MIN_SUPPORTED_WEBPACK_VERSION = "0.17.0"; constructor(private $bundleValidatorHelper: IBundleValidatorHelper, + private $errors: IErrors, private $liveSyncService: ILiveSyncService, private $networkConnectivityValidator: INetworkConnectivityValidator, private $projectData: IProjectData, @@ -27,6 +28,10 @@ export class PreviewCommand implements ICommand { } public async canExecute(args: string[]): Promise { + if (args && args.length) { + this.$errors.fail(`The arguments '${args.join(" ")}' are not valid for the preview command.`); + } + await this.$networkConnectivityValidator.validate(); this.$bundleValidatorHelper.validate(PreviewCommand.MIN_SUPPORTED_WEBPACK_VERSION); return true; From a3320f0796138b2a314ac19cf1faa201c0cc93a7 Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 25 Oct 2018 08:32:48 +0300 Subject: [PATCH 08/26] fix: improve the message when not able to start an emulator so it can be useful for sidekick as well. --- lib/common/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/constants.ts b/lib/common/constants.ts index b3b1b5bcab..272291a758 100644 --- a/lib/common/constants.ts +++ b/lib/common/constants.ts @@ -155,5 +155,5 @@ export class AndroidVirtualDevice { static TIMEOUT_SECONDS = 120; static GENYMOTION_DEFAULT_STDERR_STRING = "Logging activities to file"; - static UNABLE_TO_START_EMULATOR_MESSAGE = "Cannot run your app in the native emulator. Increase the timeout of the operation with the --timeout option or try to restart your adb server with 'adb kill-server' command. Alternatively, run the Android Virtual Device manager and increase the allocated RAM for the virtual device."; + static UNABLE_TO_START_EMULATOR_MESSAGE = "Cannot run the app in the selected native emulator. Try to restart the adb server by running the `adb kill-server` command in the Command Prompt, or increase the allocated RAM of the virtual device through the Android Virtual Device manager. NativeScript CLI users can try to increase the timeout of the operation by adding the `--timeout` flag."; } From 7cefe21ec2aeaf3f0aec56e0a5b3b2f74ba890bf Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Fri, 26 Oct 2018 17:31:50 +0300 Subject: [PATCH 09/26] fix: update an old Xcode warning as now its applicable for all supported Xcode versions --- lib/services/ios-project-service.ts | 2 +- test/ios-project-service.ts | 10 +++++----- test/xcconfig-service.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 655b3dabea..5054ef5ae1 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -1290,7 +1290,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f if (!teamId) { const teams = await this.$iOSProvisionService.getDevelopmentTeams(); - this.$logger.warn("Xcode 8 requires a team id to be specified when building for device."); + this.$logger.warn("Xcode requires a team id to be specified when building for device."); this.$logger.warn("You can specify the team id by setting the DEVELOPMENT_TEAM setting in build.xcconfig file located in App_Resources folder of your app, or by using the --teamId option when calling run, debug or livesync commands."); if (teams.length === 1) { teamId = teams[0].id; diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 1d538e3439..ac4c60a342 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -71,7 +71,7 @@ function createTestInjector(projectPath: string, projectName: string, xcode?: IX projectPath: projectPath, projectFilePath: path.join(projectPath, "package.json"), projectId: "", - projectIdentifiers: { android: "", ios: ""} + projectIdentifiers: { android: "", ios: "" } }); testInjector.register("projectData", projectData); testInjector.register("projectHelper", {}); @@ -505,7 +505,7 @@ describe("Source code in plugin support", () => { const mockPrepareMethods = ["prepareFrameworks", "prepareStaticLibs", "prepareResources", "prepareNativeSourceCode"]; mockPrepareMethods.filter(m => m !== prepareMethodToCall).forEach(methodName => { - iOSProjectService[methodName] = (pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise => { + iOSProjectService[methodName] = (pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise => { return Promise.resolve(); }; }); @@ -513,7 +513,7 @@ describe("Source code in plugin support", () => { iOSProjectService.getXcodeprojPath = () => { return path.join(__dirname, "files"); }; - let pbxProj : any; + let pbxProj: any; iOSProjectService.savePbxProj = (project: any): Promise => { pbxProj = project; return Promise.resolve(); @@ -544,7 +544,7 @@ describe("Source code in plugin support", () => { it("adds plugin with Source files", async () => { const sourceFileNames = [ - "src/Header.h", "src/ObjC.m", + "src/Header.h", "src/ObjC.m", "src/nested/Header.hpp", "src/nested/Source.cpp", "src/nested/ObjCpp.mm", "src/nested/level2/Header2.hxx", "src/nested/level2/Source2.cxx", "src/nested/level2/Source3.c", "src/SomeOtherExtension.donotadd", @@ -949,7 +949,7 @@ describe("Merge Project XCConfig files", () => { appResourcesXcconfigPath = path.join(projectData.appResourcesDirectoryPath, "iOS", BUILD_XCCONFIG_FILE_NAME); appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution - // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // To build for device with XCode you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html // DEVELOPMENT_TEAM = YOUR_TEAM_ID; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; diff --git a/test/xcconfig-service.ts b/test/xcconfig-service.ts index 9a96daecf8..c60c5998e4 100644 --- a/test/xcconfig-service.ts +++ b/test/xcconfig-service.ts @@ -58,7 +58,7 @@ describe("XCConfig Service Tests", () => { return `// You can add custom settings here // for example you can uncomment the following line to force distribution code signing CODE_SIGN_IDENTITY = iPhone Distribution - // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // To build for device with XCode you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html // DEVELOPMENT_TEAM = YOUR_TEAM_ID; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;`; @@ -80,7 +80,7 @@ describe("XCConfig Service Tests", () => { return `// You can add custom settings here // for example you can uncomment the following line to force distribution code signing CODE_SIGN_IDENTITY = iPhone Distribution - // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // To build for device with XCode you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html // DEVELOPMENT_TEAM = YOUR_TEAM_ID ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage`; From 77785d3c41c5ea362e058aed6b16a45033d94d90 Mon Sep 17 00:00:00 2001 From: fatme Date: Mon, 29 Oct 2018 08:12:06 +0200 Subject: [PATCH 10/26] fix: reset errors when fallback to list avds from directory --- .../android/android-virtual-device-service.ts | 4 +++- .../mobile/android-virtual-device-service.ts | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/common/mobile/android/android-virtual-device-service.ts b/lib/common/mobile/android/android-virtual-device-service.ts index c98b859131..3aeb0fbe8f 100644 --- a/lib/common/mobile/android/android-virtual-device-service.ts +++ b/lib/common/mobile/android/android-virtual-device-service.ts @@ -151,6 +151,7 @@ export class AndroidVirtualDeviceService implements Mobile.IAndroidVirtualDevice private async getEmulatorImagesCore(): Promise { let result: ISpawnResult = null; let devices: Mobile.IDeviceInfo[] = []; + let errors: string[] = []; if (this.pathToAvdManagerExecutable && this.$fs.exists(this.pathToAvdManagerExecutable)) { result = await this.$childProcess.trySpawnFromCloseEvent(this.pathToAvdManagerExecutable, ["list", "avds"]); @@ -160,11 +161,12 @@ export class AndroidVirtualDeviceService implements Mobile.IAndroidVirtualDevice if (result && result.stdout) { devices = this.parseListAvdsOutput(result.stdout); + errors = result && result.stderr ? [result.stderr] : []; } else { devices = this.listAvdsFromDirectory(); } - return { devices, errors: result && result.stderr ? [result.stderr] : [] }; + return { devices, errors }; } private async getRunningEmulatorData(runningEmulatorId: string, availableEmulators: Mobile.IDeviceInfo[]): Promise { diff --git a/lib/common/test/unit-tests/mobile/android-virtual-device-service.ts b/lib/common/test/unit-tests/mobile/android-virtual-device-service.ts index 3130cb51a4..b11590d900 100644 --- a/lib/common/test/unit-tests/mobile/android-virtual-device-service.ts +++ b/lib/common/test/unit-tests/mobile/android-virtual-device-service.ts @@ -214,6 +214,24 @@ describe("androidVirtualDeviceService", () => { assert.deepEqual(result[1], getAvailableEmulatorData({ displayName: "Nexus_5X_API_28", imageIdentifier: "Nexus_5X_API_28", version: "9.0.0", model: "Nexus 5X" })); assert.deepEqual(result[2], getAvailableEmulatorData({ displayName: "Nexus_6P_API_28", imageIdentifier: "Nexus_6P_API_28", version: "9.0.0", model: "Nexus 6P" })); }); + // In this case we should fallback to list avd directory and should't report errors from avdmanager + it("should return devices and no errors when there is an error on avdmanager's stderr", async () => { + const iniFilesData = getIniFilesData(); + const testInjector = createTestInjector({ + avdManagerOutput: "", + avdManagerError: "my test error", + iniFilesData + }); + + const fs = testInjector.resolve("fs"); + fs.readDirectory = () => _.keys(iniFilesData); + + const avdService = testInjector.resolve("androidVirtualDeviceService"); + const result = await avdService.getEmulatorImages(["emulator-5554 device"]); + + assert.deepEqual(result.devices.length, 3); + assert.deepEqual(result.errors.length, 0); + }); }); describe("when avdmanager is not found", () => { From 5761f74dd91306995f0c1fcd72f2bab4b199124a Mon Sep 17 00:00:00 2001 From: fatme Date: Tue, 30 Oct 2018 01:55:01 +0200 Subject: [PATCH 11/26] fix: show correct messages on `tns test init` command --- lib/commands/test-init.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/commands/test-init.ts b/lib/commands/test-init.ts index 10099c9461..0133a56665 100644 --- a/lib/commands/test-init.ts +++ b/lib/commands/test-init.ts @@ -93,9 +93,10 @@ class TestInitCommand implements ICommand { await this.$pluginsService.add('nativescript-unit-test-runner', this.$projectData); const testsDir = path.join(this.$projectData.appDirectoryPath, 'tests'); + const relativeTestsDir = path.relative(this.$projectData.projectDir, testsDir); let shouldCreateSampleTests = true; if (this.$fs.exists(testsDir)) { - this.$logger.info('app/tests/ directory already exists, will not create an example test project.'); + this.$logger.info(`${relativeTestsDir} directory already exists, will not create an example test project.`); shouldCreateSampleTests = false; } @@ -113,9 +114,9 @@ class TestInitCommand implements ICommand { if (shouldCreateSampleTests && this.$fs.exists(exampleFilePath)) { this.$fs.copyFile(exampleFilePath, path.join(testsDir, 'example.js')); - this.$logger.info('\nExample test file created in app/tests/'.yellow); + this.$logger.info(`\nExample test file created in ${relativeTestsDir}`.yellow); } else { - this.$logger.info('\nPlace your test files under app/tests/'.yellow); + this.$logger.info(`\nPlace your test files under ${relativeTestsDir}`.yellow); } this.$logger.info('Run your tests using the "$ tns test " command.'.yellow); From 4f67566eba9e86142186b5e9ce7ee48c8872a936 Mon Sep 17 00:00:00 2001 From: fatme Date: Tue, 30 Oct 2018 08:29:34 +0200 Subject: [PATCH 12/26] fix: fix unit tests --- .../test/unit-tests/mobile/android-virtual-device-service.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/common/test/unit-tests/mobile/android-virtual-device-service.ts b/lib/common/test/unit-tests/mobile/android-virtual-device-service.ts index b11590d900..9cf9795fe3 100644 --- a/lib/common/test/unit-tests/mobile/android-virtual-device-service.ts +++ b/lib/common/test/unit-tests/mobile/android-virtual-device-service.ts @@ -176,14 +176,13 @@ describe("androidVirtualDeviceService", () => { assert.deepEqual(result.devices, []); assert.deepEqual(result.errors, []); }); - it("should return an empty array when `avdmanager list avds` command fails", async () => { + it("should return an empty array and no errors when `avdmanager list avds` command fails", async () => { const avdManagerError = "some error while executing avdmanager list avds"; const avdService = mockAvdService({ avdManagerError }); const result = await avdService.getEmulatorImages([]); assert.lengthOf(result.devices, 0); assert.deepEqual(result.devices, []); - assert.lengthOf(result.errors, 1); - assert.deepEqual(result.errors, [avdManagerError]); + assert.lengthOf(result.errors, 0); }); it("should return all emulators when there are available emulators and no running emulators", async () => { const avdService = mockAvdService({ From 75b777d9223978dcc62ec4ef91eac9e7f589168a Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 25 Oct 2018 16:28:22 +0300 Subject: [PATCH 13/26] refactor: move the private method after the public --- .../preview-app-livesync-service.ts | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index 8ea4af7c54..502c2d4986 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -52,6 +52,27 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { }); } + public async syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise { + this.showWarningsForNativeFiles(filesToSync); + + for (const device of this.$previewSdkService.connectedDevices) { + await this.$previewAppPluginsService.comparePluginsOnDevice(data, device); + } + + const platforms = _(this.$previewSdkService.connectedDevices) + .map(device => device.platform) + .uniq() + .value(); + + for (const platform of platforms) { + await this.syncFilesForPlatformSafe(data, platform, { filesToSync, filesToRemove, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload }); + } + } + + public async stopLiveSync(): Promise { + this.$previewSdkService.stop(); + } + private async initializePreviewForDevice(data: IPreviewAppLiveSyncData, device: Device): Promise { const filesToSyncMap: IDictionary = {}; const hmrData: IDictionary = {}; @@ -98,27 +119,6 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { return payloads; } - public async syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise { - this.showWarningsForNativeFiles(filesToSync); - - for (const device of this.$previewSdkService.connectedDevices) { - await this.$previewAppPluginsService.comparePluginsOnDevice(data, device); - } - - const platforms = _(this.$previewSdkService.connectedDevices) - .map(device => device.platform) - .uniq() - .value(); - - for (const platform of platforms) { - await this.syncFilesForPlatformSafe(data, platform, { filesToSync, filesToRemove, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload }); - } - } - - public async stopLiveSync(): Promise { - this.$previewSdkService.stop(); - } - private async syncFilesForPlatformSafe(data: IPreviewAppLiveSyncData, platform: string, opts?: ISyncFilesOptions): Promise { this.$logger.info(`Start syncing changes for platform ${platform}.`); From 2bfae7c2e4e2766cc4d583711dd25579438fda42 Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 25 Oct 2018 16:33:21 +0300 Subject: [PATCH 14/26] refactor: extract preview sync hook to separate method --- .../preview-app-livesync-service.ts | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index 502c2d4986..a968797994 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -74,51 +74,57 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { } private async initializePreviewForDevice(data: IPreviewAppLiveSyncData, device: Device): Promise { - const filesToSyncMap: IDictionary = {}; - const hmrData: IDictionary = {}; - let promise = Promise.resolve(null); - const startSyncFilesTimeout = async (platform: string) => { - await promise - .then(async () => { - const currentHmrData = _.cloneDeep(hmrData); - const platformHmrData = currentHmrData[platform] || {}; - const filesToSync = _.cloneDeep(filesToSyncMap[platform]); - // We don't need to prepare when webpack emits changed files. We just need to send a message to pubnub. - promise = this.syncFilesForPlatformSafe(data, platform, { filesToSync, skipPrepare: true, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload }); - await promise; - - if (data.appFilesUpdaterOptions.useHotModuleReload && platformHmrData.hash) { - const devices = _.filter(this.$previewSdkService.connectedDevices, { platform: platform.toLowerCase() }); - - await Promise.all(_.map(devices, async (previewDevice: Device) => { - const status = await this.$hmrStatusService.getHmrStatus(previewDevice.id, platformHmrData.hash); - if (status === HmrConstants.HMR_ERROR_STATUS) { - await this.syncFilesForPlatformSafe(data, platform, { filesToSync: platformHmrData.fallbackFiles, useHotModuleReload: false, deviceId: previewDevice.id }); - } - })); - } - }); - filesToSyncMap[platform] = []; - }; - await this.$hooksService.executeBeforeHooks("preview-sync", { - hookArgs: { - projectData: this.$projectDataService.getProjectData(data.projectDir), - hmrData, - config: { - env: data.env, - platform: device.platform, - appFilesUpdaterOptions: data.appFilesUpdaterOptions, - }, - externals: this.$previewAppPluginsService.getExternalPlugins(device), - filesToSyncMap, - startSyncFilesTimeout: startSyncFilesTimeout.bind(this) - } - }); + const hookArgs = this.getHookArgs(data, device); + await this.$hooksService.executeBeforeHooks("preview-sync", { hookArgs }); await this.$previewAppPluginsService.comparePluginsOnDevice(data, device); const payloads = await this.syncFilesForPlatformSafe(data, device.platform, { isInitialSync: true, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload }); return payloads; } + private getHookArgs(data: IPreviewAppLiveSyncData, device: Device) { + const filesToSyncMap: IDictionary = {}; + const hmrData: IDictionary = {}; + const promise = Promise.resolve(null); + const result = { + projectData: this.$projectDataService.getProjectData(data.projectDir), + hmrData, + config: { + env: data.env, + platform: device.platform, + appFilesUpdaterOptions: data.appFilesUpdaterOptions, + }, + externals: this.$previewAppPluginsService.getExternalPlugins(device), + filesToSyncMap, + startSyncFilesTimeout: async (platform: string) => await this.onWebpackCompilationComplete(data, hmrData, filesToSyncMap, promise, platform) + }; + + return result; + } + + private async onWebpackCompilationComplete(data: IPreviewAppLiveSyncData, hmrData: IDictionary, filesToSyncMap: IDictionary, promise: Promise, platform: string) { + await promise + .then(async () => { + const currentHmrData = _.cloneDeep(hmrData); + const platformHmrData = currentHmrData[platform] || {}; + const filesToSync = _.cloneDeep(filesToSyncMap[platform]); + // We don't need to prepare when webpack emits changed files. We just need to send a message to pubnub. + promise = this.syncFilesForPlatformSafe(data, platform, { filesToSync, skipPrepare: true, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload }); + await promise; + + if (data.appFilesUpdaterOptions.useHotModuleReload && platformHmrData.hash) { + const devices = _.filter(this.$previewSdkService.connectedDevices, { platform: platform.toLowerCase() }); + + await Promise.all(_.map(devices, async (previewDevice: Device) => { + const status = await this.$hmrStatusService.getHmrStatus(previewDevice.id, platformHmrData.hash); + if (status === HmrConstants.HMR_ERROR_STATUS) { + await this.syncFilesForPlatformSafe(data, platform, { filesToSync: platformHmrData.fallbackFiles, useHotModuleReload: false, deviceId: previewDevice.id }); + } + })); + } + }); + filesToSyncMap[platform] = []; + } + private async syncFilesForPlatformSafe(data: IPreviewAppLiveSyncData, platform: string, opts?: ISyncFilesOptions): Promise { this.$logger.info(`Start syncing changes for platform ${platform}.`); From f47f81eb786fb4a98fd5d75be6e31525c6119edd Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 08:36:48 +0200 Subject: [PATCH 15/26] feat(preview): add api for deviceFound and deviceLost for preview devices --- lib/bootstrap.ts | 1 + lib/definitions/preview-app-livesync.d.ts | 8 ++++- .../devices/preview-devices-service.ts | 36 +++++++++++++++++++ .../preview-app-livesync-service.ts | 7 ++-- .../playground/preview-sdk-service.ts | 22 +++++------- 5 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 lib/services/livesync/playground/devices/preview-devices-service.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 770a938183..adbc81da63 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -134,6 +134,7 @@ $injector.require("usbLiveSyncService", "./services/livesync/livesync-service"); $injector.require("previewAppLiveSyncService", "./services/livesync/playground/preview-app-livesync-service"); $injector.require("previewAppPluginsService", "./services/livesync/playground/preview-app-plugins-service"); $injector.require("previewSdkService", "./services/livesync/playground/preview-sdk-service"); +$injector.requirePublicClass("previewDevicesService", "./services/livesync/playground/devices/preview-devices-service"); $injector.require("playgroundQrCodeGenerator", "./services/livesync/playground/qr-code-generator"); $injector.requirePublic("sysInfo", "./sys-info"); diff --git a/lib/definitions/preview-app-livesync.d.ts b/lib/definitions/preview-app-livesync.d.ts index 81907e41ef..1ffdf34205 100644 --- a/lib/definitions/preview-app-livesync.d.ts +++ b/lib/definitions/preview-app-livesync.d.ts @@ -12,7 +12,6 @@ declare global { interface IPreviewSdkService extends EventEmitter { getQrCodeUrl(options: IHasUseHotModuleReloadOption): string; - connectedDevices: Device[]; initialize(getInitialFiles: (device: Device) => Promise): void; applyChanges(filesPayload: FilesPayload): Promise; stop(): void; @@ -34,4 +33,11 @@ declare global { */ link: boolean; } + + interface IPreviewDevicesService extends EventEmitter { + connectedDevices: Device[]; + onDevicesPresence(devices: Device[]): void; + getDeviceById(id: string): Device; + getDevicesForPlatform(platform: string): Device[]; + } } \ No newline at end of file diff --git a/lib/services/livesync/playground/devices/preview-devices-service.ts b/lib/services/livesync/playground/devices/preview-devices-service.ts new file mode 100644 index 0000000000..b6b560fced --- /dev/null +++ b/lib/services/livesync/playground/devices/preview-devices-service.ts @@ -0,0 +1,36 @@ +import { Device } from "nativescript-preview-sdk"; +import { EventEmitter } from "events"; +import { DeviceDiscoveryEventNames } from "../../../../common/constants"; + +export class PreviewDevicesService extends EventEmitter implements IPreviewDevicesService { + public connectedDevices: Device[] = []; + + public onDevicesPresence(devices: Device[]): void { + _(devices) + .reject(d => _.find(this.connectedDevices, device => d.id === device.id)) + .each(device => this.raiseDeviceFound(device)); + + _(this.connectedDevices) + .reject(d => _.find(devices, device => d.id === device.id)) + .each(device => this.raiseDeviceLost(device)); + } + + public getDeviceById(id: string): Device { + return _.find(this.connectedDevices, { id }); + } + + public getDevicesForPlatform(platform: string): Device[] { + return _.filter(this.connectedDevices, { platform: platform.toLowerCase() }); + } + + private raiseDeviceFound(device: Device) { + this.emit(DeviceDiscoveryEventNames.DEVICE_FOUND, device); + this.connectedDevices.push(device); + } + + private raiseDeviceLost(device: Device) { + this.emit(DeviceDiscoveryEventNames.DEVICE_LOST, device); + _.remove(this.connectedDevices, d => d.id === device.id); + } +} +$injector.register("previewDevicesService", PreviewDevicesService); diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index a968797994..77809dcf87 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -28,6 +28,7 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { private $projectDataService: IProjectDataService, private $previewSdkService: IPreviewSdkService, private $previewAppPluginsService: IPreviewAppPluginsService, + private $previewDevicesService: IPreviewDevicesService, private $projectFilesManager: IProjectFilesManager, private $hmrStatusService: IHmrStatusService, private $projectFilesProvider: IProjectFilesProvider) { } @@ -55,11 +56,11 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { public async syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise { this.showWarningsForNativeFiles(filesToSync); - for (const device of this.$previewSdkService.connectedDevices) { + for (const device of this.$previewDevicesService.connectedDevices) { await this.$previewAppPluginsService.comparePluginsOnDevice(data, device); } - const platforms = _(this.$previewSdkService.connectedDevices) + const platforms = _(this.$previewDevicesService.connectedDevices) .map(device => device.platform) .uniq() .value(); @@ -112,7 +113,7 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { await promise; if (data.appFilesUpdaterOptions.useHotModuleReload && platformHmrData.hash) { - const devices = _.filter(this.$previewSdkService.connectedDevices, { platform: platform.toLowerCase() }); + const devices = this.$previewDevicesService.getDevicesForPlatform(platform); await Promise.all(_.map(devices, async (previewDevice: Device) => { const status = await this.$hmrStatusService.getHmrStatus(previewDevice.id, platformHmrData.hash); diff --git a/lib/services/livesync/playground/preview-sdk-service.ts b/lib/services/livesync/playground/preview-sdk-service.ts index df72e02298..fe1dccefdc 100644 --- a/lib/services/livesync/playground/preview-sdk-service.ts +++ b/lib/services/livesync/playground/preview-sdk-service.ts @@ -1,18 +1,18 @@ import { MessagingService, Config, Device, DeviceConnectedMessage, SdkCallbacks, ConnectedDevices, FilesPayload } from "nativescript-preview-sdk"; import { PubnubKeys } from "./preview-app-constants"; -import { DEVICE_LOG_EVENT_NAME } from "../../../common/constants"; import { EventEmitter } from "events"; +import { DEVICE_LOG_EVENT_NAME } from "../../../common/constants"; const pako = require("pako"); export class PreviewSdkService extends EventEmitter implements IPreviewSdkService { private static MAX_FILES_UPLOAD_BYTE_LENGTH = 15 * 1024 * 1024; // In MBs private messagingService: MessagingService = null; private instanceId: string = null; - public connectedDevices: Device[] = []; - constructor(private $logger: ILogger, + constructor(private $config: IConfiguration, private $httpClient: Server.IHttpClient, - private $config: IConfiguration) { + private $logger: ILogger, + private $previewDevicesService: IPreviewDevicesService) { super(); } @@ -60,9 +60,8 @@ export class PreviewSdkService extends EventEmitter implements IPreviewSdkServic onLogSdkMessage: (log: string) => { this.$logger.trace("Received onLogSdkMessage message: ", log); }, - onConnectedDevicesChange: (connectedDevices: ConnectedDevices) => ({ }), onLogMessage: (log: string, deviceName: string, deviceId: string) => { - const device = _.find(this.connectedDevices, { id: deviceId}); + const device = this.$previewDevicesService.getDeviceById(deviceId); this.emit(DEVICE_LOG_EVENT_NAME, log, deviceId, device ? device.platform : ""); this.$logger.info(`LOG from device ${deviceName}: ${log}`); }, @@ -72,13 +71,10 @@ export class PreviewSdkService extends EventEmitter implements IPreviewSdkServic onUncaughtErrorMessage: () => { this.$logger.warn("The Preview app has terminated unexpectedly. Please run it again to get a detailed crash report."); }, - onDeviceConnectedMessage: (deviceConnectedMessage: DeviceConnectedMessage) => ({ }), - onDeviceConnected: (device: Device) => { - if (!_.find(this.connectedDevices, {id: device.id})) { - this.connectedDevices.push(device); - } - }, - onDevicesPresence: (devices: Device[]) => ({ }), + onConnectedDevicesChange: (connectedDevices: ConnectedDevices) => ({}), + onDeviceConnectedMessage: (deviceConnectedMessage: DeviceConnectedMessage) => ({}), + onDeviceConnected: (device: Device) => ({}), + onDevicesPresence: (devices: Device[]) => this.$previewDevicesService.onDevicesPresence(devices), onSendingChange: (sending: boolean) => ({ }), onBiggerFilesUpload: async (filesContent, callback) => { const gzippedContent = Buffer.from(pako.gzip(filesContent)); From ef016cf634b8ffb3a5d9094e43ba0e82808c48f4 Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 08:37:17 +0200 Subject: [PATCH 16/26] test(preview): add unit tests for previewDevicesService --- test/services/preview-devices-service.ts | 102 +++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 test/services/preview-devices-service.ts diff --git a/test/services/preview-devices-service.ts b/test/services/preview-devices-service.ts new file mode 100644 index 0000000000..3ab1988825 --- /dev/null +++ b/test/services/preview-devices-service.ts @@ -0,0 +1,102 @@ +import { Yok } from "../../lib/common/yok"; +import { PreviewDevicesService } from "../../lib/services/livesync/playground/devices/preview-devices-service"; +import { Device } from "nativescript-preview-sdk"; +import { assert } from "chai"; +import { DeviceDiscoveryEventNames } from "../../lib/common/constants"; +import { LoggerStub } from "../stubs"; + +let foundDevices: Device[] = []; +let lostDevices: Device[] = []; + +function createTestInjector(): IInjector { + const injector = new Yok(); + injector.register("previewDevicesService", PreviewDevicesService); + injector.register("logger", LoggerStub); + return injector; +} + +function createDevice(id: string): Device { + return { + id, + platform: "ios", + model: "my test model", + name: "my test name", + osVersion: "10.0.0", + previewAppVersion: "19.0.0", + runtimeVersion: "5.0.0" + }; +} + +describe("PreviewDevicesService", () => { + describe("onDevicesPresence", () => { + let previewDevicesService: IPreviewDevicesService = null; + beforeEach(() => { + const injector = createTestInjector(); + previewDevicesService = injector.resolve("previewDevicesService"); + previewDevicesService.on(DeviceDiscoveryEventNames.DEVICE_FOUND, device => { + foundDevices.push(device); + }); + previewDevicesService.on(DeviceDiscoveryEventNames.DEVICE_LOST, device => { + lostDevices.push(device); + }); + }); + + afterEach(() => { + previewDevicesService.removeAllListeners(); + foundDevices = []; + lostDevices = []; + }); + + it("should add new device", () => { + const device = createDevice("device1"); + + previewDevicesService.onDevicesPresence([device]); + + assert.deepEqual(previewDevicesService.connectedDevices, [device]); + assert.deepEqual(foundDevices, [device]); + assert.deepEqual(lostDevices, []); + }); + it("should add new device when there are already connected devices", () => { + const device1 = createDevice("device1"); + const device2 = createDevice("device2"); + previewDevicesService.connectedDevices = [device1]; + + previewDevicesService.onDevicesPresence([device1, device2]); + + assert.deepEqual(previewDevicesService.connectedDevices, [device1, device2]); + assert.deepEqual(foundDevices, [device2]); + assert.deepEqual(lostDevices, []); + }); + it("should add more than one new device", () => { + const device1 = createDevice("device1"); + const device2 = createDevice("device2"); + const device3 = createDevice("device3"); + + previewDevicesService.onDevicesPresence([device1, device2, device3]); + + assert.deepEqual(previewDevicesService.connectedDevices, [device1, device2, device3]); + assert.deepEqual(foundDevices, [device1, device2, device3]); + assert.deepEqual(lostDevices, []); + }); + it("should remove device", () => { + const device1 = createDevice("device1"); + previewDevicesService.connectedDevices = [device1]; + + previewDevicesService.onDevicesPresence([]); + + assert.deepEqual(foundDevices, []); + assert.deepEqual(lostDevices, [device1]); + }); + it("should add and remove devices in the same time", () => { + const device1 = createDevice("device1"); + const device2 = createDevice("device2"); + previewDevicesService.connectedDevices = [device1]; + + previewDevicesService.onDevicesPresence([device2]); + + assert.deepEqual(previewDevicesService.connectedDevices, [device2]); + assert.deepEqual(foundDevices, [device2]); + assert.deepEqual(lostDevices, [device1]); + }); + }); +}); From 2ab6781d6a7f72726d72d0ab75a5e70597253433 Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 09:33:17 +0200 Subject: [PATCH 17/26] fix(preview-unit-tests): inject previewDevicesService in order to fix unit tests --- test/services/playground/preview-app-livesync-service.ts | 4 +++- test/services/preview-sdk-service.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/services/playground/preview-app-livesync-service.ts b/test/services/playground/preview-app-livesync-service.ts index f93061b347..af6dfeb9c6 100644 --- a/test/services/playground/preview-app-livesync-service.ts +++ b/test/services/playground/preview-app-livesync-service.ts @@ -75,7 +75,6 @@ class PreviewSdkServiceMock extends EventEmitter implements IPreviewSdkService { return "my_cool_qr_code_url"; } - public connectedDevices: Device[] = [deviceMockData]; public initialize(getInitialFiles: (device: Device) => Promise) { this.getInitialFiles = async (device) => { const filesPayload = await getInitialFiles(device); @@ -158,6 +157,9 @@ function createTestInjector(options?: { isHookCalledWithHMR = args.hookArgs.config.appFilesUpdaterOptions.useHotModuleReload; } }); + injector.register("previewDevicesService", { + connectedDevices: [deviceMockData] + }); return injector; } diff --git a/test/services/preview-sdk-service.ts b/test/services/preview-sdk-service.ts index 0790c0de42..e4698c03e4 100644 --- a/test/services/preview-sdk-service.ts +++ b/test/services/preview-sdk-service.ts @@ -8,7 +8,7 @@ const getPreviewSdkService = (): IPreviewSdkService => { testInjector.register("logger", LoggerStub); testInjector.register("config", {}); testInjector.register("previewSdkService", PreviewSdkService); - + testInjector.register("previewDevicesService", {}); testInjector.register("httpClient", { httpRequest: async (options: any, proxySettings?: IProxySettings): Promise => undefined }); From fcd6c4c5eea40745cdd58aeaf82c1113207ef4bc Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 13:53:22 +0200 Subject: [PATCH 18/26] chore(preview): handle PR comments --- lib/definitions/preview-app-livesync.d.ts | 4 +- .../devices/preview-devices-service.ts | 8 +++- .../preview-app-livesync-service.ts | 6 +-- .../playground/preview-sdk-service.ts | 2 +- .../preview-app-livesync-service.ts | 2 +- test/services/preview-devices-service.ts | 46 +++++++++++++------ 6 files changed, 45 insertions(+), 23 deletions(-) diff --git a/lib/definitions/preview-app-livesync.d.ts b/lib/definitions/preview-app-livesync.d.ts index 1ffdf34205..23bcbdb491 100644 --- a/lib/definitions/preview-app-livesync.d.ts +++ b/lib/definitions/preview-app-livesync.d.ts @@ -35,8 +35,8 @@ declare global { } interface IPreviewDevicesService extends EventEmitter { - connectedDevices: Device[]; - onDevicesPresence(devices: Device[]): void; + getConnectedDevices(): Device[]; + updateConnectedDevices(devices: Device[]): void; getDeviceById(id: string): Device; getDevicesForPlatform(platform: string): Device[]; } diff --git a/lib/services/livesync/playground/devices/preview-devices-service.ts b/lib/services/livesync/playground/devices/preview-devices-service.ts index b6b560fced..09e18f275a 100644 --- a/lib/services/livesync/playground/devices/preview-devices-service.ts +++ b/lib/services/livesync/playground/devices/preview-devices-service.ts @@ -3,9 +3,13 @@ import { EventEmitter } from "events"; import { DeviceDiscoveryEventNames } from "../../../../common/constants"; export class PreviewDevicesService extends EventEmitter implements IPreviewDevicesService { - public connectedDevices: Device[] = []; + private connectedDevices: Device[] = []; - public onDevicesPresence(devices: Device[]): void { + public getConnectedDevices(): Device[] { + return this.connectedDevices; + } + + public updateConnectedDevices(devices: Device[]): void { _(devices) .reject(d => _.find(this.connectedDevices, device => d.id === device.id)) .each(device => this.raiseDeviceFound(device)); diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index 77809dcf87..a1727430a0 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -56,15 +56,15 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService { public async syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise { this.showWarningsForNativeFiles(filesToSync); - for (const device of this.$previewDevicesService.connectedDevices) { + const connectedDevices = this.$previewDevicesService.getConnectedDevices(); + for (const device of connectedDevices) { await this.$previewAppPluginsService.comparePluginsOnDevice(data, device); } - const platforms = _(this.$previewDevicesService.connectedDevices) + const platforms = _(connectedDevices) .map(device => device.platform) .uniq() .value(); - for (const platform of platforms) { await this.syncFilesForPlatformSafe(data, platform, { filesToSync, filesToRemove, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload }); } diff --git a/lib/services/livesync/playground/preview-sdk-service.ts b/lib/services/livesync/playground/preview-sdk-service.ts index fe1dccefdc..603f1bbae5 100644 --- a/lib/services/livesync/playground/preview-sdk-service.ts +++ b/lib/services/livesync/playground/preview-sdk-service.ts @@ -74,7 +74,7 @@ export class PreviewSdkService extends EventEmitter implements IPreviewSdkServic onConnectedDevicesChange: (connectedDevices: ConnectedDevices) => ({}), onDeviceConnectedMessage: (deviceConnectedMessage: DeviceConnectedMessage) => ({}), onDeviceConnected: (device: Device) => ({}), - onDevicesPresence: (devices: Device[]) => this.$previewDevicesService.onDevicesPresence(devices), + onDevicesPresence: (devices: Device[]) => this.$previewDevicesService.updateConnectedDevices(devices), onSendingChange: (sending: boolean) => ({ }), onBiggerFilesUpload: async (filesContent, callback) => { const gzippedContent = Buffer.from(pako.gzip(filesContent)); diff --git a/test/services/playground/preview-app-livesync-service.ts b/test/services/playground/preview-app-livesync-service.ts index af6dfeb9c6..d4ab2e2551 100644 --- a/test/services/playground/preview-app-livesync-service.ts +++ b/test/services/playground/preview-app-livesync-service.ts @@ -158,7 +158,7 @@ function createTestInjector(options?: { } }); injector.register("previewDevicesService", { - connectedDevices: [deviceMockData] + getConnectedDevices: () => [deviceMockData] }); return injector; diff --git a/test/services/preview-devices-service.ts b/test/services/preview-devices-service.ts index 3ab1988825..7fc2305115 100644 --- a/test/services/preview-devices-service.ts +++ b/test/services/preview-devices-service.ts @@ -27,6 +27,11 @@ function createDevice(id: string): Device { }; } +function resetDevices() { + foundDevices = []; + lostDevices = []; +} + describe("PreviewDevicesService", () => { describe("onDevicesPresence", () => { let previewDevicesService: IPreviewDevicesService = null; @@ -43,27 +48,31 @@ describe("PreviewDevicesService", () => { afterEach(() => { previewDevicesService.removeAllListeners(); - foundDevices = []; - lostDevices = []; + resetDevices(); }); it("should add new device", () => { const device = createDevice("device1"); - previewDevicesService.onDevicesPresence([device]); + previewDevicesService.updateConnectedDevices([device]); - assert.deepEqual(previewDevicesService.connectedDevices, [device]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device]); assert.deepEqual(foundDevices, [device]); assert.deepEqual(lostDevices, []); }); it("should add new device when there are already connected devices", () => { const device1 = createDevice("device1"); const device2 = createDevice("device2"); - previewDevicesService.connectedDevices = [device1]; - previewDevicesService.onDevicesPresence([device1, device2]); + previewDevicesService.updateConnectedDevices([device1]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]); + assert.deepEqual(foundDevices, [device1]); + assert.deepEqual(lostDevices, []); + resetDevices(); + + previewDevicesService.updateConnectedDevices([device1, device2]); - assert.deepEqual(previewDevicesService.connectedDevices, [device1, device2]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1, device2]); assert.deepEqual(foundDevices, [device2]); assert.deepEqual(lostDevices, []); }); @@ -72,17 +81,21 @@ describe("PreviewDevicesService", () => { const device2 = createDevice("device2"); const device3 = createDevice("device3"); - previewDevicesService.onDevicesPresence([device1, device2, device3]); + previewDevicesService.updateConnectedDevices([device1, device2, device3]); - assert.deepEqual(previewDevicesService.connectedDevices, [device1, device2, device3]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1, device2, device3]); assert.deepEqual(foundDevices, [device1, device2, device3]); assert.deepEqual(lostDevices, []); }); it("should remove device", () => { const device1 = createDevice("device1"); - previewDevicesService.connectedDevices = [device1]; + previewDevicesService.updateConnectedDevices([device1]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]); + assert.deepEqual(foundDevices, [device1]); + assert.deepEqual(lostDevices, []); + resetDevices(); - previewDevicesService.onDevicesPresence([]); + previewDevicesService.updateConnectedDevices([]); assert.deepEqual(foundDevices, []); assert.deepEqual(lostDevices, [device1]); @@ -90,11 +103,16 @@ describe("PreviewDevicesService", () => { it("should add and remove devices in the same time", () => { const device1 = createDevice("device1"); const device2 = createDevice("device2"); - previewDevicesService.connectedDevices = [device1]; - previewDevicesService.onDevicesPresence([device2]); + previewDevicesService.updateConnectedDevices([device1]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device1]); + assert.deepEqual(foundDevices, [device1]); + assert.deepEqual(lostDevices, []); + resetDevices(); + + previewDevicesService.updateConnectedDevices([device2]); - assert.deepEqual(previewDevicesService.connectedDevices, [device2]); + assert.deepEqual(previewDevicesService.getConnectedDevices(), [device2]); assert.deepEqual(foundDevices, [device2]); assert.deepEqual(lostDevices, [device1]); }); From 7d78af98bde1da98932ee08133fa666bb812c57e Mon Sep 17 00:00:00 2001 From: fatme Date: Thu, 1 Nov 2018 18:58:18 +0200 Subject: [PATCH 19/26] fix(unit-testing): add correct files pattern in karma.conf.js when the project has nsconfig file --- lib/commands/test-init.ts | 3 ++- resources/test/karma.conf.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/commands/test-init.ts b/lib/commands/test-init.ts index 0133a56665..2f8921ed9c 100644 --- a/lib/commands/test-init.ts +++ b/lib/commands/test-init.ts @@ -105,8 +105,9 @@ class TestInitCommand implements ICommand { const frameworks = [frameworkToInstall].concat(this.karmaConfigAdditionalFrameworks[frameworkToInstall] || []) .map(fw => `'${fw}'`) .join(', '); + const testFiles = `'${relativeTestsDir}/**/*.js'`; const karmaConfTemplate = this.$resources.readText('test/karma.conf.js'); - const karmaConf = _.template(karmaConfTemplate)({ frameworks }); + const karmaConf = _.template(karmaConfTemplate)({ frameworks, testFiles }); this.$fs.writeFile(path.join(projectDir, 'karma.conf.js'), karmaConf); diff --git a/resources/test/karma.conf.js b/resources/test/karma.conf.js index 0dd269a0ac..5fc70a7fb1 100644 --- a/resources/test/karma.conf.js +++ b/resources/test/karma.conf.js @@ -12,7 +12,7 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ - 'app/**/*.js', + ${ testFiles } ], From e6b79687af8a64912881aca817750b0c198879fb Mon Sep 17 00:00:00 2001 From: fatme Date: Mon, 29 Oct 2018 19:36:25 +0200 Subject: [PATCH 20/26] fix(debug-ios): start log process on `tns debug ios --justlaunch` command --- lib/common/definitions/mobile.d.ts | 17 ++++++--- lib/common/mobile/device-log-provider-base.ts | 6 +++- lib/common/mobile/device-log-provider.ts | 8 ++++- lib/common/test/unit-tests/stubs.ts | 2 ++ lib/services/ios-debug-service.ts | 35 +++++++++++-------- 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/lib/common/definitions/mobile.d.ts b/lib/common/definitions/mobile.d.ts index 532c3aee2d..6a25892a7c 100644 --- a/lib/common/definitions/mobile.d.ts +++ b/lib/common/definitions/mobile.d.ts @@ -143,10 +143,6 @@ declare module Mobile { --predicate 'eventType == logEvent and subsystem contains "com.example.my_subsystem"' */ predicate?: string; - /** - * If set to true, device's log will not be displayed on the console. - */ - muted?: boolean; } interface IDeviceAppData extends IPlatform, IConnectTimeoutOption { @@ -221,12 +217,18 @@ declare module Mobile { * @param {string} projectName The project name of the currently running application for which we need the logs. */ setProjectNameForDevice(deviceIdentifier: string, projectName: string): void; + + /** + * Disables logs on the specified device and does not print any logs on the console. + * @param {string} deviceIdentifier The unique identifier of the device. + */ + muteLogsForDevice(deviceIdentifier: string): void; } /** * Describes different options for filtering device logs. */ - interface IDeviceLogOptions extends IStringDictionary { + interface IDeviceLogOptions extends IDictionary { /** * Process id of the application on the device. */ @@ -241,6 +243,11 @@ declare module Mobile { * The project name. */ projectName?: string; + + /** + * Specifies if the logs will be printed on the console. + */ + muteLogs?: boolean; } /** diff --git a/lib/common/mobile/device-log-provider-base.ts b/lib/common/mobile/device-log-provider-base.ts index 4662b4fbe0..9954d599bb 100644 --- a/lib/common/mobile/device-log-provider-base.ts +++ b/lib/common/mobile/device-log-provider-base.ts @@ -26,6 +26,10 @@ export abstract class DeviceLogProviderBase extends EventEmitter implements Mobi this.setLogLevel(logLevel, deviceIdentifier); } + public muteLogsForDevice(deviceIdentifier: string): void { + this.setDeviceLogOptionsProperty(deviceIdentifier, (deviceLogOptions: Mobile.IDeviceLogOptions) => deviceLogOptions.muteLogs, true); + } + protected getApplicationPidForDevice(deviceIdentifier: string): string { return this.devicesLogOptions[deviceIdentifier] && this.devicesLogOptions[deviceIdentifier].applicationPid; } @@ -39,7 +43,7 @@ export abstract class DeviceLogProviderBase extends EventEmitter implements Mobi return this.devicesLogOptions[deviceIdentifier]; } - protected setDeviceLogOptionsProperty(deviceIdentifier: string, propNameFunction: Function, propertyValue: string): void { + protected setDeviceLogOptionsProperty(deviceIdentifier: string, propNameFunction: Function, propertyValue: string | boolean): void { const propertyName = getPropertyName(propNameFunction); if (propertyName) { diff --git a/lib/common/mobile/device-log-provider.ts b/lib/common/mobile/device-log-provider.ts index d6dded6d46..6a83624eb2 100644 --- a/lib/common/mobile/device-log-provider.ts +++ b/lib/common/mobile/device-log-provider.ts @@ -11,7 +11,7 @@ export class DeviceLogProvider extends DeviceLogProviderBase { const loggingOptions = this.getDeviceLogOptionsForDevice(deviceIdentifier); const data = this.$logFilter.filterData(platform, lineText, loggingOptions); if (data) { - this.$logger.write(data); + this.logDataCore(data, loggingOptions); this.emit(DEVICE_LOG_EVENT_NAME, lineText, deviceIdentifier, platform); } } @@ -19,5 +19,11 @@ export class DeviceLogProvider extends DeviceLogProviderBase { public setLogLevel(logLevel: string, deviceIdentifier?: string): void { this.$logFilter.loggingLevel = logLevel.toUpperCase(); } + + private logDataCore(data: string, loggingOptions: Mobile.IDeviceLogOptions): void { + if (!loggingOptions || (loggingOptions && !loggingOptions.muteLogs)) { + this.$logger.write(data); + } + } } $injector.register("deviceLogProvider", DeviceLogProvider); diff --git a/lib/common/test/unit-tests/stubs.ts b/lib/common/test/unit-tests/stubs.ts index f1f9709623..52f17bc965 100644 --- a/lib/common/test/unit-tests/stubs.ts +++ b/lib/common/test/unit-tests/stubs.ts @@ -171,4 +171,6 @@ export class DeviceLogProviderStub extends EventEmitter implements Mobile.IDevic setProjectNameForDevice(deviceIdentifier: string, projectName: string): void { this.currentDeviceProjectNames[deviceIdentifier] = projectName; } + + muteLogsForDevice(deviceIdentifier: string): void { } } diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 2fa6749ad6..91234ee881 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -50,20 +50,7 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS debugOptions.emulator = true; } - if (!debugOptions.justlaunch) { - let projectName = debugData.projectName; - if (!projectName && debugData.projectDir) { - const projectData = this.$projectDataService.getProjectData(debugData.projectDir); - projectName = projectData.projectName; - } - - if (projectName) { - this.$deviceLogProvider.setProjectNameForDevice(debugData.deviceIdentifier, projectName); - } - - await this.device.openDeviceLogStream({ predicate: IOS_LOG_PREDICATE }); - } - + await this.startDeviceLogProcess(debugData, debugOptions); await this.$iOSDebuggerPortService.attachToDebuggerPortFoundEvent(this.device, debugData, debugOptions); if (debugOptions.emulator) { @@ -113,6 +100,26 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS return chromeDebugUrl; } + private async startDeviceLogProcess(debugData: IDebugData, debugOptions: IDebugOptions): Promise { + if (debugOptions.justlaunch) { + // No logs should be printed on console when `--justlaunch` option is passed. + // On the other side we need to start log process in order to get debugger port from logs. + this.$deviceLogProvider.muteLogsForDevice(debugData.deviceIdentifier); + } + + let projectName = debugData.projectName; + if (!projectName && debugData.projectDir) { + const projectData = this.$projectDataService.getProjectData(debugData.projectDir); + projectName = projectData.projectName; + } + + if (projectName) { + this.$deviceLogProvider.setProjectNameForDevice(debugData.deviceIdentifier, projectName); + } + + await this.device.openDeviceLogStream({ predicate: IOS_LOG_PREDICATE }); + } + private async killProcess(childProcess: ChildProcess): Promise { if (childProcess) { return new Promise((resolve, reject) => { From bb04c9913882f6c8899eef96010b2701429b1234 Mon Sep 17 00:00:00 2001 From: fatme Date: Fri, 2 Nov 2018 11:48:16 +0200 Subject: [PATCH 21/26] refactor(preview): rename playgroundQrCodeGenerator to previewQrCodeService --- lib/bootstrap.ts | 2 +- lib/commands/preview.ts | 4 ++-- lib/definitions/preview-app-livesync.d.ts | 2 +- .../{qr-code-generator.ts => preview-qr-code-service.ts} | 4 ++-- lib/services/platform-environment-requirements.ts | 4 ++-- test/services/platform-environment-requirements.ts | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) rename lib/services/livesync/playground/{qr-code-generator.ts => preview-qr-code-service.ts} (90%) diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index adbc81da63..c7eee58e08 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -135,7 +135,7 @@ $injector.require("previewAppLiveSyncService", "./services/livesync/playground/p $injector.require("previewAppPluginsService", "./services/livesync/playground/preview-app-plugins-service"); $injector.require("previewSdkService", "./services/livesync/playground/preview-sdk-service"); $injector.requirePublicClass("previewDevicesService", "./services/livesync/playground/devices/preview-devices-service"); -$injector.require("playgroundQrCodeGenerator", "./services/livesync/playground/qr-code-generator"); +$injector.require("previewQrCodeService", "./services/livesync/playground/preview-qr-code-service"); $injector.requirePublic("sysInfo", "./sys-info"); $injector.require("iOSNotificationService", "./services/ios-notification-service"); diff --git a/lib/commands/preview.ts b/lib/commands/preview.ts index 1926fcf8bf..a0dd1ac865 100644 --- a/lib/commands/preview.ts +++ b/lib/commands/preview.ts @@ -8,7 +8,7 @@ export class PreviewCommand implements ICommand { private $networkConnectivityValidator: INetworkConnectivityValidator, private $projectData: IProjectData, private $options: IOptions, - private $playgroundQrCodeGenerator: IPlaygroundQrCodeGenerator) { } + private $previewQrCodeService: IPreviewQrCodeService) { } public async execute(): Promise { await this.$liveSyncService.liveSync([], { @@ -24,7 +24,7 @@ export class PreviewCommand implements ICommand { useHotModuleReload: this.$options.hmr }); - await this.$playgroundQrCodeGenerator.generateQrCode({ useHotModuleReload: this.$options.hmr, link: this.$options.link }); + await this.$previewQrCodeService.generateQrCode({ useHotModuleReload: this.$options.hmr, link: this.$options.link }); } public async canExecute(args: string[]): Promise { diff --git a/lib/definitions/preview-app-livesync.d.ts b/lib/definitions/preview-app-livesync.d.ts index 23bcbdb491..d8b75c1aa1 100644 --- a/lib/definitions/preview-app-livesync.d.ts +++ b/lib/definitions/preview-app-livesync.d.ts @@ -22,7 +22,7 @@ declare global { getExternalPlugins(device: Device): string[]; } - interface IPlaygroundQrCodeGenerator { + interface IPreviewQrCodeService { generateQrCode(options: IGenerateQrCodeOptions): Promise; } diff --git a/lib/services/livesync/playground/qr-code-generator.ts b/lib/services/livesync/playground/preview-qr-code-service.ts similarity index 90% rename from lib/services/livesync/playground/qr-code-generator.ts rename to lib/services/livesync/playground/preview-qr-code-service.ts index cea5810478..8a57058814 100644 --- a/lib/services/livesync/playground/qr-code-generator.ts +++ b/lib/services/livesync/playground/preview-qr-code-service.ts @@ -2,7 +2,7 @@ import * as util from "util"; import { EOL } from "os"; import { PlaygroundStoreUrls } from "./preview-app-constants"; -export class PlaygroundQrCodeGenerator implements IPlaygroundQrCodeGenerator { +export class PreviewQrCodeService implements IPreviewQrCodeService { constructor(private $previewSdkService: IPreviewSdkService, private $httpClient: Server.IHttpClient, private $qrCodeTerminalService: IQrCodeTerminalService, @@ -39,4 +39,4 @@ To scan the QR code and deploy your app on a device, you need to have the \`Nati } } } -$injector.register("playgroundQrCodeGenerator", PlaygroundQrCodeGenerator); +$injector.register("previewQrCodeService", PreviewQrCodeService); diff --git a/lib/services/platform-environment-requirements.ts b/lib/services/platform-environment-requirements.ts index 284a399006..130b8256b3 100644 --- a/lib/services/platform-environment-requirements.ts +++ b/lib/services/platform-environment-requirements.ts @@ -13,7 +13,7 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ private $staticConfig: IStaticConfig, private $analyticsService: IAnalyticsService, private $injector: IInjector, - private $playgroundQrCodeGenerator: IPlaygroundQrCodeGenerator) { } + private $previewQrCodeService: IPreviewQrCodeService) { } @cache() private get $liveSyncService(): ILiveSyncService { @@ -194,7 +194,7 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ useHotModuleReload: options.hmr }); - await this.$playgroundQrCodeGenerator.generateQrCode({ useHotModuleReload: options.hmr, link: options.link }); + await this.$previewQrCodeService.generateQrCode({ useHotModuleReload: options.hmr, link: options.link }); } } diff --git a/test/services/platform-environment-requirements.ts b/test/services/platform-environment-requirements.ts index dce5ab3806..5dab5825ff 100644 --- a/test/services/platform-environment-requirements.ts +++ b/test/services/platform-environment-requirements.ts @@ -28,7 +28,7 @@ function createTestInjector() { testInjector.register("platformEnvironmentRequirements", PlatformEnvironmentRequirements); testInjector.register("staticConfig", { SYS_REQUIREMENTS_LINK: "" }); testInjector.register("nativeScriptCloudExtensionService", {}); - testInjector.register("playgroundQrCodeGenerator", {}); + testInjector.register("previewQrCodeService", {}); return testInjector; } From 8d55d274c4d43e96498756ceec808fdb7d5e7c51 Mon Sep 17 00:00:00 2001 From: fatme Date: Fri, 2 Nov 2018 11:55:07 +0200 Subject: [PATCH 22/26] refactor(preview): rename method generateQrCode to printLiveSyncQrCode --- lib/commands/preview.ts | 2 +- lib/definitions/preview-app-livesync.d.ts | 2 +- lib/services/livesync/playground/preview-qr-code-service.ts | 2 +- lib/services/platform-environment-requirements.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/commands/preview.ts b/lib/commands/preview.ts index a0dd1ac865..bc42413b3f 100644 --- a/lib/commands/preview.ts +++ b/lib/commands/preview.ts @@ -24,7 +24,7 @@ export class PreviewCommand implements ICommand { useHotModuleReload: this.$options.hmr }); - await this.$previewQrCodeService.generateQrCode({ useHotModuleReload: this.$options.hmr, link: this.$options.link }); + await this.$previewQrCodeService.printLiveSyncQrCode({ useHotModuleReload: this.$options.hmr, link: this.$options.link }); } public async canExecute(args: string[]): Promise { diff --git a/lib/definitions/preview-app-livesync.d.ts b/lib/definitions/preview-app-livesync.d.ts index d8b75c1aa1..98a950eaa2 100644 --- a/lib/definitions/preview-app-livesync.d.ts +++ b/lib/definitions/preview-app-livesync.d.ts @@ -23,7 +23,7 @@ declare global { } interface IPreviewQrCodeService { - generateQrCode(options: IGenerateQrCodeOptions): Promise; + printLiveSyncQrCode(options: IGenerateQrCodeOptions): Promise; } interface IGenerateQrCodeOptions extends IHasUseHotModuleReloadOption { diff --git a/lib/services/livesync/playground/preview-qr-code-service.ts b/lib/services/livesync/playground/preview-qr-code-service.ts index 8a57058814..a9cda830bd 100644 --- a/lib/services/livesync/playground/preview-qr-code-service.ts +++ b/lib/services/livesync/playground/preview-qr-code-service.ts @@ -10,7 +10,7 @@ export class PreviewQrCodeService implements IPreviewQrCodeService { private $logger: ILogger) { } - public async generateQrCode(options: IGenerateQrCodeOptions): Promise { + public async printLiveSyncQrCode(options: IGenerateQrCodeOptions): Promise { let url = this.$previewSdkService.getQrCodeUrl(options); const shortenUrlEndpoint = util.format(this.$config.SHORTEN_URL_ENDPOINT, encodeURIComponent(url)); try { diff --git a/lib/services/platform-environment-requirements.ts b/lib/services/platform-environment-requirements.ts index 130b8256b3..4cbd86c4ad 100644 --- a/lib/services/platform-environment-requirements.ts +++ b/lib/services/platform-environment-requirements.ts @@ -194,7 +194,7 @@ export class PlatformEnvironmentRequirements implements IPlatformEnvironmentRequ useHotModuleReload: options.hmr }); - await this.$previewQrCodeService.generateQrCode({ useHotModuleReload: options.hmr, link: options.link }); + await this.$previewQrCodeService.printLiveSyncQrCode({ useHotModuleReload: options.hmr, link: options.link }); } } From 7ad00182a2186f66dad3919bf4ee398f0adab162 Mon Sep 17 00:00:00 2001 From: fatme Date: Fri, 2 Nov 2018 12:48:33 +0200 Subject: [PATCH 23/26] feat(preview-api): expose public method for getting qr code of playground app --- lib/bootstrap.ts | 2 +- lib/common/declarations.d.ts | 15 +++++ lib/definitions/preview-app-livesync.d.ts | 5 ++ .../playground/preview-qr-code-service.ts | 59 +++++++++++++++---- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index c7eee58e08..b6283ba8f9 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -135,7 +135,7 @@ $injector.require("previewAppLiveSyncService", "./services/livesync/playground/p $injector.require("previewAppPluginsService", "./services/livesync/playground/preview-app-plugins-service"); $injector.require("previewSdkService", "./services/livesync/playground/preview-sdk-service"); $injector.requirePublicClass("previewDevicesService", "./services/livesync/playground/devices/preview-devices-service"); -$injector.require("previewQrCodeService", "./services/livesync/playground/preview-qr-code-service"); +$injector.requirePublic("previewQrCodeService", "./services/livesync/playground/preview-qr-code-service"); $injector.requirePublic("sysInfo", "./sys-info"); $injector.require("iOSNotificationService", "./services/ios-notification-service"); diff --git a/lib/common/declarations.d.ts b/lib/common/declarations.d.ts index 8aedd1092c..06bd4e3061 100644 --- a/lib/common/declarations.d.ts +++ b/lib/common/declarations.d.ts @@ -969,6 +969,21 @@ interface IQrCodeGenerator { generateDataUri(data: string): Promise; } +interface IQrCodeImageData { + /** + * The original URL used for generating QR code image. + */ + originalUrl: string; + /** + * The shorten URL used for generating QR code image. + */ + shortenUrl: string; + /** + * Base64 encoded data used for generating QR code image. + */ + imageData: string; +} + interface IDynamicHelpProvider { /** * Checks if current project's framework is one of the specified as arguments. diff --git a/lib/definitions/preview-app-livesync.d.ts b/lib/definitions/preview-app-livesync.d.ts index 98a950eaa2..18d61ac080 100644 --- a/lib/definitions/preview-app-livesync.d.ts +++ b/lib/definitions/preview-app-livesync.d.ts @@ -23,9 +23,14 @@ declare global { } interface IPreviewQrCodeService { + getPlaygroundAppQrCode(options?: IPlaygroundAppQrCodeOptions): Promise>; printLiveSyncQrCode(options: IGenerateQrCodeOptions): Promise; } + interface IPlaygroundAppQrCodeOptions { + platform?: string; + } + interface IGenerateQrCodeOptions extends IHasUseHotModuleReloadOption { /** * If set to true, a link will be shown on console instead of QR code diff --git a/lib/services/livesync/playground/preview-qr-code-service.ts b/lib/services/livesync/playground/preview-qr-code-service.ts index a9cda830bd..b9bd64e777 100644 --- a/lib/services/livesync/playground/preview-qr-code-service.ts +++ b/lib/services/livesync/playground/preview-qr-code-service.ts @@ -1,25 +1,37 @@ import * as util from "util"; import { EOL } from "os"; import { PlaygroundStoreUrls } from "./preview-app-constants"; +import { exported } from "../../../common/decorators"; export class PreviewQrCodeService implements IPreviewQrCodeService { - constructor(private $previewSdkService: IPreviewSdkService, + constructor( + private $config: IConfiguration, private $httpClient: Server.IHttpClient, + private $logger: ILogger, + private $mobileHelper: Mobile.IMobileHelper, + private $previewSdkService: IPreviewSdkService, private $qrCodeTerminalService: IQrCodeTerminalService, - private $config: IConfiguration, - private $logger: ILogger) { + private $qr: IQrCodeGenerator + ) { } + + @exported("previewQrCodeService") + public async getPlaygroundAppQrCode(options?: IPlaygroundAppQrCodeOptions): Promise> { + const result = Object.create(null); + + if (!options || !options.platform || this.$mobileHelper.isAndroidPlatform(options.platform)) { + result.android = await this.getQrCodeImageData(PlaygroundStoreUrls.GOOGLE_PLAY_URL); + } + + if (!options || !options.platform || this.$mobileHelper.isiOSPlatform(options.platform)) { + result.ios = await this.getQrCodeImageData(PlaygroundStoreUrls.APP_STORE_URL); + } + + return result; } public async printLiveSyncQrCode(options: IGenerateQrCodeOptions): Promise { - let url = this.$previewSdkService.getQrCodeUrl(options); - const shortenUrlEndpoint = util.format(this.$config.SHORTEN_URL_ENDPOINT, encodeURIComponent(url)); - try { - const response = await this.$httpClient.httpRequest(shortenUrlEndpoint); - const responseBody = JSON.parse(response.body); - url = responseBody.shortURL || url; - } catch (e) { - // use the longUrl - } + const qrCodeUrl = this.$previewSdkService.getQrCodeUrl(options); + const url = await this.getShortenUrl(qrCodeUrl); this.$logger.info(); const message = `${EOL} Generating qrcode for url ${url}.`; @@ -38,5 +50,28 @@ To scan the QR code and deploy your app on a device, you need to have the \`Nati Google Play (Android): ${PlaygroundStoreUrls.GOOGLE_PLAY_URL}`); } } + + private async getShortenUrl(url: string): Promise { + const shortenUrlEndpoint = util.format(this.$config.SHORTEN_URL_ENDPOINT, encodeURIComponent(url)); + try { + const response = await this.$httpClient.httpRequest(shortenUrlEndpoint); + const responseBody = JSON.parse(response.body); + url = responseBody.shortURL || url; + } catch (e) { + // use the longUrl + } + + return url; + } + + private async getQrCodeImageData(url: string): Promise { + const shortenUrl = await this.getShortenUrl(url); + const imageData = await this.$qr.generateDataUri(shortenUrl); + return { + originalUrl: url, + shortenUrl, + imageData + }; + } } $injector.register("previewQrCodeService", PreviewQrCodeService); From bd663026196530226a5c22365590c31a5f748f59 Mon Sep 17 00:00:00 2001 From: fatme Date: Fri, 2 Nov 2018 12:51:44 +0200 Subject: [PATCH 24/26] refactor(preview): rename IGenerateQrCodeOptions to IPrintLiveSyncOptions --- lib/definitions/preview-app-livesync.d.ts | 4 ++-- lib/services/livesync/playground/preview-qr-code-service.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/definitions/preview-app-livesync.d.ts b/lib/definitions/preview-app-livesync.d.ts index 18d61ac080..bbee82aa56 100644 --- a/lib/definitions/preview-app-livesync.d.ts +++ b/lib/definitions/preview-app-livesync.d.ts @@ -24,14 +24,14 @@ declare global { interface IPreviewQrCodeService { getPlaygroundAppQrCode(options?: IPlaygroundAppQrCodeOptions): Promise>; - printLiveSyncQrCode(options: IGenerateQrCodeOptions): Promise; + printLiveSyncQrCode(options: IPrintLiveSyncOptions): Promise; } interface IPlaygroundAppQrCodeOptions { platform?: string; } - interface IGenerateQrCodeOptions extends IHasUseHotModuleReloadOption { + interface IPrintLiveSyncOptions extends IHasUseHotModuleReloadOption { /** * If set to true, a link will be shown on console instead of QR code * Default value is false. diff --git a/lib/services/livesync/playground/preview-qr-code-service.ts b/lib/services/livesync/playground/preview-qr-code-service.ts index b9bd64e777..3421fe4903 100644 --- a/lib/services/livesync/playground/preview-qr-code-service.ts +++ b/lib/services/livesync/playground/preview-qr-code-service.ts @@ -29,7 +29,7 @@ export class PreviewQrCodeService implements IPreviewQrCodeService { return result; } - public async printLiveSyncQrCode(options: IGenerateQrCodeOptions): Promise { + public async printLiveSyncQrCode(options: IPrintLiveSyncOptions): Promise { const qrCodeUrl = this.$previewSdkService.getQrCodeUrl(options); const url = await this.getShortenUrl(qrCodeUrl); From 59f2ed30e2c277a2f3e7a1f93fc76c7e6d1483fa Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Fri, 2 Nov 2018 15:37:59 +0200 Subject: [PATCH 25/26] fix: install the latest compatible v2 templates in stead of the latest tag --- lib/services/project-templates-service.ts | 3 ++- test/project-templates-service.ts | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/services/project-templates-service.ts b/lib/services/project-templates-service.ts index fba5c5cdd8..08aaba1af2 100644 --- a/lib/services/project-templates-service.ts +++ b/lib/services/project-templates-service.ts @@ -30,7 +30,8 @@ export class ProjectTemplatesService implements IProjectTemplatesService { } const templateName = constants.RESERVED_TEMPLATE_NAMES[name.toLowerCase()] || name; - const fullTemplateName = version ? `${templateName}@${version}` : templateName; + version = version || await this.$npmInstallationManager.getLatestCompatibleVersion(templateName); + const fullTemplateName = `${templateName}@${version}`; const templatePackageJsonContent = await this.getTemplatePackageJsonContent(fullTemplateName); const templateVersion = await this.getTemplateVersion(fullTemplateName); diff --git a/test/project-templates-service.ts b/test/project-templates-service.ts index ad9e12b27d..f145080c75 100644 --- a/test/project-templates-service.ts +++ b/test/project-templates-service.ts @@ -8,6 +8,7 @@ import { format } from "util"; let isDeleteDirectoryCalledForNodeModulesDir = false; const nativeScriptValidatedTemplatePath = "nsValidatedTemplatePath"; +const compatibleTemplateVersion = "1.2.3"; function createTestInjector(configuration: { shouldNpmInstallThrow?: boolean, packageJsonContent?: any } = {}): IInjector { const injector = new Yok(); @@ -42,6 +43,9 @@ function createTestInjector(configuration: { shouldNpmInstallThrow?: boolean, pa } return Promise.resolve(nativeScriptValidatedTemplatePath); + }, + getLatestCompatibleVersion: (packageName: string) => { + return compatibleTemplateVersion; } }); @@ -164,7 +168,7 @@ describe("project-templates-service", () => { const fs = testInjector.resolve("fs"); fs.exists = (localPath: string): boolean => path.basename(localPath) !== constants.PACKAGE_JSON_FILE_NAME; const pacoteService = testInjector.resolve("pacoteService"); - pacoteService.manifest = () => Promise.resolve({ }); + pacoteService.manifest = () => Promise.resolve({}); await projectTemplatesService.prepareTemplate(localTemplatePath, "tempFolder"); assert.deepEqual(dataSentToGoogleAnalytics, [ { @@ -215,7 +219,7 @@ describe("project-templates-service", () => { const notSupportedVersionString = "not supported version"; const testInjector = createTestInjector({ packageJsonContent: { nativescript: { templateVersion: notSupportedVersionString } } }); const projectTemplatesService = testInjector.resolve("projectTemplatesService"); - const expectedError = format(constants.ProjectTemplateErrors.InvalidTemplateVersionStringFormat, 'tns-template-hello-world-ts', notSupportedVersionString); + const expectedError = format(constants.ProjectTemplateErrors.InvalidTemplateVersionStringFormat, `tns-template-hello-world-ts@${compatibleTemplateVersion}`, notSupportedVersionString); await assert.isRejected(projectTemplatesService.prepareTemplate("typescript", "tempFolder"), expectedError); }); @@ -238,7 +242,7 @@ describe("project-templates-service", () => { { name: "is correct when scoped package name without version is passed", templateName: "@nativescript/vue-template", - expectedVersion: "", + expectedVersion: compatibleTemplateVersion, expectedTemplateName: "@nativescript/vue-template" }, { From 2e45034248720b1eadd086aa00451889caa52bc3 Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Fri, 2 Nov 2018 17:12:55 +0200 Subject: [PATCH 26/26] fix: fix: install the latest compatible v2 templates in stead of the latest tag --- lib/services/project-templates-service.ts | 7 +++++-- test/project-templates-service.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/services/project-templates-service.ts b/lib/services/project-templates-service.ts index 08aaba1af2..712202148f 100644 --- a/lib/services/project-templates-service.ts +++ b/lib/services/project-templates-service.ts @@ -30,8 +30,11 @@ export class ProjectTemplatesService implements IProjectTemplatesService { } const templateName = constants.RESERVED_TEMPLATE_NAMES[name.toLowerCase()] || name; - version = version || await this.$npmInstallationManager.getLatestCompatibleVersion(templateName); - const fullTemplateName = `${templateName}@${version}`; + if (!this.$fs.exists(templateName)) { + version = version || await this.$npmInstallationManager.getLatestCompatibleVersion(templateName); + } + + const fullTemplateName = version ? `${templateName}@${version}` : templateName; const templatePackageJsonContent = await this.getTemplatePackageJsonContent(fullTemplateName); const templateVersion = await this.getTemplateVersion(fullTemplateName); diff --git a/test/project-templates-service.ts b/test/project-templates-service.ts index f145080c75..48743641c4 100644 --- a/test/project-templates-service.ts +++ b/test/project-templates-service.ts @@ -15,7 +15,7 @@ function createTestInjector(configuration: { shouldNpmInstallThrow?: boolean, pa injector.register("errors", stubs.ErrorsStub); injector.register("logger", stubs.LoggerStub); injector.register("fs", { - exists: (pathToCheck: string) => true, + exists: (pathToCheck: string) => false, readJson: (pathToFile: string) => configuration.packageJsonContent || {},