Skip to content

Fix unit test runner on Windows #1207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/man_pages/project/testing/dev-test-android.md
Original file line number Diff line number Diff line change
@@ -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 <Device ID> [--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
* `<Device ID>` 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` &#8594; `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.
<% } %>
36 changes: 36 additions & 0 deletions docs/man_pages/project/testing/dev-test-ios.md
Original file line number Diff line number Diff line change
@@ -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 <Device ID> [--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
* `<Device ID>` 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` &#8594; `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.
<% } %>
2 changes: 1 addition & 1 deletion lib/common
167 changes: 92 additions & 75 deletions lib/services/test-execution-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<void> {
return (() => {
this.$options.justlaunch = true;
let blockingOperationFuture = new Future<void>();
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<any> = {
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<void> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can move these callbacks outside the body of startTestRunner.

return (() => {
this.$platformService.installOnDevice(platform).wait();
this.detourEntryPoint(projectFilesPath).wait();
}).future<void>()();
};

let notRunningiOSSimulatorAction = (): IFuture<void> => {
return (() => {
this.$platformService.deployOnEmulator(this.$devicePlatformsConstants.iOS.toLowerCase()).wait();
this.detourEntryPoint(projectFilesPath).wait();
}).future<void>()();
};

let beforeBatchLiveSyncAction = (filePath: string): IFuture<string> => {
return (() => {
this.$platformService.preparePlatform(platform).wait();
return path.join(projectFilesPath, path.relative(path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), filePath));
}).future<string>()();
};

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<any> = {
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<void> => {
return (() => {
this.$platformService.installOnDevice(platform).wait();
this.detourEntryPoint(projectFilesPath).wait();
}).future<void>()();
};

let notRunningiOSSimulatorAction = (): IFuture<void> => {
return (() => {
this.$platformService.deployOnEmulator(this.$devicePlatformsConstants.iOS.toLowerCase()).wait();
this.detourEntryPoint(projectFilesPath).wait();
}).future<void>()();
};

let beforeBatchLiveSyncAction = (filePath: string): IFuture<string> => {
return (() => {
this.$platformService.preparePlatform(platform).wait();
return path.join(projectFilesPath, path.relative(path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), filePath));
}).future<string>()();
};

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<void>()();
}

public startKarmaServer(platform: string): IFuture<void> {
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) {
Expand Down