You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by st...@apache.org on 2017/02/04 01:04:48 UTC

[01/12] cordova-lib git commit: CB-11960: Added support to package.json for platform/plugin add/rm

Repository: cordova-lib
Updated Branches:
  refs/heads/master 9f704a70b -> a9605411a


CB-11960: Added support to package.json for platform/plugin add/rm


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/197cba97
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/197cba97
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/197cba97

Branch: refs/heads/master
Commit: 197cba977e38ffbb8783d6d3f5bec93e7e2a37f7
Parents: 9f704a7
Author: audreyso <au...@adobe.com>
Authored: Fri Oct 7 14:19:06 2016 -0700
Committer: Audrey So <au...@apache.org>
Committed: Thu Jan 26 17:36:50 2017 -0800

----------------------------------------------------------------------
 .../fixtures/basePkgJson/config.xml             |  14 +
 .../fixtures/basePkgJson/merges/.svn            |   0
 .../fixtures/basePkgJson/package.json           |  11 +
 .../fixtures/basePkgJson/plugins/.svn           |   0
 .../fixtures/basePkgJson/www/css/index.css      | 115 ++++++
 .../fixtures/basePkgJson/www/img/logo.png       | Bin 0 -> 21814 bytes
 .../fixtures/basePkgJson/www/index.html         |  43 ++
 .../fixtures/basePkgJson/www/js/index.js        |  49 +++
 .../fixtures/basePkgJson/www/spec.html          |  68 ++++
 .../fixtures/platforms/android/package.json     |  49 +++
 cordova-lib/spec-cordova/package.json.spec.js   | 389 +++++++++++++++++++
 .../spec-cordova/pkgJson-restore.spec.js        | 254 ++++++++++++
 cordova-lib/spec-cordova/pkgJson.spec.js        | 348 +++++++++++++++++
 cordova-lib/spec-cordova/platform.spec.js       |  37 +-
 cordova-lib/src/cordova/platform.js             |  63 ++-
 cordova-lib/src/cordova/plugin.js               |  53 ++-
 16 files changed, 1483 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson/config.xml
new file mode 100644
index 0000000..9e7b9e0
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson/config.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/merges/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/merges/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson/merges/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson/package.json
new file mode 100644
index 0000000..eaf9d87
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson/package.json
@@ -0,0 +1,11 @@
+{
+  "name": "testbase",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "ISC"
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/www/img/logo.png
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/www/img/logo.png b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/img/logo.png
new file mode 100644
index 0000000..9519e7d
Binary files /dev/null and b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/img/logo.png differ

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/basePkgJson/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/fixtures/platforms/android/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/platforms/android/package.json b/cordova-lib/spec-cordova/fixtures/platforms/android/package.json
new file mode 100644
index 0000000..6b81b07
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/platforms/android/package.json
@@ -0,0 +1,49 @@
+{
+  "name": "cordova-android",
+  "version": "5.2.2",
+  "description": "cordova-android release",
+  "bin": {
+    "create": "bin/create"
+  },
+  "main": "bin/templates/cordova/Api.js",
+  "repository": {
+    "type": "git",
+    "url": "https://git-wip-us.apache.org/repos/asf/cordova-android.git"
+  },
+  "keywords": [
+    "android",
+    "cordova",
+    "apache"
+  ],
+  "scripts": {
+    "test": "npm run jshint && jasmine-node --color spec/unit",
+    "cover": "istanbul cover --root bin/templates/cordova --print detail node_modules/jasmine-node/bin/jasmine-node -- spec/unit",
+    "test-build": "jasmine-node --captureExceptions --color spec/e2e",
+    "jshint": "node node_modules/jshint/bin/jshint bin && node node_modules/jshint/bin/jshint spec"
+  },
+  "author": "Apache Software Foundation",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "cordova-common": "^1.4.0",
+    "elementtree": "^0.1.6",
+    "nopt": "^3.0.1",
+    "properties-parser": "^0.2.3",
+    "q": "^1.4.1",
+    "shelljs": "^0.5.3"
+  },
+  "bundledDependencies": [
+    "cordova-common",
+    "elementtree",
+    "nopt",
+    "properties-parser",
+    "q",
+    "shelljs"
+  ],
+  "devDependencies": {
+    "istanbul": "^0.4.2",
+    "jasmine-node": "^1.14.5",
+    "jshint": "^2.6.0",
+    "promise-matchers": "~0",
+    "rewire": "^2.1.3"
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/package.json.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/package.json.spec.js b/cordova-lib/spec-cordova/package.json.spec.js
new file mode 100644
index 0000000..1755834
--- /dev/null
+++ b/cordova-lib/spec-cordova/package.json.spec.js
@@ -0,0 +1,389 @@
+/**
+    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 helpers = require('./helpers'),
+    path = require('path'),
+    fs = require('fs'),
+    shell = require('shelljs'),
+    superspawn = require('cordova-common').superspawn,
+    Q = require('q'),
+    events = require('cordova-common').events,
+    cordova = require('../src/cordova/cordova'),
+    rewire = require('rewire'),
+    prepare = require('../src/cordova/prepare'),
+    platforms = require('../src/platforms/platforms'),
+    platform = rewire('../src/cordova/platform.js');
+
+var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
+var pluginsDir = path.join(__dirname, 'fixtures', 'plugins');
+
+function addPlugin(target, id, options) {
+    // Checks that there are no plugins yet.
+    return cordova.raw.plugin('list').then(function() {
+        expect(results).toMatch(/No plugins added/gi);
+    }).then(function() {
+        // Adds a fake plugin from fixtures.
+        return cordova.raw.plugin('add', target, options);
+    }).then(function() {
+        expect(path.join(project, 'plugins', id, 'plugin.xml')).toExist();
+    }).then(function() {
+        return cordova.raw.plugin('ls');
+    }).then(function() {
+        expect(results).toContain(id);
+    });
+}
+// Runs: remove, list.
+function removePlugin(id, options) {
+    return cordova.raw.plugin('rm', id)
+    .then(function() {
+        // The whole dir should be gone.
+        expect(path.join(project, 'plugins', id)).not.toExist();
+    }).then(function() {
+        return cordova.raw.plugin('ls');
+    }).then(function() {
+        expect(results).toMatch(/No plugins added/gi);
+    });
+}
+// Checks if plugin list is empty.
+function emptyPluginList() {
+    return cordova.raw.plugin('list').then(function() {
+        var installed = results.match(/Installed plugins:\n  (.*)/);
+        expect(installed).toBeDefined();
+        expect(installed[1].indexOf('fake-plugin')).toBe(-1);
+    });
+}
+// Checks if plugin list exists.
+function fullPluginList() {
+    return cordova.raw.plugin('list').then(function() {
+        var installed = results.match(/Installed plugins:\n  (.*)/);
+        expect(installed).toBeDefined();
+        expect(installed[1].indexOf('fake-plugin')).toBeGreaterThan(-1);
+    });
+}
+
+// This group of tests checks if plugins are added and removed as expected.
+describe('plugin end-to-end', function() {
+    var pluginId = 'cordova-plugin-device';
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    events.on('results', function(res) { results = res; });
+
+    beforeEach(function() {
+        shell.rm('-rf', project);
+
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
+        // Copy some platform to avoid working on a project with no platforms.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'platforms', helpers.testPlatform), path.join(project, 'platforms'));
+        process.chdir(project);
+
+        delete process.env.PWD;
+
+        spyOn(prepare, 'preparePlatforms').andCallThrough();
+    });
+
+    afterEach(function() {
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    it('Test#001 : should successfully add and remove a plugin with no options', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+        var retPromise;
+
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save
+        return cordova.raw.plugin('add', pluginId, {'save':true})
+        .then(function() {
+            // Check that the plugin add was successful.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+        }).then(function() {
+            // And now remove it with --save.
+            return cordova.raw.plugin('rm', pluginId, {'save':true})
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Checking that the plugin removed is in not in the platforms
+            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#002 : should successfully NOT add a plugin if save is not there', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save.
+        return cordova.raw.plugin('add', 'cordova-plugin-camera', {'save':true})
+        .then(function() {
+            // Add a second plugin without save
+            return cordova.raw.plugin('add', pluginId);
+        }).then(function() {
+            // Check the plugin add was successful for the first plugin that had --save.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+            // Expect that the second plugin is not added.
+            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#003 : should NOT remove plugin if there is no --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+        var retPromise;
+
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save.
+        return cordova.raw.plugin('add', pluginId, {'save':true})
+        .then(function() {
+            // Check the platform add was successful.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins).toBeDefined();
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+        }).then(function() {
+            // And now remove it, but without --save.
+            return cordova.raw.plugin('rm', 'cordova-plugin-device')
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // The plugin should still be in package.json.
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#004 : should successfully add and remove a plugin with variables and save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+        var retPromise;
+
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save.
+        return cordova.raw.plugin('add', pluginId, {'save':true, 'cli_variables': {'someKey':'someValue'}})
+        .then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Check the plugin add was successful and that variables have been added too.
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins).toBeDefined();
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+            expect(pkgJson.cordova.plugins[pluginId]['someKey']).toEqual('someValue');
+        }).then(function() {
+            // And now remove it with --save.
+            return cordova.raw.plugin('rm', pluginId, {'save':true})
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Checking that the plugin and variables were removed successfully.
+            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+});
+
+// This group of tests checks if platforms are added and removed as expected.
+describe('platform end-to-end with --save', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+
+        // cp then mv because we need to copy everything, but that means it'll copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
+        process.chdir(project);
+
+        // The config.json in the fixture project points at fake "local" paths.
+        // Since it's not a URL, the lazy-loader will just return the junk path.
+        spyOn(superspawn, 'spawn').andCallFake(function(cmd, args) {
+            if (cmd.match(/create\b/)) {
+                // This is a call to the bin/create script, so do the copy ourselves.
+                shell.cp('-R', path.join(__dirname, 'fixtures', 'platforms', 'android'), path.join(project, 'platforms'));
+            } else if(cmd.match(/version\b/)) {
+                return Q('3.3.0');
+            } else if(cmd.match(/update\b/)) {
+                fs.writeFileSync(path.join(project, 'platforms', helpers.testPlatform, 'updated'), 'I was updated!', 'utf-8');
+            }
+            return Q();
+        });
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+
+    it('Test#006 : platform is added and removed correctly with --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        expect(pkgJsonPath).toExist();
+        var pkgJson = require(pkgJsonPath);
+
+        // Check there are no platforms yet.
+        emptyPlatformList().then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Check the platform add was successful.
+            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+        .then(function() {
+            // And now remove it with --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Checking that the platform removed is in not in the platforms key
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
+        }).then(emptyPlatformList) // platform ls should be empty too.
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#007 : should not remove platforms from package.json when removing without --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        expect(pkgJsonPath).toExist();
+        var pkgJson = require(pkgJsonPath);
+        emptyPlatformList().then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Check the platform add was successful.
+            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+        .then(function() {
+            // And now remove it without --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform]);
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+
+            // Check that the platform removed without --save is still in platforms key.
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        }).then(emptyPlatformList)
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#008 : should not add platform to package.json when adding without --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+        var platformToAdd = helpers.testPlatform;
+        expect(pkgJsonPath).toExist();
+
+        // Add platform without --save.
+        cordova.raw.platform('add',platformToAdd)
+        .then(function() {
+            // Check the platform add was successful, reload, skipping cache
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Beware empty/missing cordova object
+            var pckJsonCordova = pkgJson.cordova || {platforms:[]};
+            // Check that the platform has NOT been added.
+            expect(pckJsonCordova.platforms.indexOf(platformToAdd)).toEqual(-1);
+        })
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        })
+        .fin(done);
+    });
+
+    it('Test#009 : should only add the platform to package.json with --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+        var platformToAdd = helpers.testPlatform;
+        var platformNotToAdd = 'ios';
+        expect(pkgJsonPath).toExist();
+
+        // Add a platform without --save.
+        cordova.raw.platform('add',platformNotToAdd)
+        .then(function() {
+            // And now add another platform with --save.
+            return cordova.raw.platform('add', platformToAdd, {'save':true});
+        }).then(function() {
+            // Check the platform add was successful, reload, skipping cache
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Beware empty/missing cordova object
+            var pckJsonCordova = pkgJson.cordova || {platforms:[]};
+            // Check that only the platform added with --save was added to package.json.
+            expect(pckJsonCordova.platforms.indexOf(platformToAdd)).toBeGreaterThan(-1);
+            expect(pckJsonCordova.platforms.indexOf(platformNotToAdd)).toEqual(-1);
+        })
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        })
+        .fin(done);
+    });
+});
+

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
new file mode 100644
index 0000000..1a1966d
--- /dev/null
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -0,0 +1,254 @@
+/**
+    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 helpers = require('./helpers'),
+    path = require('path'),
+    fs = require('fs'),
+    shell = require('shelljs'),
+    superspawn = require('cordova-common').superspawn,
+    Q = require('q'),
+    events = require('cordova-common').events,
+    cordova = require('../src/cordova/cordova'),
+    rewire = require('rewire'),
+    prepare = require('../src/cordova/prepare'),
+    platforms = require('../src/platforms/platforms'),
+    platform = rewire('../src/cordova/platform.js');
+
+var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
+var pluginsDir = path.join(__dirname, 'fixtures', 'plugins');
+
+// Testing will check if "cordova prepare" is restoring platforms as expected
+describe('platform end-to-end with --save', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project'); // call it projectPath
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // cp then mv because we need to copy everything, but that means it'll copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson2'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson2'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        var cwd = process.cwd();
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        //delete require.cache[require.resolve(path.join(cwd,'platforms/platforms.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+
+    it('Test#001 : should restore platform that has been removed from project', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        var pkgJson = require(pkgJsonPath);
+        var platformsFolderPath;
+
+        emptyPlatformList().then(function() {
+            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Check the platform add was successful.
+            platformsFolderPath = (path.join(cwd,'platforms/platforms.json'));
+            var platformsJson = require(platformsFolderPath);
+
+            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            expect(platformsJson).toBeDefined();
+            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+        .then(function() {
+            // And now remove it without --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform]);
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+
+            // Check that the platform removed without --save is still in platforms key.
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            // Check that the platform was removed from the platforms.json
+            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+        }).then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        })
+        .then(function() {
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Expect "helpers.testPlatform" to be in the installed platforms list
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+        })
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    },30000);
+
+    it('Test#002 : should NOT restore platform that was removed with --save', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        var pkgJson = require(pkgJsonPath);
+        var platformsFolderPath;
+        var secondPlatformAdded = 'ios';
+
+        emptyPlatformList().then(function() {
+            return cordova.raw.platform('rm', [helpers.testPlatform], {'save': true});
+        }).then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            return cordova.raw.platform('add',secondPlatformAdded, {'save':true});
+        }).then(function() {
+            // Check the platform add was successful.
+            platformsFolderPath = (path.join(cwd,'platforms/platforms.json'));
+            var platformsJson = require(platformsFolderPath);
+            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(0);
+            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(1);
+            expect(platformsJson).toBeDefined();
+            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+          .then(function() {
+            // And now remove it without --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            return cordova.raw.platform('rm', secondPlatformAdded);
+        }).then(function() {
+            // Delete any previous caches of require(package.json) and (platformsJson)
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Check that the platform removed without --save is still in platforms key.
+            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(0);
+            // Check that the platform was removed from the platforms.json
+            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+        }).then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        })
+        .then(function() {
+            // Delete any previous caches of platformsJson
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Expect "helpers.testPlatform" to be in the installed platforms list
+            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+            expect(platformsJson[secondPlatformAdded]).toBeDefined();
+        })
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    },30000);
+
+
+    it('Test#003 : should NOT restore platform that was not saved and removed', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        var pkgJson = require(pkgJsonPath);
+        var platformsFolderPath;
+        var secondPlatformAdded = 'ios';
+
+        emptyPlatformList().then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            return cordova.raw.platform('add', secondPlatformAdded);
+        }).then(function() {
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Check the platform add was successful.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            platformsFolderPath = (path.join(cwd,'platforms/platforms.json'));
+            var platformsJson = require(platformsFolderPath);
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+
+            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            //expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+          .then(function() {
+            // And now remove it without --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform]);
+        }).then(function() {
+            return cordova.raw.platform('rm', secondPlatformAdded);
+        }).then(function() {
+            // Delete any previous caches of require(package.json) and (platformsJson)
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Check that the platform removed without --save is still in platforms key.
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            // Check that the platform was removed from the platforms.json
+            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
+            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+        }).then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        })
+        .then(function() {
+            // Delete any previous caches of platformsJson
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Expect "helpers.testPlatform" to be in the installed platforms list
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
+        })
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    },30000);
+});
+
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/pkgJson.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson.spec.js b/cordova-lib/spec-cordova/pkgJson.spec.js
new file mode 100644
index 0000000..93af1dc
--- /dev/null
+++ b/cordova-lib/spec-cordova/pkgJson.spec.js
@@ -0,0 +1,348 @@
+/**
+    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 helpers = require('./helpers'),
+    path = require('path'),
+    fs = require('fs'),
+    shell = require('shelljs'),
+    superspawn = require('cordova-common').superspawn,
+    Q = require('q'),
+    events = require('cordova-common').events,
+    cordova = require('../src/cordova/cordova'),
+    rewire = require('rewire'),
+    prepare = require('../src/cordova/prepare'),
+    platforms = require('../src/platforms/platforms'),
+    platform = rewire('../src/cordova/platform.js');
+
+var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
+var pluginsDir = path.join(__dirname, 'fixtures', 'plugins');
+
+function addPlugin(target, id, options) {
+    // Checks that there are no plugins yet.
+    return cordova.raw.plugin('list').then(function() {
+        expect(results).toMatch(/No plugins added/gi);
+    }).then(function() {
+        // Adds a fake plugin from fixtures.
+        return cordova.raw.plugin('add', target, options);
+    }).then(function() {
+        expect(path.join(project, 'plugins', id, 'plugin.xml')).toExist();
+    }).then(function() {
+        return cordova.raw.plugin('ls');
+    }).then(function() {
+        expect(results).toContain(id);
+    });
+}
+// Runs: remove, list.
+function removePlugin(id, options) {
+    return cordova.raw.plugin('rm', id)
+    .then(function() {
+        // The whole dir should be gone.
+        expect(path.join(project, 'plugins', id)).not.toExist();
+    }).then(function() {
+        return cordova.raw.plugin('ls');
+    }).then(function() {
+        expect(results).toMatch(/No plugins added/gi);
+    });
+}
+// This group of tests checks if plugins are added and removed as expected from package.json.
+describe('plugin end-to-end', function() {
+    var pluginId = 'cordova-plugin-device';
+    var tmpDir = helpers.tmpDir('plugin_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    events.on('results', function(res) { results = res; });
+
+    beforeEach(function() {
+        shell.rm('-rf', project);
+
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
+        // Copy some platform to avoid working on a project with no platforms.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'platforms', helpers.testPlatform), path.join(project, 'platforms'));
+        process.chdir(project);
+
+        delete process.env.PWD;
+    });
+
+    afterEach(function() {
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    it('Test#001 : should successfully add and remove a plugin with save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+    
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save
+        return cordova.raw.plugin('add', pluginId, {'save':true})
+        .then(function() {
+            // Check that the plugin add was successful.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+        }).then(function() {
+            // And now remove it with --save.
+            return cordova.raw.plugin('rm', pluginId, {'save':true})
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Checking that the plugin removed is in not in the platforms
+            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#002 : should NOT add a plugin to package.json if --save is not used', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save.
+        return cordova.raw.plugin('add', 'cordova-plugin-camera', {'save':true})
+        .then(function() {
+            // Add a second plugin without save
+            return cordova.raw.plugin('add', pluginId);
+        }).then(function() {
+            // Check the plugin add was successful for the first plugin that had --save.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+            // Expect that the second plugin is not added.
+            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#003 : should NOT remove plugin from package.json if there is no --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+        
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save.
+        return cordova.raw.plugin('add', pluginId, {'save':true})
+        .then(function() {
+            // Check the platform add was successful.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins).toBeDefined();
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+        }).then(function() {
+            // And now remove it, but without --save.
+            return cordova.raw.plugin('rm', 'cordova-plugin-device')
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // The plugin should still be in package.json.
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#004 : should successfully add and remove a plugin with variables and save to package.json', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save.
+        return cordova.raw.plugin('add', pluginId, {'save':true, 'cli_variables': {'someKey':'someValue'}})
+        .then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Check the plugin add was successful and that variables have been added too.
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins).toBeDefined();
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+            expect(pkgJson.cordova.plugins[pluginId]['someKey']).toEqual('someValue');
+        }).then(function() {
+            // And now remove it with --save.
+            return cordova.raw.plugin('rm', pluginId, {'save':true})
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Checking that the plugin and variables were removed successfully.
+            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+});
+
+// This group of tests checks if platforms are added and removed as expected from package.json.
+describe('platform end-to-end with --save', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+
+        // cp then mv because we need to copy everything, but that means it'll copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+
+    it('Test#006 : platform is added and removed correctly with --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        expect(pkgJsonPath).toExist();
+        var pkgJson;
+
+        // Check there are no platforms yet.
+        emptyPlatformList().then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Check the platform add was successful.
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+        .then(function() {
+            // And now remove it with --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Checking that the platform removed is in not in the platforms key
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
+        }).then(emptyPlatformList) // platform ls should be empty too.
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#007 : should not remove platforms from package.json when removing without --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        expect(pkgJsonPath).toExist();
+        var pkgJson;
+        emptyPlatformList().then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Check the platform add was successful.
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+        .then(function() {
+            // And now remove it without --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform]);
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Check that the platform removed without --save is still in platforms key.
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        }).then(emptyPlatformList)
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
+
+    it('Test#008 : should not add platform to package.json when adding without --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+        expect(pkgJsonPath).toExist();
+
+        // Add platform without --save.
+        cordova.raw.platform('add',[helpers.testPlatform])
+        .then(function() {
+            // Check the platform add was successful, reload, skipping cache
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Cordova key should not exist in package.json
+            expect(pkgJson.cordova).toBeUndefined();
+        }).then(fullPlatformList)
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        })
+        .fin(done);
+    });
+
+    it('Test#009 : should only add the platform to package.json with --save', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+        var platformNotToAdd = 'ios';
+        expect(pkgJsonPath).toExist();
+
+        // Add a platform without --save.
+        cordova.raw.platform('add',platformNotToAdd)
+        .then(function() {
+            // And now add another platform with --save.
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Check the platform add was successful, reload, skipping cache
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Beware empty/missing cordova object
+            var pckJsonCordova = pkgJson.cordova || {platforms:[]};
+            // Check that only the platform added with --save was added to package.json.
+            expect(pckJsonCordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            expect(pckJsonCordova.platforms.indexOf(platformNotToAdd)).toEqual(-1);
+        })
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        })
+        .fin(done);
+    });
+});
+

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/spec-cordova/platform.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/platform.spec.js b/cordova-lib/spec-cordova/platform.spec.js
index 1a40130..4a3b2f1 100644
--- a/cordova-lib/spec-cordova/platform.spec.js
+++ b/cordova-lib/spec-cordova/platform.spec.js
@@ -28,6 +28,8 @@ var helpers = require('./helpers'),
     cordova = require('../src/cordova/cordova'),
     plugman = require('../src/plugman/plugman'),
     rewire = require('rewire'),
+    prepare = require('../src/cordova/prepare'),
+    platforms = require('../src/platforms/platforms'),
     platform = rewire('../src/cordova/platform.js');
 
 var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
@@ -92,6 +94,34 @@ describe('platform end-to-end', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
         });
     }
+    // Runs: list, add, list
+    function addPlugin(target, id, options) {
+        // Check there are no plugins yet.
+        return cordova.raw.plugin('list').then(function() {
+            expect(results).toMatch(/No plugins added/gi);
+        }).then(function() {
+            // Add a fake plugin from fixtures.
+            return cordova.raw.plugin('add', target, options);
+        }).then(function() {
+            expect(path.join(project, 'plugins', id, 'plugin.xml')).toExist();
+        }).then(function() {
+            return cordova.raw.plugin('ls');
+        }).then(function() {
+            expect(results).toContain(id);
+        });
+    }
+    // Runs: remove, list
+    function removePlugin(id) {
+        return cordova.raw.plugin('rm', id)
+        .then(function() {
+            // The whole dir should be gone.
+            expect(path.join(project, 'plugins', id)).not.toExist();
+        }).then(function() {
+            return cordova.raw.plugin('ls');
+        }).then(function() {
+            expect(results).toMatch(/No plugins added/gi);
+        });
+    }        
 
     // The flows we want to test are add, rm, list, and upgrade.
     // They should run the appropriate hooks.
@@ -104,6 +134,7 @@ describe('platform end-to-end', function () {
             // Add the testing platform.
             return cordova.raw.platform('add', [helpers.testPlatform]);
         }).then(function() {
+            console.log("!!!");
             // Check the platform add was successful.
             expect(path.join(project, 'platforms', helpers.testPlatform)).toExist();
             expect(path.join(project, 'platforms', helpers.testPlatform, 'cordova')).toExist();
@@ -119,7 +150,6 @@ describe('platform end-to-end', function () {
             // And now remove it.
             return cordova.raw.platform('rm', [helpers.testPlatform]);
         }).then(function() {
-            // It should be gone.
             expect(path.join(project, 'platforms', helpers.testPlatform)).not.toExist();
         }).then(emptyPlatformList) // platform ls should be empty too.
         .fail(function(err) {
@@ -354,5 +384,8 @@ describe('plugin add and rm end-to-end --fetch', function () {
             expect(err).toBeUndefined();
         })
         .fin(done);
-    }, 100000);
+    }, 60000);
 });
+
+        // Runs: list, add, list
+    
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/src/cordova/platform.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/platform.js b/cordova-lib/src/cordova/platform.js
index 0d5f166..e34b4db 100644
--- a/cordova-lib/src/cordova/platform.js
+++ b/cordova-lib/src/cordova/platform.js
@@ -16,7 +16,6 @@
     specific language governing permissions and limitations
     under the License.
 */
-
 var config            = require('./config'),
     cordova           = require('./cordova'),
     prepare           = require('./prepare'),
@@ -38,9 +37,9 @@ var config            = require('./config'),
     _                 = require('underscore'),
     PlatformJson      = require('cordova-common').PlatformJson,
     fetch             = require('cordova-fetch'),
-    npmUninstall         = require('cordova-fetch').uninstall,
+    npmUninstall      = require('cordova-fetch').uninstall,
     platformMetadata  = require('./platform_metadata');
-
+ 
 // Expose the platform parsers on top of this command
 for (var p in platforms) {
     module.exports[p] = platforms[p];
@@ -84,6 +83,16 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
 
     return hooksRunner.fire('before_platform_' + cmd, opts)
     .then(function() {
+        var modifiedPkgJson = false;
+        var pkgJson;
+        var pkgJsonPath = path.join(projectRoot, 'package.json');
+        // If statement to see if pkgJsonPath exists in the filesystem
+        if(fs.existsSync(pkgJsonPath)) {
+            pkgJson = require(pkgJsonPath);
+        } else {
+            // Create package.json in cordova@7
+        }
+
         return promiseutil.Q_chainmap(targets, function(target) {
             // For each platform, download it and call its helper script.
             var parts = target.split('@');
@@ -217,6 +226,7 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                 .then(function() {
                     var saveVersion = !spec || semver.validRange(spec, true);
 
+
                     // Save platform@spec into platforms.json, where 'spec' is a version or a soure location. If a
                     // source location was specified, we always save that. Otherwise we save the version that was
                     // actually installed.
@@ -235,11 +245,34 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                         cfg.removeEngine(platform);
                         cfg.addEngine(platform, spec);
                         cfg.write();
+
+                        // If package.json exists, cordova key and platforms will be added to package.json if not
+                        // already there 
+                        if (pkgJson === undefined) {
+                            return;
+                        }
+                        if (pkgJson.cordova === undefined) {
+                            pkgJson.cordova = {};
+                        }
+                        if (pkgJson.cordova.platforms === undefined){
+                            pkgJson.cordova.platforms = [];
+                        }
+                        if (pkgJson.cordova.platforms.indexOf(platform) === -1) {
+                            events.emit('log','adding '+platform+' to cordova.platforms array in package.json');
+                            pkgJson.cordova.platforms.push(platform);
+                            modifiedPkgJson = true;
+                        } else {
+                            events.emit('verbose', platform + ' already added to platforms key');
+                        }
                     }
                 });
             });
+        }).then(function(){
+            if (modifiedPkgJson === true) {
+                fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+            }
         });
-    }).then(function() {
+    }).then(function(){
         return hooksRunner.fire('after_platform_' + cmd, opts);
     });
 }
@@ -379,6 +412,13 @@ function remove(hooksRunner, projectRoot, targets, opts) {
     }).then(function() {
         var config_json = config.read(projectRoot);
         var autosave =  config_json.auto_save_platforms || false;
+        var modifiedPkgJson = false;
+        var pkgJson;
+        var pkgJsonPath = path.join(projectRoot,'package.json');
+        // If statement to see if pkgJsonPath exists in the filesystem
+        if(fs.existsSync(pkgJsonPath)) {
+            pkgJson = require(pkgJsonPath);
+        }
         if(opts.save || autosave){
             targets.forEach(function(target) {
                 var platformName = target.split('@')[0];
@@ -387,7 +427,21 @@ function remove(hooksRunner, projectRoot, targets, opts) {
                 events.emit('log', 'Removing platform ' + target + ' from config.xml file...');
                 cfg.removeEngine(platformName);
                 cfg.write();
+                // If package.json exists and contains a specified platform in cordova.platforms, it will be removed
+                if(pkgJson !== undefined && pkgJson.cordova !== undefined && pkgJson.cordova.platforms !== undefined) {
+                    var index = pkgJson.cordova.platforms.indexOf(platformName);
+                    //Check if platform exists in platforms array
+                    if (pkgJson.cordova.platforms !== undefined && index > -1) {
+                        events.emit('log', 'Removing ' + platformName + ' from cordova.platforms array in package.json');
+                        pkgJson.cordova.platforms.splice(index, 1);
+                        modifiedPkgJson = true;
+                    }
+                }
             });
+            //Write out new package.json if changes have been made
+            if(modifiedPkgJson === true) {
+                fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+            }
         }
     }).then(function() {
         // Remove targets from platforms.json
@@ -402,6 +456,7 @@ function remove(hooksRunner, projectRoot, targets, opts) {
                 if(target in platforms) {
                     target = 'cordova-'+target;
                 }
+                //edits package.json
                 return npmUninstall(target, projectRoot, opts);
             });
         }

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/197cba97/cordova-lib/src/cordova/plugin.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/plugin.js b/cordova-lib/src/cordova/plugin.js
index 4ccf40e..4203ca3 100644
--- a/cordova-lib/src/cordova/plugin.js
+++ b/cordova-lib/src/cordova/plugin.js
@@ -235,6 +235,35 @@ module.exports = function plugin(command, targets, opts) {
                                 cfg.write();
 
                                 events.emit('results', 'Saved plugin info for "' + pluginInfo.id + '" to config.xml');
+
+                                var pkgJson;
+                                var pkgJsonPath = path.join(projectRoot,'package.json');
+
+                                // If statement to see if pkgJsonPath exists in the filesystem
+                                if(fs.existsSync(pkgJsonPath)) {
+                                    // Delete any previous caches of require(package.json)
+                                    delete require.cache[require.resolve(pkgJsonPath)];
+                                    pkgJson = require(pkgJsonPath);
+                                } else {
+                                    // Create package.json in cordova@7
+                                }
+                                // If package.json exists, the plugin object and plugin name will be added to package.json 
+                                // if not already there
+                                if (pkgJson === undefined) {
+                                    return;
+                                }
+                                if (pkgJson.cordova === undefined) {
+                                    pkgJson.cordova = {};
+                                }
+                                if (pkgJson.cordova.plugins === undefined) {
+                                    pkgJson.cordova.plugins = {};
+                                }
+                                if (pkgJson.cordova.plugins !== undefined) {
+                                    pkgJson.cordova.plugins[pluginInfo.id] = opts.cli_variables;
+                                    events.emit('log','Adding '+pluginInfo.id+ ' to package.json');
+                                    // Write to package.json
+                                    fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+                                }
                             }
                         });
                     }, Q());
@@ -243,7 +272,6 @@ module.exports = function plugin(command, targets, opts) {
                     if (!shouldRunPrepare) {
                         return Q();
                     }
-
                     // Need to require right here instead of doing this at the beginning of file
                     // otherwise tests are failing without any real reason.
                     return require('./prepare').preparePlatforms(platformList, projectRoot, opts);
@@ -299,9 +327,26 @@ module.exports = function plugin(command, targets, opts) {
                                     configXml.removePlugin(target);
                                     configXml.write();
                                 }
+                                var pkgJson;
+                                var pkgJsonPath = path.join(projectRoot,'package.json');
+                                // If statement to see if pkgJsonPath exists in the filesystem
+                                if(fs.existsSync(pkgJsonPath)) {
+                                    //delete any previous caches of require(package.json)
+                                    delete require.cache[require.resolve(pkgJsonPath)];
+                                    pkgJson = require(pkgJsonPath);
+                                } else {
+                                    // Create package.json in cordova@7
+                                }
+                                // If package.json exists and contains a specified plugin in cordova['plugins'], it will be removed    
+                                if(pkgJson !== undefined && pkgJson.cordova !== undefined && pkgJson.cordova.plugins !== undefined) {
+                                    events.emit('log', 'Removing ' + target + ' from package.json');
+                                    delete pkgJson.cordova.plugins[target];
+                                    //Write out new package.json 
+                                    fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+                                }
                             }
-                        })
-                        .then(function(){
+                            
+                        }).then(function(){
                             // Remove plugin from fetch.json
                             events.emit('verbose', 'Removing plugin ' + target + ' from fetch.json');
                             metadata.remove_fetch_metadata(pluginPath, target);
@@ -311,7 +356,7 @@ module.exports = function plugin(command, targets, opts) {
                     // CB-11022 We do not need to run prepare after plugin install until shouldRunPrepare flag is set to true
                     if (!shouldRunPrepare) {
                         return Q();
-                    }
+                    }   
 
                     return require('./prepare').preparePlatforms(platformList, projectRoot, opts);
                 }).then(function() {


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


[12/12] cordova-lib git commit: CB-11960 : updated android versions to 6.1.1., removed ios platforms for testing and replaced with browser, added includes function to support node 4. Test 005 commented out because not promisified correctly (CB-12170).

Posted by st...@apache.org.
CB-11960 : updated android versions to 6.1.1., removed ios platforms for testing and replaced with browser, added includes function to support node 4. Test 005 commented out because not promisified correctly (CB-12170).

 This closes #499


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/a9605411
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/a9605411
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/a9605411

Branch: refs/heads/master
Commit: a9605411ab007d700849c971043f0c211c06100c
Parents: 1acdbff
Author: Audrey So <au...@apache.org>
Authored: Wed Jan 25 15:46:43 2017 -0800
Committer: Steve Gill <st...@gmail.com>
Committed: Fri Feb 3 16:58:53 2017 -0800

----------------------------------------------------------------------
 .../fixtures/basePkgJson/plugins/.svn           |   0
 .../fixtures/basePkgJson10/config.xml           |   2 +-
 .../fixtures/basePkgJson10/package.json         |   2 +-
 .../fixtures/basePkgJson10/plugins/.svn         |   0
 .../fixtures/basePkgJson11/config.xml           |   2 +-
 .../fixtures/basePkgJson11/package.json         |   2 +-
 .../fixtures/basePkgJson11/plugins/.svn         |   0
 .../fixtures/basePkgJson12/config.xml           |   2 +-
 .../fixtures/basePkgJson12/package.json         |   2 +-
 .../fixtures/basePkgJson12/plugins/.svn         |   0
 .../fixtures/basePkgJson13/plugins/.svn         |   0
 .../fixtures/basePkgJson3/plugins/.svn          |   0
 .../fixtures/basePkgJson4/plugins/.svn          |   0
 .../fixtures/basePkgJson5/config.xml            |   2 +-
 .../fixtures/basePkgJson5/plugins/.svn          |   0
 .../fixtures/basePkgJson6/plugins/.svn          |   0
 .../fixtures/basePkgJson8/config.xml            |   2 +-
 .../fixtures/basePkgJson8/package.json          |   2 +-
 .../fixtures/basePkgJson8/plugins/.svn          |   0
 .../fixtures/basePkgJson9/config.xml            |   2 +-
 .../fixtures/basePkgJson9/package.json          |   2 +-
 .../fixtures/basePkgJson9/plugins/.svn          |   0
 .../spec-cordova/pkgJson-restore.spec.js        | 155 ++++++++++---------
 cordova-lib/spec-cordova/pkgJson.spec.js        |  45 +++---
 cordova-lib/spec-cordova/platform.spec.js       |  12 +-
 cordova-lib/spec-cordova/plugin_fetch.spec.js   |  26 ++--
 cordova-lib/spec-cordova/save.spec.js           |  10 +-
 cordova-lib/spec-plugman/fetch.spec.js          |   9 +-
 .../spec-plugman/platforms/windows.spec.js      |   2 -
 cordova-lib/src/cordova/restore-util.js         |  21 ++-
 30 files changed, 159 insertions(+), 143 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml
index 6134bed..7091fe2 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml
@@ -11,7 +11,7 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
-    <engine name="ios"/>
+    <engine name="browser"/>
     <plugin name="cordova-plugin-camera">
         <variable name="variable_3" value="value_3" />
     </plugin>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json
index 58dc4ae..01a4a7e 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json
@@ -14,7 +14,7 @@
     "license": "ISC",
     "cordova": {
         "platforms": [
-            "ios"
+            "browser"
         ],
         "plugins": {
             "cordova-plugin-device": {

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson10/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson10/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml
index f5f7238..1d975bb 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml
@@ -11,7 +11,7 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
-    <engine name="ios"/>
+    <engine name="browser"/>
     <plugin name="cordova-plugin-camera" spec="~2.2.0">
         <variable name="variable_1" value="value_1" />
         <variable name="variable_2" value="value_2" />

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json
index d86529b..9ab7986 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json
@@ -14,7 +14,7 @@
     "license": "ISC",
     "cordova": {
         "platforms": [
-            "ios"
+            "browser"
         ],
         "plugins": {
             "cordova-plugin-splashscreen": {},

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson11/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson11/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml
index 0206c02..a343278 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml
@@ -11,5 +11,5 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
-    <engine name="ios"/>
+    <engine name="browser"/>
 </widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json
index 08c11dd..6d8e08b 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json
@@ -14,7 +14,7 @@
     "license": "ISC",
     "cordova": {
         "platforms": [
-            "ios"
+            "browser"
         ],
         "plugins": {
             "cordova-plugin-camera": {

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson12/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson12/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson13/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson13/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson13/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson3/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson3/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson3/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson4/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson4/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
index 28663fc..536b125 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
@@ -11,6 +11,6 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
-    <engine name="android" spec="6.0.0"/>
+    <engine name="android" spec="6.1.1"/>
     <engine name="browser" spec="^4.1.0"/>
 </widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson5/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson5/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson6/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson6/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson6/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml
index dab76f5..bd406b7 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml
@@ -11,7 +11,7 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
-    <engine name="ios"/>
+    <engine name="browser"/>
     <plugin name="cordova-plugin-camera">
     <variable name="variable_1" value="config" />
     </plugin>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json
index 2f74834..7061317 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json
@@ -14,7 +14,7 @@
     "license": "ISC",
     "cordova": {
         "platforms": [
-            "ios"
+            "browser"
         ],
         "plugins": {
             "cordova-plugin-camera": {

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson8/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson8/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml
index a0e37ad..6c6bf58 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml
@@ -11,7 +11,7 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
-    <engine name="ios"/>
+    <engine name="browser"/>
     <plugin name="cordova-plugin-camera">
         <variable name="variable_1" value="value_1" />
     </plugin>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json
index 4e654d4..62ccdc9 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json
@@ -14,7 +14,7 @@
     "license": "ISC",
     "cordova": {
         "platforms": [
-            "ios"
+            "browser"
         ],
         "plugins": {
             "cordova-plugin-camera": {

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/fixtures/basePkgJson9/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson9/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
index dd8e197..e1c341a 100644
--- a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -15,13 +15,14 @@
     KIND, either express or implied.  See the License for the
     specific language governing permissions and limitations
     under the License.
-*/
-var helpers = require('./helpers'),
+    */
+    var helpers = require('./helpers'),
     path = require('path'),
     shell = require('shelljs'),
     events = require('cordova-common').events,
     ConfigParser = require('cordova-common').ConfigParser,
-    cordova = require('../src/cordova/cordova');
+    cordova = require('../src/cordova/cordova'),
+    TIMEOUT = 60 * 1000;
 
 /** Testing will check if "cordova prepare" is restoring platforms and plugins as expected.
 *   Uses different basePkgJson files depending on testing expecations of what (platforms/plugins/variables)
@@ -128,7 +129,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },300000);
+},300000);
 
     /** Test#017
     *   When platform is added with url and fetch and restored with fetch, 
@@ -212,7 +213,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 
     /** Test#018
     *   When plugin is added with url and fetch and restored with fetch, 
@@ -286,7 +287,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use basePkgJson
@@ -360,7 +361,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
             expect(platformsJson[secondPlatformAdded]).toBeDefined();
         }).then(fullPlatformList) // Platforms should still be in platform ls.
-          .then(function() {
+        .then(function() {
             // Remove helpers.testPlatform without --save.
             return cordova.raw.platform('rm', [helpers.testPlatform]);
         }).then(function() {
@@ -392,7 +393,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use basePkgJson6 because pkg.json and config.xml contain only android
@@ -464,7 +465,7 @@ describe('files should not be modified if their platforms are identical', functi
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use a new basePkgJson5 as config.xml contains android/browser and pkg.json contains android
@@ -517,7 +518,7 @@ describe('update pkg.json to include platforms in config.xml', function () {
         var configEngArray = engNames.slice();
         var androidPlatform = 'android';
         var browserPlatform = 'browser';
-       
+
         // Config.xml contains(android & browser) and pkg.json contains android (basePkgJson5).
         expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
         expect(configEngArray.indexOf(browserPlatform)).toBeGreaterThan(-1);
@@ -544,12 +545,12 @@ describe('update pkg.json to include platforms in config.xml', function () {
             expect(configEngArray.indexOf(browserPlatform)).toBeGreaterThan(-1);
             // Platform specs from config.xml have been added to pkg.json.
             expect(pkgJson.dependencies['cordova-browser']).toEqual('^4.1.0');
-            expect(pkgJson.dependencies['cordova-android']).toEqual('6.0.0');
+            expect(pkgJson.dependencies['cordova-android']).toEqual('6.1.1');
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use basePkgJson3 as it has 'android' in config.xml and pkg.json (no cordova key).
@@ -586,7 +587,7 @@ describe('update empty package.json to match config.xml', function () {
      *   If it does not and config.xml has a platform(s) installed already, run cordova prepare
      *   and it will add a cordova key and the platform(s) from config.xml to package.json.
      */
-    it('Test#006 : if pkg.json exists without cordova key, create one with same platforms in config.xml ', function(done) {
+     it('Test#006 : if pkg.json exists without cordova key, create one with same platforms in config.xml ', function(done) {
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var pkgJsonPath = path.join(cwd,'package.json');
@@ -628,8 +629,8 @@ describe('update empty package.json to match config.xml', function () {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
-});
+},TIMEOUT);
+ });
 
 // Use a new basePkgJson4 as pkg.json contains android/browser and config.xml contains android.
 describe('update config.xml to include platforms in pkg.json', function () {
@@ -714,7 +715,7 @@ describe('update config.xml to include platforms in pkg.json', function () {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Plugin testing begins here.
@@ -801,7 +802,7 @@ describe('update config.xml to use the variable found in pkg.json', function ()
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use basePkgJson9 as config contains 1 plugin and 1 variable and pkg.json contains 1 plugin 0 var
@@ -887,7 +888,7 @@ describe('update pkg.json to include plugin and variable found in config.xml', f
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use basePkgJson10 as pkg.json contains (camera plugin: var 1/var 2, splashscreen plugin). 
@@ -968,7 +969,7 @@ describe('update pkg.json AND config.xml to include all plugins and merge unique
         emptyPlatformList().then(function() {
             // Run cordova prepare.
             return cordova.raw.prepare();
-         }).then(function() {
+        }).then(function() {
             // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
@@ -981,7 +982,7 @@ describe('update pkg.json AND config.xml to include all plugins and merge unique
                 // Config.xml camera variables have been merged, no duplicates.
                 if(configPlugin.name === 'cordova-plugin-camera') {
                     expect(configPluginVariables).toEqual({ variable_1: ' ', 
-                    variable_2: ' ', variable_3: 'value_3' });
+                        variable_2: ' ', variable_3: 'value_3' });
                 }
                 // Expect that device has var1, val1 and splashscreen has 0 var.
                 if(configPlugin.name === 'cordova-plugin-device') {
@@ -1010,7 +1011,7 @@ describe('update pkg.json AND config.xml to include all plugins and merge unique
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use basePkgJson11 as pkg.json contains(splashscreen plugin, camera plugin: var1, value1, var2, value2) and
@@ -1112,7 +1113,7 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
                 // Config.xml camera variables have been merged, no duplicates
                 if(configPlugin.name === 'cordova-plugin-camera') {
                     expect(configPluginVariables).toEqual({ variable_1: 'value_1',
-                    variable_3: 'value_3', variable_2: 'value_2' });
+                        variable_3: 'value_3', variable_2: 'value_2' });
                     // Config.xml plugin spec should be updated to ^2.3.0
                     expect(configPlugin.spec).toEqual('^2.3.0');
                 }
@@ -1144,7 +1145,7 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
             // Expect that the variables from config have been merged with the variables 
             // from pkg.json to the camera plugin
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'value_1',
-            variable_3: 'value_3', variable_2: 'value_2' });
+                variable_3: 'value_3', variable_2: 'value_2' });
             // Expect that all 3 plugins are restored and in the installed list.
             expect(path.join(pluginsFolderPath14, 'cordova-plugin-camera')).toExist();
             expect(path.join(pluginsFolderPath14, 'cordova-plugin-splashscreen')).toExist();
@@ -1153,7 +1154,7 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use basePkgJson12 as config.xml has 0 plugins and pkg.json has 1.
@@ -1253,7 +1254,7 @@ describe('update config.xml to include the plugin that is in pkg.json', function
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });
 
 // Use basePkgJson13 - does NOT have a package.json
@@ -1313,52 +1314,52 @@ describe('platforms and plugins should be restored with config.xml even without
         expect(Object.keys(configPlugins).length === 1);
 
         emptyPlatformList().then(function() {
-        // Run cordova prepare.
-        return cordova.raw.platform('add', browserPlatform, {'save':true});
+            // Run cordova prepare.
+            return cordova.raw.platform('add', browserPlatform, {'save':true});
         }).then(function () {
-        // Android and browser are in config.xml.
-        var cfg3 = new ConfigParser(configXmlPath);
-        engines = cfg3.getEngines();
-        engNames = engines.map(function(elem) {
-            return elem.name;
-        });
-        configEngArray = engNames.slice();
-        expect(configEngArray.length === 2);
-        // Delete previouc caches of (pkg.json).
-        delete require.cache[require.resolve(platformsFolderPath1)];
-        platformsJson = require(platformsFolderPath1);
-        // Android and browser should be installed
-        expect(platformsJson).toBeDefined();
-        expect(platformsJson[androidPlatform]).toBeDefined();
-        expect(platformsJson[browserPlatform]).toBeDefined();
-        // Remove android without --save.
-        return cordova.raw.platform('rm', [browserPlatform]);
+            // Android and browser are in config.xml.
+            var cfg3 = new ConfigParser(configXmlPath);
+            engines = cfg3.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            expect(configEngArray.length === 2);
+            // Delete previouc caches of (pkg.json).
+            delete require.cache[require.resolve(platformsFolderPath1)];
+            platformsJson = require(platformsFolderPath1);
+            // Browser should be installed
+            expect(platformsJson).toBeDefined();
+            expect(platformsJson[androidPlatform]).not.toBeDefined();
+            expect(platformsJson[browserPlatform]).toBeDefined();
+            // Remove android without --save.
+            return cordova.raw.platform('rm', [browserPlatform]);
         }).then(function () {
-        // Android should not be in the installed list (only browser).
-        delete require.cache[require.resolve(platformsFolderPath1)];
-        platformsJson = require(platformsFolderPath1);
-        expect(platformsJson).toBeDefined();
-        expect(platformsJson[browserPlatform]).toBeUndefined();
-        expect(platformsJson[androidPlatform]).toBeDefined();
+            // Android should not be in the installed list (only browser).
+            delete require.cache[require.resolve(platformsFolderPath1)];
+            platformsJson = require(platformsFolderPath1);
+            expect(platformsJson).toBeDefined();
+            expect(platformsJson[browserPlatform]).toBeUndefined();
+            expect(platformsJson[androidPlatform]).not.toBeDefined();
         }).then(function () {
-        // Run cordova prepare.
-        return cordova.raw.prepare();
+            // Run cordova prepare.
+            return cordova.raw.prepare();
         }).then(function() {
-        var cfg2 = new ConfigParser(configXmlPath);
-        engines = cfg2.getEngines();
-        engNames = engines.map(function(elem) {
-            return elem.name;
-        });
-        configEngArray = engNames.slice();
-        // Config.xml should have android and browser.
-        expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
-        expect(configEngArray.indexOf(browserPlatform)).toBeGreaterThan(-1);
-        expect(configEngArray.length === 2);
-        // Expect that android and browser were restored.
-        delete require.cache[require.resolve(platformsFolderPath1)];
-        platformsJson = require(platformsFolderPath1);
-        expect(platformsJson[androidPlatform]).toBeDefined();
-        expect(platformsJson[browserPlatform]).toBeDefined();
+            var cfg2 = new ConfigParser(configXmlPath);
+            engines = cfg2.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            // Config.xml should have android and browser.
+            expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
+            expect(configEngArray.indexOf(browserPlatform)).toBeGreaterThan(-1);
+            expect(configEngArray.length === 2);
+            // Expect that android and browser were restored.
+            delete require.cache[require.resolve(platformsFolderPath1)];
+            platformsJson = require(platformsFolderPath1);
+            expect(platformsJson[androidPlatform]).toBeDefined();
+            expect(platformsJson[browserPlatform]).toBeDefined();
         }).then(function () {
             //Check plugins.
             var cfg5 = new ConfigParser(configXmlPath);
@@ -1392,12 +1393,12 @@ describe('platforms and plugins should be restored with config.xml even without
             return cordova.raw.prepare();
         }).then(function () {
         //  Plugin should be restored and returned to the installed list.
-            expect(path.join(pluginsFolderPath16, 'cordova-plugin-device')).toExist();
-        }).fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
+        expect(path.join(pluginsFolderPath16, 'cordova-plugin-device')).toExist();
+    }).fail(function(err) {
+        expect(err).toBeUndefined();
+    }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },60000);
+},TIMEOUT);
 });
 
 
@@ -1469,7 +1470,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(platformsJson).toBeDefined();
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
         }).then(fullPlatformList) // Platform should still be in platform ls.
-          .then(function() {
+        .then(function() {
             // And now remove helpers.testPlatform without --save.
             return cordova.raw.platform('rm', [helpers.testPlatform]);
         }).then(function() {
@@ -1495,7 +1496,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 
     /** Test#002 will add two platforms to package.json with the 'save' flag.
     *   It will remove one platform from pkg.json without the 'save' flag and remove
@@ -1509,7 +1510,7 @@ describe('tests platform/spec restore with --save', function () {
         var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
         var platformsJson;
         var secondPlatformAdded = 'browser';
-    
+
         emptyPlatformList().then(function() {
             // Add the testing platform with --save.
             return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
@@ -1531,7 +1532,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
             expect(platformsJson[secondPlatformAdded]).toBeDefined();
         }).then(fullPlatformList) // Platform should still be in platform ls.
-          .then(function() {
+        .then(function() {
             // Remove helpers.testPlatform with --save.
             return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
         }).then(function() {
@@ -1564,5 +1565,5 @@ describe('tests platform/spec restore with --save', function () {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+},TIMEOUT);
 });

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/pkgJson.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson.spec.js b/cordova-lib/spec-cordova/pkgJson.spec.js
index 654fb1d..c9d5440 100644
--- a/cordova-lib/spec-cordova/pkgJson.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson.spec.js
@@ -21,7 +21,8 @@ var helpers = require('./helpers'),
     shell = require('shelljs'),
     events = require('cordova-common').events,
     ConfigParser = require('cordova-common').ConfigParser,
-    cordova = require('../src/cordova/cordova');
+    cordova = require('../src/cordova/cordova'),
+    TIMEOUT = 30 * 1000;
 
 // This group of tests checks if plugins are added and removed as expected from package.json.
 describe('plugin end-to-end', function() {
@@ -95,7 +96,7 @@ describe('plugin end-to-end', function() {
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
-    });
+    }, TIMEOUT);
 
     it('Test#002 : should NOT add a plugin to package.json if --save is not used', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
@@ -120,7 +121,7 @@ describe('plugin end-to-end', function() {
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
-    });
+    }, TIMEOUT);
 
     it('Test#003 : should NOT remove plugin from package.json if there is no --save', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
@@ -150,7 +151,7 @@ describe('plugin end-to-end', function() {
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
-    });
+    }, TIMEOUT);
 
     it('Test#004 : should successfully add and remove a plugin with variables and save to package.json', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
@@ -182,8 +183,10 @@ describe('plugin end-to-end', function() {
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
-    });
-    it('Test#005 : should successfully add and remove multiple plugins with save & fetch', function(done) {
+    }, TIMEOUT);
+
+    // CB-12170 : Test is commented out because not promisified correctly in cordova-create script
+    xit('Test#005 : should successfully add and remove multiple plugins with save & fetch', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
         var pkgJson;
         // Delete any previous caches of require(package.json).
@@ -220,7 +223,7 @@ describe('plugin end-to-end', function() {
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
-    });
+    }, TIMEOUT);
 });
 
 // This group of tests checks if platforms are added and removed as expected from package.json.
@@ -291,7 +294,7 @@ describe('platform end-to-end with --save', function () {
         .fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
-    });
+    }, TIMEOUT);
 
     it('Test#007 : should not remove platforms from package.json when removing without --save', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
@@ -319,7 +322,7 @@ describe('platform end-to-end with --save', function () {
         .fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
-    });
+    }, TIMEOUT);
 
     it('Test#008 : should not add platform to package.json when adding without --save', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
@@ -343,12 +346,12 @@ describe('platform end-to-end with --save', function () {
             expect(err).toBeUndefined();
         })
         .fin(done);
-    });
+    }, TIMEOUT);
 
     it('Test#009 : should only add the platform to package.json with --save', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
         var pkgJson;
-        var platformNotToAdd = 'ios';
+        var platformNotToAdd = 'browser';
         expect(pkgJsonPath).toExist();
 
         // Add a platform without --save.
@@ -370,7 +373,7 @@ describe('platform end-to-end with --save', function () {
             expect(err).toBeUndefined();
         })
         .fin(done);
-    }, 30000);
+    }, TIMEOUT);
     it('Test#010 : two platforms are added and removed correctly with --save --fetch', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
         expect(pkgJsonPath).toExist();
@@ -393,7 +396,7 @@ describe('platform end-to-end with --save', function () {
         // Check there are no platforms yet.
         emptyPlatformList().then(function() {
             // Add the testing platform with --save and add specific version to android platform.
-            return cordova.raw.platform('add', ['android@6.1.0', 'ios@4.3.0'], {'save':true, 'fetch':true});
+            return cordova.raw.platform('add', ['android@6.1.0', 'browser@4.1.0'], {'save':true, 'fetch':true});
         }).then(function() {
             // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
@@ -401,10 +404,10 @@ describe('platform end-to-end with --save', function () {
             // Check the platform add was successful in platforms list and in dependencies.
             expect(pkgJson.cordova.platforms).toBeDefined();
             expect(pkgJson.cordova.platforms.indexOf('android')).toEqual(0);
-            expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(1);
+            expect(pkgJson.cordova.platforms.indexOf('browser')).toEqual(1);
             expect(pkgJson.dependencies).toBeDefined();
             expect(pkgJson.dependencies['cordova-android']).toBeDefined();
-            expect(pkgJson.dependencies['cordova-ios']).toBeDefined();
+            expect(pkgJson.dependencies['cordova-browser']).toBeDefined();
             // Android platform should have specific version from add.
             expect(pkgJson.dependencies['cordova-android']).toEqual('^6.1.0');
 
@@ -414,24 +417,24 @@ describe('platform end-to-end with --save', function () {
                 return elem.name;
             });
             configEngArray = engNames.slice();
-            // Check that android and ios were added to config.xml with the correct spec.
+            // Check that android and browser were added to config.xml with the correct spec.
             expect(configEngArray.length === 2);
-            expect(engines).toEqual([ { name: 'android', spec: '~6.1.0' }, { name: 'ios', spec: '~4.3.0' } ]);
+            expect(engines).toEqual([ { name: 'android', spec: '~6.1.0' }, { name: 'browser', spec: '~4.1.0' } ]);
 
         }).then(fullPlatformList) // Platform should still be in platform ls.
         .then(function() {
             // And now remove it with --save.
-            return cordova.raw.platform('rm', ['android', 'ios'], {'save':true, 'fetch':true});
+            return cordova.raw.platform('rm', ['android', 'browser'], {'save':true, 'fetch':true});
         }).then(function() {
             // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             // Checking that the platform removed is in not in the platforms key.
             expect(pkgJson.cordova.platforms.indexOf('android')).toEqual(-1);
-            expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(-1);
+            expect(pkgJson.cordova.platforms.indexOf('browser')).toEqual(-1);
             // Dependencies are removed.
             expect(pkgJson.dependencies['cordova-android']).toBeUndefined();
-            expect(pkgJson.dependencies['cordova-ios']).toBeUndefined();
+            expect(pkgJson.dependencies['cordova-browser']).toBeUndefined();
             // Platforms are removed from config.xml.
             var cfg4 = new ConfigParser(configXmlPath);
             engines = cfg4.getEngines();
@@ -445,6 +448,6 @@ describe('platform end-to-end with --save', function () {
         .fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
-    }, 30000);
+    }, TIMEOUT);
 });
 

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/platform.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/platform.spec.js b/cordova-lib/spec-cordova/platform.spec.js
index 52b4362..763ba9c 100644
--- a/cordova-lib/spec-cordova/platform.spec.js
+++ b/cordova-lib/spec-cordova/platform.spec.js
@@ -220,7 +220,7 @@ describe('platform add plugin rm end-to-end', function () {
         cordova.raw.create('hello')
         .then(function() {
             process.chdir(project);
-            return cordova.raw.platform('add', 'ios@latest');
+            return cordova.raw.platform('add', 'browser@latest');
         })
         .then(function() {
             return cordova.raw.plugin('add', 'cordova-plugin-media');
@@ -268,15 +268,15 @@ describe('platform add and remove --fetch', function () {
         cordova.raw.create('helloFetch')
         .then(function() {
             process.chdir(project);
-            return cordova.raw.platform('add', 'ios', {'fetch':true});
+            return cordova.raw.platform('add', 'browser', {'fetch':true});
         })
         .then(function() {
-            expect(path.join(nodeModulesDir, 'cordova-ios')).toExist();
-            expect(path.join(platformsDir, 'ios')).toExist();
+            expect(path.join(nodeModulesDir, 'cordova-browser')).toExist();
+            expect(path.join(platformsDir, 'browser')).toExist();
             return cordova.raw.platform('add', 'android', {'fetch':true});
         })
         .then(function() {
-            expect(path.join(nodeModulesDir, 'cordova-android')).toExist();
+            expect(path.join(nodeModulesDir, 'cordova-browser')).toExist();
             expect(path.join(platformsDir, 'android')).toExist();
             //Tests finish before this command finishes resolving
             //return cordova.raw.platform('rm', 'ios', {'fetch':true});
@@ -319,7 +319,7 @@ describe('plugin add and rm end-to-end --fetch', function () {
         cordova.raw.create('hello3')
         .then(function() {
             process.chdir(project);
-            return cordova.raw.platform('add', 'ios', {'fetch': true});
+            return cordova.raw.platform('add', 'browser', {'fetch': true});
         })
         .then(function() {
             return cordova.raw.plugin('add', 'cordova-plugin-media', {'fetch': true});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/plugin_fetch.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/plugin_fetch.spec.js b/cordova-lib/spec-cordova/plugin_fetch.spec.js
index 8673484..eab2768 100644
--- a/cordova-lib/spec-cordova/plugin_fetch.spec.js
+++ b/cordova-lib/spec-cordova/plugin_fetch.spec.js
@@ -186,12 +186,12 @@ describe('plugin fetching version selection', function() {
             '<1.0.0': { 'cordova-android': '<2.0.0' },
             '1.0.0' : { 'cordova-android': '>2.0.0' },
             '1.7.0' : { 'cordova-android': '>4.0.0' },
-            '<2.3.0': { 'cordova-android': '<6.0.0' },
-            '2.3.0' : { 'cordova-android': '6.0.0' }
+            '<2.3.0': { 'cordova-android': '<6.1.1' },
+            '2.3.0' : { 'cordova-android': '6.1.1' }
         };
 
         var after = getWarningCheckCallback(done, [
-            getPlatformRequirement('6.0.0')
+            getPlatformRequirement('6.1.1')
         ]);
         testEngineWithProject(after, testEngine, '1.3.0');
     }, 6000);
@@ -201,14 +201,14 @@ describe('plugin fetching version selection', function() {
             '1.0.0' : { 'cordova-android': '>2.0.0' },
             '1.7.0' : { 'cordova-android': '>4.0.0' },
             '<2.3.0': {
-                'cordova-android': '<6.0.0',
+                'cordova-android': '<6.1.1',
                 'ca.filmaj.AndroidPlugin': '<1.0.0'
             },
-            '2.3.0' : { 'cordova-android': '6.0.0' }
+            '2.3.0' : { 'cordova-android': '6.1.1' }
         };
 
         var after = getWarningCheckCallback(done, [
-            getPlatformRequirement('6.0.0')
+            getPlatformRequirement('6.1.1')
         ]);
         testEngineWithProject(after, testEngine, null);
     });
@@ -313,12 +313,12 @@ describe('plugin fetching version selection', function() {
     it('Test 011 : should respect unreleased versions in constraints', function(done) {
         var testEngine = {
             '1.0.0' : { 'cordova-android': '3.1.0' },
-            '1.1.2' : { 'cordova-android': '6.0.0' },
-            '1.3.0' : { 'cordova-android': '6.0.0' }
+            '1.1.2' : { 'cordova-android': '6.1.1' },
+            '1.3.0' : { 'cordova-android': '6.1.1' }
         };
 
         var after = getWarningCheckCallback(done, [
-            getPlatformRequirement('6.0.0')
+            getPlatformRequirement('6.1.1')
         ]);
 
         testEngineWithProject(after, testEngine, '1.1.0');
@@ -329,11 +329,11 @@ describe('plugin fetching version selection', function() {
         var testEngine = {
             '0.0.0' : { 'ca.filmaj.AndroidPlugin': '1.2.0' },
             '1.1.3' : { 'ca.filmaj.AndroidPlugin': '<5.0.0 || >2.3.0' },
-            '2.3.0' : { 'ca.filmaj.AndroidPlugin': '6.0.0' }
+            '2.3.0' : { 'ca.filmaj.AndroidPlugin': '6.1.1' }
         };
 
         var after = getWarningCheckCallback(done, [
-            getPluginRequirement('6.0.0')
+            getPluginRequirement('6.1.1')
         ]);
 
         testEngineWithProject(after, testEngine, '2.0.0');
@@ -344,11 +344,11 @@ describe('plugin fetching version selection', function() {
         var testEngine = {
             '0.0.0' : { 'cordova': '>1.0.0' },
             '1.1.3' : { 'cordova': '<3.0.0 || >4.0.0' },
-            '2.3.0' : { 'cordova': '6.0.0' }
+            '2.3.0' : { 'cordova': '6.1.1' }
         };
 
         var after = getWarningCheckCallback(done, [
-            getCordovaRequirement('6.0.0')
+            getCordovaRequirement('6.1.1')
         ]);
 
         testEngineWithProject(after, testEngine, '1.1.0');

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-cordova/save.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/save.spec.js b/cordova-lib/spec-cordova/save.spec.js
index a340b17..8c0aa86 100644
--- a/cordova-lib/spec-cordova/save.spec.js
+++ b/cordova-lib/spec-cordova/save.spec.js
@@ -237,7 +237,7 @@ describe('(save flag)', function () {
                 expect(helpers.getEngineSpec(appPath, platformName)).toBe(null);
                 done();
             });
-        });
+        }, TIMEOUT);
 
         it('Test 009 : spec.6 should save local path as spec if added using only local path', function (done) {
             helpers.removeEngine(appPath, platformName);
@@ -412,7 +412,7 @@ describe('(save flag)', function () {
                 console.log(err.message);
                 done();
             });
-        }, TIMEOUT);
+        }, BIG_TIMEOUT);
 
         it('Test 018 : spec.14.1 should restore plugin with variables', function (done) {
             platform('add', platformLocalPathNewer)
@@ -439,7 +439,7 @@ describe('(save flag)', function () {
                 expect(true).toBe(false);
                 done();
             });
-        }, TIMEOUT);
+        }, BIG_TIMEOUT);
 
         it('Test 019 : spec.15 save git url as spec', function (done) {
             platform('add', platformLocalPathNewer)
@@ -677,7 +677,7 @@ describe('(save flag)', function () {
                 console.log(err.message);
                 done();
             });
-        });
+        }, BIG_TIMEOUT);
 
         it('Test 033 : spec.25 should install plugins already added to the project into platform when restoring it', function (done) {
             var fail = jasmine.createSpy('fail').and.callFake(function (err) {
@@ -700,7 +700,7 @@ describe('(save flag)', function () {
                 expect(fail).not.toHaveBeenCalled();
                 done();
             });
-        });
+        }, BIG_TIMEOUT);
     });
 
     describe('(cleanup)', function () {

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-plugman/fetch.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/fetch.spec.js b/cordova-lib/spec-plugman/fetch.spec.js
index ce72895..a37df67 100644
--- a/cordova-lib/spec-plugman/fetch.spec.js
+++ b/cordova-lib/spec-plugman/fetch.spec.js
@@ -22,7 +22,8 @@ var rewire  = require('rewire'),
     os      = require('os'),
     path    = require('path'),
     shell   = require('shelljs'),
-    realrm = shell.rm,
+    realrm  = shell.rm,
+    TIMEOUT = 60 * 1000,
     //xml_helpers = require('../src/util/xml-helpers'),
     metadata = require('../src/plugman/util/metadata'),
     temp    = path.join(os.tmpdir(), 'plugman', 'fetch'),
@@ -216,21 +217,21 @@ describe('fetch', function() {
                 expect(1).toBe(1);
                 done();
             });
-        }, 20000);
+        }, TIMEOUT);
         // this branch uses the old id
         it('Test 018 : should fetch from a branch', function(done) {
             wrapper(fetch('http://github.com/apache/cordova-plugin-device.git#cdvtest', temp, { expected_id: 'org.apache.cordova.device' }), done, function() {
                 expect(1).toBe(1);
                 done();
             });
-        }, 20000);
+        }, TIMEOUT);
         // this tag uses the new id
         it('Test 019 : should fetch from a tag', function(done) {
             wrapper(fetch('http://github.com/apache/cordova-plugin-device.git#r1.0.0', temp, { expected_id: 'cordova-plugin-device' }), done, function() {
                 expect(1).toBe(1);
                 done();
             });
-        }, 20000);
+        }, TIMEOUT);
     });
 
     describe('fetch recursive error CB-8809', function(){

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/spec-plugman/platforms/windows.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/platforms/windows.spec.js b/cordova-lib/spec-plugman/platforms/windows.spec.js
index 3528a2e..bd9c909 100644
--- a/cordova-lib/spec-plugman/platforms/windows.spec.js
+++ b/cordova-lib/spec-plugman/platforms/windows.spec.js
@@ -435,8 +435,6 @@ beforeEach(function () {
 
                 projects.forEach(function (project) {
                     var spy = spyOn(project, 'removeItemGroupElement');
-                    
-
                     if (projectsAddedTo.indexOf(path.basename(project.location)) > -1) {
                         projectsAddedToSpies.push(spy);
                     } else {

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9605411/cordova-lib/src/cordova/restore-util.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/restore-util.js b/cordova-lib/src/cordova/restore-util.js
index 044941c..6f4029b 100644
--- a/cordova-lib/src/cordova/restore-util.js
+++ b/cordova-lib/src/cordova/restore-util.js
@@ -47,6 +47,7 @@ function installPlatformsFromConfigXML(platforms, opts) {
     var modifiedConfigXML = false;
     var mergedPlatformSpecs = {};
     var key;
+    var installAllPlatforms = !platforms || platforms.length === 0;
 
     // Check if path exists and require pkgJsonPath.
     if(fs.existsSync(pkgJsonPath)) {
@@ -62,7 +63,6 @@ function installPlatformsFromConfigXML(platforms, opts) {
             // Combining arrays and checking duplicates.
             comboArray = pkgJsonPlatforms.slice();
         }
-
         engines = cfg.getEngines(projectHome);
         configPlatforms = engines.map(function(Engine) {
             var configPlatName = Engine.name;
@@ -88,7 +88,7 @@ function installPlatformsFromConfigXML(platforms, opts) {
 
         // No platforms to restore from either config.xml or package.json.
         if (comboArray.length <= 0) {
-           return Q('No platforms found in config.xml or package.json. Nothing to restore');
+            return Q('No platforms found in config.xml or package.json. Nothing to restore');
         }
 
         // If no package.json, don't continue.
@@ -182,17 +182,19 @@ function installPlatformsFromConfigXML(platforms, opts) {
     // CB-9278 : Run `platform add` serially, one platform after another
     // Otherwise, we get a bug where the following line: https://github.com/apache/cordova-lib/blob/0b0dee5e403c2c6d4e7262b963babb9f532e7d27/cordova-lib/src/util/npm-helper.js#L39
     // gets executed simultaneously by each platform and leads to an exception being thrown
+
     return promiseutil.Q_chainmap_graceful(comboArray, function(target) {
         var cwd = process.cwd();
         var platformsFolderPath = path.join(cwd,'platforms');
         var platformsInstalled = path.join(platformsFolderPath, target);
         if (target) {
+            var platformName = target;
             // Add the spec to the target
             if(mergedPlatformSpecs[target]) {
                 target = target + '@' + mergedPlatformSpecs[target];
             }
             // If the platform is already installed, no need to re-install it.
-            if (!fs.existsSync(platformsInstalled)) {
+            if (!fs.existsSync(platformsInstalled) && (installAllPlatforms || platforms.indexOf(platformName) > -1)) {
                 events.emit('log', 'Discovered platform \"' + target + '\" in config.xml or package.json. Adding it to the project');
                 return cordova.raw.platform('add', target, opts);
             }
@@ -255,7 +257,18 @@ function installPluginsFromConfigXML(args) {
         // Check to see which plugins are initially the same in pkg.json and config.xml.
         // Add missing plugin variables in package.json from config.xml.
         comboPluginIdArray.forEach(function(item) {
-            if(pluginIdConfig.includes(item)) {
+
+            function includeFunc(container, value) {
+                var returnValue = false;
+                var pos = container.indexOf(value);
+                if (pos >= 0) {
+                    returnValue = true;
+                }
+                return returnValue;
+            }
+            var result = includeFunc(pluginIdConfig, item);
+
+            if (result === true) {
                 configPlugin = cfg.getPlugin(item);
                 configPluginVariables = configPlugin.variables;
                 pkgJsonPluginVariables = comboObject[item];


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


[10/12] cordova-lib git commit: CB-12001: Added more restore tests for platform/plugin/spec.

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml
new file mode 100644
index 0000000..a0e37ad
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson9/config.xml
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="ios"/>
+    <plugin name="cordova-plugin-camera">
+        <variable name="variable_1" value="value_1" />
+    </plugin>
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json
new file mode 100644
index 0000000..4e654d4
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson9/package.json
@@ -0,0 +1,25 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "ios"
+        ],
+        "plugins": {
+            "cordova-plugin-camera": {
+                
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson9/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson9/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson9/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
index a0646d6..2b65077 100644
--- a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -29,7 +29,7 @@ var helpers = require('./helpers'),
 */
 
 // Use basePkgJson
-describe('platform end-to-end with --save', function () {
+describe('tests platform/spec restore with --save', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
     var results;
@@ -66,6 +66,78 @@ describe('platform end-to-end with --save', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
         });
     }
+
+    /** Test#000 will check that when a platform is added with a spec, it will 
+    *   add to pkg.json with a '^' and to config.xml with a '~'. When prepare is run,
+    *   pkg.json will have no change and config.xml (first char) will change from a '~' to a '^'.
+    */
+    it('Test#000 : tests that the spec (~,^) is added and updated as expected in config.xml', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var pkgJson;
+        var iosPlatform = 'ios';
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var firstCharConfig;
+        var engines;
+        var engNames;
+        var engSpec;
+
+        emptyPlatformList().then(function() {
+            // Add the ios platform with save, fetch
+            return cordova.raw.platform('add', iosPlatform, {'save':true , 'fetch':true});
+        }).then(function() {
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Added to ios properly to pkg.json
+            expect(pkgJson.cordova.platforms).toBeDefined();
+            expect(pkgJson.cordova.platforms.indexOf(iosPlatform)).toBeGreaterThan(-1);
+            // When spec is added to pkg.json, first char is '^'.
+            expect(pkgJson.dependencies['cordova-'+iosPlatform].charAt(0)).toEqual('^');
+
+            var cfg = new ConfigParser(configXmlPath);
+            engines = cfg.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            engSpec = engines.map(function(elem) {
+                return elem.spec;
+            });
+            // Only ios platform added to config.xml
+            expect(engNames).toEqual([ 'ios' ]);
+            expect(engines.length === 1);
+            // When spec is added to config.xml, first char is '~'.
+            firstCharConfig = engSpec[0].charAt(0);
+            expect(firstCharConfig === '~');
+        }).then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        }).then(function() {
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // No changes to pkg.json spec for ios.
+            expect(pkgJson.dependencies['cordova-'+iosPlatform].charAt(0)).toEqual('^');
+            expect(pkgJson.cordova.platforms.indexOf(iosPlatform)).toBeGreaterThan(-1);
+            // Config.xml spec (first char) should change from '~' to '^'.
+            var cfg1 = new ConfigParser(configXmlPath);
+            engines = cfg1.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            engSpec = engines.map(function(elem) {
+                return elem.spec;
+            });
+            firstCharConfig = engSpec[0].charAt(0);
+            // When spec is added to config.xml, first char is '~'.
+            expect(firstCharConfig === '^');
+            expect(engNames).toEqual([ 'ios' ]);
+            expect(engines.length === 1);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+
     /** Test#001 will add a platform to package.json with the 'save' flag.
     *   It will remove it from platforms.json without the save flag.
     *   After running cordova prepare, that platform should be restored in the
@@ -109,10 +181,10 @@ describe('platform end-to-end with --save', function () {
             // Check that the platform was removed from the platforms.json
             expect(platformsJson[helpers.testPlatform]).toBeUndefined();
         }).then(function() {
-            // Run cordova prepare
+            // Run cordova prepare.
             return cordova.raw.prepare();
         }).then(function() {
-            // Delete any previous caches of platforms.json
+            // Delete any previous caches of platforms.json.
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
             // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json.
@@ -143,7 +215,7 @@ describe('platform end-to-end with --save', function () {
             // Add the 'ios' platform with --save
             return cordova.raw.platform('add',secondPlatformAdded, {'save':true});
         }).then(function() {
-            // Delete any previous caches of require(package.json) and (platforms.json)
+            // Delete any previous caches of require(package.json) and (platforms.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             delete require.cache[require.resolve(platformsFolderPath)];
@@ -152,7 +224,7 @@ describe('platform end-to-end with --save', function () {
             expect(pkgJson.cordova.platforms).toBeDefined();
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(0);
             expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(1);
-            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
+            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json.
             expect(platformsJson).toBeDefined();
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
             expect(platformsJson[secondPlatformAdded]).toBeDefined();
@@ -161,10 +233,10 @@ describe('platform end-to-end with --save', function () {
             // Remove helpers.testPlatform with --save.
             return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
         }).then(function() {
-            // Remove secondPlatformAdded without --save
+            // Remove secondPlatformAdded without --save.
             return cordova.raw.platform('rm', secondPlatformAdded);
         }).then(function() {
-            // Delete any previous caches of require(package.json) and (platformsJson)
+            // Delete any previous caches of require(package.json) and (platformsJson).
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
             delete require.cache[require.resolve(pkgJsonPath)];
@@ -172,14 +244,14 @@ describe('platform end-to-end with --save', function () {
             // Check that ONLY the platform removed without --save is still in (pkg.json) platforms key.
             expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(0);
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
-            // Check that both platforms were removed from the platforms.json
+            // Check that both platforms were removed from the platforms.json list.
             expect(platformsJson[helpers.testPlatform]).toBeUndefined();
             expect(platformsJson[secondPlatformAdded]).toBeUndefined();
         }).then(function() {
-            // Run cordova prepare
+            // Run cordova prepare.
             return cordova.raw.prepare();
         }).then(function() {
-            // Delete any previous caches of platformsJson
+            // Delete any previous caches of platformsJson.
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
             // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json
@@ -258,6 +330,164 @@ describe('platform end-to-end with --save', function () {
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
     },30000);
+
+    /** Test#017
+    *   When platform is added with url and fetch and restored with fetch, 
+    *   pkg.json and config.xml would add it to their files properly.
+    *   When prepare is run with fetch, platform should be installed.
+    */
+    it('Test#017 : test to make sure that platform url is added and restored properly', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        var pkgJson;
+        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
+        var platformsJson;
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var androidPlatform = 'android';
+        var engines;
+        var engNames;
+        var engSpec;
+
+        emptyPlatformList().then(function() {
+            // Add platform with save and fetch
+            return cordova.raw.platform('add', 'https://github.com/apache/cordova-android', {'save':true, 'fetch':true});
+        }).then(function() {
+            // Check that platform was added to config.xml successfully.
+            var cfg = new ConfigParser(configXmlPath);
+            engines = cfg.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            engSpec = engines.map(function(elem) {
+                return elem.spec;
+            });
+            expect(engNames).toEqual([ androidPlatform]);
+            expect(engSpec).toEqual([ 'https://github.com/apache/cordova-android' ]);
+            // Check that platform was added to pkg.json successfully.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson.cordova.platforms.indexOf('android')).toBeDefined();
+            expect(pkgJson.dependencies['cordova-android']).toEqual('git+https://github.com/apache/cordova-android.git');
+            // Check that platform was added to platforms list successfully.
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            expect(platformsJson[androidPlatform]).toBeDefined();
+        }).then(function() {
+            // Remove platform without --save.
+            return cordova.raw.platform('rm', androidPlatform, {'fetch':true});
+        }).then(function() {
+            // Platform in pkg.json should still be there.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson.cordova.platforms.indexOf('android')).toBeDefined();
+            expect(pkgJson.dependencies['cordova-android']).toEqual('git+https://github.com/apache/cordova-android.git');
+            // Platform in platforms.json should not be there.
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            expect(platformsJson[androidPlatform]).toBeUndefined();
+        }).then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare({'fetch':true});
+        }).then(function() {
+            // Check config.xml for spec modification.
+            var cfg3 = new ConfigParser(configXmlPath);
+            engines = cfg3.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            engSpec = engines.map(function(elem) {
+                return elem.spec;
+            });
+            expect(engNames).toEqual([ 'android' ]);
+            expect(engSpec).toEqual([ 'git+https://github.com/apache/cordova-android.git' ]);
+            // No change to pkg.json.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson.cordova.platforms.indexOf('android')).toBeDefined();
+            expect(pkgJson.dependencies['cordova-android']).toEqual('git+https://github.com/apache/cordova-android.git');
+            // Check that platform was restored to platform.json list successfully.
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            expect(platformsJson[androidPlatform]).toBeDefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+
+    /** Test#018
+    *   When plugin is added with url and fetch and restored with fetch, 
+    *   pkg.json and config.xml would add it to their files properly.
+    *   When prepare is run with fetch, plugin should be installed.
+    */
+    it('Test#018 : test to make sure that plugin url is added and restored properly', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        var pkgJson;
+        var pluginsFolderPath = path.join(cwd,'plugins');
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var configPlugins;
+        var configPlugin;
+
+        emptyPlatformList().then(function() {
+            // Add plugin with save and fetch.
+            return cordova.raw.plugin('add', ['https://github.com/apache/cordova-plugin-splashscreen'], {'save':true, 'fetch':true});
+        }).then(function() {
+            // Plugin id and spec were added to config.xml successfully.
+            var cfg = new ConfigParser(configXmlPath);
+            configPlugins = cfg.getPluginIdList();
+            configPlugin = cfg.getPlugin(configPlugins);
+            expect(configPlugin.spec).toEqual('https://github.com/apache/cordova-plugin-splashscreen');
+            expect(configPlugin.name).toEqual('cordova-plugin-splashscreen');
+            // Plugin was added to pkg.json successfully in plugin list and dependencies.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson.dependencies).toEqual({ 'cordova-plugin-splashscreen': 'git+https://github.com/apache/cordova-plugin-splashscreen.git' });
+            expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
+            // Plugin was added to installed plugin list successfully.
+            expect(path.join(pluginsFolderPath, 'cordova-plugin-splashscreen')).toExist();
+        }).then(function() {
+            // Remove plugin without --save.
+            return cordova.raw.plugin('rm', 'cordova-plugin-splashscreen', {'fetch':true});
+        }).then(function() {
+            // Plugin id and spec are still in config.xml.
+            var cfg2 = new ConfigParser(configXmlPath);
+            configPlugins = cfg2.getPluginIdList();
+            configPlugin = cfg2.getPlugin(configPlugins);
+            expect(configPlugin.spec).toEqual('https://github.com/apache/cordova-plugin-splashscreen');
+            expect(configPlugin.name).toEqual('cordova-plugin-splashscreen');
+            // Plugin still in pkg.json plugin list and dependencies.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson.dependencies).toEqual({ 'cordova-plugin-splashscreen': 'git+https://github.com/apache/cordova-plugin-splashscreen.git' });
+            expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
+            // Plugin was removed from the installed plugin list successfully.
+            expect(path.join(pluginsFolderPath, 'cordova-plugin-splashscreen')).not.toExist();
+        }).then(function() {
+            // Add platform (so that prepare can run).
+            return cordova.raw.platform('add', 'ios', {'save':true});
+        }).then(function() {
+            // Run cordova prepare with fetch.
+            return cordova.raw.prepare({'fetch':true});
+        }).then(function() {
+            // Config.xml spec is modified.
+            var cfg3 = new ConfigParser(configXmlPath);
+            configPlugins = cfg3.getPluginIdList();
+            configPlugin = cfg3.getPlugin(configPlugins);
+            expect(configPlugin.spec).toEqual('git+https://github.com/apache/cordova-plugin-splashscreen.git');
+            expect(configPlugin.name).toEqual('cordova-plugin-splashscreen');
+            // Pkg.json splashscreen dependency has no changes.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect (pkgJson.dependencies['cordova-plugin-splashscreen']).toEqual('git+https://github.com/apache/cordova-plugin-splashscreen.git');
+            expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
+            // Plugin was restored and added to installed plugin list successfully.
+            expect(path.join(pluginsFolderPath, 'cordova-plugin-splashscreen')).toExist();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
 });
 
 // Use basePkgJson6 because pkg.json and config.xml contain only android
@@ -306,9 +536,9 @@ describe('files should not be modified if their platforms are identical', functi
             return elem.name;
         });
         var configEngArray = engNames.slice();
-        // Pkg.json and config.xml contain only android at this point (basePkgJson6)
+        // Pkg.json and config.xml contain only android at this point (basePkgJson6).
         emptyPlatformList().then(function() {
-            // Run cordova prepare
+            // Run cordova prepare.
             return cordova.raw.prepare();
         }).then(function() {
             var cfg2 = new ConfigParser(configXmlPath);
@@ -319,10 +549,10 @@ describe('files should not be modified if their platforms are identical', functi
             configEngArray = engNames.slice();
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Expect android to be in both pkg.json and config.xml
+            // Expect android to be in both pkg.json and config.xml.
             expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
             expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
-            // Expect pkg.json and config.xml to have only 1 element each
+            // Expect pkg.json and config.xml to have only 1 element each.
             expect(configEngArray.length === 1);
             expect(pkgJson.cordova.platforms.length === 1);
         }).fail(function(err) {
@@ -364,9 +594,11 @@ describe('update pkg.json to include platforms in config.xml', function () {
     }
     /** Test#005 will check the platform list in package.json and config.xml. 
     *   When config.xml has 'android and ios' and pkg.json only contains 'android', run cordova
-    *   and pkg.json is updated to include 'ios'.
+    *   and pkg.json is updated to include 'ios'. This test will also check that pkg.json
+    *   is updated with the correct spec/dependencies when restored. Checks that specs are
+    *   added properly, too.
     */
-    it('Test#005 : if config.xml has android & ios platforms and pkg.json has android, update pkg.json to also include ios', function(done) {
+    it('Test#005 : if config.xml has android & ios platforms and pkg.json has android, update pkg.json to also include ios with spec', function(done) {
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var cfg = new ConfigParser(configXmlPath);
@@ -378,28 +610,36 @@ describe('update pkg.json to include platforms in config.xml', function () {
             return elem.name;
         });
         var configEngArray = engNames.slice();
+        var androidPlatform = 'android';
+        var iosPlatform = 'ios';
        
-        // Config.xml contains(android & ios) and pkg.json contains android (basePkgJson5)
-        expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
-        expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
-        // pkg.json should not contain 'ios' platform before cordova prepare
-        expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(-1);
-        expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
+        // Config.xml contains(android & ios) and pkg.json contains android (basePkgJson5).
+        expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
+        expect(configEngArray.indexOf(iosPlatform)).toBeGreaterThan(-1);
+        // pkg.json should not contain 'ios' platform before cordova prepare.
+        expect(pkgJson.cordova.platforms.indexOf(iosPlatform)).toEqual(-1);
+        expect(pkgJson.cordova.platforms.indexOf(androidPlatform)).toBeGreaterThan(-1);
+        // pkg.json ios/android specs should be undefined.
+        expect(pkgJson.dependencies[iosPlatform]).toBeUndefined();
+        expect(pkgJson.dependencies[androidPlatform]).toBeUndefined();
         emptyPlatformList().then(function() {
             return cordova.raw.prepare();
         }).then(function() {
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Expect 'ios' to be added to pkg.json
-            expect(pkgJson.cordova.platforms.indexOf('ios')).toBeGreaterThan(-1);
-            // Expect 'android' to still be there in pkg.json
-            expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
-            // Expect both pkg.json and config.xml to each have both platforms in their arrays
+            // Expect 'ios' to be added to pkg.json.
+            expect(pkgJson.cordova.platforms.indexOf(iosPlatform)).toBeGreaterThan(-1);
+            // Expect 'android' to still be there in pkg.json.
+            expect(pkgJson.cordova.platforms.indexOf(androidPlatform)).toBeGreaterThan(-1);
+            // Expect both pkg.json and config.xml to each have both platforms in their arrays.
             expect(configEngArray.length === 2);
             expect(pkgJson.cordova.platforms.length === 2);
-            // No changes to config.xml
-            expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
-            expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
+            // No changes to config.xml.
+            expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
+            expect(configEngArray.indexOf(iosPlatform)).toBeGreaterThan(-1);
+            // Platform specs from config.xml have been added to pkg.json.
+            expect(pkgJson.dependencies['cordova-ios']).toEqual('^4.3.0');
+            expect(pkgJson.dependencies['cordova-android']).toEqual('6.0.0');
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -407,7 +647,7 @@ describe('update pkg.json to include platforms in config.xml', function () {
     },30000);
 });
 
-// Use basePkgJson3 as it has 'android' in config.xml
+// Use basePkgJson3 as it has 'android' in config.xml and pkg.json (no cordova key).
 describe('update empty package.json to match config.xml', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
@@ -454,13 +694,13 @@ describe('update empty package.json to match config.xml', function () {
         });
         var configEngArray = engNames.slice();
     
-        // Expect that pkg.json exists without a cordova key
+        // Expect that pkg.json exists without a cordova key.
         expect(pkgJson).toBeDefined();
         expect(pkgJson.cordova).toBeUndefined();
-        // Expect that config.xml contains only android at this point
+        // Expect that config.xml contains only android at this point.
         expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
         expect(configEngArray.length === 1);
-        // Run cordova prepare
+        // Run cordova prepare.
         cordova.raw.prepare();
         emptyPlatformList().then(function() {
             var cfg2 = new ConfigParser(configXmlPath);
@@ -469,14 +709,14 @@ describe('update empty package.json to match config.xml', function () {
                 return elem.name;
             });
             configEngArray = engNames.slice();
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Expect no change to config.xml
+            // Expect no change to config.xml.
             expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
-            // Expect cordova key and 'android' platform to be added to pkg.json
+            // Expect cordova key and 'android' platform to be added to pkg.json.
             expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
-            // Expect both pkg.json and config.xml to each have (only) android in their arrays
+            // Expect both pkg.json and config.xml to each have (only) android in their arrays.
             expect(configEngArray.length === 1);
             expect(pkgJson.cordova.platforms.length === 1);
         }).fail(function(err) {
@@ -486,7 +726,7 @@ describe('update empty package.json to match config.xml', function () {
     },30000);
 });
 
-// Use a new basePkgJson4 as pkg.json contains android/ios and config.xml contains android
+// Use a new basePkgJson4 as pkg.json contains android/ios and config.xml contains android.
 describe('update config.xml to include platforms in pkg.json', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
@@ -517,10 +757,11 @@ describe('update config.xml to include platforms in pkg.json', function () {
         });
     }
     /** Test#007 will check the platform list in package.json and config.xml. 
-    *   When packge.json has 'android and ios' and config.xml only contains 'android', run cordova
-    *   and config.xml is updated to include 'ios'.
+    *   When package.json has 'android and ios' and config.xml only contains 'android', run cordova
+    *   and config.xml is updated to include 'ios'. Also, if there is a specified spec in pkg.json,
+    *   it should be added to config.xml during restore.
     */
-    it('Test#007 : if pkgJson has android & ios platforms and config.xml has android, update config to also include ios', function(done) {
+    it('Test#007 : if pkgJson has android & ios platforms and config.xml has android, update config to also include ios and spec', function(done) {
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var cfg1 = new ConfigParser(configXmlPath);
@@ -537,11 +778,13 @@ describe('update config.xml to include platforms in pkg.json', function () {
         expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
         expect(configEngArray.indexOf('ios')).toEqual(-1);
         expect(configEngArray.length === 1);
-       
+        // Pkg.json has cordova-ios in its dependencies.
+        expect(pkgJson.dependencies).toEqual({ 'cordova-android' : '^3.1.0', 'cordova-ios' : '^4.3.0' });
         emptyPlatformList().then(function() {
-            // Run cordova prepare
+            // Run cordova prepare.
             return cordova.raw.prepare();
         }).then(function() {
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
@@ -550,12 +793,18 @@ describe('update config.xml to include platforms in pkg.json', function () {
                 return elem.name;
             });
             configEngArray = engNames.slice();
-            // Expect 'ios' to be added to config.xml
+            // Expect 'ios' to be added to config.xml.
             expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
-            // Expect 'android' to still be in config.xml
+            // Expect 'android' to still be in config.xml.
             expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
-            // Expect config.xml array to have 2 elements (platforms);
+            // Expect config.xml array to have 2 elements (platforms).
             expect(configEngArray.length === 2);
+            // Check to make sure that 'ios' spec was added properly.
+            expect(engines).toEqual([ { name: 'android', spec: '^3.1.0' },{ name: 'ios', spec: '^4.3.0' } ]);
+            // No change to pkg.json dependencies.
+            expect(pkgJson.dependencies).toEqual({ 'cordova-android' : '^3.1.0', 'cordova-ios' : '^4.3.0' });
+            expect(pkgJson.dependencies['cordova-android']).toEqual('^3.1.0');
+            expect(pkgJson.dependencies['cordova-ios']).toEqual('^4.3.0');
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -610,36 +859,39 @@ describe('update config.xml to use the variable found in pkg.json', function ()
         var configPlugin = cfg1.getPlugin(configPlugins);
         var configPluginVariables = configPlugin.variables;
         var pkgJson = require(pkgJsonPath);
+        var pluginsFolderPath = path.join(cwd,'plugins');
 
-        // Expect that pkg.json exists with 1 plugin, 1 variable, and a different value (json)
+        // Expect that pkg.json exists with 1 plugin, 1 variable, and a different value ('json').
         expect(pkgJson.cordova.plugins).toBeDefined();
         expect(Object.keys(pkgJson.cordova.plugins).length === 1);
         expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
         expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'json' });
-        // Expect that config.xml exists with 1 plugin, 1 variable, but a different value (config)
+        // Expect that config.xml exists with 1 plugin, 1 variable, but a different value ('config').
         expect(configPlugin.name).toEqual('cordova-plugin-camera');
         expect(configPluginVariables).toEqual({ variable_1: 'config' });
         expect(Object.keys(configPlugin).length === 1);
 
         emptyPlatformList().then(function() {
-            // Run cordova prepare
+            // Run cordova prepare.
             return cordova.raw.prepare();
         }).then(function() {
-            // // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
             configPlugins = cfg2.getPluginIdList();
             configPlugin = cfg2.getPlugin(configPlugins);
             configPluginVariables = configPlugin.variables;
-            // Expect that pkg.json exists with 1 plugin, 1 variable, and the pkg.json value
+            // Expect that pkg.json exists with 1 plugin, 1 variable, and the pkg.json value.
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
             expect(Object.keys(pkgJson.cordova.plugins).length === 1);
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'json' });
-            // Expect that config.xml exists with 1 plugin, 1 variable and pkg.json's value
+            // Expect that config.xml exists with 1 plugin, 1 variable and pkg.json's value.
             expect(configPlugin.name).toEqual('cordova-plugin-camera');
             expect(configPluginVariables).toEqual({ variable_1: 'json' });
             expect(Object.keys(configPlugin).length === 1);
+            // Expect that the camera plugin is restored.
+            expect(path.join(pluginsFolderPath, 'cordova-plugin-camera')).toExist();
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -685,43 +937,47 @@ describe('update pkg.json to include plugin and variable found in config.xml', f
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var pkgJsonPath = path.join(cwd,'package.json');
+        // Delete any previous caches of require(package.json).
         delete require.cache[require.resolve(pkgJsonPath)];
         var cfg1 = new ConfigParser(configXmlPath);
         var configPlugins = cfg1.getPluginIdList();
         var configPlugin = cfg1.getPlugin(configPlugins);
         var configPluginVariables = configPlugin.variables;
         var pkgJson = require(pkgJsonPath);
+        var pluginsFolderPath12 = path.join(cwd,'plugins');
 
-        // Expect that pkg.json exists with 1 plugin
+        // Expect that pkg.json exists with 1 plugin without a variable.
         expect(pkgJson.cordova.plugins).toBeDefined();
         expect(Object.keys(pkgJson.cordova.plugins).length === 1);
         expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
         expect(pkgJson.cordova.plugins).toEqual({ 'cordova-plugin-camera': {} });
 
-        // Expect that config.xml exists with 1 plugin and 1 variable
+        // Expect that config.xml exists with 1 plugin and 1 variable with value_1.
         expect(configPlugin.name).toEqual('cordova-plugin-camera');
         expect(configPluginVariables).toEqual({ variable_1: 'value_1' });
         expect(Object.keys(configPlugin).length === 1);
 
         emptyPlatformList().then(function() {
-            // Run cordova prepare
+            // Run cordova prepare.
             return cordova.raw.prepare();
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
             configPlugins = cfg2.getPluginIdList();
             configPlugin = cfg2.getPlugin(configPlugins);
             configPluginVariables = configPlugin.variables;
-            // Expect that pkg.json exists with 1 plugin, 1 variable, and 1 value
+            // Expect that pkg.json exists with 1 plugin, 1 variable, and 1 value.
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
             expect(Object.keys(pkgJson.cordova.plugins).length === 1);
             expect(pkgJson.cordova.plugins).toEqual({ 'cordova-plugin-camera': { variable_1: 'value_1' } });
-            // Expect that config.xml exists with 1 plugin, 1 variable and 1 value
+            // Expect that config.xml exists with 1 plugin, 1 variable and 1 value.
             expect(configPlugin.name).toEqual('cordova-plugin-camera');
             expect(configPluginVariables).toEqual({ variable_1: 'value_1' });
             expect(Object.keys(configPlugin).length === 1);
+            //Expect camera to be restored and in the installed plugin list.
+            expect(path.join(pluginsFolderPath12, 'cordova-plugin-camera')).toExist();
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -729,9 +985,9 @@ describe('update pkg.json to include plugin and variable found in config.xml', f
     },30000);
 });
 
-// Use basePkgJson10 as pkg.json contains (camera plugin: var 1/var 2, splashscreen plugin) 
-// and config contains (camera plugin: var 3, value 1, device plugin)
-describe('update pkg.json AND config.xml to include all plugins and merge variables', function () {
+// Use basePkgJson10 as pkg.json contains (camera plugin: var 1/var 2, splashscreen plugin). 
+// and config contains (camera plugin: var 3, value 1, device plugin).
+describe('update pkg.json AND config.xml to include all plugins and merge unique variables', function () {
     var tmpDir = helpers.tmpDir('plugin_test_pkgjson');
     var project = path.join(tmpDir, 'project');
     var results;
@@ -775,13 +1031,14 @@ describe('update pkg.json AND config.xml to include all plugins and merge variab
         var pkgJson = require(pkgJsonPath);
         var configPlugin;
         var configPluginVariables;
+        var pluginsFolderPath13 = path.join(cwd,'plugins');
 
-        // Config.xml has 2 plugins and does not have device yet
+        // Config.xml has 2 plugins and does not have device plugin yet.
         expect(Object.keys(configPlugins).length === 2);
         expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(-1);
         expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
         expect(configPlugins.indexOf('cordova-plugin-splashscreen')).toEqual(1);
-        // Config.xml camera plugin has var_3,value_3 and splashscreen has 0 variables
+        // Config.xml camera plugin has var_3,value_3 and splashscreen has 0 variables.
         for (var i = 0; i < configPlugins.length; i++) {
             configPlugin = cfg1.getPlugin(configPlugins[i]);
             configPluginVariables = configPlugin.variables;
@@ -798,31 +1055,30 @@ describe('update pkg.json AND config.xml to include all plugins and merge variab
         expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
         expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
         expect(pkgJson.cordova.plugins['cordova-plugin-device']).toBeDefined();
-        // Splashscreen has no variables and camera has var 1 and var 2 and device has var1, value1
+        // Splashscreen has no variables and camera has var_1 and var_2 and device has var_1, val_1.
         expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toEqual({});
         expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: ' ', variable_2: ' ' });
         expect(pkgJson.cordova.plugins['cordova-plugin-device']).toEqual({ variable_1: 'value_1' });
 
         emptyPlatformList().then(function() {
-            // Run cordova prepare
+            // Run cordova prepare.
             return cordova.raw.prepare();
          }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
             configPlugins = cfg2.getPluginIdList();
-
-            // Check to make sure that variables were added as expected
+            // Check to make sure that variables were added as expected.
             for (var i = 0; i < configPlugins.length; i++) {
                 configPlugin = cfg2.getPlugin(configPlugins[i]);
                 configPluginVariables = configPlugin.variables;
-                // Config.xml camera variables have been merged, no duplicates
+                // Config.xml camera variables have been merged, no duplicates.
                 if(configPlugin.name === 'cordova-plugin-camera') {
                     expect(configPluginVariables).toEqual({ variable_1: ' ', 
                     variable_2: ' ', variable_3: 'value_3' });
                 }
-                // Expect that device has var1, val1 and splashscreen has 0 var
+                // Expect that device has var1, val1 and splashscreen has 0 var.
                 if(configPlugin.name === 'cordova-plugin-device') {
                     expect(configPluginVariables).toEqual({ variable_1: 'value_1'});
                 }
@@ -830,17 +1086,21 @@ describe('update pkg.json AND config.xml to include all plugins and merge variab
                     expect(configPluginVariables).toEqual({});
                 }
             }
-            // Expect pkg.json to have the variables from config.xml
+            // Expect pkg.json to have the variables from config.xml.
             expect(Object.keys(pkgJson.cordova.plugins).length === 3);
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
             expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
             expect(pkgJson.cordova.plugins['cordova-plugin-device']).toBeDefined();
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: ' ', variable_2: ' ', variable_3: 'value_3' });
-            // Expect config.xml to have the plugins from pkg.json
+            // Expect config.xml to have the plugins from pkg.json.
             expect(Object.keys(configPlugins).length === 3);
             expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
             expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(1);
             expect(configPlugins.indexOf('cordova-plugin-splashscreen')).toEqual(2);
+            // Expect all 3 plugins to be restored.
+            expect(path.join(pluginsFolderPath13, 'cordova-plugin-device')).toExist();
+            expect(path.join(pluginsFolderPath13, 'cordova-plugin-camera')).toExist();
+            expect(path.join(pluginsFolderPath13, 'cordova-plugin-splashscreen')).toExist();
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -849,7 +1109,7 @@ describe('update pkg.json AND config.xml to include all plugins and merge variab
 });
 
 // Use basePkgJson11 as pkg.json contains(splashscreen plugin, camera plugin: var1, value1, var2, value2) and
-// config.xml contains (device plugin, camera plugin: var1, value 1, var2, value 2)
+// config.xml contains (device plugin, camera plugin: var1, value 1, var2, value 2).
 describe('update pkg.json AND config.xml to include all plugins/merge variables and check for duplicates', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
@@ -894,6 +1154,7 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
         var pkgJson = require(pkgJsonPath);
         var configPlugin;
         var configPluginVariables;
+        var pluginsFolderPath14 = path.join(cwd,'plugins');
 
         // Config.xml initially has the camera and device plugin and NO splashscreen
         expect(Object.keys(configPlugins).length === 2);
@@ -906,15 +1167,21 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
             configPluginVariables = configPlugin.variables;
             if(configPlugin.name === 'cordova-plugin-camera') {
                 expect(configPluginVariables).toEqual({ variable_1: 'value_1', variable_2: 'value_2' });
+                // Config.xml camera plugin has the spec ~2.2.0
+                expect(configPlugin.spec).toEqual('~2.2.0');
             }
             if(configPlugin.name === 'cordova-plugin-device') {
                 expect(configPluginVariables).toEqual({});
+                // Config.xml device plugin has the spec ~1.0.0
+                expect(configPlugin.spec).toEqual('~1.0.0');
             }
         }
         // Expect that pkg.json exists with 2 plugins
         expect(pkgJson.cordova.plugins).toBeDefined();
         expect(Object.keys(pkgJson.cordova.plugins).length === 2);
         expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+        // Pkg.json camera plugin's spec is ^2.3.0
+        expect(pkgJson.dependencies['cordova-plugin-camera']).toEqual('^2.3.0');
         expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
         // Pkg.json does not have device yet
         expect(pkgJson.cordova.plugins['cordova-plugin-device']).toBeUndefined();
@@ -941,10 +1208,14 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
                 if(configPlugin.name === 'cordova-plugin-camera') {
                     expect(configPluginVariables).toEqual({ variable_1: 'value_1',
                     variable_3: 'value_3', variable_2: 'value_2' });
+                    // Config.xml plugin spec should be updated to ^2.3.0
+                    expect(configPlugin.spec).toEqual('^2.3.0');
                 }
                 // Expect that splashscreen and device have 0 variables
                 if(configPlugin.name === 'cordova-plugin-device') {
                     expect(configPluginVariables).toEqual({});
+                    // Config.xml device plugin still has the spec ~1.0.0
+                    expect(configPlugin.spec).toEqual('~1.0.0');
                 }
                 if(configPlugin.name === 'cordova-plugin-splashscreen') {
                     expect(configPluginVariables).toEqual({});
@@ -955,11 +1226,13 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
             expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
             expect(configPlugins.indexOf('cordova-plugin-splashscreen')).toEqual(1);
             expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(2);
-            // Pkg.json has all 3 plugins
+            // Pkg.json has all 3 plugins with the correct specs
             expect(Object.keys(pkgJson.cordova.plugins).length === 3);
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+            expect(pkgJson.dependencies['cordova-plugin-camera']).toEqual('^2.3.0');
             expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
             expect(pkgJson.cordova.plugins['cordova-plugin-device']).toBeDefined();
+            expect(pkgJson.dependencies['cordova-plugin-device']).toEqual('~1.0.0');
             // Expect that splashscreen and device have 0 variables
             expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toEqual({});
             expect(pkgJson.cordova.plugins['cordova-plugin-device']).toEqual({});
@@ -967,6 +1240,10 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
             // from pkg.json to the camera plugin
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'value_1',
             variable_3: 'value_3', variable_2: 'value_2' });
+            // Expect that all 3 plugins are restored and in the installed list.
+            expect(path.join(pluginsFolderPath14, 'cordova-plugin-camera')).toExist();
+            expect(path.join(pluginsFolderPath14, 'cordova-plugin-splashscreen')).toExist();
+            expect(path.join(pluginsFolderPath14, 'cordova-plugin-device')).toExist();
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -974,7 +1251,7 @@ describe('update pkg.json AND config.xml to include all plugins/merge variables
     },30000);
 });
 
-// Use basePkgJson12 as config.xml has 0 plugins and pkg.json has 1
+// Use basePkgJson12 as config.xml has 0 plugins and pkg.json has 1.
 describe('update config.xml to include the plugin that is in pkg.json', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
@@ -1005,54 +1282,68 @@ describe('update config.xml to include the plugin that is in pkg.json', function
         });
     }
     /** Test#015 will check the plugin/variable list in package.json and config.xml. 
-    *   When config has 0 plugins, it will get updated with the plugins from
-    *   pkg.json.
+    *   When config has 0 plugins and is restored, the plugins will be restored with the 
+    *   pkg.json plugins and with the spec from pkg.json dependencies.
     */
     it('Test#015 : update config.xml to include all plugins/variables from pkg.json', function(done) {
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var pkgJsonPath = path.join(cwd,'package.json');
         delete require.cache[require.resolve(pkgJsonPath)];
-        var cfg1 = new ConfigParser(configXmlPath);
-        var configPlugins = cfg1.getPluginIdList();
+        var cfg = new ConfigParser(configXmlPath);
+        var configPlugins = cfg.getPluginIdList();
+        var configPluginSpecs = cfg.getPlugin(configPlugins);
         var pkgJson = require(pkgJsonPath);
         var configPlugin;
         var configPluginVariables;
-        // Config.xml is initially empty and has no plugins
+        var pluginsFolderPath15 = path.join(cwd,'plugins');
+
+        // Config.xml is initially empty and has no plugins.
         expect(Object.keys(configPlugins).length === 0);
-        // Expect that pkg.json exists with 1 plugin
+        // Expect that pkg.json exists with 1 plugin.
         expect(pkgJson.cordova.plugins).toBeDefined();
         expect(Object.keys(pkgJson.cordova.plugins).length === 1);
         expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
-        // Pkg.json camera plugin has var1, value 1
+        // Pkg.json camera plugin has var_1, val_1.
         expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'value_1' });
+        // Pkg.json has '^2.3.0' spec for camera plugin.
+        expect(pkgJson.dependencies).toEqual({ 'cordova-plugin-camera': '^2.3.0' });
 
         emptyPlatformList().then(function() {
-            // Run cordova prepare
+            // Run cordova prepare.
             return cordova.raw.prepare();
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
             configPlugins = cfg2.getPluginIdList();
+            configPluginSpecs = cfg2.getPlugin(configPlugins);
 
-            // Check to make sure that the variables were added as expected
+            // Check to make sure that the variables were added as expected to config.xml.
             for (var i = 0; i < configPlugins.length; i++) {
                 configPlugin = cfg2.getPlugin(configPlugins[i]);
                 configPluginVariables = configPlugin.variables;
-                // Config.xml camera variables have been added
+                // Pkg.json dependencies should be the same.
+                expect(pkgJson.dependencies).toEqual({ 'cordova-plugin-camera': '^2.3.0' });
+                // Config.xml camera variables have been added.
                 if(configPlugin.name === 'cordova-plugin-camera') {
                     expect(configPluginVariables).toEqual({ variable_1: 'value_1' });
+                    // Check that the camera plugin has the correct spec and has been updated in config.xml
+                    expect(configPlugin.spec).toEqual('^2.3.0');
                 }
             }
-            // Config.xml now has the camera plugin
+            // Check to make sure that the config.xml spec was overwritten by the pkg.json one.
+            expect(configPluginSpecs).toEqual( { name: 'cordova-plugin-camera',spec: '^2.3.0',variables: { variable_1: 'value_1' } });
+            // Camera plugin gets added to config.xml.
             expect(Object.keys(configPlugins).length === 1);
             expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
-            // No changes to pkg.json
+            // No changes to pkg.json.
             expect(Object.keys(pkgJson.cordova.plugins).length === 1);
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'value_1' });
+            // Check if the camera plugin is in the installed list.
+            expect(path.join(pluginsFolderPath15, 'cordova-plugin-camera')).toExist();
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -1060,4 +1351,148 @@ describe('update config.xml to include the plugin that is in pkg.json', function
     },30000);
 });
 
+// Use basePkgJson13 - does NOT have a package.json
+describe('platforms and plugins should be restored with config.xml even without a pkg.json', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        // Use basePkgJson6 because pkg.json and config.xml contain only android
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson13'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson13'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+    /** Test#016 will check that cordova prepare will still restore the correct
+    *   platforms and plugins even without package.json file.
+    */
+    it('Test#016 : platforms and plugins should be restored with config.xml even without a pkg.json', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var cfg1 = new ConfigParser(configXmlPath);
+        var engines = cfg1.getEngines();
+        var engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        var configEngArray = engNames.slice();
+        var configPlugins = cfg1.getPluginIdList();
+        var platformsFolderPath1 = path.join(cwd,'platforms/platforms.json');
+        var pluginsFolderPath16 = path.join(cwd,'plugins');
+        var platformsJson;
+        var androidPlatform = 'android';
+        var iosPlatform = 'ios';
+
+        // Pkg.json does not exist.
+        expect(path.join(cwd,'package.json')).not.toExist();
+        // Config.xml contains only contains 'ios' at this point (basePkgJson13).
+        expect(configEngArray.length === 1);
+        // Config.xml contains only 1 plugin at this point.
+        expect(Object.keys(configPlugins).length === 1);
+
+        emptyPlatformList().then(function() {
+        // Run cordova prepare.
+        return cordova.raw.platform('add', iosPlatform, {'save':true});
+        }).then(function () {
+        // Android and ios are in config.xml.
+        var cfg3 = new ConfigParser(configXmlPath);
+        engines = cfg3.getEngines();
+        engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        configEngArray = engNames.slice();
+        expect(configEngArray.length === 2);
+        // Delete previouc caches of (pkg.json).
+        delete require.cache[require.resolve(platformsFolderPath1)];
+        platformsJson = require(platformsFolderPath1);
+        // Android and ios should be installed
+        expect(platformsJson).toBeDefined();
+        expect(platformsJson[androidPlatform]).toBeDefined();
+        expect(platformsJson[iosPlatform]).toBeDefined();
+        // Remove android without --save.
+        return cordova.raw.platform('rm', [iosPlatform]);
+        }).then(function () {
+        // Android should not be in the installed list (only ios).
+        delete require.cache[require.resolve(platformsFolderPath1)];
+        platformsJson = require(platformsFolderPath1);
+        expect(platformsJson).toBeDefined();
+        expect(platformsJson[iosPlatform]).toBeUndefined();
+        expect(platformsJson[androidPlatform]).toBeDefined();
+        }).then(function () {
+        // Run cordova prepare.
+        return cordova.raw.prepare();
+        }).then(function() {
+        var cfg2 = new ConfigParser(configXmlPath);
+        engines = cfg2.getEngines();
+        engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        configEngArray = engNames.slice();
+        // Config.xml should have android and ios.
+        expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
+        expect(configEngArray.indexOf(iosPlatform)).toBeGreaterThan(-1);
+        expect(configEngArray.length === 2);
+        // Expect that android and ios were restored.
+        delete require.cache[require.resolve(platformsFolderPath1)];
+        platformsJson = require(platformsFolderPath1);
+        expect(platformsJson[androidPlatform]).toBeDefined();
+        expect(platformsJson[iosPlatform]).toBeDefined();
+        }).then(function () {
+            //Check plugins.
+            var cfg5 = new ConfigParser(configXmlPath);
+            engines = cfg5.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            // Config.xml contains only one plugin.
+            expect(Object.keys(configPlugins).length === 1);
+            expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(0);
+            // Expect device plugin to be in the installed list.
+            expect(path.join(pluginsFolderPath16, 'cordova-plugin-device')).toExist();
+        }).then(function () {
+            // Remove plugin without save.
+            return cordova.raw.plugin('rm', 'cordova-plugin-device');
+        }).then(function () {
+            var cfg4 = new ConfigParser(configXmlPath);
+            engines = cfg4.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            // Config.xml plugins are the same.
+            expect(Object.keys(configPlugins).length === 1);
+            expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(0);
+            // Plugin should be removed from the installed list.
+            expect(path.join(pluginsFolderPath16, 'cordova-plugin-device')).not.toExist();
+        }).then(function () {
+            // Run cordova prepare.
+            return cordova.raw.prepare();
+        }).then(function () {
+        //  Plugin should be restored and returned to the installed list.
+            expect(path.join(pluginsFolderPath16, 'cordova-plugin-device')).toExist();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },60000);
+});
+
 


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


[05/12] cordova-lib git commit: CB-12001: Added support for platform/plugin & spec restore to sync config.xml and package.json

Posted by st...@apache.org.
CB-12001: Added support for platform/plugin & spec restore to sync config.xml and package.json


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/7df35cb0
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/7df35cb0
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/7df35cb0

Branch: refs/heads/master
Commit: 7df35cb0656af7cd428e5a07addc3874c60dc247
Parents: 197cba9
Author: audreyso <au...@adobe.com>
Authored: Tue Oct 25 17:05:12 2016 -0700
Committer: Audrey So <au...@apache.org>
Committed: Thu Jan 26 17:36:51 2017 -0800

----------------------------------------------------------------------
 .../fixtures/basePkgJson2/config.xml            |  14 +
 .../fixtures/basePkgJson2/merges/.svn           |   0
 .../fixtures/basePkgJson2/package.json          |  26 +
 .../fixtures/basePkgJson2/plugins/.svn          |   0
 .../fixtures/basePkgJson2/www/css/index.css     | 115 +++++
 .../fixtures/basePkgJson2/www/img/logo.png      | Bin 0 -> 21814 bytes
 .../fixtures/basePkgJson2/www/index.html        |  43 ++
 .../fixtures/basePkgJson2/www/js/index.js       |  49 ++
 .../fixtures/basePkgJson2/www/spec.html         |  68 +++
 .../fixtures/basePkgJson3/config.xml            |  15 +
 .../fixtures/basePkgJson3/package.json          |  15 +
 .../fixtures/basePkgJson3/plugins/.svn          |   0
 .../fixtures/basePkgJson3/www/css/index.css     | 115 +++++
 .../fixtures/basePkgJson3/www/index.html        |  43 ++
 .../fixtures/basePkgJson3/www/js/index.js       |  49 ++
 .../fixtures/basePkgJson3/www/spec.html         |  68 +++
 .../fixtures/basePkgJson4/config.xml            |  15 +
 .../fixtures/basePkgJson4/package.json          |  21 +
 .../fixtures/basePkgJson4/plugins/.svn          |   0
 .../fixtures/basePkgJson4/www/css/index.css     | 115 +++++
 .../fixtures/basePkgJson4/www/index.html        |  43 ++
 .../fixtures/basePkgJson4/www/js/index.js       |  49 ++
 .../fixtures/basePkgJson4/www/spec.html         |  68 +++
 .../fixtures/basePkgJson5/config.xml            |  16 +
 .../fixtures/basePkgJson5/package.json          |  20 +
 .../fixtures/basePkgJson5/plugins/.svn          |   0
 .../fixtures/basePkgJson5/www/css/index.css     | 115 +++++
 .../fixtures/basePkgJson5/www/index.html        |  43 ++
 .../fixtures/basePkgJson5/www/js/index.js       |  49 ++
 .../fixtures/basePkgJson5/www/spec.html         |  68 +++
 .../fixtures/basePkgJson6/config.xml            |  15 +
 .../fixtures/basePkgJson6/package.json          |  20 +
 .../fixtures/basePkgJson6/plugins/.svn          |   0
 .../fixtures/basePkgJson6/www/css/index.css     | 115 +++++
 .../fixtures/basePkgJson6/www/index.html        |  43 ++
 .../fixtures/basePkgJson6/www/js/index.js       |  49 ++
 .../fixtures/basePkgJson6/www/spec.html         |  68 +++
 .../spec-cordova/pkgJson-restore.spec.js        | 500 ++++++++++++++++---
 cordova-lib/spec-cordova/pkgJson.spec.js        |  11 +-
 cordova-lib/src/cordova/plugin.js               |  31 +-
 cordova-lib/src/cordova/restore-util.js         | 266 +++++++++-
 41 files changed, 2253 insertions(+), 107 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
new file mode 100644
index 0000000..9e7b9e0
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/merges/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/merges/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson2/merges/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
new file mode 100644
index 0000000..0155e61
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
@@ -0,0 +1,26 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "audrey",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "android"
+        ],
+        "plugins": {
+            "cordova-plugin-camera": {
+                "CAMERA_USAGE_DESCRIPTION": " ",
+                "PHOTOLIBRARY_USAGE_DESCRIPTION": " "
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson2/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/img/logo.png
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/img/logo.png b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/img/logo.png
new file mode 100644
index 0000000..9519e7d
Binary files /dev/null and b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/img/logo.png differ

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson3/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson3/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson3/config.xml
new file mode 100644
index 0000000..7895772
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson3/config.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="android"/>
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson3/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson3/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson3/package.json
new file mode 100644
index 0000000..82b450a
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson3/package.json
@@ -0,0 +1,15 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson3/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson3/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson3/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson3/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml
new file mode 100644
index 0000000..7895772
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="android"/>
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
new file mode 100644
index 0000000..79c8269
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
@@ -0,0 +1,21 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "android",
+            "ios"
+        ]
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson4/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson4/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
new file mode 100644
index 0000000..f86b2cf
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="android"/>
+    <engine name="ios"/>
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson5/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson5/package.json
new file mode 100644
index 0000000..f732cfb
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/package.json
@@ -0,0 +1,20 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "android"
+        ]
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson5/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson5/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson6/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson6/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson6/config.xml
new file mode 100644
index 0000000..7895772
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson6/config.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="android"/>
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson6/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson6/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson6/package.json
new file mode 100644
index 0000000..f732cfb
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson6/package.json
@@ -0,0 +1,20 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "android"
+        ]
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson6/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson6/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson6/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson6/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>


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


[09/12] cordova-lib git commit: CB-12001: Added more restore tests for platform/plugin/spec.

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/pkgJson.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson.spec.js b/cordova-lib/spec-cordova/pkgJson.spec.js
index 45eb8ee..3920c1a 100644
--- a/cordova-lib/spec-cordova/pkgJson.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson.spec.js
@@ -20,6 +20,7 @@ var helpers = require('./helpers'),
     path = require('path'),
     shell = require('shelljs'),
     events = require('cordova-common').events,
+    ConfigParser = require('cordova-common').ConfigParser,
     cordova = require('../src/cordova/cordova');
 
 // This group of tests checks if plugins are added and removed as expected from package.json.
@@ -41,7 +42,6 @@ describe('plugin end-to-end', function() {
         // Copy some platform to avoid working on a project with no platforms.
         shell.cp('-R', path.join(__dirname, 'fixtures', 'platforms', helpers.testPlatform), path.join(project, 'platforms'));
         process.chdir(project);
-
         delete process.env.PWD;
     });
 
@@ -50,30 +50,48 @@ describe('plugin end-to-end', function() {
         shell.rm('-rf', tmpDir);
     });
 
-    it('Test#001 : should successfully add and remove a plugin with save', function(done) {
+    it('Test#001 : should successfully add and remove a plugin with save and correct spec', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
-        var pkgJson;
-    
+        var pkgJson = require(pkgJsonPath);
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var cfg = new ConfigParser(configXmlPath);
+        var configPlugins = cfg.getPluginIdList();
+        var configPlugin = cfg.getPlugin(configPlugins);
+
+        // No plugins in config or pkg.json yet.
+        expect(configPlugins.length).toEqual(0);
+        expect(pkgJson.cordova).toBeUndefined();
         expect(pkgJsonPath).toExist();
 
-        // Add the plugin with --save
-        return cordova.raw.plugin('add', pluginId, {'save':true})
+        // Add the plugin with --save.
+        return cordova.raw.plugin('add', pluginId+'@1.1.2', {'save':true, 'fetch':true})
         .then(function() {
-            // Check that the plugin add was successful.
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            expect(pkgJson).not.toBeUndefined();
-            expect(pkgJson.cordova.plugins).not.toBeUndefined();
+            // Check that the plugin and spec add was successful to pkg.json.
+            expect(pkgJson).toBeDefined();
+            expect(pkgJson.cordova.plugins).toBeDefined();
             expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+            expect(pkgJson.dependencies['cordova-plugin-device']).toEqual('^1.1.2');
+            // Check that the plugin and spec add was successful to config.xml.
+            var cfg2 = new ConfigParser(configXmlPath);
+            configPlugins = cfg2.getPluginIdList();
+            configPlugin = cfg2.getPlugin(configPlugins);
+            expect(configPlugins.length).toEqual(1);
+            expect(configPlugin).toEqual({ name: 'cordova-plugin-device', spec: '~1.1.2', variables: {} });
         }).then(function() {
             // And now remove it with --save.
-            return cordova.raw.plugin('rm', pluginId, {'save':true})
+            return cordova.raw.plugin('rm', pluginId, {'save':true, 'fetch':true});
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Checking that the plugin removed is in not in the platforms
+            // Checking that the plugin removed is in not in the pkg.json.
             expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
+            // Spec should be removed from dependencies.
+            expect(pkgJson.dependencies['cordova-plugin-device']).toBeUndefined();
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -85,15 +103,16 @@ describe('plugin end-to-end', function() {
 
         expect(pkgJsonPath).toExist();
 
-        // Add the plugin with --save.
+        // Add the camera plugin with --save.
         return cordova.raw.plugin('add', 'cordova-plugin-camera', {'save':true})
         .then(function() {
-            // Add a second plugin without save
+            // Add a second plugin without save.
             return cordova.raw.plugin('add', pluginId);
         }).then(function() {
-            // Check the plugin add was successful for the first plugin that had --save.
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
+            // Check the plugin add was successful for the first plugin that had --save.
             expect(pkgJson).not.toBeUndefined();
             expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
             // Expect that the second plugin is not added.
@@ -112,17 +131,18 @@ describe('plugin end-to-end', function() {
         // Add the plugin with --save.
         return cordova.raw.plugin('add', pluginId, {'save':true})
         .then(function() {
-            // Check the platform add was successful.
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
+            // Check the platform add was successful.
             expect(pkgJson).not.toBeUndefined();
             expect(pkgJson.cordova.plugins).toBeDefined();
             expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
         }).then(function() {
             // And now remove it, but without --save.
-            return cordova.raw.plugin('rm', 'cordova-plugin-device')
+            return cordova.raw.plugin('rm', 'cordova-plugin-device');
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             // The plugin should still be in package.json.
@@ -135,25 +155,26 @@ describe('plugin end-to-end', function() {
     it('Test#004 : should successfully add and remove a plugin with variables and save to package.json', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
         var pkgJson;
+        var someKey = 'someKey';
 
         expect(pkgJsonPath).toExist();
 
         // Add the plugin with --save.
         return cordova.raw.plugin('add', pluginId, {'save':true, 'cli_variables': {'someKey':'someValue'}})
         .then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             // Check the plugin add was successful and that variables have been added too.
             expect(pkgJson).not.toBeUndefined();
             expect(pkgJson.cordova.plugins).toBeDefined();
             expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
-            expect(pkgJson.cordova.plugins[pluginId]['someKey']).toEqual('someValue');
+            expect(pkgJson.cordova.plugins[pluginId][someKey]).toEqual('someValue');
         }).then(function() {
             // And now remove it with --save.
-            return cordova.raw.plugin('rm', pluginId, {'save':true})
+            return cordova.raw.plugin('rm', pluginId, {'save':true});
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             // Checking that the plugin and variables were removed successfully.
@@ -165,15 +186,19 @@ describe('plugin end-to-end', function() {
     it('Test#005 : should successfully add and remove multiple plugins with save & fetch', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
         var pkgJson;
+        // Delete any previous caches of require(package.json).
+        delete require.cache[require.resolve(pkgJsonPath)];
+        pkgJson = require(pkgJsonPath);
     
         expect(pkgJsonPath).toExist();
 
-        // Add the plugin with --save
+        // Add the plugin with --save.
         return cordova.raw.plugin('add', [pluginId,'cordova-plugin-device-motion'], {'save':true, 'fetch':true})
         .then(function() {
-            // Check that the plugin add was successful.
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
+            // Check that the plugin add was successful.
             expect(pkgJson).not.toBeUndefined();
             expect(pkgJson.cordova.plugins).not.toBeUndefined();
             expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
@@ -182,12 +207,12 @@ describe('plugin end-to-end', function() {
             expect(pkgJson.dependencies['cordova-plugin-device-motion']).toBeDefined();
         }).then(function() {
             // And now remove it with --save.
-            return cordova.raw.plugin('rm', [pluginId,'cordova-plugin-device-motion'], {'save':true, 'fetch':true})
+            return cordova.raw.plugin('rm', [pluginId,'cordova-plugin-device-motion'], {'save':true, 'fetch':true});
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Checking that the plugin removed is in not in the platforms
+            // Checking that the plugin removed is in not in the platforms.
             expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
             expect(pkgJson.cordova.plugins['cordova-plugin-device-motion']).toBeUndefined();
             expect(pkgJson.dependencies[pluginId]).toBeUndefined();
@@ -216,6 +241,7 @@ describe('platform end-to-end with --save', function () {
     });
 
     afterEach(function() {
+        // Delete any previous caches of require(package.json).
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
@@ -256,10 +282,10 @@ describe('platform end-to-end with --save', function () {
             // And now remove it with --save.
             return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Checking that the platform removed is in not in the platforms key
+            // Checking that the platform removed is in not in the platforms key.
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
         }).then(emptyPlatformList) // platform ls should be empty too.
         .fail(function(err) {
@@ -284,7 +310,7 @@ describe('platform end-to-end with --save', function () {
             // And now remove it without --save.
             return cordova.raw.platform('rm', [helpers.testPlatform]);
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             // Check that the platform removed without --save is still in platforms key.
@@ -299,6 +325,7 @@ describe('platform end-to-end with --save', function () {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
         var pkgJson;
         expect(pkgJsonPath).toExist();
+        // Delete any previous caches of require(package.json).
         delete require.cache[require.resolve(pkgJsonPath)];
         pkgJson = require(pkgJsonPath);
         // Pkg.json "platforms" should be empty and helpers.testPlatform should not exist in pkg.json.
@@ -306,11 +333,11 @@ describe('platform end-to-end with --save', function () {
         // Add platform without --save.
         cordova.raw.platform('add',[helpers.testPlatform])
         .then(function() {
-            // Check the platform add was successful, reload, skipping cache
+            // Check the platform add was successful, reload, skipping cache.
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Platform list should be empty and helpers.testPlatform should NOT have been added.
-            expect(pkgJson.cordova.platforms.length).toEqual(0);
+            // PkgJson.cordova should not be defined and helpers.testPlatform should NOT have been added.
+            expect(pkgJson.cordova).toBeUndefined();
         }).then(fullPlatformList)
         .fail(function(err) {
             expect(err).toBeUndefined();
@@ -330,51 +357,90 @@ describe('platform end-to-end with --save', function () {
             // And now add another platform with --save.
             return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
         }).then(function() {
-            // Check the platform add was successful, reload, skipping cache
+            // Check the platform add was successful, reload, skipping cache.
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Beware empty/missing cordova object
-            var pckJsonCordova = pkgJson.cordova || {platforms:[]};
+            // Beware empty/missing cordova object.
+            var pkgJsonCordova = pkgJson.cordova || {platforms:[]};
             // Check that only the platform added with --save was added to package.json.
-            expect(pckJsonCordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-            expect(pckJsonCordova.platforms.indexOf(platformNotToAdd)).toEqual(-1);
+            expect(pkgJsonCordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            expect(pkgJsonCordova.platforms.indexOf(platformNotToAdd)).toEqual(-1);
         })
         .fail(function(err) {
             expect(err).toBeUndefined();
         })
         .fin(done);
-    });
+    }, 30000);
     it('Test#010 : two platforms are added and removed correctly with --save --fetch', function(done) {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
         expect(pkgJsonPath).toExist();
         var pkgJson;
+        // Delete any previous caches of require(package.json).
+        delete require.cache[require.resolve(pkgJsonPath)];
+        pkgJson = require(pkgJsonPath);
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var cfg = new ConfigParser(configXmlPath);
+        var engines = cfg.getEngines();
+        var engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        var configEngArray = engNames.slice();
 
+        // No platforms in config or pkg.json yet.
+        expect(pkgJson.cordova).toBeUndefined();
+        expect(configEngArray.length === 0);
         // Check there are no platforms yet.
         emptyPlatformList().then(function() {
-            // Add the testing platform with --save.
-            return cordova.raw.platform('add', ['android', 'ios'], {'save':true, 'fetch':true});
+            // Add the testing platform with --save and add specific version to android platform.
+            return cordova.raw.platform('add', ['android@6.1.0', 'ios'], {'save':true, 'fetch':true});
         }).then(function() {
-            // Check the platform add was successful.
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
+            // Check the platform add was successful in platforms list and in dependencies.
             expect(pkgJson.cordova.platforms).toBeDefined();
             expect(pkgJson.cordova.platforms.indexOf('android')).toEqual(0);
             expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(1);
+            expect(pkgJson.dependencies).toBeDefined();
             expect(pkgJson.dependencies['cordova-android']).toBeDefined();
             expect(pkgJson.dependencies['cordova-ios']).toBeDefined();
+            // Android platform should have specific version from add.
+            expect(pkgJson.dependencies['cordova-android']).toEqual('^6.1.0');
+
+            var cfg3 = new ConfigParser(configXmlPath);
+            engines = cfg3.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            // Check that android and ios were added to config.xml with the correct spec.
+            expect(configEngArray.length === 2);
+            expect(engines).toEqual([ { name: 'android', spec: '~6.1.0' }, { name: 'ios', spec: '~4.3.0' } ]);
+
         }).then(fullPlatformList) // Platform should still be in platform ls.
         .then(function() {
             // And now remove it with --save.
             return cordova.raw.platform('rm', ['android', 'ios'], {'save':true, 'fetch':true});
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Checking that the platform removed is in not in the platforms key
+            // Checking that the platform removed is in not in the platforms key.
             expect(pkgJson.cordova.platforms.indexOf('android')).toEqual(-1);
             expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(-1);
+            // Dependencies are removed.
             expect(pkgJson.dependencies['cordova-android']).toBeUndefined();
             expect(pkgJson.dependencies['cordova-ios']).toBeUndefined();
+            // Platforms are removed from config.xml.
+            var cfg4 = new ConfigParser(configXmlPath);
+            engines = cfg4.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            // Platforms are removed from config.xml.
+            expect(configEngArray.length === 0);
         }).then(emptyPlatformList) // platform ls should be empty too.
         .fail(function(err) {
             expect(err).toBeUndefined();

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/src/cordova/platform.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/platform.js b/cordova-lib/src/cordova/platform.js
index 2b82e8a..e395835 100644
--- a/cordova-lib/src/cordova/platform.js
+++ b/cordova-lib/src/cordova/platform.js
@@ -125,7 +125,9 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                 }
                 return downloadPlatform(projectRoot, platform, spec, opts);
             }).then(function(platDetails) {
-                delete require.cache[require.resolve(path.join(projectRoot, 'package.json'))];
+                if(fs.existsSync(path.join(projectRoot, 'package.json'))) {
+                    delete require.cache[require.resolve(path.join(projectRoot, 'package.json'))];
+                }
                 platform = platDetails.platform;
                 var platformPath = path.join(projectRoot, 'platforms', platform);
                 var platformAlreadyAdded = fs.existsSync(platformPath);
@@ -168,7 +170,6 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                 }
 
                 events.emit('log', (cmd === 'add' ? 'Adding ' : 'Updating ') + platform + ' project...');
-
                 var PlatformApi;
                 try {
                     // Try to get PlatformApi class from platform
@@ -190,12 +191,10 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                         PlatformApi = require('../platforms/PlatformApiPoly');
                     }
                 }
-
                 var destination = path.resolve(projectRoot, 'platforms', platform);
                 var promise = cmd === 'add' ?
                     PlatformApi.createPlatform.bind(null, destination, cfg, options, events) :
                     PlatformApi.updatePlatform.bind(null, destination, options, events);
-
                 return promise()
                 .then(function () {
                     if (!opts.restoring) {
@@ -250,13 +249,13 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
             var pkgJson;
             var pkgJsonPath = path.join(projectRoot, 'package.json');
             var modifiedPkgJson = false;
-
             if(fs.existsSync(pkgJsonPath)) {
                 delete require.cache[require.resolve(pkgJsonPath)]; 
                 pkgJson = require(pkgJsonPath);
             } else {
-                // Create package.json in cordova@7
+                // TODO: Create package.json in cordova@7
             }
+
             if (pkgJson === undefined) {
                 return;
             }

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/src/cordova/restore-util.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/restore-util.js b/cordova-lib/src/cordova/restore-util.js
index bfb3830..044941c 100644
--- a/cordova-lib/src/cordova/restore-util.js
+++ b/cordova-lib/src/cordova/restore-util.js
@@ -17,19 +17,20 @@
     under the License.
 */
 
-var cordova_util = require('./util'),
-    ConfigParser = require('cordova-common').ConfigParser,
-    path         = require('path'),
-    Q            = require('q'),
-    fs           = require('fs'),
-    events       = require('cordova-common').events,
-    cordova      = require('./cordova'),
-    semver      = require('semver'),
-    promiseutil = require('../util/promise-util');
+var cordova_util    = require('./util'),
+    ConfigParser    = require('cordova-common').ConfigParser,
+    path            = require('path'),
+    Q               = require('q'),
+    fs              = require('fs'),
+    events          = require('cordova-common').events,
+    cordova         = require('./cordova'),
+    semver          = require('semver'),
+    platformsList   = require('../platforms/platforms.js'),
+    promiseutil     = require('../util/promise-util');
 
 exports.installPluginsFromConfigXML = installPluginsFromConfigXML;
 exports.installPlatformsFromConfigXML = installPlatformsFromConfigXML;
-
+// Install platforms looking at config.xml and package.json (if there is one).
 function installPlatformsFromConfigXML(platforms, opts) {
     events.emit('verbose', 'Checking config.xml and package.json for saved platforms that haven\'t been added to the project');
 
@@ -41,16 +42,13 @@ function installPlatformsFromConfigXML(platforms, opts) {
     var pkgJson;
     var pkgJsonPlatforms;
     var comboArray = []; 
-    var targets;
     var configPlatforms = [];
-    var installAllPlatforms;
-    var platformPath;
-    var platformAlreadyAdded;
-    var t;
     var modifiedPkgJson = false;
     var modifiedConfigXML = false;
-    var configObject;
+    var mergedPlatformSpecs = {};
+    var key;
 
+    // Check if path exists and require pkgJsonPath.
     if(fs.existsSync(pkgJsonPath)) {
         pkgJson = require(pkgJsonPath);
     }
@@ -61,23 +59,26 @@ function installPlatformsFromConfigXML(platforms, opts) {
     if(cfg !== undefined) {
 
         if (pkgJsonPlatforms !== undefined) {
-            // Combining arrays and checking duplicates
+            // Combining arrays and checking duplicates.
             comboArray = pkgJsonPlatforms.slice();
         }
 
-        engines = cfg.getEngines(projectHome)
+        engines = cfg.getEngines(projectHome);
         configPlatforms = engines.map(function(Engine) {
-            configPlatName = Engine.name;
+            var configPlatName = Engine.name;
+            // Add specs from config into mergedPlatformSpecs.
+            if(mergedPlatformSpecs[configPlatName] === undefined && Engine.spec) {
+                mergedPlatformSpecs[configPlatName] = Engine.spec;
+            }
             return configPlatName;
         });
-        
         configPlatforms.forEach(function(item) {
             if(comboArray.indexOf(item) < 0 ) {
                 comboArray.push(item);
             }
         });
-        //comboArray should have all platforms from config.xml & package.json
-        //remove dupes in comboArray & sort
+        // ComboArray should have all platforms from config.xml & package.json.
+        // Remove duplicates in comboArray & sort.
         var uniq = comboArray.reduce(function(a,b) {
             if (a.indexOf(b) < 0 ) a.push(b);
                 return a;
@@ -85,14 +86,13 @@ function installPlatformsFromConfigXML(platforms, opts) {
         comboArray = uniq;
         comboArray = comboArray.sort();
 
-        // No platforms to restore from either config.xml or package.json
+        // No platforms to restore from either config.xml or package.json.
         if (comboArray.length <= 0) {
            return Q('No platforms found in config.xml or package.json. Nothing to restore');
         }
 
-        //if no package.json, don't bother
+        // If no package.json, don't continue.
         if (pkgJson !== undefined) { 
-
             // If config.xml & pkgJson exist and the cordova key is undefined, create a cordova key.
             if (pkgJson.cordova === undefined) {
                 pkgJson.cordova = {};
@@ -105,33 +105,71 @@ function installPlatformsFromConfigXML(platforms, opts) {
             if (comboArray.toString() === pkgJson.cordova.platforms.sort().toString()) {
                 events.emit('verbose', 'Config.xml and package.json platforms are the same. No pkg.json modification.');
             } else {
-                // modify pkg.json to include the elements
-                // from the comboArray array so that the arrays are identical
+                // Modify pkg.json to include the elements.
+                // From the comboArray array so that the arrays are identical.
                 events.emit('verbose', 'Config.xml and package.json platforms are different. Updating package.json with most current list of platforms.');
                 modifiedPkgJson = true;
             }
 
-            // If comboArray has the same platforms as config.xml, no modification to config.xml.
-            if(comboArray.length === configPlatforms.length && comboArray.toString() === configPlatforms.sort().toString()) {
-                events.emit('verbose', 'Package.json and config.xml are the same. No config.xml modification.');
-            } else {
-                events.emit('verbose', 'Package.json and config.xml platforms are different. Updating config.xml with most current list of platforms.');
-                comboArray.forEach(function(item) {
-                    if(configPlatforms.indexOf(item) < 0 ) {
-                        cfg.addEngine(item);
-                        modifiedConfigXML = true;
+            events.emit('verbose', 'Package.json and config.xml platforms are different. Updating config.xml with most current list of platforms.');
+            comboArray.forEach(function(item) {
+                var prefixItem = ('cordova-'+item);
+                // Modify package.json if any of these cases are true:
+                if((pkgJson.dependencies === undefined && Object.keys(mergedPlatformSpecs).length)||
+                    (pkgJson.dependencies[item] === undefined && mergedPlatformSpecs[item]) ||
+                    (pkgJson.dependencies[prefixItem] === undefined && mergedPlatformSpecs[prefixItem])) {
+                    modifiedPkgJson = true;
+                }
+
+                // Get the cordova- prefixed spec from package.json and add it to mergedPluginSpecs.
+                if (pkgJson.dependencies && pkgJson.dependencies[prefixItem]) {
+                    if(mergedPlatformSpecs[prefixItem] != pkgJson.dependencies[prefixItem]) {
+                        modifiedPkgJson = true;
                     }
-                });
-            }
+                    mergedPlatformSpecs[item] = pkgJson.dependencies[prefixItem];
+                }
+
+                // Get the spec from package.json and add it to mergedPluginSpecs.
+                if (pkgJson.dependencies && pkgJson.dependencies[item] && pkgJson.dependencies[prefixItem] === undefined) {
+                    if(mergedPlatformSpecs[item] != pkgJson.dependencies[item]) {
+                        modifiedPkgJson = true;
+                    }
+                    mergedPlatformSpecs[item] = pkgJson.dependencies[item];
+                }
+
+                // First remove the engine and then add missing engine and elements to config.xml.
+                // Remove to avoid duplicate engines.
+                if(mergedPlatformSpecs[item]) {
+                    cfg.removeEngine(item);
+                    cfg.addEngine(item, mergedPlatformSpecs[item]);
+                    modifiedConfigXML = true;
+                // If there is no spec, just remove and add the engine.
+                } else if (configPlatforms.indexOf(item) < 0) {
+                    cfg.removeEngine(item);
+                    cfg.addEngine(item);
+                    modifiedConfigXML = true;
+                } 
+            });
         }
+
         // Write and update pkg.json if it has been modified.
         if (modifiedPkgJson === true) {
             pkgJson.cordova.platforms = comboArray;
-            //pkgJson.cordova.dependencies[configPlatName] =configPlatSpec;
+            if(pkgJson.dependencies === undefined) {
+                pkgJson.dependencies = {};
+            }
+            // Check if key is part of cordova alias list.
+            // Add prefix if it is.
+            for (key in mergedPlatformSpecs) {
+                var prefixKey = key;
+                if (key in platformsList) {
+                    prefixKey = 'cordova-' + key;
+                }
+                pkgJson.dependencies[prefixKey] = mergedPlatformSpecs[key];
+            }
             fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
         }
         if (modifiedConfigXML === true) {
-            configPlatforms = comboArray;
             cfg.write();
         }
         if (!comboArray || !comboArray.length) {
@@ -145,9 +183,19 @@ function installPlatformsFromConfigXML(platforms, opts) {
     // Otherwise, we get a bug where the following line: https://github.com/apache/cordova-lib/blob/0b0dee5e403c2c6d4e7262b963babb9f532e7d27/cordova-lib/src/util/npm-helper.js#L39
     // gets executed simultaneously by each platform and leads to an exception being thrown
     return promiseutil.Q_chainmap_graceful(comboArray, function(target) {
+        var cwd = process.cwd();
+        var platformsFolderPath = path.join(cwd,'platforms');
+        var platformsInstalled = path.join(platformsFolderPath, target);
         if (target) {
-            events.emit('log', 'Discovered platform \"' + target + '\" in config.xml or package.json. Adding it to the project');
-            return cordova.raw.platform('add', target, opts);
+            // Add the spec to the target
+            if(mergedPlatformSpecs[target]) {
+                target = target + '@' + mergedPlatformSpecs[target];
+            }
+            // If the platform is already installed, no need to re-install it.
+            if (!fs.existsSync(platformsInstalled)) {
+                events.emit('log', 'Discovered platform \"' + target + '\" in config.xml or package.json. Adding it to the project');
+                return cordova.raw.platform('add', target, opts);
+            }
         }
         return Q();
     }, function(err) {
@@ -155,10 +203,10 @@ function installPlatformsFromConfigXML(platforms, opts) {
     });
 }
 
-//returns a Promise
+// Returns a promise.
 function installPluginsFromConfigXML(args) {
     events.emit('verbose', 'Checking config.xml for saved plugins that haven\'t been added to the project');
-    //Install plugins that are listed on config.xml
+    //Install plugins that are listed in config.xml.
     var projectRoot = cordova_util.cdProjectRoot();
     var configPath = cordova_util.projectConfig(projectRoot);
     var cfg = new ConfigParser(configPath);
@@ -167,29 +215,30 @@ function installPluginsFromConfigXML(args) {
     var pkgJson;
     var modifiedPkgJson = false;
     var modifiedConfigXML = false;
-    //TODO: rename to comboObject?
-    var mergedPluginDataObj;
+    var comboObject;
+    var mergedPluginSpecs = {};
     var comboPluginIdArray;
     var configPlugin;
     var configPluginVariables;
     var pkgJsonPluginVariables;
+    var key;
 
-    //Check if path exists and require pkgJsonPath
+    // Check if path exists and require pkgJsonPath.
     if(fs.existsSync(pkgJsonPath)) {
         pkgJson = require(pkgJsonPath);
     }
 
     if(pkgJson !== undefined && pkgJson.cordova !== undefined && pkgJson.cordova.plugins !== undefined) {
         comboPluginIdArray = Object.keys(pkgJson.cordova.plugins);
-        // Create a merged plugin data array (mergedPluginDataObj)
-        // and add all of the package.json plugins to mergedPluginDataObj
-        mergedPluginDataObj = pkgJson.cordova.plugins;
+        // Create a merged plugin data array (comboObject)
+        // and add all of the package.json plugins to comboObject.
+        comboObject = pkgJson.cordova.plugins;
     } else {
-        mergedPluginDataObj = {};
+        comboObject = {};
         comboPluginIdArray = [];
     }
 
-    // Get all config.xml plugin ids
+    // Get all config.xml plugin ids (names).
     var pluginIdConfig = cfg.getPluginIdList();
     if(pluginIdConfig === undefined) {
         pluginIdConfig = [];
@@ -203,63 +252,86 @@ function installPluginsFromConfigXML(args) {
             pkgJson.cordova.plugins = {};
         }
 
-        // Check to see which plugins are initially the same in pkg.json and config.xml
-        // add missing plugin variables in package.json from config.xml
+        // Check to see which plugins are initially the same in pkg.json and config.xml.
+        // Add missing plugin variables in package.json from config.xml.
         comboPluginIdArray.forEach(function(item) {
             if(pluginIdConfig.includes(item)) {
                 configPlugin = cfg.getPlugin(item);
                 configPluginVariables = configPlugin.variables;
-                pkgJsonPluginVariables = mergedPluginDataObj[item];
+                pkgJsonPluginVariables = comboObject[item];
                 for(var key in configPluginVariables) {
-                    // Handle conflicts, package.json wins
-                    // only add the variable to package.json if it doesn't already exist
+                    // Handle conflicts, package.json wins.
+                    // Only add the variable to package.json if it doesn't already exist.
                     if(pkgJsonPluginVariables[key] === undefined) {
                         pkgJsonPluginVariables[key] = configPluginVariables[key];
-                        mergedPluginDataObj[item][key] = configPluginVariables[key];
+                        comboObject[item][key] = configPluginVariables[key];
                         modifiedPkgJson = true;
                     }
                 }
             }
-        })
+            // Get the spec from package.json and add it to mergedPluginSpecs.
+            if (pkgJson.dependencies && pkgJson.dependencies[item]) {
+                mergedPluginSpecs[item] = pkgJson.dependencies[item];
+            }
+        });
 
-        // Check to see if pkg.json plugin(id) and config plugin(id) match
+        // Check to see if pkg.json plugin(id) and config plugin(id) match.
         if(comboPluginIdArray.sort().toString() !== pluginIdConfig.sort().toString()) {
             // If there is a config plugin that does NOT already exist in
-            // mergedPluginDataArray, add it and its variables
+            // mergedPluginDataArray, add it and its variables.
             pluginIdConfig.forEach(function(item) {
                 if(comboPluginIdArray.indexOf(item) < 0) {
                     comboPluginIdArray.push(item);
                     var configXMLPlugin = cfg.getPlugin(item);
-                    mergedPluginDataObj[item] = configXMLPlugin.variables;
+                    comboObject[item] = configXMLPlugin.variables;
                     modifiedPkgJson = true;
                 }
             });
         }
 
+        // Add specs from config.xml to mergedPluginSpecs.
+        pluginIdConfig.forEach(function(item) {
+            var configXMLPlugin = cfg.getPlugin(item);
+            if(mergedPluginSpecs[item] === undefined && configXMLPlugin.spec) {
+                mergedPluginSpecs[item] = configXMLPlugin.spec;
+                modifiedPkgJson = true;
+            }
+        });
+        // If pkg.json plugins have been modified, write to it.
         if (modifiedPkgJson === true) {
-            pkgJson.cordova.plugins = mergedPluginDataObj;
+            pkgJson.cordova.plugins = comboObject;
+            if(pkgJson.dependencies === undefined) {
+                pkgJson.dependencies = {};
+            }
+            for(key in mergedPluginSpecs) {
+                pkgJson.dependencies[key] = mergedPluginSpecs[key];
+            }
             fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
         }
     }
-    
-    // Write config.xml
+    // Write config.xml (only if plugins exist in package.json).
     comboPluginIdArray.forEach(function(plugID) {
-        pluginIdConfig.push(plugID);
+        if(pluginIdConfig.indexOf(plugID) < 0) {
+            pluginIdConfig.push(plugID);
+        }
         cfg.removePlugin(plugID);
-        //TODO: spec needs to be added to addPlugin
-        cfg.addPlugin({name:plugID}, mergedPluginDataObj[plugID]); 
-        modifiedConfigXML = true;
+        if (mergedPluginSpecs[plugID]) {
+            cfg.addPlugin({name:plugID, spec: mergedPluginSpecs[plugID]}, comboObject[plugID]); 
+            modifiedConfigXML = true;
+        // If no spec, just add the plugin.
+        } else {
+            cfg.addPlugin({name:plugID}, comboObject[plugID]); 
+            modifiedConfigXML = true;
+        }
     });
 
     if (modifiedConfigXML === true) {
         cfg.write();    
     }
     
-
     // Intermediate variable to store current installing plugin name
     // to be able to create informative warning on plugin failure
     var pluginName;
-
     // CB-9560 : Run `plugin add` serially, one plugin after another
     // We need to wait for the plugin and its dependencies to be installed
     // before installing the next root plugin otherwise we can have common
@@ -280,8 +352,11 @@ function installPluginsFromConfigXML(args) {
 
         // CB-10761 If plugin spec is not specified, use plugin name
         var installFrom = pluginSpec || pluginName;
-        if (pluginSpec && semver.validRange(pluginSpec, true))
+        if (pluginSpec && semver.validRange(pluginSpec, true)) {
             installFrom = pluginName + '@' + pluginSpec;
+        } else if(args.fetch && pluginSpec) {
+            installFrom = pluginName + '@' + pluginSpec;
+        }
 
         // Add feature preferences as CLI variables if have any
         var options = {


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


[03/12] cordova-lib git commit: CB-11960: Refactored add/remove/restore code and added tests.

Posted by st...@apache.org.
CB-11960: Refactored add/remove/restore code and added tests.


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/b247e12d
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/b247e12d
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/b247e12d

Branch: refs/heads/master
Commit: b247e12df2458a75893f605b49544ce032dee54b
Parents: 4cd1be8
Author: Steve Gill <st...@gmail.com>
Authored: Mon Nov 7 16:45:00 2016 -0800
Committer: Audrey So <au...@apache.org>
Committed: Thu Jan 26 17:36:51 2017 -0800

----------------------------------------------------------------------
 .../spec-cordova/pkgJson-restore.spec.js        | 590 +++++++++++++------
 cordova-lib/spec-cordova/pkgJson.spec.js        |  77 ++-
 cordova-lib/src/cordova/restore-util.js         | 350 +++++------
 3 files changed, 575 insertions(+), 442 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/b247e12d/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
index 61a8945..a0646d6 100644
--- a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -18,30 +18,17 @@
 */
 var helpers = require('./helpers'),
     path = require('path'),
-    fs = require('fs'),
     shell = require('shelljs'),
     events = require('cordova-common').events,
-    cordova = require('../src/cordova/cordova'),
-    rewire = require('rewire'),
-    prepare = require('../src/cordova/prepare'),
-    platforms = require('../src/platforms/platforms'),
-    cordova_util = require('util'),
     ConfigParser = require('cordova-common').ConfigParser,
-    platform = rewire('../src/cordova/platform.js');
+    cordova = require('../src/cordova/cordova');
 
-var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
-var pluginsDir = path.join(__dirname, 'fixtures', 'plugins');
-
-/** Testing will check if "cordova prepare" is restoring platforms as expected.
-*   Uses different basePkgJson files depending on testing expecations of what should
-*   initially be in pkg.json and/or config.xml.
+/** Testing will check if "cordova prepare" is restoring platforms and plugins as expected.
+*   Uses different basePkgJson files depending on testing expecations of what (platforms/plugins/variables)
+*   should initially be in pkg.json and/or config.xml.
 */
 
-var req = function(someModule) {
-    delete require.cache[require.resolve(someModule)];
-    return require(someModule);
-}
-
+// Use basePkgJson
 describe('platform end-to-end with --save', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
@@ -58,7 +45,6 @@ describe('platform end-to-end with --save', function () {
     });
 
     afterEach(function() {
-        var cwd = process.cwd();
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
@@ -81,7 +67,7 @@ describe('platform end-to-end with --save', function () {
         });
     }
     /** Test#001 will add a platform to package.json with the 'save' flag.
-    *   It will remove it from pkg.json without the save flag.
+    *   It will remove it from platforms.json without the save flag.
     *   After running cordova prepare, that platform should be restored in the
     *   installed platform list in platforms.json.
     */
@@ -89,7 +75,7 @@ describe('platform end-to-end with --save', function () {
         var cwd = process.cwd();
         var pkgJsonPath = path.join(cwd,'package.json');
         delete require.cache[require.resolve(pkgJsonPath)];
-        var pkgJson = require(pkgJsonPath);
+        var pkgJson;
         var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
         var platformsJson;
 
@@ -97,6 +83,8 @@ describe('platform end-to-end with --save', function () {
             // Add the testing platform with --save.
             return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
         }).then(function() {
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
             // Require platformsFolderPath
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
@@ -147,8 +135,7 @@ describe('platform end-to-end with --save', function () {
         var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
         var platformsJson;
         var secondPlatformAdded = 'ios';
-        
-
+    
         emptyPlatformList().then(function() {
             // Add the testing platform with --save.
             return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
@@ -161,8 +148,7 @@ describe('platform end-to-end with --save', function () {
             pkgJson = require(pkgJsonPath);
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-            
-            // Check the platform add of both platforms was successful.
+            // Check the platform add of both platforms (to pkg.Json) was successful.
             expect(pkgJson.cordova.platforms).toBeDefined();
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(0);
             expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(1);
@@ -183,10 +169,10 @@ describe('platform end-to-end with --save', function () {
             platformsJson = require(platformsFolderPath);
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Check that the platform removed without --save is still in platforms key.
+            // Check that ONLY the platform removed without --save is still in (pkg.json) platforms key.
             expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(0);
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
-            // Check that the helpers.testPlatform (removed with --save) was removed from the platforms.json
+            // Check that both platforms were removed from the platforms.json
             expect(platformsJson[helpers.testPlatform]).toBeUndefined();
             expect(platformsJson[secondPlatformAdded]).toBeUndefined();
         }).then(function() {
@@ -231,7 +217,7 @@ describe('platform end-to-end with --save', function () {
             pkgJson = require(pkgJsonPath);
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-            // Check the platform add of both platforms was successful in package.json.
+            // Check the platform add of only helpers.testPlatform was successful in package.json.
             expect(pkgJson.cordova.platforms).toBeDefined();
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
             expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(-1);
@@ -263,7 +249,7 @@ describe('platform end-to-end with --save', function () {
             // Delete any previous caches of platformsJson
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-            // Expect "helpers.testPlatform" to be in the installed platforms list
+            // Expect "helpers.testPlatform" to be in the installed platforms list.
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
             // Expect that 'ios' will not be in platforms.json and has not been restored.
             expect(platformsJson[secondPlatformAdded]).toBeUndefined();
@@ -272,7 +258,6 @@ describe('platform end-to-end with --save', function () {
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
     },30000);
-
 });
 
 // Use basePkgJson6 because pkg.json and config.xml contain only android
@@ -293,7 +278,6 @@ describe('files should not be modified if their platforms are identical', functi
     });
 
     afterEach(function() {
-        var cwd = process.cwd();
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
@@ -307,15 +291,6 @@ describe('files should not be modified if their platforms are identical', functi
             expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
-
-    function fullPlatformList() {
-        return cordova.raw.platform('list').then(function() {
-            var installed = results.match(/Installed platforms:\n  (.*)/);
-            expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-        });
-    }
-    
     /** Test#004 will check the platform list in package.json and config.xml. 
     *   When both files contain the same platforms and cordova prepare is run, 
     *   neither file is modified.
@@ -327,7 +302,6 @@ describe('files should not be modified if their platforms are identical', functi
         var engines = cfg1.getEngines();
         var pkgJsonPath = path.join(cwd,'package.json');
         var pkgJson;
-        var platformsFolderPath;
         var engNames = engines.map(function(elem) {
             return elem.name;
         });
@@ -375,7 +349,6 @@ describe('update pkg.json to include platforms in config.xml', function () {
     });
 
     afterEach(function() {
-        var cwd = process.cwd();
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
@@ -389,14 +362,6 @@ describe('update pkg.json to include platforms in config.xml', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
-
-    function fullPlatformList() {
-        return cordova.raw.platform('list').then(function() {
-            var installed = results.match(/Installed platforms:\n  (.*)/);
-            expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-        });
-    }
     /** Test#005 will check the platform list in package.json and config.xml. 
     *   When config.xml has 'android and ios' and pkg.json only contains 'android', run cordova
     *   and pkg.json is updated to include 'ios'.
@@ -409,15 +374,14 @@ describe('update pkg.json to include platforms in config.xml', function () {
         var pkgJsonPath = path.join(cwd,'package.json');
         delete require.cache[require.resolve(pkgJsonPath)];
         var pkgJson = require(pkgJsonPath);
-        var platformsFolderPath;
         var engNames = engines.map(function(elem) {
             return elem.name;
         });
         var configEngArray = engNames.slice();
        
         // Config.xml contains(android & ios) and pkg.json contains android (basePkgJson5)
-        expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
         expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+        expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
         // pkg.json should not contain 'ios' platform before cordova prepare
         expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(-1);
         expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
@@ -431,8 +395,11 @@ describe('update pkg.json to include platforms in config.xml', function () {
             // Expect 'android' to still be there in pkg.json
             expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
             // Expect both pkg.json and config.xml to each have both platforms in their arrays
-            expect(configEngArray.length === 1);
-            expect(pkgJson.cordova.platforms.length === 1);
+            expect(configEngArray.length === 2);
+            expect(pkgJson.cordova.platforms.length === 2);
+            // No changes to config.xml
+            expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+            expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -457,7 +424,6 @@ describe('update empty package.json to match config.xml', function () {
     });
 
     afterEach(function() {
-        var cwd = process.cwd();
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
@@ -471,15 +437,6 @@ describe('update empty package.json to match config.xml', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
-
-    function fullPlatformList() {
-        return cordova.raw.platform('list').then(function() {
-            var installed = results.match(/Installed platforms:\n  (.*)/);
-            expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-        });
-    }
-
     /** Test#006 will check if pkg.json has a cordova key and platforms installed already.
      *   If it does not and config.xml has a platform(s) installed already, run cordova prepare
      *   and it will add a cordova key and the platform(s) from config.xml to package.json.
@@ -492,7 +449,6 @@ describe('update empty package.json to match config.xml', function () {
         var cfg1 = new ConfigParser(configXmlPath);
         var engines = cfg1.getEngines();
         var pkgJson = require(pkgJsonPath);
-        var platformsFolderPath;
         var engNames = engines.map(function(elem) {
             return elem.name;
         });
@@ -521,8 +477,8 @@ describe('update empty package.json to match config.xml', function () {
             // Expect cordova key and 'android' platform to be added to pkg.json
             expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
             // Expect both pkg.json and config.xml to each have (only) android in their arrays
-            expect(configEngArray.length === 0);
-            expect(pkgJson.cordova.platforms.length === 0);
+            expect(configEngArray.length === 1);
+            expect(pkgJson.cordova.platforms.length === 1);
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -547,7 +503,6 @@ describe('update config.xml to include platforms in pkg.json', function () {
     });
 
     afterEach(function() {
-        var cwd = process.cwd();
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
@@ -561,14 +516,6 @@ describe('update config.xml to include platforms in pkg.json', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
-
-    function fullPlatformList() {
-        return cordova.raw.platform('list').then(function() {
-            var installed = results.match(/Installed platforms:\n  (.*)/);
-            expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-        });
-    }
     /** Test#007 will check the platform list in package.json and config.xml. 
     *   When packge.json has 'android and ios' and config.xml only contains 'android', run cordova
     *   and config.xml is updated to include 'ios'.
@@ -581,7 +528,6 @@ describe('update config.xml to include platforms in pkg.json', function () {
         var pkgJsonPath = path.join(cwd,'package.json');
         delete require.cache[require.resolve(pkgJsonPath)];
         var pkgJson = require(pkgJsonPath);
-        var platformsFolderPath;
         var engNames = engines.map(function(elem) {
             return elem.name;
         });
@@ -596,6 +542,8 @@ describe('update config.xml to include platforms in pkg.json', function () {
             // Run cordova prepare
             return cordova.raw.prepare();
         }).then(function() {
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
             engines = cfg2.getEngines();
             engNames = engines.map(function(elem) {
@@ -606,6 +554,8 @@ describe('update config.xml to include platforms in pkg.json', function () {
             expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
             // Expect 'android' to still be in config.xml
             expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+            // Expect config.xml array to have 2 elements (platforms);
+            expect(configEngArray.length === 2);
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -613,10 +563,11 @@ describe('update config.xml to include platforms in pkg.json', function () {
     },30000);
 });
 
-// PLUGINS START HERE
+// Plugin testing begins here.
 
-// Use basePkgJson2 as pkg.json contains 2 plugins and config.xml contains 1 plugin
-describe('update config.xml to include plugins found in pkg.json', function () {
+// Use basePkgJson8 as pkg.json contains 1 plugin and 1 variable and config contains 1 plugin 1 var
+// Same variable, different values... pkg.json should win
+describe('update config.xml to use the variable found in pkg.json', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
     var results;
@@ -625,14 +576,13 @@ describe('update config.xml to include plugins found in pkg.json', function () {
         shell.rm('-rf', tmpDir);
         // Copy then move because we need to copy everything, but that means it will copy the whole directory.
         // Using /* doesn't work because of hidden files.
-        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson2'), tmpDir);
-        shell.mv(path.join(tmpDir, 'basePkgJson2'), project);
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson8'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson8'), project);
         process.chdir(project);
         events.on('results', function(res) { results = res; });
     });
 
     afterEach(function() {
-        var cwd = process.cwd();
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
@@ -646,42 +596,112 @@ describe('update config.xml to include plugins found in pkg.json', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
+    /** Test#011 will check the plugin/variable list in package.json and config.xml. 
+    *   When pkg.json and config.xml have the same variables, but different values,
+    *   pkg.json should win and that value will be used and replaces config's value.
+    */
+    it('Test#011 : if pkg.Json has 1 plugin and 1 variable, update config.xml to include these variables', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var cfg1 = new ConfigParser(configXmlPath);
+        var configPlugins = cfg1.getPluginIdList();
+        var configPlugin = cfg1.getPlugin(configPlugins);
+        var configPluginVariables = configPlugin.variables;
+        var pkgJson = require(pkgJsonPath);
 
-    function fullPlatformList() {
+        // Expect that pkg.json exists with 1 plugin, 1 variable, and a different value (json)
+        expect(pkgJson.cordova.plugins).toBeDefined();
+        expect(Object.keys(pkgJson.cordova.plugins).length === 1);
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'json' });
+        // Expect that config.xml exists with 1 plugin, 1 variable, but a different value (config)
+        expect(configPlugin.name).toEqual('cordova-plugin-camera');
+        expect(configPluginVariables).toEqual({ variable_1: 'config' });
+        expect(Object.keys(configPlugin).length === 1);
+
+        emptyPlatformList().then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        }).then(function() {
+            // // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            var cfg2 = new ConfigParser(configXmlPath);
+            configPlugins = cfg2.getPluginIdList();
+            configPlugin = cfg2.getPlugin(configPlugins);
+            configPluginVariables = configPlugin.variables;
+            // Expect that pkg.json exists with 1 plugin, 1 variable, and the pkg.json value
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+            expect(Object.keys(pkgJson.cordova.plugins).length === 1);
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'json' });
+            // Expect that config.xml exists with 1 plugin, 1 variable and pkg.json's value
+            expect(configPlugin.name).toEqual('cordova-plugin-camera');
+            expect(configPluginVariables).toEqual({ variable_1: 'json' });
+            expect(Object.keys(configPlugin).length === 1);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
+
+// Use basePkgJson9 as config contains 1 plugin and 1 variable and pkg.json contains 1 plugin 0 var
+describe('update pkg.json to include plugin and variable found in config.xml', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson9'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson9'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
         return cordova.raw.platform('list').then(function() {
             var installed = results.match(/Installed platforms:\n  (.*)/);
             expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
-
-    /** Test#008 will check the platform list in package.json and config.xml. 
-    *   When packge.json has 'splashscreen and camera' and config.xml only contains 'splashscreen', run cordova prepare
-    *   and config.xml is updated to include 'camera'.
+    /** Test#012 will check the plugin/variable list in package.json and config.xml. 
+    *   When config.xml has a 'camera plugin and 1 variable' and pkg.json has 1 plugins/0 variables,
+    *   cordova prepare runs and will update pkg.json to match config.xml's plugins/variables.
     */
-    it('Test#008 :  if pkgJson has 2 plugins and config.xml has only one of those plugins, update config to include both', function(done) {
+    it('Test#012 : if pkg.Json has 1 plugin and 2 variables, update config.xml to include these plugins/variables', function(done) {
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var pkgJsonPath = path.join(cwd,'package.json');
         delete require.cache[require.resolve(pkgJsonPath)];
         var cfg1 = new ConfigParser(configXmlPath);
-        var plugins = cfg1.getPluginIdList();
+        var configPlugins = cfg1.getPluginIdList();
+        var configPlugin = cfg1.getPlugin(configPlugins);
+        var configPluginVariables = configPlugin.variables;
         var pkgJson = require(pkgJsonPath);
-        var pluginNames = plugins.map(function(elem) {
-            return elem;
-        });
-        var configPlugArray = pluginNames.slice();
 
-        // Expect that pkg.json exists with 2 plugins
+        // Expect that pkg.json exists with 1 plugin
         expect(pkgJson.cordova.plugins).toBeDefined();
-        expect(pkgJson.cordova.plugins.length === 1);
-        expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(0);
-        expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(1);
-        // Expect that config.xml exists with 1 plugin
-        expect(configPlugArray).toBeDefined();
-        expect(configPlugArray.length === 0);
-        expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(-1);
-        expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
+        expect(Object.keys(pkgJson.cordova.plugins).length === 1);
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+        expect(pkgJson.cordova.plugins).toEqual({ 'cordova-plugin-camera': {} });
+
+        // Expect that config.xml exists with 1 plugin and 1 variable
+        expect(configPlugin.name).toEqual('cordova-plugin-camera');
+        expect(configPluginVariables).toEqual({ variable_1: 'value_1' });
+        expect(Object.keys(configPlugin).length === 1);
 
         emptyPlatformList().then(function() {
             // Run cordova prepare
@@ -691,23 +711,17 @@ describe('update config.xml to include plugins found in pkg.json', function () {
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
-            plugins = cfg2.getPluginIdList();
-            pluginNames = plugins.map(function(elem) {
-                return elem;
-            });
-            configPlugArray = pluginNames.slice();
-            // Expect that pkg.json exists with 2 plugins
-            expect(pkgJson.cordova.plugins).toBeDefined();
-            expect(pkgJson.cordova.plugins.length === 1);
-            // Expect that config.xml exists with 2 plugins
-            expect(configPlugArray).toBeDefined();
-            expect(configPlugArray.length === 1);
-            // Expect config to contain camera and splashscreen
-            expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
-            expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(1);
-            // Expect pkg.json to contain camera and splashscreen
-            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(0);
-            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+            configPlugins = cfg2.getPluginIdList();
+            configPlugin = cfg2.getPlugin(configPlugins);
+            configPluginVariables = configPlugin.variables;
+            // Expect that pkg.json exists with 1 plugin, 1 variable, and 1 value
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+            expect(Object.keys(pkgJson.cordova.plugins).length === 1);
+            expect(pkgJson.cordova.plugins).toEqual({ 'cordova-plugin-camera': { variable_1: 'value_1' } });
+            // Expect that config.xml exists with 1 plugin, 1 variable and 1 value
+            expect(configPlugin.name).toEqual('cordova-plugin-camera');
+            expect(configPluginVariables).toEqual({ variable_1: 'value_1' });
+            expect(Object.keys(configPlugin).length === 1);
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -715,9 +729,10 @@ describe('update config.xml to include plugins found in pkg.json', function () {
     },30000);
 });
 
-// Use basePkgJson7 as config.xml contains 2 plugins and pkg.json contains 1 plugin
-describe('update pkg.json to include plugins found in config.xml', function () {
-    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+// Use basePkgJson10 as pkg.json contains (camera plugin: var 1/var 2, splashscreen plugin) 
+// and config contains (camera plugin: var 3, value 1, device plugin)
+describe('update pkg.json AND config.xml to include all plugins and merge variables', function () {
+    var tmpDir = helpers.tmpDir('plugin_test_pkgjson');
     var project = path.join(tmpDir, 'project');
     var results;
 
@@ -725,14 +740,13 @@ describe('update pkg.json to include plugins found in config.xml', function () {
         shell.rm('-rf', tmpDir);
         // Copy then move because we need to copy everything, but that means it will copy the whole directory.
         // Using /* doesn't work because of hidden files.
-        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson7'), tmpDir);
-        shell.mv(path.join(tmpDir, 'basePkgJson7'), project);
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson10'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson10'), project);
         process.chdir(project);
         events.on('results', function(res) { results = res; });
     });
 
     afterEach(function() {
-        var cwd = process.cwd();
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
@@ -746,43 +760,168 @@ describe('update pkg.json to include plugins found in config.xml', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
+    /** Test#013 will check the plugin/variable list in package.json and config.xml. 
+    *   For plugins that are the same, it will merge their variables together for the final list.
+    *   Plugins that are unique to that file, will be copied over to the file that is missing it.
+    *   Config.xml and pkg.json will have identical plugins and variables after cordova prepare.
+    */
+    it('Test#013 : update pkg.json AND config.xml to include all plugins and merge unique variables', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var cfg1 = new ConfigParser(configXmlPath);
+        var configPlugins = cfg1.getPluginIdList();
+        var pkgJson = require(pkgJsonPath);
+        var configPlugin;
+        var configPluginVariables;
 
-    function fullPlatformList() {
+        // Config.xml has 2 plugins and does not have device yet
+        expect(Object.keys(configPlugins).length === 2);
+        expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(-1);
+        expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
+        expect(configPlugins.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+        // Config.xml camera plugin has var_3,value_3 and splashscreen has 0 variables
+        for (var i = 0; i < configPlugins.length; i++) {
+            configPlugin = cfg1.getPlugin(configPlugins[i]);
+            configPluginVariables = configPlugin.variables;
+            if(configPlugin.name === 'cordova-plugin-camera') {
+                expect(configPluginVariables).toEqual({ variable_3: 'value_3'});
+            }
+            if(configPlugin.name === 'cordova-plugin-splashscreen') {
+                expect(configPluginVariables).toEqual({});
+            }
+        }
+        // Expect that pkg.json exists with 3 plugins (camera, device, and splashscreen)
+        expect(pkgJson.cordova.plugins).toBeDefined();
+        expect(Object.keys(pkgJson.cordova.plugins).length === 3);
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+        expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
+        expect(pkgJson.cordova.plugins['cordova-plugin-device']).toBeDefined();
+        // Splashscreen has no variables and camera has var 1 and var 2 and device has var1, value1
+        expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toEqual({});
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: ' ', variable_2: ' ' });
+        expect(pkgJson.cordova.plugins['cordova-plugin-device']).toEqual({ variable_1: 'value_1' });
+
+        emptyPlatformList().then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+         }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            var cfg2 = new ConfigParser(configXmlPath);
+            configPlugins = cfg2.getPluginIdList();
+
+            // Check to make sure that variables were added as expected
+            for (var i = 0; i < configPlugins.length; i++) {
+                configPlugin = cfg2.getPlugin(configPlugins[i]);
+                configPluginVariables = configPlugin.variables;
+                // Config.xml camera variables have been merged, no duplicates
+                if(configPlugin.name === 'cordova-plugin-camera') {
+                    expect(configPluginVariables).toEqual({ variable_1: ' ', 
+                    variable_2: ' ', variable_3: 'value_3' });
+                }
+                // Expect that device has var1, val1 and splashscreen has 0 var
+                if(configPlugin.name === 'cordova-plugin-device') {
+                    expect(configPluginVariables).toEqual({ variable_1: 'value_1'});
+                }
+                if(configPlugin.name === 'cordova-plugin-splashscreen') {
+                    expect(configPluginVariables).toEqual({});
+                }
+            }
+            // Expect pkg.json to have the variables from config.xml
+            expect(Object.keys(pkgJson.cordova.plugins).length === 3);
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-device']).toBeDefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: ' ', variable_2: ' ', variable_3: 'value_3' });
+            // Expect config.xml to have the plugins from pkg.json
+            expect(Object.keys(configPlugins).length === 3);
+            expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
+            expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(1);
+            expect(configPlugins.indexOf('cordova-plugin-splashscreen')).toEqual(2);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
+
+// Use basePkgJson11 as pkg.json contains(splashscreen plugin, camera plugin: var1, value1, var2, value2) and
+// config.xml contains (device plugin, camera plugin: var1, value 1, var2, value 2)
+describe('update pkg.json AND config.xml to include all plugins/merge variables and check for duplicates', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson11'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson11'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
         return cordova.raw.platform('list').then(function() {
             var installed = results.match(/Installed platforms:\n  (.*)/);
             expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
-
-    /** Test#009 will check the platform list in package.json and config.xml. 
-    *   When config.xml has 'splashscreen and camera' and pkg.json only contains 'splashscreen', run cordova prepare
-    *   and pkg.json is updated to include 'camera'. After both files are identical, run cordova prepare
-    *   to double check that neither file is modified once they are the same.
+    /** Test#014 will check the plugin/variable list in package.json and config.xml. 
+    *   If either file is missing a plugin, it will be added with the correct variables.
+    *   If there is a matching plugin name, the variables will be merged and then added
+    *   to config and pkg.json. 
     */
-    it('Test#009 :  if config.xml has 2 plugins and pkg.json has only one of those plugins, update pkg.json to include both', function(done) {
+    it('Test#014 : update pkg.json AND config.xml to include all plugins and merge variables (no dupes)', function(done) {
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var pkgJsonPath = path.join(cwd,'package.json');
         delete require.cache[require.resolve(pkgJsonPath)];
         var cfg1 = new ConfigParser(configXmlPath);
-        var plugins = cfg1.getPluginIdList();
+        var configPlugins = cfg1.getPluginIdList();
         var pkgJson = require(pkgJsonPath);
-        var pluginNames = plugins.map(function(elem) {
-            return elem;
-        });
-        var configPlugArray = pluginNames.slice();
+        var configPlugin;
+        var configPluginVariables;
 
-        // Expect that pkg.json exists with 1 plugin
+        // Config.xml initially has the camera and device plugin and NO splashscreen
+        expect(Object.keys(configPlugins).length === 2);
+        expect(configPlugins.indexOf('cordova-plugin-splashscreen')).toEqual(-1);
+        expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
+        expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(1);
+        // Config.xml camera initially has var1,val1 and var2, val2 and device has no variables
+        for (var i = 0; i < configPlugins.length; i++) {
+            configPlugin = cfg1.getPlugin(configPlugins[i]);
+            configPluginVariables = configPlugin.variables;
+            if(configPlugin.name === 'cordova-plugin-camera') {
+                expect(configPluginVariables).toEqual({ variable_1: 'value_1', variable_2: 'value_2' });
+            }
+            if(configPlugin.name === 'cordova-plugin-device') {
+                expect(configPluginVariables).toEqual({});
+            }
+        }
+        // Expect that pkg.json exists with 2 plugins
         expect(pkgJson.cordova.plugins).toBeDefined();
-        expect(pkgJson.cordova.plugins.length === 0);
-        expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(-1);
-        expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(0);
-        // Expect that config.xml exists with 2 plugins
-        expect(configPlugArray).toBeDefined();
-        expect(configPlugArray.length === 1);
-        expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
-        expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+        expect(Object.keys(pkgJson.cordova.plugins).length === 2);
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+        expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
+        // Pkg.json does not have device yet
+        expect(pkgJson.cordova.plugins['cordova-plugin-device']).toBeUndefined();
+        // Pkg.json camera plugin has var1, value 1 and var3, value 3
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'value_1', variable_3: 'value_3' });
+        // Pkg.json splashscreen has no variables
+        expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toEqual({});
 
         emptyPlatformList().then(function() {
             // Run cordova prepare
@@ -792,49 +931,128 @@ describe('update pkg.json to include plugins found in config.xml', function () {
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
-            plugins = cfg2.getPluginIdList();
-            pluginNames = plugins.map(function(elem) {
-                return elem;
-            });
-            configPlugArray = pluginNames.slice();
-            // Expect that pkg.json exists with 2 plugins
-            expect(pkgJson.cordova.plugins).toBeDefined();
-            expect(pkgJson.cordova.plugins.length === 1);
-            // Expect that config.xml exists with 2 plugins
-            expect(configPlugArray).toBeDefined();
-            expect(configPlugArray.length === 1);
-            // Expect config to contain camera and splashscreen
-            expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
-            expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(1);
-            // Expect pkg.json to contain camera and splashscreen
-            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(0);
-            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(1);
-        }).then(function() {
-            // Now that files are the same, run cordova prepare again to check that neither 
-            // file gets modified again since both are identical now
+            configPlugins = cfg2.getPluginIdList();
+
+            // Check to make sure that variables were added as expected
+            for (var i = 0; i < configPlugins.length; i++) {
+                configPlugin = cfg2.getPlugin(configPlugins[i]);
+                configPluginVariables = configPlugin.variables;
+                // Config.xml camera variables have been merged, no duplicates
+                if(configPlugin.name === 'cordova-plugin-camera') {
+                    expect(configPluginVariables).toEqual({ variable_1: 'value_1',
+                    variable_3: 'value_3', variable_2: 'value_2' });
+                }
+                // Expect that splashscreen and device have 0 variables
+                if(configPlugin.name === 'cordova-plugin-device') {
+                    expect(configPluginVariables).toEqual({});
+                }
+                if(configPlugin.name === 'cordova-plugin-splashscreen') {
+                    expect(configPluginVariables).toEqual({});
+                }
+            }
+            // Config.xml now has the camera, splashscreen, and device plugin
+            expect(Object.keys(configPlugins).length === 3);
+            expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
+            expect(configPlugins.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+            expect(configPlugins.indexOf('cordova-plugin-device')).toEqual(2);
+            // Pkg.json has all 3 plugins
+            expect(Object.keys(pkgJson.cordova.plugins).length === 3);
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toBeDefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-device']).toBeDefined();
+            // Expect that splashscreen and device have 0 variables
+            expect(pkgJson.cordova.plugins['cordova-plugin-splashscreen']).toEqual({});
+            expect(pkgJson.cordova.plugins['cordova-plugin-device']).toEqual({});
+            // Expect that the variables from config have been merged with the variables 
+            // from pkg.json to the camera plugin
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'value_1',
+            variable_3: 'value_3', variable_2: 'value_2' });
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
+
+// Use basePkgJson12 as config.xml has 0 plugins and pkg.json has 1
+describe('update config.xml to include the plugin that is in pkg.json', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson12'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson12'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+    /** Test#015 will check the plugin/variable list in package.json and config.xml. 
+    *   When config has 0 plugins, it will get updated with the plugins from
+    *   pkg.json.
+    */
+    it('Test#015 : update config.xml to include all plugins/variables from pkg.json', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var cfg1 = new ConfigParser(configXmlPath);
+        var configPlugins = cfg1.getPluginIdList();
+        var pkgJson = require(pkgJsonPath);
+        var configPlugin;
+        var configPluginVariables;
+        // Config.xml is initially empty and has no plugins
+        expect(Object.keys(configPlugins).length === 0);
+        // Expect that pkg.json exists with 1 plugin
+        expect(pkgJson.cordova.plugins).toBeDefined();
+        expect(Object.keys(pkgJson.cordova.plugins).length === 1);
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+        // Pkg.json camera plugin has var1, value 1
+        expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'value_1' });
+
+        emptyPlatformList().then(function() {
+            // Run cordova prepare
             return cordova.raw.prepare();
         }).then(function() {
             // Delete any previous caches of require(package.json)
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             var cfg2 = new ConfigParser(configXmlPath);
-            plugins = cfg2.getPluginIdList();
-            pluginNames = plugins.map(function(elem) {
-                return elem;
-            });
-            configPlugArray = pluginNames.slice();
-            // Expect that pkg.json exists with 2 plugins
-            expect(pkgJson.cordova.plugins).toBeDefined();
-            expect(pkgJson.cordova.plugins.length === 1);
-            // Expect that config.xml exists with 2 plugins
-            expect(configPlugArray).toBeDefined();
-            expect(configPlugArray.length === 1);
-            // Expect config to contain camera and splashscreen
-            expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
-            expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(1);
-            // Expect pkg.json to contain camera and splashscreen
-            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(0);
-            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(1);
+            configPlugins = cfg2.getPluginIdList();
+
+            // Check to make sure that the variables were added as expected
+            for (var i = 0; i < configPlugins.length; i++) {
+                configPlugin = cfg2.getPlugin(configPlugins[i]);
+                configPluginVariables = configPlugin.variables;
+                // Config.xml camera variables have been added
+                if(configPlugin.name === 'cordova-plugin-camera') {
+                    expect(configPluginVariables).toEqual({ variable_1: 'value_1' });
+                }
+            }
+            // Config.xml now has the camera plugin
+            expect(Object.keys(configPlugins).length === 1);
+            expect(configPlugins.indexOf('cordova-plugin-camera')).toEqual(0);
+            // No changes to pkg.json
+            expect(Object.keys(pkgJson.cordova.plugins).length === 1);
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toEqual({ variable_1: 'value_1' });
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/b247e12d/cordova-lib/spec-cordova/pkgJson.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson.spec.js b/cordova-lib/spec-cordova/pkgJson.spec.js
index 4347a0e..45eb8ee 100644
--- a/cordova-lib/spec-cordova/pkgJson.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson.spec.js
@@ -18,47 +18,10 @@
 */
 var helpers = require('./helpers'),
     path = require('path'),
-    fs = require('fs'),
     shell = require('shelljs'),
-    superspawn = require('cordova-common').superspawn,
-    Q = require('q'),
     events = require('cordova-common').events,
-    cordova = require('../src/cordova/cordova'),
-    rewire = require('rewire'),
-    prepare = require('../src/cordova/prepare'),
-    platforms = require('../src/platforms/platforms'),
-    platform = rewire('../src/cordova/platform.js');
-
-var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
-var pluginsDir = path.join(__dirname, 'fixtures', 'plugins');
-
-function addPlugin(target, id, options) {
-    // Checks that there are no plugins yet.
-    return cordova.raw.plugin('list').then(function() {
-        expect(results).toMatch(/No plugins added/gi);
-    }).then(function() {
-        // Adds a fake plugin from fixtures.
-        return cordova.raw.plugin('add', target, options);
-    }).then(function() {
-        expect(path.join(project, 'plugins', id, 'plugin.xml')).toExist();
-    }).then(function() {
-        return cordova.raw.plugin('ls');
-    }).then(function() {
-        expect(results).toContain(id);
-    });
-}
-// Runs: remove, list.
-function removePlugin(id, options) {
-    return cordova.raw.plugin('rm', id)
-    .then(function() {
-        // The whole dir should be gone.
-        expect(path.join(project, 'plugins', id)).not.toExist();
-    }).then(function() {
-        return cordova.raw.plugin('ls');
-    }).then(function() {
-        expect(results).toMatch(/No plugins added/gi);
-    });
-}
+    cordova = require('../src/cordova/cordova');
+
 // This group of tests checks if plugins are added and removed as expected from package.json.
 describe('plugin end-to-end', function() {
     var pluginId = 'cordova-plugin-device';
@@ -199,6 +162,40 @@ describe('plugin end-to-end', function() {
             expect(err).toBeUndefined();
         }).fin(done);
     });
+    it('Test#005 : should successfully add and remove multiple plugins with save & fetch', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        var pkgJson;
+    
+        expect(pkgJsonPath).toExist();
+
+        // Add the plugin with --save
+        return cordova.raw.plugin('add', [pluginId,'cordova-plugin-device-motion'], {'save':true, 'fetch':true})
+        .then(function() {
+            // Check that the plugin add was successful.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins).not.toBeUndefined();
+            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-device-motion']).toBeDefined();
+            expect(pkgJson.dependencies[pluginId]).toBeDefined();
+            expect(pkgJson.dependencies['cordova-plugin-device-motion']).toBeDefined();
+        }).then(function() {
+            // And now remove it with --save.
+            return cordova.raw.plugin('rm', [pluginId,'cordova-plugin-device-motion'], {'save':true, 'fetch':true})
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Checking that the plugin removed is in not in the platforms
+            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
+            expect(pkgJson.cordova.plugins['cordova-plugin-device-motion']).toBeUndefined();
+            expect(pkgJson.dependencies[pluginId]).toBeUndefined();
+            expect(pkgJson.dependencies['cordova-plugin-device-motion']).toBeUndefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    });
 });
 
 // This group of tests checks if platforms are added and removed as expected from package.json.
@@ -313,7 +310,7 @@ describe('platform end-to-end with --save', function () {
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             // Platform list should be empty and helpers.testPlatform should NOT have been added.
-            expect(pkgJson.cordova).toBeUndefined();
+            expect(pkgJson.cordova.platforms.length).toEqual(0);
         }).then(fullPlatformList)
         .fail(function(err) {
             expect(err).toBeUndefined();

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/b247e12d/cordova-lib/src/cordova/restore-util.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/restore-util.js b/cordova-lib/src/cordova/restore-util.js
index 692c374..bfb3830 100644
--- a/cordova-lib/src/cordova/restore-util.js
+++ b/cordova-lib/src/cordova/restore-util.js
@@ -30,7 +30,6 @@ var cordova_util = require('./util'),
 exports.installPluginsFromConfigXML = installPluginsFromConfigXML;
 exports.installPlatformsFromConfigXML = installPlatformsFromConfigXML;
 
-
 function installPlatformsFromConfigXML(platforms, opts) {
     events.emit('verbose', 'Checking config.xml and package.json for saved platforms that haven\'t been added to the project');
 
@@ -40,169 +39,104 @@ function installPlatformsFromConfigXML(platforms, opts) {
     var engines = cfg.getEngines();
     var pkgJsonPath = path.join(projectHome,'package.json');
     var pkgJson;
-    var targetsPkgJson;
-    var comboArray; 
+    var pkgJsonPlatforms;
+    var comboArray = []; 
     var targets;
-    var targetsConfig;
-    var engines;
+    var configPlatforms = [];
     var installAllPlatforms;
     var platformPath;
     var platformAlreadyAdded;
     var t;
-    var ConfigAndPkgJsonArray = [];
     var modifiedPkgJson = false;
     var modifiedConfigXML = false;
-    var modifiedPkgJsonPlugin = false;
-    var modifiedConfigXMLPlugin = false;
-    var targetPlatformsArray = [];
+    var configObject;
 
     if(fs.existsSync(pkgJsonPath)) {
         pkgJson = require(pkgJsonPath);
     }
     if(pkgJson !== undefined && pkgJson.cordova !== undefined && pkgJson.cordova.platforms !== undefined) {
-        targetsPkgJson = pkgJson.cordova.platforms;
+        pkgJsonPlatforms = pkgJson.cordova.platforms;
     } 
+    
     if(cfg !== undefined) {
-        targetsConfig = [];
-        engines = cfg.getEngines(projectHome);
-        installAllPlatforms = !platforms || platforms.length === 0;
-
-        targets = engines.map(function(engine) {
-            platformPath = path.join(projectHome, 'platforms', engine.name);
-            platformAlreadyAdded = fs.existsSync(platformPath);
 
-            // If no platforms are specified we add all.
-            if ((installAllPlatforms || platforms.indexOf(engine.name) > -1) && !platformAlreadyAdded) {
-                t = engine.name;
-                if (engine.spec) {
-                    //t += '@' + engine.spec;
-                    targetsConfig.push(t);
-                }
-                return t;
-            }
-        });   
-        if (targetsPkgJson !== undefined) {
+        if (pkgJsonPlatforms !== undefined) {
             // Combining arrays and checking duplicates
-            comboArray = targetsPkgJson.slice();
-        } else {
-            comboArray = [];
+            comboArray = pkgJsonPlatforms.slice();
         }
-        targetsConfig.forEach(function(item) {
+
+        engines = cfg.getEngines(projectHome)
+        configPlatforms = engines.map(function(Engine) {
+            configPlatName = Engine.name;
+            return configPlatName;
+        });
+        
+        configPlatforms.forEach(function(item) {
             if(comboArray.indexOf(item) < 0 ) {
                 comboArray.push(item);
             }
         });
-        // If config.xml & pkgJson exist and the cordova key is undefined, create a cordova key.
-        if (cfg !== undefined && pkgJson !== undefined && pkgJson.cordova === undefined) {
-            pkgJson.cordova = {};
-        }
-        // If there is no platforms array, create an empty one.
-        if (pkgJson.cordova.platforms === undefined) {
-            pkgJson.cordova.platforms = [];
-            // Get a list of platforms names from config.xml.
-            targetPlatformsArray = engines.map(function(mEngine) {
-                t = mEngine.name;
-                return t;
-            }); 
-
-        if (!targets || !targets.length) {
-           return Q('No platforms found in config.xml that haven\'t been added to the project');
-        }
-
-            var uniq = targetPlatformsArray.reduce(function(a,b){
-                if (a.indexOf(b) < 0 ) a.push(b);
+        //comboArray should have all platforms from config.xml & package.json
+        //remove dupes in comboArray & sort
+        var uniq = comboArray.reduce(function(a,b) {
+            if (a.indexOf(b) < 0 ) a.push(b);
                 return a;
-            },[]);
-            targetPlatformsArray = uniq;
+        },[]);
+        comboArray = uniq;
+        comboArray = comboArray.sort();
 
-            // Add the platforms from config.xml to package.json
-            pkgJson.cordova.platforms = targetPlatformsArray;
-            modifiedPkgJson = true;
-            //fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+        // No platforms to restore from either config.xml or package.json
+        if (comboArray.length <= 0) {
+           return Q('No platforms found in config.xml or package.json. Nothing to restore');
         }
-        // If config.xml and pkg.json exist and both already contain platforms, run cordova prepare
-        // so that both files are identical
-        if(cfg !== undefined && targetsConfig !== undefined && pkgJson.cordova.platforms !== undefined) {
-            
-            targetsConfig = engines.map(function(mEngine) {
-                t = mEngine.name;
-                //targetsConfig.push(t);
-                return t;
-            });
-
-            var uniq = targetsConfig.reduce(function(a,b){
-                if (a.indexOf(b) < 0 ) a.push(b);
-                return a;
-            },[]);
-            targetsConfig = uniq;
-        }
-        if (pkgJson.cordova.platforms !== undefined) {
-            // Create a new array based on cordova.platforms
-            pkgJson.cordova.platforms.forEach(function(item) {
-                if (ConfigAndPkgJsonArray.indexOf(item) < 0 ) 
-                    ConfigAndPkgJsonArray.push(item);
-            });
-        }
-        targetsConfig.forEach(function(item) {
-            if(ConfigAndPkgJsonArray.indexOf(item) < 0 ) {
-                ConfigAndPkgJsonArray.push(item);
-            }
-        });
 
-        //sort the arrays alphabetically 
-        ConfigAndPkgJsonArray = ConfigAndPkgJsonArray.sort();
-        pkgJson.cordova.platforms = pkgJson.cordova.platforms.sort();
+        //if no package.json, don't bother
+        if (pkgJson !== undefined) { 
 
-        // Get list of engine names and create a new array of engines from config.xml.
-        var engNames = engines.map(function(elem) {
-            return elem.name;
-        });
-        var configEngArray = engNames.sort().slice();
+            // If config.xml & pkgJson exist and the cordova key is undefined, create a cordova key.
+            if (pkgJson.cordova === undefined) {
+                pkgJson.cordova = {};
+            }
+            // If there is no platforms array, create an empty one.
+            if (pkgJson.cordova.platforms === undefined) {
+                pkgJson.cordova.platforms = [];
+            }
+            // If comboArray has the same platforms as pkg.json, no modification to pkg.json.
+            if (comboArray.toString() === pkgJson.cordova.platforms.sort().toString()) {
+                events.emit('verbose', 'Config.xml and package.json platforms are the same. No pkg.json modification.');
+            } else {
+                // modify pkg.json to include the elements
+                // from the comboArray array so that the arrays are identical
+                events.emit('verbose', 'Config.xml and package.json platforms are different. Updating package.json with most current list of platforms.');
+                modifiedPkgJson = true;
+            }
 
-        // If ConfigAndPkgJson array has the same platforms as pkg.json, no modification to pkg.json.
-        if(ConfigAndPkgJsonArray.toString() === pkgJson.cordova.platforms.toString()) {
-            events.emit('warn', 'Config.xml and package.json platforms are the same. No pkg.json modification.');
-        }
-        // If ConfigAndPkgJson array has the same platforms as config.xml, no modification to config.xml.
-        if(ConfigAndPkgJsonArray.toString() === configEngArray.toString()) {
-            events.emit('warn', 'Package.json and config.xml are the same. No config.xml modification.');
-        }
-        // If ConfigAndPkgJson array do not have the same platforms, modify pkg.json to include the elements
-        // from the ConfigAndPkgJson array so that the arrays are identical
-        if(ConfigAndPkgJsonArray.toString() !== pkgJson.cordova.platforms.toString()) {
-            events.emit('warn', 'Config.xml and package.json platforms are not the same. Updating package.json with most current list of platforms.');
-            ConfigAndPkgJsonArray.forEach(function(item) {
-                if(pkgJson.cordova.platforms.indexOf(item) < 0 ) {
-                    pkgJson.cordova.platforms.push(item);
-                    modifiedPkgJson = true;
-                }
-            });
-        }
-        // If ConfigAndPkgJson array do not have the same platforms, modify config.xml to include the elements
-        // from the ConfigAndPkgJson array so that the arrays are identical
-        if(ConfigAndPkgJsonArray.toString() !== configEngArray.toString()) {
-            events.emit('warn', 'Package.json and config.xml platforms are not the same. Updating config.xml with most current list of platforms.');
-            ConfigAndPkgJsonArray.forEach(function(item) {
-                if(configEngArray.indexOf(item) < 0 ) {
-                    configEngArray.push(item);
-                    modifiedConfigXML = true;
-                    cfg.addEngine(item);
-                }
-            });
+            // If comboArray has the same platforms as config.xml, no modification to config.xml.
+            if(comboArray.length === configPlatforms.length && comboArray.toString() === configPlatforms.sort().toString()) {
+                events.emit('verbose', 'Package.json and config.xml are the same. No config.xml modification.');
+            } else {
+                events.emit('verbose', 'Package.json and config.xml platforms are different. Updating config.xml with most current list of platforms.');
+                comboArray.forEach(function(item) {
+                    if(configPlatforms.indexOf(item) < 0 ) {
+                        cfg.addEngine(item);
+                        modifiedConfigXML = true;
+                    }
+                });
+            }
         }
         // Write and update pkg.json if it has been modified.
         if (modifiedPkgJson === true) {
-            //sort then write
-            pkgJson.cordova.platforms = pkgJson.cordova.platforms.sort();
+            pkgJson.cordova.platforms = comboArray;
+            //pkgJson.cordova.dependencies[configPlatName] =configPlatSpec;
             fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
         }
         if (modifiedConfigXML === true) {
+            configPlatforms = comboArray;
             cfg.write();
         }
         if (!comboArray || !comboArray.length) {
             return Q('No platforms found in config.xml and/or package.json that haven\'t been added to the project');
         }
-
     }
     // Run `platform add` for all the platforms separately
     // so that failure on one does not affect the other.
@@ -231,113 +165,97 @@ function installPluginsFromConfigXML(args) {
     var plugins_dir = path.join(projectRoot, 'plugins');
     var pkgJsonPath = path.join(projectRoot,'package.json');
     var pkgJson;
-    var pkgJsonArray = [];
-    var comboPluginArray;
-
-    // Get all configured plugins
-    var pluginIdConfig = cfg.getPluginIdList();
-    if (0 === pluginIdConfig.length) {
-        return Q('No plugins found in config.xml that haven\'t been added to the project');
-    }
-    // Check if path exists and require pkgJsonPath
+    var modifiedPkgJson = false;
+    var modifiedConfigXML = false;
+    //TODO: rename to comboObject?
+    var mergedPluginDataObj;
+    var comboPluginIdArray;
+    var configPlugin;
+    var configPluginVariables;
+    var pkgJsonPluginVariables;
+
+    //Check if path exists and require pkgJsonPath
     if(fs.existsSync(pkgJsonPath)) {
         pkgJson = require(pkgJsonPath);
     }
-    if (pkgJson.cordova === undefined) {
-        pkgJson.cordova = {};
-    }
-    // (In pkg.json), if there is a platforms array and not plugins array, create a new pluginIdConfig array 
-    // and add all plugins from config.xml to the new plugins array.
-    if (cfg !== undefined && pkgJson !== undefined && pkgJson.cordova.platforms !== undefined && 
-    pkgJson.cordova.plugins === undefined) {
-        pkgJson.cordova.plugins = {};
-        pluginIdConfig.forEach(function(foo) {
-            pkgJson.cordova.plugins[foo] = {}
-        });
-        fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
-    }
-    // (In pkg.json), if there is no platforms array and no plugins array, create a new plugins array
-    // and add all plugins from config.xml to the new plugins array.
-    if (cfg !== undefined && pkgJson !== undefined && pkgJson.cordova.platforms === undefined && 
-    pkgJson.cordova.plugins === undefined) {
-        pkgJson.cordova.platforms = [];
-        pkgJson.cordova.plugins = {};
-        pluginIdConfig.forEach(function(foo) {
-            pkgJson.cordova.plugins[foo] = {}
-        });
-        fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
-    }
 
-    pkgJsonArray = Object.keys(pkgJson.cordova.plugins);
+    if(pkgJson !== undefined && pkgJson.cordova !== undefined && pkgJson.cordova.plugins !== undefined) {
+        comboPluginIdArray = Object.keys(pkgJson.cordova.plugins);
+        // Create a merged plugin data array (mergedPluginDataObj)
+        // and add all of the package.json plugins to mergedPluginDataObj
+        mergedPluginDataObj = pkgJson.cordova.plugins;
+    } else {
+        mergedPluginDataObj = {};
+        comboPluginIdArray = [];
+    }
 
-    var pluginNames = pluginIdConfig.map(function(elem) {
-        return elem.name;
-    });
+    // Get all config.xml plugin ids
+    var pluginIdConfig = cfg.getPluginIdList();
+    if(pluginIdConfig === undefined) {
+        pluginIdConfig = [];
+    }
 
-    // Create a merged plugin data array (mergedPluginDataObj)
-    // and add all of the package.json plugins to mergedPluginDataObj
-    var mergedPluginDataObj = pkgJson.cordova.plugins;
-    var mergedPluginDataArray = pkgJsonArray;
+    if(pkgJson !== undefined) {
+        if (pkgJson.cordova === undefined) {
+            pkgJson.cordova = {};
+        }
+        if (pkgJson.cordova.plugins === undefined) {
+            pkgJson.cordova.plugins = {};
+        }
 
-    // Check to see which plugins are initially the same in pkg.json and config.xml
-    // Merge identical plugins and their variables together first
-    for (var i = 0; i < mergedPluginDataArray.length; i++) {
-        if(pluginIdConfig.includes(mergedPluginDataArray[i])) {
-            var configPlugin = cfg.getPlugin(mergedPluginDataArray[i]);
-            var configPluginVariables = configPlugin.variables;
-            var pkgJsonPluginVariables = mergedPluginDataObj[mergedPluginDataArray[i]];
-            for(var key in configPluginVariables) {
-                // Handle conflicts, package.json wins
-                if(pkgJsonPluginVariables[key] === undefined) {
-                    pkgJsonPluginVariables[key] = configPluginVariables[key];
-                    mergedPluginDataObj[mergedPluginDataArray[i]][key] = configPluginVariables[key];
+        // Check to see which plugins are initially the same in pkg.json and config.xml
+        // add missing plugin variables in package.json from config.xml
+        comboPluginIdArray.forEach(function(item) {
+            if(pluginIdConfig.includes(item)) {
+                configPlugin = cfg.getPlugin(item);
+                configPluginVariables = configPlugin.variables;
+                pkgJsonPluginVariables = mergedPluginDataObj[item];
+                for(var key in configPluginVariables) {
+                    // Handle conflicts, package.json wins
+                    // only add the variable to package.json if it doesn't already exist
+                    if(pkgJsonPluginVariables[key] === undefined) {
+                        pkgJsonPluginVariables[key] = configPluginVariables[key];
+                        mergedPluginDataObj[item][key] = configPluginVariables[key];
+                        modifiedPkgJson = true;
+                    }
                 }
             }
+        })
+
+        // Check to see if pkg.json plugin(id) and config plugin(id) match
+        if(comboPluginIdArray.sort().toString() !== pluginIdConfig.sort().toString()) {
+            // If there is a config plugin that does NOT already exist in
+            // mergedPluginDataArray, add it and its variables
+            pluginIdConfig.forEach(function(item) {
+                if(comboPluginIdArray.indexOf(item) < 0) {
+                    comboPluginIdArray.push(item);
+                    var configXMLPlugin = cfg.getPlugin(item);
+                    mergedPluginDataObj[item] = configXMLPlugin.variables;
+                    modifiedPkgJson = true;
+                }
+            });
         }
-    }
-    // Check to see if pkg.json plugin(id) and config plugin(id) match
-    if(mergedPluginDataArray.sort().toString() !== pluginIdConfig.sort().toString()) {
-        // If there is a config plugin that does NOT already exist in
-        // mergedPluginDataArray, add it and its variables
-        pluginIdConfig.forEach(function(item) {
-            if(mergedPluginDataArray.indexOf(item) < 0) {
-                mergedPluginDataArray.push(item);
-                var configXMLPlugin = cfg.getPlugin(item);
-                mergedPluginDataObj[item] = configXMLPlugin.variables;
-            }
-        });
-    }
-    // Write to pkg.Json
-    // TODO: toString comparision to see if they are the same and then write
-    pkgJson.cordova.plugins = mergedPluginDataObj;
-    fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
 
+        if (modifiedPkgJson === true) {
+            pkgJson.cordova.plugins = mergedPluginDataObj;
+            fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+        }
+    }
+    
     // Write config.xml
-    mergedPluginDataArray.forEach(function(plugID) {
+    comboPluginIdArray.forEach(function(plugID) {
+        pluginIdConfig.push(plugID);
         cfg.removePlugin(plugID);
+        //TODO: spec needs to be added to addPlugin
         cfg.addPlugin({name:plugID}, mergedPluginDataObj[plugID]); 
+        modifiedConfigXML = true;
     });
-    cfg.write();
 
-    // If config.xml and pkg.json exist and both already contain plugins, run cordova prepare
-    // to check if these plugins are identical.
-    if(cfg !== undefined && pkgJson.cordova.plugins !== undefined) {
-        if(pkgJsonArray.toString() === pluginIdConfig.toString() && pkgJsonArray.length === pluginIdConfig.length) {
-            events.emit('warn', 'Config.xml and pkgJson plugins are the same. No pkg.json or config.xml modification.');
-        }
-
-        if(pkgJsonArray.toString() !== pluginIdConfig.toString() || pkgJsonArray.length !== pluginIdConfig.length) {
-           events.emit('warn', 'Config.xml and pkgJson plugins are different.');
-            // Combining arrays and checking duplicates
-
-            comboPluginArray = pkgJsonArray.slice();
-            pluginIdConfig.forEach(function(item) {
-                if(comboPluginArray.indexOf(item) < 0) {
-                    comboPluginArray.push(item);
-                }
-            });
-        }
+    if (modifiedConfigXML === true) {
+        cfg.write();    
     }
+    
+
     // Intermediate variable to store current installing plugin name
     // to be able to create informative warning on plugin failure
     var pluginName;


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


[06/12] cordova-lib git commit: CB-11960 - updated failing tests due to bug in cordova-create script

Posted by st...@apache.org.
CB-11960 - updated failing tests due to bug in cordova-create script


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/1acdbff5
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/1acdbff5
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/1acdbff5

Branch: refs/heads/master
Commit: 1acdbff5ca5c9df4e523176c7b41676cdd9f9e59
Parents: b77129a
Author: Audrey So <au...@apache.org>
Authored: Wed Jan 25 10:41:40 2017 -0800
Committer: Audrey So <au...@apache.org>
Committed: Thu Jan 26 17:36:52 2017 -0800

----------------------------------------------------------------------
 .../fixtures/basePkgJson4/package.json          |   4 +-
 .../fixtures/basePkgJson5/config.xml            |   2 +-
 .../spec-cordova/pkgJson-restore.spec.js        | 125 ++++++++++---------
 3 files changed, 66 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/1acdbff5/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
index f26e7c8..10248f3 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
@@ -5,7 +5,7 @@
     "main": "index.js",
     "dependencies": {
         "cordova-android": "^3.1.0",
-        "cordova-ios": "^4.3.0"
+        "cordova-browser": "^4.1.0"
     },
     "devDependencies": {},
     "scripts": {
@@ -16,7 +16,7 @@
     "cordova": {
         "platforms": [
             "android",
-            "ios"
+            "browser"
         ]
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/1acdbff5/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
index d6dc496..28663fc 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
@@ -12,5 +12,5 @@
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
     <engine name="android" spec="6.0.0"/>
-    <engine name="ios" spec="^4.3.0"/>
+    <engine name="browser" spec="^4.1.0"/>
 </widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/1acdbff5/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
index 496ec32..dd8e197 100644
--- a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -35,12 +35,13 @@ describe('tests platform/spec restore with --save', function () {
     var results;
 
     beforeEach(function() {
-        shell.rm('-rf', tmpDir);
+        shell.rm('-rf', project);
         // Copy then move because we need to copy everything, but that means it will copy the whole directory.
         // Using /* doesn't work because of hidden files.
         shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
         shell.mv(path.join(tmpDir, 'basePkgJson'), project);
         process.chdir(project);
+        delete process.env.PWD;
         events.on('results', function(res) { results = res; });
     });
 
@@ -67,7 +68,7 @@ describe('tests platform/spec restore with --save', function () {
         var pkgJsonPath = path.join(cwd,'package.json');
         delete require.cache[require.resolve(pkgJsonPath)];
         var pkgJson;
-        var iosPlatform = 'ios';
+        var androidPlatform = 'android';
         var configXmlPath = path.join(cwd, 'config.xml');
         var firstCharConfig;
         var engines;
@@ -75,16 +76,16 @@ describe('tests platform/spec restore with --save', function () {
         var engSpec;
 
         emptyPlatformList().then(function() {
-            // Add the ios platform with save, fetch
-            return cordova.raw.platform('add', iosPlatform, {'save':true , 'fetch':true});
+            // Add platform with save, fetch
+            return cordova.raw.platform('add', androidPlatform, {'save':true , 'fetch':true});
         }).then(function() {
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Added to ios properly to pkg.json
+            // Added platform properly to pkg.json
             expect(pkgJson.cordova.platforms).toBeDefined();
-            expect(pkgJson.cordova.platforms.indexOf(iosPlatform)).toBeGreaterThan(-1);
+            expect(pkgJson.cordova.platforms.indexOf(androidPlatform)).toBeGreaterThan(-1);
             // When spec is added to pkg.json, first char is '^'.
-            expect(pkgJson.dependencies['cordova-'+iosPlatform].charAt(0)).toEqual('^');
+            expect(pkgJson.dependencies['cordova-'+androidPlatform].charAt(0)).toEqual('^');
 
             var cfg = new ConfigParser(configXmlPath);
             engines = cfg.getEngines();
@@ -94,8 +95,8 @@ describe('tests platform/spec restore with --save', function () {
             engSpec = engines.map(function(elem) {
                 return elem.spec;
             });
-            // Only ios platform added to config.xml
-            expect(engNames).toEqual([ 'ios' ]);
+            // Only android platform added to config.xml
+            expect(engNames).toEqual([ androidPlatform ]);
             expect(engines.length === 1);
             // When spec is added to config.xml, first char is '~'.
             firstCharConfig = engSpec[0].charAt(0);
@@ -106,9 +107,9 @@ describe('tests platform/spec restore with --save', function () {
         }).then(function() {
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // No changes to pkg.json spec for ios.
-            expect(pkgJson.dependencies['cordova-'+iosPlatform].charAt(0)).toEqual('^');
-            expect(pkgJson.cordova.platforms.indexOf(iosPlatform)).toBeGreaterThan(-1);
+            // No changes to pkg.json spec for android.
+            expect(pkgJson.dependencies['cordova-'+androidPlatform].charAt(0)).toEqual('^');
+            expect(pkgJson.cordova.platforms.indexOf(androidPlatform)).toBeGreaterThan(-1);
             // Config.xml spec (first char) should change from '~' to '^'.
             var cfg1 = new ConfigParser(configXmlPath);
             engines = cfg1.getEngines();
@@ -121,13 +122,13 @@ describe('tests platform/spec restore with --save', function () {
             firstCharConfig = engSpec[0].charAt(0);
             // When spec is added to config.xml, first char is '~'.
             expect(firstCharConfig === '^');
-            expect(engNames).toEqual([ 'ios' ]);
+            expect(engNames).toEqual([ androidPlatform ]);
             expect(engines.length === 1);
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
     // Cordova prepare needs extra wait time to complete.
-    },30000);
+    },300000);
 
     /** Test#017
     *   When platform is added with url and fetch and restored with fetch, 
@@ -263,7 +264,7 @@ describe('tests platform/spec restore with --save', function () {
             expect(path.join(pluginsFolderPath, 'cordova-plugin-splashscreen')).not.toExist();
         }).then(function() {
             // Add platform (so that prepare can run).
-            return cordova.raw.platform('add', 'ios', {'save':true});
+            return cordova.raw.platform('add', 'browser', {'save':true});
         }).then(function() {
             // Run cordova prepare with fetch.
             return cordova.raw.prepare({'fetch':true});
@@ -336,11 +337,11 @@ describe('tests platform/spec restore with --save', function () {
         var pkgJsonPath = path.join(cwd,'package.json');
         var pkgJson;
         var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
-        var secondPlatformAdded = 'ios';
+        var secondPlatformAdded = 'browser';
         var platformsJson;
 
         emptyPlatformList().then(function() {
-            // Add 'ios' platform to project without --save
+            // Add 'browser' platform to project without --save
             return cordova.raw.platform('add', secondPlatformAdded);
         }).then(function() {
             // Add helpers.testPlatform to project with --save
@@ -385,7 +386,7 @@ describe('tests platform/spec restore with --save', function () {
             platformsJson = require(platformsFolderPath);
             // Expect "helpers.testPlatform" to be in the installed platforms list.
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
-            // Expect that 'ios' will not be in platforms.json and has not been restored.
+            // Expect that 'browser' will not be in platforms.json and has not been restored.
             expect(platformsJson[secondPlatformAdded]).toBeUndefined();
         }).fail(function(err) {
             expect(err).toBeUndefined();
@@ -466,7 +467,7 @@ describe('files should not be modified if their platforms are identical', functi
     },30000);
 });
 
-// Use a new basePkgJson5 as config.xml contains android/ios and pkg.json contains android
+// Use a new basePkgJson5 as config.xml contains android/browser and pkg.json contains android
 describe('update pkg.json to include platforms in config.xml', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
@@ -497,12 +498,12 @@ describe('update pkg.json to include platforms in config.xml', function () {
         });
     }
     /** Test#005 will check the platform list in package.json and config.xml. 
-    *   When config.xml has 'android and ios' and pkg.json only contains 'android', run cordova
-    *   and pkg.json is updated to include 'ios'. This test will also check that pkg.json
+    *   When config.xml has 'android and browser' and pkg.json only contains 'android', run cordova
+    *   and pkg.json is updated to include 'browser'. This test will also check that pkg.json
     *   is updated with the correct spec/dependencies when restored. Checks that specs are
     *   added properly, too.
     */
-    it('Test#005 : if config.xml has android & ios platforms and pkg.json has android, update pkg.json to also include ios with spec', function(done) {
+    it('Test#005 : if config.xml has android & browser platforms and pkg.json has android, update pkg.json to also include browser with spec', function(done) {
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var cfg = new ConfigParser(configXmlPath);
@@ -515,24 +516,24 @@ describe('update pkg.json to include platforms in config.xml', function () {
         });
         var configEngArray = engNames.slice();
         var androidPlatform = 'android';
-        var iosPlatform = 'ios';
+        var browserPlatform = 'browser';
        
-        // Config.xml contains(android & ios) and pkg.json contains android (basePkgJson5).
+        // Config.xml contains(android & browser) and pkg.json contains android (basePkgJson5).
         expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
-        expect(configEngArray.indexOf(iosPlatform)).toBeGreaterThan(-1);
-        // pkg.json should not contain 'ios' platform before cordova prepare.
-        expect(pkgJson.cordova.platforms.indexOf(iosPlatform)).toEqual(-1);
+        expect(configEngArray.indexOf(browserPlatform)).toBeGreaterThan(-1);
+        // pkg.json should not contain 'browser' platform before cordova prepare.
+        expect(pkgJson.cordova.platforms.indexOf(browserPlatform)).toEqual(-1);
         expect(pkgJson.cordova.platforms.indexOf(androidPlatform)).toBeGreaterThan(-1);
-        // pkg.json ios/android specs should be undefined.
-        expect(pkgJson.dependencies[iosPlatform]).toBeUndefined();
+        // pkg.json browser/android specs should be undefined.
+        expect(pkgJson.dependencies[browserPlatform]).toBeUndefined();
         expect(pkgJson.dependencies[androidPlatform]).toBeUndefined();
         emptyPlatformList().then(function() {
             return cordova.raw.prepare();
         }).then(function() {
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Expect 'ios' to be added to pkg.json.
-            expect(pkgJson.cordova.platforms.indexOf(iosPlatform)).toBeGreaterThan(-1);
+            // Expect 'browser' to be added to pkg.json.
+            expect(pkgJson.cordova.platforms.indexOf(browserPlatform)).toBeGreaterThan(-1);
             // Expect 'android' to still be there in pkg.json.
             expect(pkgJson.cordova.platforms.indexOf(androidPlatform)).toBeGreaterThan(-1);
             // Expect both pkg.json and config.xml to each have both platforms in their arrays.
@@ -540,9 +541,9 @@ describe('update pkg.json to include platforms in config.xml', function () {
             expect(pkgJson.cordova.platforms.length === 2);
             // No changes to config.xml.
             expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
-            expect(configEngArray.indexOf(iosPlatform)).toBeGreaterThan(-1);
+            expect(configEngArray.indexOf(browserPlatform)).toBeGreaterThan(-1);
             // Platform specs from config.xml have been added to pkg.json.
-            expect(pkgJson.dependencies['cordova-ios']).toEqual('^4.3.0');
+            expect(pkgJson.dependencies['cordova-browser']).toEqual('^4.1.0');
             expect(pkgJson.dependencies['cordova-android']).toEqual('6.0.0');
         }).fail(function(err) {
             expect(err).toBeUndefined();
@@ -630,7 +631,7 @@ describe('update empty package.json to match config.xml', function () {
     },30000);
 });
 
-// Use a new basePkgJson4 as pkg.json contains android/ios and config.xml contains android.
+// Use a new basePkgJson4 as pkg.json contains android/browser and config.xml contains android.
 describe('update config.xml to include platforms in pkg.json', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
     var project = path.join(tmpDir, 'project');
@@ -661,11 +662,11 @@ describe('update config.xml to include platforms in pkg.json', function () {
         });
     }
     /** Test#007 will check the platform list in package.json and config.xml. 
-    *   When package.json has 'android and ios' and config.xml only contains 'android', run cordova
-    *   and config.xml is updated to include 'ios'. Also, if there is a specified spec in pkg.json,
+    *   When package.json has 'android and browser' and config.xml only contains 'android', run cordova
+    *   and config.xml is updated to include 'browser'. Also, if there is a specified spec in pkg.json,
     *   it should be added to config.xml during restore.
     */
-    it('Test#007 : if pkgJson has android & ios platforms and config.xml has android, update config to also include ios and spec', function(done) {
+    it('Test#007 : if pkgJson has android & browser platforms and config.xml has android, update config to also include browser and spec', function(done) {
         var cwd = process.cwd();
         var configXmlPath = path.join(cwd, 'config.xml');
         var cfg1 = new ConfigParser(configXmlPath);
@@ -680,10 +681,10 @@ describe('update config.xml to include platforms in pkg.json', function () {
 
         // Expect that config.xml contains only android at this point (basePjgJson4)
         expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
-        expect(configEngArray.indexOf('ios')).toEqual(-1);
+        expect(configEngArray.indexOf('browser')).toEqual(-1);
         expect(configEngArray.length === 1);
-        // Pkg.json has cordova-ios in its dependencies.
-        expect(pkgJson.dependencies).toEqual({ 'cordova-android' : '^3.1.0', 'cordova-ios' : '^4.3.0' });
+        // Pkg.json has cordova-browser in its dependencies.
+        expect(pkgJson.dependencies).toEqual({ 'cordova-android' : '^3.1.0', 'cordova-browser' : '^4.1.0' });
         emptyPlatformList().then(function() {
             // Run cordova prepare.
             return cordova.raw.prepare();
@@ -697,18 +698,18 @@ describe('update config.xml to include platforms in pkg.json', function () {
                 return elem.name;
             });
             configEngArray = engNames.slice();
-            // Expect 'ios' to be added to config.xml.
-            expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
+            // Expect 'browser' to be added to config.xml.
+            expect(configEngArray.indexOf('browser')).toBeGreaterThan(-1);
             // Expect 'android' to still be in config.xml.
             expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
             // Expect config.xml array to have 2 elements (platforms).
             expect(configEngArray.length === 2);
-            // Check to make sure that 'ios' spec was added properly.
-            expect(engines).toEqual([ { name: 'android', spec: '^3.1.0' },{ name: 'ios', spec: '^4.3.0' } ]);
+            // Check to make sure that 'browser' spec was added properly.
+            expect(engines).toEqual([ { name: 'android', spec: '^3.1.0' },{ name: 'browser', spec: '^4.1.0' } ]);
             // No change to pkg.json dependencies.
-            expect(pkgJson.dependencies).toEqual({ 'cordova-android' : '^3.1.0', 'cordova-ios' : '^4.3.0' });
+            expect(pkgJson.dependencies).toEqual({ 'cordova-android' : '^3.1.0', 'cordova-browser' : '^4.1.0' });
             expect(pkgJson.dependencies['cordova-android']).toEqual('^3.1.0');
-            expect(pkgJson.dependencies['cordova-ios']).toEqual('^4.3.0');
+            expect(pkgJson.dependencies['cordova-browser']).toEqual('^4.1.0');
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
@@ -1302,20 +1303,20 @@ describe('platforms and plugins should be restored with config.xml even without
         var pluginsFolderPath16 = path.join(cwd,'plugins');
         var platformsJson;
         var androidPlatform = 'android';
-        var iosPlatform = 'ios';
+        var browserPlatform = 'browser';
 
         // Pkg.json does not exist.
         expect(path.join(cwd,'package.json')).not.toExist();
-        // Config.xml contains only contains 'ios' at this point (basePkgJson13).
+        // Config.xml contains only contains 'android' at this point (basePkgJson13).
         expect(configEngArray.length === 1);
         // Config.xml contains only 1 plugin at this point.
         expect(Object.keys(configPlugins).length === 1);
 
         emptyPlatformList().then(function() {
         // Run cordova prepare.
-        return cordova.raw.platform('add', iosPlatform, {'save':true});
+        return cordova.raw.platform('add', browserPlatform, {'save':true});
         }).then(function () {
-        // Android and ios are in config.xml.
+        // Android and browser are in config.xml.
         var cfg3 = new ConfigParser(configXmlPath);
         engines = cfg3.getEngines();
         engNames = engines.map(function(elem) {
@@ -1326,18 +1327,18 @@ describe('platforms and plugins should be restored with config.xml even without
         // Delete previouc caches of (pkg.json).
         delete require.cache[require.resolve(platformsFolderPath1)];
         platformsJson = require(platformsFolderPath1);
-        // Android and ios should be installed
+        // Android and browser should be installed
         expect(platformsJson).toBeDefined();
         expect(platformsJson[androidPlatform]).toBeDefined();
-        expect(platformsJson[iosPlatform]).toBeDefined();
+        expect(platformsJson[browserPlatform]).toBeDefined();
         // Remove android without --save.
-        return cordova.raw.platform('rm', [iosPlatform]);
+        return cordova.raw.platform('rm', [browserPlatform]);
         }).then(function () {
-        // Android should not be in the installed list (only ios).
+        // Android should not be in the installed list (only browser).
         delete require.cache[require.resolve(platformsFolderPath1)];
         platformsJson = require(platformsFolderPath1);
         expect(platformsJson).toBeDefined();
-        expect(platformsJson[iosPlatform]).toBeUndefined();
+        expect(platformsJson[browserPlatform]).toBeUndefined();
         expect(platformsJson[androidPlatform]).toBeDefined();
         }).then(function () {
         // Run cordova prepare.
@@ -1349,15 +1350,15 @@ describe('platforms and plugins should be restored with config.xml even without
             return elem.name;
         });
         configEngArray = engNames.slice();
-        // Config.xml should have android and ios.
+        // Config.xml should have android and browser.
         expect(configEngArray.indexOf(androidPlatform)).toBeGreaterThan(-1);
-        expect(configEngArray.indexOf(iosPlatform)).toBeGreaterThan(-1);
+        expect(configEngArray.indexOf(browserPlatform)).toBeGreaterThan(-1);
         expect(configEngArray.length === 2);
-        // Expect that android and ios were restored.
+        // Expect that android and browser were restored.
         delete require.cache[require.resolve(platformsFolderPath1)];
         platformsJson = require(platformsFolderPath1);
         expect(platformsJson[androidPlatform]).toBeDefined();
-        expect(platformsJson[iosPlatform]).toBeDefined();
+        expect(platformsJson[browserPlatform]).toBeDefined();
         }).then(function () {
             //Check plugins.
             var cfg5 = new ConfigParser(configXmlPath);
@@ -1507,13 +1508,13 @@ describe('tests platform/spec restore with --save', function () {
         var pkgJson;
         var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
         var platformsJson;
-        var secondPlatformAdded = 'ios';
+        var secondPlatformAdded = 'browser';
     
         emptyPlatformList().then(function() {
             // Add the testing platform with --save.
             return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
         }).then(function() {
-            // Add the 'ios' platform with --save
+            // Add the 'browser' platform with --save
             return cordova.raw.platform('add',secondPlatformAdded, {'save':true});
         }).then(function() {
             // Delete any previous caches of require(package.json) and (platforms.json).
@@ -1557,7 +1558,7 @@ describe('tests platform/spec restore with --save', function () {
             platformsJson = require(platformsFolderPath);
             // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json
             expect(platformsJson[helpers.testPlatform]).toBeUndefined();
-            // Expect 'ios' not to be in platforms.json and has not been restored.
+            // Expect 'browser' not to be in platforms.json and has not been restored.
             expect(platformsJson[secondPlatformAdded]).toBeDefined();
         }).fail(function(err) {
             expect(err).toBeUndefined();


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


[04/12] cordova-lib git commit: CB-12001: Added support for platform/plugin & spec restore to sync config.xml and package.json

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
index 1a1966d..e4018e3 100644
--- a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -20,30 +20,39 @@ var helpers = require('./helpers'),
     path = require('path'),
     fs = require('fs'),
     shell = require('shelljs'),
-    superspawn = require('cordova-common').superspawn,
-    Q = require('q'),
     events = require('cordova-common').events,
     cordova = require('../src/cordova/cordova'),
     rewire = require('rewire'),
     prepare = require('../src/cordova/prepare'),
     platforms = require('../src/platforms/platforms'),
+    cordova_util = require('util'),
+    ConfigParser = require('cordova-common').ConfigParser,
     platform = rewire('../src/cordova/platform.js');
 
 var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
 var pluginsDir = path.join(__dirname, 'fixtures', 'plugins');
 
-// Testing will check if "cordova prepare" is restoring platforms as expected
+/** Testing will check if "cordova prepare" is restoring platforms as expected.
+*   Uses different basePkgJson files depending on testing expecations of what should
+*   initially be in pkg.json and/or config.xml.
+*/
+
+var req = function(someModule) {
+    delete require.cache[require.resolve(someModule)];
+    return require(someModule);
+}
+
 describe('platform end-to-end with --save', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
-    var project = path.join(tmpDir, 'project'); // call it projectPath
+    var project = path.join(tmpDir, 'project');
     var results;
 
     beforeEach(function() {
         shell.rm('-rf', tmpDir);
-        // cp then mv because we need to copy everything, but that means it'll copy the whole directory.
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
         // Using /* doesn't work because of hidden files.
-        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson2'), tmpDir);
-        shell.mv(path.join(tmpDir, 'basePkgJson2'), project);
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
         process.chdir(project);
         events.on('results', function(res) { results = res; });
     });
@@ -51,7 +60,6 @@ describe('platform end-to-end with --save', function () {
     afterEach(function() {
         var cwd = process.cwd();
         delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
-        //delete require.cache[require.resolve(path.join(cwd,'platforms/platforms.json'))];
         process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
         shell.rm('-rf', tmpDir);
     });
@@ -64,6 +72,7 @@ describe('platform end-to-end with --save', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
+
     function fullPlatformList() {
         return cordova.raw.platform('list').then(function() {
             var installed = results.match(/Installed platforms:\n  (.*)/);
@@ -71,40 +80,42 @@ describe('platform end-to-end with --save', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
         });
     }
-
+    /** Test#001 will add a platform to package.json with the 'save' flag.
+    *   It will remove it from pkg.json without the save flag.
+    *   After running cordova prepare, that platform should be restored in the
+    *   installed platform list in platforms.json.
+    */
     it('Test#001 : should restore platform that has been removed from project', function(done) {
         var cwd = process.cwd();
         var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
         var pkgJson = require(pkgJsonPath);
-        var platformsFolderPath;
+        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
+        var platformsJson;
 
         emptyPlatformList().then(function() {
-            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
-        }).then(function() {
             // Add the testing platform with --save.
             return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
         }).then(function() {
-            // Check the platform add was successful.
-            platformsFolderPath = (path.join(cwd,'platforms/platforms.json'));
-            var platformsJson = require(platformsFolderPath);
-
-            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            // Require platformsFolderPath
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Check the platform add was successful in package.json.
+            expect(pkgJson.cordova.platforms).toBeDefined();
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-            expect(platformsJson).toBeDefined();
             // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
+            expect(platformsJson).toBeDefined();
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
-
         }).then(fullPlatformList) // Platform should still be in platform ls.
-        .then(function() {
-            // And now remove it without --save.
+          .then(function() {
+            // And now remove helpers.testPlatform without --save.
             return cordova.raw.platform('rm', [helpers.testPlatform]);
         }).then(function() {
-            // Delete any previous caches of require(package.json)
+            // Delete any previous caches of require(package.json) and (platforms.json)
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-
             // Check that the platform removed without --save is still in platforms key.
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
             // Check that the platform was removed from the platforms.json
@@ -112,48 +123,59 @@ describe('platform end-to-end with --save', function () {
         }).then(function() {
             // Run cordova prepare
             return cordova.raw.prepare();
-        })
-        .then(function() {
+        }).then(function() {
+            // Delete any previous caches of platforms.json
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-            // Expect "helpers.testPlatform" to be in the installed platforms list
+            // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json.
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
-        })
-        .fail(function(err) {
+        }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
     },30000);
 
+    /** Test#002 will add two platforms to package.json with the 'save' flag.
+    *   It will remove one platform from pkg.json without the 'save' flag and remove
+    *   the other platform with the 'save' flag. After running cordova prepare, 
+    *   the platform removed with the 'save' flag should NOT be restored in platforms.json.
+    */
     it('Test#002 : should NOT restore platform that was removed with --save', function(done) {
         var cwd = process.cwd();
         var pkgJsonPath = path.join(cwd,'package.json');
-        var pkgJson = require(pkgJsonPath);
-        var platformsFolderPath;
+        var pkgJson;
+        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
+        var platformsJson;
         var secondPlatformAdded = 'ios';
+        
 
         emptyPlatformList().then(function() {
-            return cordova.raw.platform('rm', [helpers.testPlatform], {'save': true});
-        }).then(function() {
             // Add the testing platform with --save.
             return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
         }).then(function() {
+            // Add the 'ios' platform with --save
             return cordova.raw.platform('add',secondPlatformAdded, {'save':true});
         }).then(function() {
-            // Check the platform add was successful.
-            platformsFolderPath = (path.join(cwd,'platforms/platforms.json'));
-            var platformsJson = require(platformsFolderPath);
-            expect(pkgJson.cordova.platforms).not.toBeUndefined();
+            // Delete any previous caches of require(package.json) and (platforms.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            
+            // Check the platform add of both platforms was successful.
+            expect(pkgJson.cordova.platforms).toBeDefined();
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(0);
             expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(1);
-            expect(platformsJson).toBeDefined();
             // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
+            expect(platformsJson).toBeDefined();
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
-
+            expect(platformsJson[secondPlatformAdded]).toBeDefined();
         }).then(fullPlatformList) // Platform should still be in platform ls.
           .then(function() {
-            // And now remove it without --save.
+            // Remove helpers.testPlatform with --save.
             return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
         }).then(function() {
+            // Remove secondPlatformAdded without --save
             return cordova.raw.platform('rm', secondPlatformAdded);
         }).then(function() {
             // Delete any previous caches of require(package.json) and (platformsJson)
@@ -163,91 +185,433 @@ describe('platform end-to-end with --save', function () {
             pkgJson = require(pkgJsonPath);
             // Check that the platform removed without --save is still in platforms key.
             expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(0);
-            // Check that the platform was removed from the platforms.json
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
+            // Check that the helpers.testPlatform (removed with --save) was removed from the platforms.json
             expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
         }).then(function() {
             // Run cordova prepare
             return cordova.raw.prepare();
-        })
-        .then(function() {
+        }).then(function() {
             // Delete any previous caches of platformsJson
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-            // Expect "helpers.testPlatform" to be in the installed platforms list
+            // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json
             expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+            // Expect 'ios' not to be in platforms.json and has not been restored.
             expect(platformsJson[secondPlatformAdded]).toBeDefined();
-        })
-        .fail(function(err) {
+        }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
     },30000);
 
-
+    /** Test#003 will add two platforms to package.json - one with the 'save' flag and one
+    *   without the 'save' flag. It will remove both platforms without a 'save' flag.
+    *   After running cordova prepare, only the platform added with the 'save' flag is restored
+    *   in platforms.json.
+    */
     it('Test#003 : should NOT restore platform that was not saved and removed', function(done) {
         var cwd = process.cwd();
         var pkgJsonPath = path.join(cwd,'package.json');
-        var pkgJson = require(pkgJsonPath);
-        var platformsFolderPath;
+        var pkgJson;
+        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
         var secondPlatformAdded = 'ios';
+        var platformsJson;
 
         emptyPlatformList().then(function() {
-            // Add the testing platform with --save.
-            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
-        }).then(function() {
+            // Add 'ios' platform to project without --save
             return cordova.raw.platform('add', secondPlatformAdded);
         }).then(function() {
+            // Add helpers.testPlatform to project with --save
             return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
         }).then(function() {
-            // Check the platform add was successful.
+            // Delete any previous caches of require(package.json) and (platformsJson)
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            platformsFolderPath = (path.join(cwd,'platforms/platforms.json'));
-            var platformsJson = require(platformsFolderPath);
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-
-            expect(pkgJson.cordova.platforms).not.toBeUndefined();
-            //expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
+            // Check the platform add of both platforms was successful in package.json.
+            expect(pkgJson.cordova.platforms).toBeDefined();
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
+            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(-1);
+            // Expect both platforms to be installed platform list in platforms.json
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
-
-        }).then(fullPlatformList) // Platform should still be in platform ls.
+            expect(platformsJson[secondPlatformAdded]).toBeDefined();
+        }).then(fullPlatformList) // Platforms should still be in platform ls.
           .then(function() {
-            // And now remove it without --save.
+            // Remove helpers.testPlatform without --save.
             return cordova.raw.platform('rm', [helpers.testPlatform]);
         }).then(function() {
+            // Remove secondPlatformAdded without --save.
             return cordova.raw.platform('rm', secondPlatformAdded);
         }).then(function() {
-            // Delete any previous caches of require(package.json) and (platformsJson)
+            // Delete any previous caches of require(pkgJson) and (platformsJson)
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Check that the platform removed without --save is still in platforms key.
+            // Check that the platform that was added with --save is still in package.json.
             expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-            // Check that the platform was removed from the platforms.json
+            // Check that both platforms were removed from the platforms.json.
             expect(platformsJson[secondPlatformAdded]).toBeUndefined();
             expect(platformsJson[helpers.testPlatform]).toBeUndefined();
         }).then(function() {
             // Run cordova prepare
             return cordova.raw.prepare();
-        })
-        .then(function() {
+        }).then(function() {
             // Delete any previous caches of platformsJson
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
             // Expect "helpers.testPlatform" to be in the installed platforms list
             expect(platformsJson[helpers.testPlatform]).toBeDefined();
+            // Expect that 'ios' will not be in platforms.json and has not been restored.
             expect(platformsJson[secondPlatformAdded]).toBeUndefined();
-        })
-        .fail(function(err) {
+        }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
     },30000);
+
 });
 
+// Use basePkgJson6 because pkg.json and config.xml contain only android
+describe('files should not be modified if their platforms are identical', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
 
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        // Use basePkgJson6 because pkg.json and config.xml contain only android
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson6'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson6'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        var cwd = process.cwd();
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+    
+    /** Test#004 will check the platform list in package.json and config.xml. 
+    *   When both files contain the same platforms and cordova prepare is run, 
+    *   neither file is modified.
+    */
+    it('Test#004 : if pkg.json and config.xml have the same platforms, do not modify either file', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var cfg1 = new ConfigParser(configXmlPath);
+        var engines = cfg1.getEngines();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        var pkgJson;
+        var platformsFolderPath;
+        var engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        var configEngArray = engNames.slice();
+        // Pkg.json and config.xml contain only android at this point (basePkgJson6)
+        emptyPlatformList().then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        }).then(function() {
+            var cfg2 = new ConfigParser(configXmlPath);
+            engines = cfg2.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Expect android to be in both pkg.json and config.xml
+            expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
+            expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+            // Expect pkg.json and config.xml to have only 1 element each
+            expect(configEngArray.length === 1);
+            expect(pkgJson.cordova.platforms.length === 1);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
+
+// Use a new basePkgJson5 as config.xml contains android/ios and pkg.json contains android
+describe('update pkg.json to include platforms in config.xml', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson5'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson5'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        var cwd = process.cwd();
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+    /** Test#005 will check the platform list in package.json and config.xml. 
+    *   When config.xml has 'android and ios' and pkg.json only contains 'android', run cordova
+    *   and pkg.json is updated to include 'ios'.
+    */
+    it('Test#005 : if config.xml has android & ios platforms and pkg.json has android, update pkg.json to also include ios', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var cfg = new ConfigParser(configXmlPath);
+        var engines = cfg.getEngines();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var pkgJson = require(pkgJsonPath);
+        var platformsFolderPath;
+        var engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        var configEngArray = engNames.slice();
+       
+        // Config.xml contains(android & ios) and pkg.json contains android (basePkgJson5)
+        expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
+        expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+        // pkg.json should not contain 'ios' platform before cordova prepare
+        expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(-1);
+        expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
+        emptyPlatformList().then(function() {
+            return cordova.raw.prepare();
+        }).then(function() {
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Expect 'ios' to be added to pkg.json
+            expect(pkgJson.cordova.platforms.indexOf('ios')).toBeGreaterThan(-1);
+            // Expect 'android' to still be there in pkg.json
+            expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
+            // Expect both pkg.json and config.xml to each have both platforms in their arrays
+            expect(configEngArray.length === 1);
+            expect(pkgJson.cordova.platforms.length === 1);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
+
+// Use basePkgJson3 as it has 'android' in config.xml
+describe('update empty package.json to match config.xml', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson3'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson3'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        var cwd = process.cwd();
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+
+    /** Test#006 will check if pkg.json has a cordova key and platforms installed already.
+     *   If it does not and config.xml has a platform(s) installed already, run cordova prepare
+     *   and it will add a cordova key and the platform(s) from config.xml to package.json.
+     */
+    it('Test#006 : if pkg.json exists without cordova key, create one with same platforms in config.xml ', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var cfg1 = new ConfigParser(configXmlPath);
+        var engines = cfg1.getEngines();
+        var pkgJson = require(pkgJsonPath);
+        var platformsFolderPath;
+        var engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        var configEngArray = engNames.slice();
+    
+        // Expect that pkg.json exists without a cordova key
+        expect(pkgJson).toBeDefined();
+        expect(pkgJson.cordova).toBeUndefined();
+        // Expect that config.xml contains only android at this point
+        expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+        expect(configEngArray.length === 1);
+        // Run cordova prepare
+        cordova.raw.prepare();
+        emptyPlatformList().then(function() {
+            var cfg2 = new ConfigParser(configXmlPath);
+            engines = cfg2.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Expect no change to config.xml
+            expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+            // Expect cordova key and 'android' platform to be added to pkg.json
+            expect(pkgJson.cordova.platforms.indexOf('android')).toBeGreaterThan(-1);
+            // Expect both pkg.json and config.xml to each have (only) android in their arrays
+            expect(configEngArray.length === 0);
+            expect(pkgJson.cordova.platforms.length === 0);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
+
+// Use a new basePkgJson4 as pkg.json contains android/ios and config.xml contains android
+describe('update config.xml to include platforms in pkg.json', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson4'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson4'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        var cwd = process.cwd();
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+    /** Test#007 will check the platform list in package.json and config.xml. 
+    *   When packge.json has 'android and ios' and config.xml only contains 'android', run cordova
+    *   and config.xml is updated to include 'ios'.
+    */
+    it('Test#007 : if pkgJson has android & ios platforms and config.xml has android, update config to also include ios', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var cfg1 = new ConfigParser(configXmlPath);
+        var engines = cfg1.getEngines();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var pkgJson = require(pkgJsonPath);
+        var platformsFolderPath;
+        var engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        var configEngArray = engNames.slice();
+
+        // Expect that config.xml contains only android at this point (basePjgJson4)
+        expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+        expect(configEngArray.indexOf('ios')).toEqual(-1);
+        expect(configEngArray.length === 1);
+       
+        emptyPlatformList().then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        }).then(function() {
+            var cfg2 = new ConfigParser(configXmlPath);
+            engines = cfg2.getEngines();
+            engNames = engines.map(function(elem) {
+                return elem.name;
+            });
+            configEngArray = engNames.slice();
+            // Expect 'ios' to be added to config.xml
+            expect(configEngArray.indexOf('ios')).toBeGreaterThan(-1);
+            // Expect 'android' to still be in config.xml
+            expect(configEngArray.indexOf('android')).toBeGreaterThan(-1);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
 
 
 

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/spec-cordova/pkgJson.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson.spec.js b/cordova-lib/spec-cordova/pkgJson.spec.js
index 93af1dc..4e90a99 100644
--- a/cordova-lib/spec-cordova/pkgJson.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson.spec.js
@@ -253,7 +253,7 @@ describe('platform end-to-end with --save', function () {
             // Check the platform add was successful.
             pkgJson = require(pkgJsonPath);
             expect(pkgJson.cordova.platforms).not.toBeUndefined();
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(0);
         }).then(fullPlatformList) // Platform should still be in platform ls.
         .then(function() {
             // And now remove it with --save.
@@ -302,15 +302,18 @@ describe('platform end-to-end with --save', function () {
         var pkgJsonPath = path.join(process.cwd(),'package.json');
         var pkgJson;
         expect(pkgJsonPath).toExist();
-
+        delete require.cache[require.resolve(pkgJsonPath)];
+        pkgJson = require(pkgJsonPath);
+        // Pkg.json "platforms" should be empty and helpers.testPlatform should not exist in pkg.json.
+        expect(pkgJson.cordova).toBeUndefined();
         // Add platform without --save.
         cordova.raw.platform('add',[helpers.testPlatform])
         .then(function() {
             // Check the platform add was successful, reload, skipping cache
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            // Cordova key should not exist in package.json
-            expect(pkgJson.cordova).toBeUndefined();
+            // Platform list should be empty and helpers.testPlatform should NOT have been added.
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
         }).then(fullPlatformList)
         .fail(function(err) {
             expect(err).toBeUndefined();

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/src/cordova/plugin.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/plugin.js b/cordova-lib/src/cordova/plugin.js
index 4203ca3..c03207e 100644
--- a/cordova-lib/src/cordova/plugin.js
+++ b/cordova-lib/src/cordova/plugin.js
@@ -247,23 +247,19 @@ module.exports = function plugin(command, targets, opts) {
                                 } else {
                                     // Create package.json in cordova@7
                                 }
-                                // If package.json exists, the plugin object and plugin name will be added to package.json 
-                                // if not already there
-                                if (pkgJson === undefined) {
+                                // If package.json exists, the plugin object and plugin name 
+                                // will be added to package.json if not already there.
+                                if (!pkgJson) {
                                     return;
                                 }
-                                if (pkgJson.cordova === undefined) {
-                                    pkgJson.cordova = {};
-                                }
-                                if (pkgJson.cordova.plugins === undefined) {
-                                    pkgJson.cordova.plugins = {};
-                                }
-                                if (pkgJson.cordova.plugins !== undefined) {
-                                    pkgJson.cordova.plugins[pluginInfo.id] = opts.cli_variables;
-                                    events.emit('log','Adding '+pluginInfo.id+ ' to package.json');
-                                    // Write to package.json
-                                    fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
-                                }
+                                pkgJson.cordova = pkgJson.cordova || {};
+                                pkgJson.cordova.plugins = pkgJson.cordova.plugins || {};
+                                // Plugin and variables are added.
+                                pkgJson.cordova.plugins[pluginInfo.id] = opts.cli_variables;
+
+                                events.emit('log','Adding '+pluginInfo.id+ ' to package.json');
+                                // Write to package.json
+                                fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
                             }
                         });
                     }, Q());
@@ -339,9 +335,10 @@ module.exports = function plugin(command, targets, opts) {
                                 }
                                 // If package.json exists and contains a specified plugin in cordova['plugins'], it will be removed    
                                 if(pkgJson !== undefined && pkgJson.cordova !== undefined && pkgJson.cordova.plugins !== undefined) {
-                                    events.emit('log', 'Removing ' + target + ' from package.json');
+                                    events.emit('log', 'Removing [' + target + '] from package.json');
+                                    // Remove plugin from package.json
                                     delete pkgJson.cordova.plugins[target];
-                                    //Write out new package.json 
+                                    //Write out new package.json with plugin removed correctly.
                                     fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
                                 }
                             }

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/7df35cb0/cordova-lib/src/cordova/restore-util.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/restore-util.js b/cordova-lib/src/cordova/restore-util.js
index ce14903..2ae8ec3 100644
--- a/cordova-lib/src/cordova/restore-util.js
+++ b/cordova-lib/src/cordova/restore-util.js
@@ -32,43 +32,188 @@ exports.installPlatformsFromConfigXML = installPlatformsFromConfigXML;
 
 
 function installPlatformsFromConfigXML(platforms, opts) {
-    events.emit('verbose', 'Checking config.xml for saved platforms that haven\'t been added to the project');
+    events.emit('verbose', 'Checking config.xml and package.json for saved platforms that haven\'t been added to the project');
 
     var projectHome = cordova_util.cdProjectRoot();
     var configPath = cordova_util.projectConfig(projectHome);
     var cfg = new ConfigParser(configPath);
+    var engines = cfg.getEngines();
+    var pkgJsonPath = path.join(projectHome,'package.json');
+    var pkgJson;
+    var targetsPkgJson;
+    var comboArray; 
+    var targets;
+    var targetsConfig;
+    var engines;
+    var installAllPlatforms;
+    var platformPath;
+    var platformAlreadyAdded;
+    var t;
+    var ConfigAndPkgJsonArray = [];
+    var modifiedPkgJson = false;
+    var modifiedConfigXML = false;
+    var modifiedPkgJsonPlugin = false;
+    var modifiedConfigXMLPlugin = false;
 
-    var engines = cfg.getEngines(projectHome);
-    var installAllPlatforms = !platforms || platforms.length === 0;
+    var targetPlatformsArray = [];
 
-    var targets = engines.map(function(engine) {
-        var platformPath = path.join(projectHome, 'platforms', engine.name);
-        var platformAlreadyAdded = fs.existsSync(platformPath);
+    if(fs.existsSync(pkgJsonPath)) {
+        pkgJson = require(pkgJsonPath);
+    }
+    if(pkgJson !== undefined && pkgJson.cordova !== undefined && pkgJson.cordova.platforms !== undefined) {
+        targetsPkgJson = pkgJson.cordova.platforms;
+    } 
+    if(cfg !== undefined) {
+        targetsConfig = [];
+        engines = cfg.getEngines(projectHome);
+        installAllPlatforms = !platforms || platforms.length === 0;
+
+        targets = engines.map(function(engine) {
+            platformPath = path.join(projectHome, 'platforms', engine.name);
+            platformAlreadyAdded = fs.existsSync(platformPath);
 
-        //if no platforms are specified we add all.
-        if ((installAllPlatforms || platforms.indexOf(engine.name) > -1) && !platformAlreadyAdded) {
-            var t = engine.name;
-            if (engine.spec) {
-                t += '@' + engine.spec;
+            // If no platforms are specified we add all.
+            if ((installAllPlatforms || platforms.indexOf(engine.name) > -1) && !platformAlreadyAdded) {
+                t = engine.name;
+                if (engine.spec) {
+                    //t += '@' + engine.spec;
+                    targetsConfig.push(t);
+                }
+                return t;
             }
-            return t;
+        });   
+        if (targetsPkgJson !== undefined) {
+            // Combining arrays and checking duplicates
+            comboArray = targetsPkgJson.slice();
+        } else {
+            comboArray = [];
         }
-    });
+        targetsConfig.forEach(function(item) {
+            if(comboArray.indexOf(item) < 0 ) {
+                comboArray.push(item);
+            }
+        });
+        // If config.xml & pkgJson exist and the cordova key is undefined, create a cordova key.
+        if (cfg !== undefined && pkgJson !== undefined && pkgJson.cordova === undefined) {
+            pkgJson.cordova = {};
+        }
+        // If there is no platforms array, create an empty one.
+        if (pkgJson.cordova.platforms === undefined) {
+            pkgJson.cordova.platforms = [];
+            // Get a list of platforms names from config.xml.
+            targetPlatformsArray = engines.map(function(mEngine) {
+                t = mEngine.name;
+                return t;
+            }); 
 
-    if (!targets || !targets.length) {
-        return Q('No platforms found in config.xml that haven\'t been added to the project');
-    }
+        if (!targets || !targets.length) {
+           return Q('No platforms found in config.xml that haven\'t been added to the project');
+        }
+
+            var uniq = targetPlatformsArray.reduce(function(a,b){
+                if (a.indexOf(b) < 0 ) a.push(b);
+                return a;
+            },[]);
+            targetPlatformsArray = uniq;
+
+            // Add the platforms from config.xml to package.json
+            pkgJson.cordova.platforms = targetPlatformsArray;
+            modifiedPkgJson = true;
+            //fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+        }
+        // If config.xml and pkg.json exist and both already contain platforms, run cordova prepare
+        // so that both files are identical
+        if(cfg !== undefined && targetsConfig !== undefined && pkgJson.cordova.platforms !== undefined) {
+            
+            targetsConfig = engines.map(function(mEngine) {
+                t = mEngine.name;
+                //targetsConfig.push(t);
+                return t;
+            });
 
+            var uniq = targetsConfig.reduce(function(a,b){
+                if (a.indexOf(b) < 0 ) a.push(b);
+                return a;
+            },[]);
+            targetsConfig = uniq;
+        }
+        if (pkgJson.cordova.platforms !== undefined) {
+            // Create a new array based on cordova.platforms
+            pkgJson.cordova.platforms.forEach(function(item) {
+                if (ConfigAndPkgJsonArray.indexOf(item) < 0 ) 
+                    ConfigAndPkgJsonArray.push(item);
+            });
+        }
+        targetsConfig.forEach(function(item) {
+            if(ConfigAndPkgJsonArray.indexOf(item) < 0 ) {
+                ConfigAndPkgJsonArray.push(item);
+            }
+        });
+
+        //sort the arrays alphabetically 
+        ConfigAndPkgJsonArray = ConfigAndPkgJsonArray.sort();
+        pkgJson.cordova.platforms = pkgJson.cordova.platforms.sort();
+
+        // Get list of engine names and create a new array of engines from config.xml.
+        var engNames = engines.map(function(elem) {
+            return elem.name;
+        });
+        var configEngArray = engNames.sort().slice();
+
+        // If ConfigAndPkgJson array has the same platforms as pkg.json, no modification to pkg.json.
+        if(ConfigAndPkgJsonArray.toString() === pkgJson.cordova.platforms.toString()) {
+            events.emit('warn', 'Config.xml and package.json platforms are the same. No pkg.json modification.');
+        }
+        // If ConfigAndPkgJson array has the same platforms as config.xml, no modification to config.xml.
+        if(ConfigAndPkgJsonArray.toString() === configEngArray.toString()) {
+            events.emit('warn', 'Package.json and config.xml are the same. No config.xml modification.');
+        }
+        // If ConfigAndPkgJson array do not have the same platforms, modify pkg.json to include the elements
+        // from the ConfigAndPkgJson array so that the arrays are identical
+        if(ConfigAndPkgJsonArray.toString() !== pkgJson.cordova.platforms.toString()) {
+            events.emit('warn', 'Config.xml and package.json platforms are not the same. Updating package.json with most current list of platforms.');
+            ConfigAndPkgJsonArray.forEach(function(item) {
+                if(pkgJson.cordova.platforms.indexOf(item) < 0 ) {
+                    pkgJson.cordova.platforms.push(item);
+                    modifiedPkgJson = true;
+                }
+            });
+        }
+        // If ConfigAndPkgJson array do not have the same platforms, modify config.xml to include the elements
+        // from the ConfigAndPkgJson array so that the arrays are identical
+        if(ConfigAndPkgJsonArray.toString() !== configEngArray.toString()) {
+            events.emit('warn', 'Package.json and config.xml platforms are not the same. Updating config.xml with most current list of platforms.');
+            ConfigAndPkgJsonArray.forEach(function(item) {
+                if(configEngArray.indexOf(item) < 0 ) {
+                    configEngArray.push(item);
+                    modifiedConfigXML = true;
+                    cfg.addEngine(item);
+                }
+            });
+        }
+        // Write and update pkg.json if it has been modified.
+        if (modifiedPkgJson === true) {
+            //sort then write
+            pkgJson.cordova.platforms = pkgJson.cordova.platforms.sort();
+            fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+        }
+        if (modifiedConfigXML === true) {
+            cfg.write();
+        }
+        if (!comboArray || !comboArray.length) {
+            return Q('No platforms found in config.xml and/or package.json that haven\'t been added to the project');
+        }
 
+    }
     // Run `platform add` for all the platforms separately
     // so that failure on one does not affect the other.
 
     // CB-9278 : Run `platform add` serially, one platform after another
     // Otherwise, we get a bug where the following line: https://github.com/apache/cordova-lib/blob/0b0dee5e403c2c6d4e7262b963babb9f532e7d27/cordova-lib/src/util/npm-helper.js#L39
     // gets executed simultaneously by each platform and leads to an exception being thrown
-    return promiseutil.Q_chainmap_graceful(targets, function(target) {
+    return promiseutil.Q_chainmap_graceful(comboArray, function(target) {
         if (target) {
-            events.emit('log', 'Discovered platform \"' + target + '\" in config.xml. Adding it to the project');
+            events.emit('log', 'Discovered platform \"' + target + '\" in config.xml or package.json. Adding it to the project');
             return cordova.raw.platform('add', target, opts);
         }
         return Q();
@@ -86,14 +231,99 @@ function installPluginsFromConfigXML(args) {
     var configPath = cordova_util.projectConfig(projectRoot);
     var cfg = new ConfigParser(configPath);
     var plugins_dir = path.join(projectRoot, 'plugins');
+    var pkgJsonPath = path.join(projectRoot,'package.json');
+    var pkgJson;
+
+    var pkgJsonArray = [];
+    var comboPluginArray;
 
     // Get all configured plugins
+
     var plugins = cfg.getPluginIdList();
     if (0 === plugins.length) {
         return Q('No plugins found in config.xml that haven\'t been added to the project');
     }
+    // Check if path exists and require pkgJsonPath
+    if(fs.existsSync(pkgJsonPath)) {
+        pkgJson = require(pkgJsonPath);
+    }
+    if (pkgJson.cordova === undefined) {
+        pkgJson.cordova = {};
+    }
+    // (In pkg.json), if there is a platforms array and not plugins array, create a new plugins array 
+    // and add all plugins from config.xml to the new plugins array.
+    if (cfg !== undefined && pkgJson !== undefined && pkgJson.cordova.platforms !== undefined && 
+    pkgJson.cordova.plugins === undefined) {
+        pkgJson.cordova.plugins = {};
+        plugins.forEach(function(foo) {
+            pkgJson.cordova.plugins[foo] = {}
+        })
+        fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+    }
+    // Created, but don't think this is necessary because there has to be at least one platform
+    // added for restore to run.
+    // (In pkg.json), if there is no platforms array and no plugins array, create a new plugins array
+    // and add all plugins from config.xml to the new plugins array.
+    if (cfg !== undefined && pkgJson !== undefined && pkgJson.cordova.platforms === undefined && 
+    pkgJson.cordova.plugins === undefined) {
+        pkgJson.cordova.platforms = [];
+        pkgJson.cordova.plugins = {};
+        plugins.forEach(function(foo) {
+            pkgJson.cordova.plugins[foo] = {}
+        })
+        fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+    }
 
+    pkgJsonArray = Object.keys(pkgJson.cordova.plugins);
 
+    var pluginNames = plugins.map(function(elem) {
+        return elem.name;
+    });
+
+    // If config.xml and pkg.json exist and both already contain plugins, run cordova prepare
+    // to check if these plugins are identical.
+    if(cfg !== undefined && pkgJson.cordova.plugins !== undefined) {
+        if(pkgJsonArray.toString() === plugins.toString() && pkgJsonArray.length === plugins.length) {
+            events.emit('warn', 'Config.xml and pkgJson plugins are the same. No pkg.json or config.xml modification.');
+        }
+
+        if(pkgJsonArray.toString() !== plugins.toString() || pkgJsonArray.length !== plugins.length) {
+           events.emit('warn', 'Config.xml and pkgJson plugins are different.');
+            // Combining arrays and checking duplicates
+
+            comboPluginArray = pkgJsonArray.slice();
+            plugins.forEach(function(item) {
+                if(comboPluginArray.indexOf(item) < 0) {
+                    comboPluginArray.push(item);
+                }
+            });
+
+            // Update pkg.json if it's missing any plugins based on plugin.id 
+            if(comboPluginArray.sort().toString() !== pkgJsonArray.sort().toString() && pkgJsonArray.length > 0) {
+                events.emit('warn', 'Config.xml and package.json plugins are not the same. Updating package.json with most current list of plugins.');
+                comboPluginArray.forEach(function(item) {
+                    if(pkgJsonArray.indexOf(item) < 0) {
+                        pkgJsonArray.push(item);
+                        var pluginVar = cfg.getPlugin(item);
+                        var variables = pluginVar.variables;
+                        pkgJson.cordova.plugins[item] = variables;
+                    }
+                });
+                fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+            }
+            // Update pkg.json if it's missing any plugins based on plugin.id 
+            if(comboPluginArray.toString() !== plugins.toString()) {
+                events.emit('warn', 'Config.xml and package.json plugins are not the same. Updating config.xml with most current list of plugins.');
+                comboPluginArray.forEach(function(item) {
+                    if(plugins.indexOf(item) < 0) {
+                        plugins.push(item); 
+                        cfg.addPlugin({name:item}, pkgJson.cordova.plugins[item]);
+                        cfg.write();
+                    }
+                });
+            }
+        }
+    }
     // Intermediate variable to store current installing plugin name
     // to be able to create informative warning on plugin failure
     var pluginName;


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


[07/12] cordova-lib git commit: CB-11960 : rebased, fixed async issues, and updated tests, tests 000, 005, and 007 still fail in pkgJson-restore

Posted by st...@apache.org.
CB-11960 : rebased, fixed async issues, and updated tests, tests 000, 005, and 007 still fail in pkgJson-restore


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/b77129a9
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/b77129a9
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/b77129a9

Branch: refs/heads/master
Commit: b77129a97687f3ca31678bb18399e40cf8697759
Parents: d7a1487
Author: audreyso <au...@adobe.com>
Authored: Wed Jan 18 10:55:59 2017 -0800
Committer: Audrey So <au...@apache.org>
Committed: Thu Jan 26 17:36:52 2017 -0800

----------------------------------------------------------------------
 .../spec-cordova/pkgJson-restore.spec.js        | 475 +++++++++++--------
 cordova-lib/spec-cordova/pkgJson.spec.js        |   2 +-
 cordova-lib/spec-plugman/fetch.spec.js          |   9 +-
 3 files changed, 279 insertions(+), 207 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/b77129a9/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
index 4d5dfec..496ec32 100644
--- a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -58,15 +58,6 @@ describe('tests platform/spec restore with --save', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
         });
     }
-
-    function fullPlatformList() {
-        return cordova.raw.platform('list').then(function() {
-            var installed = results.match(/Installed platforms:\n  (.*)/);
-            expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-        });
-    }
-
     /** Test#000 will check that when a platform is added with a spec, it will 
     *   add to pkg.json with a '^' and to config.xml with a '~'. When prepare is run,
     *   pkg.json will have no change and config.xml (first char) will change from a '~' to a '^'.
@@ -138,199 +129,6 @@ describe('tests platform/spec restore with --save', function () {
     // Cordova prepare needs extra wait time to complete.
     },30000);
 
-    /** Test#001 will add a platform to package.json with the 'save' flag.
-    *   It will remove it from platforms.json without the save flag.
-    *   After running cordova prepare, that platform should be restored in the
-    *   installed platform list in platforms.json.
-    */
-    it('Test#001 : should restore platform that has been removed from project', function(done) {
-        var cwd = process.cwd();
-        var pkgJsonPath = path.join(cwd,'package.json');
-        delete require.cache[require.resolve(pkgJsonPath)];
-        var pkgJson;
-        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
-        var platformsJson;
-
-        emptyPlatformList().then(function() {
-            // Add the testing platform with --save.
-            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
-        }).then(function() {
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Require platformsFolderPath
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            // Check the platform add was successful in package.json.
-            expect(pkgJson.cordova.platforms).toBeDefined();
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
-            expect(platformsJson).toBeDefined();
-            expect(platformsJson[helpers.testPlatform]).toBeDefined();
-        }).then(fullPlatformList) // Platform should still be in platform ls.
-          .then(function() {
-            // And now remove helpers.testPlatform without --save.
-            return cordova.raw.platform('rm', [helpers.testPlatform]);
-        }).then(function() {
-            // Delete any previous caches of require(package.json) and (platforms.json)
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Check that the platform removed without --save is still in platforms key.
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-            // Check that the platform was removed from the platforms.json
-            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
-        }).then(function() {
-            // Run cordova prepare.
-            return cordova.raw.prepare();
-        }).then(function() {
-            // Delete any previous caches of platforms.json.
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json.
-            expect(platformsJson[helpers.testPlatform]).toBeDefined();
-        }).fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    // Cordova prepare needs extra wait time to complete.
-    },30000);
-
-    /** Test#002 will add two platforms to package.json with the 'save' flag.
-    *   It will remove one platform from pkg.json without the 'save' flag and remove
-    *   the other platform with the 'save' flag. After running cordova prepare, 
-    *   the platform removed with the 'save' flag should NOT be restored in platforms.json.
-    */
-    it('Test#002 : should NOT restore platform that was removed with --save', function(done) {
-        var cwd = process.cwd();
-        var pkgJsonPath = path.join(cwd,'package.json');
-        var pkgJson;
-        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
-        var platformsJson;
-        var secondPlatformAdded = 'ios';
-    
-        emptyPlatformList().then(function() {
-            // Add the testing platform with --save.
-            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
-        }).then(function() {
-            // Add the 'ios' platform with --save
-            return cordova.raw.platform('add',secondPlatformAdded, {'save':true});
-        }).then(function() {
-            // Delete any previous caches of require(package.json) and (platforms.json).
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            // Check the platform add of both platforms (to pkg.Json) was successful.
-            expect(pkgJson.cordova.platforms).toBeDefined();
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(0);
-            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(1);
-            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json.
-            expect(platformsJson).toBeDefined();
-            expect(platformsJson[helpers.testPlatform]).toBeDefined();
-            expect(platformsJson[secondPlatformAdded]).toBeDefined();
-        }).then(fullPlatformList) // Platform should still be in platform ls.
-          .then(function() {
-            // Remove helpers.testPlatform with --save.
-            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
-        }).then(function() {
-            // Remove secondPlatformAdded without --save.
-            return cordova.raw.platform('rm', secondPlatformAdded);
-        }).then(function() {
-            // Delete any previous caches of require(package.json) and (platformsJson).
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Check that ONLY the platform removed without --save is still in (pkg.json) platforms key.
-            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(0);
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
-            // Check that both platforms were removed from the platforms.json list.
-            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
-            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
-        }).then(function() {
-            // Run cordova prepare.
-            return cordova.raw.prepare();
-        }).then(function() {
-            // Delete any previous caches of platformsJson.
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json
-            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
-            // Expect 'ios' not to be in platforms.json and has not been restored.
-            expect(platformsJson[secondPlatformAdded]).toBeDefined();
-        }).fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    // Cordova prepare needs extra wait time to complete.
-    },30000);
-
-    /** Test#003 will add two platforms to package.json - one with the 'save' flag and one
-    *   without the 'save' flag. It will remove both platforms without a 'save' flag.
-    *   After running cordova prepare, only the platform added with the 'save' flag is restored
-    *   in platforms.json.
-    */
-    it('Test#003 : should NOT restore platform that was not saved and removed', function(done) {
-        var cwd = process.cwd();
-        var pkgJsonPath = path.join(cwd,'package.json');
-        var pkgJson;
-        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
-        var secondPlatformAdded = 'ios';
-        var platformsJson;
-
-        emptyPlatformList().then(function() {
-            // Add 'ios' platform to project without --save
-            return cordova.raw.platform('add', secondPlatformAdded);
-        }).then(function() {
-            // Add helpers.testPlatform to project with --save
-            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
-        }).then(function() {
-            // Delete any previous caches of require(package.json) and (platformsJson)
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            // Check the platform add of only helpers.testPlatform was successful in package.json.
-            expect(pkgJson.cordova.platforms).toBeDefined();
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(-1);
-            // Expect both platforms to be installed platform list in platforms.json
-            expect(platformsJson[helpers.testPlatform]).toBeDefined();
-            expect(platformsJson[secondPlatformAdded]).toBeDefined();
-        }).then(fullPlatformList) // Platforms should still be in platform ls.
-          .then(function() {
-            // Remove helpers.testPlatform without --save.
-            return cordova.raw.platform('rm', [helpers.testPlatform]);
-        }).then(function() {
-            // Remove secondPlatformAdded without --save.
-            return cordova.raw.platform('rm', secondPlatformAdded);
-        }).then(function() {
-            // Delete any previous caches of require(pkgJson) and (platformsJson)
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Check that the platform that was added with --save is still in package.json.
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-            // Check that both platforms were removed from the platforms.json.
-            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
-            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
-        }).then(function() {
-            // Run cordova prepare
-            return cordova.raw.prepare();
-        }).then(function() {
-            // Delete any previous caches of platformsJson
-            delete require.cache[require.resolve(platformsFolderPath)];
-            platformsJson = require(platformsFolderPath);
-            // Expect "helpers.testPlatform" to be in the installed platforms list.
-            expect(platformsJson[helpers.testPlatform]).toBeDefined();
-            // Expect that 'ios' will not be in platforms.json and has not been restored.
-            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
-        }).fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    // Cordova prepare needs extra wait time to complete.
-    },30000);
-
     /** Test#017
     *   When platform is added with url and fetch and restored with fetch, 
     *   pkg.json and config.xml would add it to their files properly.
@@ -490,6 +288,112 @@ describe('tests platform/spec restore with --save', function () {
     },30000);
 });
 
+// Use basePkgJson
+describe('tests platform/spec restore with --save', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson2');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+    /** Test#003 will add two platforms to package.json - one with the 'save' flag and one
+    *   without the 'save' flag. It will remove both platforms without a 'save' flag.
+    *   After running cordova prepare, only the platform added with the 'save' flag is restored
+    *   in platforms.json.
+    */
+    it('Test#003 : should NOT restore platform that was not saved and removed', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        var pkgJson;
+        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
+        var secondPlatformAdded = 'ios';
+        var platformsJson;
+
+        emptyPlatformList().then(function() {
+            // Add 'ios' platform to project without --save
+            return cordova.raw.platform('add', secondPlatformAdded);
+        }).then(function() {
+            // Add helpers.testPlatform to project with --save
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Delete any previous caches of require(package.json) and (platformsJson)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Check the platform add of only helpers.testPlatform was successful in package.json.
+            expect(pkgJson.cordova.platforms).toBeDefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(-1);
+            // Expect both platforms to be installed platform list in platforms.json
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+            expect(platformsJson[secondPlatformAdded]).toBeDefined();
+        }).then(fullPlatformList) // Platforms should still be in platform ls.
+          .then(function() {
+            // Remove helpers.testPlatform without --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform]);
+        }).then(function() {
+            // Remove secondPlatformAdded without --save.
+            return cordova.raw.platform('rm', secondPlatformAdded);
+        }).then(function() {
+            // Delete any previous caches of require(pkgJson) and (platformsJson)
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Check that the platform that was added with --save is still in package.json.
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            // Check that both platforms were removed from the platforms.json.
+            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
+            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+        }).then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        }).then(function() {
+            // Delete any previous caches of platformsJson
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Expect "helpers.testPlatform" to be in the installed platforms list.
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+            // Expect that 'ios' will not be in platforms.json and has not been restored.
+            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
+
 // Use basePkgJson6 because pkg.json and config.xml contain only android
 describe('files should not be modified if their platforms are identical', function () {
     var tmpDir = helpers.tmpDir('platform_test_pkgjson');
@@ -693,7 +597,7 @@ describe('update empty package.json to match config.xml', function () {
             return elem.name;
         });
         var configEngArray = engNames.slice();
-    
+
         // Expect that pkg.json exists without a cordova key.
         expect(pkgJson).toBeDefined();
         expect(pkgJson.cordova).toBeUndefined();
@@ -1496,3 +1400,168 @@ describe('platforms and plugins should be restored with config.xml even without
 });
 
 
+// Use basePkgJson
+describe('tests platform/spec restore with --save', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson2');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+
+    /** Test#001 will add a platform to package.json with the 'save' flag.
+    *   It will remove it from platforms.json without the save flag.
+    *   After running cordova prepare, that platform should be restored in the
+    *   installed platform list in platforms.json.
+    */
+    it('Test#001 : should restore platform that has been removed from project', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var pkgJson;
+        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
+        var platformsJson;
+
+        emptyPlatformList().then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', 'android', {'save':true});
+        }).then(function() {
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Require platformsFolderPath
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Check the platform add was successful in package.json.
+            expect(pkgJson.cordova.platforms).toBeDefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json
+            expect(platformsJson).toBeDefined();
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+          .then(function() {
+            // And now remove helpers.testPlatform without --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform]);
+        }).then(function() {
+            // Delete any previous caches of require(package.json) and (platforms.json)
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Check that the platform removed without --save is still in platforms key.
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+            // Check that the platform was removed from the platforms.json
+            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+        }).then(function() {
+            // Run cordova prepare.
+            return cordova.raw.prepare();
+        }).then(function() {
+            // Delete any previous caches of platforms.json.
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json.
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+
+    /** Test#002 will add two platforms to package.json with the 'save' flag.
+    *   It will remove one platform from pkg.json without the 'save' flag and remove
+    *   the other platform with the 'save' flag. After running cordova prepare, 
+    *   the platform removed with the 'save' flag should NOT be restored in platforms.json.
+    */
+    it('Test#002 : should NOT restore platform that was removed with --save', function(done) {
+        var cwd = process.cwd();
+        var pkgJsonPath = path.join(cwd,'package.json');
+        var pkgJson;
+        var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
+        var platformsJson;
+        var secondPlatformAdded = 'ios';
+    
+        emptyPlatformList().then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Add the 'ios' platform with --save
+            return cordova.raw.platform('add',secondPlatformAdded, {'save':true});
+        }).then(function() {
+            // Delete any previous caches of require(package.json) and (platforms.json).
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Check the platform add of both platforms (to pkg.Json) was successful.
+            expect(pkgJson.cordova.platforms).toBeDefined();
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(0);
+            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(1);
+            // Expect that "helpers.testPlatform" in the installed platform list in platforms.json.
+            expect(platformsJson).toBeDefined();
+            expect(platformsJson[helpers.testPlatform]).toBeDefined();
+            expect(platformsJson[secondPlatformAdded]).toBeDefined();
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+          .then(function() {
+            // Remove helpers.testPlatform with --save.
+            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
+        }).then(function() {
+            // Remove secondPlatformAdded without --save.
+            return cordova.raw.platform('rm', secondPlatformAdded);
+        }).then(function() {
+            // Delete any previous caches of require(package.json) and (platformsJson).
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Check that ONLY the platform removed without --save is still in (pkg.json) platforms key.
+            expect(pkgJson.cordova.platforms.indexOf(secondPlatformAdded)).toEqual(0);
+            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
+            // Check that both platforms were removed from the platforms.json list.
+            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+            expect(platformsJson[secondPlatformAdded]).toBeUndefined();
+        }).then(function() {
+            // Run cordova prepare.
+            return cordova.raw.prepare();
+        }).then(function() {
+            // Delete any previous caches of platformsJson.
+            delete require.cache[require.resolve(platformsFolderPath)];
+            platformsJson = require(platformsFolderPath);
+            // Expect "helpers.testPlatform" to be in the installed platforms list in platforms.json
+            expect(platformsJson[helpers.testPlatform]).toBeUndefined();
+            // Expect 'ios' not to be in platforms.json and has not been restored.
+            expect(platformsJson[secondPlatformAdded]).toBeDefined();
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/b77129a9/cordova-lib/spec-cordova/pkgJson.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson.spec.js b/cordova-lib/spec-cordova/pkgJson.spec.js
index 3920c1a..654fb1d 100644
--- a/cordova-lib/spec-cordova/pkgJson.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson.spec.js
@@ -393,7 +393,7 @@ describe('platform end-to-end with --save', function () {
         // Check there are no platforms yet.
         emptyPlatformList().then(function() {
             // Add the testing platform with --save and add specific version to android platform.
-            return cordova.raw.platform('add', ['android@6.1.0', 'ios'], {'save':true, 'fetch':true});
+            return cordova.raw.platform('add', ['android@6.1.0', 'ios@4.3.0'], {'save':true, 'fetch':true});
         }).then(function() {
             // Delete any previous caches of require(package.json).
             delete require.cache[require.resolve(pkgJsonPath)];

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/b77129a9/cordova-lib/spec-plugman/fetch.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/fetch.spec.js b/cordova-lib/spec-plugman/fetch.spec.js
index 10e93c4..ce72895 100644
--- a/cordova-lib/spec-plugman/fetch.spec.js
+++ b/cordova-lib/spec-plugman/fetch.spec.js
@@ -214,20 +214,23 @@ describe('fetch', function() {
         it('Test 017 : should fetch from a commit-sha', function(done) {
             wrapper(fetch('http://github.com/apache/cordova-plugin-device.git#ad5f1e7bfd05ef98c01df549a0fa98036a5625db', temp, { expected_id: 'cordova-plugin-device' }), done, function() {
                 expect(1).toBe(1);
+                done();
             });
-        });
+        }, 20000);
         // this branch uses the old id
         it('Test 018 : should fetch from a branch', function(done) {
             wrapper(fetch('http://github.com/apache/cordova-plugin-device.git#cdvtest', temp, { expected_id: 'org.apache.cordova.device' }), done, function() {
                 expect(1).toBe(1);
+                done();
             });
-        });
+        }, 20000);
         // this tag uses the new id
         it('Test 019 : should fetch from a tag', function(done) {
             wrapper(fetch('http://github.com/apache/cordova-plugin-device.git#r1.0.0', temp, { expected_id: 'cordova-plugin-device' }), done, function() {
                 expect(1).toBe(1);
+                done();
             });
-        });
+        }, 20000);
     });
 
     describe('fetch recursive error CB-8809', function(){


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


[11/12] cordova-lib git commit: CB-12001: Added more restore tests for platform/plugin/spec.

Posted by st...@apache.org.
CB-12001: Added more restore tests for platform/plugin/spec.


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/ed9ba79a
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/ed9ba79a
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/ed9ba79a

Branch: refs/heads/master
Commit: ed9ba79ae9892f2962db9aa13cf7a110235c88c3
Parents: b247e12
Author: audreyso <au...@adobe.com>
Authored: Fri Nov 11 12:45:05 2016 -0800
Committer: Audrey So <au...@apache.org>
Committed: Thu Jan 26 17:36:52 2017 -0800

----------------------------------------------------------------------
 .../fixtures/basePkgJson10/config.xml           |  19 +
 .../fixtures/basePkgJson10/package.json         |  30 +
 .../fixtures/basePkgJson10/plugins/.svn         |   0
 .../fixtures/basePkgJson10/www/css/index.css    | 115 ++++
 .../fixtures/basePkgJson10/www/index.html       |  43 ++
 .../fixtures/basePkgJson10/www/js/index.js      |  49 ++
 .../fixtures/basePkgJson10/www/spec.html        |  68 ++
 .../fixtures/basePkgJson11/config.xml           |  20 +
 .../fixtures/basePkgJson11/package.json         |  27 +
 .../fixtures/basePkgJson11/plugins/.svn         |   0
 .../fixtures/basePkgJson11/www/css/index.css    | 115 ++++
 .../fixtures/basePkgJson11/www/index.html       |  43 ++
 .../fixtures/basePkgJson11/www/js/index.js      |  49 ++
 .../fixtures/basePkgJson11/www/spec.html        |  68 ++
 .../fixtures/basePkgJson12/config.xml           |  15 +
 .../fixtures/basePkgJson12/package.json         |  25 +
 .../fixtures/basePkgJson12/plugins/.svn         |   0
 .../fixtures/basePkgJson12/www/css/index.css    | 115 ++++
 .../fixtures/basePkgJson12/www/index.html       |  43 ++
 .../fixtures/basePkgJson12/www/js/index.js      |  49 ++
 .../fixtures/basePkgJson12/www/spec.html        |  68 ++
 .../fixtures/basePkgJson13/config.xml           |  16 +
 .../fixtures/basePkgJson13/plugins/.svn         |   0
 .../fixtures/basePkgJson13/www/css/index.css    | 115 ++++
 .../fixtures/basePkgJson13/www/index.html       |  43 ++
 .../fixtures/basePkgJson13/www/js/index.js      |  49 ++
 .../fixtures/basePkgJson13/www/spec.html        |  68 ++
 .../fixtures/basePkgJson2/config.xml            |  16 -
 .../fixtures/basePkgJson2/package.json          |  24 -
 .../fixtures/basePkgJson2/plugins/.svn          |   0
 .../fixtures/basePkgJson2/www/css/index.css     | 115 ----
 .../fixtures/basePkgJson2/www/index.html        |  43 --
 .../fixtures/basePkgJson2/www/js/index.js       |  49 --
 .../fixtures/basePkgJson2/www/spec.html         |  68 --
 .../fixtures/basePkgJson4/config.xml            |   2 +-
 .../fixtures/basePkgJson4/package.json          |   3 +-
 .../fixtures/basePkgJson5/config.xml            |   4 +-
 .../fixtures/basePkgJson8/config.xml            |  18 +
 .../fixtures/basePkgJson8/package.json          |  25 +
 .../fixtures/basePkgJson8/plugins/.svn          |   0
 .../fixtures/basePkgJson8/www/css/index.css     | 115 ++++
 .../fixtures/basePkgJson8/www/index.html        |  43 ++
 .../fixtures/basePkgJson8/www/js/index.js       |  49 ++
 .../fixtures/basePkgJson8/www/spec.html         |  68 ++
 .../fixtures/basePkgJson9/config.xml            |  18 +
 .../fixtures/basePkgJson9/package.json          |  25 +
 .../fixtures/basePkgJson9/plugins/.svn          |   0
 .../fixtures/basePkgJson9/www/css/index.css     | 115 ++++
 .../fixtures/basePkgJson9/www/index.html        |  43 ++
 .../fixtures/basePkgJson9/www/js/index.js       |  49 ++
 .../fixtures/basePkgJson9/www/spec.html         |  68 ++
 .../spec-cordova/pkgJson-restore.spec.js        | 613 ++++++++++++++++---
 cordova-lib/spec-cordova/pkgJson.spec.js        | 154 +++--
 cordova-lib/src/cordova/platform.js             |  11 +-
 cordova-lib/src/cordova/restore-util.js         | 219 ++++---
 55 files changed, 2679 insertions(+), 530 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml
new file mode 100644
index 0000000..6134bed
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson10/config.xml
@@ -0,0 +1,19 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="ios"/>
+    <plugin name="cordova-plugin-camera">
+        <variable name="variable_3" value="value_3" />
+    </plugin>
+    <plugin name="cordova-plugin-splashscreen" />
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json
new file mode 100644
index 0000000..58dc4ae
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson10/package.json
@@ -0,0 +1,30 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "ios"
+        ],
+        "plugins": {
+            "cordova-plugin-device": {
+                "variable_1": "value_1"
+            },
+            "cordova-plugin-splashscreen": {},
+            "cordova-plugin-camera": {
+                "variable_1": " ",
+                "variable_2": " "
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson10/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson10/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson10/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml
new file mode 100644
index 0000000..f5f7238
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson11/config.xml
@@ -0,0 +1,20 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="ios"/>
+    <plugin name="cordova-plugin-camera" spec="~2.2.0">
+        <variable name="variable_1" value="value_1" />
+        <variable name="variable_2" value="value_2" />
+    </plugin>
+    <plugin name="cordova-plugin-device" spec="~1.0.0" />
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json
new file mode 100644
index 0000000..d86529b
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson11/package.json
@@ -0,0 +1,27 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "ios"
+        ],
+        "plugins": {
+            "cordova-plugin-splashscreen": {},
+            "cordova-plugin-camera": {
+                "variable_1": "value_1",
+                "variable_3": "value_3"
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson11/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson11/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson11/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml
new file mode 100644
index 0000000..0206c02
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson12/config.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="ios"/>
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json
new file mode 100644
index 0000000..08c11dd
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson12/package.json
@@ -0,0 +1,25 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "ios"
+        ],
+        "plugins": {
+            "cordova-plugin-camera": {
+                "variable_1": "value_1"
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson12/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson12/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson12/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson13/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson13/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson13/config.xml
new file mode 100644
index 0000000..e8d7783
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson13/config.xml
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="android"/>
+    <plugin name="cordova-plugin-device" />
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson13/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson13/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson13/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/js/index.js
new file mode 100644
index 0000000..31d9064
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/js/index.js
@@ -0,0 +1,49 @@
+/*
+ * 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 app = {
+    // Application Constructor
+    initialize: function() {
+        this.bindEvents();
+    },
+    // Bind Event Listeners
+    //
+    // Bind any events that are required on startup. Common events are:
+    // 'load', 'deviceready', 'offline', and 'online'.
+    bindEvents: function() {
+        document.addEventListener('deviceready', this.onDeviceReady, false);
+    },
+    // deviceready Event Handler
+    //
+    // The scope of 'this' is the event. In order to call the 'receivedEvent'
+    // function, we must explicity call 'app.receivedEvent(...);'
+    onDeviceReady: function() {
+        app.receivedEvent('deviceready');
+    },
+    // Update DOM on a Received Event
+    receivedEvent: function(id) {
+        var parentElement = document.getElementById(id);
+        var listeningElement = parentElement.querySelector('.listening');
+        var receivedElement = parentElement.querySelector('.received');
+
+        listeningElement.setAttribute('style', 'display:none;');
+        receivedElement.setAttribute('style', 'display:block;');
+
+        console.log('Received Event: ' + id);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/spec.html
new file mode 100644
index 0000000..71f00de
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson13/www/spec.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <title>Jasmine Spec Runner</title>
+
+        <!-- jasmine source -->
+        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
+        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
+        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
+
+        <!-- include source files here... -->
+        <script type="text/javascript" src="js/index.js"></script>
+
+        <!-- include spec files here... -->
+        <script type="text/javascript" src="spec/helper.js"></script>
+        <script type="text/javascript" src="spec/index.js"></script>
+
+        <script type="text/javascript">
+            (function() {
+                var jasmineEnv = jasmine.getEnv();
+                jasmineEnv.updateInterval = 1000;
+
+                var htmlReporter = new jasmine.HtmlReporter();
+
+                jasmineEnv.addReporter(htmlReporter);
+
+                jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                };
+
+                var currentWindowOnload = window.onload;
+
+                window.onload = function() {
+                    if (currentWindowOnload) {
+                        currentWindowOnload();
+                    }
+                    execJasmine();
+                };
+
+                function execJasmine() {
+                    jasmineEnv.execute();
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <div id="stage" style="display:none;"></div>
+    </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
deleted file mode 100644
index 9c807f1..0000000
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version='1.0' encoding='utf-8'?>
-<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
-    <name>TestBase</name>
-    <description>
-        A sample Apache Cordova application that responds to the deviceready event.
-    </description>
-    <author email="dev@cordova.apache.org" href="http://cordova.io">
-        Apache Cordova Team
-    </author>
-    <content src="index.html" />
-    <access origin="*" />
-    <preference name="fullscreen" value="true" />
-    <preference name="webviewbounce" value="true" />
-    <engine name="ios"/>
-    <plugin name="cordova-plugin-camera"/>
-</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
deleted file mode 100644
index 746681f..0000000
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    "name": "testbase",
-    "version": "1.0.0",
-    "description": "",
-    "main": "index.js",
-    "dependencies": {
-        "cordova-plugin-camera": "^2.3.0"
-    },
-    "devDependencies": {},
-    "scripts": {
-        "test": "echo \"Error: no test specified\" && exit 1"
-    },
-    "author": "",
-    "license": "ISC",
-    "cordova": {
-        "platforms": [
-            "ios"
-        ],
-        "plugins": [
-            "cordova-plugin-camera",
-            "cordova-plugin-splashscreen"
-        ]
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson2/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson2/plugins/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/css/index.css
deleted file mode 100644
index 51daa79..0000000
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/css/index.css
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.
- */
-* {
-    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
-}
-
-body {
-    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
-    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
-    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
-    background-color:#E4E4E4;
-    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
-    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
-    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
-    background-image:-webkit-gradient(
-        linear,
-        left top,
-        left bottom,
-        color-stop(0, #A7A7A7),
-        color-stop(0.51, #E4E4E4)
-    );
-    background-attachment:fixed;
-    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
-    font-size:12px;
-    height:100%;
-    margin:0px;
-    padding:0px;
-    text-transform:uppercase;
-    width:100%;
-}
-
-/* Portrait layout (default) */
-.app {
-    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
-    position:absolute;             /* position in the center of the screen */
-    left:50%;
-    top:50%;
-    height:50px;                   /* text area height */
-    width:225px;                   /* text area width */
-    text-align:center;
-    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
-    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
-                                   /* offset horizontal: half of text area width */
-}
-
-/* Landscape layout (with min-width) */
-@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
-    .app {
-        background-position:left center;
-        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
-        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
-                                      /* offset horizontal: half of image width and text area width */
-    }
-}
-
-h1 {
-    font-size:24px;
-    font-weight:normal;
-    margin:0px;
-    overflow:visible;
-    padding:0px;
-    text-align:center;
-}
-
-.event {
-    border-radius:4px;
-    -webkit-border-radius:4px;
-    color:#FFFFFF;
-    font-size:12px;
-    margin:0px 30px;
-    padding:2px 0px;
-}
-
-.event.listening {
-    background-color:#333333;
-    display:block;
-}
-
-.event.received {
-    background-color:#4B946A;
-    display:none;
-}
-
-@keyframes fade {
-    from { opacity: 1.0; }
-    50% { opacity: 0.4; }
-    to { opacity: 1.0; }
-}
- 
-@-webkit-keyframes fade {
-    from { opacity: 1.0; }
-    50% { opacity: 0.4; }
-    to { opacity: 1.0; }
-}
- 
-.blink {
-    animation:fade 3000ms infinite;
-    -webkit-animation:fade 3000ms infinite;
-}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/index.html
deleted file mode 100644
index bde5741..0000000
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/index.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE html>
-<!--
-    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.
--->
-<html>
-    <head>
-        <meta charset="utf-8" />
-        <meta name="format-detection" content="telephone=no" />
-        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
-        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
-        <link rel="stylesheet" type="text/css" href="css/index.css" />
-        <title>Hello World</title>
-    </head>
-    <body>
-        <div class="app">
-            <h1>Apache Cordova</h1>
-            <div id="deviceready" class="blink">
-                <p class="event listening">Connecting to Device</p>
-                <p class="event received">Device is Ready</p>
-            </div>
-        </div>
-        <script type="text/javascript" src="cordova.js"></script>
-        <script type="text/javascript" src="js/index.js"></script>
-        <script type="text/javascript">
-            app.initialize();
-        </script>
-    </body>
-</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/js/index.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/js/index.js b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/js/index.js
deleted file mode 100644
index 31d9064..0000000
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/js/index.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 app = {
-    // Application Constructor
-    initialize: function() {
-        this.bindEvents();
-    },
-    // Bind Event Listeners
-    //
-    // Bind any events that are required on startup. Common events are:
-    // 'load', 'deviceready', 'offline', and 'online'.
-    bindEvents: function() {
-        document.addEventListener('deviceready', this.onDeviceReady, false);
-    },
-    // deviceready Event Handler
-    //
-    // The scope of 'this' is the event. In order to call the 'receivedEvent'
-    // function, we must explicity call 'app.receivedEvent(...);'
-    onDeviceReady: function() {
-        app.receivedEvent('deviceready');
-    },
-    // Update DOM on a Received Event
-    receivedEvent: function(id) {
-        var parentElement = document.getElementById(id);
-        var listeningElement = parentElement.querySelector('.listening');
-        var receivedElement = parentElement.querySelector('.received');
-
-        listeningElement.setAttribute('style', 'display:none;');
-        receivedElement.setAttribute('style', 'display:block;');
-
-        console.log('Received Event: ' + id);
-    }
-};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/spec.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/spec.html b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/spec.html
deleted file mode 100644
index 71f00de..0000000
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/spec.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!DOCTYPE html>
-<!--
-    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.
--->
-<html>
-    <head>
-        <title>Jasmine Spec Runner</title>
-
-        <!-- jasmine source -->
-        <link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
-        <link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
-        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
-        <script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
-
-        <!-- include source files here... -->
-        <script type="text/javascript" src="js/index.js"></script>
-
-        <!-- include spec files here... -->
-        <script type="text/javascript" src="spec/helper.js"></script>
-        <script type="text/javascript" src="spec/index.js"></script>
-
-        <script type="text/javascript">
-            (function() {
-                var jasmineEnv = jasmine.getEnv();
-                jasmineEnv.updateInterval = 1000;
-
-                var htmlReporter = new jasmine.HtmlReporter();
-
-                jasmineEnv.addReporter(htmlReporter);
-
-                jasmineEnv.specFilter = function(spec) {
-                    return htmlReporter.specFilter(spec);
-                };
-
-                var currentWindowOnload = window.onload;
-
-                window.onload = function() {
-                    if (currentWindowOnload) {
-                        currentWindowOnload();
-                    }
-                    execJasmine();
-                };
-
-                function execJasmine() {
-                    jasmineEnv.execute();
-                }
-            })();
-        </script>
-    </head>
-    <body>
-        <div id="stage" style="display:none;"></div>
-    </body>
-</html>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml
index 7895772..b39102b 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/config.xml
@@ -11,5 +11,5 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
-    <engine name="android"/>
+    <engine name="android" spec="2.1.0"/>
 </widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
index 79c8269..f26e7c8 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson4/package.json
@@ -4,7 +4,8 @@
     "description": "",
     "main": "index.js",
     "dependencies": {
-        "cordova-plugin-camera": "^2.3.0"
+        "cordova-android": "^3.1.0",
+        "cordova-ios": "^4.3.0"
     },
     "devDependencies": {},
     "scripts": {

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
index f86b2cf..d6dc496 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson5/config.xml
@@ -11,6 +11,6 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
-    <engine name="android"/>
-    <engine name="ios"/>
+    <engine name="android" spec="6.0.0"/>
+    <engine name="ios" spec="^4.3.0"/>
 </widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml
new file mode 100644
index 0000000..dab76f5
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson8/config.xml
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>TestBase</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+    <engine name="ios"/>
+    <plugin name="cordova-plugin-camera">
+    <variable name="variable_1" value="config" />
+    </plugin>
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json
new file mode 100644
index 0000000..2f74834
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson8/package.json
@@ -0,0 +1,25 @@
+{
+    "name": "testbase",
+    "version": "1.0.0",
+    "description": "",
+    "main": "index.js",
+    "dependencies": {
+        "cordova-plugin-camera": "^2.3.0"
+    },
+    "devDependencies": {},
+    "scripts": {
+        "test": "echo \"Error: no test specified\" && exit 1"
+    },
+    "author": "",
+    "license": "ISC",
+    "cordova": {
+        "platforms": [
+            "ios"
+        ],
+        "plugins": {
+            "cordova-plugin-camera": {
+                "variable_1": "json"
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson8/plugins/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/plugins/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson8/plugins/.svn
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/css/index.css
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/css/index.css b/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/css/index.css
new file mode 100644
index 0000000..51daa79
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/css/index.css
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+ 
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/ed9ba79a/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/index.html
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/index.html b/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/index.html
new file mode 100644
index 0000000..bde5741
--- /dev/null
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson8/www/index.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <meta name="format-detection" content="telephone=no" />
+        <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
+        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
+        <link rel="stylesheet" type="text/css" href="css/index.css" />
+        <title>Hello World</title>
+    </head>
+    <body>
+        <div class="app">
+            <h1>Apache Cordova</h1>
+            <div id="deviceready" class="blink">
+                <p class="event listening">Connecting to Device</p>
+                <p class="event received">Device is Ready</p>
+            </div>
+        </div>
+        <script type="text/javascript" src="cordova.js"></script>
+        <script type="text/javascript" src="js/index.js"></script>
+        <script type="text/javascript">
+            app.initialize();
+        </script>
+    </body>
+</html>


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


[08/12] cordova-lib git commit: CB-12001: fixed failing test

Posted by st...@apache.org.
CB-12001: fixed failing test


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/d7a1487a
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/d7a1487a
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/d7a1487a

Branch: refs/heads/master
Commit: d7a1487a422452a74ce0efe4f5222431ca7c0fed
Parents: ed9ba79
Author: Steve Gill <st...@gmail.com>
Authored: Tue Nov 22 12:07:15 2016 -0800
Committer: Audrey So <au...@apache.org>
Committed: Thu Jan 26 17:36:52 2017 -0800

----------------------------------------------------------------------
 .../spec-cordova/pkgJson-restore.spec.js        | 34 ++++++++++----------
 cordova-lib/src/cordova/platform.js             |  7 ++--
 2 files changed, 22 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/d7a1487a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
index 2b65077..4d5dfec 100644
--- a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -30,7 +30,7 @@ var helpers = require('./helpers'),
 
 // Use basePkgJson
 describe('tests platform/spec restore with --save', function () {
-    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson2');
     var project = path.join(tmpDir, 'project');
     var results;
 
@@ -343,14 +343,14 @@ describe('tests platform/spec restore with --save', function () {
         var platformsFolderPath = path.join(cwd,'platforms/platforms.json');
         var platformsJson;
         var configXmlPath = path.join(cwd, 'config.xml');
-        var androidPlatform = 'android';
+        var bPlatform = 'browser';
         var engines;
         var engNames;
         var engSpec;
 
         emptyPlatformList().then(function() {
             // Add platform with save and fetch
-            return cordova.raw.platform('add', 'https://github.com/apache/cordova-android', {'save':true, 'fetch':true});
+            return cordova.raw.platform('add', 'https://github.com/apache/cordova-browser', {'save':true, 'fetch':true});
         }).then(function() {
             // Check that platform was added to config.xml successfully.
             var cfg = new ConfigParser(configXmlPath);
@@ -361,30 +361,30 @@ describe('tests platform/spec restore with --save', function () {
             engSpec = engines.map(function(elem) {
                 return elem.spec;
             });
-            expect(engNames).toEqual([ androidPlatform]);
-            expect(engSpec).toEqual([ 'https://github.com/apache/cordova-android' ]);
+            expect(engNames).toEqual([bPlatform]);
+            expect(engSpec).toEqual([ 'https://github.com/apache/cordova-browser' ]);
             // Check that platform was added to pkg.json successfully.
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            expect(pkgJson.cordova.platforms.indexOf('android')).toBeDefined();
-            expect(pkgJson.dependencies['cordova-android']).toEqual('git+https://github.com/apache/cordova-android.git');
+            expect(pkgJson.cordova.platforms.indexOf('browser')).toBeDefined();
+            expect(pkgJson.dependencies['cordova-browser']).toEqual('git+https://github.com/apache/cordova-browser.git');
             // Check that platform was added to platforms list successfully.
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-            expect(platformsJson[androidPlatform]).toBeDefined();
+            expect(platformsJson[bPlatform]).toBeDefined();
         }).then(function() {
             // Remove platform without --save.
-            return cordova.raw.platform('rm', androidPlatform, {'fetch':true});
+            return cordova.raw.platform('rm', bPlatform, {'fetch':true});
         }).then(function() {
             // Platform in pkg.json should still be there.
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            expect(pkgJson.cordova.platforms.indexOf('android')).toBeDefined();
-            expect(pkgJson.dependencies['cordova-android']).toEqual('git+https://github.com/apache/cordova-android.git');
+            expect(pkgJson.cordova.platforms.indexOf('browser')).toBeDefined();
+            expect(pkgJson.dependencies['cordova-browser']).toEqual('git+https://github.com/apache/cordova-browser.git');
             // Platform in platforms.json should not be there.
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-            expect(platformsJson[androidPlatform]).toBeUndefined();
+            expect(platformsJson[bPlatform]).toBeUndefined();
         }).then(function() {
             // Run cordova prepare
             return cordova.raw.prepare({'fetch':true});
@@ -398,17 +398,17 @@ describe('tests platform/spec restore with --save', function () {
             engSpec = engines.map(function(elem) {
                 return elem.spec;
             });
-            expect(engNames).toEqual([ 'android' ]);
-            expect(engSpec).toEqual([ 'git+https://github.com/apache/cordova-android.git' ]);
+            expect(engNames).toEqual([ 'browser' ]);
+            expect(engSpec).toEqual([ 'git+https://github.com/apache/cordova-browser.git' ]);
             // No change to pkg.json.
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
-            expect(pkgJson.cordova.platforms.indexOf('android')).toBeDefined();
-            expect(pkgJson.dependencies['cordova-android']).toEqual('git+https://github.com/apache/cordova-android.git');
+            expect(pkgJson.cordova.platforms.indexOf('browser')).toBeDefined();
+            expect(pkgJson.dependencies['cordova-browser']).toEqual('git+https://github.com/apache/cordova-browser.git');
             // Check that platform was restored to platform.json list successfully.
             delete require.cache[require.resolve(platformsFolderPath)];
             platformsJson = require(platformsFolderPath);
-            expect(platformsJson[androidPlatform]).toBeDefined();
+            expect(platformsJson[bPlatform]).toBeDefined();
         }).fail(function(err) {
             expect(err).toBeUndefined();
         }).fin(done);

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/d7a1487a/cordova-lib/src/cordova/platform.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/platform.js b/cordova-lib/src/cordova/platform.js
index e395835..c529632 100644
--- a/cordova-lib/src/cordova/platform.js
+++ b/cordova-lib/src/cordova/platform.js
@@ -180,6 +180,7 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                     // basically a valid NodeJS script but intended to be used as a regular
                     // executable script.
                     if (path.basename(apiEntryPoint) === 'Api.js') {
+                        delete require.cache[apiEntryPoint];
                         PlatformApi = require(apiEntryPoint);
                         events.emit('verbose', 'PlatformApi successfully found for platform ' + platform);
                     }
@@ -324,7 +325,6 @@ function downloadPlatform(projectRoot, platform, version, opts) {
             if(!platform) {
                 target = version;
             }
-
             events.emit('log', 'Using cordova-fetch for '+ target);
             return fetch(target, projectRoot, opts);
         }
@@ -369,7 +369,10 @@ function getPlatformDetailsFromDir(dir, platformIfKnown){
     var version;
 
     try {
-        var pkg = require(path.join(libDir, 'package'));
+        var pkgPath = path.join(libDir, 'package.json');
+        delete require.cache[pkgPath];
+        var pkg = require(pkgPath);
+
         platform = platformFromName(pkg.name);
         version = pkg.version;
     } catch(e) {


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


[02/12] cordova-lib git commit: CB-11960: Added more save/restore tests.

Posted by st...@apache.org.
CB-11960: Added more save/restore tests.


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/4cd1be81
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/4cd1be81
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/4cd1be81

Branch: refs/heads/master
Commit: 4cd1be81598406082b075c74081eaf85c9363c66
Parents: 7df35cb
Author: Steve Gill <st...@gmail.com>
Authored: Fri Nov 4 13:55:52 2016 -0700
Committer: Audrey So <au...@apache.org>
Committed: Thu Jan 26 17:36:51 2017 -0800

----------------------------------------------------------------------
 .../fixtures/basePkgJson/merges/.svn            |   0
 .../fixtures/basePkgJson/www/img/logo.png       | Bin 21814 -> 0 bytes
 .../fixtures/basePkgJson2/config.xml            |   2 +
 .../fixtures/basePkgJson2/merges/.svn           |   0
 .../fixtures/basePkgJson2/package.json          |  14 +-
 .../fixtures/basePkgJson2/www/img/logo.png      | Bin 21814 -> 0 bytes
 cordova-lib/spec-cordova/package.json.spec.js   | 389 -------------------
 .../spec-cordova/pkgJson-restore.spec.js        | 227 +++++++++++
 cordova-lib/spec-cordova/pkgJson.spec.js        |  38 +-
 cordova-lib/spec-cordova/platform.spec.js       |  39 +-
 cordova-lib/src/cordova/platform.js             |  72 ++--
 cordova-lib/src/cordova/restore-util.js         | 100 +++--
 12 files changed, 372 insertions(+), 509 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/fixtures/basePkgJson/merges/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/merges/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson/merges/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/fixtures/basePkgJson/www/img/logo.png
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson/www/img/logo.png b/cordova-lib/spec-cordova/fixtures/basePkgJson/www/img/logo.png
deleted file mode 100644
index 9519e7d..0000000
Binary files a/cordova-lib/spec-cordova/fixtures/basePkgJson/www/img/logo.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml b/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
index 9e7b9e0..9c807f1 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson2/config.xml
@@ -11,4 +11,6 @@
     <access origin="*" />
     <preference name="fullscreen" value="true" />
     <preference name="webviewbounce" value="true" />
+    <engine name="ios"/>
+    <plugin name="cordova-plugin-camera"/>
 </widget>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/fixtures/basePkgJson2/merges/.svn
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/merges/.svn b/cordova-lib/spec-cordova/fixtures/basePkgJson2/merges/.svn
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json b/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
index 0155e61..746681f 100644
--- a/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
+++ b/cordova-lib/spec-cordova/fixtures/basePkgJson2/package.json
@@ -10,17 +10,15 @@
     "scripts": {
         "test": "echo \"Error: no test specified\" && exit 1"
     },
-    "author": "audrey",
+    "author": "",
     "license": "ISC",
     "cordova": {
         "platforms": [
-            "android"
+            "ios"
         ],
-        "plugins": {
-            "cordova-plugin-camera": {
-                "CAMERA_USAGE_DESCRIPTION": " ",
-                "PHOTOLIBRARY_USAGE_DESCRIPTION": " "
-            }
-        }
+        "plugins": [
+            "cordova-plugin-camera",
+            "cordova-plugin-splashscreen"
+        ]
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/img/logo.png
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/img/logo.png b/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/img/logo.png
deleted file mode 100644
index 9519e7d..0000000
Binary files a/cordova-lib/spec-cordova/fixtures/basePkgJson2/www/img/logo.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/package.json.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/package.json.spec.js b/cordova-lib/spec-cordova/package.json.spec.js
deleted file mode 100644
index 1755834..0000000
--- a/cordova-lib/spec-cordova/package.json.spec.js
+++ /dev/null
@@ -1,389 +0,0 @@
-/**
-    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 helpers = require('./helpers'),
-    path = require('path'),
-    fs = require('fs'),
-    shell = require('shelljs'),
-    superspawn = require('cordova-common').superspawn,
-    Q = require('q'),
-    events = require('cordova-common').events,
-    cordova = require('../src/cordova/cordova'),
-    rewire = require('rewire'),
-    prepare = require('../src/cordova/prepare'),
-    platforms = require('../src/platforms/platforms'),
-    platform = rewire('../src/cordova/platform.js');
-
-var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
-var pluginsDir = path.join(__dirname, 'fixtures', 'plugins');
-
-function addPlugin(target, id, options) {
-    // Checks that there are no plugins yet.
-    return cordova.raw.plugin('list').then(function() {
-        expect(results).toMatch(/No plugins added/gi);
-    }).then(function() {
-        // Adds a fake plugin from fixtures.
-        return cordova.raw.plugin('add', target, options);
-    }).then(function() {
-        expect(path.join(project, 'plugins', id, 'plugin.xml')).toExist();
-    }).then(function() {
-        return cordova.raw.plugin('ls');
-    }).then(function() {
-        expect(results).toContain(id);
-    });
-}
-// Runs: remove, list.
-function removePlugin(id, options) {
-    return cordova.raw.plugin('rm', id)
-    .then(function() {
-        // The whole dir should be gone.
-        expect(path.join(project, 'plugins', id)).not.toExist();
-    }).then(function() {
-        return cordova.raw.plugin('ls');
-    }).then(function() {
-        expect(results).toMatch(/No plugins added/gi);
-    });
-}
-// Checks if plugin list is empty.
-function emptyPluginList() {
-    return cordova.raw.plugin('list').then(function() {
-        var installed = results.match(/Installed plugins:\n  (.*)/);
-        expect(installed).toBeDefined();
-        expect(installed[1].indexOf('fake-plugin')).toBe(-1);
-    });
-}
-// Checks if plugin list exists.
-function fullPluginList() {
-    return cordova.raw.plugin('list').then(function() {
-        var installed = results.match(/Installed plugins:\n  (.*)/);
-        expect(installed).toBeDefined();
-        expect(installed[1].indexOf('fake-plugin')).toBeGreaterThan(-1);
-    });
-}
-
-// This group of tests checks if plugins are added and removed as expected.
-describe('plugin end-to-end', function() {
-    var pluginId = 'cordova-plugin-device';
-    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
-    var project = path.join(tmpDir, 'project');
-    var results;
-
-    events.on('results', function(res) { results = res; });
-
-    beforeEach(function() {
-        shell.rm('-rf', project);
-
-        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
-        // Using /* doesn't work because of hidden files.
-        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
-        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
-        // Copy some platform to avoid working on a project with no platforms.
-        shell.cp('-R', path.join(__dirname, 'fixtures', 'platforms', helpers.testPlatform), path.join(project, 'platforms'));
-        process.chdir(project);
-
-        delete process.env.PWD;
-
-        spyOn(prepare, 'preparePlatforms').andCallThrough();
-    });
-
-    afterEach(function() {
-        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
-        shell.rm('-rf', tmpDir);
-    });
-
-    it('Test#001 : should successfully add and remove a plugin with no options', function(done) {
-        var pkgJsonPath = path.join(process.cwd(),'package.json');
-        var pkgJson;
-        var retPromise;
-
-        expect(pkgJsonPath).toExist();
-
-        // Add the plugin with --save
-        return cordova.raw.plugin('add', pluginId, {'save':true})
-        .then(function() {
-            // Check that the plugin add was successful.
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            expect(pkgJson).not.toBeUndefined();
-            expect(pkgJson.cordova.plugins).not.toBeUndefined();
-            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
-        }).then(function() {
-            // And now remove it with --save.
-            return cordova.raw.plugin('rm', pluginId, {'save':true})
-        }).then(function() {
-            // Delete any previous caches of require(package.json)
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Checking that the plugin removed is in not in the platforms
-            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
-        }).fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    });
-
-    it('Test#002 : should successfully NOT add a plugin if save is not there', function(done) {
-        var pkgJsonPath = path.join(process.cwd(),'package.json');
-        var pkgJson;
-
-        expect(pkgJsonPath).toExist();
-
-        // Add the plugin with --save.
-        return cordova.raw.plugin('add', 'cordova-plugin-camera', {'save':true})
-        .then(function() {
-            // Add a second plugin without save
-            return cordova.raw.plugin('add', pluginId);
-        }).then(function() {
-            // Check the plugin add was successful for the first plugin that had --save.
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            expect(pkgJson).not.toBeUndefined();
-            expect(pkgJson.cordova.plugins['cordova-plugin-camera']).toBeDefined();
-            // Expect that the second plugin is not added.
-            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
-        }).fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    });
-
-    it('Test#003 : should NOT remove plugin if there is no --save', function(done) {
-        var pkgJsonPath = path.join(process.cwd(),'package.json');
-        var pkgJson;
-        var retPromise;
-
-        expect(pkgJsonPath).toExist();
-
-        // Add the plugin with --save.
-        return cordova.raw.plugin('add', pluginId, {'save':true})
-        .then(function() {
-            // Check the platform add was successful.
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            expect(pkgJson).not.toBeUndefined();
-            expect(pkgJson.cordova.plugins).toBeDefined();
-            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
-        }).then(function() {
-            // And now remove it, but without --save.
-            return cordova.raw.plugin('rm', 'cordova-plugin-device')
-        }).then(function() {
-            // Delete any previous caches of require(package.json)
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // The plugin should still be in package.json.
-            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
-        }).fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    });
-
-    it('Test#004 : should successfully add and remove a plugin with variables and save', function(done) {
-        var pkgJsonPath = path.join(process.cwd(),'package.json');
-        var pkgJson;
-        var retPromise;
-
-        expect(pkgJsonPath).toExist();
-
-        // Add the plugin with --save.
-        return cordova.raw.plugin('add', pluginId, {'save':true, 'cli_variables': {'someKey':'someValue'}})
-        .then(function() {
-            // Delete any previous caches of require(package.json)
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Check the plugin add was successful and that variables have been added too.
-            expect(pkgJson).not.toBeUndefined();
-            expect(pkgJson.cordova.plugins).toBeDefined();
-            expect(pkgJson.cordova.plugins[pluginId]).toBeDefined();
-            expect(pkgJson.cordova.plugins[pluginId]['someKey']).toEqual('someValue');
-        }).then(function() {
-            // And now remove it with --save.
-            return cordova.raw.plugin('rm', pluginId, {'save':true})
-        }).then(function() {
-            // Delete any previous caches of require(package.json)
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Checking that the plugin and variables were removed successfully.
-            expect(pkgJson.cordova.plugins[pluginId]).toBeUndefined();
-        }).fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    });
-});
-
-// This group of tests checks if platforms are added and removed as expected.
-describe('platform end-to-end with --save', function () {
-    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
-    var project = path.join(tmpDir, 'project');
-    var results;
-
-    beforeEach(function() {
-        shell.rm('-rf', tmpDir);
-
-        // cp then mv because we need to copy everything, but that means it'll copy the whole directory.
-        // Using /* doesn't work because of hidden files.
-        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson'), tmpDir);
-        shell.mv(path.join(tmpDir, 'basePkgJson'), project);
-        process.chdir(project);
-
-        // The config.json in the fixture project points at fake "local" paths.
-        // Since it's not a URL, the lazy-loader will just return the junk path.
-        spyOn(superspawn, 'spawn').andCallFake(function(cmd, args) {
-            if (cmd.match(/create\b/)) {
-                // This is a call to the bin/create script, so do the copy ourselves.
-                shell.cp('-R', path.join(__dirname, 'fixtures', 'platforms', 'android'), path.join(project, 'platforms'));
-            } else if(cmd.match(/version\b/)) {
-                return Q('3.3.0');
-            } else if(cmd.match(/update\b/)) {
-                fs.writeFileSync(path.join(project, 'platforms', helpers.testPlatform, 'updated'), 'I was updated!', 'utf-8');
-            }
-            return Q();
-        });
-        events.on('results', function(res) { results = res; });
-    });
-
-    afterEach(function() {
-        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
-        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
-        shell.rm('-rf', tmpDir);
-
-    });
-
-    // Factoring out some repeated checks.
-    function emptyPlatformList() {
-        return cordova.raw.platform('list').then(function() {
-            var installed = results.match(/Installed platforms:\n  (.*)/);
-            expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
-        });
-    }
-    function fullPlatformList() {
-        return cordova.raw.platform('list').then(function() {
-            var installed = results.match(/Installed platforms:\n  (.*)/);
-            expect(installed).toBeDefined();
-            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-        });
-    }
-
-    it('Test#006 : platform is added and removed correctly with --save', function(done) {
-        var pkgJsonPath = path.join(process.cwd(),'package.json');
-        expect(pkgJsonPath).toExist();
-        var pkgJson = require(pkgJsonPath);
-
-        // Check there are no platforms yet.
-        emptyPlatformList().then(function() {
-            // Add the testing platform with --save.
-            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
-        }).then(function() {
-            // Check the platform add was successful.
-            expect(pkgJson.cordova.platforms).not.toBeUndefined();
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-
-        }).then(fullPlatformList) // Platform should still be in platform ls.
-        .then(function() {
-            // And now remove it with --save.
-            return cordova.raw.platform('rm', [helpers.testPlatform], {'save':true});
-        }).then(function() {
-            // Delete any previous caches of require(package.json)
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Checking that the platform removed is in not in the platforms key
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
-        }).then(emptyPlatformList) // platform ls should be empty too.
-        .fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    });
-
-    it('Test#007 : should not remove platforms from package.json when removing without --save', function(done) {
-        var pkgJsonPath = path.join(process.cwd(),'package.json');
-        expect(pkgJsonPath).toExist();
-        var pkgJson = require(pkgJsonPath);
-        emptyPlatformList().then(function() {
-            // Add the testing platform with --save.
-            return cordova.raw.platform('add', [helpers.testPlatform], {'save':true});
-        }).then(function() {
-            // Check the platform add was successful.
-            expect(pkgJson.cordova.platforms).not.toBeUndefined();
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-        }).then(fullPlatformList) // Platform should still be in platform ls.
-        .then(function() {
-            // And now remove it without --save.
-            return cordova.raw.platform('rm', [helpers.testPlatform]);
-        }).then(function() {
-            // Delete any previous caches of require(package.json)
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-
-            // Check that the platform removed without --save is still in platforms key.
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
-        }).then(emptyPlatformList)
-        .fail(function(err) {
-            expect(err).toBeUndefined();
-        }).fin(done);
-    });
-
-    it('Test#008 : should not add platform to package.json when adding without --save', function(done) {
-        var pkgJsonPath = path.join(process.cwd(),'package.json');
-        var pkgJson;
-        var platformToAdd = helpers.testPlatform;
-        expect(pkgJsonPath).toExist();
-
-        // Add platform without --save.
-        cordova.raw.platform('add',platformToAdd)
-        .then(function() {
-            // Check the platform add was successful, reload, skipping cache
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Beware empty/missing cordova object
-            var pckJsonCordova = pkgJson.cordova || {platforms:[]};
-            // Check that the platform has NOT been added.
-            expect(pckJsonCordova.platforms.indexOf(platformToAdd)).toEqual(-1);
-        })
-        .fail(function(err) {
-            expect(err).toBeUndefined();
-        })
-        .fin(done);
-    });
-
-    it('Test#009 : should only add the platform to package.json with --save', function(done) {
-        var pkgJsonPath = path.join(process.cwd(),'package.json');
-        var pkgJson;
-        var platformToAdd = helpers.testPlatform;
-        var platformNotToAdd = 'ios';
-        expect(pkgJsonPath).toExist();
-
-        // Add a platform without --save.
-        cordova.raw.platform('add',platformNotToAdd)
-        .then(function() {
-            // And now add another platform with --save.
-            return cordova.raw.platform('add', platformToAdd, {'save':true});
-        }).then(function() {
-            // Check the platform add was successful, reload, skipping cache
-            delete require.cache[require.resolve(pkgJsonPath)];
-            pkgJson = require(pkgJsonPath);
-            // Beware empty/missing cordova object
-            var pckJsonCordova = pkgJson.cordova || {platforms:[]};
-            // Check that only the platform added with --save was added to package.json.
-            expect(pckJsonCordova.platforms.indexOf(platformToAdd)).toBeGreaterThan(-1);
-            expect(pckJsonCordova.platforms.indexOf(platformNotToAdd)).toEqual(-1);
-        })
-        .fail(function(err) {
-            expect(err).toBeUndefined();
-        })
-        .fin(done);
-    });
-});
-

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/pkgJson-restore.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson-restore.spec.js b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
index e4018e3..61a8945 100644
--- a/cordova-lib/spec-cordova/pkgJson-restore.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson-restore.spec.js
@@ -613,6 +613,233 @@ describe('update config.xml to include platforms in pkg.json', function () {
     },30000);
 });
 
+// PLUGINS START HERE
 
+// Use basePkgJson2 as pkg.json contains 2 plugins and config.xml contains 1 plugin
+describe('update config.xml to include plugins found in pkg.json', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson2'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson2'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        var cwd = process.cwd();
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+
+    /** Test#008 will check the platform list in package.json and config.xml. 
+    *   When packge.json has 'splashscreen and camera' and config.xml only contains 'splashscreen', run cordova prepare
+    *   and config.xml is updated to include 'camera'.
+    */
+    it('Test#008 :  if pkgJson has 2 plugins and config.xml has only one of those plugins, update config to include both', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var cfg1 = new ConfigParser(configXmlPath);
+        var plugins = cfg1.getPluginIdList();
+        var pkgJson = require(pkgJsonPath);
+        var pluginNames = plugins.map(function(elem) {
+            return elem;
+        });
+        var configPlugArray = pluginNames.slice();
+
+        // Expect that pkg.json exists with 2 plugins
+        expect(pkgJson.cordova.plugins).toBeDefined();
+        expect(pkgJson.cordova.plugins.length === 1);
+        expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(0);
+        expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+        // Expect that config.xml exists with 1 plugin
+        expect(configPlugArray).toBeDefined();
+        expect(configPlugArray.length === 0);
+        expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(-1);
+        expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
+
+        emptyPlatformList().then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            var cfg2 = new ConfigParser(configXmlPath);
+            plugins = cfg2.getPluginIdList();
+            pluginNames = plugins.map(function(elem) {
+                return elem;
+            });
+            configPlugArray = pluginNames.slice();
+            // Expect that pkg.json exists with 2 plugins
+            expect(pkgJson.cordova.plugins).toBeDefined();
+            expect(pkgJson.cordova.plugins.length === 1);
+            // Expect that config.xml exists with 2 plugins
+            expect(configPlugArray).toBeDefined();
+            expect(configPlugArray.length === 1);
+            // Expect config to contain camera and splashscreen
+            expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
+            expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+            // Expect pkg.json to contain camera and splashscreen
+            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(0);
+            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
+
+// Use basePkgJson7 as config.xml contains 2 plugins and pkg.json contains 1 plugin
+describe('update pkg.json to include plugins found in config.xml', function () {
+    var tmpDir = helpers.tmpDir('platform_test_pkgjson');
+    var project = path.join(tmpDir, 'project');
+    var results;
+
+    beforeEach(function() {
+        shell.rm('-rf', tmpDir);
+        // Copy then move because we need to copy everything, but that means it will copy the whole directory.
+        // Using /* doesn't work because of hidden files.
+        shell.cp('-R', path.join(__dirname, 'fixtures', 'basePkgJson7'), tmpDir);
+        shell.mv(path.join(tmpDir, 'basePkgJson7'), project);
+        process.chdir(project);
+        events.on('results', function(res) { results = res; });
+    });
+
+    afterEach(function() {
+        var cwd = process.cwd();
+        delete require.cache[require.resolve(path.join(process.cwd(),'package.json'))];
+        process.chdir(path.join(__dirname, '..'));  // Needed to rm the dir on Windows.
+        shell.rm('-rf', tmpDir);
+    });
+
+    // Factoring out some repeated checks.
+    function emptyPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBe(-1);
+        });
+    }
+
+    function fullPlatformList() {
+        return cordova.raw.platform('list').then(function() {
+            var installed = results.match(/Installed platforms:\n  (.*)/);
+            expect(installed).toBeDefined();
+            expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
+        });
+    }
+
+    /** Test#009 will check the platform list in package.json and config.xml. 
+    *   When config.xml has 'splashscreen and camera' and pkg.json only contains 'splashscreen', run cordova prepare
+    *   and pkg.json is updated to include 'camera'. After both files are identical, run cordova prepare
+    *   to double check that neither file is modified once they are the same.
+    */
+    it('Test#009 :  if config.xml has 2 plugins and pkg.json has only one of those plugins, update pkg.json to include both', function(done) {
+        var cwd = process.cwd();
+        var configXmlPath = path.join(cwd, 'config.xml');
+        var pkgJsonPath = path.join(cwd,'package.json');
+        delete require.cache[require.resolve(pkgJsonPath)];
+        var cfg1 = new ConfigParser(configXmlPath);
+        var plugins = cfg1.getPluginIdList();
+        var pkgJson = require(pkgJsonPath);
+        var pluginNames = plugins.map(function(elem) {
+            return elem;
+        });
+        var configPlugArray = pluginNames.slice();
+
+        // Expect that pkg.json exists with 1 plugin
+        expect(pkgJson.cordova.plugins).toBeDefined();
+        expect(pkgJson.cordova.plugins.length === 0);
+        expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(-1);
+        expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(0);
+        // Expect that config.xml exists with 2 plugins
+        expect(configPlugArray).toBeDefined();
+        expect(configPlugArray.length === 1);
+        expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
+        expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+
+        emptyPlatformList().then(function() {
+            // Run cordova prepare
+            return cordova.raw.prepare();
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            var cfg2 = new ConfigParser(configXmlPath);
+            plugins = cfg2.getPluginIdList();
+            pluginNames = plugins.map(function(elem) {
+                return elem;
+            });
+            configPlugArray = pluginNames.slice();
+            // Expect that pkg.json exists with 2 plugins
+            expect(pkgJson.cordova.plugins).toBeDefined();
+            expect(pkgJson.cordova.plugins.length === 1);
+            // Expect that config.xml exists with 2 plugins
+            expect(configPlugArray).toBeDefined();
+            expect(configPlugArray.length === 1);
+            // Expect config to contain camera and splashscreen
+            expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
+            expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+            // Expect pkg.json to contain camera and splashscreen
+            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(0);
+            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(1);
+        }).then(function() {
+            // Now that files are the same, run cordova prepare again to check that neither 
+            // file gets modified again since both are identical now
+            return cordova.raw.prepare();
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            var cfg2 = new ConfigParser(configXmlPath);
+            plugins = cfg2.getPluginIdList();
+            pluginNames = plugins.map(function(elem) {
+                return elem;
+            });
+            configPlugArray = pluginNames.slice();
+            // Expect that pkg.json exists with 2 plugins
+            expect(pkgJson.cordova.plugins).toBeDefined();
+            expect(pkgJson.cordova.plugins.length === 1);
+            // Expect that config.xml exists with 2 plugins
+            expect(configPlugArray).toBeDefined();
+            expect(configPlugArray.length === 1);
+            // Expect config to contain camera and splashscreen
+            expect(configPlugArray.indexOf('cordova-plugin-camera')).toEqual(0);
+            expect(configPlugArray.indexOf('cordova-plugin-splashscreen')).toEqual(1);
+            // Expect pkg.json to contain camera and splashscreen
+            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-splashscreen')).toEqual(0);
+            expect(pkgJson.cordova.plugins.indexOf('cordova-plugin-camera')).toEqual(1);
+        }).fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    // Cordova prepare needs extra wait time to complete.
+    },30000);
+});
 
 

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/pkgJson.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/pkgJson.spec.js b/cordova-lib/spec-cordova/pkgJson.spec.js
index 4e90a99..4347a0e 100644
--- a/cordova-lib/spec-cordova/pkgJson.spec.js
+++ b/cordova-lib/spec-cordova/pkgJson.spec.js
@@ -313,7 +313,7 @@ describe('platform end-to-end with --save', function () {
             delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
             // Platform list should be empty and helpers.testPlatform should NOT have been added.
-            expect(pkgJson.cordova.platforms.indexOf(helpers.testPlatform)).toEqual(-1);
+            expect(pkgJson.cordova).toBeUndefined();
         }).then(fullPlatformList)
         .fail(function(err) {
             expect(err).toBeUndefined();
@@ -347,5 +347,41 @@ describe('platform end-to-end with --save', function () {
         })
         .fin(done);
     });
+    it('Test#010 : two platforms are added and removed correctly with --save --fetch', function(done) {
+        var pkgJsonPath = path.join(process.cwd(),'package.json');
+        expect(pkgJsonPath).toExist();
+        var pkgJson;
+
+        // Check there are no platforms yet.
+        emptyPlatformList().then(function() {
+            // Add the testing platform with --save.
+            return cordova.raw.platform('add', ['android', 'ios'], {'save':true, 'fetch':true});
+        }).then(function() {
+            // Check the platform add was successful.
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            expect(pkgJson.cordova.platforms).toBeDefined();
+            expect(pkgJson.cordova.platforms.indexOf('android')).toEqual(0);
+            expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(1);
+            expect(pkgJson.dependencies['cordova-android']).toBeDefined();
+            expect(pkgJson.dependencies['cordova-ios']).toBeDefined();
+        }).then(fullPlatformList) // Platform should still be in platform ls.
+        .then(function() {
+            // And now remove it with --save.
+            return cordova.raw.platform('rm', ['android', 'ios'], {'save':true, 'fetch':true});
+        }).then(function() {
+            // Delete any previous caches of require(package.json)
+            delete require.cache[require.resolve(pkgJsonPath)];
+            pkgJson = require(pkgJsonPath);
+            // Checking that the platform removed is in not in the platforms key
+            expect(pkgJson.cordova.platforms.indexOf('android')).toEqual(-1);
+            expect(pkgJson.cordova.platforms.indexOf('ios')).toEqual(-1);
+            expect(pkgJson.dependencies['cordova-android']).toBeUndefined();
+            expect(pkgJson.dependencies['cordova-ios']).toBeUndefined();
+        }).then(emptyPlatformList) // platform ls should be empty too.
+        .fail(function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    }, 30000);
 });
 

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/spec-cordova/platform.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/platform.spec.js b/cordova-lib/spec-cordova/platform.spec.js
index 4a3b2f1..52b4362 100644
--- a/cordova-lib/spec-cordova/platform.spec.js
+++ b/cordova-lib/spec-cordova/platform.spec.js
@@ -6,9 +6,7 @@
     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
@@ -28,8 +26,6 @@ var helpers = require('./helpers'),
     cordova = require('../src/cordova/cordova'),
     plugman = require('../src/plugman/plugman'),
     rewire = require('rewire'),
-    prepare = require('../src/cordova/prepare'),
-    platforms = require('../src/platforms/platforms'),
     platform = rewire('../src/cordova/platform.js');
 
 var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker';
@@ -94,34 +90,6 @@ describe('platform end-to-end', function () {
             expect(installed[1].indexOf(helpers.testPlatform)).toBeGreaterThan(-1);
         });
     }
-    // Runs: list, add, list
-    function addPlugin(target, id, options) {
-        // Check there are no plugins yet.
-        return cordova.raw.plugin('list').then(function() {
-            expect(results).toMatch(/No plugins added/gi);
-        }).then(function() {
-            // Add a fake plugin from fixtures.
-            return cordova.raw.plugin('add', target, options);
-        }).then(function() {
-            expect(path.join(project, 'plugins', id, 'plugin.xml')).toExist();
-        }).then(function() {
-            return cordova.raw.plugin('ls');
-        }).then(function() {
-            expect(results).toContain(id);
-        });
-    }
-    // Runs: remove, list
-    function removePlugin(id) {
-        return cordova.raw.plugin('rm', id)
-        .then(function() {
-            // The whole dir should be gone.
-            expect(path.join(project, 'plugins', id)).not.toExist();
-        }).then(function() {
-            return cordova.raw.plugin('ls');
-        }).then(function() {
-            expect(results).toMatch(/No plugins added/gi);
-        });
-    }        
 
     // The flows we want to test are add, rm, list, and upgrade.
     // They should run the appropriate hooks.
@@ -134,7 +102,6 @@ describe('platform end-to-end', function () {
             // Add the testing platform.
             return cordova.raw.platform('add', [helpers.testPlatform]);
         }).then(function() {
-            console.log("!!!");
             // Check the platform add was successful.
             expect(path.join(project, 'platforms', helpers.testPlatform)).toExist();
             expect(path.join(project, 'platforms', helpers.testPlatform, 'cordova')).toExist();
@@ -150,6 +117,7 @@ describe('platform end-to-end', function () {
             // And now remove it.
             return cordova.raw.platform('rm', [helpers.testPlatform]);
         }).then(function() {
+            // It should be gone.
             expect(path.join(project, 'platforms', helpers.testPlatform)).not.toExist();
         }).then(emptyPlatformList) // platform ls should be empty too.
         .fail(function(err) {
@@ -385,7 +353,4 @@ describe('plugin add and rm end-to-end --fetch', function () {
         })
         .fin(done);
     }, 60000);
-});
-
-        // Runs: list, add, list
-    
\ No newline at end of file
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/src/cordova/platform.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/platform.js b/cordova-lib/src/cordova/platform.js
index e34b4db..2b82e8a 100644
--- a/cordova-lib/src/cordova/platform.js
+++ b/cordova-lib/src/cordova/platform.js
@@ -83,16 +83,11 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
 
     return hooksRunner.fire('before_platform_' + cmd, opts)
     .then(function() {
-        var modifiedPkgJson = false;
-        var pkgJson;
-        var pkgJsonPath = path.join(projectRoot, 'package.json');
-        // If statement to see if pkgJsonPath exists in the filesystem
-        if(fs.existsSync(pkgJsonPath)) {
-            pkgJson = require(pkgJsonPath);
-        } else {
-            // Create package.json in cordova@7
-        }
+        
+        var platformsToSave = []; 
 
+        // If statement to see if pkgJsonPath exists in the filesystem
+        
         return promiseutil.Q_chainmap(targets, function(target) {
             // For each platform, download it and call its helper script.
             var parts = target.split('@');
@@ -130,6 +125,7 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                 }
                 return downloadPlatform(projectRoot, platform, spec, opts);
             }).then(function(platDetails) {
+                delete require.cache[require.resolve(path.join(projectRoot, 'package.json'))];
                 platform = platDetails.platform;
                 var platformPath = path.join(projectRoot, 'platforms', platform);
                 var platformAlreadyAdded = fs.existsSync(platformPath);
@@ -226,7 +222,6 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                 .then(function() {
                     var saveVersion = !spec || semver.validRange(spec, true);
 
-
                     // Save platform@spec into platforms.json, where 'spec' is a version or a soure location. If a
                     // source location was specified, we always save that. Otherwise we save the version that was
                     // actually installed.
@@ -245,30 +240,44 @@ function addHelper(cmd, hooksRunner, projectRoot, targets, opts) {
                         cfg.removeEngine(platform);
                         cfg.addEngine(platform, spec);
                         cfg.write();
-
-                        // If package.json exists, cordova key and platforms will be added to package.json if not
-                        // already there 
-                        if (pkgJson === undefined) {
-                            return;
-                        }
-                        if (pkgJson.cordova === undefined) {
-                            pkgJson.cordova = {};
-                        }
-                        if (pkgJson.cordova.platforms === undefined){
-                            pkgJson.cordova.platforms = [];
-                        }
-                        if (pkgJson.cordova.platforms.indexOf(platform) === -1) {
-                            events.emit('log','adding '+platform+' to cordova.platforms array in package.json');
-                            pkgJson.cordova.platforms.push(platform);
-                            modifiedPkgJson = true;
-                        } else {
-                            events.emit('verbose', platform + ' already added to platforms key');
-                        }
+                        
+                        //save to add to pacakge.json's cordova.platforms array in the next then
+                        platformsToSave.push(platform);
                     }
                 });
             });
-        }).then(function(){
+        }).then(function() {
+            var pkgJson;
+            var pkgJsonPath = path.join(projectRoot, 'package.json');
+            var modifiedPkgJson = false;
+
+            if(fs.existsSync(pkgJsonPath)) {
+                delete require.cache[require.resolve(pkgJsonPath)]; 
+                pkgJson = require(pkgJsonPath);
+            } else {
+                // Create package.json in cordova@7
+            }
+            if (pkgJson === undefined) {
+                return;
+            }
+            if (pkgJson.cordova === undefined) {
+                pkgJson.cordova = {};
+            }
+            if (pkgJson.cordova.platforms === undefined && platformsToSave.length) {
+                pkgJson.cordova.platforms = platformsToSave;
+                modifiedPkgJson = true;
+            } else {
+                platformsToSave.forEach(function(plat){
+                    if(pkgJson.cordova.platforms.indexOf(plat) === -1) {
+                        events.emit('verbose','adding '+plat+' to cordova.platforms array in package.json');
+                        pkgJson.cordova.platforms.push(plat);
+                        modifiedPkgJson = true;
+                    } 
+                });
+            }
+            //save to package.json
             if (modifiedPkgJson === true) {
+                pkgJson.cordova.platforms = pkgJson.cordova.platforms.sort();
                 fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
             }
         });
@@ -417,6 +426,7 @@ function remove(hooksRunner, projectRoot, targets, opts) {
         var pkgJsonPath = path.join(projectRoot,'package.json');
         // If statement to see if pkgJsonPath exists in the filesystem
         if(fs.existsSync(pkgJsonPath)) {
+            delete require.cache[require.resolve(pkgJsonPath)];
             pkgJson = require(pkgJsonPath);
         }
         if(opts.save || autosave){
@@ -452,7 +462,7 @@ function remove(hooksRunner, projectRoot, targets, opts) {
     }).then(function() {
         //Remove from node_modules if it exists and --fetch was used
         if(opts.fetch) {
-            targets.forEach(function(target) {
+            return promiseutil.Q_chainmap(targets, function(target) {
                 if(target in platforms) {
                     target = 'cordova-'+target;
                 }

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/4cd1be81/cordova-lib/src/cordova/restore-util.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/restore-util.js b/cordova-lib/src/cordova/restore-util.js
index 2ae8ec3..692c374 100644
--- a/cordova-lib/src/cordova/restore-util.js
+++ b/cordova-lib/src/cordova/restore-util.js
@@ -54,7 +54,6 @@ function installPlatformsFromConfigXML(platforms, opts) {
     var modifiedConfigXML = false;
     var modifiedPkgJsonPlugin = false;
     var modifiedConfigXMLPlugin = false;
-
     var targetPlatformsArray = [];
 
     if(fs.existsSync(pkgJsonPath)) {
@@ -225,7 +224,6 @@ function installPlatformsFromConfigXML(platforms, opts) {
 //returns a Promise
 function installPluginsFromConfigXML(args) {
     events.emit('verbose', 'Checking config.xml for saved plugins that haven\'t been added to the project');
-
     //Install plugins that are listed on config.xml
     var projectRoot = cordova_util.cdProjectRoot();
     var configPath = cordova_util.projectConfig(projectRoot);
@@ -233,14 +231,12 @@ function installPluginsFromConfigXML(args) {
     var plugins_dir = path.join(projectRoot, 'plugins');
     var pkgJsonPath = path.join(projectRoot,'package.json');
     var pkgJson;
-
     var pkgJsonArray = [];
     var comboPluginArray;
 
     // Get all configured plugins
-
-    var plugins = cfg.getPluginIdList();
-    if (0 === plugins.length) {
+    var pluginIdConfig = cfg.getPluginIdList();
+    if (0 === pluginIdConfig.length) {
         return Q('No plugins found in config.xml that haven\'t been added to the project');
     }
     // Check if path exists and require pkgJsonPath
@@ -250,78 +246,96 @@ function installPluginsFromConfigXML(args) {
     if (pkgJson.cordova === undefined) {
         pkgJson.cordova = {};
     }
-    // (In pkg.json), if there is a platforms array and not plugins array, create a new plugins array 
+    // (In pkg.json), if there is a platforms array and not plugins array, create a new pluginIdConfig array 
     // and add all plugins from config.xml to the new plugins array.
     if (cfg !== undefined && pkgJson !== undefined && pkgJson.cordova.platforms !== undefined && 
     pkgJson.cordova.plugins === undefined) {
         pkgJson.cordova.plugins = {};
-        plugins.forEach(function(foo) {
+        pluginIdConfig.forEach(function(foo) {
             pkgJson.cordova.plugins[foo] = {}
-        })
+        });
         fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
     }
-    // Created, but don't think this is necessary because there has to be at least one platform
-    // added for restore to run.
     // (In pkg.json), if there is no platforms array and no plugins array, create a new plugins array
     // and add all plugins from config.xml to the new plugins array.
     if (cfg !== undefined && pkgJson !== undefined && pkgJson.cordova.platforms === undefined && 
     pkgJson.cordova.plugins === undefined) {
         pkgJson.cordova.platforms = [];
         pkgJson.cordova.plugins = {};
-        plugins.forEach(function(foo) {
+        pluginIdConfig.forEach(function(foo) {
             pkgJson.cordova.plugins[foo] = {}
-        })
+        });
         fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
     }
 
     pkgJsonArray = Object.keys(pkgJson.cordova.plugins);
 
-    var pluginNames = plugins.map(function(elem) {
+    var pluginNames = pluginIdConfig.map(function(elem) {
         return elem.name;
     });
 
+    // Create a merged plugin data array (mergedPluginDataObj)
+    // and add all of the package.json plugins to mergedPluginDataObj
+    var mergedPluginDataObj = pkgJson.cordova.plugins;
+    var mergedPluginDataArray = pkgJsonArray;
+
+    // Check to see which plugins are initially the same in pkg.json and config.xml
+    // Merge identical plugins and their variables together first
+    for (var i = 0; i < mergedPluginDataArray.length; i++) {
+        if(pluginIdConfig.includes(mergedPluginDataArray[i])) {
+            var configPlugin = cfg.getPlugin(mergedPluginDataArray[i]);
+            var configPluginVariables = configPlugin.variables;
+            var pkgJsonPluginVariables = mergedPluginDataObj[mergedPluginDataArray[i]];
+            for(var key in configPluginVariables) {
+                // Handle conflicts, package.json wins
+                if(pkgJsonPluginVariables[key] === undefined) {
+                    pkgJsonPluginVariables[key] = configPluginVariables[key];
+                    mergedPluginDataObj[mergedPluginDataArray[i]][key] = configPluginVariables[key];
+                }
+            }
+        }
+    }
+    // Check to see if pkg.json plugin(id) and config plugin(id) match
+    if(mergedPluginDataArray.sort().toString() !== pluginIdConfig.sort().toString()) {
+        // If there is a config plugin that does NOT already exist in
+        // mergedPluginDataArray, add it and its variables
+        pluginIdConfig.forEach(function(item) {
+            if(mergedPluginDataArray.indexOf(item) < 0) {
+                mergedPluginDataArray.push(item);
+                var configXMLPlugin = cfg.getPlugin(item);
+                mergedPluginDataObj[item] = configXMLPlugin.variables;
+            }
+        });
+    }
+    // Write to pkg.Json
+    // TODO: toString comparision to see if they are the same and then write
+    pkgJson.cordova.plugins = mergedPluginDataObj;
+    fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
+
+    // Write config.xml
+    mergedPluginDataArray.forEach(function(plugID) {
+        cfg.removePlugin(plugID);
+        cfg.addPlugin({name:plugID}, mergedPluginDataObj[plugID]); 
+    });
+    cfg.write();
+
     // If config.xml and pkg.json exist and both already contain plugins, run cordova prepare
     // to check if these plugins are identical.
     if(cfg !== undefined && pkgJson.cordova.plugins !== undefined) {
-        if(pkgJsonArray.toString() === plugins.toString() && pkgJsonArray.length === plugins.length) {
+        if(pkgJsonArray.toString() === pluginIdConfig.toString() && pkgJsonArray.length === pluginIdConfig.length) {
             events.emit('warn', 'Config.xml and pkgJson plugins are the same. No pkg.json or config.xml modification.');
         }
 
-        if(pkgJsonArray.toString() !== plugins.toString() || pkgJsonArray.length !== plugins.length) {
+        if(pkgJsonArray.toString() !== pluginIdConfig.toString() || pkgJsonArray.length !== pluginIdConfig.length) {
            events.emit('warn', 'Config.xml and pkgJson plugins are different.');
             // Combining arrays and checking duplicates
 
             comboPluginArray = pkgJsonArray.slice();
-            plugins.forEach(function(item) {
+            pluginIdConfig.forEach(function(item) {
                 if(comboPluginArray.indexOf(item) < 0) {
                     comboPluginArray.push(item);
                 }
             });
-
-            // Update pkg.json if it's missing any plugins based on plugin.id 
-            if(comboPluginArray.sort().toString() !== pkgJsonArray.sort().toString() && pkgJsonArray.length > 0) {
-                events.emit('warn', 'Config.xml and package.json plugins are not the same. Updating package.json with most current list of plugins.');
-                comboPluginArray.forEach(function(item) {
-                    if(pkgJsonArray.indexOf(item) < 0) {
-                        pkgJsonArray.push(item);
-                        var pluginVar = cfg.getPlugin(item);
-                        var variables = pluginVar.variables;
-                        pkgJson.cordova.plugins[item] = variables;
-                    }
-                });
-                fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 4), 'utf8');
-            }
-            // Update pkg.json if it's missing any plugins based on plugin.id 
-            if(comboPluginArray.toString() !== plugins.toString()) {
-                events.emit('warn', 'Config.xml and package.json plugins are not the same. Updating config.xml with most current list of plugins.');
-                comboPluginArray.forEach(function(item) {
-                    if(plugins.indexOf(item) < 0) {
-                        plugins.push(item); 
-                        cfg.addPlugin({name:item}, pkgJson.cordova.plugins[item]);
-                        cfg.write();
-                    }
-                });
-            }
         }
     }
     // Intermediate variable to store current installing plugin name
@@ -332,7 +346,7 @@ function installPluginsFromConfigXML(args) {
     // We need to wait for the plugin and its dependencies to be installed
     // before installing the next root plugin otherwise we can have common
     // plugin dependencies installed twice which throws a nasty error.
-    return promiseutil.Q_chainmap_graceful(plugins, function(featureId) {
+    return promiseutil.Q_chainmap_graceful(pluginIdConfig, function(featureId) {
         var pluginPath = path.join(plugins_dir, featureId);
         if (fs.existsSync(pluginPath)) {
             // Plugin already exists


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