You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by fi...@apache.org on 2013/04/29 09:33:31 UTC
[1/2] git commit: pretty much there for a config handler module. a
few uninstall tests missing,
and need to wire up to install/uninstall/prepare properly (and add tests for
interaction between those modules)
Updated Branches:
refs/heads/master 8c2e7ba18 -> 94be165cc
pretty much there for a config handler module. a few uninstall tests missing, and need to wire up to install/uninstall/prepare properly (and add tests for interaction between those modules)
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/1395f63a
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/1395f63a
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/1395f63a
Branch: refs/heads/master
Commit: 1395f63a0c6f2db2f9d50a5b1fda4ab2fc0c10aa
Parents: 8c2e7ba
Author: Fil Maj <ma...@gmail.com>
Authored: Mon Apr 29 00:11:49 2013 -0700
Committer: Fil Maj <ma...@gmail.com>
Committed: Mon Apr 29 00:12:29 2013 -0700
----------------------------------------------------------------------
README.md | 4 +-
spec/install.spec.js | 10 +-
spec/platforms/android.spec.js | 74 ------
spec/platforms/blackberry.spec.js | 36 ---
spec/platforms/ios.spec.js | 48 ----
spec/plugins/ConfigTestPlugin/plugin.xml | 37 +++
spec/plugins/VariablePlugin/plugin.xml | 2 +-
spec/uninstall.spec.js | 7 +
spec/util/config-changes.spec.js | 303 +++++++++++++++++++++++++
src/install.js | 41 +++-
src/uninstall.js | 22 +-
src/util/config-changes.js | 269 +++++++++++++++++++++-
src/util/xml-helpers.js | 8 +-
13 files changed, 662 insertions(+), 199 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 75e83a9..795d250 100644
--- a/README.md
+++ b/README.md
@@ -15,14 +15,14 @@ This document defines tool usage and the plugin format specification.
## Usage
plugman --fetch --plugin <directory|git-url|name> [--plugins_dir <directory>]
- plugman --install --platform <ios|android|bb10> --project <directory> --plugin <name> [--plugins_dir <directory>]
+ plugman --install --platform <ios|android|bb10> --project <directory> --plugin <name|url> [--plugins_dir <directory>]
plugman --uninstall --platform <ios|android|bb10> --project <directory> --plugin <name> [--plugins_dir <directory>]
plugman --remove --plugin <name> [--plugins_dir <directory>]
plugman --list [--plugins_dir <directory>]
plugman --prepare --platform <ios|android|bb10> --project <directory> [--plugins_dir <directory>]
* `--fetch`: Retrieves and stores a plugin
-* `--install`: Installs an already-`--fetch`'ed plugin into a cordova project
+* `--install`: Installs a plugin into a cordova project. if `<name|url>` does not exist under the `plugins_dir`, will call `fetch` to retrieve it first.
* `--uninstall`: Uninstalls an already-`--install`'ed plugin from a cordova project
* `--remove`: Removes a `--fetch`'ed plugin
* `--list`: Lists all `--fetch`'ed plugins
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/spec/install.spec.js
----------------------------------------------------------------------
diff --git a/spec/install.spec.js b/spec/install.spec.js
index dd48d87..e993388 100644
--- a/spec/install.spec.js
+++ b/spec/install.spec.js
@@ -2,6 +2,7 @@ var install = require('../src/install'),
android = require('../src/platforms/android'),
ios = require('../src/platforms/ios'),
blackberry = require('../src/platforms/blackberry'),
+ config_changes = require('../src/util/config-changes'),
plugman = require('../plugman'),
fs = require('fs'),
os = require('osenv'),
@@ -51,9 +52,15 @@ describe('install', function() {
install('android', temp, 'DummyPlugin', plugins_dir, {});
var transactions = android_installer.mostRecentCall.args[0];
- expect(transactions.length).toEqual(6);
+ expect(transactions.length).toEqual(3);
expect(transactions[0].tag).toBe('source-file');
});
+ it('should call the config-changes module\'s add_installed_plugin_to_prepare_queue method', function() {
+ install('android', temp, 'DummyPlugin', plugins_dir, {});
+ var spy = spyOn(config_changes, 'add_installed_plugin_to_prepare_queue');
+ android_installer.mostRecentCall.args[5](null); // fake out handler install callback
+ expect(spy).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android', {});
+ });
});
describe('failure', function() {
@@ -77,6 +84,7 @@ describe('install', function() {
expect(executed_txs.length).toEqual(0);
});
it('should throw if plugin is already installed into project', function() {
+ // TODO: plugins and their version can be recognized using the platform.json file
shell.cp('-rf', dummyplugin, plugins_dir);
expect(function() {
install('android', temp, 'DummyPlugin', plugins_dir, {});
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/spec/platforms/android.spec.js
----------------------------------------------------------------------
diff --git a/spec/platforms/android.spec.js b/spec/platforms/android.spec.js
index 198a4bd..beb7c30 100644
--- a/spec/platforms/android.spec.js
+++ b/spec/platforms/android.spec.js
@@ -94,42 +94,6 @@ describe('android project handler', function() {
}).toThrow('"' + target + '" already exists!');
});
});
- describe('of <config-file> elements', function() {
- it('should only target config.xml if that is applicable', function() {
- var config = copyArray(configChanges);
- shell.cp('-rf', android_two_project, temp);
- var s = spyOn(xml_helpers, 'parseElementtreeSync').andCallThrough();
- android.install(config, dummy_id, temp, dummyplugin, {});
- expect(s).toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'config.xml'));
- expect(s).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'));
- });
- it('should only target plugins.xml if that is applicable', function() {
- shell.cp('-rf', android_one_project, temp);
- var config = copyArray(configChanges);
- var s = spyOn(xml_helpers, 'parseElementtreeSync').andCallThrough();
- android.install(config, dummy_id, temp, dummyplugin, {});
- expect(s).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'config.xml'));
- expect(s).toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'));
- });
- it('should call into xml helper\'s graftXML', function() {
- shell.cp('-rf', android_one_project, temp);
- var config = copyArray(configChanges);
- var s = spyOn(xml_helpers, 'graftXML').andReturn(true);
- android.install(config, dummy_id, temp, dummyplugin, {});
- expect(s).toHaveBeenCalled();
- });
- });
- it('should call into plugins\'s searchAndReplace to interpolate variables properly', function() {
- shell.cp('-rf', android_one_project, temp);
- var config = copyArray(variable_configs);
- var s = spyOn(plugins_module, 'searchAndReplace');
- var vars = {
- 'API_KEY':'batcountry'
- };
- android.install(config, variable_id, temp, variableplugin, vars);
- expect(s).toHaveBeenCalledWith(path.resolve(temp, 'AndroidManifest.xml'), vars);
- expect(s).toHaveBeenCalledWith(path.resolve(temp, 'res', 'xml', 'plugins.xml'), vars);
- });
});
describe('uninstallation', function() {
@@ -153,44 +117,6 @@ describe('android project handler', function() {
});
});
});
- describe('of <config-file> elements', function() {
- it('should only target config.xml if that is applicable', function(done) {
- var config = copyArray(configChanges);
- var s = spyOn(xml_helpers, 'parseElementtreeSync').andCallThrough();
- install('android', temp, 'DummyPlugin', plugins_dir, {}, function() {
- var config = copyArray(configChanges);
- android.uninstall(config, dummy_id, temp, path.join(plugins_dir, 'DummyPlugin'));
- expect(s).toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'config.xml'));
- expect(s).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'));
- done();
- });
- });
- it('should only target plugins.xml if that is applicable', function(done) {
- shell.rm('-rf', temp);
- shell.mkdir('-p', temp);
- shell.mkdir('-p', plugins_dir);
- shell.cp('-rf', android_one_project, temp);
- shell.cp('-rf', dummyplugin, plugins_dir);
- var config = copyArray(configChanges);
- var s = spyOn(xml_helpers, 'parseElementtreeSync').andCallThrough();
- install('android', temp, 'DummyPlugin', plugins_dir, {}, function() {
- var config = copyArray(configChanges);
- android.uninstall(config, dummy_id, temp, path.join(plugins_dir, 'DummyPlugin'));
- expect(s).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'config.xml'));
- expect(s).toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'));
- done();
- });
- });
- it('should call into xml helper\'s pruneXML', function(done) {
- var config = copyArray(configChanges);
- install('android', temp, 'DummyPlugin', plugins_dir, {}, function() {
- var s = spyOn(xml_helpers, 'pruneXML').andReturn(true);
- android.uninstall(config, dummy_id, temp, path.join(plugins_dir, 'DummyPlugin'));
- expect(s).toHaveBeenCalled();
- done();
- });
- });
- });
describe('of <asset> elements', function() {
it('should remove www\'s plugins/<plugin-id> directory', function(done) {
var as = copyArray(assets);
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/spec/platforms/blackberry.spec.js
----------------------------------------------------------------------
diff --git a/spec/platforms/blackberry.spec.js b/spec/platforms/blackberry.spec.js
index a4d7e63..049ec95 100644
--- a/spec/platforms/blackberry.spec.js
+++ b/spec/platforms/blackberry.spec.js
@@ -83,21 +83,6 @@ describe('blackberry project handler', function() {
}).toThrow('"' + target + '" already exists!');
});
});
- describe('of <config-file> elements', function() {
- it('should target config.xml', function() {
- var config = copyArray(configChanges);
- var s = spyOn(xml_helpers, 'parseElementtreeSync').andCallThrough();
- blackberry.install(config, dummy_id, temp, dummyplugin, {});
- expect(s).toHaveBeenCalledWith(path.join(temp, 'www', 'config.xml'));
- });
- it('should call into xml helper\'s graftXML', function() {
- shell.cp('-rf', blackberry_project, temp);
- var config = copyArray(configChanges);
- var s = spyOn(xml_helpers, 'graftXML').andReturn(true);
- blackberry.install(config, dummy_id, temp, dummyplugin, {});
- expect(s).toHaveBeenCalled();
- });
- });
});
describe('uninstallation', function() {
@@ -123,27 +108,6 @@ describe('blackberry project handler', function() {
});
});
});
- describe('of <config-file> elements', function() {
- it('should target config.xml', function(done) {
- var config = copyArray(configChanges);
- var s = spyOn(xml_helpers, 'parseElementtreeSync').andCallThrough();
- install('blackberry', temp, 'DummyPlugin', plugins_dir, {}, function() {
- var config = copyArray(configChanges);
- blackberry.uninstall(config, dummy_id, temp, path.join(plugins_dir, 'DummyPlugin'));
- expect(s).toHaveBeenCalledWith(path.join(temp, 'www', 'config.xml'));
- done();
- });
- });
- it('should call into xml helper\'s pruneXML', function(done) {
- var config = copyArray(configChanges);
- install('blackberry', temp, 'DummyPlugin', plugins_dir, {}, function() {
- var s = spyOn(xml_helpers, 'pruneXML').andReturn(true);
- blackberry.uninstall(config, dummy_id, temp, path.join(plugins_dir, 'DummyPlugin'));
- expect(s).toHaveBeenCalled();
- done();
- });
- });
- });
describe('of <asset> elements', function() {
it('should remove www\'s plugins/<plugin-id> directory', function(done) {
var as = copyArray(assets);
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/spec/platforms/ios.spec.js
----------------------------------------------------------------------
diff --git a/spec/platforms/ios.spec.js b/spec/platforms/ios.spec.js
index dc3bee5..cb5521f 100644
--- a/spec/platforms/ios.spec.js
+++ b/spec/platforms/ios.spec.js
@@ -97,13 +97,6 @@ describe('ios project handler', function() {
ios.install([], 'someid', temp, plugins_dir, {});
}).toThrow('could not find PhoneGap/Cordova plist file, or config.xml file.');
});
- it('should interpolate any variables correctly into pbx, plist and config files', function() {
- shell.cp('-rf', ios_config_xml_project, temp);
- ios.install(variable_configs, variable_id, temp, plugins_dir, {'API_KEY':'ruthless competency'});
- var config_file = path.join(temp, 'SampleApp', 'config.xml');
- var contents = fs.readFileSync(config_file, 'utf-8');
- expect(contents).toMatch(/awesome value="ruthless competency"/gi);
- });
describe('of <source-file> elements', function() {
beforeEach(function() {
@@ -171,47 +164,6 @@ describe('ios project handler', function() {
});
});
- describe('of <plugins-plist> elements', function() {
- it('should only be used in an applicably old cordova-ios projects', function() {
- shell.cp('-rf', ios_plist_project, temp);
- var pls = copyArray(plist_els);
- var spy = spyOn(plist, 'parseFileSync').andReturn({Plugins:{}});
- ios.install(pls, dummy_id, temp, dummyplugin, {});
- expect(spy).toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'PhoneGap.plist'));
- });
- it('should not be used in an applicably new cordova-ios projects', function() {
- shell.cp('-rf', ios_config_xml_project, temp);
- var pls = copyArray(plist_els);
- var spy = spyOn(plist, 'parseFileSync').andReturn({Plugins:{}});
- ios.install(pls, dummy_id, temp, dummyplugin, {});
- expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'config.xml'));
- });
- it('should add a <plugin> element in applicably new cordova-ios projects with old-style plugins using only <plugins-plist> elements', function() {
- shell.cp('-rf', ios_config_xml_project, temp);
- var pls = copyArray(plist_only_els);
- ios.install(pls, plist_id, temp, plistplugin, {});
- expect(fs.readFileSync(path.join(temp, 'SampleApp', 'config.xml'), 'utf-8')).toMatch(/<plugin name="OldSkewl"/gi);
- });
- });
-
- describe('of <config-file> elements', function() {
- beforeEach(function() {
- shell.cp('-rf', ios_config_xml_project, temp);
- });
- it('should call xml_helpers\' graftXML', function() {
- var config = copyArray(dummy_configs);
- var spy = spyOn(xml_helpers, 'graftXML').andReturn(true);
- ios.install(config, dummy_id, temp, dummyplugin, {});
- expect(spy).toHaveBeenCalledWith(jasmine.any(Object), dummy_configs[0]._children, '/widget/plugins');
- });
- it('should write the new config file out after successfully grafting', function() {
- var config = copyArray(dummy_configs);
- var spy = spyOn(fs, 'writeFileSync');
- ios.install(config, dummy_id, temp, dummyplugin, {});
- expect(spy).toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'config.xml'), jasmine.any(String));
- });
- });
-
describe('of <header-file> elements', function() {
beforeEach(function() {
shell.cp('-rf', ios_config_xml_project, temp);
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/spec/plugins/ConfigTestPlugin/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/ConfigTestPlugin/plugin.xml b/spec/plugins/ConfigTestPlugin/plugin.xml
new file mode 100644
index 0000000..57dd1a8
--- /dev/null
+++ b/spec/plugins/ConfigTestPlugin/plugin.xml
@@ -0,0 +1,37 @@
+<?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://www.phonegap.com/ns/plugins/1.0"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ id="com.phonegap.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>
+</plugin>
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/spec/plugins/VariablePlugin/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/VariablePlugin/plugin.xml b/spec/plugins/VariablePlugin/plugin.xml
index 67bddd0..7249ae8 100644
--- a/spec/plugins/VariablePlugin/plugin.xml
+++ b/spec/plugins/VariablePlugin/plugin.xml
@@ -21,7 +21,7 @@
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
xmlns="http://www.phonegap.com/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
- id="com.pushwoosh.plugins.pushwoosh"
+ id="com.adobe.vars"
version="3.0.0">
<name>Use Variables</name>
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/spec/uninstall.spec.js
----------------------------------------------------------------------
diff --git a/spec/uninstall.spec.js b/spec/uninstall.spec.js
index e6b16db..6404b4f 100644
--- a/spec/uninstall.spec.js
+++ b/spec/uninstall.spec.js
@@ -4,6 +4,7 @@ var uninstall = require('../src/uninstall'),
ios = require('../src/platforms/ios'),
blackberry = require('../src/platforms/blackberry'),
xml_helpers = require('../src/util/xml-helpers'),
+ config_changes = require('../src/util/config-changes'),
plugman = require('../plugman'),
fs = require('fs'),
et = require('elementtree'),
@@ -46,6 +47,12 @@ describe('uninstall', function() {
expect(transactions.length).toEqual(6);
expect(transactions[0].tag).toBe('source-file');
});
+ it('should call the config-changes module\'s add_uninstalled_plugin_to_prepare_queue method', function() {
+ uninstall('android', temp, 'DummyPlugin', plugins_dir, {});
+ var spy = spyOn(config_changes, 'add_uninstalled_plugin_to_prepare_queue');
+ android_uninstaller.mostRecentCall.args[4](null); // fake out handler uninstall callback
+ expect(spy).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android');
+ });
});
describe('failure', function() {
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/spec/util/config-changes.spec.js
----------------------------------------------------------------------
diff --git a/spec/util/config-changes.spec.js b/spec/util/config-changes.spec.js
new file mode 100644
index 0000000..1d88bf7
--- /dev/null
+++ b/spec/util/config-changes.spec.js
@@ -0,0 +1,303 @@
+var configChanges = require('../../src/util/config-changes'),
+ xml_helpers = require('../../src/util/xml-helpers'),
+ fs = require('fs'),
+ os = require('osenv'),
+ et = require('elementtree'),
+ path = require('path'),
+ shell = require('shelljs'),
+ temp = path.join(os.tmpdir(), 'plugman'),
+ dummyplugin = path.join(__dirname, '..', 'plugins', 'DummyPlugin'),
+ childrenplugin = path.join(__dirname, '..', 'plugins', 'multiple-children'),
+ configplugin = path.join(__dirname, '..', 'plugins', 'ConfigTestPlugin'),
+ varplugin = path.join(__dirname, '..', 'plugins', 'VariablePlugin'),
+ android_two_project = path.join(__dirname, '..', 'projects', 'android_two', '*'),
+ ios_plist_project = path.join(__dirname, '..', 'projects', 'ios-plist', '*'),
+ plugins_dir = path.join(temp, 'cordova', 'plugins');
+
+var dummy_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(dummyplugin, 'plugin.xml'), 'utf-8')));
+
+function innerXML(xmltext) {
+ return xmltext.replace(/^<[\w\s\-=\/"\.]+>/, '').replace(/<\/[\w\s\-=\/"\.]+>$/,'');
+}
+
+describe('config-changes module', function() {
+ beforeEach(function() {
+ shell.mkdir('-p', temp);
+ shell.mkdir('-p', plugins_dir);
+ });
+ afterEach(function() {
+ shell.rm('-rf', temp);
+ });
+
+ it('should have queue methods', function() {
+ expect(configChanges.add_installed_plugin_to_prepare_queue).toBeDefined();
+ expect(configChanges.add_uninstalled_plugin_to_prepare_queue).toBeDefined();
+ });
+ it('should have a get_platform_json method', function() {
+ expect(configChanges.get_platform_json).toBeDefined();
+ });
+ it('should have a save_platform_json method', function() {
+ expect(configChanges.save_platform_json).toBeDefined();
+ });
+ it('should have a generate_plugin_config_munge method', function() {
+ expect(configChanges.generate_plugin_config_munge).toBeDefined();
+ });
+ it('should have a process method', function() {
+ expect(configChanges.process).toBeDefined();
+ });
+
+ describe('queue methods', function() {
+ describe('add_installed_plugin_to_prepare_queue', function() {
+ it('should call get_platform_json method', function() {
+ var spy = spyOn(configChanges, 'get_platform_json').andReturn({
+ prepare_queue:{
+ installed:[],
+ uninstalled:[]
+ }
+ });
+ configChanges.add_installed_plugin_to_prepare_queue(plugins_dir, 'PooPlugin', 'android', {});
+ expect(spy).toHaveBeenCalledWith(plugins_dir, 'android');
+ });
+ it('should append specified plugin to platform.json', function() {
+ configChanges.add_installed_plugin_to_prepare_queue(plugins_dir, 'PooPlugin', 'android', {});
+ var json = configChanges.get_platform_json(plugins_dir, 'android');
+ expect(json.prepare_queue.installed[0].plugin).toEqual('PooPlugin');
+ expect(json.prepare_queue.installed[0].vars).toEqual({});
+ });
+ it('should append specified plugin with any variables to platform.json', function() {
+ configChanges.add_installed_plugin_to_prepare_queue(plugins_dir, 'PooPlugin', 'android', {'dude':'man'});
+ var json = configChanges.get_platform_json(plugins_dir, 'android');
+ expect(json.prepare_queue.installed[0].plugin).toEqual('PooPlugin');
+ expect(json.prepare_queue.installed[0].vars).toEqual({'dude':'man'});
+ });
+ it('should call save_platform_json with updated config', function() {
+ var spy = spyOn(configChanges, 'save_platform_json');
+ configChanges.add_installed_plugin_to_prepare_queue(plugins_dir, 'PooPlugin', 'android', {});
+ var config = spy.mostRecentCall.args[0];
+ expect(config.prepare_queue.installed[0].plugin).toEqual('PooPlugin');
+ });
+ });
+
+ describe('add_uninstalled_plugin_to_prepare_queue', function() {
+ beforeEach(function() {
+ shell.cp('-rf', dummyplugin, plugins_dir);
+ });
+
+ it('should call get_platform_json method', function() {
+ var spy = spyOn(configChanges, 'get_platform_json').andReturn({
+ prepare_queue:{
+ installed:[],
+ uninstalled:[]
+ }
+ });
+ configChanges.add_uninstalled_plugin_to_prepare_queue(plugins_dir, 'DummyPlugin', 'android');
+ expect(spy).toHaveBeenCalledWith(plugins_dir, 'android');
+ });
+ it('should append specified plugin to platform.json', function() {
+ configChanges.add_uninstalled_plugin_to_prepare_queue(plugins_dir, 'DummyPlugin', 'android');
+ var json = configChanges.get_platform_json(plugins_dir, 'android');
+ expect(json.prepare_queue.uninstalled[0].plugin).toEqual('DummyPlugin');
+ expect(json.prepare_queue.uninstalled[0].id).toEqual('com.phonegap.plugins.dummyplugin');
+ });
+ it('should call save_platform_json with updated config', function() {
+ var spy = spyOn(configChanges, 'save_platform_json');
+ configChanges.add_uninstalled_plugin_to_prepare_queue(plugins_dir, 'DummyPlugin', 'android', {});
+ var config = spy.mostRecentCall.args[0];
+ expect(config.prepare_queue.uninstalled[0].plugin).toEqual('DummyPlugin');
+ expect(config.prepare_queue.uninstalled[0].id).toEqual('com.phonegap.plugins.dummyplugin');
+ });
+ });
+ });
+
+ describe('get_platform_json method', function() {
+ it('should write out and return an empty config json file if it doesn\'t exist', function() {
+ var filepath = path.join(plugins_dir, 'android.json');
+ var cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ expect(cfg).toBeDefined();
+ expect(cfg.prepare_queue).toBeDefined();
+ expect(cfg.config_munge).toBeDefined();
+ expect(cfg.installed_plugins).toBeDefined();
+ expect(fs.existsSync(filepath)).toBe(true);
+ expect(fs.readFileSync(filepath, 'utf-8')).toEqual(JSON.stringify(cfg));
+ });
+ 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:{somechange:"blah"},installed_plugins:{}};
+ fs.writeFileSync(filepath, JSON.stringify(json), 'utf-8');
+ var cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ expect(JSON.stringify(json)).toEqual(JSON.stringify(cfg));
+ });
+ });
+
+ xdescribe('save_platform_json method', function() {
+ it('should write out specified json', function() {
+ var filepath = path.join(plugins_dir, 'android.json');
+ var cfg = {poop:true};
+ configChanges.save_platform_json(cfg, plugins_dir, 'android');
+ expect(fs.existsSync(filepath)).toBe(true);
+ expect(fs.readFileSync(filepath, 'utf-8')).toEqual(JSON.stringify(cfg));
+ });
+ });
+
+ describe('generate_plugin_config_munge method', function() {
+ it('should return a flat config heirarchy for simple, one-off config changes', function() {
+ var xml;
+ var munge = configChanges.generate_plugin_config_munge(dummyplugin, 'android');
+ expect(munge['AndroidManifest.xml']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/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(munge['AndroidManifest.xml']['/manifest/application'][xml]).toEqual(1);
+ expect(munge['res/xml/plugins.xml']).toBeDefined();
+ expect(munge['res/xml/plugins.xml']['/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(munge['res/xml/plugins.xml']['/plugins'][xml]).toEqual(1);
+ expect(munge['res/xml/config.xml']).toBeDefined();
+ expect(munge['res/xml/config.xml']['/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(munge['res/xml/config.xml']['/cordova/plugins'][xml]).toEqual(1);
+ });
+ it('should split out multiple children of config-file elements into individual leaves', function() {
+ var munge = configChanges.generate_plugin_config_munge(childrenplugin, 'android');
+ expect(munge['AndroidManifest.xml']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="android.permission.READ_PHONE_STATE" />']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="android.permission.INTERNET" />']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="android.permission.GET_ACCOUNTS" />']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="android.permission.WAKE_LOCK" />']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']['<permission android:name="$PACKAGE_NAME.permission.C2D_MESSAGE" android:protectionLevel="signature" />']).toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="$PACKAGE_NAME.permission.C2D_MESSAGE" />']).toBeDefined();
+ expect(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 munge = configChanges.generate_plugin_config_munge(childrenplugin, 'android');
+ expect(munge['AndroidManifest.xml']['/manifest']['<!--library-->']).not.toBeDefined();
+ expect(munge['AndroidManifest.xml']['/manifest']['<!-- GCM connects to Google Services. -->']).not.toBeDefined();
+ });
+ it('should increment config heirarchy leaves if dfferent config-file elements target the same file + selector + xml', function() {
+ var munge = configChanges.generate_plugin_config_munge(configplugin, 'android');
+ expect(munge['res/xml/config.xml']['/widget']['<poop />']).toEqual(2);
+ });
+ it('should take into account interpolation variables', function() {
+ var munge = configChanges.generate_plugin_config_munge(childrenplugin, 'android', {PACKAGE_NAME:'ca.filmaj.plugins'});
+ expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="ca.filmaj.plugins.permission.C2D_MESSAGE" />']).toBeDefined();
+ });
+ it('should special case plugins-plist elements into own property', function() {
+ });
+ });
+
+ 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 cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ cfg.prepare_queue.installed = [{'plugin':'DummyPlugin', 'vars':{}}];
+ configChanges.save_platform_json(cfg, plugins_dir, 'android');
+ var spy = spyOn(configChanges, 'generate_plugin_config_munge').andReturn({});
+ configChanges.process(plugins_dir, temp, 'android');
+ expect(spy).toHaveBeenCalledWith(path.join(plugins_dir, 'DummyPlugin'), 'android', {});
+ });
+ it('should get a reference to existing config munge by calling get_platform_json', function() {
+ shell.cp('-rf', android_two_project, temp);
+ var spy = spyOn(configChanges, 'get_platform_json').andReturn({
+ prepare_queue:{
+ installed:[],
+ uninstalled:[]
+ },
+ config_munge:{}
+ });
+ configChanges.process(plugins_dir, temp, 'android');
+ expect(spy).toHaveBeenCalledWith(plugins_dir, 'android');
+ });
+ describe(': installation', function() {
+
+ it('should call graftXML for every new config munge it introduces (every leaf in config munge that does not exist)', function() {
+ shell.cp('-rf', android_two_project, temp);
+ var cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ cfg.prepare_queue.installed = [{'plugin':'DummyPlugin', 'vars':{}}];
+ configChanges.save_platform_json(cfg, plugins_dir, 'android');
+
+ var spy = spyOn(xml_helpers, 'graftXML').andReturn(true);
+
+ var manifest_doc = new et.ElementTree(et.XML(fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf-8')));
+ var munge = dummy_xml.find('./platform[@name="android"]/config-file[@target="AndroidManifest.xml"]');
+ configChanges.process(plugins_dir, temp, 'android');
+ expect(spy.calls.length).toEqual(2);
+ expect(spy.argsForCall[0][2]).toEqual('/manifest/application');
+ expect(spy.argsForCall[1][2]).toEqual('/cordova/plugins');
+ });
+ it('should not call graftXML for a config munge that already exists from another plugin', function() {
+ shell.cp('-rf', android_two_project, temp);
+ shell.cp('-rf', configplugin, plugins_dir);
+ var cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ cfg.prepare_queue.installed = [{'plugin':'ConfigTestPlugin', 'vars':{}}];
+ configChanges.save_platform_json(cfg, plugins_dir, 'android');
+
+ var spy = spyOn(xml_helpers, 'graftXML').andReturn(true);
+ configChanges.process(plugins_dir, temp, 'android');
+ expect(spy.calls.length).toEqual(1);
+ });
+ it('should not call graftXML for a config munge targeting a config file that does not exist', function() {
+ shell.cp('-rf', android_two_project, temp);
+ var cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ cfg.prepare_queue.installed = [{'plugin':'DummyPlugin', 'vars':{}}];
+ configChanges.save_platform_json(cfg, plugins_dir, 'android');
+
+ var spy = spyOn(fs, 'readFileSync').andCallThrough();
+
+ configChanges.process(plugins_dir, temp, 'android');
+ expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'), 'utf-8');
+ });
+ it('should move successfully installed plugins from queue to installed plugins section, and include/retain vars if applicable', function() {
+ shell.cp('-rf', varplugin, plugins_dir);
+ var cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ cfg.prepare_queue.installed = [{'plugin':'VariablePlugin', 'vars':{"API_KEY":"hi"}}];
+ configChanges.save_platform_json(cfg, plugins_dir, 'android');
+
+ configChanges.process(plugins_dir, temp, 'android');
+
+ cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ expect(cfg.prepare_queue.installed.length).toEqual(0);
+ expect(cfg.installed_plugins['com.adobe.vars']).toBeDefined();
+ expect(cfg.installed_plugins['com.adobe.vars']['API_KEY']).toEqual('hi');
+ });
+ it('should save changes to global config munge after completing an install', function() {
+ shell.cp('-rf', android_two_project, temp);
+ shell.cp('-rf', varplugin, plugins_dir);
+ var cfg = configChanges.get_platform_json(plugins_dir, 'android');
+ cfg.prepare_queue.installed = [{'plugin':'VariablePlugin', 'vars':{"API_KEY":"hi"}}];
+ configChanges.save_platform_json(cfg, plugins_dir, 'android');
+
+ var spy = spyOn(configChanges, 'save_platform_json');
+ configChanges.process(plugins_dir, temp, 'android');
+ expect(spy).toHaveBeenCalled();
+ });
+ describe('of <plugins-plist> elements', function() {
+ it('should only be used in an applicably old cordova-ios projects', function() {
+ shell.cp('-rf', ios_plist_project, temp);
+ });
+ it('should not be used in an applicably new cordova-ios projects', function() {
+ shell.cp('-rf', ios_config_xml_project, temp);
+ var pls = copyArray(plist_els);
+ var spy = spyOn(plist, 'parseFileSync').andReturn({Plugins:{}});
+ ios.install(pls, dummy_id, temp, dummyplugin, {});
+ expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'config.xml'));
+ });
+ });
+ });
+
+ describe(': uninstallation', function() {
+ it('should call pruneXML for every config munge it completely removes from the app (every leaf that is decremented to 0)');
+ it('should call pruneXML with variables to interpolate if applicable');
+ it('should not call pruneXML for a config munge that another plugin depends on');
+ it('should not call pruneXML for a config munge targeting a config file that does not exist');
+ it('should remove uninstalled plugins from installed plugins list');
+ it('should save changes to global config munge after completing an uninstall');
+ });
+ });
+});
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/src/install.js
----------------------------------------------------------------------
diff --git a/src/install.js b/src/install.js
index c71d2ea..001f5e6 100644
--- a/src/install.js
+++ b/src/install.js
@@ -1,6 +1,7 @@
var path = require('path'),
fs = require('fs'),
et = require('elementtree'),
+ config_changes = require('./util/config-changes');
platform_modules = require('./platforms');
// TODO: is name necessary as a param ehre?
@@ -53,13 +54,35 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable
});
if (missing_vars.length > 0) {
var err = new Error('Variable(s) missing: ' + missing_vars.join(", "));
- if (callback) {
- callback(err);
- return;
+ if (callback) callback(err);
+ else throw err;
+ return;
+ }
+
+ // check if platform has plugin fully installed or queued already.
+ var platform_config = config_changes.get_platform_json(plugins_dir, platform);
+ var plugin_basename = path.basename(plugin_dir);
+ if (platform_config.prepare_queue.installed.indexOf(plugin_basename) > -1) {
+ var err = new Error('plugin "' + plugin_basename + '" is already installed (but needs to be prepared)');
+ if (callback) callback(err);
+ else throw err;
+ return;
+ }
+ var is_fully_installed = false;
+ Object.keys(platform_config.installed_plugins).forEach(function(installed_plugin_id) {
+ if (installed_plugin_id == plugin_id) {
+ is_fully_installed = true;
}
+ });
+ if (is_fully_installed) {
+ var err = new Error('plugin "' + plugin_basename + '" (id: '+plugin_id+') is already installed');
+ if (callback) callback(err);
else throw err;
+ return;
}
+ // TODO: handle asset elements
+ // TODO: if plugin does not have platform tag but has platform-agnostic config changes, should we queue it up?
var platformTag = plugin_et.find('./platform[@name="'+platform+'"]');
if (!platformTag) {
// Either this plugin doesn't support this platform, or it's a JS-only plugin.
@@ -69,23 +92,19 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable
if (callback) callback();
return;
}
- var handler = platform_modules[platform];
-
- // TODO: check if platform has plugin installed already.
// parse plugin.xml into transactions
+ var handler = platform_modules[platform];
var txs = [];
var sourceFiles = platformTag.findall('./source-file'),
headerFiles = platformTag.findall('./header-file'),
resourceFiles = platformTag.findall('./resource-file'),
assets = platformTag.findall('./asset'),
- frameworks = platformTag.findall('./framework'),
- pluginsPlist = platformTag.findall('./plugins-plist'),
- configChanges = platformTag.findall('./config-file');
+ frameworks = platformTag.findall('./framework');
assets = assets.concat(plugin_et.findall('./asset'));
- txs = txs.concat(sourceFiles, headerFiles, resourceFiles, frameworks, configChanges, assets, pluginsPlist);
+ txs = txs.concat(sourceFiles, headerFiles, resourceFiles, frameworks, assets);
// pass platform-specific transactions into install
handler.install(txs, plugin_id, project_dir, plugin_dir, filtered_variables, function(err) {
@@ -110,6 +129,8 @@ function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variable
}
} else {
// WIN!
+ // queue up the plugin so prepare knows what to do.
+ config_changes.add_installed_plugin_to_prepare_queue(plugins_dir, path.basename(plugin_dir), platform, filtered_variables);
// call prepare after a successful install
require('./../plugman').prepare(project_dir, platform, plugins_dir);
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/src/uninstall.js
----------------------------------------------------------------------
diff --git a/src/uninstall.js b/src/uninstall.js
index aca3f1f..a740d4f 100644
--- a/src/uninstall.js
+++ b/src/uninstall.js
@@ -1,17 +1,15 @@
var path = require('path'),
fs = require('fs'),
et = require('elementtree'),
- platform_modules = require('./platforms');
-
+ platform_modules = require('./platforms'),
+ config_changes = require('./util/config-changes');
module.exports = function uninstallPlugin(platform, project_dir, name, plugins_dir, cli_variables, callback) {
if (!platform_modules[platform]) {
var err = new Error(platform + " not supported.");
- if (callback) {
- callback(err);
- return;
- }
+ if (callback) callback(err);
else throw err;
+ return;
}
// Check that the plugin has already been fetched.
@@ -19,11 +17,9 @@ module.exports = function uninstallPlugin(platform, project_dir, name, plugins_d
if (!fs.existsSync(plugin_dir)) {
var err = new Error('Plugin "' + name + '" not found. Already uninstalled?');
- if (callback) {
- callback(err);
- return;
- }
+ if (callback) callback(err);
else throw err;
+ return;
}
runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback);
@@ -36,6 +32,8 @@ function runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variab
var name = plugin_et.findall('name').text;
var plugin_id = plugin_et._root.attrib['id'];
+ // TODO: remove any asset elements
+
var platformTag = plugin_et.find('./platform[@name="'+platform+'"]');
var platformTag = plugin_et.find('./platform[@name="'+platform+'"]');
if (!platformTag) {
@@ -46,9 +44,9 @@ function runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variab
if (callback) callback();
return;
}
- var handler = platform_modules[platform];
// parse plugin.xml into transactions
+ var handler = platform_modules[platform];
var txs = [];
var sourceFiles = platformTag.findall('./source-file'),
headerFiles = platformTag.findall('./header-file'),
@@ -84,6 +82,8 @@ function runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variab
}
} else {
// WIN!
+ // queue up the plugin so prepare can remove the config changes
+ config_changes.add_uninstalled_plugin_to_prepare_queue(plugins_dir, path.basename(plugin_dir), platform);
// call prepare after a successful uninstall
require('./../plugman').prepare(project_dir, platform, plugins_dir);
if (callback) callback();
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/src/util/config-changes.js
----------------------------------------------------------------------
diff --git a/src/util/config-changes.js b/src/util/config-changes.js
index d931039..83f4544 100644
--- a/src/util/config-changes.js
+++ b/src/util/config-changes.js
@@ -17,22 +17,267 @@
*
*/
-// takes an xml list of config-file tags
-// returns a JS object with an easy to use structure
+var fs = require('fs'),
+ path = require('path'),
+ glob = require('glob'),
+ plist = require('plist'),
+ bplist = require('bplist-parser'),
+ et = require('elementtree'),
+ platforms = require('./../platforms'),
+ xml_helpers = require('./../util/xml-helpers');
-module.exports = function configChanges(baseTag) {
- var tags = baseTag.findall('./config-file'),
- files = {};
+function checkPlatform(platform) {
+ if (!(platform in platforms)) throw new Error('platform "' + platform + '" not recognized.');
+}
+
+module.exports = {
+ add_installed_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, vars) {
+ checkPlatform(platform);
+
+ var config = module.exports.get_platform_json(plugins_dir, platform);
+ config.prepare_queue.installed.push({'plugin':plugin, 'vars':vars});
+ module.exports.save_platform_json(config, plugins_dir, platform);
+ },
+ add_uninstalled_plugin_to_prepare_queue:function(plugins_dir, plugin, platform) {
+ checkPlatform(platform);
- tags.forEach(function (tag) {
- var target = tag.attrib['target'];
+ var plugin_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(plugins_dir, plugin, 'plugin.xml'), 'utf-8')));
+ var config = module.exports.get_platform_json(plugins_dir, platform);
+ config.prepare_queue.uninstalled.push({'plugin':plugin, 'id':plugin_xml._root.attrib['id']});
+ module.exports.save_platform_json(config, plugins_dir, platform);
+ },
+ get_platform_json:function(plugins_dir, platform) {
+ checkPlatform(platform);
- if (files[target]) {
- files[target].push(tag);
+ var filepath = path.join(plugins_dir, platform + '.json');
+ if (fs.existsSync(filepath)) {
+ return JSON.parse(fs.readFileSync(filepath, 'utf-8'));
} else {
- files[target] = [tag];
+ var config = {
+ prepare_queue:{installed:[], uninstalled:[]},
+ config_munge:{},
+ installed_plugins:{}
+ };
+ fs.writeFileSync(filepath, JSON.stringify(config), 'utf-8');
+ return config;
}
- });
+ },
+ save_platform_json:function(config, plugins_dir, platform) {
+ checkPlatform(platform);
+
+ var filepath = path.join(plugins_dir, platform + '.json');
+ fs.writeFileSync(filepath, JSON.stringify(config), 'utf-8');
+ },
+ generate_plugin_config_munge:function(plugin_dir, platform, vars) {
+ checkPlatform(platform);
+
+ var munge = {};
+ var plugin_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(plugin_dir, 'plugin.xml'), 'utf-8')));
+
+ var platformTag = plugin_xml.find('platform[@name="' + platform + '"]');
+ var changes = platformTag.findall('config-file');
+ // note down plugins-plist munges in special section of munge obj
+ var plugins_plist = platformTag.findall('plugins-plist');
+ plugins_plist.forEach(function(pl) {
+ if (!munge['plugins-plist']) {
+ munge['plugins-plist'] = {};
+ }
+ var key = pl.attrib['key'];
+ var value = pl.attrib['string'];
+ if (!munge['plugins-plist'][key]) {
+ munge['plugins-plist'][key] = string;
+ }
+ });
+
+ changes.forEach(function(change) {
+ var target = change.attrib['target'];
+ if (!munge[target]) {
+ munge[target] = {};
+ }
+ if (!munge[target][change.attrib['parent']]) {
+ munge[target][change.attrib['parent']] = {};
+ }
+ var xmls = change.getchildren();
+ xmls.forEach(function(xml) {
+ // 1. stringify each xml
+ var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});
+ // interp vars
+ vars && Object.keys(vars).forEach(function(key) {
+ var regExp = new RegExp("\\$" + key, "g");
+ stringified = stringified.replace(regExp, vars[key]);
+ });
+ // 2. add into munge
+ if (!munge[target][change.attrib['parent']][stringified]) {
+ munge[target][change.attrib['parent']][stringified] = 0;
+ }
+ munge[target][change.attrib['parent']][stringified] += 1;
+ });
+ });
+ return munge;
+ },
+ process:function(plugins_dir, project_dir, platform) {
+ checkPlatform(platform);
+
+ var platform_config = module.exports.get_platform_json(plugins_dir, platform);
+ // Uninstallation first
+ platform_config.prepare_queue.uninstalled.forEach(function(u) {
+ var plugin_dir = path.join(plugins_dir, u.plugin);
+ var plugin_id = u.id;
+ var plugin_vars = platform_config.installed_plugins[plugin_id].vars;
+
+ // get config munge, aka how did this plugin change various config files
+ var config_munge = module.exports.generate_plugin_config_munge(plugin_dir, platform, plugin_vars);
+ // global munge looks at all plugins' changes to config files
+ var global_munge = platform_config.config_munge;
+
+ // Traverse config munge and decrement global munge
+ Object.keys(config_munge).forEach(function(file) {
+ if (file == 'plugins-plist' && platform == 'ios') {
+ if (global_munge[file]) {
+ Object.keys(config_munge[file]).forEach(function(key) {
+ if (global_munge[file][key]) {
+ // prune from old plist if exists
+ var plistfile = glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist'));
+ if (plistfile.length > 0) {
+ plistfile = plistfile[0];
+ // determine if this is a binary or ascii plist and choose the parser
+ // this is temporary until binary support is added to node-plist
+ var pl = (isBinaryPlist(plistfile) ? bplist : plist);
+ var plistObj = pl.parseFileSync(plistfile);
+ delete plistObj.Plugins[key];
+ fs.writeFileSync(plistfile, plist.build(plistObj));
+ }
+ delete global_munge[file][key];
+ }
+ });
+ }
+ } else if (global_munge[file]) {
+ Object.keys(config_munge[file]).forEach(function(selector) {
+ if (global_munge[file][selector]) {
+ Object.keys(config_munge[file][selector]).forEach(function(xml_child) {
+ if (global_munge[file][selector][xml_child]) {
+ global_munge[file][selector][xml_child] -= 1;
+ if (global_munge[file][selector][xml_child] === 0) {
+ // this xml child is no longer necessary, prune it
+ // config.xml referenced in ios config changes refer to the project's config.xml, which we need to glob for.
+ var filepath = resolveConfigFilePath(project_dir, platform, file);
+ if (fs.existsSync(filepath)) {
+ var doc = new et.ElementTree(et.XML(fs.readFileSync(filepath, 'utf-8')));
+ var xml_to_prune = [new et.ElementTree(et.XML(xml_child)).getroot()];
+ if (xml_helpers.pruneXML(doc, xml_to_prune, selector)) {
+ // were good, write out the file!
+ fs.writeFileSync(filepath, doc.write(), 'utf-8');
+ } else {
+ // uh oh
+ throw new Error('pruning xml during config uninstall went bad :(');
+ }
+ }
+ delete global_munge[file][selector][xml_child];
+ }
+ }
+ });
+ }
+ });
+ }
+ });
+ platform_config.config_munge = global_munge;
+
+ // Remove from installed_plugins
+ delete platform_config.installed_plugins[plugin_id]
+ });
+
+ // Empty out uninstalled queue.
+ platform_config.prepare_queue.uninstalled = [];
+
+ // Now handle instalation
+ platform_config.prepare_queue.installed.forEach(function(u) {
+ var plugin_dir = path.join(plugins_dir, u.plugin);
+ var plugin_vars = u.vars;
+ var plugin_id = (new et.ElementTree(et.XML(fs.readFileSync(path.join(plugin_dir, 'plugin.xml'), 'utf-8'))))._root.attrib['id'];
+
+ // get config munge, aka how should this plugin change various config files
+ var config_munge = module.exports.generate_plugin_config_munge(plugin_dir, platform, plugin_vars);
+ // global munge looks at all plugins' changes to config files
+ var global_munge = platform_config.config_munge;
+
+ // Traverse config munge and decrement global munge
+ Object.keys(config_munge).forEach(function(file) {
+ if (!global_munge[file]) {
+ global_munge[file] = {};
+ }
+ Object.keys(config_munge[file]).forEach(function(selector) {
+ if (file == 'config.xml' && platform == 'ios') {
+ var key = selector;
+ if (!global_munge[file][key]) {
+ // this key does not exist, so add it to plist
+ global_munge[file][key] = config_munge[file][key];
+ var plistfile = glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist'));
+ if (plistfile.length > 0) {
+ plistfile = plistfile[0];
+ // determine if this is a binary or ascii plist and choose the parser
+ // this is temporary until binary support is added to node-plist
+ var pl = (isBinaryPlist(plistfile) ? bplist : plist);
+ var plistObj = pl.parseFileSync(plistfile);
+ plistObj.Plugins[key] = config_munge[file][key];
+ fs.writeFileSync(plistfile, plist.build(plistObj));
+ }
+ }
+ } else {
+ if (!global_munge[file][selector]) {
+ global_munge[file][selector] = {};
+ }
+ Object.keys(config_munge[file][selector]).forEach(function(xml_child) {
+ if (!global_munge[file][selector][xml_child]) {
+ global_munge[file][selector][xml_child] = 0;
+ }
+ global_munge[file][selector][xml_child] += 1;
+ if (global_munge[file][selector][xml_child] == 1) {
+ // this xml child is new, graft it (only if config file exists)
+ // config file may be in a place not exactly specified in the target
+ var filepath = resolveConfigFilePath(project_dir, platform, file);
+ if (fs.existsSync(filepath)) {
+ var doc = new et.ElementTree(et.XML(fs.readFileSync(filepath, 'utf-8')));
+ var xml_to_graft = [new et.ElementTree(et.XML(xml_child)).getroot()];
+ if (xml_helpers.graftXML(doc, xml_to_graft, selector)) {
+ // were good, write out the file!
+ fs.writeFileSync(filepath, doc.write(), 'utf-8');
+ } else {
+ // uh oh
+ throw new Error('grafting xml during config install went bad :(');
+ }
+ }
+ }
+ });
+ }
+ });
+ });
+ platform_config.config_munge = global_munge;
+
+ // Move to installed_plugins
+ platform_config.installed_plugins[plugin_id] = plugin_vars;
+ });
+
+ // Empty out installed queue.
+ platform_config.prepare_queue.installed = [];
+
+ // save
+ module.exports.save_platform_json(platform_config, plugins_dir, platform);
+ }
+};
+
+// determine if a plist file is binary
+function isBinaryPlist(filename) {
+ // I wish there was a synchronous way to read only the first 6 bytes of a
+ // file. This is wasteful :/
+ var buf = '' + fs.readFileSync(filename, 'utf8');
+ // binary plists start with a magic header, "bplist"
+ return buf.substring(0, 6) === 'bplist';
+}
- return files;
+function resolveConfigFilePath(project_dir, platform, file) {
+ var filepath = path.join(project_dir, file);
+ if (platform == 'ios' && file == 'config.xml') {
+ filepath = glob.sync(path.join(project_dir, '**', 'config.xml'))[0];
+ }
+ return filepath;
}
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/1395f63a/src/util/xml-helpers.js
----------------------------------------------------------------------
diff --git a/src/util/xml-helpers.js b/src/util/xml-helpers.js
index f270132..e96fc20 100644
--- a/src/util/xml-helpers.js
+++ b/src/util/xml-helpers.js
@@ -25,7 +25,7 @@ var fs = require('fs')
, path = require('path')
, et = require('elementtree');
-exports = module.exports = {
+module.exports = {
moveProjFile: function(origFile, projPath, callback) {
var src = path.resolve(projPath, origFile)
, dest = src.replace('.orig', '');
@@ -63,7 +63,7 @@ exports = module.exports = {
}
for (i; i < one._children.length; i++) {
- if (!exports.equalNodes(one._children[i], two._children[i])) {
+ if (!module.exports.equalNodes(one._children[i], two._children[i])) {
return false;
}
}
@@ -72,7 +72,7 @@ exports = module.exports = {
},
// adds node to doc at selector
- graftXML: function (doc, nodes, selector) {
+ graftXML: function(doc, nodes, selector) {
var ROOT = /^\/([^\/]*)/
, ABSOLUTE = /^\/([^\/]*)\/(.*)/
, parent, tagName, subSelector;
@@ -166,7 +166,7 @@ function uniqueChild(node, parent) {
return true;
} else {
for (i; i < matchingKids.length; i++) {
- if (exports.equalNodes(node, matchingKids[i])) {
+ if (module.exports.equalNodes(node, matchingKids[i])) {
return false;
}
}
[2/2] git commit: a few more specs and a tweak to plugins-plist
handling.
Posted by fi...@apache.org.
a few more specs and a tweak to plugins-plist handling.
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/94be165c
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/94be165c
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/94be165c
Branch: refs/heads/master
Commit: 94be165cca2d9340cdbdd5153c93efe045bfa812
Parents: 1395f63
Author: Fil Maj <ma...@gmail.com>
Authored: Mon Apr 29 00:33:09 2013 -0700
Committer: Fil Maj <ma...@gmail.com>
Committed: Mon Apr 29 00:33:09 2013 -0700
----------------------------------------------------------------------
spec/util/config-changes.spec.js | 38 ++++++++++++++++++++++----------
src/util/config-changes.js | 4 +-
2 files changed, 28 insertions(+), 14 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/94be165c/spec/util/config-changes.spec.js
----------------------------------------------------------------------
diff --git a/spec/util/config-changes.spec.js b/spec/util/config-changes.spec.js
index 1d88bf7..1c2b27a 100644
--- a/spec/util/config-changes.spec.js
+++ b/spec/util/config-changes.spec.js
@@ -4,6 +4,7 @@ var configChanges = require('../../src/util/config-changes'),
os = require('osenv'),
et = require('elementtree'),
path = require('path'),
+ plist = require('plist'),
shell = require('shelljs'),
temp = path.join(os.tmpdir(), 'plugman'),
dummyplugin = path.join(__dirname, '..', 'plugins', 'DummyPlugin'),
@@ -12,6 +13,7 @@ var configChanges = require('../../src/util/config-changes'),
varplugin = path.join(__dirname, '..', 'plugins', 'VariablePlugin'),
android_two_project = path.join(__dirname, '..', 'projects', 'android_two', '*'),
ios_plist_project = path.join(__dirname, '..', 'projects', 'ios-plist', '*'),
+ ios_config_xml = path.join(__dirname, '..', 'projects', 'ios-config-xml', '*'),
plugins_dir = path.join(temp, 'cordova', 'plugins');
var dummy_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(dummyplugin, 'plugin.xml'), 'utf-8')));
@@ -186,6 +188,9 @@ describe('config-changes module', function() {
expect(munge['AndroidManifest.xml']['/manifest']['<uses-permission android:name="ca.filmaj.plugins.permission.C2D_MESSAGE" />']).toBeDefined();
});
it('should special case plugins-plist elements into own property', function() {
+ var munge = configChanges.generate_plugin_config_munge(dummyplugin, 'ios', {});
+ expect(munge['plugins-plist']).toBeDefined();
+ expect(munge['plugins-plist']['com.phonegap.plugins.dummyplugin']).toEqual('DummyPluginCommand');
});
});
@@ -280,24 +285,33 @@ describe('config-changes module', function() {
describe('of <plugins-plist> elements', function() {
it('should only be used in an applicably old cordova-ios projects', function() {
shell.cp('-rf', ios_plist_project, temp);
- });
- it('should not be used in an applicably new cordova-ios projects', function() {
- shell.cp('-rf', ios_config_xml_project, temp);
- var pls = copyArray(plist_els);
+ shell.cp('-rf', dummyplugin, plugins_dir);
+ var cfg = configChanges.get_platform_json(plugins_dir, 'ios');
+ cfg.prepare_queue.installed = [{'plugin':'DummyPlugin', 'vars':{}}];
+ configChanges.save_platform_json(cfg, plugins_dir, 'ios');
+
var spy = spyOn(plist, 'parseFileSync').andReturn({Plugins:{}});
- ios.install(pls, dummy_id, temp, dummyplugin, {});
- expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'config.xml'));
+ configChanges.process(plugins_dir, temp, 'ios');
+ expect(spy).toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'PhoneGap.plist'));
});
});
});
describe(': uninstallation', function() {
- it('should call pruneXML for every config munge it completely removes from the app (every leaf that is decremented to 0)');
- it('should call pruneXML with variables to interpolate if applicable');
- it('should not call pruneXML for a config munge that another plugin depends on');
- it('should not call pruneXML for a config munge targeting a config file that does not exist');
- it('should remove uninstalled plugins from installed plugins list');
- it('should save changes to global config munge after completing an uninstall');
+ it('should call pruneXML for every config munge it completely removes from the app (every leaf that is decremented to 0)', function() {
+ });
+ it('should call pruneXML with variables to interpolate if applicable', function() {
+ });
+ it('should not call pruneXML for a config munge that another plugin depends on', function() {
+ });
+ it('should not call pruneXML for a config munge targeting a config file that does not exist', function() {
+ });
+ it('should remove uninstalled plugins from installed plugins list', function() {
+ });
+ it('should only parse + remove plist plugin entries in applicably old ios projects', function() {
+ });
+ it('should save changes to global config munge after completing an uninstall', function() {
+ });
});
});
});
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/94be165c/src/util/config-changes.js
----------------------------------------------------------------------
diff --git a/src/util/config-changes.js b/src/util/config-changes.js
index 83f4544..534c8d3 100644
--- a/src/util/config-changes.js
+++ b/src/util/config-changes.js
@@ -85,7 +85,7 @@ module.exports = {
var key = pl.attrib['key'];
var value = pl.attrib['string'];
if (!munge['plugins-plist'][key]) {
- munge['plugins-plist'][key] = string;
+ munge['plugins-plist'][key] = value;
}
});
@@ -206,7 +206,7 @@ module.exports = {
global_munge[file] = {};
}
Object.keys(config_munge[file]).forEach(function(selector) {
- if (file == 'config.xml' && platform == 'ios') {
+ if (file == 'plugins-plist' && platform == 'ios') {
var key = selector;
if (!global_munge[file][key]) {
// this key does not exist, so add it to plist