You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by sg...@apache.org on 2015/09/20 14:18:07 UTC

[08/10] cordova-lib git commit: CB-9598 Adds tests and fixtures based on existing cordova-lib ones

CB-9598 Adds tests and fixtures based on existing cordova-lib ones


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

Branch: refs/heads/master
Commit: 59042baebaf8bee0763339dfe61ad538960b0897
Parents: 28ce0d1
Author: Vladimir Kotikov <v-...@microsoft.com>
Authored: Wed Sep 16 16:39:41 2015 +0300
Committer: sgrebnov <v-...@microsoft.com>
Committed: Sun Sep 20 15:14:54 2015 +0300

----------------------------------------------------------------------
 cordova-common/spec/ActionStack.spec.js         |   77 +
 .../spec/ConfigChanges/ConfigChanges.spec.js    |  445 +
 cordova-common/spec/ConfigParser.spec.js        |  224 -
 .../spec/ConfigParser/ConfigParser.spec.js      |  224 +
 .../spec/PluginInfo/PluginInfo.spec.js          |   50 +
 .../spec/PluginInfo/PluginInfoProvider.spec.js  |   33 +
 .../fixtures/plugins/ChildBrowser/plugin.xml    |  126 +
 .../ChildBrowser/src/android/ChildBrowser.java  |   19 +
 .../plugins/ChildBrowser/www/childbrowser.js    |   19 +
 .../ChildBrowser/www/childbrowser/image.jpg     |    1 +
 .../ChildBrowser/www/childbrowser_file.html     |    1 +
 .../fixtures/plugins/com.adobe.vars/plugin.xml  |   59 +
 .../plugins/org.apache.plist/plugin.xml         |   54 +
 .../plugins/org.test.configtest/plugin.xml      |   61 +
 .../org.test.multiple-children/plugin.xml       |  108 +
 .../org.test.plugins.childbrowser/plugin.xml    |  124 +
 .../src/android/ChildBrowser.java               |    1 +
 .../src/ios/ChildBrowser.bundle/arrow_left.png  |    1 +
 .../ios/ChildBrowser.bundle/arrow_left@2x.png   |    1 +
 .../src/ios/ChildBrowser.bundle/arrow_right.png |    1 +
 .../ios/ChildBrowser.bundle/arrow_right@2x.png  |    1 +
 .../src/ios/ChildBrowser.bundle/but_refresh.png |    1 +
 .../ios/ChildBrowser.bundle/but_refresh@2x.png  |    1 +
 .../src/ios/ChildBrowser.bundle/compass.png     |    1 +
 .../src/ios/ChildBrowser.bundle/compass@2x.png  |    1 +
 .../src/ios/ChildBrowserCommand.h               |    1 +
 .../src/ios/ChildBrowserCommand.m               |    1 +
 .../src/ios/ChildBrowserViewController.h        |    1 +
 .../src/ios/ChildBrowserViewController.m        |    1 +
 .../src/ios/ChildBrowserViewController.xib      |    1 +
 .../src/ios/TargetDirTest.h                     |    1 +
 .../src/ios/TargetDirTest.m                     |    1 +
 .../src/ios/preserveDirs/PreserveDirsTest.h     |    1 +
 .../src/ios/preserveDirs/PreserveDirsTest.m     |    1 +
 .../www/childbrowser.js                         |    1 +
 .../www/childbrowser/image.jpg                  |    1 +
 .../www/childbrowser_file.html                  |    1 +
 .../android-resource.xml                        |    1 +
 .../org.test.plugins.dummyplugin/extra.gradle   |    1 +
 .../plugin-lib/AndroidManifest.xml              |    5 +
 .../plugin-lib/libFile                          |    1 +
 .../plugin-lib/project.properties               |    1 +
 .../org.test.plugins.dummyplugin/plugin.xml     |  207 +
 .../src/android/DummyPlugin.java                |    1 +
 .../src/blackberry10/index.js                   |    1 +
 .../src/ios/Custom.framework/someFheader.h      |    1 +
 .../src/ios/Custom.framework/somebinlib         |    1 +
 .../src/ios/DummyPlugin.bundle                  |    1 +
 .../src/ios/DummyPluginCommand.h                |    1 +
 .../src/ios/DummyPluginCommand.m                |    1 +
 .../src/ios/SourceWithFramework.m               |    1 +
 .../src/ios/TargetDirTest.h                     |    1 +
 .../src/ios/TargetDirTest.m                     |    1 +
 .../src/ios/libsqlite3.dylib                    |    1 +
 .../src/tizen/dummer.js                         |    1 +
 .../src/windows/dummer.js                       |    1 +
 .../src/windows/dummy1.dll                      |    0
 .../src/windows/dummy1.vcxproj                  |    7 +
 .../src/windows/dummy2.dll                      |    0
 .../src/windows/dummy2.vcxproj                  |    7 +
 .../src/windows/dummy3.dll                      |    0
 .../src/windows/dummy3.vcxproj                  |    7 +
 .../src/windows/dummy4.dll                      |    0
 .../src/windows/dummy4.vcxproj                  |    7 +
 .../src/wp7/DummyPlugin.cs                      |    1 +
 .../src/wp8/DummyPlugin.cs                      |    1 +
 .../www/dummyplugin.js                          |    1 +
 .../www/dummyplugin/image.jpg                   |    1 +
 .../plugins/org.test.shareddeps/plugin.xml      |   34 +
 .../projects/android/AndroidManifest.xml        |   69 +
 .../projects/android/assets/www/.gitkeep        |    0
 .../projects/android/res/xml/config.xml         |   54 +
 .../spec/fixtures/projects/android/src/.gitkeep |    0
 .../projects/android_two/AndroidManifest.xml    |   69 +
 .../projects/android_two/assets/www/.gitkeep    |    0
 .../projects/android_two/res/xml/config.xml     |   54 +
 .../fixtures/projects/android_two/src/.gitkeep  |    0
 .../android_two_no_perms/AndroidManifest.xml    |   49 +
 .../android_two_no_perms/assets/www/.gitkeep    |    0
 .../android_two_no_perms/res/xml/config.xml     |   54 +
 .../projects/android_two_no_perms/src/.gitkeep  |    0
 .../CordovaLib.xcodeproj/project.pbxproj        |  636 ++
 .../projects/ios-config-xml/CordovaLib/VERSION  |    1 +
 .../SampleApp.xcodeproj/project.orig.pbxproj    |  498 ++
 .../SampleApp.xcodeproj/project.pbxproj         |  496 ++
 .../SampleApp/SampleApp-Info.plist              |   82 +
 .../ios-config-xml/SampleApp/config.xml         |   59 +
 .../projects/ios-config-xml/www/.gitkeep        |    0
 .../spec/fixtures/projects/windows/bom_test.xml |   24 +
 .../windows8/CordovaApp_TemporaryKey.pfx        |  Bin 0 -> 2504 bytes
 .../fixtures/projects/windows8/TestApp.jsproj   |   81 +
 .../spec/fixtures/projects/windows8/TestApp.sln |   46 +
 .../projects/windows8/package.appxmanifest      |   27 +
 .../projects/windows8/www/cordova-2.6.0.js      | 8075 ++++++++++++++++++
 .../projects/windows8/www/css/index.css         |  115 +
 .../fixtures/projects/windows8/www/img/logo.png |  Bin 0 -> 11600 bytes
 .../projects/windows8/www/img/smalllogo.png     |  Bin 0 -> 2831 bytes
 .../projects/windows8/www/img/splashscreen.png  |  Bin 0 -> 24855 bytes
 .../projects/windows8/www/img/storelogo.png     |  Bin 0 -> 4052 bytes
 .../fixtures/projects/windows8/www/index.html   |   42 +
 .../fixtures/projects/windows8/www/js/index.js  |   49 +
 cordova-common/spec/fixtures/test-config.xml    |   43 +
 cordova-common/spec/util/xml-helpers.spec.js    |  271 +
 103 files changed, 12611 insertions(+), 224 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/ActionStack.spec.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/ActionStack.spec.js b/cordova-common/spec/ActionStack.spec.js
new file mode 100644
index 0000000..5fb6819
--- /dev/null
+++ b/cordova-common/spec/ActionStack.spec.js
@@ -0,0 +1,77 @@
+/**
+    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 path = require('path');
+var action_stack = require('../src/ActionStack');
+var android_one_project = path.join(__dirname, '..', 'projects', 'android_one');
+
+describe('action-stack', function() {
+    var stack;
+    beforeEach(function() {
+        stack = new action_stack();
+    });
+    describe('processing of actions', function() {
+        it('should process actions one at a time until all are done', function() {
+            var first_spy = jasmine.createSpy();
+            var first_args = [1];
+            var second_spy = jasmine.createSpy();
+            var second_args = [2];
+            var third_spy = jasmine.createSpy();
+            var third_args = [3];
+            stack.push(stack.createAction(first_spy, first_args, function(){}, []));
+            stack.push(stack.createAction(second_spy, second_args, function(){}, []));
+            stack.push(stack.createAction(third_spy, third_args, function(){}, []));
+            stack.process('android', android_one_project);
+            expect(first_spy).toHaveBeenCalledWith(first_args[0]);
+            expect(second_spy).toHaveBeenCalledWith(second_args[0]);
+            expect(third_spy).toHaveBeenCalledWith(third_args[0]);
+        });
+        it('should revert processed actions if an exception occurs', function() {
+            spyOn(console, 'log');
+            var first_spy = jasmine.createSpy();
+            var first_args = [1];
+            var first_reverter = jasmine.createSpy();
+            var first_reverter_args = [true];
+            var process_err = new Error('process_err');
+            var second_spy = jasmine.createSpy().andCallFake(function() {
+                throw process_err;
+            });
+            var second_args = [2];
+            var third_spy = jasmine.createSpy();
+            var third_args = [3];
+            stack.push(stack.createAction(first_spy, first_args, first_reverter, first_reverter_args));
+            stack.push(stack.createAction(second_spy, second_args, function(){}, []));
+            stack.push(stack.createAction(third_spy, third_args, function(){}, []));
+            // process should throw
+            var error;
+            runs(function() {
+                stack.process('android', android_one_project).fail(function(err) { error = err; });
+            });
+            waitsFor(function(){ return error; }, 'process promise never resolved', 500);
+            runs(function() {
+                expect(error).toEqual(process_err);
+                // first two actions should have been called, but not the third
+                expect(first_spy).toHaveBeenCalledWith(first_args[0]);
+                expect(second_spy).toHaveBeenCalledWith(second_args[0]);
+                expect(third_spy).not.toHaveBeenCalledWith(third_args[0]);
+                // first reverter should have been called after second action exploded
+                expect(first_reverter).toHaveBeenCalledWith(first_reverter_args[0]);
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/ConfigChanges/ConfigChanges.spec.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/ConfigChanges/ConfigChanges.spec.js b/cordova-common/spec/ConfigChanges/ConfigChanges.spec.js
new file mode 100644
index 0000000..2c854c6
--- /dev/null
+++ b/cordova-common/spec/ConfigChanges/ConfigChanges.spec.js
@@ -0,0 +1,445 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+/* jshint sub:true */
+
+var configChanges = require('../../src/ConfigChanges/ConfigChanges'),
+    xml_helpers = require('../../src/util/xml-helpers'),
+    fs      = require('fs'),
+    os      = require('osenv'),
+    et      = require('elementtree'),
+    path    = require('path'),
+    shell   = require('shelljs'),
+    xcode = require('xcode'),
+    temp    = path.join(os.tmpdir(), 'plugman'),
+    dummyplugin = path.join(__dirname, '../fixtures/plugins/org.test.plugins.dummyplugin'),
+    cbplugin = path.join(__dirname, '../fixtures/plugins/org.test.plugins.childbrowser'),
+    childrenplugin = path.join(__dirname, '../fixtures/plugins/org.test.multiple-children'),
+    shareddepsplugin = path.join(__dirname, '../fixtures/plugins/org.test.shareddeps'),
+    configplugin = path.join(__dirname, '../fixtures/plugins/org.test.configtest'),
+    varplugin = path.join(__dirname, '../fixtures/plugins/com.adobe.vars'),
+    plistplugin = path.join(__dirname, '../fixtures/plugins/org.apache.plist'),
+    android_two_project = path.join(__dirname, '../fixtures/projects/android_two/*'),
+    android_two_no_perms_project = path.join(__dirname, '../fixtures/projects/android_two_no_perms', '*'),
+    ios_config_xml = path.join(__dirname, '../fixtures/projects/ios-config-xml/*'),
+    windows_testapp_jsproj = path.join(__dirname, '../fixtures/projects/windows8/TestApp.jsproj'),
+    plugins_dir = path.join(temp, 'cordova', 'plugins');
+var mungeutil = require('../../src/ConfigChanges/munge-util');
+var PlatformJson = require('../../src/PlatformJson');
+var PluginInfoProvider = require('../../src/PluginInfo/PluginInfoProvider');
+var PluginInfo = require('../../src/PluginInfo/PluginInfo');
+
+// TODO: dont do fs so much
+
+var pluginInfoProvider = new PluginInfoProvider();
+
+function innerXML(xmltext) {
+    return xmltext.replace(/^<[\w\s\-=\/"\.]+>/, '').replace(/<\/[\w\s\-=\/"\.]+>$/,'');
+}
+
+function get_munge_change(munge, keys) {
+    return mungeutil.deep_find.apply(null, arguments);
+}
+
+describe('config-changes module', function() {
+    beforeEach(function() {
+        shell.mkdir('-p', temp);
+        shell.mkdir('-p', plugins_dir);
+    });
+    afterEach(function() {
+        shell.rm('-rf', temp);
+    });
+
+    describe('queue methods', function() {
+        describe('addInstalledPluginToPrepareQueue', function() {
+            it('should append specified plugin to platform.json', function() {
+                var platformJson = new PlatformJson(null, 'android', null);
+                platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {});
+                var json = platformJson.root;
+                expect(json.prepare_queue.installed[0].plugin).toEqual('org.test.plugins.dummyplugin');
+                expect(json.prepare_queue.installed[0].vars).toEqual({});
+            });
+            it('should append specified plugin with any variables to platform.json', function() {
+                var platformJson = new PlatformJson(null, 'android', null);
+                platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {'dude':'man'});
+                var json = platformJson.root;
+                expect(json.prepare_queue.installed[0].plugin).toEqual('org.test.plugins.dummyplugin');
+                expect(json.prepare_queue.installed[0].vars).toEqual({'dude':'man'});
+            });
+        });
+
+        describe('addUninstalledPluginToPrepareQueue', function() {
+            it('should append specified plugin to platform.json', function() {
+                var platformJson = new PlatformJson(null, 'android', null);
+                platformJson.addUninstalledPluginToPrepareQueue('org.test.plugins.dummyplugin');
+                var json = platformJson.root;
+                expect(json.prepare_queue.uninstalled[0].plugin).toEqual('org.test.plugins.dummyplugin');
+            });
+        });
+    });
+
+    describe('load method', function() {
+        it('should return an empty config json object if file doesn\'t exist', function() {
+            var platformJson = PlatformJson.load(plugins_dir, 'android');
+            expect(platformJson.root).toBeDefined();
+            expect(platformJson.root.prepare_queue).toBeDefined();
+            expect(platformJson.root.config_munge).toBeDefined();
+            expect(platformJson.root.installed_plugins).toBeDefined();
+        });
+        it('should return the json file if it exists', function() {
+            var filepath = path.join(plugins_dir, 'android.json');
+            var json = {
+                prepare_queue: {installed: [], uninstalled: []},
+                config_munge: {files: {'some_file': {parents: {'some_parent': [{'xml': 'some_change', 'count': 1}]}}}},
+                installed_plugins: {},
+                dependent_plugins: {}};
+            fs.writeFileSync(filepath, JSON.stringify(json), 'utf-8');
+            var platformJson = PlatformJson.load(plugins_dir, 'android');
+            expect(JSON.stringify(json)).toEqual(JSON.stringify(platformJson.root));
+        });
+    });
+
+    describe('save method', function() {
+        it('should write out specified json', function() {
+            var filepath = path.join(plugins_dir, 'android.json');
+            var platformJson = new PlatformJson(filepath, 'android', {foo:true});
+            platformJson.save();
+            expect(JSON.parse(fs.readFileSync(filepath, 'utf-8'))).toEqual(platformJson.root);
+        });
+    });
+
+    describe('generate_plugin_config_munge method', function() {
+        describe('for android projects', function() {
+            beforeEach(function() {
+                shell.cp('-rf', android_two_project, temp);
+            });
+            it('should return a flat config hierarchy for simple, one-off config changes', function() {
+                var xml;
+                var dummy_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(dummyplugin, 'plugin.xml'), 'utf-8')));
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider);
+                var munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(dummyplugin), {});
+                expect(munge.files['AndroidManifest.xml']).toBeDefined();
+                expect(munge.files['AndroidManifest.xml'].parents['/manifest/application']).toBeDefined();
+                xml = (new et.ElementTree(dummy_xml.find('./platform[@name="android"]/config-file[@target="AndroidManifest.xml"]'))).write({xml_declaration:false});
+                xml = innerXML(xml);
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest/application', xml).count).toEqual(1);
+                expect(munge.files['res/xml/plugins.xml']).toBeDefined();
+                expect(munge.files['res/xml/plugins.xml'].parents['/plugins']).toBeDefined();
+                xml = (new et.ElementTree(dummy_xml.find('./platform[@name="android"]/config-file[@target="res/xml/plugins.xml"]'))).write({xml_declaration:false});
+                xml = innerXML(xml);
+                expect(get_munge_change(munge, 'res/xml/plugins.xml', '/plugins', xml).count).toEqual(1);
+                expect(munge.files['res/xml/config.xml']).toBeDefined();
+                expect(munge.files['res/xml/config.xml'].parents['/cordova/plugins']).toBeDefined();
+                xml = (new et.ElementTree(dummy_xml.find('./platform[@name="android"]/config-file[@target="res/xml/config.xml"]'))).write({xml_declaration:false});
+                xml = innerXML(xml);
+                expect(get_munge_change(munge, 'res/xml/config.xml', '/cordova/plugins', xml).count).toEqual(1);
+            });
+            it('should split out multiple children of config-file elements into individual leaves', function() {
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider);
+                var munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(childrenplugin), {PACKAGE_NAME: 'com.alunny.childapp'});
+                expect(munge.files['AndroidManifest.xml']).toBeDefined();
+                expect(munge.files['AndroidManifest.xml'].parents['/manifest']).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />')).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />')).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.READ_PHONE_STATE" />')).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.INTERNET" />')).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.GET_ACCOUNTS" />')).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="android.permission.WAKE_LOCK" />')).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<permission android:name="com.alunny.childapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />')).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="com.alunny.childapp.permission.C2D_MESSAGE" />')).toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />')).toBeDefined();
+            });
+            it('should not use xml comments as config munge leaves', function() {
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider);
+                var munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(childrenplugin), {});
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<!--library-->')).not.toBeDefined();
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<!-- GCM connects to Google Services. -->')).not.toBeDefined();
+            });
+            it('should increment config hierarchy leaves if different config-file elements target the same file + selector + xml', function() {
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider);
+                var munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(configplugin), {});
+                expect(get_munge_change(munge, 'res/xml/config.xml', '/widget', '<poop />').count).toEqual(2);
+            });
+            it('should take into account interpolation variables', function() {
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider);
+                var munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(childrenplugin), {PACKAGE_NAME:'ca.filmaj.plugins'});
+                expect(get_munge_change(munge, 'AndroidManifest.xml', '/manifest', '<uses-permission android:name="ca.filmaj.plugins.permission.C2D_MESSAGE" />')).toBeDefined();
+            });
+            it('should create munges for platform-agnostic config.xml changes', function() {
+                var munger = new configChanges.PlatformMunger('android', temp, 'unused', null, pluginInfoProvider);
+                var munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(dummyplugin), {});
+                expect(get_munge_change(munge, 'config.xml', '/*', '<access origin="build.phonegap.com" />')).toBeDefined();
+                expect(get_munge_change(munge, 'config.xml', '/*', '<access origin="s3.amazonaws.com" />')).toBeDefined();
+            });
+        });
+
+        describe('for ios projects', function() {
+            beforeEach(function() {
+                shell.cp('-rf', ios_config_xml, temp);
+            });
+            it('should special case framework elements for ios', function() {
+                var munger = new configChanges.PlatformMunger('ios', temp, 'unused', null, pluginInfoProvider);
+                var munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(cbplugin), {});
+                expect(munge.files['framework']).toBeDefined();
+                expect(get_munge_change(munge, 'framework', 'libsqlite3.dylib', false)).toBeDefined();
+                expect(get_munge_change(munge, 'framework', 'social.framework', true)).toBeDefined();
+                expect(get_munge_change(munge, 'framework', 'music.framework', false)).toBeDefined();
+                expect(munge.files['framework'].parents['Custom.framework']).not.toBeDefined();
+            });
+        });
+        describe('for windows project', function() {
+            beforeEach(function() {
+                shell.cp('-rf', windows_testapp_jsproj, temp);
+            });
+            it('should special case config-file elements for windows', function() {
+                var munger = new configChanges.PlatformMunger('windows', temp, 'unused', null, pluginInfoProvider);
+
+                var munge = munger.generate_plugin_config_munge(pluginInfoProvider.get(configplugin), {});
+                var packageAppxManifest = munge.files['package.appxmanifest'];
+                var windows80AppxManifest = munge.files['package.windows80.appxmanifest'];
+                var windows81AppxManifest = munge.files['package.windows.appxmanifest'];
+                var winphone81AppxManifest = munge.files['package.phone.appxmanifest'];
+                var windows10AppxManifest = munge.files['package.windows10.appxmanifest'];
+
+                expect(packageAppxManifest.parents['/Parent/Capabilities'][0].xml).toBe('<Capability Note="should-exist-for-all-appxmanifest-target-files" />');
+                expect(windows80AppxManifest.parents['/Parent/Capabilities'][0].xml).toBe('<Capability Note="should-exist-for-win8-only" />');
+                expect(windows80AppxManifest.parents['/Parent/Capabilities'][1].xml).toBe('<Capability Note="should-exist-for-win8-and-win81-win-only" />');
+                expect(windows80AppxManifest.parents['/Parent/Capabilities'][2].xml).toBe('<Capability Note="should-exist-for-win8-and-win81-both" />');
+                expect(windows81AppxManifest.parents['/Parent/Capabilities'][0].xml).toBe('<Capability Note="should-exist-for-win81-win-and-phone" />');
+                expect(windows81AppxManifest.parents['/Parent/Capabilities'][1].xml).toBe('<Capability Note="should-exist-for-win8-and-win81-win-only" />');
+                expect(windows81AppxManifest.parents['/Parent/Capabilities'][2].xml).toBe('<Capability Note="should-exist-for-win8-and-win81-both" />');
+                expect(winphone81AppxManifest.parents['/Parent/Capabilities'][0].xml).toBe('<Capability Note="should-exist-for-win81-win-and-phone" />');
+                expect(winphone81AppxManifest.parents['/Parent/Capabilities'][1].xml).toBe('<Capability Note="should-exist-for-win81-phone-only" />');
+                expect(winphone81AppxManifest.parents['/Parent/Capabilities'][2].xml).toBe('<Capability Note="should-exist-for-win8-and-win81-both" />');
+                expect(windows10AppxManifest.parents['/Parent/Capabilities'][0].xml).toBe('<Capability Note="should-exist-in-win10-only" />');
+            });
+        });
+    });
+
+    describe('processing of plugins (via process method)', function() {
+        beforeEach(function() {
+            shell.cp('-rf', dummyplugin, plugins_dir);
+        });
+        it('should generate config munges for queued plugins', function() {
+            shell.cp('-rf', android_two_project, temp);
+            var platformJson = PlatformJson.load(plugins_dir, 'android');
+            platformJson.root.prepare_queue.installed = [{'plugin':'org.test.plugins.dummyplugin', 'vars':{}}];
+            var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+            var spy = spyOn(munger, 'generate_plugin_config_munge').andReturn({});
+            munger.process(plugins_dir);
+            expect(spy).toHaveBeenCalledWith(jasmine.any(PluginInfo), {});
+        });
+        describe(': installation', function() {
+            describe('of xml config files', function() {
+                beforeEach(function() {
+                    shell.cp('-rf', android_two_project, temp);
+                });
+                it('should call graftXML for every new config munge it introduces (every leaf in config munge that does not exist)', function() {
+                    var platformJson = PlatformJson.load(plugins_dir, 'android');
+                    platformJson.root.prepare_queue.installed = [{'plugin':'org.test.plugins.dummyplugin', 'vars':{}}];
+
+                    var spy = spyOn(xml_helpers, 'graftXML').andReturn(true);
+
+                    var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                    munger.process(plugins_dir);
+                    expect(spy.calls.length).toEqual(4);
+                    expect(spy.argsForCall[0][2]).toEqual('/*');
+                    expect(spy.argsForCall[1][2]).toEqual('/*');
+                    expect(spy.argsForCall[2][2]).toEqual('/manifest/application');
+                    expect(spy.argsForCall[3][2]).toEqual('/cordova/plugins');
+                });
+                it('should not call graftXML for a config munge that already exists from another plugin', function() {
+                    shell.cp('-rf', configplugin, plugins_dir);
+                    var platformJson = PlatformJson.load(plugins_dir, 'android');
+                    platformJson.addInstalledPluginToPrepareQueue('org.test.configtest', {});
+
+                    var spy = spyOn(xml_helpers, 'graftXML').andReturn(true);
+                    var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                    munger.process(plugins_dir);
+                    expect(spy.calls.length).toEqual(1);
+                });
+                it('should not call graftXML for a config munge targeting a config file that does not exist', function() {
+                    var platformJson = PlatformJson.load(plugins_dir, 'android');
+                    platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {});
+
+                    var spy = spyOn(fs, 'readFileSync').andCallThrough();
+
+                    var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                    munger.process(plugins_dir);
+                    expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'), 'utf-8');
+                });
+            });
+            describe('of plist config files', function() {
+                it('should write empty string nodes with no whitespace', function() {
+                    shell.cp('-rf', ios_config_xml, temp);
+                    shell.cp('-rf', varplugin, plugins_dir);
+                    var platformJson = PlatformJson.load(plugins_dir, 'ios');
+                    platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {});
+                    configChanges.process(plugins_dir, temp, 'ios', platformJson, pluginInfoProvider);
+                    expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf-8')).toMatch(/<key>APluginNode<\/key>\n    <string><\/string>/m);
+                });
+                it('should merge dictionaries and arrays, removing duplicates', function() {
+                    shell.cp('-rf', ios_config_xml, temp);
+                    shell.cp('-rf', plistplugin, plugins_dir);
+                    var platformJson = PlatformJson.load(plugins_dir, 'ios');
+                    platformJson.addInstalledPluginToPrepareQueue('org.apache.plist', {});
+                    configChanges.process(plugins_dir, temp, 'ios', platformJson, pluginInfoProvider);
+                    expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf-8')).toMatch(/<key>UINewsstandIcon<\/key>[\s\S]*<key>CFBundlePrimaryIcon<\/key>/);
+                    expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf-8')).toMatch(/<string>schema-b<\/string>/);
+                    expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf-8')).not.toMatch(/(<string>schema-a<\/string>[^]*){2,}/);
+                });
+            });
+            describe('of pbxproject framework files', function() {
+                var xcode_add;
+                beforeEach(function() {
+                    shell.cp('-rf', ios_config_xml, temp);
+                    shell.cp('-rf', cbplugin, plugins_dir);
+                    xcode_add = spyOn(xcode.project.prototype, 'addFramework').andCallThrough();
+                });
+                it('should call into xcode.addFramework if plugin has <framework> file defined and is ios',function() {
+                    var platformJson = PlatformJson.load(plugins_dir, 'ios');
+                    platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.childbrowser', {});
+                    var munger = new configChanges.PlatformMunger('ios', temp, platformJson, pluginInfoProvider);
+                    munger.process(plugins_dir);
+                    expect(xcode_add).toHaveBeenCalledWith('libsqlite3.dylib', {weak:false});
+                    expect(xcode_add).toHaveBeenCalledWith('social.framework', {weak:true});
+                    expect(xcode_add).toHaveBeenCalledWith('music.framework', {weak:false});
+                    expect(xcode_add).not.toHaveBeenCalledWith('Custom.framework');
+                });
+            });
+            it('should resolve wildcard config-file targets to the project, if applicable', function() {
+                shell.cp('-rf', ios_config_xml, temp);
+                shell.cp('-rf', cbplugin, plugins_dir);
+                var platformJson = PlatformJson.load(plugins_dir, 'ios');
+                platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.childbrowser', {});
+                var spy = spyOn(fs, 'readFileSync').andCallThrough();
+
+                var munger = new configChanges.PlatformMunger('ios', temp, platformJson, pluginInfoProvider);
+                munger.process(plugins_dir);
+                expect(spy).toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'SampleApp-Info.plist').replace(/\\/g, '/'), 'utf8');
+            });
+            it('should move successfully installed plugins from queue to installed plugins section, and include/retain vars if applicable', function() {
+                shell.cp('-rf', android_two_project, temp);
+                shell.cp('-rf', varplugin, plugins_dir);
+                var platformJson = PlatformJson.load(plugins_dir, 'android');
+                platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {'API_KEY':'hi'}, true);
+
+                var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                munger.process(plugins_dir);
+
+                expect(platformJson.root.prepare_queue.installed.length).toEqual(0);
+                expect(platformJson.root.installed_plugins['com.adobe.vars']).toBeDefined();
+                expect(platformJson.root.installed_plugins['com.adobe.vars']['API_KEY']).toEqual('hi');
+            });
+        });
+
+        describe(': uninstallation', function() {
+            it('should call pruneXML for every config munge it completely removes from the app (every leaf that is decremented to 0)', function() {
+                shell.cp('-rf', android_two_project, temp);
+
+                // Run through an "install"
+                var platformJson = PlatformJson.load(plugins_dir, 'android');
+                platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {});
+                var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                munger.process(plugins_dir);
+
+                // Now set up an uninstall and make sure prunexml is called properly
+                platformJson.addUninstalledPluginToPrepareQueue('org.test.plugins.dummyplugin');
+                var spy = spyOn(xml_helpers, 'pruneXML').andReturn(true);
+                munger.process(plugins_dir);
+                expect(spy.calls.length).toEqual(4);
+                expect(spy.argsForCall[0][2]).toEqual('/*');
+                expect(spy.argsForCall[1][2]).toEqual('/*');
+                expect(spy.argsForCall[2][2]).toEqual('/manifest/application');
+                expect(spy.argsForCall[3][2]).toEqual('/cordova/plugins');
+            });
+            it('should generate a config munge that interpolates variables into config changes, if applicable', function() {
+                shell.cp('-rf', android_two_project, temp);
+                shell.cp('-rf', varplugin, plugins_dir);
+                // Run through an "install"
+                var platformJson = PlatformJson.load(plugins_dir, 'android');
+                platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {'API_KEY':'canucks'});
+                var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                munger.process(plugins_dir);
+
+                // Now set up an uninstall and make sure prunexml is called properly
+                platformJson.addUninstalledPluginToPrepareQueue('com.adobe.vars');
+                var spy = spyOn(munger, 'generate_plugin_config_munge').andReturn({});
+                munger.process(plugins_dir);
+                var munge_params = spy.mostRecentCall.args;
+                expect(munge_params[0]).toEqual(jasmine.any(PluginInfo));
+                expect(munge_params[0].dir).toEqual(path.join(plugins_dir, 'com.adobe.vars'));
+                expect(munge_params[1]['API_KEY']).toEqual('canucks');
+            });
+            it('should not call pruneXML for a config munge that another plugin depends on', function() {
+                shell.cp('-rf', android_two_no_perms_project, temp);
+                shell.cp('-rf', childrenplugin, plugins_dir);
+                shell.cp('-rf', shareddepsplugin, plugins_dir);
+
+                // Run through and "install" two plugins (they share a permission for INTERNET)
+                var platformJson = PlatformJson.load(plugins_dir, 'android');
+                platformJson.addInstalledPluginToPrepareQueue('org.test.multiple-children', {});
+                platformJson.addInstalledPluginToPrepareQueue('org.test.shareddeps', {});
+                var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                munger.process(plugins_dir);
+
+                // Now set up an uninstall for multi-child plugin
+                platformJson.addUninstalledPluginToPrepareQueue('org.test.multiple-children');
+                munger.process(plugins_dir);
+                munger.save_all();
+                var am_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf-8')));
+                var permission = am_xml.find('./uses-permission');
+                expect(permission).toBeDefined();
+                expect(permission.attrib['android:name']).toEqual('android.permission.INTERNET');
+            });
+            it('should not call pruneXML for a config munge targeting a config file that does not exist', function() {
+                shell.cp('-rf', android_two_project, temp);
+                // install a plugin
+                var platformJson = PlatformJson.load(plugins_dir, 'android');
+                platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {});
+                var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                munger.process(plugins_dir);
+
+                // set up an uninstall for the same plugin
+                platformJson.addUninstalledPluginToPrepareQueue('org.test.plugins.dummyplugin');
+
+                var spy = spyOn(fs, 'readFileSync').andCallThrough();
+                munger.process(plugins_dir);
+
+                expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'), 'utf-8');
+            });
+            it('should remove uninstalled plugins from installed plugins list', function() {
+                shell.cp('-rf', android_two_project, temp);
+                shell.cp('-rf', varplugin, plugins_dir);
+                // install the var plugin
+                var platformJson = PlatformJson.load(plugins_dir, 'android');
+                platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {'API_KEY':'eat my shorts'});
+                var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                munger.process(plugins_dir);
+
+                // queue up an uninstall for the same plugin
+                platformJson.addUninstalledPluginToPrepareQueue('com.adobe.vars');
+                munger.process(plugins_dir);
+
+                expect(platformJson.root.prepare_queue.uninstalled.length).toEqual(0);
+                expect(platformJson.root.installed_plugins['com.adobe.vars']).not.toBeDefined();
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/ConfigParser.spec.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/ConfigParser.spec.js b/cordova-common/spec/ConfigParser.spec.js
deleted file mode 100644
index a888cf4..0000000
--- a/cordova-common/spec/ConfigParser.spec.js
+++ /dev/null
@@ -1,224 +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 path = require('path'),
-    fs = require('fs'),
-    ConfigParser = require('../src/configparser/ConfigParser'),
-    xml = path.join(__dirname, 'test-config.xml'),
-    xml_contents = fs.readFileSync(xml, 'utf-8');
-
-describe('config.xml parser', function () {
-    var readFile;
-    beforeEach(function() {
-        readFile = spyOn(fs, 'readFileSync').andReturn(xml_contents);
-    });
-
-    it('should create an instance based on an xml file', function() {
-        var cfg;
-        expect(function () {
-            cfg = new ConfigParser(xml);
-        }).not.toThrow();
-        expect(cfg).toBeDefined();
-        expect(cfg.doc).toBeDefined();
-    });
-
-    describe('methods', function() {
-        var cfg;
-        beforeEach(function() {
-            cfg = new ConfigParser(xml);
-        });
-
-        describe('package name / id', function() {
-            it('should get the (default) packagename', function() {
-                expect(cfg.packageName()).toEqual('io.cordova.hellocordova');
-            });
-            it('should allow setting the packagename', function() {
-                cfg.setPackageName('this.is.bat.country');
-                expect(cfg.packageName()).toEqual('this.is.bat.country');
-            });
-        });
-
-        describe('package name / android-packageName', function() {
-            it('should get the android packagename', function() {
-                expect(cfg.android_packageName()).toEqual('io.cordova.hellocordova.android');
-            });
-        });
-
-        describe('package name / ios-CFBundleIdentifier', function() {
-            it('should get the ios packagename', function() {
-                expect(cfg.ios_CFBundleIdentifier()).toEqual('io.cordova.hellocordova.ios');
-            });
-        });
-
-        describe('version', function() {
-            it('should get the version', function() {
-                expect(cfg.version()).toEqual('0.0.1');
-            });
-            it('should allow setting the version', function() {
-                cfg.setVersion('2.0.1');
-                expect(cfg.version()).toEqual('2.0.1');
-            });
-        });
-
-        describe('app name', function() {
-            it('should get the (default) app name', function() {
-                expect(cfg.name()).toEqual('Hello Cordova');
-            });
-            it('should allow setting the app name', function() {
-                cfg.setName('this.is.bat.country');
-                expect(cfg.name()).toEqual('this.is.bat.country');
-            });
-        });
-        describe('preference', function() {
-            it('should return the value of a global preference', function() {
-                expect(cfg.getPreference('fullscreen')).toEqual('true');
-            });
-            it('should return the value of a platform-specific preference', function() {
-                expect(cfg.getPreference('android-minSdkVersion', 'android')).toEqual('10');
-            });
-            it('should return an empty string for a non-existing preference', function() {
-                expect(cfg.getPreference('zimzooo!')).toEqual('');
-            });
-        });
-        describe('global preference', function() {
-            it('should return the value of a global preference', function() {
-                expect(cfg.getGlobalPreference('orientation')).toEqual('portrait');
-            });
-            it('should return an empty string for a non-existing preference', function() {
-                expect(cfg.getGlobalPreference('foobar')).toEqual('');
-            });
-        });
-        describe('platform-specific preference', function() {
-            it('should return the value of a platform specific preference', function() {
-                expect(cfg.getPlatformPreference('orientation', 'android')).toEqual('landscape');
-            });
-            it('should return an empty string when querying for a non-existing preference', function() {
-                expect(cfg.getPlatformPreference('foobar', 'android')).toEqual('');
-            });
-            it('should return an empty string when querying with unsupported platform', function() {
-                expect(cfg.getPlatformPreference('orientation', 'foobar')).toEqual('');
-            });
-        });
-        describe('plugin',function(){
-            it('should read plugin id list', function() {
-               var expectedList = [
-                   'org.apache.cordova.pluginwithvars',
-                   'org.apache.cordova.pluginwithurl',
-                   'org.apache.cordova.pluginwithversion',
-                   'org.apache.cordova.pluginwithurlandversion',
-                   'org.apache.cordova.justaplugin',
-                   'org.apache.cordova.legacyfeatureversion',
-                   'org.apache.cordova.legacyfeatureurl',
-                   'org.apache.cordova.legacyfeatureversionandurl'
-               ];
-               var list = cfg.getPluginIdList();
-               expect(list.length).toEqual(expectedList.length);
-               expectedList.forEach(function(plugin){
-                   expect(list).toContain(plugin);
-               });
-            });
-            it('should read plugin given id', function(){
-                var plugin = cfg.getPlugin('org.apache.cordova.justaplugin');
-                expect(plugin).toBeDefined();
-                expect(plugin.name).toEqual('org.apache.cordova.justaplugin');
-                expect(plugin.variables).toBeDefined();
-            });
-            it('should not read plugin given undefined id', function(){
-                var plugin = cfg.getPlugin('org.apache.cordova.undefinedplugin');
-                expect(plugin).not.toBeDefined();
-            });
-            it('should read plugin with src and store it in spec field', function(){
-                var plugin = cfg.getPlugin('org.apache.cordova.pluginwithurl');
-                expect(plugin.spec).toEqual('http://cordova.apache.org/pluginwithurl');
-            });
-            it('should read plugin with version and store it in spec field', function(){
-                var plugin = cfg.getPlugin('org.apache.cordova.pluginwithversion');
-                expect(plugin.spec).toEqual('1.1.1');
-            });
-            it('should read plugin with source and version and store source in spec field', function(){
-                var plugin = cfg.getPlugin('org.apache.cordova.pluginwithurlandversion');
-                expect(plugin.spec).toEqual('http://cordova.apache.org/pluginwithurlandversion');
-            });
-            it('should read plugin variables', function () {
-                var plugin = cfg.getPlugin('org.apache.cordova.pluginwithvars');
-                expect(plugin.variables).toBeDefined();
-                expect(plugin.variables.var).toBeDefined();
-                expect(plugin.variables.var).toEqual('varvalue');
-            });
-            it('should allow adding a new plugin', function(){
-                cfg.addPlugin({name:'myplugin'});
-                var plugins = cfg.doc.findall('plugin');
-                var pluginNames = plugins.map(function(plugin){
-                    return plugin.attrib.name;
-                });
-                expect(pluginNames).toContain('myplugin');
-            });
-            it('should allow adding features with params', function(){
-                cfg.addPlugin({name:'aplugin'}, [{name:'paraname',value:'paravalue'}]);
-                // Additional check for new parameters syntax
-                cfg.addPlugin({name:'bplugin'}, {paraname: 'paravalue'});
-                var plugins = cfg.doc.findall('plugin')
-                .filter(function (plugin) {
-                    return plugin.attrib.name === 'aplugin' || plugin.attrib.name === 'bplugin';
-                });
-                expect(plugins.length).toBe(2);
-                plugins.forEach(function (plugin) {
-                    var variables = plugin.findall('variable');
-                    expect(variables[0].attrib.name).toEqual('paraname');
-                    expect(variables[0].attrib.value).toEqual('paravalue');
-                });
-            });
-            it('should be able to read legacy feature entries with a version', function(){
-                var plugin = cfg.getPlugin('org.apache.cordova.legacyfeatureversion');
-                expect(plugin).toBeDefined();
-                expect(plugin.name).toEqual('org.apache.cordova.legacyfeatureversion');
-                expect(plugin.spec).toEqual('1.2.3');
-                expect(plugin.variables).toBeDefined();
-                expect(plugin.variables.aVar).toEqual('aValue');
-            });
-            it('should be able to read legacy feature entries with a url', function(){
-                var plugin = cfg.getPlugin('org.apache.cordova.legacyfeatureurl');
-                expect(plugin).toBeDefined();
-                expect(plugin.name).toEqual('org.apache.cordova.legacyfeatureurl');
-                expect(plugin.spec).toEqual('http://cordova.apache.org/legacyfeatureurl');
-            });
-            it('should be able to read legacy feature entries with a version and a url', function(){
-                var plugin = cfg.getPlugin('org.apache.cordova.legacyfeatureversionandurl');
-                expect(plugin).toBeDefined();
-                expect(plugin.name).toEqual('org.apache.cordova.legacyfeatureversionandurl');
-                expect(plugin.spec).toEqual('http://cordova.apache.org/legacyfeatureversionandurl');
-            });
-            it('it should remove given plugin', function(){
-                cfg.removePlugin('org.apache.cordova.justaplugin');
-                var plugins = cfg.doc.findall('plugin');
-                var pluginNames = plugins.map(function(plugin){
-                    return plugin.attrib.name;
-                });
-                expect(pluginNames).not.toContain('org.apache.cordova.justaplugin');
-            });
-            it('it should remove given legacy feature id', function(){
-                cfg.removePlugin('org.apache.cordova.legacyplugin');
-                var plugins = cfg.doc.findall('feature');
-                var pluginNames = plugins.map(function(plugin){
-                    return plugin.attrib.name;
-                });
-                expect(pluginNames).not.toContain('org.apache.cordova.legacyplugin');
-            });
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/ConfigParser/ConfigParser.spec.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/ConfigParser/ConfigParser.spec.js b/cordova-common/spec/ConfigParser/ConfigParser.spec.js
new file mode 100644
index 0000000..9d4696d
--- /dev/null
+++ b/cordova-common/spec/ConfigParser/ConfigParser.spec.js
@@ -0,0 +1,224 @@
+/**
+    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 path = require('path'),
+    fs = require('fs'),
+    ConfigParser = require('../../src/ConfigParser/ConfigParser'),
+    xml = path.join(__dirname, '../fixtures/test-config.xml'),
+    xml_contents = fs.readFileSync(xml, 'utf-8');
+
+describe('config.xml parser', function () {
+    var readFile;
+    beforeEach(function() {
+        readFile = spyOn(fs, 'readFileSync').andReturn(xml_contents);
+    });
+
+    it('should create an instance based on an xml file', function() {
+        var cfg;
+        expect(function () {
+            cfg = new ConfigParser(xml);
+        }).not.toThrow();
+        expect(cfg).toBeDefined();
+        expect(cfg.doc).toBeDefined();
+    });
+
+    describe('methods', function() {
+        var cfg;
+        beforeEach(function() {
+            cfg = new ConfigParser(xml);
+        });
+
+        describe('package name / id', function() {
+            it('should get the (default) packagename', function() {
+                expect(cfg.packageName()).toEqual('io.cordova.hellocordova');
+            });
+            it('should allow setting the packagename', function() {
+                cfg.setPackageName('this.is.bat.country');
+                expect(cfg.packageName()).toEqual('this.is.bat.country');
+            });
+        });
+
+        describe('package name / android-packageName', function() {
+            it('should get the android packagename', function() {
+                expect(cfg.android_packageName()).toEqual('io.cordova.hellocordova.android');
+            });
+        });
+
+        describe('package name / ios-CFBundleIdentifier', function() {
+            it('should get the ios packagename', function() {
+                expect(cfg.ios_CFBundleIdentifier()).toEqual('io.cordova.hellocordova.ios');
+            });
+        });
+
+        describe('version', function() {
+            it('should get the version', function() {
+                expect(cfg.version()).toEqual('0.0.1');
+            });
+            it('should allow setting the version', function() {
+                cfg.setVersion('2.0.1');
+                expect(cfg.version()).toEqual('2.0.1');
+            });
+        });
+
+        describe('app name', function() {
+            it('should get the (default) app name', function() {
+                expect(cfg.name()).toEqual('Hello Cordova');
+            });
+            it('should allow setting the app name', function() {
+                cfg.setName('this.is.bat.country');
+                expect(cfg.name()).toEqual('this.is.bat.country');
+            });
+        });
+        describe('preference', function() {
+            it('should return the value of a global preference', function() {
+                expect(cfg.getPreference('fullscreen')).toEqual('true');
+            });
+            it('should return the value of a platform-specific preference', function() {
+                expect(cfg.getPreference('android-minSdkVersion', 'android')).toEqual('10');
+            });
+            it('should return an empty string for a non-existing preference', function() {
+                expect(cfg.getPreference('zimzooo!')).toEqual('');
+            });
+        });
+        describe('global preference', function() {
+            it('should return the value of a global preference', function() {
+                expect(cfg.getGlobalPreference('orientation')).toEqual('portrait');
+            });
+            it('should return an empty string for a non-existing preference', function() {
+                expect(cfg.getGlobalPreference('foobar')).toEqual('');
+            });
+        });
+        describe('platform-specific preference', function() {
+            it('should return the value of a platform specific preference', function() {
+                expect(cfg.getPlatformPreference('orientation', 'android')).toEqual('landscape');
+            });
+            it('should return an empty string when querying for a non-existing preference', function() {
+                expect(cfg.getPlatformPreference('foobar', 'android')).toEqual('');
+            });
+            it('should return an empty string when querying with unsupported platform', function() {
+                expect(cfg.getPlatformPreference('orientation', 'foobar')).toEqual('');
+            });
+        });
+        describe('plugin',function(){
+            it('should read plugin id list', function() {
+               var expectedList = [
+                   'org.apache.cordova.pluginwithvars',
+                   'org.apache.cordova.pluginwithurl',
+                   'org.apache.cordova.pluginwithversion',
+                   'org.apache.cordova.pluginwithurlandversion',
+                   'org.apache.cordova.justaplugin',
+                   'org.apache.cordova.legacyfeatureversion',
+                   'org.apache.cordova.legacyfeatureurl',
+                   'org.apache.cordova.legacyfeatureversionandurl'
+               ];
+               var list = cfg.getPluginIdList();
+               expect(list.length).toEqual(expectedList.length);
+               expectedList.forEach(function(plugin){
+                   expect(list).toContain(plugin);
+               });
+            });
+            it('should read plugin given id', function(){
+                var plugin = cfg.getPlugin('org.apache.cordova.justaplugin');
+                expect(plugin).toBeDefined();
+                expect(plugin.name).toEqual('org.apache.cordova.justaplugin');
+                expect(plugin.variables).toBeDefined();
+            });
+            it('should not read plugin given undefined id', function(){
+                var plugin = cfg.getPlugin('org.apache.cordova.undefinedplugin');
+                expect(plugin).not.toBeDefined();
+            });
+            it('should read plugin with src and store it in spec field', function(){
+                var plugin = cfg.getPlugin('org.apache.cordova.pluginwithurl');
+                expect(plugin.spec).toEqual('http://cordova.apache.org/pluginwithurl');
+            });
+            it('should read plugin with version and store it in spec field', function(){
+                var plugin = cfg.getPlugin('org.apache.cordova.pluginwithversion');
+                expect(plugin.spec).toEqual('1.1.1');
+            });
+            it('should read plugin with source and version and store source in spec field', function(){
+                var plugin = cfg.getPlugin('org.apache.cordova.pluginwithurlandversion');
+                expect(plugin.spec).toEqual('http://cordova.apache.org/pluginwithurlandversion');
+            });
+            it('should read plugin variables', function () {
+                var plugin = cfg.getPlugin('org.apache.cordova.pluginwithvars');
+                expect(plugin.variables).toBeDefined();
+                expect(plugin.variables.var).toBeDefined();
+                expect(plugin.variables.var).toEqual('varvalue');
+            });
+            it('should allow adding a new plugin', function(){
+                cfg.addPlugin({name:'myplugin'});
+                var plugins = cfg.doc.findall('plugin');
+                var pluginNames = plugins.map(function(plugin){
+                    return plugin.attrib.name;
+                });
+                expect(pluginNames).toContain('myplugin');
+            });
+            it('should allow adding features with params', function(){
+                cfg.addPlugin({name:'aplugin'}, [{name:'paraname',value:'paravalue'}]);
+                // Additional check for new parameters syntax
+                cfg.addPlugin({name:'bplugin'}, {paraname: 'paravalue'});
+                var plugins = cfg.doc.findall('plugin')
+                .filter(function (plugin) {
+                    return plugin.attrib.name === 'aplugin' || plugin.attrib.name === 'bplugin';
+                });
+                expect(plugins.length).toBe(2);
+                plugins.forEach(function (plugin) {
+                    var variables = plugin.findall('variable');
+                    expect(variables[0].attrib.name).toEqual('paraname');
+                    expect(variables[0].attrib.value).toEqual('paravalue');
+                });
+            });
+            it('should be able to read legacy feature entries with a version', function(){
+                var plugin = cfg.getPlugin('org.apache.cordova.legacyfeatureversion');
+                expect(plugin).toBeDefined();
+                expect(plugin.name).toEqual('org.apache.cordova.legacyfeatureversion');
+                expect(plugin.spec).toEqual('1.2.3');
+                expect(plugin.variables).toBeDefined();
+                expect(plugin.variables.aVar).toEqual('aValue');
+            });
+            it('should be able to read legacy feature entries with a url', function(){
+                var plugin = cfg.getPlugin('org.apache.cordova.legacyfeatureurl');
+                expect(plugin).toBeDefined();
+                expect(plugin.name).toEqual('org.apache.cordova.legacyfeatureurl');
+                expect(plugin.spec).toEqual('http://cordova.apache.org/legacyfeatureurl');
+            });
+            it('should be able to read legacy feature entries with a version and a url', function(){
+                var plugin = cfg.getPlugin('org.apache.cordova.legacyfeatureversionandurl');
+                expect(plugin).toBeDefined();
+                expect(plugin.name).toEqual('org.apache.cordova.legacyfeatureversionandurl');
+                expect(plugin.spec).toEqual('http://cordova.apache.org/legacyfeatureversionandurl');
+            });
+            it('it should remove given plugin', function(){
+                cfg.removePlugin('org.apache.cordova.justaplugin');
+                var plugins = cfg.doc.findall('plugin');
+                var pluginNames = plugins.map(function(plugin){
+                    return plugin.attrib.name;
+                });
+                expect(pluginNames).not.toContain('org.apache.cordova.justaplugin');
+            });
+            it('it should remove given legacy feature id', function(){
+                cfg.removePlugin('org.apache.cordova.legacyplugin');
+                var plugins = cfg.doc.findall('feature');
+                var pluginNames = plugins.map(function(plugin){
+                    return plugin.attrib.name;
+                });
+                expect(pluginNames).not.toContain('org.apache.cordova.legacyplugin');
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/PluginInfo/PluginInfo.spec.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/PluginInfo/PluginInfo.spec.js b/cordova-common/spec/PluginInfo/PluginInfo.spec.js
new file mode 100644
index 0000000..935d8cd
--- /dev/null
+++ b/cordova-common/spec/PluginInfo/PluginInfo.spec.js
@@ -0,0 +1,50 @@
+/**
+    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 PluginInfo = require('../../src/PluginInfo/PluginInfo'),
+    path = require('path');
+
+var pluginsDir = path.join(__dirname, '../fixtures/plugins');
+
+describe('PluginInfo', function () {
+    it('should read a plugin.xml file', function () {
+        var p, prefs, assets, deps, configFiles, infos, srcFiles;
+        var headerFiles, libFiles, resourceFiles;
+        expect(function () {
+            p = new PluginInfo(path.join(pluginsDir, 'ChildBrowser'));
+            prefs = p.getPreferences('android');
+            assets = p.getAssets('android');
+            deps = p.getDependencies('android');
+            configFiles = p.getConfigFiles('android');
+            infos = p.getInfo('android');
+            srcFiles = p.getSourceFiles('android');
+            headerFiles = p.getHeaderFiles('android');
+            libFiles = p.getLibFiles('android');
+            resourceFiles = p.getResourceFiles('android');
+        }).not.toThrow();
+        expect(p).toBeDefined();
+        expect(p.name).toEqual('Child Browser');
+        // TODO: Add some expectations for results of getSomething.
+    });
+    it('should throw when there is no plugin.xml file', function () {
+        expect(function () {
+            new PluginInfo('/non/existent/dir');
+        }).toThrow();
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/PluginInfo/PluginInfoProvider.spec.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/PluginInfo/PluginInfoProvider.spec.js b/cordova-common/spec/PluginInfo/PluginInfoProvider.spec.js
new file mode 100644
index 0000000..40ec6bb
--- /dev/null
+++ b/cordova-common/spec/PluginInfo/PluginInfoProvider.spec.js
@@ -0,0 +1,33 @@
+/**
+    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 PluginInfoProvider = require('../../src/PluginInfo/PluginInfoProvider'),
+    path = require('path');
+
+var pluginsDir = path.join(__dirname, '../fixtures/plugins');
+
+describe('PluginInfoProvider', function () {
+    describe('getAllWithinSearchPath', function () {
+        it('should load all plugins in a dir', function () {
+            var pluginInfoProvider = new PluginInfoProvider();
+            var plugins = pluginInfoProvider.getAllWithinSearchPath(pluginsDir);
+            expect(plugins.length).not.toBe(0);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/fixtures/plugins/ChildBrowser/plugin.xml
----------------------------------------------------------------------
diff --git a/cordova-common/spec/fixtures/plugins/ChildBrowser/plugin.xml b/cordova-common/spec/fixtures/plugins/ChildBrowser/plugin.xml
new file mode 100644
index 0000000..11ddd86
--- /dev/null
+++ b/cordova-common/spec/fixtures/plugins/ChildBrowser/plugin.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ Licensed 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="com.phonegap.plugins.childbrowser"
+    version="0.6.0">
+
+    <name>Child Browser</name>
+
+    <asset src="www/childbrowser" target="childbrowser" />
+    <asset src="www/childbrowser_file.html" target="childbrowser_file.html" />
+
+    <js-module src="www/childbrowser.js" name="ChildBrowser">
+        <clobbers target="childbrowser" />
+    </js-module>
+
+    <config-file target="config.xml" parent="/*">
+        <access origin="build.phonegap.com" />
+        <access origin="s3.amazonaws.com" />
+    </config-file>
+    
+    <info>No matter what platform you are installing to, this notice is very important.</info>
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="AndroidManifest.xml" parent="/manifest/application">
+            <activity android:name="com.phonegap.plugins.childBrowser.ChildBrowser"
+                      android:label="@string/app_name">
+                <intent-filter>
+                </intent-filter>
+            </activity>
+        </config-file>
+
+        <!-- CDV < 2.0 -->
+        <config-file target="res/xml/plugins.xml" parent="/plugins">
+            <plugin name="ChildBrowser"
+                value="com.phonegap.plugins.childBrowser.ChildBrowser"/>
+        </config-file>
+
+        <!-- CDV 2.0+ (for now) -->
+        <config-file target="res/xml/config.xml" parent="/cordova/plugins">
+            <plugin name="ChildBrowser"
+                value="com.phonegap.plugins.childBrowser.ChildBrowser"/>
+        </config-file>
+
+        <source-file src="src/android/ChildBrowser.java"
+                target-dir="src/com/phonegap/plugins/childBrowser" />
+        <info>Please make sure you read this because it is very important to complete the installation of your plugin.</info>
+    </platform>
+
+    <!-- ios -->
+    <platform name="ios">
+        <plugins-plist key="com.phonegap.plugins.childbrowser"
+            string="ChildBrowserCommand" />
+
+        <config-file target="config.xml" parent="/widget/plugins">
+            <plugin name="ChildBrowser"
+                value="ChildBrowserCommand" />
+        </config-file>
+
+        <resource-file src="src/ios/ChildBrowser.bundle" />
+        <resource-file src="src/ios/ChildBrowserViewController.xib" />
+
+        <config-file target="*-Info.plist" parent="AppId">
+            <string>$APP_ID</string>
+        </config-file>
+        
+        <config-file target="*-Info.plist" parent="CFBundleURLTypes">
+            <array>
+              <dict>
+                <key>PackageName</key>
+                <string>$PACKAGE_NAME</string>
+              </dict>
+            </array>
+        </config-file>
+
+        <header-file src="src/ios/ChildBrowserCommand.h" />
+        <header-file src="src/ios/ChildBrowserViewController.h" />
+        <header-file src="src/ios/TargetDirTest.h" target-dir="targetDir"/>
+
+        <source-file src="src/ios/ChildBrowserCommand.m" />
+        <source-file src="src/ios/ChildBrowserViewController.m" />
+        <source-file src="src/ios/preserveDirs/PreserveDirsTest.m" preserve-dirs="true" />
+        <header-file src="src/ios/TargetDirTest.m" target-dir="targetDir"/>
+
+        <!-- framework for testing (not actual dependency of ChildBrowser -->
+        <framework src="libsqlite3.dylib" />
+        <framework src="social.framework" weak="true" />
+        <framework src="music.framework" weak="rabbit" />
+    </platform>
+
+    <!-- wp8 -->
+    <platform name="wp8">
+        <resource-file src="src\wp7\Images\appbar.back.rest.png" />
+        <config-file target="config.xml" parent="/widget/plugins">
+            <plugin name="ChildBrowser"
+                value="ChildBrowser"/>
+        </config-file>
+
+        <source-file src="src\wp7\ChildBrowserCommand.cs"
+                     target-dir="Plugins\" />
+
+        <!-- modify the project file to include the added files -->
+        <config-file target=".csproj" parent=".">  
+        </config-file> 
+
+    </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/fixtures/plugins/ChildBrowser/src/android/ChildBrowser.java
----------------------------------------------------------------------
diff --git a/cordova-common/spec/fixtures/plugins/ChildBrowser/src/android/ChildBrowser.java b/cordova-common/spec/fixtures/plugins/ChildBrowser/src/android/ChildBrowser.java
new file mode 100644
index 0000000..5263b0c
--- /dev/null
+++ b/cordova-common/spec/fixtures/plugins/ChildBrowser/src/android/ChildBrowser.java
@@ -0,0 +1,19 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * Licensed 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.
+ *
+*/
+

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser.js b/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser.js
new file mode 100644
index 0000000..5263b0c
--- /dev/null
+++ b/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser.js
@@ -0,0 +1,19 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * Licensed 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.
+ *
+*/
+

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser/image.jpg
----------------------------------------------------------------------
diff --git a/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser/image.jpg b/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser/image.jpg
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser/image.jpg
@@ -0,0 +1 @@
+foo

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser_file.html
----------------------------------------------------------------------
diff --git a/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser_file.html b/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser_file.html
new file mode 100644
index 0000000..6de7b8c
--- /dev/null
+++ b/cordova-common/spec/fixtures/plugins/ChildBrowser/www/childbrowser_file.html
@@ -0,0 +1 @@
+This is a test file.

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/fixtures/plugins/com.adobe.vars/plugin.xml
----------------------------------------------------------------------
diff --git a/cordova-common/spec/fixtures/plugins/com.adobe.vars/plugin.xml b/cordova-common/spec/fixtures/plugins/com.adobe.vars/plugin.xml
new file mode 100644
index 0000000..9eff729
--- /dev/null
+++ b/cordova-common/spec/fixtures/plugins/com.adobe.vars/plugin.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ Licensed 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="com.adobe.vars"
+    version="3.0.0">
+
+    <name>Use Variables</name>
+
+    <preference name="API_KEY" />
+
+    <info>Remember that your api key is $API_KEY!</info>
+    <!-- android -->
+    <platform name="android">
+		<config-file target="AndroidManifest.xml" parent="/manifest">
+            <poop name="GoogleMapsApiKey" value="$API_KEY" />
+            <package>$PACKAGE_NAME</package>
+		</config-file>
+		
+    </platform>
+    
+    <!-- amazon fireos -->
+    <platform name="amazon-fireos">
+		<config-file target="AndroidManifest.xml" parent="/manifest">
+            <poop name="GoogleMapsApiKey" value="$API_KEY" />
+            <package>$PACKAGE_NAME</package>
+		</config-file>
+		
+    </platform>
+
+    <!-- ios -->
+    <platform name="ios">
+        <config-file target="config.xml" parent="/widget">
+            <awesome value="$API_KEY" />
+            <cfbundleid>$PACKAGE_NAME</cfbundleid>
+        </config-file>
+        <config-file target="*-Info.plist" parent="APluginNode">
+            <string></string>
+        </config-file>
+    </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/fixtures/plugins/org.apache.plist/plugin.xml
----------------------------------------------------------------------
diff --git a/cordova-common/spec/fixtures/plugins/org.apache.plist/plugin.xml b/cordova-common/spec/fixtures/plugins/org.apache.plist/plugin.xml
new file mode 100644
index 0000000..b7d5439
--- /dev/null
+++ b/cordova-common/spec/fixtures/plugins/org.apache.plist/plugin.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ Licensed 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="org.apache.plist"
+    version="3.0.0">
+
+    <name>PList updates</name>
+
+    <!-- ios -->
+    <platform name="ios">
+        <config-file target="*-Info.plist" parent="CFBundleIcons">
+        <dict>
+        <key>UINewsstandIcon</key>
+        <dict>
+        <key>CFBundleIconFiles</key>
+        <array>
+            <string>Newsstand-Cover-Icon.png</string>
+            <string>Newsstand-Cover-Icon@2x.png</string>
+        </array>
+        <key>UINewsstandBindingType</key>
+        <string>UINewsstandBindingTypeMagazine</string>
+        <key>UINewsstandBindingEdge</key>
+        <string>UINewsstandBindingEdgeLeft</string>
+        </dict>
+        </dict>
+        </config-file>
+
+        <config-file target="*-Info.plist" parent="CFValidSchemas">
+        <array>
+            <string>schema-a</string>
+            <string>schema-b</string>
+        </array>
+        </config-file>
+    </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/59042bae/cordova-common/spec/fixtures/plugins/org.test.configtest/plugin.xml
----------------------------------------------------------------------
diff --git a/cordova-common/spec/fixtures/plugins/org.test.configtest/plugin.xml b/cordova-common/spec/fixtures/plugins/org.test.configtest/plugin.xml
new file mode 100644
index 0000000..fa1ff51
--- /dev/null
+++ b/cordova-common/spec/fixtures/plugins/org.test.configtest/plugin.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ Licensed 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="org.test.configtest"
+    version="3.0.0">
+
+    <name>Does Code Fil Write Even Work? Hopefully the Tests Will Tell Us</name>
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="res/xml/config.xml" parent="/widget">
+            <poop/>
+        </config-file>
+        <config-file target="res/xml/config.xml" parent="/widget">
+            <poop/>
+        </config-file>
+    </platform>
+
+    <platform name="windows">
+        <config-file target="package.appxmanifest" parent="/Parent/Capabilities">
+            <Capability Note="should-exist-for-all-appxmanifest-target-files" />
+        </config-file>
+        <config-file target="package.appxmanifest" parent="/Parent/Capabilities" versions="<=8.0.0">
+            <Capability Note="should-exist-for-win8-only" />
+        </config-file>
+        <config-file target="package.appxmanifest" parent="/Parent/Capabilities" versions="=8.1.0">
+            <Capability Note="should-exist-for-win81-win-and-phone" />
+        </config-file>
+        <config-file target="package.appxmanifest" parent="/Parent/Capabilities" versions="<=8.1.0" device-target="windows">
+            <Capability Note="should-exist-for-win8-and-win81-win-only" />
+        </config-file>
+        <config-file target="package.appxmanifest" parent="/Parent/Capabilities" versions="<=8.1.0" device-target="phone">
+            <Capability Note="should-exist-for-win81-phone-only" />
+        </config-file>
+        <config-file target="package.appxmanifest" parent="/Parent/Capabilities" versions="<=8.1.0" device-target="all">
+            <Capability Note="should-exist-for-win8-and-win81-both" />
+        </config-file>
+        <config-file target="package.appxmanifest" parent="/Parent/Capabilities" versions=">=10.0.0">
+            <Capability Note="should-exist-in-win10-only" />
+        </config-file>
+    </platform>
+</plugin>


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