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/common b/lib/common index ad92d5cca7..2b343d331b 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit ad92d5cca781e756b8cea6c8dfd8f38d0aee6f7c +Subproject commit 2b343d331bbb7c0793f85ffa4bfdfb3c3923df9e 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) {