You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by an...@apache.org on 2015/12/11 10:02:43 UTC

[04/49] cordova-windows git commit: CB-9828 Implement and expose PlatformApi for Windows

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/node_modules/winjs/package.json
----------------------------------------------------------------------
diff --git a/node_modules/winjs/package.json b/node_modules/winjs/package.json
index 50dd0d2..938812a 100644
--- a/node_modules/winjs/package.json
+++ b/node_modules/winjs/package.json
@@ -2,7 +2,7 @@
   "name": "winjs",
   "title": "Windows Library for JavaScript (WinJS)",
   "description": "WinJS is a set of JavaScript toolkits that allow developers to build applications using HTML/JS/CSS technology.",
-  "version": "4.0.1",
+  "version": "4.4.0",
   "main": "js/ui.js",
   "homepage": "http://try.buildwinjs.com/",
   "author": {
@@ -11,7 +11,7 @@
   },
   "repository": {
     "type": "git",
-    "url": "https://github.com/winjs/winjs"
+    "url": "git+https://github.com/winjs/winjs.git"
   },
   "licenses": [
     {
@@ -23,11 +23,13 @@
     "url": "https://github.com/winjs/winjs/issues"
   },
   "devDependencies": {
+    "bowser": "~1.0.0",
     "chalk": "~0.5.1",
     "fs-extra": "^0.10.0",
     "glob": "^4.0.5",
     "grunt": "~0.4.5",
     "grunt-contrib-clean": "~0.6.0",
+    "grunt-contrib-compress": "^0.13.0",
     "grunt-contrib-concat": "~0.5.0",
     "grunt-contrib-connect": "^0.8.0",
     "grunt-contrib-copy": "~0.5.0",
@@ -36,7 +38,10 @@
     "grunt-contrib-less": "~0.11.4",
     "grunt-contrib-requirejs": "^0.4.4",
     "grunt-contrib-uglify": "^0.5.1",
-    "grunt-jscs": "^0.6.2",
+    "grunt-git": "^0.3.5",
+    "grunt-github-releaser": "^0.1.17",
+    "grunt-jscs": "^1.8.0",
+    "grunt-nuget": "^0.1.4",
     "grunt-replace": "~0.7.8",
     "grunt-saucelabs": "git+https://github.com/xirzec/grunt-saucelabs.git#debug",
     "grunt-shell": "~0.7.0",
@@ -51,24 +56,33 @@
     "typescript": "1.4.1",
     "websocket": "^1.0.8"
   },
-  "_id": "winjs@4.0.1",
+  "_id": "winjs@4.4.0",
   "scripts": {},
-  "_shasum": "7bf65fadab719fec2bed306540e2cf69948e2e30",
-  "_from": "winjs@>=4.0.0",
-  "_npmVersion": "2.7.4",
-  "_nodeVersion": "0.12.2",
+  "_shasum": "ca43f1d75eefa6e04e995262bb0e76ac51160c9a",
+  "_from": "winjs@>=4.4.0 <5.0.0",
+  "_resolved": "https://registry.npmjs.org/winjs/-/winjs-4.4.0.tgz",
+  "_npmVersion": "2.14.0",
+  "_nodeVersion": "4.1.1",
   "_npmUser": {
-    "name": "rigdern",
-    "email": "adam.comella@outlook.com"
+    "name": "amazingjaze",
+    "email": "AmazingJaze@gmail.com"
   },
   "dist": {
-    "shasum": "7bf65fadab719fec2bed306540e2cf69948e2e30",
-    "tarball": "http://registry.npmjs.org/winjs/-/winjs-4.0.1.tgz"
+    "shasum": "ca43f1d75eefa6e04e995262bb0e76ac51160c9a",
+    "tarball": "http://registry.npmjs.org/winjs/-/winjs-4.4.0.tgz"
   },
   "maintainers": [
     {
-      "name": "xirzec",
-      "email": "xirzec@xirzec.com"
+      "name": "amazingjaze",
+      "email": "AmazingJaze@gmail.com"
+    },
+    {
+      "name": "jdalton",
+      "email": "john.david.dalton@gmail.com"
+    },
+    {
+      "name": "jseanxu",
+      "email": "jseanxu@live.com"
     },
     {
       "name": "phosphoer",
@@ -77,9 +91,16 @@
     {
       "name": "rigdern",
       "email": "adam.comella@outlook.com"
+    },
+    {
+      "name": "winjs",
+      "email": "joshrenn@microsoft.com"
+    },
+    {
+      "name": "xirzec",
+      "email": "xirzec@xirzec.com"
     }
   ],
   "directories": {},
-  "_resolved": "https://registry.npmjs.org/winjs/-/winjs-4.0.1.tgz",
   "readme": "ERROR: No README data found!"
 }

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 265bc88..4054421 100644
--- a/package.json
+++ b/package.json
@@ -1,46 +1,50 @@
 {
-    "name": "cordova-windows",
-    "version": "4.3.0-dev",
-    "description": "cordova-windows release",
-    "main": "bin/create",
-    "repository": {
-        "type": "git",
-        "url": "https://github.com/apache/cordova-windows"
-    },
-    "keywords": [
-        "windows",
-        "cordova",
-        "apache"
-    ],
-    "scripts": {
-        "test": "npm run jshint && npm run test-unit && npm run test-e2e",
-        "test-unit": "node node_modules/jasmine-node/lib/jasmine-node/cli.js --captureExceptions spec/unit",
-        "test-e2e": "node node_modules/jasmine-node/lib/jasmine-node/cli.js --captureExceptions spec/e2e",
-        "cover": "node node_modules/istanbul/lib/cli.js cover --root template --print detail node_modules/jasmine-node/bin/jasmine-node -- spec/unit",
-        "jshint": "node node_modules/jshint/bin/jshint bin && node node_modules/jshint/bin/jshint template && node node_modules/jshint/bin/jshint spec"
-    },
-    "dependencies": {
-        "elementtree": "~0.1.5",
-        "node-uuid": "~1.4",
-        "nopt": "~3",
-        "q": ">0.9.0",
-        "shelljs": "~0.3",
-        "winjs": "^4.0.0"
-    },
-    "devDependencies": {
-        "jasmine-node": "1.14.5",
-        "jshint": "^2.6.0",
-        "rewire": ">=2.1.3",
-        "istanbul": "^0.3.4"
-    },
-    "bundledDependencies": [
-        "q",
-        "nopt",
-        "node-uuid",
-        "shelljs",
-        "elementtree",
-        "winjs"
-    ],
-    "author": "Apache Software Foundation",
-    "license": "Apache Version 2.0"
-}
\ No newline at end of file
+  "name": "cordova-windows",
+  "version": "4.3.0-dev",
+  "description": "cordova-windows release",
+  "bin": "bin/create",
+  "main": "template/cordova/Api.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/apache/cordova-windows"
+  },
+  "keywords": [
+    "windows",
+    "cordova",
+    "apache"
+  ],
+  "scripts": {
+    "test": "npm run jshint && npm run test-unit && npm run test-e2e",
+    "test-unit": "jasmine-node --captureExceptions spec/unit",
+    "test-e2e": "jasmine-node --captureExceptions spec/e2e",
+    "cover": "istanbul cover --root template --print detail node_modules/jasmine-node/bin/jasmine-node -- spec/unit",
+    "jshint": "jshint bin && jshint template && jshint spec"
+  },
+  "dependencies": {
+    "cordova-common": "^1.0.0",
+    "elementtree": "^0.1.6",
+    "node-uuid": "^1.4.3",
+    "nopt": "^3.0.4",
+    "q": "^1.4.1",
+    "semver": "^5.0.3",
+    "shelljs": "^0.5.3",
+    "winjs": "^4.4.0"
+  },
+  "devDependencies": {
+    "istanbul": "^0.4.0",
+    "jasmine-node": "1.14.5",
+    "jshint": "^2.8.0",
+    "rewire": "^2.1.3"
+  },
+  "bundledDependencies": [
+    "cordova-common",
+    "elementtree",
+    "node-uuid",
+    "nopt",
+    "q",
+    "shelljs",
+    "winjs"
+  ],
+  "author": "Apache Software Foundation",
+  "license": "Apache-2.0"
+}

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/spec/unit/AppxManifest.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/AppxManifest.spec.js b/spec/unit/AppxManifest.spec.js
new file mode 100644
index 0000000..d158144
--- /dev/null
+++ b/spec/unit/AppxManifest.spec.js
@@ -0,0 +1,102 @@
+/**
+    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.
+*/
+
+var rewire = require('rewire');
+var et = require('elementtree');
+var xml = require('cordova-common').xmlHelpers;
+var AppxManifest = rewire('../../template/cordova/lib/AppxManifest');
+var Win10AppxManifest = AppxManifest.__get__('Win10AppxManifest');
+
+var WINDOWS_MANIFEST = 'template/package.windows.appxmanifest';
+var WINDOWS_PHONE_MANIFEST = 'template/package.phone.appxmanifest';
+
+describe('AppxManifest', function () {
+
+    var XMLS = {
+        '/no/prefixed': new et.ElementTree(et.XML('<?xml version="1.0" encoding="UTF-8"?><Package/>')),
+        '/uap/prefixed': new et.ElementTree(et.XML('<?xml version="1.0" encoding="UTF-8"?><Package xmlns:uap=""/>'))
+    };
+
+    beforeEach(function () {
+        var parseElementtreeSyncOrig = xml.parseElementtreeSync;
+        spyOn(xml, 'parseElementtreeSync').andCallFake(function (manifestPath) {
+            return XMLS[manifestPath] || parseElementtreeSyncOrig(manifestPath);
+        });
+    });
+
+    describe('constructor', function () {
+
+        it('should create a new AppxManifest instance', function () {
+            var manifest;
+            expect(function () { manifest = new AppxManifest(WINDOWS_MANIFEST); }).not.toThrow();
+            expect(manifest instanceof AppxManifest).toBe(true);
+        });
+
+        it('should throw if first parameter is not a file', function () {
+            expect(function () { new AppxManifest('/invalid/path'); }).toThrow();
+        });
+
+        it('should throw if first parameter is not a valid manifest file (no "Package" tag)', function () {
+            expect(function () { new AppxManifest('/invalid/manifest'); }).toThrow();
+        });
+
+        it('should add ":" to manifest prefix if needed', function () {
+            expect(new AppxManifest(WINDOWS_MANIFEST, 'prefix').prefix).toEqual('prefix:');
+        });
+    });
+
+    describe('static get() method', function () {
+
+        it('should return an AppxManifest instance', function () {
+            expect(AppxManifest.get(WINDOWS_MANIFEST) instanceof AppxManifest).toBe(true);
+        });
+
+        it('should detect manifest prefix based on "Package" element attributes', function () {
+            expect(AppxManifest.get(WINDOWS_MANIFEST).prefix).toEqual('m2:');
+            expect(AppxManifest.get(WINDOWS_PHONE_MANIFEST).prefix).toEqual('m3:');
+        });
+
+        it('should instantiate either AppxManifest or Windows 10 AppxManifest based on manifest prefix', function () {
+            expect(AppxManifest.get('/no/prefixed').prefix).toEqual('');
+            expect(AppxManifest.get('/no/prefixed') instanceof AppxManifest).toBe(true);
+            expect(AppxManifest.get('/no/prefixed') instanceof Win10AppxManifest).toBe(false);
+
+            expect(AppxManifest.get('/uap/prefixed').prefix).toEqual('uap:');
+            expect(AppxManifest.get('/uap/prefixed') instanceof Win10AppxManifest).toBe(true);
+        });
+    });
+
+    describe('instance get* methods', function () {
+        var methods = ['getPhoneIdentity','getIdentity','getProperties','getApplication','getVisualElements'];
+
+        it('should exists', function () {
+            var manifest = AppxManifest.get(WINDOWS_PHONE_MANIFEST);
+            var emptyManifest = AppxManifest.get('/no/prefixed');
+
+            methods.forEach(function (method) {
+                expect(manifest[method]).toBeDefined();
+                expect(manifest[method]).toEqual(jasmine.any(Function));
+                expect(function () { manifest[method](); }).not.toThrow();
+                expect(function () { emptyManifest[method](); }).toThrow();
+                expect(manifest[method]()).toBeDefined();
+            });
+        });
+    });
+});
+

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/spec/unit/MSBuildTools.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/MSBuildTools.spec.js b/spec/unit/MSBuildTools.spec.js
index d9ed253..4850946 100644
--- a/spec/unit/MSBuildTools.spec.js
+++ b/spec/unit/MSBuildTools.spec.js
@@ -16,12 +16,12 @@
     specific language governing permissions and limitations
     under the License.
 */
-var Q = require('q'),
-    shell = require('shelljs'),
-    rewire = require('rewire'),
-    platformRoot = '../../template',
-    buildTools = rewire(platformRoot + '/cordova/lib/MSBuildTools.js'),
-    Version = require(platformRoot + '/cordova/lib/Version.js');
+var Q = require('q');
+var shell = require('shelljs');
+var rewire = require('rewire');
+var platformRoot = '../../template';
+var buildTools = rewire(platformRoot + '/cordova/lib/MSBuildTools.js');
+var Version = require(platformRoot + '/cordova/lib/Version.js');
 
 var fakeToolsPath = function (version) {
     return 'C:\\Program Files (x86)\\MSBuild\\' + version;
@@ -89,23 +89,26 @@ describe('findAvailableVersion method', function(){
 });
 
 describe('checkMSBuildVersion method', function(){
-    var checkMSBuildVersion = buildTools.__get__('checkMSBuildVersion'),
-        execOriginal;
+    var checkMSBuildVersion = buildTools.__get__('checkMSBuildVersion');
+
+    var spawnOriginal = buildTools.__get__('spawn');
+    var spawnSpy = jasmine.createSpy('spawn');
 
     beforeEach(function () {
-        execOriginal = buildTools.__get__('exec');
+        buildTools.__set__('spawn', spawnSpy);
     });
 
     afterEach(function () {
-        buildTools.__set__('exec', execOriginal);
+        buildTools.__set__('spawn', spawnOriginal);
     });
 
     it('spec.6 should return valid version and path', function(){
         var version  = '14.0';
 
-        buildTools.__set__('exec', function(cmd) {
-            return Q.resolve('\r\nHKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\12.0\r\n\tMSBuildToolsPath\tREG_SZ\t' + fakeToolsPath(version) + '\r\n\r\n');
-        });
+        spawnSpy.andReturn(Q.resolve(
+            '\r\nHKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\12.0\r\n\t' +
+            'MSBuildToolsPath\tREG_SZ\t' + fakeToolsPath(version) + '\r\n\r\n')
+        );
 
         checkMSBuildVersion(version).then(function (actual) {
             expect(actual.version).toBe(version);
@@ -114,37 +117,24 @@ describe('checkMSBuildVersion method', function(){
     });
 
     it('spec.7 should return null if no tools found for version', function(){
-        buildTools.__set__('exec', function(cmd) {
-            return Q.resolve('ERROR: The system was unable to find the specified registry key or value.');
-        });
+        spawnSpy.andReturn(Q.resolve('ERROR: The system was unable to find the specified registry key or value.'));
 
         checkMSBuildVersion('14.0').then(function (actual) {
-            expect(actual).toBeNull();
+            expect(actual).not.toBeDefined();
         });
     });
 
     it('spec.8 should return null on internal error', function(){
-        buildTools.__set__('exec', function(cmd) {
-            return Q.reject();
-        });
+        spawnSpy.andReturn(Q.reject());
 
         checkMSBuildVersion('14.0').then(function (actual) {
-            expect(actual).toBeNull();
+            expect(actual).not.toBeDefined();
         });
     });
 });
 
 describe('MSBuildTools object', function(){
-    var MSBuildTools = buildTools.__get__('MSBuildTools'),
-        spawnOriginal;
-
-    beforeEach(function () {
-        spawnOriginal = buildTools.__get__('spawn');
-    });
-
-    afterEach(function () {
-        buildTools.__set__('spawn', spawnOriginal);
-    });
+    var MSBuildTools = buildTools.__get__('MSBuildTools');
 
     it('spec.9 should have fields and methods defined', function() {
         var version   = '14.0',

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/spec/unit/Prepare.Win10.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/Prepare.Win10.spec.js b/spec/unit/Prepare.Win10.spec.js
index dc1568d..caa2e3b 100644
--- a/spec/unit/Prepare.Win10.spec.js
+++ b/spec/unit/Prepare.Win10.spec.js
@@ -19,100 +19,22 @@
 
 var rewire  = require('rewire'),
     prepare = rewire('../../template/cordova/lib/prepare'),
-    Version = require('../../template/cordova/lib/Version'),
-    et      = require('elementtree'),
+    AppxManifest = require('../../template/cordova/lib/AppxManifest'),
+    ConfigParser = require('../../template/cordova/lib/ConfigParser'),
     fs      = require('fs'),
-    getAllMinMaxUAPVersions         = prepare.__get__('getAllMinMaxUAPVersions'),
+    et      = require('elementtree'),
+    events  = require('cordova-common').events,
+    xml     = require('cordova-common').xmlHelpers,
+    updateManifestFile              = prepare.__get__('updateManifestFile'),
     applyCoreProperties             = prepare.__get__('applyCoreProperties'),
     applyAccessRules                = prepare.__get__('applyAccessRules'),
-    checkForRestrictedCaps          = prepare.__get__('checkForRestrictedCapabilities'),
-    ensureUapPrefixedCapabilities   = prepare.__get__('ensureUapPrefixedCapabilities');
+    applyNavigationWhitelist        = prepare.__get__('applyNavigationWhitelist'),
+    applyStartPage                  = prepare.__get__('applyStartPage');
 
 var Win10ManifestPath = 'template/package.windows10.appxmanifest',
     Win81ManifestPath = 'template/package.windows.appxmanifest';
 
 /***
-  * Unit tests for validating that min/max versions are correctly obtained 
-  * (for the function getAllMinMaxUAPVersions) from prepare.js.
-  **/
-describe('Min/Max UAP versions are correctly read from the config file.', function() {
-
-    var mockConfig = {
-        getMatchingPreferences: function(regexp) {
-            return [
-                { name: 'Windows.Universal-MinVersion', value: '10.0.9910.0' },
-                { name: 'Windows.Universal-MaxVersionTested', value: '10.0.9917.0' },
-                { name: 'Windows.Desktop-MinVersion', value: '10.0.9910.0' },
-                { name: 'Microsoft.Xbox-MaxVersionTested', value: '10.0.9917.0' }
-            ];
-        }
-    };
-
-    it('Should correctly transform all versions as a baseline.', function() {
-        var versionSet = getAllMinMaxUAPVersions(mockConfig);
-        var ver9910 = new Version(10, 0, 9910, 0);
-        var ver9917 = new Version(10, 0, 9917, 0);
-
-        expect(versionSet['Windows.Universal']).toBeDefined();
-        expect(ver9910.eq(versionSet['Windows.Universal'].MinVersion)).toBe(true);
-        expect(ver9917.eq(versionSet['Windows.Universal'].MaxVersionTested)).toBe(true);
-
-        expect(versionSet['Windows.Desktop']).toBeDefined();
-        expect(ver9910.eq(versionSet['Windows.Desktop'].MinVersion)).toBe(true);
-        expect(ver9910.eq(versionSet['Windows.Desktop'].MaxVersionTested)).toBe(true);
-
-        expect(versionSet['Microsoft.Xbox']).toBeDefined();
-        expect(ver9917.eq(versionSet['Microsoft.Xbox'].MinVersion)).toBe(true);
-        expect(ver9917.eq(versionSet['Microsoft.Xbox'].MaxVersionTested)).toBe(true);
-
-        expect(Object.keys(versionSet).length).toBe(3);
-    });
-
-});
-
-describe('Min/Max UAP versions are produced correctly even when the config file has no settings.', function() {
-    var mockConfig = {
-        getMatchingPreferences: function(regexp) {
-            return [];
-        }
-    };
-
-    it('Should correctly transform all versions as a baseline.', function() {
-        var versionSet = getAllMinMaxUAPVersions(mockConfig);
-        var verBaseline = prepare.__get__('BASE_UAP_VERSION');
-
-        expect(versionSet['Windows.Universal']).toBeDefined();
-        expect(verBaseline.eq(versionSet['Windows.Universal'].MinVersion)).toBe(true);
-        expect(verBaseline.eq(versionSet['Windows.Universal'].MaxVersionTested)).toBe(true);
-
-        expect(Object.keys(versionSet).length).toBe(1);
-    });
-});
-
-describe('Min/Max UAP versions are correctly read from the config file.', function() {
-
-    var mockConfig = {
-        getMatchingPreferences: function(regexp) {
-            return [
-                { name: 'Windows.Universal-MinVersion', value: '10.0.9910.f' },
-                { name: 'Windows.Universal-MaxVersionTested', value: '10.0.9917.0' },
-            ];
-        }
-    };
-
-    it('Should fail to produce min/max versions with a RangeError.', function() {
-        try {
-            getAllMinMaxUAPVersions(mockConfig);
-            expect(false).toBe(true);
-        }
-        catch (ex) {
-            expect(ex.constructor).toBe(RangeError);
-        }
-    });
-
-});
-
-/***
   * Unit tests for validating default ms-appx-web:// URI scheme in Win10
   * (for the function applyCoreProperties) from prepare.js.
   **/
@@ -124,6 +46,7 @@ var PreferencesBaseline = {
     WindowsStorePublisherName: null,
     WindowsStoreIdentityName: null
 };
+
 function createMockConfigAndManifestForApplyCoreProperties(startPage, preferences, win10, winPackageVersion) {
     if (!preferences) {
         preferences = { };
@@ -148,128 +71,135 @@ function createMockConfigAndManifestForApplyCoreProperties(startPage, preference
     };
 
     var filePath = win10 ? Win10ManifestPath : Win81ManifestPath;
-    var fileContents = fs.readFileSync(filePath, 'utf-8');
-    var manifest = new et.ElementTree(et.XML(fileContents.trim()));
+    var manifest = AppxManifest.get(filePath);
+    spyOn(fs, 'writeFileSync');
 
     return { config: config, manifest: manifest };
 }
 
 function addCapabilityDeclarationToMockManifest(manifest, capability) {
-    var capRoot = manifest.find('.//Capabilities');
+    var capRoot = manifest.doc.find('.//Capabilities');
     var cap = new et.Element('Capability');
     cap.attrib.Name = capability;
     capRoot.append(cap);
 }
 
-describe('A Windows 8.1 project should not have an HTTP or HTTPS scheme for its startup URI.', function() {
+describe('Windows 8.1 project', function() {
 
-    // arrange
-    var mockConfig = createMockConfigAndManifestForApplyCoreProperties('index.html', { 'WindowsDefaultUriPrefix': 'http://' }, false);
+    it('should not have an HTTP or HTTPS scheme for its startup URI.', function() {
 
-    // act
-    applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'm2:', false);
+        // arrange
+        var mockConfig = createMockConfigAndManifestForApplyCoreProperties('index.html', { 'WindowsDefaultUriPrefix': 'http://' }, false);
 
-    var app = mockConfig.manifest.find('.//Application');
-    expect(app.attrib.StartPage).toBe('www/index.html');
-});
+        // act
+        applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'm2:', false);
 
-describe('A Windows 8.1 project should not have any scheme for its startup URI.', function() {
+        var app = mockConfig.manifest.doc.find('.//Application');
+        expect(app.attrib.StartPage).toBe('www/index.html');
+    });
 
-    // arrange
-    var mockConfig = createMockConfigAndManifestForApplyCoreProperties('index.html', { 'WindowsDefaultUriPrefix': 'ms-appx://' }, false);
+    it('should not have any scheme for its startup URI.', function() {
 
-    // act
-    applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'm2:', false);
+        // arrange
+        var mockConfig = createMockConfigAndManifestForApplyCoreProperties('index.html', { 'WindowsDefaultUriPrefix': 'ms-appx://' }, false);
 
-    var app = mockConfig.manifest.find('.//Application');
-    expect(app.attrib.StartPage).toBe('www/index.html');
+        // act
+        applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'm2:', false);
+
+        var app = mockConfig.manifest.doc.find('.//Application');
+        expect(app.attrib.StartPage).toBe('www/index.html');
+    });
 });
 
-describe('A Windows 10 project default to ms-appx-web for its startup URI.', function() {
+describe('Windows 10 project', function() {
+    it('should default to ms-appx-web for its startup URI.', function() {
 
-    // arrange
-    var mockConfig = createMockConfigAndManifestForApplyCoreProperties('index.html', { }, true);
+        // arrange
+        var mockConfig = createMockConfigAndManifestForApplyCoreProperties('index.html', { }, true);
 
-    // act
-    applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'uap:', true);
+        // act
+        applyStartPage(mockConfig.config, mockConfig.manifest, true);
 
-    var app = mockConfig.manifest.find('.//Application');
-    expect(app.attrib.StartPage).toBe('ms-appx-web:///www/index.html');
-});
+        var app = mockConfig.manifest.doc.find('.//Application');
+        expect(app.attrib.StartPage).toBe('ms-appx-web:///www/index.html');
+    });
 
-describe('A Windows 10 project should allow ms-appx as its startup URI, and it gets removed from the final output.', function() {
+    it ('should allow ms-appx as its startup URI, and it gets removed from the final output.', function() {
 
-    // arrange
-    var mockConfig = createMockConfigAndManifestForApplyCoreProperties('index.html', { 'WindowsDefaultUriPrefix': 'ms-appx://' }, true);
+        // arrange
+        var mockConfig = createMockConfigAndManifestForApplyCoreProperties('index.html', { 'WindowsDefaultUriPrefix': 'ms-appx://' }, true);
 
-    // act
-    applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'uap:', true);
+        // act
+        applyStartPage(mockConfig.config, mockConfig.manifest, true);
 
-    var app = mockConfig.manifest.find('.//Application');
-    expect(app.attrib.StartPage).toBe('www/index.html');
-});
+        var app = mockConfig.manifest.doc.find('.//Application');
+        expect(app.attrib.StartPage).toBe('www/index.html');
+    });
 
-describe('A Windows 10 project should allow an HTTP or HTTPS scheme for its startup URI.', function() {
+    it('should allow an HTTP or HTTPS scheme for its startup URI.', function() {
 
-    // arrange
-    var mockConfig = createMockConfigAndManifestForApplyCoreProperties('www.contoso.com/', { 'WindowsDefaultUriPrefix': 'http://' }, true);
+        // arrange
+        var mockConfig = createMockConfigAndManifestForApplyCoreProperties('www.contoso.com/', { 'WindowsDefaultUriPrefix': 'http://' }, true);
 
-    // act
-    applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'uap:', true);
+        // act
+        applyStartPage(mockConfig.config, mockConfig.manifest, true);
 
-    var app = mockConfig.manifest.find('.//Application');
-    expect(app.attrib.StartPage).toBe('http://www.contoso.com/');
+        var app = mockConfig.manifest.doc.find('.//Application');
+        expect(app.attrib.StartPage).toBe('http://www.contoso.com/');
+    });
 });
 
-describe('An app specifying a Store DisplayName in its config.xml should have it reflected in the manifest.', function() {
 
-    // arrange
-    var mockConfig = createMockConfigAndManifestForApplyCoreProperties('www.contoso.com/', { 'WindowsDefaultUriPrefix': 'http://', 'WindowsStoreDisplayName': 'ContosoApp' }, true);
+describe('Windows Store preference', function () {
 
-    // act
-    applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'uap:', true);
+    it('"WindowsStoreDisplayName" should be reflected in the manifest.', function() {
 
-    var app = mockConfig.manifest.find('.//Properties/DisplayName');
-    expect(app.text).toBe('ContosoApp');
-});
+        // arrange
+        var mockConfig = createMockConfigAndManifestForApplyCoreProperties('www.contoso.com/', { 'WindowsDefaultUriPrefix': 'http://', 'WindowsStoreDisplayName': 'ContosoApp' }, true);
 
-describe('An app specifying a Store PublisherName in its config.xml should have it reflected in the manifest.', function() {
+        // act
+        applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'uap:', true);
 
-    // arrange
-    var mockConfig = createMockConfigAndManifestForApplyCoreProperties('www.contoso.com/', { 'WindowsDefaultUriPrefix': 'http://', 'WindowsStorePublisherName': 'Contoso Inc' }, true);
+        var app = mockConfig.manifest.doc.find('.//Properties/DisplayName');
+        expect(app.text).toBe('ContosoApp');
+    });
+
+    it('"WindowsStorePublisherName" should be reflected in the manifest.', function() {
 
-    // act
-    applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'uap:', true);
+        // arrange
+        var mockConfig = createMockConfigAndManifestForApplyCoreProperties('www.contoso.com/', { 'WindowsDefaultUriPrefix': 'http://', 'WindowsStorePublisherName': 'Contoso Inc' }, true);
 
-    var app = mockConfig.manifest.find('.//Properties/PublisherDisplayName');
-    expect(app.text).toBe('Contoso Inc');
+        // act
+        applyCoreProperties(mockConfig.config, mockConfig.manifest, 'fake-path', 'uap:', true);
+
+        var app = mockConfig.manifest.doc.find('.//Properties/PublisherDisplayName');
+        expect(app.text).toBe('Contoso Inc');
+    });
 });
 
 describe('A Windows 10 project should warn if it supports remote mode and restricted capabilities.', function() {
 
     // arrange
-    var mockConfig = createMockConfigAndManifestForApplyAccessRules(true, 'http://www.bing.com/*');
-    addCapabilityDeclarationToMockManifest(mockConfig.manifest, 'documentsLibrary');
-
+    var mockConfig;
     var stringFound     = false,
-        searchStr       = '   documentsLibrary',
-        oldConsoleWarn  = console.warn;
+        searchStr       = 'documentsLibrary';
 
     beforeEach(function() {
+        mockConfig = createMockConfigAndManifestForApplyAccessRules(true, 'http://www.bing.com/*');
+        addCapabilityDeclarationToMockManifest(mockConfig.manifest, 'documentsLibrary');
+
+        spyOn(AppxManifest, 'get').andReturn(mockConfig.manifest);
+
         stringFound = false;
-        spyOn(console, 'warn').andCallFake(function(msg) {
-            if (msg === searchStr)
+        events.on('warn', function (msg) {
+            if (msg.indexOf(searchStr) >= 0)
                 stringFound = true;
         });
     });
-    afterEach(function() {
-        console.warn = oldConsoleWarn;
-    });
 
-    
     it('asserts that the documentsLibrary capability is restricted', function() {
         // act
-        checkForRestrictedCaps(mockConfig.config, mockConfig.manifest);
+        updateManifestFile(mockConfig.config, '/manifest/path');
 
         // assert
         expect(stringFound).toBe(true);
@@ -287,143 +217,158 @@ function createMockConfigAndManifestForApplyAccessRules(isWin10) {
         rules.push(arguments[i]);
     }
 
-    var config = {
-        version: function() { return '1.0.0.0'; },
-        name: function() { return 'HelloCordova'; },
-        packageName: function() { return 'org.apache.cordova.HelloCordova'; },
-        author: function() { return 'Apache'; },
-        startPage: function() { return 'index.html'; },
-        getPreference: function(preferenceName) {
-            if (preferenceName === 'WindowsDefaultUriPrefix') {
-                return isWin10 ? 'ms-appx-web://' : 'ms-appx://';
-            }
-            else {
-                throw new RangeError('Unexpected call to config.getPreference in unit test.');
-            }
-        }, 
-        getAccessRules: function() {
-            if (isWin10) {
-                return [];
-            }
+    var TEST_XML = '<?xml version="1.0" encoding="UTF-8"?>\n' +
+    '<widget xmlns     = "http://www.w3.org/ns/widgets"\n' +
+    '        xmlns:cdv = "http://cordova.apache.org/ns/1.0"\n' +
+    '        id        = "org.apache.cordova.HelloCordova"\n' +
+    '        version   = "1.0.0.0">\n' +
+    '    <name>HelloCordova</name>\n' +
+    '    <author href="http://cordova.io" email="dev@cordova.apache.org">\n' +
+    '        Apache\n' +
+    '    </author>\n' +
+    '    <content src="index.html" />\n' +
+    '</widget>\n';
+
+    var origParseElementtreeSync = xml.parseElementtreeSync;
+    spyOn(xml, 'parseElementtreeSync').andCallFake(function(path) {
+        if (path ==='config.xml') return new et.ElementTree(et.XML(TEST_XML));
+        return origParseElementtreeSync(path);
+    });
 
-            return rules;
-        },
-        getNavigationWhitelistRules: function() {
-            if (isWin10) {
-                return rules;
-            }
+    var config = new ConfigParser('config.xml');
+
+    var origGetPreference = config.getPreference;
+    spyOn(config, 'getPreference').andCallFake(function (prefName) {
+        if (prefName === 'WindowsDefaultUriPrefix') {
+            return isWin10 ? 'ms-appx-web://' : 'ms-appx://';
+        }
 
+        return origGetPreference.call(config, prefName);
+    });
+
+    config.getAccesses = function() {
+        if (isWin10) {
             return [];
         }
+
+        return rules.map(function (rule) {
+            return { 'origin': rule };
+        });
+    };
+
+    config.getAllowNavigations = function() {
+        if (isWin10) {
+            return rules.map(function (rule) {
+                return { 'href': rule };
+            });
+        }
+
+        return [];
     };
 
     var filePath = isWin10 ? Win10ManifestPath : Win81ManifestPath;
-    var fileContents = fs.readFileSync(filePath, 'utf-8');
-    var manifest = new et.ElementTree(et.XML(fileContents.trim()));
+    var manifest = AppxManifest.get(filePath);
+    spyOn(fs, 'writeFileSync');
 
     return { config: config, manifest: manifest };
 }
 
-describe('A Windows 8.1 project should not have WindowsRuntimeAccess attributes in access rules.', function() {
+describe('Access rules management', function () {
+    // body...
+    it('A Windows 8.1 project should not have WindowsRuntimeAccess attributes in access rules.', function() {
 
-    var mockConfig = createMockConfigAndManifestForApplyAccessRules(false, 'https://www.contoso.com');
+        var mockConfig = createMockConfigAndManifestForApplyAccessRules(false, 'https://www.contoso.com');
 
-    applyAccessRules(mockConfig.config, mockConfig.manifest, false);
+        applyAccessRules(mockConfig.config, mockConfig.manifest);
 
-    var app         = mockConfig.manifest.find('.//Application'),
-        accessRules = app.find('.//ApplicationContentUriRules');
+        var app         = mockConfig.manifest.doc.find('.//Application'),
+            accessRules = app.find('.//ApplicationContentUriRules');
 
-    expect(accessRules).toBeDefined();
-    expect(accessRules.len()).toBe(1);
+        expect(accessRules).toBeDefined();
+        expect(accessRules.len()).toBe(1);
 
-    var rule = accessRules.getItem(0);
-    expect(rule).toBeDefined();
-    expect(rule.attrib.WindowsRuntimeAccess).toBeUndefined();
+        var rule = accessRules.getItem(0);
+        expect(rule).toBeDefined();
+        expect(rule.attrib.WindowsRuntimeAccess).toBeUndefined();
 
-});
+    });
 
-describe('A Windows 10 project should have WindowsRuntimeAccess attributes in access rules.', function() {
+    it('A Windows 10 project should have WindowsRuntimeAccess attributes in access rules.', function() {
 
-    var mockConfig = createMockConfigAndManifestForApplyAccessRules(true, 'https://www.contoso.com');
+        var mockConfig = createMockConfigAndManifestForApplyAccessRules(true, 'https://www.contoso.com');
 
-    applyAccessRules(mockConfig.config, mockConfig.manifest, true);
+        applyNavigationWhitelist(mockConfig.config, mockConfig.manifest, true);
 
-    var app         = mockConfig.manifest.find('.//Application'),
-        accessRules = app.find('.//uap:ApplicationContentUriRules');
+        var app         = mockConfig.manifest.doc.find('.//Application'),
+            accessRules = app.find('.//uap:ApplicationContentUriRules');
 
-    expect(accessRules).toBeDefined();
-    expect(accessRules.len()).toBe(2);
+        expect(accessRules).toBeDefined();
+        expect(accessRules.len()).toBe(2);
 
-    var rule = accessRules.getItem(0);
-    expect(rule).toBeDefined();
-    expect(rule.attrib.WindowsRuntimeAccess).toBeDefined();
-    expect(rule.attrib.WindowsRuntimeAccess).toBe('all');
+        var rule = accessRules.getItem(0);
+        expect(rule).toBeDefined();
+        expect(rule.attrib.WindowsRuntimeAccess).toBeDefined();
+        expect(rule.attrib.WindowsRuntimeAccess).toBe('all');
 
-});
+    });
 
-describe('A Windows 8.1 project should reject http:// URI scheme rules.', function() {
-    
-    var stringIndex     = -1,
-        searchStr       = 'Access rules must begin with "https://", the following rule will be ignored: ',
-        oldConsoleWarn  = console.warn;
-    beforeEach(function() {
-        spyOn(console, 'warn').andCallFake(function(msg) {
-            stringIndex = msg.indexOf(searchStr);
+    describe('A Windows 8.1 project should reject http:// URI scheme rules.', function() {
+
+        var stringIndex     = -1,
+            searchStr       = 'Access rules must begin with "https://", the following rule will be ignored: ';
+
+        beforeEach(function() {
+            require('cordova-common').events.on('warn', function (evt) {
+                stringIndex = evt.indexOf(searchStr);
+            });
         });
-    });
-    afterEach(function() {
-        console.warn = oldConsoleWarn;
-    });
-    
-    it('applies access rules and verifies at least one was rejected', function() {
-        var mockConfig = createMockConfigAndManifestForApplyAccessRules(false, 'http://www.contoso.com');
-        applyAccessRules(mockConfig.config, mockConfig.manifest, false);
 
-        expect(stringIndex).toBe(0);
+        it('applies access rules and verifies at least one was rejected', function() {
+            var mockConfig = createMockConfigAndManifestForApplyAccessRules(false, 'http://www.contoso.com');
+            applyAccessRules(mockConfig.config, mockConfig.manifest, false);
+
+            expect(stringIndex).toBe(0);
+        });
     });
-});
 
-describe('A Windows 10 project should accept http:// URI access rules.', function() {
+    describe('A Windows 10 project should accept http:// URI access rules.', function() {
 
-    var stringIndex     = -1,
-        searchStr       = 'The following navigation rule had an invalid URI scheme and is ignored:',
-        oldConsoleWarn  = console.warn;
-    beforeEach(function() {
-        spyOn(console, 'warn').andCallFake(function(msg) {
-            stringIndex = msg.indexOf(searchStr);
+        var stringIndex     = -1,
+            searchStr       = 'The following navigation rule had an invalid URI scheme and is ignored:';
+        beforeEach(function() {
+            require('cordova-common').events.on('warn', function (evt) {
+                stringIndex = evt.indexOf(searchStr);
+            });
         });
-    });
-    afterEach(function() {
-        console.warn = oldConsoleWarn;
-    });
 
-    it('applies access rules and verifies they were accepted', function() {
-        var mockConfig = createMockConfigAndManifestForApplyAccessRules(true, 'http://www.contoso.com');
-        applyAccessRules(mockConfig.config, mockConfig.manifest, true);
+        it('applies access rules and verifies they were accepted', function() {
+            var mockConfig = createMockConfigAndManifestForApplyAccessRules(true, 'http://www.contoso.com');
+            applyAccessRules(mockConfig.config, mockConfig.manifest, true);
 
-        expect(stringIndex).toBe(-1);
+            expect(stringIndex).toBe(-1);
+        });
     });
-
 });
 
 describe('A Windows 10 project should apply the uap: namespace prefix to certain capabilities.', function() {
-    
-    var element = null;
+
+    var manifest;
 
     beforeEach(function() {
-        element = new et.Element('Capabilities');
+        manifest = createMockConfigAndManifestForApplyAccessRules(true, 'https://www.contoso.com').manifest;
+        var element = manifest.doc.find('.//Capabilities');
+        element.clear();
         element.append(new et.Element('Capability', { Name: 'internetClient' }));
         element.append(new et.Element('Capability', { Name: 'documentsLibrary' }));
         element.append(new et.Element('DeviceCapability', { Name: 'location' }));
+        manifest.write();
     });
 
     it('Applies the uap: prefix to the documentsLibrary capability.', function() {
-        ensureUapPrefixedCapabilities(element);
-        var children = element.getchildren();
         var testResults = {};
         // map capabilities to tag
-        children.forEach(function(child) {
-            testResults[child.attrib.Name] = child.tag;
+        manifest.getCapabilities().forEach(function(child) {
+            testResults[child.name] = child.type;
         });
 
         expect(testResults.internetClient).toBe('Capability');

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/spec/unit/Version.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/Version.spec.js b/spec/unit/Version.spec.js
index 074afb2..4ee16a8 100644
--- a/spec/unit/Version.spec.js
+++ b/spec/unit/Version.spec.js
@@ -19,29 +19,30 @@
 
 var Version = require('../../template/cordova/lib/Version.js');
 
-describe('Version constructors behave correctly.', function() {
-
-    var v1 = new Version(1);
-    expect(v1.major).toBe(1);
-    expect(v1.minor).toBe(0);
-    expect(v1.build).toBe(0);
-    expect(v1.qfe).toBe(0);
-    var v2 = new Version(1, 2);
-    expect(v2.major).toBe(1);
-    expect(v2.minor).toBe(2);
-    expect(v2.build).toBe(0);
-    expect(v2.qfe).toBe(0);
-    var v3 = new Version(1, 2, 4);
-    expect(v3.major).toBe(1);
-    expect(v3.minor).toBe(2);
-    expect(v3.build).toBe(4);
-    expect(v3.qfe).toBe(0);
-    var v4 = new Version(1, 2, 4, 7);
-    expect(v4.major).toBe(1);
-    expect(v4.minor).toBe(2);
-    expect(v4.build).toBe(4);
-    expect(v4.qfe).toBe(7);
-
+describe('Version constructor', function () {
+
+    it('should behave correctly', function () {
+        var v1 = new Version(1);
+        expect(v1.major).toBe(1);
+        expect(v1.minor).toBe(0);
+        expect(v1.build).toBe(0);
+        expect(v1.qfe).toBe(0);
+        var v2 = new Version(1, 2);
+        expect(v2.major).toBe(1);
+        expect(v2.minor).toBe(2);
+        expect(v2.build).toBe(0);
+        expect(v2.qfe).toBe(0);
+        var v3 = new Version(1, 2, 4);
+        expect(v3.major).toBe(1);
+        expect(v3.minor).toBe(2);
+        expect(v3.build).toBe(4);
+        expect(v3.qfe).toBe(0);
+        var v4 = new Version(1, 2, 4, 7);
+        expect(v4.major).toBe(1);
+        expect(v4.minor).toBe(2);
+        expect(v4.build).toBe(4);
+        expect(v4.qfe).toBe(7);
+    });
 });
 
 describe('Version parse functions work as expected.', function() {

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/spec/unit/WindowsConfigParser.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/WindowsConfigParser.spec.js b/spec/unit/WindowsConfigParser.spec.js
new file mode 100644
index 0000000..a4b1bd3
--- /dev/null
+++ b/spec/unit/WindowsConfigParser.spec.js
@@ -0,0 +1,104 @@
+/**
+    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.
+*/
+
+var rewire = require('rewire');
+var et = require('elementtree');
+var xml = require('cordova-common').xmlHelpers;
+var ConfigParser = require('../../template/cordova/lib/ConfigParser');
+var ConfigParserOrig = require('cordova-common').ConfigParser;
+
+var TEST_XML = '<?xml version="1.0" encoding="UTF-8"?><widget/>';
+
+describe('Windows ConfigParser', function () {
+    it('should extend ConfigParser from cordova-common', function () {
+        expect(ConfigParser.prototype instanceof ConfigParserOrig).toBe(true);
+    });
+});
+
+/***
+ * Unit tests for validating that min/max versions are correctly obtained
+ * (for the function getAllMinMaxUAPVersions) from prepare.js.
+ **/
+
+describe('getAllMinMaxUAPVersions method', function () {
+
+    var mockConfig;
+    beforeEach(function () {
+        spyOn(xml, 'parseElementtreeSync').andReturn(new et.ElementTree(et.XML(TEST_XML)));
+
+        mockConfig = new ConfigParser('/some/file');
+    });
+
+    it('should correctly transform all versions as a baseline.', function() {
+        spyOn(mockConfig, 'getMatchingPreferences').andReturn([
+            { name: 'Windows.Universal-MinVersion', value: '10.0.9910.0' },
+            { name: 'Windows.Universal-MaxVersionTested', value: '10.0.9917.0' },
+            { name: 'Windows.Desktop-MinVersion', value: '10.0.9910.0' },
+            { name: 'Microsoft.Xbox-MaxVersionTested', value: '10.0.9917.0' }
+        ]);
+
+        var versionSet = mockConfig.getAllMinMaxUAPVersions();
+        var ver9910 = '10.0.9910.0';
+        var ver9917 = '10.0.9917.0';
+
+        expect(versionSet.length).toBe(3);
+
+        expect(versionSet[0].Name).toBe('Windows.Universal');
+        expect(versionSet[0].MinVersion).toBe(ver9910);
+        expect(versionSet[0].MaxVersionTested).toBe(ver9917);
+
+        expect(versionSet[1].Name).toBe('Windows.Desktop');
+        expect(versionSet[1].MinVersion).toBe(ver9910);
+        expect(versionSet[1].MaxVersionTested).toBe(ver9910);
+
+        expect(versionSet[2].Name).toBe('Microsoft.Xbox');
+        expect(versionSet[2].MinVersion).toBe(ver9917);
+        expect(versionSet[2].MaxVersionTested).toBe(ver9917);
+    });
+
+    it('should produce versions correctly even when the config file has no settings.', function() {
+        spyOn(mockConfig, 'getMatchingPreferences').andReturn([]);
+
+        var versionSet = mockConfig.getAllMinMaxUAPVersions();
+        var verBaseline = rewire('../../template/cordova/lib/ConfigParser')
+            .__get__('BASE_UAP_VERSION').toString();
+
+        expect(versionSet.length).toBe(1);
+        expect(versionSet[0].Name).toBe('Windows.Universal');
+        expect(versionSet[0].MinVersion).toBe(verBaseline);
+        expect(versionSet[0].MaxVersionTested).toBe(verBaseline);
+
+    });
+
+    it('should fail with a RangeError if version specified incorrectly', function() {
+        spyOn(mockConfig, 'getMatchingPreferences')
+        .andReturn([
+            { name: 'Windows.Universal-MinVersion', value: '10.0.9910.f' },
+            { name: 'Windows.Universal-MaxVersionTested', value: '10.0.9917.0' },
+        ]);
+
+        try {
+            mockConfig.getAllMinMaxUAPVersions();
+            expect(false).toBe(true);
+        }
+        catch (ex) {
+            expect(ex.constructor).toBe(RangeError);
+        }
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/spec/unit/build.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/build.spec.js b/spec/unit/build.spec.js
index e87ca3e..5638d63 100644
--- a/spec/unit/build.spec.js
+++ b/spec/unit/build.spec.js
@@ -157,7 +157,7 @@ describe('run method', function() {
         createFindAllAvailableVersionsMock([{version: '14.0', buildProject: buildSpy, path: testPath }]);
         build.__set__('prepare.applyPlatformConfig', function() {} );
 
-        build.run([ 'node', buildPath, '--release' ])
+        build.run({ release: true })
         .finally(function() {
             expect(buildSpy).toHaveBeenCalled();
             done();
@@ -189,7 +189,7 @@ describe('run method', function() {
         createFindAllAvailableVersionsMock([{version: '14.0', buildProject: buildSpy, path: testPath }]);
         build.__set__('prepare.applyPlatformConfig', function() {} );
 
-        build.run([ 'node', buildPath, '--archs=arm' ])
+        build.run({argv: ['--archs=arm'] })
         .finally(function() {
             expect(buildSpy).toHaveBeenCalled();
             done();
@@ -230,7 +230,7 @@ describe('run method', function() {
              }]);
         build.__set__('prepare.applyPlatformConfig', function() {} );
 
-        build.run([ 'node', buildPath, '--archs=arm x86 x64 anycpu', '--phone' ])
+        build.run({ argv: ['--archs=arm x86 x64 anycpu', '--phone'] })
         .finally(function() {
             expect(armBuild).toHaveBeenCalled();
             expect(x86Build).toHaveBeenCalled();
@@ -248,7 +248,7 @@ describe('run method', function() {
         build.__set__('prepare.applyPlatformConfig', function() {} );
         createConfigParserMock('8.0');
 
-        build.run([ 'node', buildPath, '--win' ])
+        build.run({argv: ['--win']})
         .finally(function() {
             expect(buildSpy).toHaveBeenCalled();
             done();
@@ -263,7 +263,7 @@ describe('run method', function() {
         build.__set__('prepare.applyPlatformConfig', function() {} );
         createConfigParserMock('8.1');
 
-        build.run([ 'node', buildPath, '--win' ])
+        build.run({argv: ['--win']})
         .finally(function() {
             expect(buildSpy).toHaveBeenCalled();
             done();
@@ -279,7 +279,7 @@ describe('run method', function() {
         build.__set__('prepare.applyPlatformConfig', function() {} );
         createConfigParserMock('unsupported value here');
 
-        build.run([ 'node', buildPath, '--win' ])
+        build.run({argv: ['--win']})
         .fail(function(error) {
             errorSpy();
             expect(error).toBeDefined();
@@ -299,7 +299,7 @@ describe('run method', function() {
         build.__set__('prepare.applyPlatformConfig', function() {} );
         createConfigParserMock(null, '8.1');
 
-        build.run([ 'node', buildPath, '--phone' ])
+        build.run({argv: ['--phone']})
         .finally(function() {
             expect(buildSpy).toHaveBeenCalled();
             done();
@@ -315,7 +315,7 @@ describe('run method', function() {
         build.__set__('prepare.applyPlatformConfig', function() {} );
         createConfigParserMock(null, 'unsupported value here');
 
-        build.run([ 'node', buildPath, '--phone' ])
+        build.run({argv: ['--phone']})
         .fail(function(error) {
             errorSpy();
             expect(error).toBeDefined();
@@ -339,7 +339,7 @@ describe('run method', function() {
         // provision config to target Windows 8.1
         createConfigParserMock('8.1', '8.1');
         // explicitly specify Windows 10 as target
-        build.run([ 'node', buildPath, '--appx=uap' ])
+        build.run({argv: ['--appx=uap']})
         .finally(function() {
             expect(buildSpy).toHaveBeenCalled();
             done();

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/spec/unit/deployment.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/deployment.spec.js b/spec/unit/deployment.spec.js
index 46393bb..97365fb 100644
--- a/spec/unit/deployment.spec.js
+++ b/spec/unit/deployment.spec.js
@@ -19,10 +19,8 @@
 
 var rewire     = require('rewire'),
     deployment = rewire('../../template/cordova/lib/deployment'),
-    run        = deployment.__get__('run'),
     Q          = require('q'),
     path       = require('path'),
-
     AppDeployCmdTool = deployment.__get__('AppDeployCmdTool'),
     WinAppDeployCmdTool = deployment.__get__('WinAppDeployCmdTool');
 
@@ -44,12 +42,11 @@ describe('The correct version of the app deployment tool is obtained.', function
         expect(tool instanceof WinAppDeployCmdTool).toBe(true);
 
     });
-
 });
 
 describe('Windows 10 deployment interacts with the file system as expected.', function() {
 
-    function runMock(cmd, args, cwd) {
+    function fakeSpawn(cmd, args, cwd) {
         expect(cmd).toBe(path.join('c:/Program Files (x86)/Windows Kits/10/bin/x86/WinAppDeployCmd.exe'));
         switch (args[0]) {
             case 'devices':
@@ -70,13 +67,16 @@ describe('Windows 10 deployment interacts with the file system as expected.', fu
         }
     }
 
+    var mockedSpawn = deployment.__get__('spawn');
     var mockedProgramFiles = process.env['ProgramFiles(x86)'];
+
     beforeEach(function() {
-        deployment.__set__('run', runMock);    
+        deployment.__set__('spawn', fakeSpawn);
         process.env['ProgramFiles(x86)'] = path.join('c:/Program Files (x86)');
     });
+
     afterEach(function() {
-        deployment.__set__('run', run);
+        deployment.__set__('spawn', mockedSpawn);
         if (mockedProgramFiles) {
             process.env['ProgramFiles(x86)'] = mockedProgramFiles;
         } else {
@@ -93,7 +93,7 @@ describe('Windows 10 deployment interacts with the file system as expected.', fu
             expect(deviceList[0].name).toBe('Lumia 1520 (RM-940)');
             expect(deviceList[0].index).toBe(0);
             expect(deviceList[0].type).toBe('device');
-            
+
             done = true;
 
         });
@@ -107,7 +107,7 @@ describe('Windows 10 deployment interacts with the file system as expected.', fu
         deploymentTool.enumerateDevices().then(function(deviceList) {
             deploymentTool.installAppPackage(TEST_APP_PACKAGE_NAME, deviceList[0], /*shouldLaunch*/ false, /*shouldUpdate*/ false).then(function() {
 
-                // expect() calls are in the runMock function
+                // expect() calls are in the fakeSpawn function
                 done = true;
 
             });
@@ -122,7 +122,7 @@ describe('Windows 10 deployment interacts with the file system as expected.', fu
         deploymentTool.enumerateDevices().then(function(deviceList) {
             deploymentTool.installAppPackage(TEST_APP_PACKAGE_NAME, deviceList[0], /*shouldLaunch*/ false, /*shouldUpdate*/ true).then(function() {
 
-                // expect() calls are in the runMock function
+                // expect() calls are in the fakeSpawn function
                 done = true;
 
             });
@@ -137,7 +137,7 @@ describe('Windows 10 deployment interacts with the file system as expected.', fu
         deploymentTool.enumerateDevices().then(function(deviceList) {
             deploymentTool.uninstallAppPackage(TEST_APP_PACKAGE_ID, deviceList[2]).then(function() {
 
-                // expect() calls are in the runMock function
+                // expect() calls are in the fakeSpawn function
                 done = true;
 
             });
@@ -145,12 +145,11 @@ describe('Windows 10 deployment interacts with the file system as expected.', fu
 
         waitsFor(function() { return done; });
     });
-
 });
 
 describe('Windows 8.1 deployment interacts with the file system as expected.', function() {
 
-    function runMock(cmd, args, cwd) {
+    function fakeSpawn(cmd, args, cwd) {
         expect(cmd).toBe(path.join('c:/Program Files (x86)/Microsoft SDKs/Windows Phone/v8.1/Tools/AppDeploy/AppDeployCmd.exe'));
         switch (args[0]) {
             case '/EnumerateDevices':
@@ -176,13 +175,16 @@ describe('Windows 8.1 deployment interacts with the file system as expected.', f
         }
     }
 
+    var mockedSpawn = deployment.__get__('spawn');
     var mockedProgramFiles = process.env['ProgramFiles(x86)'];
+
     beforeEach(function() {
-        deployment.__set__('run', runMock);    
+        deployment.__set__('spawn', fakeSpawn);
         process.env['ProgramFiles(x86)'] = path.join('c:/Program Files (x86)');
     });
+
     afterEach(function() {
-        deployment.__set__('run', run);
+        deployment.__set__('spawn', mockedSpawn);
         if (mockedProgramFiles) {
             process.env['ProgramFiles(x86)'] = mockedProgramFiles;
         } else {
@@ -216,7 +218,7 @@ describe('Windows 8.1 deployment interacts with the file system as expected.', f
         deploymentTool.enumerateDevices().then(function(deviceList) {
             deploymentTool.installAppPackage(TEST_APP_PACKAGE_NAME, deviceList[0], /*shouldLaunch*/ false, /*shouldUpdate*/ false).then(function() {
 
-                // expect() calls are in the runMock function
+                // expect() calls are in the fakeSpawn function
                 done = true;
 
             });
@@ -230,7 +232,7 @@ describe('Windows 8.1 deployment interacts with the file system as expected.', f
         deploymentTool.enumerateDevices().then(function(deviceList) {
             deploymentTool.installAppPackage(TEST_APP_PACKAGE_NAME, deviceList[0], /*shouldLaunch*/ false, /*shouldUpdate*/ true).then(function() {
 
-                // expect() calls are in the runMock function
+                // expect() calls are in the fakeSpawn function
                 done = true;
 
             });
@@ -244,7 +246,7 @@ describe('Windows 8.1 deployment interacts with the file system as expected.', f
         deploymentTool.enumerateDevices().then(function(deviceList) {
             deploymentTool.installAppPackage(TEST_APP_PACKAGE_NAME, deviceList[0], /*shouldLaunch*/ true, /*shouldUpdate*/ false).then(function() {
 
-                // expect() calls are in the runMock function
+                // expect() calls are in the fakeSpawn function
                 done = true;
 
             });
@@ -258,7 +260,7 @@ describe('Windows 8.1 deployment interacts with the file system as expected.', f
         deploymentTool.enumerateDevices().then(function(deviceList) {
             deploymentTool.installAppPackage(TEST_APP_PACKAGE_NAME, deviceList[0], /*shouldLaunch*/ true, /*shouldUpdate*/ true).then(function() {
 
-                // expect() calls are in the runMock function
+                // expect() calls are in the fakeSpawn function
                 done = true;
 
             });
@@ -272,12 +274,11 @@ describe('Windows 8.1 deployment interacts with the file system as expected.', f
         deploymentTool.enumerateDevices().then(function(deviceList) {
             deploymentTool.uninstallAppPackage(TEST_APP_PACKAGE_ID, deviceList[5]).then(function() {
 
-                // expect() calls are in the runMock function
+                // expect() calls are in the fakeSpawn function
                 done = true;
 
             });
         });
         waitsFor(function() { return done; });
     });
-
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/spec/unit/run.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/run.spec.js b/spec/unit/run.spec.js
index 3171dfb..179c4a7 100644
--- a/spec/unit/run.spec.js
+++ b/spec/unit/run.spec.js
@@ -89,7 +89,7 @@ describe('run method', function() {
             return Q.reject(); // rejecting to break run chain
         });
 
-        run.run([ 'node', buildPath, '--release', '--debug' ])
+        run.run({ release: true, debug: true })
         .finally(function() {
             expect(buildRun).not.toHaveBeenCalled();
             done();
@@ -105,7 +105,7 @@ describe('run method', function() {
             return Q.reject(); // rejecting to break run chain
         });
 
-        run.run([ 'node', buildPath, '--device', '--emulator' ])
+        run.run({ device: true, emulator: true })
         .finally(function() {
             expect(buildRun).not.toHaveBeenCalled();
             done();
@@ -121,7 +121,7 @@ describe('run method', function() {
             return Q.reject(); // rejecting to break run chain
         });
 
-        run.run([ 'node', buildPath, '--device', '--target=sometargethere' ])
+        run.run({ device: true, target: 'sometargethere' })
         .finally(function() {
             expect(buildRun).not.toHaveBeenCalled();
             done();
@@ -237,7 +237,7 @@ describe('run method', function() {
             return Q();
         });
 
-        run.run([ 'node', buildPath, '--nobuild' ])
+        run.run({ nobuild: true })
         .finally(function() {
             expect(deployToDesktop).toHaveBeenCalled();
             expect(build).not.toHaveBeenCalled();

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/template/cordova/Api.js
----------------------------------------------------------------------
diff --git a/template/cordova/Api.js b/template/cordova/Api.js
new file mode 100644
index 0000000..5803817
--- /dev/null
+++ b/template/cordova/Api.js
@@ -0,0 +1,470 @@
+/**
+    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.
+*/
+
+var Q = require('q');
+var fs = require('fs');
+var path = require('path');
+var shell = require('shelljs');
+
+var JsprojManager = require('./lib/JsprojManager');
+var PluginHandler = require('./lib/PluginHandler');
+var ConsoleLogger = require('./lib/ConsoleLogger');
+var ActionStack = require('cordova-common').ActionStack;
+var CordovaError = require('cordova-common').CordovaError;
+var PlatformJson = require('cordova-common').PlatformJson;
+var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
+var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
+
+var PLATFORM = 'windows';
+
+/**
+ * Class, that acts as abstraction over particular platform. Encapsulates the
+ *   platform's properties and methods.
+ *
+ * Platform that implements own PlatformApi instance _should implement all
+ *   prototype methods_ of this class to be fully compatible with cordova-lib.
+ *
+ * The PlatformApi instance also should define the following field:
+ *
+ * * platform: String that defines a platform name.
+ */
+function Api(platform, platformRootDir, events) {
+    this.platform = PLATFORM;
+    this.root = path.resolve(__dirname, '..');
+    this.events = events || ConsoleLogger.get();
+    // NOTE: trick to share one EventEmitter instance across all js code
+    require('cordova-common').events = this.events;
+
+    this._platformJson = PlatformJson.load(this.root, platform);
+    this._pluginInfoProvider = new PluginInfoProvider();
+    this._munger = new PlatformMunger(this.platform, this.root, this._platformJson, this._pluginInfoProvider);
+
+    var self = this;
+
+    this.locations = {
+        root: self.root,
+        www: path.join(self.root, 'www'),
+        platformWww: path.join(self.root, 'platform_www'),
+        configXml: path.join(self.root, 'config.xml'),
+        defaultConfigXml: path.join(self.root, 'cordova/defaults.xml'),
+        // NOTE: Due to platformApi spec we need to return relative paths here
+        cordovaJs: 'template/www/cordova.js',
+        cordovaJsSrc: 'cordova-js-src'
+    };
+}
+
+/**
+ * Installs platform to specified directory and creates a platform project.
+ *
+ * @param  {String}  destinationDir  A directory, where platform should be
+ *   created/installed.
+ * @param  {ConfigParser} [projectConfig] A ConfigParser instance, used to get
+ *   some application properties for new platform like application name, package
+ *   id, etc. If not defined, this means that platform is used as standalone
+ *   project and is not a part of cordova project, so platform will use some
+ *   default values.
+ * @param  {Object}   [options]  An options object. The most common options are:
+ * @param  {String}   [options.customTemplate]  A path to custom template, that
+ *   should override the default one from platform.
+ * @param  {Boolean}  [options.link=false]  Flag that indicates that platform's
+ *   sources will be linked to installed platform instead of copying.
+ *
+ * @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
+ *   instance or rejected with CordovaError.
+ */
+Api.createPlatform = function (destinationDir, projectConfig, options, events) {
+    return require('../../bin/lib/create')
+    .create(destinationDir, projectConfig, options, events || ConsoleLogger.get())
+    .then(function () {
+        var PlatformApi = require(path.resolve(destinationDir, 'cordova/Api'));
+        return new PlatformApi(PLATFORM, destinationDir, events);
+    });
+};
+
+/**
+ * Updates already installed platform.
+ *
+ * @param  {String}  destinationDir  A directory, where existing platform
+ *   installed, that should be updated.
+ * @param  {Object}  [options]  An options object. The most common options are:
+ * @param  {String}  [options.customTemplate]  A path to custom template, that
+ *   should override the default one from platform.
+ * @param  {Boolean}  [options.link=false]  Flag that indicates that platform's sources
+ *   will be linked to installed platform instead of copying.
+ *
+ * @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
+ *   instance or rejected with CordovaError.
+ */
+Api.updatePlatform = function (destinationDir, options, events) {
+    return require('../../bin/lib/update')
+    .update(destinationDir, options, events || ConsoleLogger.get())
+    .then(function () {
+        var PlatformApi = require(path.resolve(destinationDir, 'cordova/Api'));
+        return new PlatformApi(PLATFORM, destinationDir, events);
+    });
+};
+
+/**
+ * Gets a CordovaPlatform object, that represents the platform structure.
+ *
+ * @return  {CordovaPlatform}  A structure that contains the description of
+ *   platform's file structure and other properties of platform.
+ */
+Api.prototype.getPlatformInfo = function () {
+
+    var result = {};
+    result.locations = this.locations;
+    result.root = this.root;
+    result.name = this.platform;
+    result.version = require('./version');
+    result.projectConfig = this._config;
+
+    return result;
+};
+
+/**
+ * Updates installed platform with provided www assets and new app
+ *   configuration. This method is required for CLI workflow and will be called
+ *   each time before build, so the changes, made to app configuration and www
+ *   code, will be applied to platform.
+ *
+ * @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a
+ *   project structure and configuration, that should be applied to platform
+ *   (contains project's www location and ConfigParser instance for project's
+ *   config).
+ *
+ * @return  {Promise}  Return a promise either fulfilled, or rejected with
+ *   CordovaError instance.
+ */
+Api.prototype.prepare = function (cordovaProject) {
+    return require('./lib/prepare').prepare.call(this, cordovaProject);
+};
+
+/**
+ * Installs a new plugin into platform. This method only copies non-www files
+ *   (sources, libs, etc.) to platform. It also doesn't resolves the
+ *   dependencies of plugin. Both of handling of www files, such as assets and
+ *   js-files and resolving dependencies are the responsibility of caller.
+ *
+ * @param  {PluginInfo}  plugin  A PluginInfo instance that represents plugin
+ *   that will be installed.
+ * @param  {Object}  installOptions  An options object. Possible options below:
+ * @param  {Boolean}  installOptions.link: Flag that specifies that plugin
+ *   sources will be symlinked to app's directory instead of copying (if
+ *   possible).
+ * @param  {Object}  installOptions.variables  An object that represents
+ *   variables that will be used to install plugin. See more details on plugin
+ *   variables in documentation:
+ *   https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html
+ *
+ * @return  {Promise}  Return a promise either fulfilled, or rejected with
+ *   CordovaError instance.
+ */
+Api.prototype.addPlugin = function (plugin, installOptions) {
+
+    if (!plugin || plugin.constructor.name !== 'PluginInfo')
+        return Q.reject(new CordovaError('The parameter is incorrect. The first parameter ' +
+            'should be valid PluginInfo instance'));
+
+    installOptions = installOptions || {};
+    installOptions.variables = installOptions.variables || {};
+
+    var self = this;
+    var actions = new ActionStack();
+    var jsProject = JsprojManager.getProject(this.root);
+
+    // Add PACKAGE_NAME variable into vars
+    if (!installOptions.variables.PACKAGE_NAME) {
+        installOptions.variables.PACKAGE_NAME = jsProject.getPackageName();
+    }
+
+    // gather all files needs to be handled during install
+    plugin.getFilesAndFrameworks(this.platform)
+        .concat(plugin.getAssets(this.platform))
+        .concat(plugin.getJsModules(this.platform))
+    .forEach(function(item) {
+        actions.push(actions.createAction(
+            PluginHandler.getInstaller(item.itemType), [item, plugin, jsProject, installOptions],
+            PluginHandler.getUninstaller(item.itemType), [item, plugin, jsProject, installOptions]));
+    });
+
+    // run through the action stack
+    return actions.process(this.platform)
+    .then(function () {
+        jsProject.write();
+
+        self._munger
+            // Ignore passed `is_top_level` option since platform itself doesn't know
+            // anything about managing dependencies - it's responsibility of caller.
+            .add_plugin_changes(plugin, installOptions.variables, /*is_top_level=*/true, /*should_increment=*/true)
+            .save_all();
+
+        var targetDir = installOptions.usePlatformWww ?
+            self.getPlatformInfo().locations.platformWww :
+            self.getPlatformInfo().locations.www;
+
+        self._addModulesInfo(plugin, targetDir);
+    });
+};
+
+/**
+ * Removes an installed plugin from platform.
+ *
+ * Since method accepts PluginInfo instance as input parameter instead of plugin
+ *   id, caller shoud take care of managing/storing PluginInfo instances for
+ *   future uninstalls.
+ *
+ * @param  {PluginInfo}  plugin  A PluginInfo instance that represents plugin
+ *   that will be installed.
+ *
+ * @return  {Promise}  Return a promise either fulfilled, or rejected with
+ *   CordovaError instance.
+ */
+Api.prototype.removePlugin = function (plugin, uninstallOptions) {
+
+    var self = this;
+    var actions = new ActionStack();
+    var projectFile = JsprojManager.getProject(this.root);
+
+    // queue up plugin files
+    plugin.getFilesAndFrameworks(this.platform)
+        .concat(plugin.getAssets(this.platform))
+        .concat(plugin.getJsModules(this.platform))
+    .forEach(function(item) {
+        actions.push(actions.createAction(
+            PluginHandler.getUninstaller(item.itemType), [item, plugin, projectFile, uninstallOptions],
+            PluginHandler.getInstaller(item.itemType),   [item, plugin, projectFile, uninstallOptions]));
+    });
+
+    // run through the action stack
+    return actions.process(this.platform)
+    .then(function() {
+        projectFile.write();
+
+        self._munger
+            // Ignore passed `is_top_level` option since platform itself doesn't know
+            // anything about managing dependencies - it's responsibility of caller.
+            .remove_plugin_changes(plugin, /*is_top_level=*/true)
+            .save_all();
+
+        var targetDir = uninstallOptions.usePlatformWww ?
+            self.getPlatformInfo().locations.platformWww :
+            self.getPlatformInfo().locations.www;
+
+        self._removeModulesInfo(plugin, targetDir);
+        // Remove stale plugin directory
+        shell.rm('-rf', path.resolve(self.root, 'Plugins', plugin.id));
+    });
+};
+
+
+/**
+ * Builds an application package for current platform.
+ *
+ * @param  {Object}  buildOptions  A build options. This object's structure is
+ *   highly depends on platform's specific. The most common options are:
+ * @param  {Boolean}  buildOptions.debug  Indicates that packages should be
+ *   built with debug configuration. This is set to true by default unless the
+ *   'release' option is not specified.
+ * @param  {Boolean}  buildOptions.release  Indicates that packages should be
+ *   built with release configuration. If not set to true, debug configuration
+ *   will be used.
+ * @param   {Boolean}  buildOptions.device  Specifies that built app is intended
+ *   to run on device
+ * @param   {Boolean}  buildOptions.emulator: Specifies that built app is
+ *   intended to run on emulator
+ * @param   {String}  buildOptions.target  Specifies the device id that will be
+ *   used to run built application.
+ * @param   {Boolean}  buildOptions.nobuild  Indicates that this should be a
+ *   dry-run call, so no build artifacts will be produced.
+ * @param   {String[]}  buildOptions.archs  Specifies chip architectures which
+ *   app packages should be built for. List of valid architectures is depends on
+ *   platform.
+ * @param   {String}  buildOptions.buildConfig  The path to build configuration
+ *   file. The format of this file is depends on platform.
+ * @param   {String[]} buildOptions.argv Raw array of command-line arguments,
+ *   passed to `build` command. The purpose of this property is to pass a
+ *   platform-specific arguments, and eventually let platform define own
+ *   arguments processing logic.
+ *
+ * @return {Promise<Object[]>} A promise either fulfilled with an array of build
+ *   artifacts (application packages) if package was built successfully,
+ *   or rejected with CordovaError. The resultant build artifact objects is not
+ *   strictly typed and may conatin arbitrary set of fields as in sample below.
+ *
+ *     {
+ *         architecture: 'x86',
+ *         buildType: 'debug',
+ *         path: '/path/to/build',
+ *         type: 'app'
+ *     }
+ *
+ * The return value in most cases will contain only one item but in some cases
+ *   there could be multiple items in output array, e.g. when multiple
+ *   arhcitectures is specified.
+ */
+Api.prototype.build = function(buildOptions) {
+    // TODO: Should we run check_reqs first? Android does this, but Windows appears doesn't.
+    return require('./lib/build').run.call(this, buildOptions)
+    .then(function (result) {
+        // Wrap result into array according to PlatformApi spec
+        return [result];
+    });
+};
+
+/**
+ * Builds an application package for current platform and runs it on
+ *   specified/default device. If no 'device'/'emulator'/'target' options are
+ *   specified, then tries to run app on default device if connected, otherwise
+ *   runs the app on emulator.
+ *
+ * @param   {Object}  runOptions  An options object. The structure is the same
+ *   as for build options.
+ *
+ * @return {Promise} A promise either fulfilled if package was built and ran
+ *   successfully, or rejected with CordovaError.
+ */
+Api.prototype.run = function(runOptions) {
+    // TODO: Should we run check_reqs first? Android does this, but Windows appears doesn't.
+    return require('./lib/run').run.call(this, runOptions);
+};
+
+/**
+ * Cleans out the build artifacts from platform's directory.
+ *
+ * @return  {Promise}  Return a promise either fulfilled, or rejected with
+ *   CordovaError.
+ */
+Api.prototype.clean = function(cleanOpts) {
+    return require('./lib/build').clean.call(this, cleanOpts);
+};
+
+/**
+ * Performs a requirements check for current platform. Each platform defines its
+ *   own set of requirements, which should be resolved before platform can be
+ *   built successfully.
+ *
+ * @return  {Promise<Requirement[]>}  Promise, resolved with set of Requirement
+ *   objects for current platform.
+ */
+Api.prototype.requirements = function() {
+    return require('./lib/check_reqs').check_all();
+};
+
+module.exports = Api;
+
+/**
+ * Removes the specified modules from list of installed modules and updates
+ *   platform_json and cordova_plugins.js on disk.
+ *
+ * @param   {PluginInfo}  plugin  PluginInfo instance for plugin, which modules
+ *   needs to be added.
+ * @param   {String}  targetDir  The directory, where updated cordova_plugins.js
+ *   should be written to.
+ */
+Api.prototype._addModulesInfo = function(plugin, targetDir) {
+    var installedModules = this._platformJson.root.modules || [];
+
+    var installedPaths = installedModules.map(function (installedModule) {
+        return installedModule.file;
+    });
+
+    var modulesToInstall = plugin.getJsModules(this.platform)
+    .filter(function (moduleToInstall) {
+        return installedPaths.indexOf(moduleToInstall.file) === -1;
+    }).map(function (moduleToInstall) {
+        var moduleName = plugin.id + '.' + ( moduleToInstall.name || moduleToInstall.src.match(/([^\/]+)\.js/)[1] );
+        var obj = {
+            file: ['plugins', plugin.id, moduleToInstall.src].join('/'),
+            id: moduleName,
+            pluginId: plugin.id
+        };
+        if (moduleToInstall.clobbers.length > 0) {
+            obj.clobbers = moduleToInstall.clobbers.map(function(o) { return o.target; });
+        }
+        if (moduleToInstall.merges.length > 0) {
+            obj.merges = moduleToInstall.merges.map(function(o) { return o.target; });
+        }
+        if (moduleToInstall.runs) {
+            obj.runs = true;
+        }
+
+        return obj;
+    });
+
+    this._platformJson.root.modules = installedModules.concat(modulesToInstall);
+    this._writePluginModules(targetDir);
+    this._platformJson.save();
+};
+
+/**
+ * Removes the specified modules from list of installed modules and updates
+ *   platform_json and cordova_plugins.js on disk.
+ *
+ * @param   {PluginInfo}  plugin  PluginInfo instance for plugin, which modules
+ *   needs to be removed.
+ * @param   {String}  targetDir  The directory, where updated cordova_plugins.js
+ *   should be written to.
+ */
+Api.prototype._removeModulesInfo = function(plugin, targetDir) {
+    var installedModules = this._platformJson.root.modules || [];
+    var modulesToRemove = plugin.getJsModules(this.platform)
+    .map(function (jsModule) {
+        return  ['plugins', plugin.id, jsModule.src].join('/');
+    });
+
+    var updatedModules = installedModules
+    .filter(function (installedModule) {
+        return (modulesToRemove.indexOf(installedModule.file) === -1);
+    });
+
+    this._platformJson.root.modules = updatedModules;
+    this._writePluginModules(targetDir);
+    this._platformJson.save();
+};
+
+/**
+ * Fetches all installed modules, generates cordova_plugins contents and writes
+ *   it to file.
+ *
+ * @param   {String}  targetDir  Directory, where write cordova_plugins.js to.
+ *   Ususally it is either <platform>/www or <platform>/platform_www
+ *   directories.
+ */
+Api.prototype._writePluginModules = function (targetDir) {
+    var self = this;
+    // Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js
+    var final_contents = 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {\n';
+    final_contents += 'module.exports = ' + JSON.stringify(this._platformJson.root.modules, null, '    ') + ';\n';
+    final_contents += 'module.exports.metadata = \n';
+    final_contents += '// TOP OF METADATA\n';
+
+    var pluginMetadata = Object.keys(this._platformJson.root.installed_plugins)
+    .reduce(function (metadata, plugin) {
+        metadata[plugin] = self._platformJson.root.installed_plugins[plugin].version;
+        return metadata;
+    }, {});
+
+    final_contents += JSON.stringify(pluginMetadata, null, '    ') + '\n';
+    final_contents += '// BOTTOM OF METADATA\n';
+    final_contents += '});'; // Close cordova.define.
+
+    shell.mkdir('-p', targetDir);
+    fs.writeFileSync(path.join(targetDir, 'cordova_plugins.js'), final_contents, 'utf-8');
+};

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/template/cordova/build
----------------------------------------------------------------------
diff --git a/template/cordova/build b/template/cordova/build
index 8fbceae..181cd63 100644
--- a/template/cordova/build
+++ b/template/cordova/build
@@ -19,16 +19,62 @@
        under the License.
 */
 
-var build = require('./lib/build'),
-    args  = process.argv;
-
-// Handle help flag
-if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(args[2]) > -1) {
-    build.help();
-} else {
-    build.run(args).done(null, function(err) {
-        var errorMessage = (err && err.stack) ? err.stack : err;
-        console.error('ERROR: ' + errorMessage);
-        process.exit(2);
-    });
-}
\ No newline at end of file
+var Api = require('./Api');
+var nopt = require('nopt');
+var path = require('path');
+
+// Support basic help commands
+if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
+    console.log('');
+    console.log('Usage: build [--debug | --release] [--phone | --win] [--bundle]');
+    console.log('             [--archs="<list of architectures...>"');
+    console.log('             [--packageCertificateKeyFile="key path"]');
+    console.log('             [--packageThumbprint="thumbprint"] [--publisherId]');
+    console.log('             [--buildConfig="file path"]');
+    console.log('    --help                      : Displays this dialog.');
+    console.log('    --debug                     : Builds project in debug mode. (Default).');
+    console.log('    --release  (-r)             : Builds project in release mode.');
+    console.log('    --phone, --win              : Specifies, what type of project to build.');
+    console.log('    --bundle                    : Tells the compiler to create a .appxbundle.');
+    console.log('                                  Bundling is disabled when `anycpu` is built.');
+    console.log('    --archs                     : Builds project binaries for specific chip');
+    console.log('                                  architectures (`anycpu`, `arm`, `x86`, `x64`).');
+    console.log('                                  Separate multiple choices with spaces and if');
+    console.log('                                  passing multiple choices, enclose with " ".');
+    console.log('    --appx=<8.1-win|8.1-phone|uap>');
+    console.log('                                : Overrides windows-target-version to build');
+    console.log('                                  Windows 8.1, Windows Phone 8.1, or');
+    console.log('                                  Windows 10 Universal.');
+    console.log('    --packageCertificateKeyFile : Builds the project using provided certificate.');
+    console.log('    --packageThumbprint         : Thumbprint associated with the certificate.');
+    console.log('    --publisherId               : Sets publisher id field in manifest.');
+    console.log('    --buildConfig               : Sets build settings from configuration file.');
+    console.log('');
+    console.log('examples:');
+    console.log('    build ');
+    console.log('    build --debug');
+    console.log('    build --release');
+    console.log('    build --release --archs="arm x86" --bundle');
+    console.log('    build --appx=8.1-phone -r');
+    console.log('    build --packageCertificateKeyFile="CordovaApp_TemporaryKey.pfx"');
+    console.log('    build --publisherId="CN=FakeCorp, C=US"');
+    console.log('    build --buildConfig="build.json"');
+    console.log('');
+
+    process.exit(0);
+}
+
+// Do some basic argument parsing
+var buildOpts = nopt({
+    'silent' : Boolean,
+    'verbose' : Boolean,
+    'debug' : Boolean,
+    'release' : Boolean,
+    'nobuild': Boolean,
+    'buildConfig' : path
+}, { d : '--verbose', r: '--release' });
+
+// Make buildOptions compatible with PlatformApi build method spec
+buildOpts.argv = buildOpts.argv.original;
+
+new Api().build(buildOpts).done();

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/58047a3d/template/cordova/clean
----------------------------------------------------------------------
diff --git a/template/cordova/clean b/template/cordova/clean
index 71877fe..2964746 100644
--- a/template/cordova/clean
+++ b/template/cordova/clean
@@ -19,9 +19,13 @@
        under the License.
 */
 
-var clean = require('./lib/clean');
+var Api = require('./Api');
 
-clean.run(process.argv).done(null, function(err) {
-    console.error('ERROR: ' + err);
-    process.exit(2);
-});
\ No newline at end of file
+// Support basic help commands
+if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
+    console.log('Usage: \n    clean\n');
+    console.log('Cleans the project directory.');
+    process.exit(0);
+}
+
+new Api().clean().done();


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