You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by st...@apache.org on 2017/09/07 03:22:33 UTC

[2/4] cordova-lib git commit: CB-12361: added more unit tests for plugin add.spec.js

CB-12361: added more unit tests for plugin add.spec.js


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

Branch: refs/heads/master
Commit: d3e5fd7bae6758c5829650ce3f1b650dc5fd36cb
Parents: 7e136ab
Author: Steve Gill <st...@gmail.com>
Authored: Thu Jun 29 12:01:03 2017 -0600
Committer: Steve Gill <st...@gmail.com>
Committed: Wed Sep 6 00:32:59 2017 -0700

----------------------------------------------------------------------
 spec/cordova/plugin/add.spec.js | 442 +++++++++++++++++++++++++++++------
 spec/cordova/prepare.spec.js    |   2 +-
 src/cordova/plugin/add.js       |  32 ++-
 3 files changed, 396 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/d3e5fd7b/spec/cordova/plugin/add.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova/plugin/add.spec.js b/spec/cordova/plugin/add.spec.js
index 9abced8..f148e8e 100644
--- a/spec/cordova/plugin/add.spec.js
+++ b/spec/cordova/plugin/add.spec.js
@@ -22,47 +22,59 @@
 
 var Q = require('q');
 var rewire = require('rewire');
-var add = rewire('../../src/cordova/plugin/add');
-var plugman = require('../../src/plugman/plugman');
-var cordova_util = require('../../src/cordova/util');
+var add = rewire('../../../src/cordova/plugin/add');
+var plugman = require('../../../src/plugman/plugman');
+var cordova_util = require('../../../src/cordova/util');
 var path = require('path');
 var fs = require('fs');
-var config = require('../../src/cordova/config');
+var config = require('../../../src/cordova/config');
+var events = require('cordova-common').events;
+var registry = require('../../../src/plugman/registry/registry');
+var plugin_util = require('../../../src/cordova/plugin/util');
 
 describe('cordova/plugin/add', function () {
     var projectRoot = '/some/path';
     var hook_mock;
-    var cfg_parser_mock = function () {};
+    var Cfg_parser_mock = function () {};
     var cfg_parser_revert_mock;
     var plugin_info_provider_mock = function () {};
     var plugin_info_provider_revert_mock;
     var plugin_info;
+    var package_json_mock;
 
     beforeEach(function () {
         hook_mock = jasmine.createSpyObj('hooks runner mock', ['fire']);
         hook_mock.fire.and.returnValue(Q());
-        cfg_parser_mock.prototype = jasmine.createSpyObj('config parser prototype mock', ['getPlugin', 'removePlugin', 'addPlugin', 'write']);
-        cfg_parser_mock.prototype.getPlugin.and.callFake(function (pluginId) {});
-        cfg_parser_mock.prototype.removePlugin.and.callFake(function () {});
-        cfg_parser_mock.prototype.addPlugin.and.callFake(function () {});
-        cfg_parser_mock.prototype.write.and.callFake(function () {});
-        cfg_parser_revert_mock = add.__set__('ConfigParser', cfg_parser_mock);
+        Cfg_parser_mock.prototype = jasmine.createSpyObj('config parser prototype mock', ['getPlugin', 'removePlugin', 'addPlugin', 'write']);
+        /* eslint-disable */
+        Cfg_parser_mock.prototype.getPlugin;
+        Cfg_parser_mock.prototype.removePlugin;
+        Cfg_parser_mock.prototype.addPlugin;
+        Cfg_parser_mock.prototype.write;
+        /* eslint-enable */
+        cfg_parser_revert_mock = add.__set__('ConfigParser', Cfg_parser_mock);
         plugin_info = jasmine.createSpyObj('pluginInfo', ['getPreferences']);
         plugin_info.getPreferences.and.returnValue({});
-        plugin_info.dir = 'some\plugin\path';
+        plugin_info.dir = 'some\\plugin\\path';
         plugin_info.id = 'cordova-plugin-device';
         plugin_info.version = '1.0.0';
         plugin_info_provider_mock.prototype = jasmine.createSpyObj('plugin info provider mock', ['get']);
         plugin_info_provider_mock.prototype.get = function (directory) {
-            console.log('fake get');
-            //id version dir getPreferences() engines engines.cordovaDependencies name versions
+            // id version dir getPreferences() engines engines.cordovaDependencies name versions
             return plugin_info;
         };
         plugin_info_provider_revert_mock = add.__set__('PluginInfoProvider', plugin_info_provider_mock);
-        spyOn(fs,'existsSync').and.returnValue(false);
-        spyOn(fs,'writeFileSync').and.returnValue(false);
-        //requireNoCache is used to require package.json
-        spyOn(cordova_util, 'requireNoCache').and.returnValue({});
+        spyOn(fs, 'existsSync').and.returnValue(false);
+        spyOn(fs, 'writeFileSync').and.returnValue(false);
+        package_json_mock = jasmine.createSpyObj('package json mock', ['cordova', 'dependencies']);
+        package_json_mock.cordova = {};
+        package_json_mock.dependencies = {};
+        // requireNoCache is used to require package.json
+        spyOn(cordova_util, 'requireNoCache').and.returnValue(package_json_mock);
+        spyOn(events, 'emit');
+        spyOn(registry, 'info').and.returnValue(Q());
+        spyOn(add, 'getFetchVersion').and.returnValue(Q());
+        spyOn(plugin_util, 'saveToConfigXmlOn').and.returnValue(true);
     });
     afterEach(function () {
         cfg_parser_revert_mock();
@@ -71,7 +83,7 @@ describe('cordova/plugin/add', function () {
     describe('main method', function () {
 
         beforeEach(function () {
-            spyOn(add,'determinePluginTarget').and.callFake(function(projRoot, cfg, target, opts) {
+            spyOn(add, 'determinePluginTarget').and.callFake(function (projRoot, cfg, target, opts) {
                 return Q(target);
             });
             spyOn(plugman, 'fetch').and.callFake(function (target, pluginPath, opts) {
@@ -81,7 +93,7 @@ describe('cordova/plugin/add', function () {
             spyOn(cordova_util, 'listPlatforms').and.callFake(function () {
                 return ['android'];
             });
-            spyOn(cordova_util,'findPlugins').and.returnValue({plugins:[]});
+            spyOn(cordova_util, 'findPlugins').and.returnValue({plugins: []});
             spyOn(config, 'read').and.returnValue({});
         });
         describe('error/warning conditions', function () {
@@ -93,7 +105,7 @@ describe('cordova/plugin/add', function () {
                 }).done(done);
             });
             it('should error out if any mandatory plugin variables are not provided', function (done) {
-                plugin_info.getPreferences.and.returnValue({'some':undefined});
+                plugin_info.getPreferences.and.returnValue({'some': undefined});
 
                 add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device']}).then(function () {
                     fail('success handler unexpectedly invoked');
@@ -104,8 +116,8 @@ describe('cordova/plugin/add', function () {
         });
         describe('happy path', function () {
             it('should fire the before_plugin_add hook', function (done) {
-                add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device']}).then(function () {
-                    expect(hook_mock.fire).toHaveBeenCalledWith('before_plugin_add', jasmine.any(Object)); 
+                add(projectRoot, hook_mock, {plugins: ['https://github.com/apache/cordova-plugin-device'], save: true}).then(function () {
+                    expect(hook_mock.fire).toHaveBeenCalledWith('before_plugin_add', jasmine.any(Object));
                 }).fail(function (e) {
                     fail('fail handler unexpectedly invoked');
                     console.log(e);
@@ -113,24 +125,24 @@ describe('cordova/plugin/add', function () {
             });
             it('should determine where to fetch a plugin from using determinePluginTarget and invoke plugman.fetch with the resolved target', function (done) {
                 add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device']}).then(function () {
-                    expect(add.determinePluginTarget).toHaveBeenCalledWith(projectRoot, jasmine.any(Object), 'cordova-plugin-device', jasmine.any(Object)); 
-                    expect(plugman.fetch).toHaveBeenCalledWith('cordova-plugin-device', path.join(projectRoot,'plugins'), jasmine.any(Object));
+                    expect(add.determinePluginTarget).toHaveBeenCalledWith(projectRoot, jasmine.any(Object), 'cordova-plugin-device', jasmine.any(Object));
+                    expect(plugman.fetch).toHaveBeenCalledWith('cordova-plugin-device', path.join(projectRoot, 'plugins'), jasmine.any(Object));
                 }).fail(function (e) {
                     fail('fail handler unexpectedly invoked');
                     console.log(e);
                 }).done(done);
             });
             it('should retrieve any variables for the plugin from config.xml and add them as cli variables only when the variables were not already provided via options', function (done) {
-                var cfg_plugin_variables = {'some':'variable'};
-                cfg_parser_mock.prototype.getPlugin.and.callFake(function (plugin_id) {
+                var cfg_plugin_variables = {'some': 'variable'};
+                Cfg_parser_mock.prototype.getPlugin.and.callFake(function (plugin_id) {
                     return {'variables': cfg_plugin_variables};
                 });
                 add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device']}).then(function () {
-                    //confirm cli_variables are undefind
-                    expect(add.determinePluginTarget.calls.argsFor(0)[3]['variables']).toBeUndefined;
+                    // confirm cli_variables are undefind
+                    expect(add.determinePluginTarget).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), jasmine.anything(), jasmine.objectContaining({'variables': undefined}));
                     expect(plugman.install).toHaveBeenCalled();
-                    //check that the plugin variables from config.xml got added to cli_variables
-                    expect(plugman.install.calls.argsFor(0)[4]['cli_variables']).toEqual(cfg_plugin_variables);
+                    // check that the plugin variables from config.xml got added to cli_variables
+                    expect(plugman.install).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), jasmine.anything(), jasmine.anything(), jasmine.objectContaining({'cli_variables': cfg_plugin_variables}));
                 }).fail(function (e) {
                     fail('fail handler unexpectedly invoked');
                     console.log(e);
@@ -145,44 +157,50 @@ describe('cordova/plugin/add', function () {
                 }).done(done);
             });
             it('should save plugin variable information to package.json file (if exists)', function (done) {
-                var cli_plugin_variables = {'some':'variable'};
+                var cli_plugin_variables = {'some': 'variable'};
 
-                fs.existsSync.and.returnValue(true);
+                fs.existsSync.and.callFake(function (file_path) {
+                    if (path.basename(file_path) === 'package.json') {
+                        return true;
+                    } else {
+                        return false;
+                    }
+                });
 
-                add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device'], cli_variables: cli_plugin_variables, save:'true'}).then(function () {
-                    expect(fs.writeFileSync).toHaveBeenCalledWith(jasmine.any(String), JSON.stringify({'cordova':{'plugins':{'cordova-plugin-device':cli_plugin_variables}}}, null, 2), 'utf8');
+                add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device'], cli_variables: cli_plugin_variables, save: 'true'}).then(function () {
+                    expect(fs.writeFileSync).toHaveBeenCalledWith(jasmine.any(String), JSON.stringify({'cordova': {'plugins': {'cordova-plugin-device': cli_plugin_variables}}, 'dependencies': {}}, null, 2), 'utf8');
                 }).fail(function (e) {
                     fail('fail handler unexpectedly invoked');
                     console.log(e);
                 }).done(done);
             });
             it('should overwrite plugin information in config.xml after a successful installation', function (done) {
-                var cfg_plugin_variables = {'some':'variable'};
-                var cli_plugin_variables = {'some':'new_variable'};
-                cfg_parser_mock.prototype.getPlugin.and.callFake(function (plugin_id) {
+                var cfg_plugin_variables = {'some': 'variable'};
+                var cli_plugin_variables = {'some': 'new_variable'};
+                Cfg_parser_mock.prototype.getPlugin.and.callFake(function (plugin_id) {
                     return {'variables': cfg_plugin_variables};
                 });
 
-                add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device'], cli_variables: cli_plugin_variables, save:'true'}).then(function () {
-                    //confirm cli_variables got passed through
-                    expect(add.determinePluginTarget.calls.argsFor(0)[3]['variables']).toEqual(cli_plugin_variables);
-                    //check that the plugin variables from config.xml got added to cli_variables
-                    expect(plugman.install.calls.argsFor(0)[4]['cli_variables']).toEqual(cli_plugin_variables);
-                    expect(cfg_parser_mock.prototype.removePlugin).toHaveBeenCalledWith('cordova-plugin-device');
-                    expect(cfg_parser_mock.prototype.addPlugin).toHaveBeenCalledWith(jasmine.any(Object), cli_plugin_variables);
-                    expect(cfg_parser_mock.prototype.write).toHaveBeenCalled();
+                add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device'], cli_variables: cli_plugin_variables, save: 'true'}).then(function () {
+                    // confirm cli_variables got passed through
+                    expect(add.determinePluginTarget).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), jasmine.anything(), jasmine.objectContaining({'variables': cli_plugin_variables}));
+                    // check that the plugin variables from config.xml got added to cli_variables
+                    expect(plugman.install).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), jasmine.anything(), jasmine.anything(), jasmine.objectContaining({'cli_variables': cli_plugin_variables}));
+                    expect(Cfg_parser_mock.prototype.removePlugin).toHaveBeenCalledWith('cordova-plugin-device');
+                    expect(Cfg_parser_mock.prototype.addPlugin).toHaveBeenCalledWith(jasmine.any(Object), cli_plugin_variables);
+                    expect(Cfg_parser_mock.prototype.write).toHaveBeenCalled();
                 }).fail(function (e) {
                     fail('fail handler unexpectedly invoked');
                     console.log(e);
                 }).done(done);
             });
-            //can't test the following due to inline require of preparePlatforms
+            // can't test the following due to inline require of preparePlatforms
             xit('should invoke preparePlatforms if plugman.install returned a falsey value', function () {
                 plugman.install.and.returnValue(false);
             });
             it('should fire after_plugin_add hook', function (done) {
                 add(projectRoot, hook_mock, {plugins: ['cordova-plugin-device']}).then(function () {
-                    expect(hook_mock.fire).toHaveBeenCalledWith('after_plugin_add', jasmine.any(Object)); 
+                    expect(hook_mock.fire).toHaveBeenCalledWith('after_plugin_add', jasmine.any(Object));
                 }).fail(function (e) {
                     fail('fail handler unexpectedly invoked');
                     console.log(e);
@@ -191,41 +209,327 @@ describe('cordova/plugin/add', function () {
         });
     });
     describe('determinePluginTarget helper method', function () {
-        it('should return the target directly if the target is pluginSpec-parseable');
-        it('should return the target directly if the target is a URL');
-        it('should return the target directly if the target is a directory');
-        it('should retrieve plugin version from package.json (if exists)');
-        it('should retrieve plugin version from config.xml as a last resort');
-        describe('with fetchOptions.save=true, writing out plugin versions retrieved from config files (either config.xml or package.json)', function () {
-            it('should write out URL-as-plugin-spec to package.json');
-            it('should write out directory-as-plugin-spec to package.json');
-        });
-        it('should return plugin version retrieved from package.json or config.xml if it is a URL');
-        it('should return plugin version retrieved from package.json or config.xml if it is a directory');
-        it('should return plugin version retrieved from package.json or config.xml if it has a scope');
-        it('should return plugin-id@plugin-version if retrieved from package.json or config.xml ');
+        beforeEach(function () {
+            spyOn(cordova_util, 'isDirectory').and.returnValue(false);
+            spyOn(add, 'getVersionFromConfigFile').and.returnValue(undefined);
+            package_json_mock.dependencies['cordova-plugin-device'] = undefined;
+        });
+        afterEach(function () {
+        });
+        it('should return the target directly if the target is pluginSpec-parseable', function (done) {
+            add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device@1.0.0', {}).then(function (target) {
+                expect(target).toEqual('cordova-plugin-device@1.0.0');
+            }).fail(function (e) {
+                fail('fail handler unexpectedly invoked');
+                console.log(e);
+            }).done(done);
+        });
+        it('should return the target directly if the target is a URL', function (done) {
+            add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'https://github.com/apache/cordova-plugin-device.git', {}).then(function (target) {
+                expect(target).toEqual('https://github.com/apache/cordova-plugin-device.git');
+            }).fail(function (e) {
+                fail('fail handler unexpectedly invoked');
+                console.log(e);
+            }).done(done);
+        });
+        it('should return the target directly if the target is a directory', function (done) {
+            cordova_util.isDirectory.and.returnValue(true);
+            add.determinePluginTarget(projectRoot, Cfg_parser_mock, '../some/dir/cordova-plugin-device', {}).then(function (target) {
+                expect(target).toEqual('../some/dir/cordova-plugin-device');
+            }).fail(function (e) {
+                fail('fail handler unexpectedly invoked');
+                console.log(e);
+            }).done(done);
+        });
+        it('should retrieve plugin version from package.json (if exists)', function (done) {
+            fs.existsSync.and.callFake(function (file_path) {
+                if (path.basename(file_path) === 'package.json') {
+                    return true;
+                } else {
+                    return false;
+                }
+            });
+
+            package_json_mock.dependencies['cordova-plugin-device'] = '^1.0.0';
+
+            add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {}).then(function (target) {
+                expect(target).toEqual('cordova-plugin-device@^1.0.0');
+            }).fail(function (e) {
+                fail('fail handler unexpectedly invoked');
+                console.log(e);
+            }).done(done);
+        });
+        it('should retrieve plugin version from config.xml as a last resort', function (done) {
+            add.getVersionFromConfigFile.and.returnValue('~1.0.0');
+            add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {}).then(function (target) {
+                expect(add.getVersionFromConfigFile).toHaveBeenCalled();
+                expect(target).toEqual('cordova-plugin-device@~1.0.0');
+            }).fail(function (e) {
+                fail('fail handler unexpectedly invoked');
+                console.log(e);
+            }).done(done);
+        });
+        it('should return plugin version retrieved from package.json or config.xml if it is a URL', function (done) {
+            fs.existsSync.and.callFake(function (file_path) {
+                if (path.basename(file_path) === 'package.json') {
+                    return true;
+                } else {
+                    return false;
+                }
+            });
+
+            package_json_mock.dependencies['cordova-plugin-device'] = 'https://github.com/apache/cordova-plugin-device.git';
+
+            add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {}).then(function (target) {
+                expect(target).toEqual('https://github.com/apache/cordova-plugin-device.git');
+            }).fail(function (e) {
+                fail('fail handler unexpectedly invoked');
+                console.log(e);
+            }).done(done);
+        });
+        it('should return plugin version retrieved from package.json or config.xml if it is a directory', function (done) {
+            fs.existsSync.and.callFake(function (file_path) {
+                if (path.basename(file_path) === 'package.json') {
+                    return true;
+                } else {
+                    return false;
+                }
+            });
+            cordova_util.isDirectory.and.callFake(function (dir) {
+                if (dir === '../some/dir/cordova-plugin-device') {
+                    return true;
+                }
+                return false;
+            });
+            package_json_mock.dependencies['cordova-plugin-device'] = '../some/dir/cordova-plugin-device';
+
+            add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {}).then(function (target) {
+                expect(target).toEqual('../some/dir/cordova-plugin-device');
+            }).fail(function (e) {
+                fail('fail handler unexpectedly invoked');
+                console.log(e);
+            }).done(done);
+        });
+        it('should return plugin version retrieved from package.json or config.xml if it has a scope', function (done) {
+            fs.existsSync.and.callFake(function (file_path) {
+                if (path.basename(file_path) === 'package.json') {
+                    return true;
+                } else {
+                    return false;
+                }
+            });
+
+            package_json_mock.dependencies['@cordova/cordova-plugin-device'] = '^1.0.0';
+
+            add.determinePluginTarget(projectRoot, Cfg_parser_mock, '@cordova/cordova-plugin-device', {}).then(function (target) {
+                expect(target).toEqual('@cordova/cordova-plugin-device@^1.0.0');
+            }).fail(function (e) {
+                fail('fail handler unexpectedly invoked');
+                console.log(e);
+            }).done(done);
+        });
         describe('with no version inferred from config files or provided plugin target', function () {
             describe('when searchpath or noregistry flag is provided', function () {
-                it('should end up just returning the target passed in');
+                it('should end up just returning the target passed in case of searchpath', function (done) {
+                    add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {'searchpath': 'some/path'})
+                        .then(function (target) {
+                            expect(target).toEqual('cordova-plugin-device');
+                            expect(events.emit).toHaveBeenCalledWith('verbose', 'Not checking npm info for cordova-plugin-device because searchpath or noregistry flag was given');
+                        }).fail(function (e) {
+                            fail('fail handler unexpectedly invoked');
+                            console.log(e);
+                        }).done(done);
+                });
+                it('should end up just returning the target passed in case of noregistry', function (done) {
+                    add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {'noregistry': true})
+                        .then(function (target) {
+                            expect(target).toEqual('cordova-plugin-device');
+                            expect(events.emit).toHaveBeenCalledWith('verbose', 'Not checking npm info for cordova-plugin-device because searchpath or noregistry flag was given');
+                        }).fail(function (e) {
+                            fail('fail handler unexpectedly invoked');
+                            console.log(e);
+                        }).done(done);
+                });
             });
             describe('when registry/npm is to be used (neither searchpath nor noregistry flag is provided)', function () {
-                it('should retrieve plugin info via registry.info');
-                it('should feed registry.info plugin information into getFetchVersion');
-                it('should return the target as plugin-id@fetched-version');
+                it('should retrieve plugin info via registry.info', function (done) {
+                    add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {})
+                        .then(function (target) {
+                            expect(registry.info).toHaveBeenCalledWith(['cordova-plugin-device']);
+                            expect(events.emit).toHaveBeenCalledWith('verbose', 'Attempting to use npm info for cordova-plugin-device to choose a compatible release');
+                            expect(target).toEqual('cordova-plugin-device');
+                        }).fail(function (e) {
+                            fail('fail handler unexpectedly invoked');
+                            console.log(e);
+                        }).done(done);
+                });
+                it('should feed registry.info plugin information into getFetchVersion', function (done) {
+                    registry.info.and.returnValue(Q({'plugin': 'info'}));
+                    add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {})
+                        .then(function (target) {
+                            expect(registry.info).toHaveBeenCalled();
+                            expect(add.getFetchVersion).toHaveBeenCalledWith(jasmine.anything(), {'plugin': 'info'}, jasmine.anything());
+                            expect(target).toEqual('cordova-plugin-device');
+                            expect(events.emit).toHaveBeenCalledWith('verbose', 'Attempting to use npm info for cordova-plugin-device to choose a compatible release');
+                        }).fail(function (e) {
+                            fail('fail handler unexpectedly invoked');
+                            console.log(e);
+                        }).done(done);
+                });
+                it('should return the target as plugin-id@fetched-version', function (done) {
+                    add.getFetchVersion.and.returnValue(Q('1.0.0'));
+                    add.determinePluginTarget(projectRoot, Cfg_parser_mock, 'cordova-plugin-device', {})
+                        .then(function (target) {
+                            expect(registry.info).toHaveBeenCalled();
+                            expect(add.getFetchVersion).toHaveBeenCalled();
+                            expect(target).toEqual('cordova-plugin-device@1.0.0');
+                            expect(events.emit).toHaveBeenCalledWith('verbose', 'Attempting to use npm info for cordova-plugin-device to choose a compatible release');
+                        }).fail(function (e) {
+                            fail('fail handler unexpectedly invoked');
+                            console.log(e);
+                        }).done(done);
+                });
             });
         });
     });
+    describe('parseSource helper method', function () {
+        it('should return target when url is passed', function (done) {
+            expect(add.parseSource('https://github.com/apache/cordova-plugin-device', {})).toEqual('https://github.com/apache/cordova-plugin-device');
+            done();
+        });
+        it('should return target when local path is passed', function (done) {
+            fs.existsSync.and.returnValue(true);
+            expect(add.parseSource('../cordova-plugin-device', {})).toEqual('../cordova-plugin-device');
+            done();
+        });
+        it('should return null when target is not url or local path', function (done) {
+            expect(add.parseSource('cordova-plugin-device', {})).toEqual(null);
+            done();
+        });
+    });
+    describe('getVersionFromConfigFile helper method', function () {
+        it('should return spec', function (done) {
+            var fakePlugin = {};
+            fakePlugin.name = '';
+            fakePlugin.spec = '1.0.0';
+            fakePlugin.variables = {};
+
+            Cfg_parser_mock.prototype.getPlugin.and.returnValue(fakePlugin);
+            var new_cfg = new Cfg_parser_mock();
+            expect(add.getVersionFromConfigFile('cordova-plugin-device', new_cfg)).toEqual('1.0.0');
+            done();
+        });
+    });
+
     // TODO: reorganize these tests once the logic here is understood! -filmaj
     // TODO: rewrite the tests from integration-tests/plugin_fetch.spec.js to here.
-    describe('TODO! unit tests to replace integration-tests/plugin_fetch.spec.js', function () {
+    describe('unit tests to replace integration-tests/plugin_fetch.spec.js', function () {
         describe('getFetchVersion helper method', function () {
-            it('should resolve with null if plugin info does not contain engines and engines.cordovaDependencies properties');
-            it('should retrieve platform version info via getInstalledPlatformsWithVersions and feed that information into determinePluginVersionToFetch');
+            var pluginInfo;
+
+            beforeEach(function () {
+                add.getFetchVersion.and.callThrough();
+                pluginInfo = {};
+                spyOn(plugin_util, 'getInstalledPlugins').and.returnValue([]);
+                spyOn(cordova_util, 'getInstalledPlatformsWithVersions').and.returnValue(Q({}));
+                spyOn(add, 'determinePluginVersionToFetch');
+            });
+            it('should resolve with null if plugin info does not contain engines and engines.cordovaDependencies properties', function (done) {
+                add.getFetchVersion(projectRoot, pluginInfo, '7.0.0')
+                    .then(function (value) {
+                        expect(value).toBe(null);
+                    }).fail(function (e) {
+                        fail('fail handler unexpectedly invoked');
+                        console.log(e);
+                    }).done(done);
+            });
+            it('should retrieve installed plugins and installed platforms version and feed that information into determinePluginVersionToFetch', function (done) {
+                plugin_util.getInstalledPlugins.and.returnValue([{'id': 'cordova-plugin-camera', 'version': '2.0.0'}]);
+                cordova_util.getInstalledPlatformsWithVersions.and.returnValue(Q({'android': '6.0.0'}));
+                pluginInfo.engines = {};
+                pluginInfo.engines.cordovaDependencies = {'^1.0.0': {'cordova': '>7.0.0'}};
+                add.getFetchVersion(projectRoot, pluginInfo, '7.0.0')
+                    .then(function () {
+                        expect(plugin_util.getInstalledPlugins).toHaveBeenCalledWith(projectRoot);
+                        expect(cordova_util.getInstalledPlatformsWithVersions).toHaveBeenCalledWith(projectRoot);
+                        expect(add.determinePluginVersionToFetch).toHaveBeenCalledWith(pluginInfo, {'cordova-plugin-camera': '2.0.0'}, {'android': '6.0.0'}, '7.0.0');
+                    }).fail(function (e) {
+                        fail('fail handler unexpectedly invoked');
+                        console.log(e);
+                    }).done(done);
+            });
         });
         describe('determinePluginVersionToFetch helper method', function () {
-            it('should return null if no valid semver versions exist and no upperbound constraints were placed');
+            var pluginInfo;
+            beforeEach(function () {
+                pluginInfo = {};
+                pluginInfo.name = 'cordova-plugin-device';
+                pluginInfo.versions = ['0.1.0', '1.0.0', '1.5.0', '2.0.0'];
+                spyOn(add, 'getFailedRequirements').and.returnValue([]);
+                spyOn(add, 'findVersion').and.returnValue(null);
+                spyOn(add, 'listUnmetRequirements');
+            });
+            it('should return null if no valid semver versions exist and no upperbound constraints were placed', function (done) {
+                pluginInfo.engines = {};
+                pluginInfo.engines.cordovaDependencies = {'^1.0.0': {'cordova': '<7.0.0'}};
+                expect(add.determinePluginVersionToFetch(pluginInfo, {}, {}, '7.0.0')).toBe(null);
+                done();
+            });
+
+            xit('', function (done) {
+                pluginInfo.engines = {};
+                pluginInfo.engines.cordovaDependencies = {'1.0.0': {'cordova': '<7.0.0'}, '<3.0.0': {'cordova': '>=7.0.0'}};
+                expect(add.determinePluginVersionToFetch(pluginInfo, {}, {}, '7.0.0')).toBe(null);
+                done();
+            });
+
+            it('should return highest version of plugin available based on constraints', function (done) {
+                pluginInfo.engines = {};
+                pluginInfo.engines.cordovaDependencies = {'1.0.0': {'cordova': '<7.0.0'}, '<3.0.0': {'cordova': '>=7.0.0'}};
+                expect(add.determinePluginVersionToFetch(pluginInfo, {}, {}, '7.0.0')).toEqual('2.0.0');
+                done();
+            });
         });
         describe('getFailedRequirements helper method', function () {
+            it('should return an empty array if no failed requirements', function (done) {
+                expect(add.getFailedRequirements({'cordova': '>=7.0.0'}, {}, {}, '7.0.0').length).toBe(0);
+                done();
+            });
+            it('should return an empty array if invalid dependency constraint', function (done) {
+                expect(add.getFailedRequirements({1: 'wrong'}, {}, {}, '7.0.0').length).toBe(0);
+                expect(events.emit).toHaveBeenCalledWith('verbose', jasmine.stringMatching(/Ignoring invalid plugin dependency constraint/));
+                done();
+            });
+            it('should return an array with failed plugin requirements ', function (done) {
+                expect(add.getFailedRequirements({'cordova-plugin-camera': '>1.0.0'}, {'cordova-plugin-camera': '1.0.0'}, {}, '7.0.0')).toEqual([{ dependency: 'cordova-plugin-camera', installed: '1.0.0', required: '>1.0.0' }]);
+                done();
+            });
+            it('should return an array with failed cordova requirements ', function (done) {
+                expect(add.getFailedRequirements({'cordova': '>=7.0.0'}, {}, {}, '6.5.0')).toEqual([{ dependency: 'cordova', installed: '6.5.0', required: '>=7.0.0' }]);
+                done();
+            });
+            it('should return an array with failed platform requirements ', function (done) {
+                expect(add.getFailedRequirements({'cordova-android': '>=6.0.0'}, {}, {'android': '5.5.0'}, '7.0.0')).toEqual([{ dependency: 'cordova-android', installed: '5.5.0', required: '>=6.0.0' }]);
+                done();
+            });
+        });
+        describe('listUnmetRequirements helper method', function () {
+            it('should emit warnings for failed requirements', function (done) {
+                add.listUnmetRequirements('cordova-plugin-device', [{ dependency: 'cordova', installed: '6.5.0', required: '>=7.0.0' }]);
+                expect(events.emit).toHaveBeenCalledWith('warn', 'Unmet project requirements for latest version of cordova-plugin-device:');
+                expect(events.emit).toHaveBeenCalledWith('warn', '    cordova (6.5.0 in project, >=7.0.0 required)');
+                done();
+            });
+        });
+        describe('findVersion helper method', function () {
+            it('should return null if version is not in array', function (done) {
+                expect(add.findVersion(['0.0.1', '1.0.0', '2.0.0'], '0.0.0')).toEqual(null);
+                done();
+            });
+            it('should return the version if it is in the array', function (done) {
+                expect(add.findVersion(['0.0.1', '1.0.0', '2.0.0'], '1.0.0')).toEqual('1.0.0');
+                done();
+            });
         });
     });
 });

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/d3e5fd7b/spec/cordova/prepare.spec.js
----------------------------------------------------------------------
diff --git a/spec/cordova/prepare.spec.js b/spec/cordova/prepare.spec.js
index c15497a..d89742c 100644
--- a/spec/cordova/prepare.spec.js
+++ b/spec/cordova/prepare.spec.js
@@ -88,7 +88,7 @@ describe('cordova/prepare', function () {
                     throw new Error('cdProjectRoot error');
                 });
 
-                prepare({}).then(function () { 
+                prepare({}).then(function () {
                     fail('unexpected success handler invoked');
                 }).fail(function (e) {
                     expect(e.message).toBe('cdProjectRoot error');

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/d3e5fd7b/src/cordova/plugin/add.js
----------------------------------------------------------------------
diff --git a/src/cordova/plugin/add.js b/src/cordova/plugin/add.js
index 5db0805..056a451 100644
--- a/src/cordova/plugin/add.js
+++ b/src/cordova/plugin/add.js
@@ -20,7 +20,7 @@
 var cordova_util = require('../util');
 var plugin_util = require('./util');
 var config = require('../config');
-var pkgJson = require('../../../package.json');
+var cordova_pkgJson = require('../../../package.json');
 var pluginSpec = require('./plugin_spec_parser');
 var plugman = require('../../plugman/plugman');
 var registry = require('../../plugman/registry/registry');
@@ -213,8 +213,9 @@ function determinePluginTarget (projectRoot, cfg, target, fetchOptions) {
         return Q(target);
     }
     // Require project pkgJson.
+    var pkgJson;
     var pkgJsonPath = path.join(projectRoot, 'package.json');
-    var cordovaVersion = pkgJson.version;
+    var cordovaVersion = cordova_pkgJson.version;
     if (fs.existsSync(pkgJsonPath)) {
         pkgJson = cordova_util.requireNoCache(pkgJsonPath);
     }
@@ -234,8 +235,10 @@ function determinePluginTarget (projectRoot, cfg, target, fetchOptions) {
 
     // If parsedSpec.version satisfies pkgJson version, no writing to pkg.json. Only write when
     // it does not satisfy.
-    if (parsedSpec.version) {
+    /* if (parsedSpec.version) {
+        
         if (pkgJson && pkgJson.dependencies && pkgJson.dependencies[parsedSpec.package]) {
+            //it can only go in here if 
             var noSymbolVersion = parsedSpec.version;
             if (parsedSpec.version.charAt(0) === '^' || parsedSpec.version.charAt(0) === '~') {
                 noSymbolVersion = parsedSpec.version.slice(1);
@@ -255,7 +258,7 @@ function determinePluginTarget (projectRoot, cfg, target, fetchOptions) {
                 }
             }
         }
-    }
+    } */
 
     if (cordova_util.isUrl(parsedSpec.version) || cordova_util.isDirectory(parsedSpec.version) || pluginSpec.parse(parsedSpec.version).scope) {
         return Q(parsedSpec.version);
@@ -265,7 +268,6 @@ function determinePluginTarget (projectRoot, cfg, target, fetchOptions) {
     if (parsedSpec.version) {
         return Q(id + '@' + parsedSpec.version);
     }
-
     // If no version is given at all and we are fetching from npm, we
     // can attempt to use the Cordova dependencies the plugin lists in
     // their package.json
@@ -277,8 +279,8 @@ function determinePluginTarget (projectRoot, cfg, target, fetchOptions) {
     } else {
         events.emit('verbose', 'Not checking npm info for ' + parsedSpec.package + ' because searchpath or noregistry flag was given');
     }
-
-    // TODO: whoa wat
+    // if noregistry or searchpath are true, then shouldUseNpmInfo is false. Just return target
+    // else run `npm info` on the target via registry.info so we could get engines elemenent in package.json. Pass that info to getFetchVersion which determines the correct plugin to fetch based on engines element.
     return (shouldUseNpmInfo ? registry.info([id])
         .then(function (pluginInfo) {
             return module.exports.getFetchVersion(projectRoot, pluginInfo, cordovaVersion);
@@ -332,13 +334,12 @@ function getVersionFromConfigFile (plugin, cfg) {
 function getFetchVersion (projectRoot, pluginInfo, cordovaVersion) {
     // Figure out the project requirements
     if (pluginInfo.engines && pluginInfo.engines.cordovaDependencies) {
+        // grab array of already installed plugins
         var pluginList = plugin_util.getInstalledPlugins(projectRoot);
         var pluginMap = {};
-
         pluginList.forEach(function (plugin) {
             pluginMap[plugin.id] = plugin.version;
         });
-
         return cordova_util.getInstalledPlatformsWithVersions(projectRoot)
             .then(function (platformVersions) {
                 return module.exports.determinePluginVersionToFetch(
@@ -389,6 +390,7 @@ function determinePluginVersionToFetch (pluginInfo, pluginMap, platformMap, cord
     // platform version, plugin version. The below for loop: what version is it
     // iterating over? plugin version? please clarify the variable name.
     for (var version in engine) {
+        // if a single version && less than latest
         if (semver.valid(semver.clean(version)) && semver.lte(version, latest)) {
             versions.push(version);
         } else {
@@ -409,7 +411,6 @@ function determinePluginVersionToFetch (pluginInfo, pluginMap, platformMap, cord
             }
         }
     }
-
     // If there were no valid requirements, we fall back to old behavior
     if (!upperBoundExists && versions.length === 0) {
         events.emit('verbose', 'Ignoring ' + name + ' cordovaDependencies entry because it did not contain any valid plugin version entries');
@@ -491,6 +492,13 @@ function determinePluginVersionToFetch (pluginInfo, pluginMap, platformMap, cord
     return null;
 }
 
+/*
+ * Returns an array full of objects of dependency requirements that are not met.
+ * reqs - CordovaDependency object from plugin's package.json
+ * pluginMap - previously installed plugins in the project
+ * platformMap - previously installed platforms in the project
+ * cordovaVersion - version of cordova being used
+ */
 function getFailedRequirements (reqs, pluginMap, platformMap, cordovaVersion) {
     var failed = [];
     var version = cordovaVersion;
@@ -502,6 +510,7 @@ function getFailedRequirements (reqs, pluginMap, platformMap, cordovaVersion) {
     for (var req in reqs) {
         if (reqs.hasOwnProperty(req) && typeof req === 'string' && semver.validRange(reqs[req])) {
             var badInstalledVersion = null;
+            // remove potential whitespace
             var trimmedReq = req.trim();
 
             if (pluginMap[trimmedReq] && !semver.satisfies(pluginMap[trimmedReq], reqs[req])) {
@@ -531,6 +540,8 @@ function getFailedRequirements (reqs, pluginMap, platformMap, cordovaVersion) {
     return failed;
 }
 
+// return the version if it is in the versions array
+// return null if the version doesn't exist in the array
 function findVersion (versions, version) {
     var cleanedVersion = semver.clean(version);
     for (var i = 0; i < versions.length; i++) {
@@ -541,6 +552,7 @@ function findVersion (versions, version) {
     return null;
 }
 
+// emits warnings to users of failed dependnecy requirements in their projects
 function listUnmetRequirements (name, failedRequirements) {
     events.emit('warn', 'Unmet project requirements for latest version of ' + name + ':');
 


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