cordova-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "ASF GitHub Bot (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (CB-13948) Apps with ampersand (&) in the name throw build error in cordova-ios@4.3.1
Date Mon, 05 Mar 2018 01:57:01 GMT

    [ https://issues.apache.org/jira/browse/CB-13948?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16385480#comment-16385480 ] 

ASF GitHub Bot commented on CB-13948:
-------------------------------------

ronaldozanoni closed pull request #361: CB-13948 Properly encode app name to generate XML files using cordova-ios 4.3.1
URL: https://github.com/apache/cordova-ios/pull/361
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/.gitignore b/.gitignore
index ac9b10187..4e6a1e946 100644
--- a/.gitignore
+++ b/.gitignore
@@ -215,4 +215,12 @@ node_modules/yallist/
 node_modules/yargs/
 coverage/
 npm-debug.log
-
+node_modules/.bin/color-support
+node_modules/.bin/jasmine
+node_modules/asynckit/
+node_modules/aws4/
+node_modules/bcrypt-pbkdf/
+node_modules/color-support/
+node_modules/fs.realpath/
+node_modules/jasmine-core/
+node_modules/jasmine/
diff --git a/CordovaLib/Classes/Public/CDVAppDelegate.m b/CordovaLib/Classes/Public/CDVAppDelegate.m
index 13c2e7bdb..821b957e3 100644
--- a/CordovaLib/Classes/Public/CDVAppDelegate.m
+++ b/CordovaLib/Classes/Public/CDVAppDelegate.m
@@ -85,9 +85,9 @@ - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplic
     return YES;
 }
 
-#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
+#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000  
 - (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
-#else
+#else //CB-12098.  Defaults to UIInterfaceOrientationMask for iOS 9+
 - (UIInterfaceOrientationMask)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
 #endif
 {
diff --git a/CordovaLib/Classes/Public/CDVScreenOrientationDelegate.h b/CordovaLib/Classes/Public/CDVScreenOrientationDelegate.h
index 7226205a5..519dd490c 100644
--- a/CordovaLib/Classes/Public/CDVScreenOrientationDelegate.h
+++ b/CordovaLib/Classes/Public/CDVScreenOrientationDelegate.h
@@ -21,7 +21,12 @@
 
 @protocol CDVScreenOrientationDelegate <NSObject>
 
-- (NSUInteger)supportedInterfaceOrientations;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000  
+- (NSUInteger)supportedInterfaceOrientations;  
+#else  
+- (UIInterfaceOrientationMask)supportedInterfaceOrientations;
+#endif
+
 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
 - (BOOL)shouldAutorotate;
 
diff --git a/CordovaLib/Classes/Public/CDVUserAgentUtil.m b/CordovaLib/Classes/Public/CDVUserAgentUtil.m
index b589e1f35..b17107e14 100644
--- a/CordovaLib/Classes/Public/CDVUserAgentUtil.m
+++ b/CordovaLib/Classes/Public/CDVUserAgentUtil.m
@@ -90,7 +90,7 @@ + (void)acquireLock:(void (^)(NSInteger lockToken))block
 
 + (void)releaseLock:(NSInteger*)lockToken
 {
-    if (*lockToken == 0) {
+    if (lockToken == nil || *lockToken == 0) {
         return;
     }
     NSAssert(gCurrentLockToken == *lockToken, @"Got token %ld, expected %ld", (long)*lockToken, (long)gCurrentLockToken);
diff --git a/CordovaLib/Classes/Public/CDVViewController.h b/CordovaLib/Classes/Public/CDVViewController.h
index 90d33d228..605dbbbfb 100644
--- a/CordovaLib/Classes/Public/CDVViewController.h
+++ b/CordovaLib/Classes/Public/CDVViewController.h
@@ -79,6 +79,7 @@
 - (NSString*)appURLScheme;
 - (NSURL*)errorURL;
 
+- (UIColor*)colorFromColorString:(NSString*)colorString;
 - (NSArray*)parseInterfaceOrientations:(NSArray*)orientations;
 - (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation;
 
diff --git a/CordovaLib/Classes/Public/CDVViewController.m b/CordovaLib/Classes/Public/CDVViewController.m
index 4019c2043..6e329aa92 100644
--- a/CordovaLib/Classes/Public/CDVViewController.m
+++ b/CordovaLib/Classes/Public/CDVViewController.m
@@ -207,11 +207,11 @@ - (NSURL*)appUrl
         appURL = [NSURL URLWithString:self.startPage];
     } else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) {
         appURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.wwwFolderName, self.startPage]];
-    } else if([self.wwwFolderName hasSuffix:@".bundle"]){
+    } else if([self.wwwFolderName rangeOfString:@".bundle"].location != NSNotFound){
         // www folder is actually a bundle
         NSBundle* bundle = [NSBundle bundleWithPath:self.wwwFolderName];
         appURL = [bundle URLForResource:self.startPage withExtension:nil];
-    } else if([self.wwwFolderName hasSuffix:@".framework"]){
+    } else if([self.wwwFolderName rangeOfString:@".framework"].location != NSNotFound){
         // www folder is actually a framework
         NSBundle* bundle = [NSBundle bundleWithPath:self.wwwFolderName];
         appURL = [bundle URLForResource:self.startPage withExtension:nil];
@@ -319,10 +319,11 @@ - (void)viewDidLoad
 
     // /////////////////
     NSURL* appURL = [self appUrl];
+    __weak __typeof__(self) weakSelf = self;
 
     [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
-        _userAgentLockToken = lockToken;
-        [CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
+        // Fix the memory leak caused by the strong reference.
+        [weakSelf setLockToken:lockToken];
         if (appURL) {
             NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
             [self.webViewEngine loadRequest:appReq];
@@ -341,6 +342,18 @@ - (void)viewDidLoad
             }
         }
     }];
+    
+    // /////////////////
+    
+    NSString* bgColorString = [self.settings cordovaSettingForKey:@"BackgroundColor"];
+    UIColor* bgColor = [self colorFromColorString:bgColorString];
+    [self.webView setBackgroundColor:bgColor];
+}
+
+- (void)setLockToken:(NSInteger)lockToken
+{
+	_userAgentLockToken = lockToken;
+	[CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
 }
 
 -(void)viewWillAppear:(BOOL)animated
@@ -385,6 +398,52 @@ -(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIVie
     [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillTransitionToSizeNotification object:[NSValue valueWithCGSize:size]]];
 }
 
+- (UIColor*)colorFromColorString:(NSString*)colorString
+{
+    // No value, nothing to do
+    if (!colorString) {
+        return nil;
+    }
+    
+    // Validate format
+    NSError* error = NULL;
+    NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"^(#[0-9A-F]{3}|(0x|#)([0-9A-F]{2})?[0-9A-F]{6})$" options:NSRegularExpressionCaseInsensitive error:&error];
+    NSUInteger countMatches = [regex numberOfMatchesInString:colorString options:0 range:NSMakeRange(0, [colorString length])];
+    
+    if (!countMatches) {
+        return nil;
+    }
+    
+    // #FAB to #FFAABB
+    if ([colorString hasPrefix:@"#"] && [colorString length] == 4) {
+        NSString* r = [colorString substringWithRange:NSMakeRange(1, 1)];
+        NSString* g = [colorString substringWithRange:NSMakeRange(2, 1)];
+        NSString* b = [colorString substringWithRange:NSMakeRange(3, 1)];
+        colorString = [NSString stringWithFormat:@"#%@%@%@%@%@%@", r, r, g, g, b, b];
+    }
+    
+    // #RRGGBB to 0xRRGGBB
+    colorString = [colorString stringByReplacingOccurrencesOfString:@"#" withString:@"0x"];
+    
+    // 0xRRGGBB to 0xAARRGGBB
+    if ([colorString hasPrefix:@"0x"] && [colorString length] == 8) {
+        colorString = [@"0xFF" stringByAppendingString:[colorString substringFromIndex:2]];
+    }
+    
+    // 0xAARRGGBB to int
+    unsigned colorValue = 0;
+    NSScanner *scanner = [NSScanner scannerWithString:colorString];
+    if (![scanner scanHexInt:&colorValue]) {
+        return nil;
+    }
+    
+    // int to UIColor
+    return [UIColor colorWithRed:((float)((colorValue & 0x00FF0000) >> 16))/255.0
+                           green:((float)((colorValue & 0x0000FF00) >>  8))/255.0
+                            blue:((float)((colorValue & 0x000000FF) >>  0))/255.0
+                           alpha:((float)((colorValue & 0xFF000000) >> 24))/255.0];
+}
+
 - (NSArray*)parseInterfaceOrientations:(NSArray*)orientations
 {
     NSMutableArray* result = [[NSMutableArray alloc] init];
@@ -419,7 +478,12 @@ - (BOOL)shouldAutorotate
     return YES;
 }
 
-- (NSUInteger)supportedInterfaceOrientations
+// CB-12098
+#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000  
+- (NSUInteger)supportedInterfaceOrientations  
+#else  
+- (UIInterfaceOrientationMask)supportedInterfaceOrientations
+#endif
 {
     NSUInteger ret = 0;
 
@@ -493,7 +557,7 @@ - (NSString*)userAgent
         _userAgent = [NSString stringWithFormat:@"%@ %@", localBaseUserAgent, appendUserAgent];
     } else {
         // Use our address as a unique number to append to the User-Agent.
-        _userAgent = [NSString stringWithFormat:@"%@ (%lld)", localBaseUserAgent, (long long)self];
+        _userAgent = localBaseUserAgent;
     }
     return _userAgent;
 }
diff --git a/CordovaLib/VERSION b/CordovaLib/VERSION
index f77856a6f..cc2fbe89b 100644
--- a/CordovaLib/VERSION
+++ b/CordovaLib/VERSION
@@ -1 +1 @@
-4.3.1
+4.3.2
diff --git a/CordovaLib/cordova.js b/CordovaLib/cordova.js
index 29be90991..17231650b 100644
--- a/CordovaLib/cordova.js
+++ b/CordovaLib/cordova.js
@@ -19,7 +19,7 @@
  under the License.
 */
 ;(function() {
-var PLATFORM_VERSION_BUILD_LABEL = '4.3.1';
+var PLATFORM_VERSION_BUILD_LABEL = '4.3.2';
 // file: src/scripts/require.js
 
 /*jshint -W079 */
diff --git a/appveyor.yml b/appveyor.yml
index 6e92b889a..1cc5ba88c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -9,7 +9,7 @@ install:
   - ps: Install-Product node $env:nodejs_version
   # Lines below required due to uncrustify installation failure on Windows
   - npm install --prod
-  - npm install jshint jasmine-node rewire
+  - npm install jshint jasmine rewire
 
 build: off
 
diff --git a/bin/diagnose_project b/bin/diagnose_project
deleted file mode 100755
index 1879fe1ba..000000000
--- a/bin/diagnose_project
+++ /dev/null
@@ -1,217 +0,0 @@
-#!/usr/bin/python
-"""
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
-"""
-"""
-Prints out information regarding a Cordova project for diagnostic purposes.
-Currently this only reports information but does not give any recommendations yet.
-
-Usage: CordovaVersion/bin/diagnose_project path/to/your/app.xcodeproj
-"""
-
-import os
-import sys
-import plistlib
-import shutil
-import tempfile
-import pprint
-
-def Usage():
-  print __doc__
-  sys.exit(1)
-
-def AbsParentPath(path):
-  return os.path.abspath(os.path.join(path, os.path.pardir))
-
-def AbsProjectPath(relative_path):
-  # Do an extra abspath here to strip off trailing / if present.
-  project_path = os.path.abspath(relative_path)
-  if project_path.endswith('.pbxproj'):
-    project_path = AbsParentPath(project_path)
-  elif project_path.endswith('.xcodeproj'):
-    pass
-  else:
-    raise Exception('The following is not a valid path to an XCode project: %s' % project_path)
-  return project_path
-
-def getXcodePlist(pbxPath):
-	tmpfile = tempfile.mktemp (".xml")
-	os.system("plutil -convert xml1 -o %s %s" % (tmpfile, pbxPath))
-
-	return plistlib.readPlist( tmpfile )
-	
-def getTargetBuildSettings(diagKeys, xcodePlist):
-	allObjects = xcodePlist['objects']
-	rootObj = allObjects[ xcodePlist['rootObject'] ]
-	
-	buildSettings = {};
-	
-	targetguids = rootObj['targets']
-	for targetguid in targetguids:
-		target = allObjects[ targetguid ]
-		targetname = target['name']
-		targetSettings = {}
-		bclist = allObjects[ target['buildConfigurationList'] ]['buildConfigurations']
-		for conflist in bclist:
-			cl = allObjects[conflist];
-			clname = cl.get("name", 'no name')
-			targetSettings[clname] = {}
-			for key in diagKeys:
-				val = cl['buildSettings'].get(key, '(not found)')
-				targetSettings[clname][key] = val
-		buildSettings[targetname] = targetSettings
-
-	return buildSettings
-
-def getProjectBuildSettings(diagKeys, xcodePlist):
-	allObjects = xcodePlist['objects']
-	rootObj = allObjects[ xcodePlist['rootObject'] ]
-	
-	buildSettings = {};
-
-	bclist = allObjects[ rootObj['buildConfigurationList'] ]['buildConfigurations']
-	for conflist in bclist:
-		cl = allObjects[conflist];
-		clname = cl.get("name", 'no name')
-		buildSettings[clname] = {}
-		for key in diagKeys:
-			val = cl['buildSettings'].get(key, '(not found)')
-			buildSettings[clname][key] = val
-	
-	return buildSettings
-	
-def getXcodeBuildSettings(diagKeys, xcodePlist):
-	projectSettings = getProjectBuildSettings(diagKeys, xcodePlist)
-	targetSettings = getTargetBuildSettings(diagKeys, xcodePlist)
-
-	settings = {}
-	settings['Project'] = projectSettings
-	settings['Targets'] = targetSettings
-	
-	return settings
-  
-def main(argv):
-	if len(argv) != 2:
-		Usage()
-
-	project_path = AbsProjectPath(argv[1])
-	parent_project_path = AbsParentPath(project_path)
-
-	projPbx = os.path.join(project_path, 'project.pbxproj')
-
-	buildSettingsKeys = ['HEADER_SEARCH_PATHS', 'ARCHS', 'USER_HEADER_SEARCH_PATHS', 'IPHONEOS_DEPLOYMENT_TARGET', 'OTHER_LDFLAGS', 'GCC_VERSION']
-
-	projPlist = getXcodePlist(projPbx)
-	allObjects = projPlist['objects']
-	rootObj = allObjects[ projPlist['rootObject'] ]
-
-	print "\n\n-------------------------------------BEGIN--------------------------------------"
-	print "Inspecting project: %s" % (projPbx) 
-
-	print "\n\n--------------------------------------------------------------------------------"
-	print "Finding your project's sub-projects...\n"
-
-	subprojKeys = ['name', 'path', 'sourceTree']
-	subprojValues = []
-
-	subprojRef = rootObj['projectReferences']
-	for subproj in subprojRef:
-	  sp = {}
-	  subprojGroup = allObjects[ subproj['ProjectRef'] ]
-	  for key in subprojKeys:
-	  	val = subprojGroup.get(key, "(not found)")
-	  	sp[key] = val;
-	  print "Sub-project:", sp
-	  subprojValues.append(sp)
-  	
-  
-	print "\n\n--------------------------------------------------------------------------------"
-	print "Inspecting your project's Build Settings...\n"
-
-	buildSettings = getXcodeBuildSettings(buildSettingsKeys, projPlist)
-	pp = pprint.PrettyPrinter(indent=4)
-
-	for key in buildSettings:
-	  print key, ":" 
-	  pp.pprint(buildSettings[key])
-	
-	print "\n\n--------------------------------------------------------------------------------"
-	print "Inspecting Xcode Preferences...\n"
-
-	xcodeBinaryPrefsPath = os.path.join( os.path.expanduser("~"), "Library", "Preferences", "com.apple.dt.Xcode.plist" );
-	xcodePrefsPlist = getXcodePlist(xcodeBinaryPrefsPath)
-
-	ideSetting = xcodePrefsPlist.get('IDEApplicationwideBuildSettings', {})
-	xcodeCordovaLib = ideSetting.get("CORDOVALIB", "(not found)")
-	print "CORDOVALIB:", xcodeCordovaLib
-	print "Build Location Style:", xcodePrefsPlist.get('IDEBuildLocationStyle', "(unknown)")
-
-	print "\n\n--------------------------------------------------------------------------------"
-	print "Inspecting your CordovaLib's Build Settings...\n"
-
-	cdvlibPath = None
-	cdvlibProjName = 'CordovaLib.xcodeproj'
-	
-	for sp in subprojValues:
-	  if cdvlibProjName in sp['path']:
-	  	if 'CORDOVALIB' in sp['sourceTree']:
-			print "Your project *IS* using the CORDOVALIB Xcode variable (source tree)."
-	  		cdvlibPath = os.path.join( xcodeCordovaLib, cdvlibProjName)
-	  	else:
-			print "Your project is *NOT* using the CORDOVALIB Xcode variable (source tree)."
-	  		cdvlibPath = sp['path']
-  	
-	cdvlibNormalizedPath = os.path.normpath( os.path.join(parent_project_path, cdvlibPath) )
-	cdvlibPbx = os.path.join( cdvlibNormalizedPath , 'project.pbxproj' )
-
-	print "Path is:", cdvlibNormalizedPath, "\n"
-
-	cdvPlist = getXcodePlist(cdvlibPbx)
-	cdvBuildSettingsKeys = ['PUBLIC_HEADERS_FOLDER_PATH', 'ARCHS', 'ARCHS[sdk=iphoneos*]', 'ARCHS[sdk=iphoneos6.*]', 'ARCHS[sdk=iphonesimulator*]', 'USER_HEADER_SEARCH_PATHS', 'IPHONEOS_DEPLOYMENT_TARGET', 'OTHER_LDFLAGS', 'GCC_VERSION']
-
-	cdvBuildSettings = getXcodeBuildSettings(cdvBuildSettingsKeys, cdvPlist)
-	pp.pprint( cdvBuildSettings )
-
-	print "\n\n--------------------------------------------------------------------------------"
-	print "Inspecting CordovaLib Version...\n"
-  
-	cdvlibFolder = AbsParentPath(cdvlibNormalizedPath)
-	cdvlibVersionFile = os.path.join( cdvlibFolder, "VERSION")
-
-	try:
-		vf = open(cdvlibVersionFile, 'r')
-	  	print "VERSION file:", vf.readline()
-	  	vf.close()
-	except:
-		print "VERSION file not found at:", cdvlibVersionFile
-
-	cdvlibAvailabilityFile = os.path.join( cdvlibFolder, "Classes", "CDVAvailability.h" )
-	try:
-	    af = open(cdvlibAvailabilityFile, 'r')
-	    match = "#define CORDOVA_VERSION_MIN_REQUIRED"
-	    for line in af:
-	  		if match in line:
-	  			print "CDVAvailability.h version:", line.strip().replace(match, ""),
-	    af.close()
-	except:
-		print "CDVAvailability.h file not found at:", cdvlibAvailabilityFile, 
-
-	print "\n\n--------------------------------------END---------------------------------------"
-
-if __name__ == '__main__':
-  main(sys.argv)
diff --git a/bin/lib/create.js b/bin/lib/create.js
index 89b9c8a9f..d62a44cd5 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -24,6 +24,7 @@ var shell = require('shelljs'),
     path = require('path'),
     fs = require('fs'),
     plist = require('plist'),
+    xmlescape = require('xml-escape'),
     ROOT = path.join(__dirname, '..', '..'),
     events = require('cordova-common').events;
 
@@ -145,10 +146,14 @@ function copyTemplateFiles(project_path, project_name, project_template_dir, pac
      * - ./__PROJECT_NAME__/Resources/__PROJECT_NAME__-info.plist
      * - ./__PROJECT_NAME__/Resources/__PROJECT_NAME__-Prefix.plist
      */
+
+    // https://issues.apache.org/jira/browse/CB-12402 - Encode XML characters properly
+    var project_name_xml_esc = xmlescape(project_name);
+    shell.sed('-i', /__PROJECT_NAME__/g, project_name_xml_esc, path.join(r+'.xcworkspace', 'contents.xcworkspacedata'));
+    shell.sed('-i', /__PROJECT_NAME__/g, project_name_xml_esc, path.join(r+'.xcworkspace', 'xcshareddata', 'xcschemes', project_name +'.xcscheme'));
+
     var project_name_esc = project_name.replace(/&/g, '\\&');
     shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r+'.xcodeproj', 'project.pbxproj'));
-    shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r+'.xcworkspace', 'contents.xcworkspacedata'));
-    shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r+'.xcworkspace', 'xcshareddata', 'xcschemes', project_name +'.xcscheme'));
     shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, 'Classes', 'AppDelegate.h'));
     shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, 'Classes', 'AppDelegate.m'));
     shell.sed('-i', /__PROJECT_NAME__/g, project_name_esc, path.join(r, 'Classes', 'MainViewController.h'));
diff --git a/bin/templates/project/__PROJECT_NAME__/Classes/MainViewController.m b/bin/templates/project/__PROJECT_NAME__/Classes/MainViewController.m
index 7dfe48f42..9d690e3d9 100644
--- a/bin/templates/project/__PROJECT_NAME__/Classes/MainViewController.m
+++ b/bin/templates/project/__PROJECT_NAME__/Classes/MainViewController.m
@@ -92,7 +92,12 @@ - (UIWebView*) newCordovaViewWithFrame:(CGRect)bounds
     return[super newCordovaViewWithFrame:bounds];
 }
 
-- (NSUInteger)supportedInterfaceOrientations 
+// CB-12098
+#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000  
+- (NSUInteger)supportedInterfaceOrientations
+#else  
+- (UIInterfaceOrientationMask)supportedInterfaceOrientations
+#endif
 {
     return [super supportedInterfaceOrientations];
 }
diff --git a/bin/templates/scripts/cordova/lib/build.js b/bin/templates/scripts/cordova/lib/build.js
index c40bfe8b9..c276424b3 100644
--- a/bin/templates/scripts/cordova/lib/build.js
+++ b/bin/templates/scripts/cordova/lib/build.js
@@ -53,7 +53,33 @@ var buildFlagMatchers = {
     'shared_precomps_dir' : /^(SHARED_PRECOMPS_DIR=.*)/
 };
 
+/**
+ * Returns a promise that resolves to the default simulator target; the logic here
+ * matches what `cordova emulate ios` does. 
+ * 
+ * The return object has two properties: `name` (the Xcode destination name),
+ * `identifier` (the simctl identifier), and `simIdentifier` (essentially the cordova emulate target)
+ * 
+ * @return {Promise}
+ */
+function getDefaultSimulatorTarget() {
+    return require('./list-emulator-build-targets').run()
+    .then(function (emulators) {
+        var targetEmulator;
+        if (emulators.length > 0) {
+            targetEmulator = emulators[0];
+        }
+        emulators.forEach(function (emulator) {
+            if (emulator.name.indexOf('iPhone') === 0) {
+                targetEmulator = emulator;
+            }
+        });
+        return targetEmulator;
+    });
+}
+
 module.exports.run = function (buildOpts) {
+    var emulatorTarget = '';
 
     buildOpts = buildOpts || {};
 
@@ -92,6 +118,22 @@ return require('./list-devices').run()
             buildOpts.device = true;
             return check_reqs.check_ios_deploy();
         }
+    }).then(function () {
+        // CB-12287: Determine the device we should target when building for a simulator
+        if (!buildOpts.device) {
+            var promise;
+            if (buildOpts.target) {
+                // a target was given to us, find the matching Xcode destination name
+                promise = require('./list-emulator-build-targets').targetForSimIdentifier(buildOpts.target);
+            } else {
+                // no target provided, pick a default one (matching our emulator logic)
+                promise = getDefaultSimulatorTarget();
+            }
+            return promise.then(function(theTarget) { 
+                emulatorTarget = theTarget.name;
+                events.emit('log', 'Building for ' + emulatorTarget + ' Simulator');
+            });
+        }
     }).then(function () {
         return check_reqs.run();
     }).then(function () {
@@ -125,7 +167,7 @@ return require('./list-devices').run()
         // remove the build/device folder before building
         return spawn('rm', [ '-rf', buildOutputDir ], projectPath)
         .then(function() {
-            var xcodebuildArgs = getXcodeBuildArgs(projectName, projectPath, configuration, buildOpts.device, buildOpts.buildFlag);
+            var xcodebuildArgs = getXcodeBuildArgs(projectName, projectPath, configuration, buildOpts.device, buildOpts.buildFlag, emulatorTarget);
             return spawn('xcodebuild', xcodebuildArgs, projectPath);
         });
 
@@ -224,13 +266,15 @@ module.exports.findXCodeProjectIn = findXCodeProjectIn;
 
 /**
  * Returns array of arguments for xcodebuild
- * @param  {String}  projectName   Name of xcode project
- * @param  {String}  projectPath   Path to project file. Will be used to set CWD for xcodebuild
- * @param  {String}  configuration Configuration name: debug|release
- * @param  {Boolean} isDevice      Flag that specify target for package (device/emulator)
- * @return {Array}                 Array of arguments that could be passed directly to spawn method
+ * @param  {String}  projectName    Name of xcode project
+ * @param  {String}  projectPath    Path to project file. Will be used to set CWD for xcodebuild
+ * @param  {String}  configuration  Configuration name: debug|release
+ * @param  {Boolean} isDevice       Flag that specify target for package (device/emulator)
+ * @param  {Array}   buildFlags
+ * @param  {String}  emulatorTarget Target for emulator (rather than default)
+ * @return {Array}                  Array of arguments that could be passed directly to spawn method
  */
-function getXcodeBuildArgs(projectName, projectPath, configuration, isDevice, buildFlags) {
+function getXcodeBuildArgs(projectName, projectPath, configuration, isDevice, buildFlags, emulatorTarget) {
     var xcodebuildArgs;
     var options;
     var buildActions;
@@ -274,7 +318,7 @@ function getXcodeBuildArgs(projectName, projectPath, configuration, isDevice, bu
             '-scheme', customArgs.scheme || projectName,
             '-configuration', customArgs.configuration || configuration,
             '-sdk', customArgs.sdk || 'iphonesimulator',
-            '-destination', customArgs.destination || 'platform=iOS Simulator,name=iPhone 5s'
+            '-destination', customArgs.destination || 'platform=iOS Simulator,name=' + emulatorTarget
         ];
         buildActions = [ 'build' ];
         settings = [
diff --git a/bin/templates/scripts/cordova/lib/list-emulator-build-targets b/bin/templates/scripts/cordova/lib/list-emulator-build-targets
new file mode 100755
index 000000000..d17fc8ca7
--- /dev/null
+++ b/bin/templates/scripts/cordova/lib/list-emulator-build-targets
@@ -0,0 +1,108 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+/*jshint node: true*/
+
+var Q = require('q'),
+    exec = require('child_process').exec;
+
+/**
+ * Returns a list of available simulator build targets of the form
+ * 
+ *     [
+ *         { name: <xcode-destination-name>,
+ *           identifier: <simctl-identifier>,
+ *           simIdentifier: <cordova emulate target>
+ *         }
+ *     ]
+ * 
+ */
+function listEmulatorBuildTargets () {
+    return Q.nfcall(exec, 'xcrun simctl list --json')
+    .then(function(stdio) {
+        return JSON.parse(stdio[0]);
+    })
+    .then(function(simInfo) {
+        var devices = simInfo.devices;
+        var deviceTypes = simInfo.devicetypes;
+        return deviceTypes.reduce(function (typeAcc, deviceType) {
+            if (!deviceType.name.match(/^[iPad|iPhone]/)) {
+                // ignore targets we don't support (like Apple Watch or Apple TV)
+                return typeAcc;
+            }
+            var availableDevices = Object.keys(devices).reduce(function (availAcc, deviceCategory) {
+                var availableDevicesInCategory = devices[deviceCategory];
+                availableDevicesInCategory.forEach(function (device) {
+                    if (device.name === deviceType.name.replace(/\-inch/g, ' inch') && 
+                        device.availability.toLowerCase().indexOf('unavailable') < 0) {
+                            availAcc.push(device);
+                        }
+                });
+                return availAcc;
+            }, []);
+            // we only want device types that have at least one available device
+            // (regardless of OS); this filters things out like iPhone 4s, which
+            // is present in deviceTypes, but probably not available on the user's
+            // system.
+            if (availableDevices.length > 0) {
+                typeAcc.push(deviceType);
+            }
+            return typeAcc;
+        }, []);
+    })
+    .then(function(filteredTargets) {
+        // the simIdentifier, or cordova emulate target name, is the very last part
+        // of identifier.
+        return filteredTargets.map(function (target) {
+            var identifierPieces = target.identifier.split(".");
+            target.simIdentifier = identifierPieces[identifierPieces.length-1];
+            return target;
+        });
+    });
+}
+
+exports.run = listEmulatorBuildTargets;
+
+/**
+ * Given a simIdentifier, return the matching target.
+ * 
+ * @param {string} simIdentifier       a target, like "iPhone-SE"
+ * @return {Object}                    the matching target, or undefined if no match
+ */
+exports.targetForSimIdentifier = function(simIdentifier) {
+    return listEmulatorBuildTargets()
+    .then(function(targets) {
+        return targets.reduce(function(acc, target) {
+            if (!acc && target.simIdentifier.toLowerCase() === simIdentifier.toLowerCase()) {
+                acc = target;
+            }
+            return acc;
+        }, undefined);
+    });
+}
+
+// Check if module is started as separate script.
+// If so, then invoke main method and print out results.
+if (!module.parent) {
+    listEmulatorBuildTargets().then(function (targets) {
+        console.log(JSON.stringify(targets, null, 2));
+    });
+}
diff --git a/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js b/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js
index 297e38631..a9d2ac86e 100644
--- a/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js
+++ b/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js
@@ -304,7 +304,7 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
     shell.mkdir('-p', path.dirname(dest));
 
     if (link) {
-        symlinkFileOrDirTree(src, dest);
+        linkFileOrDirTree(src, dest);
     } else if (fs.statSync(src).isDirectory()) {
         // XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
         shell.cp('-Rf', path.join(src, '/*'), dest);
@@ -322,7 +322,7 @@ function copyNewFile (plugin_dir, src, project_dir, dest, link) {
     copyFile(plugin_dir, src, project_dir, dest, !!link);
 }
 
-function symlinkFileOrDirTree(src, dest) {
+function linkFileOrDirTree(src, dest) {
     if (fs.existsSync(dest)) {
         shell.rm('-Rf', dest);
     }
@@ -330,11 +330,11 @@ function symlinkFileOrDirTree(src, dest) {
     if (fs.statSync(src).isDirectory()) {
         shell.mkdir('-p', dest);
         fs.readdirSync(src).forEach(function(entry) {
-            symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
+            linkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
         });
     }
     else {
-        fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
+        fs.linkSync(src, dest);
     }
 }
 
diff --git a/bin/templates/scripts/cordova/version b/bin/templates/scripts/cordova/version
index 9f3eb4799..e3d0fb4c4 100755
--- a/bin/templates/scripts/cordova/version
+++ b/bin/templates/scripts/cordova/version
@@ -26,7 +26,7 @@
 */
 
 // Coho updates this line
-var VERSION="4.3.1";
+var VERSION="4.3.2";
 
 module.exports.version = VERSION;
 
diff --git a/node_modules/xml-escape/LICENSE b/node_modules/xml-escape/LICENSE
new file mode 100644
index 000000000..bd261effa
--- /dev/null
+++ b/node_modules/xml-escape/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Michael Hernandez
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/xml-escape/README.md b/node_modules/xml-escape/README.md
new file mode 100644
index 000000000..5faf9f91a
--- /dev/null
+++ b/node_modules/xml-escape/README.md
@@ -0,0 +1,38 @@
+xml-escape
+==========
+
+Escape XML in javascript (NodeJS)
+
+npm install xml-escape
+
+```javascript
+// Warning escape is a reserved word, so maybe best to use xmlescape for var name
+var xmlescape = require('xml-escape');
+
+xmlescape('"hello" \'world\' & false < true > -1');
+
+// output
+// '&quot;hello&quot; &apos;world&apos; &amp; false &lt; true &gt; -1'
+
+// don't escape some characters
+xmlescape('"hello" \'world\' & false < true > -1', '>"&')
+
+// output
+// '"hello" &apos;world&apos; & false &lt; true > -1'
+```
+
+
+There is also now an ignore function thanks to @jayflo
+
+```javascript
+esc = require('./');
+
+ignore = '"<&'
+// note you should never ignore an &
+output = esc('I am "<&not>" escaped', ignore)
+console.log(output)
+
+//I am "<&not&gt;" escaped
+```
+
+[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/miketheprogrammer/xml-escape/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
diff --git a/node_modules/xml-escape/index.js b/node_modules/xml-escape/index.js
new file mode 100644
index 000000000..8c37325ea
--- /dev/null
+++ b/node_modules/xml-escape/index.js
@@ -0,0 +1,22 @@
+
+
+var escape = module.exports = function escape(string, ignore) {
+  var pattern;
+
+  if (string === null || string === undefined) return;
+
+  ignore = (ignore || '').replace(/[^&"<>\']/g, '');
+  pattern = '([&"<>\'])'.replace(new RegExp('[' + ignore + ']', 'g'), '');
+
+  return string.replace(new RegExp(pattern, 'g'), function(str, item) {
+            return escape.map[item];
+          })
+}
+
+var map = escape.map = {
+    '>': '&gt;'
+  , '<': '&lt;'
+  , "'": '&apos;'
+  , '"': '&quot;'
+  , '&': '&amp;'
+}
\ No newline at end of file
diff --git a/node_modules/xml-escape/package.json b/node_modules/xml-escape/package.json
new file mode 100644
index 000000000..51a5714e9
--- /dev/null
+++ b/node_modules/xml-escape/package.json
@@ -0,0 +1,91 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "xml-escape@^1.1.0",
+        "scope": null,
+        "escapedName": "xml-escape",
+        "name": "xml-escape",
+        "rawSpec": "^1.1.0",
+        "spec": ">=1.1.0 <2.0.0",
+        "type": "range"
+      }
+    ]
+  ],
+  "_from": "xml-escape@>=1.1.0 <2.0.0",
+  "_id": "xml-escape@1.1.0",
+  "_inCache": true,
+  "_location": "/xml-escape",
+  "_nodeVersion": "5.5.0",
+  "_npmOperationalInternal": {
+    "host": "packages-13-west.internal.npmjs.com",
+    "tmp": "tmp/xml-escape-1.1.0.tgz_1457961063163_0.10634087934158742"
+  },
+  "_npmUser": {
+    "name": "mhernandez",
+    "email": "michael.hernandez1988@gmail.com"
+  },
+  "_npmVersion": "3.3.12",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "xml-escape@^1.1.0",
+    "scope": null,
+    "escapedName": "xml-escape",
+    "name": "xml-escape",
+    "rawSpec": "^1.1.0",
+    "spec": ">=1.1.0 <2.0.0",
+    "type": "range"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz",
+  "_shasum": "3904c143fa8eb3a0030ec646d2902a2f1b706c44",
+  "_shrinkwrap": null,
+  "_spec": "xml-escape@^1.1.0",
+  "author": {
+    "name": "Michael Hernandez - michael.hernandez1988@gmail.com"
+  },
+  "bugs": {
+    "url": "https://github.com/miketheprogrammer/xml-escape/issues"
+  },
+  "dependencies": {},
+  "description": "Escape XML ",
+  "devDependencies": {
+    "tape": "~2.4.2"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "3904c143fa8eb3a0030ec646d2902a2f1b706c44",
+    "tarball": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz"
+  },
+  "gitHead": "c42a09afa81e645ca6881dbaf384d7f3c63735e4",
+  "homepage": "https://github.com/miketheprogrammer/xml-escape",
+  "keywords": [
+    "Escape",
+    "XML",
+    "Unesacpe",
+    "encoding",
+    "xml-escape"
+  ],
+  "license": "MIT License",
+  "main": "index.js",
+  "maintainers": [
+    {
+      "name": "mhernandez",
+      "email": "michael.hernandez1988@gmail.com"
+    }
+  ],
+  "name": "xml-escape",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/miketheprogrammer/xml-escape.git"
+  },
+  "scripts": {
+    "test": "node test.js"
+  },
+  "version": "1.1.0"
+}
diff --git a/node_modules/xml-escape/test.js b/node_modules/xml-escape/test.js
new file mode 100644
index 000000000..21ad21896
--- /dev/null
+++ b/node_modules/xml-escape/test.js
@@ -0,0 +1,29 @@
+var test = require('tape');
+var escape = require('./index');
+test("Characters should be escaped properly", function (t) {
+    t.plan(1);
+
+    t.equals(escape('" \' < > &'), '&quot; &apos; &lt; &gt; &amp;');
+})
+
+test("Module should respect ignore string", function (t) {
+    t.plan(3);
+
+    t.equals(escape('" \' < > &', '"'), '" &apos; &lt; &gt; &amp;');
+    t.equals(escape('" \' < > &', '>&'), '&quot; &apos; &lt; > &');
+    t.equals(escape('" \' < > &', '"\'<>&'), '" \' < > &');
+})
+
+test("Module should not escape random characters", function (t) {
+    t.plan(1);
+
+    t.equals(escape('<[whats up]>', '<]what'), '<[whats up]&gt;');
+})
+
+test("Module should not crash on null or undefined input", function (t) {
+    t.plan(3);
+
+    t.equals((escape("")), "");
+    t.doesNotThrow(function(){escape(null);}, TypeError);
+    t.doesNotThrow(function(){escape(undefined);}, TypeError);
+})
diff --git a/package.json b/package.json
index 3d8ce767a..235c2f345 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cordova-ios",
-  "version": "4.3.1",
+  "version": "4.3.2",
   "description": "cordova-ios release",
   "main": "bin/templates/scripts/cordova/Api.js",
   "repository": {
@@ -17,11 +17,11 @@
   "scripts": {
     "test": "npm run e2e-tests && npm run objc-tests && npm run unit-tests",
     "posttest": "npm run jshint",
-    "cover": "istanbul cover node_modules/jasmine-node/bin/jasmine-node -- tests/spec/unit",
-    "e2e-tests": "jasmine-node --captureExceptions --color tests/spec",
+    "cover": "istanbul cover --root bin/templates/cordova --print detail jasmine",
+    "e2e-tests": "jasmine --captureExceptions --color tests/spec/create.spec.js",
     "objc-tests": "xcodebuild test -workspace tests/cordova-ios.xcworkspace -scheme CordovaLibTests -destination \"platform=iOS Simulator,name=iPhone 5\" CONFIGURATION_BUILD_DIR=\"`mktemp -d 2>/dev/null || mktemp -d -t 'cordova-ios'`\"",
     "preobjc-tests": "tests/scripts/killsim.js",
-    "unit-tests": "jasmine-node --captureExceptions --color tests/spec/unit",
+    "unit-tests": "jasmine --captureExceptions --color",
     "jshint": "jshint bin tests"
   },
   "author": "Apache Software Foundation",
@@ -29,7 +29,7 @@
   "devDependencies": {
     "coffee-script": "^1.7.1",
     "istanbul": "^0.4.2",
-    "jasmine-node": "~1",
+    "jasmine": "^2.5.3",
     "jshint": "^2.6.0",
     "nodeunit": "^0.8.7",
     "rewire": "^2.5.1",
@@ -43,7 +43,8 @@
     "plist": "^1.2.0",
     "q": "^1.4.1",
     "shelljs": "^0.5.3",
-    "xcode": "^0.8.5"
+    "xcode": "^0.8.5",
+    "xml-escape": "^1.1.0"
   },
   "bundledDependencies": [
     "cordova-common",
@@ -52,6 +53,7 @@
     "plist",
     "q",
     "shelljs",
-    "xcode"
+    "xcode",
+    "xml-escape"
   ]
 }
diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json
new file mode 100644
index 000000000..6824f8593
--- /dev/null
+++ b/spec/support/jasmine.json
@@ -0,0 +1,8 @@
+{
+    "spec_dir": "spec",
+    "spec_files": [
+        "../tests/spec/unit/**/*[sS]pec.js"
+    ],
+    "stopSpecOnExpectationFailure": false,
+    "random": false
+}
diff --git a/tests/CordovaLibTests/CDVUserAgentTest.m b/tests/CordovaLibTests/CDVUserAgentTest.m
index f52178ac7..d97ae47f1 100644
--- a/tests/CordovaLibTests/CDVUserAgentTest.m
+++ b/tests/CordovaLibTests/CDVUserAgentTest.m
@@ -21,6 +21,7 @@ Licensed to the Apache Software Foundation (ASF) under one
 
 #import "CDVWebViewTest.h"
 #import <Cordova/CDVViewController.h>
+#import <Cordova/CDVUserAgentUtil.h>
 #import "AppDelegate.h"
 
 @interface CDVUserAgentTestViewController : UIViewController
@@ -129,7 +130,7 @@ - (void)testMultipleViews
     NSString* ua1 = [vc1WebView stringByEvaluatingJavaScriptFromString:getUserAgentCode];
     NSString* ua2 = [vc2WebView stringByEvaluatingJavaScriptFromString:getUserAgentCode];
 
-    XCTAssertFalse([ua1 isEqual:ua2], @"User-Agents should be different.");
+    XCTAssertTrue([ua1 isEqual:ua2], @"User-Agents should be different.");
 }
 
 - (void)testBaseUserAgent
@@ -159,4 +160,22 @@ - (void)testBaseUserAgent
     XCTAssertTrue([cordovaUserAgent2 hasPrefix:rootVc.vc2.baseUserAgent], @"Cordova user agent should be based on base user agent.");
 }
 
+- (void)testUserAgentReleaseLock
+{
+    __block NSInteger myLockToken;
+
+    [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
+        myLockToken = lockToken;
+        [CDVUserAgentUtil releaseLock:&myLockToken];
+
+        NSInteger nullInteger = 0;
+        // test releasing NULL token
+        [CDVUserAgentUtil releaseLock:&nullInteger];
+        NSInteger* ni = nil;
+        // test releasing nil object
+        [CDVUserAgentUtil releaseLock:ni];
+    }];
+}
+
+
 @end
diff --git a/tests/CordovaLibTests/CDVViewControllerTest.m b/tests/CordovaLibTests/CDVViewControllerTest.m
index 3a9e32e87..9d0f7c06f 100644
--- a/tests/CordovaLibTests/CDVViewControllerTest.m
+++ b/tests/CordovaLibTests/CDVViewControllerTest.m
@@ -65,5 +65,29 @@ -(void)testInitWithCustomConfigFileRelativePath{
     [self doTestInitWithConfigFile:configFileRelativePath expectedSettingValue:CDVViewControllerTestSettingValueCustom];
 }
 
+-(void)testColorFromColorString{
+    CDVViewController* viewController = [self viewController];
+    
+    // Comparison values: #FFAABB and #99FFAABB
+    UIColor* referenceColor = [UIColor colorWithRed:255.0/255.0 green:170.0/255.0 blue:187.0/255.0 alpha:1.0];
+    UIColor* referenceColorAlpha = [UIColor colorWithRed:255.0/255.0 green:170.0/255.0 blue:187.0/255.0 alpha:0.6];
+    
+    // Valid values
+    XCTAssertTrue([[viewController colorFromColorString:@"#FAB"] isEqual:referenceColor]);
+    XCTAssertTrue([[viewController colorFromColorString:@"#FFAABB"] isEqual:referenceColor]);
+    XCTAssertTrue([[viewController colorFromColorString:@"0xFFAABB"] isEqual:referenceColor]);
+    XCTAssertTrue([[viewController colorFromColorString:@"#99FFAABB"] isEqual:referenceColorAlpha]);
+    XCTAssertTrue([[viewController colorFromColorString:@"0x99FFAABB"] isEqual:referenceColorAlpha]);
+    
+    // Invalid values
+    XCTAssertNil([viewController colorFromColorString:nil]);
+    XCTAssertNil([viewController colorFromColorString:@"black"]);
+    XCTAssertNil([viewController colorFromColorString:@"0xFAB"]);
+    XCTAssertNil([viewController colorFromColorString:@"#1234"]);
+    XCTAssertNil([viewController colorFromColorString:@"#12345"]);
+    XCTAssertNil([viewController colorFromColorString:@"#1234567"]);
+    XCTAssertNil([viewController colorFromColorString:@"#NOTHEX"]);
+}
+
 @end
 
diff --git a/tests/spec/create.spec.js b/tests/spec/create.spec.js
index 07a2bffc0..079163206 100644
--- a/tests/spec/create.spec.js
+++ b/tests/spec/create.spec.js
@@ -53,59 +53,66 @@ function createAndBuild(projectname, projectid) {
 
 describe('create', function() {
 
-    it('create project with ascii name, no spaces', function() {
+    it('Test#001 : create project with ascii name, no spaces', function() {
         var projectname = 'testcreate';
         var projectid = 'com.test.app1';
 
         createAndBuild(projectname, projectid);
     });
 
-    it('create project with ascii name, and spaces', function() {
+    it('Test#002 : create project with ascii name, and spaces', function() {
         var projectname = 'test create';
         var projectid = 'com.test.app2';
 
         createAndBuild(projectname, projectid);
     });
 
-    it('create project with unicode name, no spaces', function() {
+    it('Test#003 : create project with unicode name, no spaces', function() {
         var projectname = '応応応応用用用用';
         var projectid = 'com.test.app3';
 
         createAndBuild(projectname, projectid);
     });
 
-    it('create project with unicode name 2, no spaces', function() {
+    it('Test#004 : create project with unicode name 2, no spaces', function() {
         var projectname = 'إثرا';
         var projectid = 'com.test.app3.2';
 
         createAndBuild(projectname, projectid);
     });
 
-    it('create project with unicode name, and spaces', function() {
+    it('Test#005 : create project with unicode name, and spaces', function() {
         var projectname = '応応応応 用用用用';
         var projectid = 'com.test.app4';
 
         createAndBuild(projectname, projectid);
     });
 
-    it('create project with ascii+unicode name, no spaces', function() {
+    it('Test#006 : create project with ascii+unicode name, no spaces', function() {
         var projectname = '応応応応hello用用用用';
         var projectid = 'com.test.app5';
 
         createAndBuild(projectname, projectid);
     });
 
-    it('create project with ascii+unicode name, and spaces', function() {
+    it('Test#007 : create project with ascii+unicode name, and spaces', function() {
         var projectname = '応応応応 hello 用用用用';
         var projectid = 'com.test.app6';
 
         createAndBuild(projectname, projectid);
     });
 
+    it('Test#008 : create project with ascii name, and spaces, ampersand(&)', function() {
+       var projectname = 'hello & world';
+       var projectid = 'com.test.app7';
+
+       createAndBuild(projectname, projectid);
+    });
+
 });
 
 describe('end-to-end list validation', function(){
-    it('handles list parameter', function() {
+    it('Test#008 : handles list parameter', function() {
         shell.cp('-f', path.join(cordova_bin, 'check_reqs'), path.join(cordova_bin, 'templates', 'scripts', 'cordova', 'check_reqs'));
         shell.cp('-f', path.join(cordova_bin, 'lib', 'check_reqs.js'), path.join(cordova_bin, 'templates', 'scripts', 'cordova', 'lib', 'check_reqs.js'));
         shell.cp('-f', path.join(cordova_bin, 'lib', 'versions.js'), path.join(cordova_bin, 'templates', 'scripts', 'cordova', 'lib', 'versions.js'));
diff --git a/tests/spec/unit/Api.spec.js b/tests/spec/unit/Api.spec.js
index 9c25f5908..facd507c8 100644
--- a/tests/spec/unit/Api.spec.js
+++ b/tests/spec/unit/Api.spec.js
@@ -25,10 +25,10 @@ var iosProjectFixture = path.join(FIXTURES, 'ios-config-xml');
 describe('Platform Api', function () {
 
     describe('constructor', function() {
-        it('should throw if provided directory does not contain an xcodeproj file', function() {
+        it('Test 001 : should throw if provided directory does not contain an xcodeproj file', function() {
             expect(function() { new Api('ios', path.join(FIXTURES, '..')); }).toThrow();
         });
-        it('should create an instance with path, pbxproj, xcodeproj, originalName and cordovaproj properties', function() {
+        it('Test 002 : should create an instance with path, pbxproj, xcodeproj, originalName and cordovaproj properties', function() {
             expect(function() {
                 var p = new Api('ios',iosProjectFixture);
                 expect(p.locations.root).toEqual(iosProjectFixture);
diff --git a/tests/spec/unit/Plugman/common.spec.js b/tests/spec/unit/Plugman/common.spec.js
index 58eb27b87..7a0c34495 100644
--- a/tests/spec/unit/Plugman/common.spec.js
+++ b/tests/spec/unit/Plugman/common.spec.js
@@ -40,13 +40,13 @@ var removeFileAndParents = common.__get__('removeFileAndParents');
 describe('common handler routines', function() {
 
     describe('copyFile', function() {
-        it('should throw if source path not found', function(){
+        it('Test 001 : should throw if source path not found', function(){
             shell.rm('-rf', test_dir);
             expect(function(){copyFile(test_dir, src, project_dir, dest);}).
                 toThrow(new Error('"' + src + '" not found!'));
         });
 
-        it('should throw if src not in plugin directory', function(){
+        it('Test 002 : should throw if src not in plugin directory', function(){
             shell.mkdir('-p', project_dir);
             fs.writeFileSync(non_plugin_file, 'contents', 'utf-8');
             var outside_file = '../non_plugin_file';
@@ -55,7 +55,7 @@ describe('common handler routines', function() {
             shell.rm('-rf', test_dir);
         });
 
-        it('should allow symlink src, if inside plugin', function(){
+        it('Test 003 : should allow symlink src, if inside plugin', function(){
             shell.mkdir('-p', srcDirTree);
             fs.writeFileSync(srcFile, 'contents', 'utf-8');
 
@@ -68,7 +68,7 @@ describe('common handler routines', function() {
             shell.rm('-rf', project_dir);
         });
 
-        it('should throw if symlink is linked to a file outside the plugin', function(){
+        it('Test 004 : should throw if symlink is linked to a file outside the plugin', function(){
             shell.mkdir('-p', srcDirTree);
             fs.writeFileSync(non_plugin_file, 'contents', 'utf-8');
 
@@ -82,7 +82,7 @@ describe('common handler routines', function() {
             shell.rm('-rf', project_dir);
         });
 
-        it('should throw if dest is outside the project directory', function(){
+        it('Test 005 : should throw if dest is outside the project directory', function(){
             shell.mkdir('-p', srcDirTree);
             fs.writeFileSync(srcFile, 'contents', 'utf-8');
             expect(function(){copyFile(test_dir, srcFile, project_dir, non_plugin_file);}).
@@ -90,11 +90,11 @@ describe('common handler routines', function() {
             shell.rm('-rf', project_dir);
         });
 
-        it('should call mkdir -p on target path', function(){
+        it('Test 006 : should call mkdir -p on target path', function(){
             shell.mkdir('-p', srcDirTree);
             fs.writeFileSync(srcFile, 'contents', 'utf-8');
 
-            var s = spyOn(shell, 'mkdir').andCallThrough();
+            var s = spyOn(shell, 'mkdir').and.callThrough();
             var resolvedDest = path.resolve(project_dir, dest);
 
             copyFile(test_dir, srcFile, project_dir, dest);
@@ -104,11 +104,11 @@ describe('common handler routines', function() {
             shell.rm('-rf', project_dir);
         });
 
-        it('should call cp source/dest paths', function(){
+        it('Test 007 : should call cp source/dest paths', function(){
             shell.mkdir('-p', srcDirTree);
             fs.writeFileSync(srcFile, 'contents', 'utf-8');
 
-            var s = spyOn(shell, 'cp').andCallThrough();
+            var s = spyOn(shell, 'cp').and.callThrough();
             var resolvedDest = path.resolve(project_dir, dest);
 
             copyFile(test_dir, srcFile, project_dir, dest);
@@ -122,7 +122,7 @@ describe('common handler routines', function() {
     });
 
     describe('copyNewFile', function () {
-        it('should throw if target path exists', function(){
+        it('Test 008 : should throw if target path exists', function(){
             shell.mkdir('-p', dest);
             expect(function(){copyNewFile(test_dir, src, project_dir, dest);}).
                 toThrow(new Error('"' + dest + '" already exists!'));
@@ -132,11 +132,11 @@ describe('common handler routines', function() {
     });
 
     describe('deleteJava', function() {
-        it('should call fs.unlinkSync on the provided paths', function(){
+        it('Test 009 : should call fs.unlinkSync on the provided paths', function(){
             shell.mkdir('-p', srcDirTree);
             fs.writeFileSync(srcFile, 'contents', 'utf-8');
 
-            var s = spyOn(fs, 'unlinkSync').andCallThrough();
+            var s = spyOn(fs, 'unlinkSync').and.callThrough();
             removeFileAndParents(project_dir, srcFile);
             expect(s).toHaveBeenCalled();
             expect(s).toHaveBeenCalledWith(path.resolve(project_dir, srcFile));
@@ -144,7 +144,7 @@ describe('common handler routines', function() {
             shell.rm('-rf', srcDirTree);
         });
 
-        it('should delete empty directories after removing source code in path hierarchy', function(){
+        it('Test 010 : should delete empty directories after removing source code in path hierarchy', function(){
             shell.mkdir('-p', srcDirTree);
             fs.writeFileSync(srcFile, 'contents', 'utf-8');
 
@@ -156,7 +156,7 @@ describe('common handler routines', function() {
             shell.rm('-rf', srcDirTree);
         });
 
-        it('should delete the top-level src directory if all plugins added were removed', function(){
+        it('Test 011 : should delete the top-level src directory if all plugins added were removed', function(){
             shell.mkdir('-p', srcDirTree);
             fs.writeFileSync(srcFile, 'contents', 'utf-8');
 
diff --git a/tests/spec/unit/Plugman/pluginHandler.spec.js b/tests/spec/unit/Plugman/pluginHandler.spec.js
index a21783814..4cf733c04 100644
--- a/tests/spec/unit/Plugman/pluginHandler.spec.js
+++ b/tests/spec/unit/Plugman/pluginHandler.spec.js
@@ -87,13 +87,13 @@ describe('ios plugin handler', function() {
                 spyOn(dummyProject.xcode, 'addSourceFile');
             });
 
-            it('should throw if source-file src cannot be found', function() {
+            it('Test 001 : should throw if source-file src cannot be found', function() {
                 var source = copyArray(invalid_source);
                 expect(function() {
                     install(source[1], faultyPluginInfo, dummyProject);
                 }).toThrow();
             });
-            it('should throw if source-file target already exists', function() {
+            it('Test 002 : should throw if source-file target already exists', function() {
                 var source = copyArray(valid_source);
                 var target = path.join(temp, 'SampleApp', 'Plugins', dummy_id, 'DummyPluginCommand.m');
                 shell.mkdir('-p', path.dirname(target));
@@ -102,31 +102,31 @@ describe('ios plugin handler', function() {
                     install(source[0], dummyPluginInfo, dummyProject);
                 }).toThrow();
             });
-            it('should call into xcodeproj\'s addSourceFile appropriately when element has no target-dir', function() {
+            it('Test 003 : should call into xcodeproj\'s addSourceFile appropriately when element has no target-dir', function() {
                 var source = copyArray(valid_source).filter(function(s) { return s.targetDir === undefined; });
                 install(source[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.addSourceFile)
                     .toHaveBeenCalledWith(slashJoin('Plugins', dummy_id, 'DummyPluginCommand.m'), {});
             });
-            it('should call into xcodeproj\'s addSourceFile appropriately when element has a target-dir', function() {
+            it('Test 004 : should call into xcodeproj\'s addSourceFile appropriately when element has a target-dir', function() {
                 var source = copyArray(valid_source).filter(function(s) { return s.targetDir !== undefined; });
                 install(source[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.addSourceFile)
                     .toHaveBeenCalledWith(slashJoin('Plugins', dummy_id, 'targetDir', 'TargetDirTest.m'), {});
             });
-            it('should cp the file to the right target location when element has no target-dir', function() {
+            it('Test 005 : should cp the file to the right target location when element has no target-dir', function() {
                 var source = copyArray(valid_source).filter(function(s) { return s.targetDir === undefined; });
                 var spy = spyOn(shell, 'cp');
                 install(source[0], dummyPluginInfo, dummyProject);
                 expect(spy).toHaveBeenCalledWith('-f', path.join(dummyplugin, 'src', 'ios', 'DummyPluginCommand.m'), path.join(temp, 'SampleApp', 'Plugins', dummy_id, 'DummyPluginCommand.m'));
             });
-            it('should cp the file to the right target location when element has a target-dir', function() {
+            it('Test 006 : should cp the file to the right target location when element has a target-dir', function() {
                 var source = copyArray(valid_source).filter(function(s) { return s.targetDir !== undefined; });
                 var spy = spyOn(shell, 'cp');
                 install(source[0], dummyPluginInfo, dummyProject);
                 expect(spy).toHaveBeenCalledWith('-f', path.join(dummyplugin, 'src', 'ios', 'TargetDirTest.m'), path.join(temp, 'SampleApp', 'Plugins', dummy_id, 'targetDir', 'TargetDirTest.m'));
             });
-            it('should call into xcodeproj\'s addFramework appropriately when element has framework=true set', function() {
+            it('Test 007 : should call into xcodeproj\'s addFramework appropriately when element has framework=true set', function() {
                 var source = copyArray(valid_source).filter(function(s) { return s.framework; });
                 spyOn(dummyProject.xcode, 'addFramework');
                 install(source[0], dummyPluginInfo, dummyProject);
@@ -142,13 +142,13 @@ describe('ios plugin handler', function() {
                 spyOn(dummyProject.xcode, 'addHeaderFile');
             });
 
-            it('should throw if header-file src cannot be found', function() {
+            it('Test 008 : should throw if header-file src cannot be found', function() {
                 var headers = copyArray(invalid_headers);
                 expect(function() {
                     install(headers[1], faultyPluginInfo, dummyProject);
                 }).toThrow();
             });
-            it('should throw if header-file target already exists', function() {
+            it('Test 009 : should throw if header-file target already exists', function() {
                 var headers = copyArray(valid_headers);
                 var target = path.join(temp, 'SampleApp', 'Plugins', dummy_id, 'DummyPluginCommand.h');
                 shell.mkdir('-p', path.dirname(target));
@@ -157,25 +157,25 @@ describe('ios plugin handler', function() {
                     install(headers[0], dummyPluginInfo, dummyProject);
                 }).toThrow();
             });
-            it('should call into xcodeproj\'s addHeaderFile appropriately when element has no target-dir', function() {
+            it('Test 010 : should call into xcodeproj\'s addHeaderFile appropriately when element has no target-dir', function() {
                 var headers = copyArray(valid_headers).filter(function(s) { return s.targetDir === undefined; });
                 install(headers[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.addHeaderFile)
                     .toHaveBeenCalledWith(slashJoin('Plugins', dummy_id, 'DummyPluginCommand.h'));
             });
-            it('should call into xcodeproj\'s addHeaderFile appropriately when element a target-dir', function() {
+            it('Test 011 : should call into xcodeproj\'s addHeaderFile appropriately when element a target-dir', function() {
                 var headers = copyArray(valid_headers).filter(function(s) { return s.targetDir !== undefined; });
                 install(headers[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.addHeaderFile)
                     .toHaveBeenCalledWith(slashJoin('Plugins', dummy_id, 'targetDir', 'TargetDirTest.h'));
             });
-            it('should cp the file to the right target location when element has no target-dir', function() {
+            it('Test 012 : should cp the file to the right target location when element has no target-dir', function() {
                 var headers = copyArray(valid_headers).filter(function(s) { return s.targetDir === undefined; });
                 var spy = spyOn(shell, 'cp');
                 install(headers[0], dummyPluginInfo, dummyProject);
                 expect(spy).toHaveBeenCalledWith('-f', path.join(dummyplugin, 'src', 'ios', 'DummyPluginCommand.h'), path.join(temp, 'SampleApp', 'Plugins', dummy_id, 'DummyPluginCommand.h'));
             });
-            it('should cp the file to the right target location when element has a target-dir', function() {
+            it('Test 013 : should cp the file to the right target location when element has a target-dir', function() {
                 var headers = copyArray(valid_headers).filter(function(s) { return s.targetDir !== undefined; });
                 var spy = spyOn(shell, 'cp');
                 install(headers[0], dummyPluginInfo, dummyProject);
@@ -190,41 +190,41 @@ describe('ios plugin handler', function() {
                 spyOn(dummyProject.xcode, 'addResourceFile');
             });
 
-            it('should throw if resource-file src cannot be found', function() {
+            it('Test 014 : should throw if resource-file src cannot be found', function() {
                 var resources = copyArray(invalid_resources);
                 expect(function() {
                     install(resources[0], faultyPluginInfo, dummyProject);
-                }).toThrow('Cannot find resource file "' + path.resolve(faultyplugin, 'src/ios/IDontExist.bundle') + '" for plugin ' + faultyPluginInfo.id + ' in iOS platform');
+                }).toThrow(new Error ('Cannot find resource file "' + path.resolve(faultyplugin, 'src/ios/IDontExist.bundle') + '" for plugin ' + faultyPluginInfo.id + ' in iOS platform'));
             });
-            it('should throw if resource-file target already exists', function() {
+            it('Test 015 : should throw if resource-file target already exists', function() {
                 var resources = copyArray(valid_resources);
                 var target = path.join(temp, 'SampleApp', 'Resources', 'DummyPlugin.bundle');
                 shell.mkdir('-p', path.dirname(target));
                 fs.writeFileSync(target, 'some bs', 'utf-8');
                 expect(function() {
                     install(resources[0], dummyPluginInfo, dummyProject);
-                }).toThrow('File already exists at detination "' + target + '" for resource file specified by plugin ' + dummyPluginInfo.id + ' in iOS platform');
+                }).toThrow(new Error('File already exists at detination "' + target + '" for resource file specified by plugin ' + dummyPluginInfo.id + ' in iOS platform'));
             });
-            it('should call into xcodeproj\'s addResourceFile', function() {
+            it('Test 016 : should call into xcodeproj\'s addResourceFile', function() {
                 var resources = copyArray(valid_resources);
                 install(resources[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.addResourceFile)
                     .toHaveBeenCalledWith(path.join('Resources', 'DummyPlugin.bundle'));
             });
-            it('should cp the file to the right target location', function() {
+            it('Test 017 : should cp the file to the right target location', function() {
                 var resources = copyArray(valid_resources);
                 var spy = spyOn(shell, 'cp');
                 install(resources[0], dummyPluginInfo, dummyProject);
                 expect(spy).toHaveBeenCalledWith('-f', path.join(dummyplugin, 'src', 'ios', 'DummyPlugin.bundle'), path.join(temp, 'SampleApp', 'Resources', 'DummyPlugin.bundle'));
             });
-            it('should symlink files to the right target location', function() {
+
+            it('Test 018 : should link files to the right target location', function() {
                 var resources = copyArray(valid_resources);
-                var spy = spyOn(fs, 'symlinkSync');
+                var spy = spyOn(fs, 'linkSync');
                 install(resources[0], dummyPluginInfo, dummyProject, { link: true });
                 var src_bundle = path.join(dummyplugin, 'src', 'ios', 'DummyPlugin.bundle');
                 var dest_bundle = path.join(temp, 'SampleApp/Resources/DummyPlugin.bundle');
-                expect(spy).toHaveBeenCalledWith(path.relative(fs.realpathSync(path.dirname(dest_bundle)), src_bundle),
-                                                    dest_bundle);
+                expect(spy).toHaveBeenCalledWith(src_bundle, dest_bundle);
             });
         });
 
@@ -235,7 +235,7 @@ describe('ios plugin handler', function() {
                 spyOn(dummyProject.xcode, 'addFramework');
             });
 
-            it('should call into xcodeproj\'s addFramework', function() {
+            it('Test 019 : should call into xcodeproj\'s addFramework', function() {
                 var frameworks = copyArray(valid_custom_frameworks);
                 install(frameworks[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.addFramework)
@@ -247,35 +247,35 @@ describe('ios plugin handler', function() {
             // * framework that shouldn't be added/removed
 
             describe('with custom="true" attribute', function () {
-                it('should throw if framework src cannot be found', function() {
+                it('Test 020 : should throw if framework src cannot be found', function() {
                     var frameworks = copyArray(invalid_custom_frameworks);
                     expect(function() {
                         install(frameworks[0], faultyPluginInfo, dummyProject);
-                    }).toThrow('Cannot find framework "' + path.resolve(faultyplugin, 'src/ios/NonExistantCustomFramework.framework')  + '" for plugin ' + faultyPluginInfo.id + ' in iOS platform');
+                    }).toThrow(new Error ('Cannot find framework "' + path.resolve(faultyplugin, 'src/ios/NonExistantCustomFramework.framework')  + '" for plugin ' + faultyPluginInfo.id + ' in iOS platform'));
                 });
-                it('should throw if framework target already exists', function() {
+                it('Test 021 : should throw if framework target already exists', function() {
                     var frameworks = copyArray(valid_custom_frameworks);
                     var target = path.join(temp, 'SampleApp/Plugins/org.test.plugins.dummyplugin/Custom.framework');
                     shell.mkdir('-p', target);
                     expect(function() {
                         install(frameworks[0], dummyPluginInfo, dummyProject);
-                    }).toThrow('Framework "' + target + '" for plugin ' + dummyPluginInfo.id + ' already exists in iOS platform');
+                    }).toThrow(new Error ('Framework "' + target + '" for plugin ' + dummyPluginInfo.id + ' already exists in iOS platform'));
                 });
-                it('should cp the file to the right target location', function() {
+                it('Test 022 : should cp the file to the right target location', function() {
                     var frameworks = copyArray(valid_custom_frameworks);
                     var spy = spyOn(shell, 'cp');
                     install(frameworks[0], dummyPluginInfo, dummyProject);
                     expect(spy).toHaveBeenCalledWith('-Rf', path.join(dummyplugin, 'src', 'ios', 'Custom.framework', '*'),
                                                      path.join(temp, 'SampleApp/Plugins/org.test.plugins.dummyplugin/Custom.framework'));
                 });
-                it('should deep symlink files to the right target location', function() {
+
+                it('Test 023 : should deep symlink files to the right target location', function() {
                     var frameworks = copyArray(valid_custom_frameworks);
-                    var spy = spyOn(fs, 'symlinkSync');
+                    var spy = spyOn(fs, 'linkSync');
                     install(frameworks[0], dummyPluginInfo, dummyProject, { link: true });
                     var src_binlib = path.join(dummyplugin, 'src', 'ios', 'Custom.framework', 'somebinlib');
                     var dest_binlib = path.join(temp, 'SampleApp/Plugins/org.test.plugins.dummyplugin/Custom.framework/somebinlib');
-                    expect(spy).toHaveBeenCalledWith(path.relative(fs.realpathSync(path.dirname(dest_binlib)), src_binlib),
-                                                     dest_binlib);
+                    expect(spy).toHaveBeenCalledWith(src_binlib, dest_binlib);
                 });
             });
         });
@@ -291,13 +291,13 @@ describe('ios plugin handler', function() {
                 platformWwwDest = path.resolve(dummyProject.platformWww, 'plugins', dummyPluginInfo.id, jsModule.src);
             });
 
-            it('should put module to both www and platform_www when options.usePlatformWww flag is specified', function () {
+            it('Test 024 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () {
                 install(jsModule, dummyPluginInfo, dummyProject, {usePlatformWww: true});
                 expect(fs.writeFileSync).toHaveBeenCalledWith(wwwDest, jasmine.any(String), 'utf-8');
                 expect(fs.writeFileSync).toHaveBeenCalledWith(platformWwwDest, jasmine.any(String), 'utf-8');
             });
 
-            it('should put module to www only when options.usePlatformWww flag is not specified', function () {
+            it('Test 025 : should put module to www only when options.usePlatformWww flag is not specified', function () {
                 install(jsModule, dummyPluginInfo, dummyProject);
                 expect(fs.writeFileSync).toHaveBeenCalledWith(wwwDest, jasmine.any(String), 'utf-8');
                 expect(fs.writeFileSync).not.toHaveBeenCalledWith(platformWwwDest, jasmine.any(String), 'utf-8');
@@ -315,20 +315,20 @@ describe('ios plugin handler', function() {
                 platformWwwDest = path.resolve(dummyProject.platformWww, asset.target);
             });
 
-            it('should put asset to both www and platform_www when options.usePlatformWww flag is specified', function () {
+            it('Test 026 : should put asset to both www and platform_www when options.usePlatformWww flag is specified', function () {
                 install(asset, dummyPluginInfo, dummyProject, {usePlatformWww: true});
                 expect(shell.cp).toHaveBeenCalledWith('-f', path.resolve(dummyPluginInfo.dir, asset.src), path.resolve(dummyProject.www, asset.target));
                 expect(shell.cp).toHaveBeenCalledWith('-f', path.resolve(dummyPluginInfo.dir, asset.src), path.resolve(dummyProject.platformWww, asset.target));
             });
 
-            it('should put asset to www only when options.usePlatformWww flag is not specified', function () {
+            it('Test 027 : should put asset to www only when options.usePlatformWww flag is not specified', function () {
                 install(asset, dummyPluginInfo, dummyProject);
                 expect(shell.cp).toHaveBeenCalledWith('-f', path.resolve(dummyPluginInfo.dir, asset.src), path.resolve(dummyProject.www, asset.target));
                 expect(shell.cp).not.toHaveBeenCalledWith(path.resolve(dummyPluginInfo.dir, asset.src), path.resolve(dummyProject.platformWww, asset.target));
             });
         });
 
-        it('of two plugins should apply xcode file changes from both', function(done){
+        it('Test 028 : of two plugins should apply xcode file changes from both', function(done){
             var api = new Api('ios', temp);
             var fail = jasmine.createSpy('fail');
 
@@ -372,29 +372,29 @@ describe('ios plugin handler', function() {
                 spyOn(dummyProject.xcode, 'removeFramework');
             });
 
-            it('should call into xcodeproj\'s removeSourceFile appropriately when element has no target-dir', function(){
+            it('Test 029 : should call into xcodeproj\'s removeSourceFile appropriately when element has no target-dir', function(){
                 var source = copyArray(valid_source).filter(function(s) { return s.targetDir === undefined; });
                 uninstall(source[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.removeSourceFile).toHaveBeenCalledWith(slashJoin('Plugins', dummy_id, 'DummyPluginCommand.m'));
             });
-            it('should call into xcodeproj\'s removeSourceFile appropriately when element a target-dir', function(){
+            it('Test 030 : should call into xcodeproj\'s removeSourceFile appropriately when element a target-dir', function(){
                 var source = copyArray(valid_source).filter(function(s) { return s.targetDir !== undefined; });
                 uninstall(source[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.removeSourceFile).toHaveBeenCalledWith(slashJoin('Plugins', dummy_id, 'targetDir', 'TargetDirTest.m'));
             });
-            it('should rm the file from the right target location when element has no target-dir', function(){
+            it('Test 031 : should rm the file from the right target location when element has no target-dir', function(){
                 var source = copyArray(valid_source).filter(function(s) { return s.targetDir === undefined; });
                 var spy = spyOn(shell, 'rm');
                 uninstall(source[0], dummyPluginInfo, dummyProject);
                 expect(spy).toHaveBeenCalledWith('-rf', path.join(temp, 'SampleApp', 'Plugins', dummy_id));
             });
-            it('should rm the file from the right target location when element has a target-dir', function(){
+            it('Test 032 : should rm the file from the right target location when element has a target-dir', function(){
                 var source = copyArray(valid_source).filter(function(s) { return s.targetDir !== undefined; });
                 var spy = spyOn(shell, 'rm');
                 uninstall(source[0], dummyPluginInfo, dummyProject);
                 expect(spy).toHaveBeenCalledWith('-rf', path.join(temp, 'SampleApp', 'Plugins', dummy_id, 'targetDir'));
             });
-            it('should call into xcodeproj\'s removeFramework appropriately when element framework=true set', function(){
+            it('Test 033 : should call into xcodeproj\'s removeFramework appropriately when element framework=true set', function(){
                 var source = copyArray(valid_source).filter(function(s) { return s.framework; });
                 uninstall(source[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.removeFramework).toHaveBeenCalledWith(path.join('SampleApp', 'Plugins', dummy_id, 'SourceWithFramework.m'));
@@ -407,17 +407,17 @@ describe('ios plugin handler', function() {
                 spyOn(dummyProject.xcode, 'removeHeaderFile');
             });
 
-            it('should call into xcodeproj\'s removeHeaderFile appropriately when element has no target-dir', function(){
+            it('Test 034 : should call into xcodeproj\'s removeHeaderFile appropriately when element has no target-dir', function(){
                 var headers = copyArray(valid_headers).filter(function(s) { return s.targetDir === undefined; });
                 uninstall(headers[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.removeHeaderFile).toHaveBeenCalledWith(slashJoin('Plugins', dummy_id, 'DummyPluginCommand.h'));
             });
-            it('should call into xcodeproj\'s removeHeaderFile appropriately when element a target-dir', function(){
+            it('Test 035 : should call into xcodeproj\'s removeHeaderFile appropriately when element a target-dir', function(){
                 var headers = copyArray(valid_headers).filter(function(s) { return s.targetDir !== undefined; });
                 uninstall(headers[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.removeHeaderFile).toHaveBeenCalledWith(slashJoin('Plugins', dummy_id, 'targetDir', 'TargetDirTest.h'));
             });
-            it('should rm the file from the right target location', function(){
+            it('Test 036 : should rm the file from the right target location', function(){
                 var headers = copyArray(valid_headers).filter(function(s) { return s.targetDir !== undefined; });
                 var spy = spyOn(shell, 'rm');
                 uninstall(headers[0], dummyPluginInfo, dummyProject);
@@ -431,12 +431,12 @@ describe('ios plugin handler', function() {
                 spyOn(dummyProject.xcode, 'removeResourceFile');
             });
 
-            it('should call into xcodeproj\'s removeResourceFile', function(){
+            it('Test 037 : should call into xcodeproj\'s removeResourceFile', function(){
                 var resources = copyArray(valid_resources);
                 uninstall(resources[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.removeResourceFile).toHaveBeenCalledWith(path.join('Resources', 'DummyPlugin.bundle'));
             });
-            it('should rm the file from the right target location', function(){
+            it('Test 038 : should rm the file from the right target location', function(){
                 var resources = copyArray(valid_resources);
                 var spy = spyOn(shell, 'rm');
                 uninstall(resources[0], dummyPluginInfo, dummyProject);
@@ -452,7 +452,7 @@ describe('ios plugin handler', function() {
 
             var frameworkPath = path.join(temp, 'SampleApp/Plugins/org.test.plugins.dummyplugin/Custom.framework').replace(/\\/g, '/');
 
-            it('should call into xcodeproj\'s removeFramework', function(){
+            it('Test 039 : should call into xcodeproj\'s removeFramework', function(){
                 var frameworks = copyArray(valid_custom_frameworks);
                 uninstall(frameworks[0], dummyPluginInfo, dummyProject);
                 expect(dummyProject.xcode.removeFramework).toHaveBeenCalledWith(frameworkPath, {customFramework:true});
@@ -463,28 +463,29 @@ describe('ios plugin handler', function() {
             // * framework that shouldn't be added/removed
 
             describe('with custom="true" attribute', function () {
-                it('should rm the file from the right target location', function(){
+                it('Test 040 : should rm the file from the right target location', function(){
                     var frameworks = copyArray(valid_custom_frameworks);
                     var spy = spyOn(shell, 'rm');
                     uninstall(frameworks[0], dummyPluginInfo, dummyProject);
                     expect(spy).toHaveBeenCalledWith('-rf', frameworkPath);
                 });
             });
-            
+
             describe('without custom="true" attribute ', function() {
-                it('should decrease framework counter after uninstallation', function() {  
+
+                it('Test 041 : should decrease framework counter after uninstallation', function() {  
                     var install = pluginHandlers.getInstaller('framework');
                     var dummyNonCustomFrameworks =  dummyPluginInfo.getFrameworks('ios').filter(function(f) {
                         return !f.custom;
                     });
                     var dummyFramework = dummyNonCustomFrameworks[0];
                     install(dummyFramework, dummyPluginInfo, dummyProject);
-                    install(dummyFramework, dummyPluginInfo, dummyProject); 
-                    var frameworkName = Object.keys(dummyProject.frameworks)[0]; 
+                    install(dummyFramework, dummyPluginInfo, dummyProject);
+                    var frameworkName = Object.keys(dummyProject.frameworks)[0];
                     expect(dummyProject.frameworks[frameworkName]).toEqual(2);
                     uninstall(dummyFramework, dummyPluginInfo, dummyProject);
-                    expect(dummyProject.frameworks[frameworkName]).toEqual(1);                  
-                    uninstall(dummyFramework, dummyPluginInfo, dummyProject); 
+                    expect(dummyProject.frameworks[frameworkName]).toEqual(1);
+                    uninstall(dummyFramework, dummyPluginInfo, dummyProject);
                     expect(dummyProject.frameworks[frameworkName]).not.toBeDefined();
                 });
             });
@@ -502,19 +503,19 @@ describe('ios plugin handler', function() {
                 spyOn(shell, 'rm');
 
                 var existsSyncOrig = fs.existsSync;
-                spyOn(fs, 'existsSync').andCallFake(function (file) {
+                spyOn(fs, 'existsSync').and.callFake(function (file) {
                     if ([wwwDest, platformWwwDest].indexOf(file) >= 0 ) return true;
                     return existsSyncOrig.call(fs, file);
                 });
             });
 
-            it('should put module to both www and platform_www when options.usePlatformWww flag is specified', function () {
+            it('Test 042 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () {
                 uninstall(jsModule, dummyPluginInfo, dummyProject, {usePlatformWww: true});
                 expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), wwwDest);
                 expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), platformWwwDest);
             });
 
-            it('should put module to www only when options.usePlatformWww flag is not specified', function () {
+            it('Test 043 : should put module to www only when options.usePlatformWww flag is not specified', function () {
                 uninstall(jsModule, dummyPluginInfo, dummyProject);
                 expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), wwwDest);
                 expect(shell.rm).not.toHaveBeenCalledWith(jasmine.any(String), platformWwwDest);
@@ -533,19 +534,19 @@ describe('ios plugin handler', function() {
                 spyOn(shell, 'rm');
 
                 var existsSyncOrig = fs.existsSync;
-                spyOn(fs, 'existsSync').andCallFake(function (file) {
+                spyOn(fs, 'existsSync').and.callFake(function (file) {
                     if ([wwwDest, platformWwwDest].indexOf(file) >= 0 ) return true;
                     return existsSyncOrig.call(fs, file);
                 });
             });
 
-            it('should put module to both www and platform_www when options.usePlatformWww flag is specified', function () {
+            it('Test 044 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () {
                 uninstall(asset, dummyPluginInfo, dummyProject, {usePlatformWww: true});
                 expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), wwwDest);
                 expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), platformWwwDest);
             });
 
-            it('should put module to www only when options.usePlatformWww flag is not specified', function () {
+            it('Test 045 : should put module to www only when options.usePlatformWww flag is not specified', function () {
                 uninstall(asset, dummyPluginInfo, dummyProject);
                 expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), wwwDest);
                 expect(shell.rm).not.toHaveBeenCalledWith(jasmine.any(String), platformWwwDest);
diff --git a/tests/spec/unit/Podfile.spec.js b/tests/spec/unit/Podfile.spec.js
index 545e63e59..8fb6ffdae 100644
--- a/tests/spec/unit/Podfile.spec.js
+++ b/tests/spec/unit/Podfile.spec.js
@@ -35,7 +35,7 @@ describe('unit tests for Podfile module', function () {
 
 	describe('tests', function () {
 
-		it ('throws CordovaError when the path filename is not named Podfile', function () {
+		it ('Test 001 : throws CordovaError when the path filename is not named Podfile', function () {
 			var dummyPath = 'NotAPodfile';
 			expect( function () { 
 				new Podfile(dummyPath);	 
@@ -43,34 +43,34 @@ describe('unit tests for Podfile module', function () {
 			.toThrow(new CordovaError(util.format('Podfile: The file at %s is not `%s`.', dummyPath, Podfile.FILENAME)));
 		});
 
-		it ('throws CordovaError when no projectName provided when creating a Podfile', function () {
+		it ('Test 002 : throws CordovaError when no projectName provided when creating a Podfile', function () {
 			expect( function () { 
 				new Podfile(fixturePodfile);	 
 			})
 			.toThrow(new CordovaError('Podfile: The projectName was not specified in the constructor.'));
 		});
 
-		it ('throws CordovaError when no pod name provided when adding a spec', function () {
+		it ('Test 003 : throws CordovaError when no pod name provided when adding a spec', function () {
 			expect( function () { 
 				podfile.addSpec(null);	 
 			})
 			.toThrow(new CordovaError('Podfile addSpec: name is not specified.'));
 		});
 
-		it ('adds the spec', function () {
+		it ('Test 004 : adds the spec', function () {
 			expect(podfile.existsSpec('Foo')).toBe(false);
 			podfile.addSpec('Foo', '1.0');
 			expect(podfile.existsSpec('Foo')).toBe(true);
 		});
 
-		it ('removes the spec', function () {
+		it ('Test 005 : removes the spec', function () {
 			podfile.addSpec('Baz', '3.0');
 			expect(podfile.existsSpec('Baz')).toBe(true);
 			podfile.removeSpec('Baz');
 			expect(podfile.existsSpec('Baz')).toBe(false);
 		});
 
-		it ('clears all specs', function () {
+		it ('Test 006 : clears all specs', function () {
 			podfile.addSpec('Bar', '2.0');
 			podfile.clear();
 
@@ -78,7 +78,7 @@ describe('unit tests for Podfile module', function () {
 			expect(podfile.existsSpec('Bar')).toBe(false);
 		});
 
-		it ('isDirty tests', function () {
+		it ('Test 007 : isDirty tests', function () {
 			podfile.addSpec('Foo', '1.0');
 			expect(podfile.isDirty()).toBe(true);
 
@@ -95,7 +95,7 @@ describe('unit tests for Podfile module', function () {
 			expect(podfile.isDirty()).toBe(false);
 		});
 
-		it ('writes specs to the Podfile', function () {
+		it ('Test 008 : writes specs to the Podfile', function () {
 			podfile.clear();
 			
 			podfile.addSpec('Foo', '1.0');
@@ -118,7 +118,7 @@ describe('unit tests for Podfile module', function () {
 			expect(newPodfile.getSpec('Bla')).toBe(podfile.getSpec('Bla'));
 		});
 
-		it ('runs before_install to install xcconfig paths', function () {
+		it ('Test 009 : runs before_install to install xcconfig paths', function () {
 			podfile.before_install();
 
 			// Template tokens in order: project name, project name, debug | release
@@ -139,7 +139,7 @@ describe('unit tests for Podfile module', function () {
 
 	});
 
-	it('tear down', function () {
+	it('Test 010 : tear down', function () {
 		podfile.destroy();
 
 		var text = '// DO NOT MODIFY -- auto-generated by Apache Cordova\n';
diff --git a/tests/spec/unit/PodsJson.spec.js b/tests/spec/unit/PodsJson.spec.js
index 6f845e931..aa9b9ca1d 100644
--- a/tests/spec/unit/PodsJson.spec.js
+++ b/tests/spec/unit/PodsJson.spec.js
@@ -30,7 +30,7 @@ describe('unit tests for Podfile module', function () {
 
 	describe('tests', function () {
 
-		it ('throws CordovaError when the path filename is not named pods.json', function () {
+		it ('Test 001 : throws CordovaError when the path filename is not named pods.json', function () {
 			var dummyPath = 'NotPodsJson';
 			expect( function () { 
 				new PodsJson(dummyPath);	 
@@ -38,7 +38,7 @@ describe('unit tests for Podfile module', function () {
 			.toThrow(new CordovaError(util.format('PodsJson: The file at %s is not `%s`.', dummyPath, PodsJson.FILENAME)));
 		});
 
-		it ('sets and gets pod test', function () {
+		it ('Test 002 : sets and gets pod test', function () {
 			var val0 = {
 				name: 'Foo',
 				type: 'podspec',
@@ -55,7 +55,7 @@ describe('unit tests for Podfile module', function () {
 			expect(val1.count).toEqual(val0.count);
 		});
 
-		it ('setsJson and remove pod test', function () {
+		it ('Test 003 : setsJson and remove pod test', function () {
 			var val0 = {
 				name: 'Bar',
 				type: 'podspec',
@@ -76,7 +76,7 @@ describe('unit tests for Podfile module', function () {
 			expect(val1).toBeFalsy();
 		});
 		
-		it ('clears all pods', function () {
+		it ('Test 004 : clears all pods', function () {
 			var val0 = {
 				name: 'Baz',
 				type: 'podspec',
@@ -91,7 +91,7 @@ describe('unit tests for Podfile module', function () {
 			expect(podsjson.get('Bar')).toBeFalsy();
 		});
 
-		it ('isDirty tests', function () {
+		it ('Test 005 : isDirty tests', function () {
 			var val0 = {
 				name: 'Foo',
 				type: 'podspec',
@@ -115,7 +115,7 @@ describe('unit tests for Podfile module', function () {
 			expect(podsjson.isDirty()).toBe(false);
 		});
 
-		it ('increment and decrement count test', function () {
+		it ('Test 006 : increment and decrement count test', function () {
 			var val0 = {
 				name: 'Bla',
 				type: 'podspec',
@@ -143,7 +143,7 @@ describe('unit tests for Podfile module', function () {
 			expect(podsjson.get(val0.name)).toBeFalsy();
 		});
 
-		it ('writes pods to the pods.json', function () {
+		it ('Test 007 : writes pods to the pods.json', function () {
 			podsjson.clear();
 
 			var vals = {
@@ -180,7 +180,7 @@ describe('unit tests for Podfile module', function () {
 
 	});
 
-	it('tear down', function () {
+	it('Test 008 : tear down', function () {
 		podsjson.destroy();
 	});
 });
diff --git a/tests/spec/unit/build.spec.js b/tests/spec/unit/build.spec.js
index f2c2da3be..7d0496652 100644
--- a/tests/spec/unit/build.spec.js
+++ b/tests/spec/unit/build.spec.js
@@ -110,7 +110,7 @@ describe('build', function () {
 
         it('should generate appropriate args for simulator', function(done) {
             var isDevice = false;
-            var args = getXcodeBuildArgs('TestProjectName', testProjectPath, 'TestConfiguration', isDevice, null);
+            var args = getXcodeBuildArgs('TestProjectName', testProjectPath, 'TestConfiguration', isDevice, null, 'iPhone 5s');
             expect(args[0]).toEqual('-xcconfig');
             expect(args[1]).toEqual(path.join('/test', 'build-testconfiguration.xcconfig'));
             expect(args[2]).toEqual('-workspace');
@@ -160,7 +160,7 @@ describe('build', function () {
             var isDevice = false;
             var buildFlags = '-archivePath TestArchivePathFlag';
 
-            var args = getXcodeBuildArgs('TestProjectName', testProjectPath, 'TestConfiguration', isDevice, buildFlags);
+            var args = getXcodeBuildArgs('TestProjectName', testProjectPath, 'TestConfiguration', isDevice, buildFlags, 'iPhone 5s');
             expect(args[0]).toEqual('-xcconfig');
             expect(args[1]).toEqual(path.join('/test', 'build-testconfiguration.xcconfig'));
             expect(args[2]).toEqual('-workspace');
diff --git a/tests/spec/unit/prepare.spec.js b/tests/spec/unit/prepare.spec.js
index bf0d30379..69d1d9233 100644
--- a/tests/spec/unit/prepare.spec.js
+++ b/tests/spec/unit/prepare.spec.js
@@ -559,11 +559,13 @@ describe('prepare', function() {
         beforeEach(function() {
             mv = spyOn(shell, 'mv');
             writeFileSyncSpy = spyOn(fs, 'writeFileSync');
-            spyOn(plist, 'parse').andReturn({});
-            spyOn(plist, 'build').andReturn('');
-            spyOn(xcode, 'project').andCallFake(function (pbxproj) {
+
+            spyOn(plist, 'parse').and.returnValue({});
+            spyOn(plist, 'build').and.returnValue('');
+            spyOn(xcode, 'project').and.callFake(function (pbxproj) {
+
                 var xc = new xcOrig(pbxproj);
-                update_name = spyOn(xc, 'updateProductName').andCallThrough();
+                update_name = spyOn(xc, 'updateProductName').and.callThrough();
                 return xc;
             });
             cfg.name = function() { return 'SampleApp'; }; // this is to match p's original project name (based on .xcodeproj)
@@ -573,7 +575,7 @@ describe('prepare', function() {
             spyOn(cfg, 'getPreference');
         });
 
-        it('should not update the app name in pbxproj', function(done) {
+        it('Test#001 : should not update the app name in pbxproj', function(done) {
             var cfg2OriginalName = cfg2.name;
 
             // originalName here will be `SampleApp` (based on the xcodeproj basename) from p
@@ -587,10 +589,11 @@ describe('prepare', function() {
             // restore cfg2 original name
             cfg2.name = cfg2OriginalName;
         });
+
         it('should write target-device preference', function(done) {
             var cfg2OriginalName = cfg2.name;
             cfg2.name = function() { return 'SampleApp'; }; // new config does *not* have a name change
-            writeFileSyncSpy.andCallThrough();
+            writeFileSyncSpy.and.callThrough();
 
             wrapper(updateProject(cfg2, p.locations), done, function() {
                 var xcode = require('xcode');
@@ -606,7 +609,7 @@ describe('prepare', function() {
         it('should write deployment-target preference', function(done) {
             var cfg2OriginalName = cfg2.name;
             cfg2.name = function() { return 'SampleApp'; }; // new config does *not* have a name change
-            writeFileSyncSpy.andCallThrough();
+            writeFileSyncSpy.and.callThrough();
 
             wrapper(updateProject(cfg2, p.locations), done, function() {
                 var xcode = require('xcode');
@@ -619,7 +622,8 @@ describe('prepare', function() {
                 cfg2.name = cfg2OriginalName;
             });
         });
-        it('should write out the app id to info plist as CFBundleIdentifier', function(done) {
+
+        it('Test#002 : should write out the app id to info plist as CFBundleIdentifier', function(done) {
             var orig = cfg.getAttribute;
             cfg.getAttribute = function(name) {
                 if (name == 'ios-CFBundleIdentifier') {
@@ -632,7 +636,7 @@ describe('prepare', function() {
                 expect(plist.build.mostRecentCall.args[0].CFBundleIdentifier).toEqual('testpkg');
             });
         });
-        it('should write out the app id to info plist as CFBundleIdentifier with ios-CFBundleIdentifier', function(done) {
+        it('Test#003 : should write out the app id to info plist as CFBundleIdentifier with ios-CFBundleIdentifier', function(done) {
             var orig = cfg.getAttribute;
             cfg.getAttribute = function(name) {
                 if (name == 'ios-CFBundleIdentifier') {
@@ -645,70 +649,70 @@ describe('prepare', function() {
                 expect(plist.build.mostRecentCall.args[0].CFBundleIdentifier).toEqual('testpkg_ios');
             });
         });
-        it('should write out the app version to info plist as CFBundleVersion', function(done) {
+        it('Test#004 : should write out the app version to info plist as CFBundleVersion', function(done) {
             wrapper(updateProject(cfg, p.locations), done, function() {
                 expect(plist.build.mostRecentCall.args[0].CFBundleShortVersionString).toEqual('one point oh');
             });
         });
-        it('should write out the orientation preference value', function(done) {
-            cfg.getPreference.andCallThrough();
+        it('Test#005 : should write out the orientation preference value', function(done) {
+            cfg.getPreference.and.callThrough();
             wrapper(updateProject(cfg, p.locations), done, function() {
                 expect(plist.build.mostRecentCall.args[0].UISupportedInterfaceOrientations).toEqual([ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown' ]);
                 expect(plist.build.mostRecentCall.args[0]['UISupportedInterfaceOrientations~ipad']).toEqual([ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown' ]);
                 expect(plist.build.mostRecentCall.args[0].UIInterfaceOrientation).toEqual([ 'UIInterfaceOrientationPortrait' ]);
             });
         });
-        it('should handle no orientation', function(done) {
-            cfg.getPreference.andReturn('');
+        it('Test#006 : should handle no orientation', function(done) {
+            cfg.getPreference.and.returnValue('');
             wrapper(updateProject(cfg, p.locations), done, function() {
                 expect(plist.build.mostRecentCall.args[0].UISupportedInterfaceOrientations).toBeUndefined();
                 expect(plist.build.mostRecentCall.args[0]['UISupportedInterfaceOrientations~ipad']).toBeUndefined();
                 expect(plist.build.mostRecentCall.args[0].UIInterfaceOrientation).toBeUndefined();
             });
         });
-        it('should handle default orientation', function(done) {
-            cfg.getPreference.andReturn('default');
+        it('Test#007 : should handle default orientation', function(done) {
+            cfg.getPreference.and.returnValue('default');
             wrapper(updateProject(cfg, p.locations), done, function() {
                 expect(plist.build.mostRecentCall.args[0].UISupportedInterfaceOrientations).toEqual([ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]);
                 expect(plist.build.mostRecentCall.args[0]['UISupportedInterfaceOrientations~ipad']).toEqual([ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]);
                 expect(plist.build.mostRecentCall.args[0].UIInterfaceOrientation).toBeUndefined();
             });
         });
-        it('should handle portrait orientation', function(done) {
-            cfg.getPreference.andReturn('portrait');
+        it('Test#008 : should handle portrait orientation', function(done) {
+            cfg.getPreference.and.returnValue('portrait');
             wrapper(updateProject(cfg, p.locations), done, function() {
                 expect(plist.build.mostRecentCall.args[0].UISupportedInterfaceOrientations).toEqual([ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown' ]);
                 expect(plist.build.mostRecentCall.args[0].UIInterfaceOrientation).toEqual([ 'UIInterfaceOrientationPortrait' ]);
             });
         });
-        it('should handle landscape orientation', function(done) {
-            cfg.getPreference.andReturn('landscape');
+        it('Test#009 : should handle landscape orientation', function(done) {
+            cfg.getPreference.and.returnValue('landscape');
             wrapper(updateProject(cfg, p.locations), done, function() {
                 expect(plist.build.mostRecentCall.args[0].UISupportedInterfaceOrientations).toEqual([ 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]);
                 expect(plist.build.mostRecentCall.args[0].UIInterfaceOrientation).toEqual([ 'UIInterfaceOrientationLandscapeLeft' ]);
             });
         });
-        it('should handle all orientation on ios', function(done) {
-            cfg.getPreference.andReturn('all');
+        it('Test#010 : should handle all orientation on ios', function(done) {
+            cfg.getPreference.and.returnValue('all');
             wrapper(updateProject(cfg, p.locations), done, function() {
                 expect(plist.build.mostRecentCall.args[0].UISupportedInterfaceOrientations).toEqual([ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]);
                 expect(plist.build.mostRecentCall.args[0].UIInterfaceOrientation).toEqual([ 'UIInterfaceOrientationPortrait' ]);
             });
         });
-        it('should handle custom orientation', function(done) {
-            cfg.getPreference.andReturn('some-custom-orientation');
+        it('Test#011 : should handle custom orientation', function(done) {
+            cfg.getPreference.and.returnValue('some-custom-orientation');
             wrapper(updateProject(cfg, p.locations), done, function() {
                 expect(plist.build.mostRecentCall.args[0].UISupportedInterfaceOrientations).toEqual([ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]);
                 expect(plist.build.mostRecentCall.args[0]['UISupportedInterfaceOrientations~ipad']).toEqual([ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]);
                 expect(plist.build.mostRecentCall.args[0].UIInterfaceOrientation).toBeUndefined();
             });
         });
+
         ///// App Transport Security Tests /////////////////////////////
         // NOTE: if an ATS value is equal to "null", it means that it was not written, 
         // thus it will use the default (check the default for the key).
         // This is to prevent the Info.plist to be too verbose.  
-
-        it('<access> - should handle wildcard', function(done) {
+        it('Test#012 : <access> - should handle wildcard', function(done) {
             wrapper(updateProject(cfg, p.locations), done, function() {
                 var ats = plist.build.mostRecentCall.args[0].NSAppTransportSecurity;
                 expect(ats.NSAllowsArbitraryLoads).toEqual(true);
@@ -724,7 +728,7 @@ describe('prepare', function() {
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-in-web-content="true" />' +
             '</widget>';
-            readFile.andReturn(configXml);
+            readFile.and.returnValue(configXml);
 
             var my_config = new ConfigParser('fake/path');
 
@@ -743,7 +747,7 @@ describe('prepare', function() {
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-in-media="true" />' +
             '</widget>';
-            readFile.andReturn(configXml);
+            readFile.and.returnValue(configXml);
 
             var my_config = new ConfigParser('fake/path');
 
@@ -762,7 +766,7 @@ describe('prepare', function() {
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-local-networking="true" />' +
             '</widget>';
-            readFile.andReturn(configXml);
+            readFile.and.returnValue(configXml);
 
             var my_config = new ConfigParser('fake/path');
 
@@ -781,7 +785,7 @@ describe('prepare', function() {
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-in-web-content="true" allows-arbitrary-loads-in-media="true" allows-local-networking="true" />' +
             '</widget>';
-            readFile.andReturn(configXml);
+            readFile.and.returnValue(configXml);
 
             var my_config = new ConfigParser('fake/path');
 
@@ -799,7 +803,7 @@ describe('prepare', function() {
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="http://cordova.apache.org" allows-arbitrary-loads-in-web-content="true" allows-arbitrary-loads-in-media="true" allows-local-networking="true" />' +
             '</widget>';
-            readFile.andReturn(configXml);
+            readFile.and.returnValue(configXml);
 
             var my_config = new ConfigParser('fake/path');
 
@@ -812,8 +816,7 @@ describe('prepare', function() {
             });
         });
         
-
-        it('<access> - https, subdomain wildcard', function(done) {
+        it('Test#13 : <access> - https, subdomain wildcard', function(done) {
             wrapper(updateProject(cfg, p.locations), done, function() {
                 var ats = plist.build.mostRecentCall.args[0].NSAppTransportSecurity;
                 var exceptionDomains = ats.NSExceptionDomains;
@@ -887,7 +890,7 @@ describe('prepare', function() {
             });
         });
 
-        it('<access> - http, no wildcard', function(done) {
+        it('Test#014 : <access> - http, no wildcard', function(done) {
             wrapper(updateProject(cfg, p.locations), done, function() {
                 var ats = plist.build.mostRecentCall.args[0].NSAppTransportSecurity;
                 var exceptionDomains = ats.NSExceptionDomains;
@@ -961,7 +964,7 @@ describe('prepare', function() {
 
             });
         });
-        it('<access> - https, no wildcard', function(done) {
+        it('Test#015 : <access> - https, no wildcard', function(done) {
             wrapper(updateProject(cfg, p.locations), done, function() {
                 var ats = plist.build.mostRecentCall.args[0].NSAppTransportSecurity;
                 var exceptionDomains = ats.NSExceptionDomains;
@@ -1035,7 +1038,7 @@ describe('prepare', function() {
             });
         });
         //////////////////////////////////////////////////
-        it('<access>, <allow-navigation> - http and https, no clobber', function(done) {
+        it('Test#016 : <access>, <allow-navigation> - http and https, no clobber', function(done) {
             var cfg2OriginalName = cfg2.name;
             // original name here is 'SampleApp' based on p
             // we are not testing a name change here, but testing a new config being used (name change test is above)
@@ -1069,7 +1072,7 @@ describe('prepare', function() {
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<allow-navigation href="*" />' +
             '</widget>';
-            readFile.andReturn(configXml);
+            readFile.and.returnValue(configXml);
 
             var my_config = new ConfigParser('fake/path');
 
@@ -1088,7 +1091,7 @@ describe('prepare', function() {
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<allow-navigation href="http://cordova.apache.org" allows-arbitrary-loads-in-web-content="true" allows-arbitrary-loads-in-media="true" allows-local-networking="true" />' +
             '</widget>';
-            readFile.andReturn(configXml);
+            readFile.and.returnValue(configXml);
 
             var my_config = new ConfigParser('fake/path');
 
@@ -1324,7 +1327,7 @@ describe('prepare', function() {
             });
         });
 
-        it('<allow-navigation> - wildcard scheme, wildcard subdomain', function(done) {
+        it('Test#017 : <allow-navigation> - wildcard scheme, wildcard subdomain', function(done) {
             wrapper(updateProject(cfg, p.locations), done, function() {
                 var ats = plist.build.mostRecentCall.args[0].NSAppTransportSecurity;
                 var exceptionDomains = ats.NSExceptionDomains;
@@ -1398,7 +1401,7 @@ describe('prepare', function() {
                 
             });
         });
-        it('<allow-navigation> - wildcard scheme, no subdomain', function(done) {
+        it('Test#018 : <allow-navigation> - wildcard scheme, no subdomain', function(done) {
             wrapper(updateProject(cfg, p.locations), done, function() {
                 var ats = plist.build.mostRecentCall.args[0].NSAppTransportSecurity;
                 var exceptionDomains = ats.NSExceptionDomains;
@@ -1472,7 +1475,7 @@ describe('prepare', function() {
                 
             });
         });
-        it('<allow-navigation> - should ignore wildcards like data:*, https:*, https://*', function(done) {
+        it('Test#019 : <allow-navigation> - should ignore wildcards like data:*, https:*, https://*', function(done) {
             wrapper(updateProject(cfg, p.locations), done, function() {
                 var ats = plist.build.mostRecentCall.args[0].NSAppTransportSecurity;
                 var exceptionDomains = ats.NSExceptionDomains;
@@ -1488,7 +1491,7 @@ describe('prepare', function() {
         var logFileOp = prepare.__get__('logFileOp');
 
         beforeEach(function () {
-            spyOn(FileUpdater, 'mergeAndUpdateDir').andReturn(true);
+            spyOn(FileUpdater, 'mergeAndUpdateDir').and.returnValue(true);
         });
 
         var project = {
@@ -1496,7 +1499,7 @@ describe('prepare', function() {
             locations: { www: path.join(iosProject, 'www') }
         };
 
-        it('should update project-level www and with platform agnostic www and merges', function() {
+        it('Test#020 : should update project-level www and with platform agnostic www and merges', function() {
             var merges_path = path.join(project.root, 'merges', 'ios');
             shell.mkdir('-p', merges_path);
             updateWww(project, p.locations);
@@ -1506,7 +1509,7 @@ describe('prepare', function() {
                 { rootDir : iosProject },
                 logFileOp);
         });
-        it('should skip merges if merges directory does not exist', function() {
+        it('Test#021 : should skip merges if merges directory does not exist', function() {
             var merges_path = path.join(project.root, 'merges', 'ios');
             shell.rm('-rf', merges_path);
             updateWww(project, p.locations);
diff --git a/tests/spec/unit/preparePlatform.spec.js b/tests/spec/unit/preparePlatform.spec.js
index 548f2f911..3c4b150c0 100644
--- a/tests/spec/unit/preparePlatform.spec.js
+++ b/tests/spec/unit/preparePlatform.spec.js
@@ -38,20 +38,6 @@ var dummyPlugin = path.join(FIXTURES, DUMMY_PLUGIN);
 
 shell.config.silent = true;
 
-var customMatchers = {
-    toBeInstalledIn: function(platformDir) {
-        var content;
-        try {
-            content = fs.readFileSync(path.join(platformDir, 'ios.json'));
-        } catch (e) {
-            return false;
-        }
-
-        var cfg = JSON.parse(content);
-        return Object.keys(cfg.installed_plugins).indexOf(this.actual) > -1;
-    }
-};
-
 describe('prepare after plugin add', function() {
     var api;
     beforeEach(function() {
@@ -59,14 +45,37 @@ describe('prepare after plugin add', function() {
         shell.cp('-rf', iosProjectFixture + '/*', iosPlatform);
         api = new Api('ios', iosPlatform, new EventEmitter());
 
-        this.addMatchers(customMatchers);
+        jasmine.addMatchers({
+            'toBeInstalledIn': function () {
+                return {
+                    compare: function(actual, expected) {
+                        var result = {};
+                        var content;
+                        try {
+                            content = fs.readFileSync(path.join(expected, 'ios.json'));
+                            var cfg = JSON.parse(content);
+                            result.pass = Object.keys(cfg.installed_plugins).indexOf(actual) > -1;
+                        } catch (e) {
+                            result.pass = false;
+                        }
+
+                        if(result.pass) {
+                            result.message = 'Expected '+ actual + ' to be installed in '+ expected+'.';
+                        } else {
+                            result.message = 'Expected '+ actual + ' to not be installed in '+ expected+'.';
+                        }
+                        return result;
+                    }
+                };
+            }
+        });
     });
 
     afterEach(function() {
         shell.rm('-rf', iosPlatform);
     });
 
-    it('should not overwrite plugin metadata added by "addPlugin"', function(done) {
+    it('Test 001 : should not overwrite plugin metadata added by "addPlugin"', function(done) {
         var project = {
             root: iosProject,
             projectConfig: new ConfigParser(path.join(iosProject, 'config.xml')),
@@ -77,7 +86,7 @@ describe('prepare after plugin add', function() {
         };
 
         var fail = jasmine.createSpy('fail')
-        .andCallFake(function (err) {
+        .and.callFake(function (err) {
             console.error(err);
         });
 
diff --git a/tests/spec/unit/projectFile.spec.js b/tests/spec/unit/projectFile.spec.js
index e4531df1a..3ba654653 100644
--- a/tests/spec/unit/projectFile.spec.js
+++ b/tests/spec/unit/projectFile.spec.js
@@ -40,21 +40,21 @@ describe('projectFile', function() {
     });
 
     describe('parse method', function () {
-        it('should throw if project is not an xcode project', function() {
+        it('Test#001 : should throw if project is not an xcode project', function() {
             shell.rm('-rf', path.join(iosProject, 'SampleApp', 'SampleApp.xcodeproj'));
             expect(function() { projectFile.parse(); }).toThrow();
         });
-        it('should throw if project does not contain an appropriate config.xml file', function() {
+        it('Test#002 : should throw if project does not contain an appropriate config.xml file', function() {
             shell.rm(path.join(iosProject, 'SampleApp', 'config.xml'));
             expect(function() { projectFile.parse(locations); })
-                .toThrow('Could not find *-Info.plist file, or config.xml file.');
+                .toThrow(new Error('Could not find *-Info.plist file, or config.xml file.'));
         });
-        it('should throw if project does not contain an appropriate -Info.plist file', function() {
+        it('Test#003 : should throw if project does not contain an appropriate -Info.plist file', function() {
             shell.rm(path.join(iosProject, 'SampleApp', 'SampleApp-Info.plist'));
             expect(function () { projectFile.parse(locations); })
-                .toThrow('Could not find *-Info.plist file, or config.xml file.');
+                .toThrow(new Error('Could not find *-Info.plist file, or config.xml file.'));
         });
-        it('should return right directory when multiple .plist files are present', function() {
+        it('Test#004 : should return right directory when multiple .plist files are present', function() {
             //Create a folder named A with config.xml and .plist files in it
             var pathToFolderA = path.join(iosProject, 'A');
             shell.mkdir(pathToFolderA);
@@ -76,7 +76,7 @@ describe('projectFile', function() {
     });
 
     describe('other methods', function () {
-        it('getPackageName method should return the CFBundleIdentifier from the project\'s Info.plist file', function() {
+        it('Test#005 : getPackageName method should return the CFBundleIdentifier from the project\'s Info.plist file', function() {
             expect(projectFile.parse(locations).getPackageName()).toEqual('com.example.friendstring');
         });
     });


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


> Apps with ampersand (&) in the name throw build error in cordova-ios@4.3.1
> --------------------------------------------------------------------------
>
>                 Key: CB-13948
>                 URL: https://issues.apache.org/jira/browse/CB-13948
>             Project: Apache Cordova
>          Issue Type: Bug
>          Components: cordova-ios
>    Affects Versions: 4.3.1
>            Reporter: Ronaldo Zanoni
>            Assignee: Suraj Pindoria
>            Priority: Major
>
> Cordova apps with an ampersand in the name give a build error in cordova-ios 4.3.1:
> "xcodebuild: error: Unable to load workspace 'Tom & Jerry.xcworkspace'.
> Reason: Could not open workspace file at ~/git/Tom & Jerry/platforms/ios/Tom & Jerry.xcworkspace/contents.xcworkspacedata"
> I found that the problem is with the .xcworkspace file. The ampersand doesn't get encoded in the XML files that are generated.
> PR that created the problem: [https://github.com/apache/cordova-ios/pull/247]
> Reproduce:
> 1. run cordova create HelloCordova
> 2. edit config.xml <name> tag inside to "Tom & Jerry"
> 3. run cordova platform add ios@4.3.1
> 4. run cordova build ios 
> --> ERROR
> *NOTE*: This was fixed in the _cordova-ios 4.4.0_, but that version has removed the _IOS 8_ support and we need that support yet.
> So, the solution is merge the branch _origin/4.3.x_ with this pull request: [https://github.com/apache/cordova-ios/pull/288]
>  



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@cordova.apache.org
For additional commands, e-mail: issues-help@cordova.apache.org


Mime
View raw message