diff --git a/lib/commands/prepare.ts b/lib/commands/prepare.ts index 7f07f4644e..2185388f0b 100644 --- a/lib/commands/prepare.ts +++ b/lib/commands/prepare.ts @@ -6,7 +6,9 @@ export class PrepareCommand implements ICommand { private $platformCommandParameter: ICommandParameter) { } execute(args: string[]): IFuture { - return this.$platformService.preparePlatform(args[0]); + return (() => { + this.$platformService.preparePlatform(args[0]); + }).future()(); } allowedParameters = [this.$platformCommandParameter]; diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index b3a068c579..7f46dcbdab 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -6,7 +6,7 @@ interface IPlatformService { removePlatforms(platforms: string[]): IFuture; updatePlatforms(platforms: string[]): IFuture; runPlatform(platform: string, buildConfig?: IBuildConfig): IFuture; - preparePlatform(platform: string): IFuture; + preparePlatform(platform: string): IFuture; buildPlatform(platform: string, buildConfig?: IBuildConfig): IFuture; installOnDevice(platform: string, buildConfig?: IBuildConfig): IFuture; deployOnDevice(platform: string, buildConfig?: IBuildConfig): IFuture; diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 5b7b510829..2f568743d7 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -154,7 +154,7 @@ export class PlatformService implements IPlatformService { }).future()(); } - public preparePlatform(platform: string): IFuture { + public preparePlatform(platform: string): IFuture { return (() => { this.validatePlatform(platform); @@ -166,12 +166,36 @@ export class PlatformService implements IPlatformService { this.$errors.failWithoutHelp(`Unable to install dependencies. Make sure your package.json is valid and all dependencies are correct. Error is: ${err.message}`); } - this.preparePlatformCore(platform).wait(); - }).future()(); + return this.preparePlatformCore(platform).wait(); + }).future()(); + } + + private checkXmlFiles(sourceFiles: string[]): IFuture { + return (() => { + let xmlHasErrors = false; + let DomParser = require("xmldom").DOMParser; + sourceFiles + .filter(file => _.endsWith(file, ".xml")) + .forEach(file => { + let fileContents = this.$fs.readText(file).wait(); + let hasErrors = false; + let domErrorHandler = (level:any, msg:string) => hasErrors = true; + let parser = new DomParser({ + locator:{}, + errorHandler: domErrorHandler + }); + parser.parseFromString(fileContents, "text/xml"); + xmlHasErrors = xmlHasErrors || hasErrors; + if (xmlHasErrors) { + this.$logger.out("Error: ".red.bold + `${file} has syntax errors.`); + } + }); + return !xmlHasErrors; + }).future()(); } @helpers.hook('prepare') - private preparePlatformCore(platform: string): IFuture { + private preparePlatformCore(platform: string): IFuture { return (() => { platform = platform.toLowerCase(); this.ensurePlatformInstalled(platform).wait(); @@ -206,6 +230,12 @@ export class PlatformService implements IPlatformService { sourceFiles = sourceFiles.filter(source => !minimatch(source, `**/${constants.TNS_MODULES_FOLDER_NAME}/**`, {nocase: true})); } + // verify .xml files are well-formed + let validXmlFiles = this.checkXmlFiles(sourceFiles).wait(); + if (!validXmlFiles) { + return false; + } + // Remove .ts and .js.map files PlatformService.EXCLUDE_FILES_PATTERN.forEach(pattern => sourceFiles = sourceFiles.filter(file => !minimatch(file, pattern, {nocase: true}))); let copyFileFutures = sourceFiles.map(source => { @@ -245,13 +275,16 @@ export class PlatformService implements IPlatformService { platformData.platformProjectService.processConfigurationFilesFromAppResources().wait(); this.$logger.out("Project successfully prepared"); - }).future()(); + return true; + }).future()(); } public buildPlatform(platform: string, buildConfig?: IBuildConfig): IFuture { return (() => { platform = platform.toLowerCase(); - this.preparePlatform(platform).wait(); + if (!this.preparePlatform(platform).wait()) { + this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); + } let platformData = this.$platformsData.getPlatformData(platform); platformData.platformProjectService.buildProject(platformData.projectRoot, buildConfig).wait(); diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts index efaf2c1ae4..0fccee6b09 100644 --- a/lib/services/test-execution-service.ts +++ b/lib/services/test-execution-service.ts @@ -33,7 +33,8 @@ class TestExecutionService implements ITestExecutionService { private $logger: ILogger, private $fs: IFileSystem, private $options: IOptions, - private $pluginsService: IPluginsService) { + private $pluginsService: IPluginsService, + private $errors: IErrors) { } public startTestRunner(platform: string) : IFuture { @@ -58,7 +59,9 @@ class TestExecutionService implements ITestExecutionService { 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(); + if (!this.$platformService.preparePlatform(platform).wait()) { + this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); + } this.detourEntryPoint(projectFilesPath).wait(); let watchGlob = path.join(projectDir, constants.APP_FOLDER_NAME); @@ -88,7 +91,10 @@ class TestExecutionService implements ITestExecutionService { let beforeBatchLiveSyncAction = (filePath: string): IFuture => { return (() => { - this.$platformService.preparePlatform(platform).wait(); + if (!this.$platformService.preparePlatform(platform).wait()) { + this.$logger.out("Verify that listed files are well-formed and try again the operation."); + return; + } return path.join(projectFilesPath, path.relative(path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), filePath)); }).future()(); }; diff --git a/lib/services/usb-livesync-service.ts b/lib/services/usb-livesync-service.ts index 26a5b3370a..299920d45a 100644 --- a/lib/services/usb-livesync-service.ts +++ b/lib/services/usb-livesync-service.ts @@ -34,6 +34,7 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $projectDataService: IProjectDataService, private $prompter: IPrompter, + private $errors: IErrors, $hostInfo: IHostInfo) { super($devicesService, $mobileHelper, $localToDevicePathDataFactory, $logger, $options, $deviceAppDataFactory, $fs, $dispatcher, $injector, $childProcess, $iOSEmulatorServices, @@ -65,7 +66,9 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer } } - this.$platformService.preparePlatform(platform).wait(); + if (!this.$platformService.preparePlatform(platform).wait()) { + this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); + } let projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); @@ -121,7 +124,10 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer let fastLiveSync = (filePath: string) => { this.$dispatcher.dispatch(() => { return (() => { - this.$platformService.preparePlatform(platform).wait(); + if (!this.$platformService.preparePlatform(platform).wait()) { + this.$logger.out("Verify that listed files are well-formed and try again the operation."); + return; + } let mappedFilePath = beforeBatchLiveSyncAction(filePath).wait(); if (this.shouldSynciOSSimulator(platform).wait()) { @@ -177,7 +183,10 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer } protected preparePlatformForSync(platform: string) { - this.$platformService.preparePlatform(platform).wait(); + if (!this.$platformService.preparePlatform(platform).wait()) { + this.$logger.out("Verify that listed files are well-formed and try again the operation."); + return; + } } private resolveUsbLiveSyncService(platform: string, device: Mobile.IDevice): IPlatformSpecificUsbLiveSyncService { diff --git a/test/platform-service.ts b/test/platform-service.ts index a4a766ecb5..edac69f78e 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -181,7 +181,8 @@ describe('Platform Service Tests', () => { testInjector.register("fs", fsLib.FileSystem); fs = testInjector.resolve("fs"); }); - it("should process only files in app folder when preparing for iOS platform", () => { + + function prepareDirStructure() { let tempFolder = temp.mkdirSync("prepare platform"); let appFolderPath = path.join(tempFolder, "app"); @@ -193,6 +194,12 @@ describe('Platform Service Tests', () => { let appDestFolderPath = path.join(tempFolder, "appDest"); let appResourcesFolderPath = path.join(appDestFolderPath, "App_Resources"); + return { tempFolder, appFolderPath, app1FolderPath, appDestFolderPath, appResourcesFolderPath }; + } + + it("should process only files in app folder when preparing for iOS platform", () => { + let { tempFolder, appFolderPath, app1FolderPath, appDestFolderPath, appResourcesFolderPath } = prepareDirStructure(); + // Add platform specific files to app and app1 folders let platformSpecificFiles = [ "test1.ios.js", "test1-ios-js", "test2.android.js", "test2-android-js" @@ -242,16 +249,7 @@ describe('Platform Service Tests', () => { assert.isFalse(fs.exists(path.join(appDestFolderPath, "app1")).wait()); }); it("should process only files in app folder when preparing for Android platform", () => { - let tempFolder = temp.mkdirSync("prepare platform"); - - let appFolderPath = path.join(tempFolder, "app"); - fs.createDirectory(appFolderPath).wait(); - - let app1FolderPath = path.join(tempFolder, "app1"); - fs.createDirectory(app1FolderPath).wait(); - - let appDestFolderPath = path.join(tempFolder, "appDest"); - let appResourcesFolderPath = path.join(appDestFolderPath, "App_Resources"); + let { tempFolder, appFolderPath, app1FolderPath, appDestFolderPath, appResourcesFolderPath } = prepareDirStructure(); // Add platform specific files to app and app1 folders let platformSpecificFiles = [ @@ -301,5 +299,42 @@ describe('Platform Service Tests', () => { // Asserts that the files in app1 folder aren't process as platform specific assert.isFalse(fs.exists(path.join(appDestFolderPath, "app1")).wait()); }); + + it("invalid xml is caught", () => { + require("colors"); + let { tempFolder, appFolderPath, appDestFolderPath, appResourcesFolderPath } = prepareDirStructure(); + + // generate invalid xml + let fileFullPath = path.join(appFolderPath, "file.xml"); + fs.writeFile(fileFullPath, "").wait(); + + let platformsData = testInjector.resolve("platformsData"); + platformsData.platformsNames = ["android"]; + platformsData.getPlatformData = (platform: string) => { + return { + appDestinationDirectoryPath: appDestFolderPath, + appResourcesDestinationDirectoryPath: appResourcesFolderPath, + normalizedPlatformName: "Android", + platformProjectService: { + prepareProject: () => Future.fromResult(), + validate: () => Future.fromResult(), + createProject: (projectRoot: string, frameworkDir: string) => Future.fromResult(), + interpolateData: (projectRoot: string) => Future.fromResult(), + afterCreateProject: (projectRoot: string) => Future.fromResult(), + getAppResourcesDestinationDirectoryPath: () => Future.fromResult(""), + processConfigurationFilesFromAppResources: () => Future.fromResult() + } + }; + }; + + let projectData = testInjector.resolve("projectData"); + projectData.projectDir = tempFolder; + + platformService = testInjector.resolve("platformService"); + let result = platformService.preparePlatform("android").wait(); + + // Asserts that prepare has caught invalid xml + assert.isFalse(result); + }); }); });