diff --git a/docs/man_pages/project/testing/dev-test-android.md b/docs/man_pages/project/testing/dev-test-android.md new file mode 100644 index 0000000000..985d98054a --- /dev/null +++ b/docs/man_pages/project/testing/dev-test-android.md @@ -0,0 +1,31 @@ +test android +========== + +Usage | Synopsis +------|------- +Run tests on all connected devices | `$ tns test android [--watch] [--debug-brk]` +Run tests on a selected device | `$ tns test android --device [--watch] [--debug-brk]` + +Runs the tests in your project on connected Android devices and running native emulators.<% if(isConsole) { %> Your project must already be configured for unit testing by running `$ tns test init`.<% } %> + +### Options +* `--watch` - If set, when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-run. +* `--device` - Specifies the serial number or the index of the connected device on which to run the tests. To list all connected devices, grouped by platform, run `$ tns device` +* `--debug-brk` - Runs the tests under the debugger. The debugger will break just before your tests are executed, so you have a chance to place breakpoints. + +### Attributes +* `` is the device index or identifier as listed by `$ tns device` + +<% if(isHtml) { %> +### Prerequisites + +* Verify that [you have configured your project for unit testing](test-init.html). +* Verify that [you have stored your unit tests in `app` → `tests`](http://docs.nativescript.org/testing). +* Verify that [you have configured your system and devices properly](http://docs.nativescript.org/testing). + +### Related Commands +Command | Description +--------|------------ +[test init](test-init.html) | Configures your project for unit testing with a selected framework. +[test ios](test-ios.html) | Runs the tests in your project on iOS devices or the iOS Simulator. +<% } %> diff --git a/docs/man_pages/project/testing/dev-test-ios.md b/docs/man_pages/project/testing/dev-test-ios.md new file mode 100644 index 0000000000..b9a038f226 --- /dev/null +++ b/docs/man_pages/project/testing/dev-test-ios.md @@ -0,0 +1,36 @@ +test ios +========== + +Usage | Synopsis +------|------- +Run tests on all connected devices | `$ tns test ios [--watch] [--debug-brk]` +Run tests on a selected device | `$ tns test ios --device [--watch] [--debug-brk]` +Run tests in the iOS Simulator | `$ tns test ios --emulator [--watch] [--debug-brk]` + +Runs the tests in your project on connected iOS devices or the iOS Simulator.<% if(isConsole && isMacOS) { %> Your project must already be configured for unit testing by running `$ tns test init`.<% } %> + +<% if(isConsole && (isLinux || isWindows)) { %>WARNING: You can run this command only on OS X systems. To view the complete help for this command, run `$ tns help test ios`<% } %> + +<% if((isConsole && isMacOS) || isHtml) { %> +### Options +* `--watch` - If set, when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-ran. +* `--device` - Specifies the serial number or the index of the connected device on which you want to run tests. To list all connected devices, grouped by platform, run `$ tns device`. You cannot set `--device` and `--emulator` simultaneously. +* `--emulator` - Runs tests on the iOS Simulator. You cannot set `--device` and `--emulator` simultaneously. +* `--debug-brk` - Runs the tests under the debugger. The debugger will break just before your tests are executed, so you have a chance to place breakpoints. + +### Attributes +* `` is the device index or identifier as listed by `$ tns device`<% } %> + +<% if(isHtml) { %> +### Prerequisites + +* Verify that [you have configured your project for unit testing](test-init.html). +* Verify that [you have stored your unit tests in `app` → `tests`](http://docs.nativescript.org/testing). +* Verify that [you have configured your system and devices properly](http://docs.nativescript.org/testing). + +### Related Commands +Command | Description +--------|------------ +[test init](test-init.html) | Configures your project for unit testing with a selected framework. +[test android](test-android.html) | Runs the tests in your project on Android devices or native emulators. +<% } %> diff --git a/lib/commands/livesync.ts b/lib/commands/livesync.ts index 3e0fb4f98f..96a766d516 100644 --- a/lib/commands/livesync.ts +++ b/lib/commands/livesync.ts @@ -9,7 +9,6 @@ export class LivesyncCommand implements ICommand { private $errors: IErrors) { } public execute(args: string[]): IFuture { - this.$options.justlaunch = true; return this.$usbLiveSyncService.liveSync(args[0]); } diff --git a/lib/common b/lib/common index ad5e363a55..6e5218d5c5 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit ad5e363a55822745caad0ef3f965ee52c2fbec18 +Subproject commit 6e5218d5c5303f359a5957c559abe613231ffe83 diff --git a/lib/nativescript-cli.ts b/lib/nativescript-cli.ts index 352f519c7f..a06792c38f 100644 --- a/lib/nativescript-cli.ts +++ b/lib/nativescript-cli.ts @@ -7,6 +7,8 @@ require("./common/verify-node-version").verifyNodeVersion(require("../package.js require("./bootstrap"); import * as fiber from "fibers"; import Future = require("fibers/future"); +import * as shelljs from "shelljs"; +shelljs.config.silent = true; import {installUncaughtExceptionListener} from "./common/errors"; installUncaughtExceptionListener(process.exit); diff --git a/lib/providers/device-log-provider.ts b/lib/providers/device-log-provider.ts index b140afa715..746a60cd65 100644 --- a/lib/providers/device-log-provider.ts +++ b/lib/providers/device-log-provider.ts @@ -2,10 +2,38 @@ "use strict"; export class DeviceLogProvider implements Mobile.IDeviceLogProvider { - constructor(private $logger: ILogger) { } + //sample line is "I/Web Console( 4438): Received Event: deviceready at file:///storage/emulated/0/Icenium/com.telerik.TestApp/js/index.js:48" + private static LINE_REGEX = /.\/(.+?)\s*\(\s*(\d+?)\): (.*)/; - public logData(line: string, platform: string, deviceIdentifier: string): void { - this.$logger.out(line); + constructor(private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + private $logger: ILogger) { } + + public logData(lineText: string, platform: string, deviceIdentifier: string): void { + if (!platform || platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { + this.$logger.out(lineText); + } else if (platform === this.$devicePlatformsConstants.Android) { + let log = this.getConsoleLogFromLine(lineText); + if (log) { + if (log.tag) { + this.$logger.out(`${log.tag}: ${log.message}`); + } else { + this.$logger.out(log.message); + } + } + } + } + + private getConsoleLogFromLine(lineText: String): any { + let acceptedTags = ["chromium", "Web Console", "JS"]; + let match = lineText.match(DeviceLogProvider.LINE_REGEX); + if (match) { + if(acceptedTags.indexOf(match[1]) !== -1) { + return {tag: match[1], message: match[3]}; + } + } else if (_.any(acceptedTags, (tag: string) => { return lineText.indexOf(tag) !== -1; })) { + return {message: match[3]}; + } + return null; } } $injector.register("deviceLogProvider", DeviceLogProvider); diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 8868de0dc4..6a66beccc3 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -296,18 +296,20 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject this.$fs.ensureDirectoryExists(configurationsDirectoryPath).wait(); let pluginConfigurationDirectoryPath = path.join(configurationsDirectoryPath, pluginName); - this.$fs.ensureDirectoryExists(pluginConfigurationDirectoryPath).wait(); + if (this.$fs.exists(pluginPlatformsFolderPath).wait()) { + this.$fs.ensureDirectoryExists(pluginConfigurationDirectoryPath).wait(); + + // Copy all resources from plugin + let resourcesDestinationDirectoryPath = path.join(this.platformData.projectRoot, "src", pluginName); + this.$fs.ensureDirectoryExists(resourcesDestinationDirectoryPath).wait(); + shell.cp("-Rf", path.join(pluginPlatformsFolderPath, "*"), resourcesDestinationDirectoryPath); + } // Copy include.gradle file let includeGradleFilePath = path.join(pluginPlatformsFolderPath, "include.gradle"); if(this.$fs.exists(includeGradleFilePath).wait()) { shell.cp("-f", includeGradleFilePath, pluginConfigurationDirectoryPath); } - - // Copy all resources from plugin - let resourcesDestinationDirectoryPath = path.join(this.platformData.projectRoot, "src", pluginName); - this.$fs.ensureDirectoryExists(resourcesDestinationDirectoryPath).wait(); - shell.cp("-Rf", path.join(pluginPlatformsFolderPath, "*"), resourcesDestinationDirectoryPath); }).future()(); } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 3afe67c7f3..5b7b510829 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -353,10 +353,6 @@ export class PlatformService implements IPlatformService { return (() => { platformData.platformProjectService.deploy(device.deviceInfo.identifier).wait(); device.deploy(packageFile, this.$projectData.projectId).wait(); - - if (!this.$options.justlaunch) { - device.openDeviceLogStream(); - } }).future()(); }; this.$devicesService.execute(action).wait(); diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts index 8cdf587fc9..0ce884f7e1 100644 --- a/lib/services/test-execution-service.ts +++ b/lib/services/test-execution-service.ts @@ -6,6 +6,7 @@ import * as constants from "../constants"; import * as path from 'path'; import Future = require('fibers/future'); import * as os from 'os'; +import * as fiberBootstrap from "../common/fiber-bootstrap"; interface IKarmaConfigOptions { debugBrk: boolean; @@ -31,93 +32,109 @@ class TestExecutionService implements ITestExecutionService { private $config: IConfiguration, private $logger: ILogger, private $fs: IFileSystem, - private $options: IOptions) { + private $options: IOptions, + private $pluginsService: IPluginsService) { } public startTestRunner(platform: string) : IFuture { return (() => { this.$options.justlaunch = true; + let blockingOperationFuture = new Future(); + process.on('message', (launcherConfig: any) => { + fiberBootstrap.run(() => { + try { + let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); + let projectDir = this.$projectData.projectDir; + + let projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + + let configOptions: IKarmaConfigOptions = JSON.parse(launcherConfig); + this.$options.debugBrk = configOptions.debugBrk; + this.$options.debugTransport = configOptions.debugTransport; + let configJs = this.generateConfig(configOptions); + this.$fs.writeFile(path.join(projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs).wait(); + + let socketIoJsUrl = `http://localhost:${this.$options.port}/socket.io/socket.io.js`; + let socketIoJs = this.$httpClient.httpRequest(socketIoJsUrl).wait().body; + this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs).wait(); + + this.$platformService.preparePlatform(platform).wait(); + this.detourEntryPoint(projectFilesPath).wait(); + + let watchGlob = path.join(projectDir, constants.APP_FOLDER_NAME); + + let platformSpecificLiveSyncServices: IDictionary = { + android: (_device: Mobile.IDevice, $injector: IInjector): IPlatformSpecificLiveSyncService => { + return $injector.resolve(this.$androidUsbLiveSyncServiceLocator.factory, {_device: _device}); + }, + ios: (_device: Mobile.IDevice, $injector: IInjector) => { + return $injector.resolve(this.$iosUsbLiveSyncServiceLocator.factory, {_device: _device}); + } + }; + + let notInstalledAppOnDeviceAction = (device: Mobile.IDevice): IFuture => { + return (() => { + this.$platformService.installOnDevice(platform).wait(); + this.detourEntryPoint(projectFilesPath).wait(); + }).future()(); + }; + + let notRunningiOSSimulatorAction = (): IFuture => { + return (() => { + this.$platformService.deployOnEmulator(this.$devicePlatformsConstants.iOS.toLowerCase()).wait(); + this.detourEntryPoint(projectFilesPath).wait(); + }).future()(); + }; + + let beforeBatchLiveSyncAction = (filePath: string): IFuture => { + return (() => { + this.$platformService.preparePlatform(platform).wait(); + return path.join(projectFilesPath, path.relative(path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), filePath)); + }).future()(); + }; + + let localProjectRootPath = platform.toLowerCase() === "ios" ? platformData.appDestinationDirectoryPath : null; + + let liveSyncData = { + platform: platform, + appIdentifier: this.$projectData.projectId, + projectFilesPath: projectFilesPath, + excludedProjectDirsAndFiles: constants.LIVESYNC_EXCLUDED_DIRECTORIES, + watchGlob: watchGlob, + platformSpecificLiveSyncServices: platformSpecificLiveSyncServices, + notInstalledAppOnDeviceAction: notInstalledAppOnDeviceAction, + notRunningiOSSimulatorAction: notRunningiOSSimulatorAction, + localProjectRootPath: localProjectRootPath, + beforeBatchLiveSyncAction: beforeBatchLiveSyncAction, + shouldRestartApplication: (localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Future.fromResult(!this.$options.debugBrk), + canExecuteFastLiveSync: (filePath: string) => false, + }; + + this.$usbLiveSyncServiceBase.sync(liveSyncData).wait(); + + if (this.$options.debugBrk) { + this.$logger.info('Starting debugger...'); + let debugService: IDebugService = this.$injector.resolve(`${platform}DebugService`); + debugService.debugStart().wait(); + } + blockingOperationFuture.return(); + } catch(err) { + // send the error to the real future + blockingOperationFuture.throw(err); + } + }); + }); - let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); - let projectDir = this.$projectData.projectDir; - - let projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - - let configOptions: IKarmaConfigOptions = JSON.parse(this.$fs.readStdin().wait()); - this.$options.debugBrk = configOptions.debugBrk; - this.$options.debugTransport = configOptions.debugTransport; - let configJs = this.generateConfig(configOptions); - this.$fs.writeFile(path.join(projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs).wait(); - - let socketIoJsUrl = `http://localhost:${this.$options.port}/socket.io/socket.io.js`; - let socketIoJs = this.$httpClient.httpRequest(socketIoJsUrl).wait().body; - this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs).wait(); - - this.$platformService.preparePlatform(platform).wait(); - this.detourEntryPoint(projectFilesPath).wait(); - - let watchGlob = path.join(projectDir, constants.APP_FOLDER_NAME); - - let platformSpecificLiveSyncServices: IDictionary = { - android: (_device: Mobile.IDevice, $injector: IInjector): IPlatformSpecificLiveSyncService => { - return $injector.resolve(this.$androidUsbLiveSyncServiceLocator.factory, {_device: _device}); - }, - ios: (_device: Mobile.IDevice, $injector: IInjector) => { - return $injector.resolve(this.$iosUsbLiveSyncServiceLocator.factory, {_device: _device}); - } - }; - - let notInstalledAppOnDeviceAction = (device: Mobile.IDevice): IFuture => { - return (() => { - this.$platformService.installOnDevice(platform).wait(); - this.detourEntryPoint(projectFilesPath).wait(); - }).future()(); - }; - - let notRunningiOSSimulatorAction = (): IFuture => { - return (() => { - this.$platformService.deployOnEmulator(this.$devicePlatformsConstants.iOS.toLowerCase()).wait(); - this.detourEntryPoint(projectFilesPath).wait(); - }).future()(); - }; - - let beforeBatchLiveSyncAction = (filePath: string): IFuture => { - return (() => { - this.$platformService.preparePlatform(platform).wait(); - return path.join(projectFilesPath, path.relative(path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), filePath)); - }).future()(); - }; - - let localProjectRootPath = platform.toLowerCase() === "ios" ? platformData.appDestinationDirectoryPath : null; - - let liveSyncData = { - platform: platform, - appIdentifier: this.$projectData.projectId, - projectFilesPath: projectFilesPath, - excludedProjectDirsAndFiles: constants.LIVESYNC_EXCLUDED_DIRECTORIES, - watchGlob: watchGlob, - platformSpecificLiveSyncServices: platformSpecificLiveSyncServices, - notInstalledAppOnDeviceAction: notInstalledAppOnDeviceAction, - notRunningiOSSimulatorAction: notRunningiOSSimulatorAction, - localProjectRootPath: localProjectRootPath, - beforeBatchLiveSyncAction: beforeBatchLiveSyncAction, - shouldRestartApplication: (localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Future.fromResult(!this.$options.debugBrk), - canExecuteFastLiveSync: (filePath: string) => false, - }; - - this.$usbLiveSyncServiceBase.sync(liveSyncData).wait(); - - if (this.$options.debugBrk) { - this.$logger.info('Starting debugger...'); - let debugService: IDebugService = this.$injector.resolve(`${platform}DebugService`); - debugService.debugStart().wait(); - } + // Tell the parent that we are ready to receive the data. + process.send("ready"); + blockingOperationFuture.wait(); }).future()(); } public startKarmaServer(platform: string): IFuture { return (() => { platform = platform.toLowerCase(); + this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); let pathToKarma = path.join(this.$projectData.projectDir, 'node_modules/karma'); let KarmaServer = require(path.join(pathToKarma, 'lib/server')); if (platform === 'ios' && this.$options.emulator) { diff --git a/lib/services/usb-livesync-service.ts b/lib/services/usb-livesync-service.ts index a478bb462b..79c378cef6 100644 --- a/lib/services/usb-livesync-service.ts +++ b/lib/services/usb-livesync-service.ts @@ -135,7 +135,9 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer let devices = this.$devicesService.getDeviceInstances(); _.each(devices, (device: Mobile.IDevice) => { - this.transferFiles(device, deviceAppData, localToDevicePaths, projectFilesPath, true).wait(); + if (this.$fs.exists(filePath).wait()) { + this.transferFiles(device, deviceAppData, localToDevicePaths, projectFilesPath, true).wait(); + } let platformSpecificUsbLiveSyncService = this.resolvePlatformSpecificLiveSyncService(platform || this.$devicesService.platform, device, platformSpecificLiveSyncServices); return platformSpecificUsbLiveSyncService.sendPageReloadMessageToDevice(deviceAppData).wait(); }); diff --git a/package.json b/package.json index 1bcc36f325..13472763f7 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "gulp": "3.9.0", "iconv-lite": "0.4.11", "inquirer": "0.9.0", - "ios-sim-portable": "1.0.13-delta", + "ios-sim-portable": "1.0.13-epsilon", "lockfile": "1.0.1", "lodash": "3.10.0", "log4js": "0.6.26",