diff --git a/lib/common b/lib/common
index 9ea72d51ec..db060b6471 160000
--- a/lib/common
+++ b/lib/common
@@ -1 +1 @@
-Subproject commit 9ea72d51ec24537f15bd6d8f72de0bc0eb20d0cc
+Subproject commit db060b647161fc2cf368be86576d2ff2052c627e
diff --git a/lib/constants.ts b/lib/constants.ts
index d2824b15e2..73ffa7610c 100644
--- a/lib/constants.ts
+++ b/lib/constants.ts
@@ -14,6 +14,7 @@ export let DEFAULT_APP_IDENTIFIER_PREFIX = "org.nativescript";
export var LIVESYNC_EXCLUDED_DIRECTORIES = ["app_resources"];
export var TESTING_FRAMEWORKS = ['jasmine', 'mocha', 'qunit'];
export let TEST_RUNNER_NAME = "nativescript-unit-test-runner";
+export let LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"];
export class ReleaseType {
static MAJOR = "major";
diff --git a/lib/services/karma-execution.ts b/lib/services/karma-execution.ts
new file mode 100644
index 0000000000..23ba20f8d1
--- /dev/null
+++ b/lib/services/karma-execution.ts
@@ -0,0 +1,15 @@
+///
+
+"use strict";
+
+import * as path from "path";
+
+process.on("message", (data: any) => {
+ if(data.karmaConfig) {
+ let pathToKarma = path.join(data.karmaConfig.projectDir, 'node_modules/karma'),
+ KarmaServer = require(path.join(pathToKarma, 'lib/server')),
+ karma = new KarmaServer(data.karmaConfig);
+
+ karma.start();
+ }
+});
diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts
index 6a81215557..9795c809b7 100644
--- a/lib/services/livesync/livesync-service.ts
+++ b/lib/services/livesync/livesync-service.ts
@@ -58,7 +58,7 @@ class LiveSyncService implements ILiveSyncService {
appIdentifier: this.$projectData.projectId,
projectFilesPath: path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME),
syncWorkingDirectory: path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME),
- excludedProjectDirsAndFiles: ["**/*.js.map", "**/*.ts"]
+ excludedProjectDirsAndFiles: constants.LIVESYNC_EXCLUDED_FILE_PATTERNS
};
this.$liveSyncServiceBase.sync(liveSyncData).wait();
}).future()();
diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts
index b7d2b33c2e..abdbcdafcc 100644
--- a/lib/services/platform-service.ts
+++ b/lib/services/platform-service.ts
@@ -11,11 +11,6 @@ import Future = require("fibers/future");
let clui = require("clui");
export class PlatformService implements IPlatformService {
- private static TNS_MODULES_FOLDER_NAME = "tns_modules";
- private static EXCLUDE_FILES_PATTERN = [
- "**/*.js.map",
- "**/*.ts"
- ];
constructor(private $devicesService: Mobile.IDevicesService,
private $errors: IErrors,
@@ -226,7 +221,7 @@ export class PlatformService implements IPlatformService {
this.$xmlValidator.validateXmlFiles(sourceFiles).wait();
// Remove .ts and .js.map files
- PlatformService.EXCLUDE_FILES_PATTERN.forEach(pattern => sourceFiles = sourceFiles.filter(file => !minimatch(file, pattern, {nocase: true})));
+ constants.LIVESYNC_EXCLUDED_FILE_PATTERNS.forEach(pattern => sourceFiles = sourceFiles.filter(file => !minimatch(file, pattern, {nocase: true})));
let copyFileFutures = sourceFiles.map(source => {
let destinationPath = path.join(appDestinationDirectoryPath, path.relative(appSourceDirectoryPath, source));
if (this.$fs.getFsStats(source).wait().isDirectory()) {
@@ -250,7 +245,7 @@ export class PlatformService implements IPlatformService {
// Process node_modules folder
let appDir = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
try {
- let tnsModulesDestinationPath = path.join(appDir, PlatformService.TNS_MODULES_FOLDER_NAME);
+ let tnsModulesDestinationPath = path.join(appDir, constants.TNS_MODULES_FOLDER_NAME);
this.$broccoliBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime).wait();
} catch(error) {
this.$logger.debug(error);
diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts
index 254d6f5eb5..a782816ac3 100644
--- a/lib/services/test-execution-service.ts
+++ b/lib/services/test-execution-service.ts
@@ -32,11 +32,17 @@ class TestExecutionService implements ITestExecutionService {
private $options: IOptions,
private $pluginsService: IPluginsService,
private $errors: IErrors,
- private $devicesService: Mobile.IDevicesService) {
+ private $androidDebugService:IDebugService,
+ private $iOSDebugService: IDebugService,
+ private $devicesService: Mobile.IDevicesService,
+ private $childProcess: IChildProcess) {
}
+ public platform: string;
+
public startTestRunner(platform: string) : IFuture {
return (() => {
+ this.platform = platform;
this.$options.justlaunch = true;
let blockingOperationFuture = new Future();
process.on('message', (launcherConfig: any) => {
@@ -50,7 +56,7 @@ class TestExecutionService implements ITestExecutionService {
let configOptions: IKarmaConfigOptions = JSON.parse(launcherConfig);
this.$options.debugBrk = configOptions.debugBrk;
this.$options.debugTransport = configOptions.debugTransport;
- let configJs = this.generateConfig(configOptions);
+ let configJs = this.generateConfig(this.$options.port.toString(), 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`;
@@ -93,37 +99,47 @@ class TestExecutionService implements ITestExecutionService {
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) {
- platform = 'ios_simulator';
- }
- let karmaConfig: any = {
- browsers: [platform],
- configFile: path.join(this.$projectData.projectDir, 'karma.conf.js'),
- _NS: {
- log: this.$logger.getLevel(),
- path: this.$options.path,
- tns: process.argv[1],
- node: process.execPath,
- options: {
- debugTransport: this.$options.debugTransport,
- debugBrk: this.$options.debugBrk,
- }
- },
- };
- if (this.$config.DEBUG || this.$logger.getLevel() === 'TRACE') {
- karmaConfig.logLevel = 'DEBUG';
- }
- if (!this.$options.watch) {
- karmaConfig.singleRun = true;
+ this.platform = platform;
+
+ if(this.$options.debugBrk && this.$options.watch) {
+ this.$errors.failWithoutHelp("You cannot use --watch and --debug-brk simultaneously. Remove one of the flags and try again.");
}
- if (this.$options.debugBrk) {
- karmaConfig.browserNoActivityTimeout = 1000000000;
+
+ if (!this.$platformService.preparePlatform(platform).wait()) {
+ this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation.");
}
- this.$logger.debug(JSON.stringify(karmaConfig, null, 4));
- new KarmaServer(karmaConfig).start();
+
+ let projectDir = this.$projectData.projectDir;
+ this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait();
+
+ let karmaConfig = this.getKarmaConfiguration(platform),
+ karmaRunner = this.$childProcess.fork(path.join(__dirname, "karma-execution.js"));
+
+ karmaRunner.send({karmaConfig: karmaConfig});
+ karmaRunner.on("message", (karmaData: any) => {
+ fiberBootstrap.run(() => {
+ this.$logger.trace("## Unit-testing: Parent process received message", karmaData);
+ let port: string;
+ if(karmaData.url) {
+ port = karmaData.url.port;
+ let socketIoJsUrl = `http://${karmaData.url.host}/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();
+ }
+
+ if(karmaData.launcherConfig) {
+ let configOptions: IKarmaConfigOptions = JSON.parse(karmaData.launcherConfig);
+ let configJs = this.generateConfig(port, configOptions);
+ this.$fs.writeFile(path.join(projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs).wait();
+ }
+
+ if(this.$options.debugBrk) {
+ this.getDebugService(platform).debug().wait();
+ } else {
+ this.liveSyncProject(platform).wait();
+ }
+ });
+ });
}).future()();
}
@@ -138,8 +154,7 @@ class TestExecutionService implements ITestExecutionService {
}).future()();
}
- private generateConfig(options: any): string {
- let port = this.$options.port;
+ private generateConfig(port: string, options: any): string {
let nics = os.networkInterfaces();
let ips = Object.keys(nics)
.map(nicName => nics[nicName].filter((binding: any) => binding.family === 'IPv4' && !binding.internal)[0])
@@ -154,5 +169,65 @@ class TestExecutionService implements ITestExecutionService {
return 'module.exports = ' + JSON.stringify(config);
}
+
+ private getDebugService(platform: string): IDebugService {
+ let lowerCasedPlatform = platform.toLowerCase();
+ if(lowerCasedPlatform === this.$devicePlatformsConstants.iOS.toLowerCase()) {
+ return this.$iOSDebugService;
+ } else if(lowerCasedPlatform === this.$devicePlatformsConstants.Android.toLowerCase()) {
+ return this.$androidDebugService;
+ }
+
+ throw new Error(`Invalid platform ${platform}. Valid platforms are ${this.$devicePlatformsConstants.iOS} and ${this.$devicePlatformsConstants.Android}`);
+ }
+
+ private getKarmaConfiguration(platform: string): any {
+ let karmaConfig: any = {
+ browsers: [platform],
+ configFile: path.join(this.$projectData.projectDir, 'karma.conf.js'),
+ _NS: {
+ log: this.$logger.getLevel(),
+ path: this.$options.path,
+ tns: process.argv[1],
+ node: process.execPath,
+ options: {
+ debugTransport: this.$options.debugTransport,
+ debugBrk: this.$options.debugBrk,
+ }
+ },
+ };
+ if (this.$config.DEBUG || this.$logger.getLevel() === 'TRACE') {
+ karmaConfig.logLevel = 'DEBUG';
+ }
+ if (!this.$options.watch) {
+ karmaConfig.singleRun = true;
+ }
+ if (this.$options.debugBrk) {
+ karmaConfig.browserNoActivityTimeout = 1000000000;
+ }
+
+ karmaConfig.projectDir = this.$projectData.projectDir;
+ this.$logger.debug(JSON.stringify(karmaConfig, null, 4));
+
+ return karmaConfig;
+ }
+
+ private liveSyncProject(platform: string): IFuture {
+ return (() => {
+ let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()),
+ projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
+
+ let liveSyncData: ILiveSyncData = {
+ platform: platform,
+ appIdentifier: this.$projectData.projectId,
+ projectFilesPath: projectFilesPath,
+ syncWorkingDirectory: path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME),
+ canExecuteFastSync: false, // Always restart the application when change is detected, so tests will be rerun.
+ excludedProjectDirsAndFiles: constants.LIVESYNC_EXCLUDED_FILE_PATTERNS
+ };
+
+ this.$liveSyncServiceBase.sync(liveSyncData).wait();
+ }).future()();
+ }
}
$injector.register('testExecutionService', TestExecutionService);