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/05/24 01:41:45 UTC

[24/38] updated android, ios, bb libraries to 2.8.x branch. fixed a few assertions with project changes. removed blackberry support until create script can be finalized.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/packager-utils.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/packager-utils.js b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/packager-utils.js
new file mode 100644
index 0000000..5acad8e
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/packager-utils.js
@@ -0,0 +1,65 @@
+var testData = require("./test-data"),
+    utils = require(testData.libPath + "/packager-utils"),
+    fs = require("fs"),
+    path = require("path"),
+    asciiFile = path.resolve("bin/test/cordova/unit/data/ascii_text.txt"),
+    utf8File = path.resolve("bin/test/cordova/unit/data/utf8_text.txt"),
+    ucs2beFile = path.resolve("bin/test/cordova/unit/data/ucs2be_text.txt"),
+    ucs2leFile = path.resolve("bin/test/cordova/unit/data/ucs2le_text.txt"),
+    helloWorld = "Hello World";
+
+describe("Encoded Buffer data to String", function () {
+    it("Ascii text to String", function () {
+        // Read text file encoded in ascii
+        var fileData = fs.readFileSync(asciiFile);
+        expect(utils.bufferToString(fileData)).toEqual(helloWorld);
+    });
+
+    it("Utf8 text to String", function () {
+        // Read text file encoded in utf8
+        var fileData = fs.readFileSync(utf8File);
+        expect(utils.bufferToString(fileData)).toEqual(helloWorld);
+    });
+
+    it("Ucs2BE text to String", function () {
+        // Read text file encoded in 2 byte Unicode big endian
+        var fileData = fs.readFileSync(ucs2beFile);
+        expect(utils.bufferToString(fileData)).toEqual(helloWorld);
+    });
+
+    it("Ucs2LE text to String", function () {
+        // Read text file encoded in 2 byte Unicode little endian
+        var fileData = fs.readFileSync(ucs2leFile);
+        expect(utils.bufferToString(fileData)).toEqual(helloWorld);
+    });
+});
+
+describe("property wrapper", function () {
+    it("wraps a property of an object in an array", function () {
+        var obj = {
+            prop: "value"
+        };
+
+        utils.wrapPropertyInArray(obj, "prop");
+        expect(obj.prop[0]).toEqual("value");
+    });
+
+    it("does not wrap an array object in an array", function () {
+        var obj = {
+            prop: ["value"]
+        };
+
+        utils.wrapPropertyInArray(obj, "prop");
+        expect(obj.prop[0][0]).not.toEqual("value");
+        expect(obj.prop[0]).toEqual("value");
+    });
+
+    it("does not wrap a property that doesn't esist in the object", function () {
+        var obj = {
+            prop: "value"
+        };
+
+        utils.wrapPropertyInArray(obj, "secondValue");
+        expect(obj.secondValue).not.toBeDefined();
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/packager-validator.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/packager-validator.js b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/packager-validator.js
new file mode 100644
index 0000000..b6ecf4c
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/packager-validator.js
@@ -0,0 +1,277 @@
+var srcPath = __dirname + "/../../../../../templates/project/cordova/lib/",
+    testData = require("./test-data"),
+    testUtilities = require("./test-utilities"),
+    localize = require(srcPath + "localize"),
+    logger = require(srcPath + "logger"),
+    packagerValidator = require(srcPath + "packager-validator"),
+    fs = require("fs"),
+    cmd,
+    extManager = {
+        getExtensionBasenameByFeatureId: function (featureId) {
+            if (featureId && featureId.indexOf("blackberry.") >= 0) {
+                return featureId.substring(featureId.indexOf(".") + 1);
+            } else {
+                return null;
+            }
+        }
+    };
+
+describe("Packager Validator", function () {
+    it("throws an exception when -g set and keys were not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = undefined;
+        session.storepass = "myPassword";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_KEY_FILE", "author.p12"));
+    });
+
+    it("throws an exception when --buildId set and keys were not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = undefined;
+        session.buildId = "100";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_KEY_FILE", "author.p12"));
+    });
+
+    it("throws an exception when -g set and barsigner.csk was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.keystoreCsk = undefined;
+        session.storepass = "myPassword";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_KEY_FILE", "barsigner.csk"));
+    });
+
+    it("throws an exception when --buildId set and barsigner.csk was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.keystoreCsk = undefined;
+        session.buildId = "100";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_KEY_FILE", "barsigner.csk"));
+    });
+
+    it("throws an exception when -g set and barsigner.db was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.keystoreCsk = "c:/barsigner.csk";
+        session.keystoreDb = undefined;
+        session.storepass = "myPassword";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_KEY_FILE", "barsigner.db"));
+    });
+
+    it("throws an exception when --buildId set and barsigner.db was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.keystoreCsk = "c:/barsigner.csk";
+        session.keystoreDb = undefined;
+        session.buildId = "100";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_KEY_FILE", "barsigner.db"));
+    });
+
+    it("generated a warning when Build ID is set in config and keys were not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //Mock the logger
+        spyOn(logger, "warn");
+
+        //setup signing parameters
+        session.keystore = undefined;
+        session.buildId = undefined;
+        configObj.buildId = "100";
+
+        packagerValidator.validateSession(session, configObj);
+        expect(logger.warn).toHaveBeenCalledWith(localize.translate("WARNING_MISSING_SIGNING_KEY_FILE", "author.p12"));
+    });
+
+    it("generated a warning when Build ID is set in config and barsigner.csk was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //Mock the logger
+        spyOn(logger, "warn");
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.keystoreCsk = undefined;
+        session.buildId = undefined;
+        configObj.buildId = "100";
+
+        packagerValidator.validateSession(session, configObj);
+        expect(logger.warn).toHaveBeenCalledWith(localize.translate("WARNING_MISSING_SIGNING_KEY_FILE", "barsigner.csk"));
+    });
+
+    it("generated a warning when Build ID is set in config and barsigner.db was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //Mock the logger
+        spyOn(logger, "warn");
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.keystoreCsk = "c:/barsigner.csk";
+        session.keystoreDb = undefined;
+        session.buildId = undefined;
+        configObj.buildId = "100";
+
+        packagerValidator.validateSession(session, configObj);
+        expect(logger.warn).toHaveBeenCalledWith(localize.translate("WARNING_MISSING_SIGNING_KEY_FILE", "barsigner.db"));
+    });
+
+    it("throws an exception when appdesc was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup appdesc which is not existing
+        session.buildId = undefined;
+        configObj.buildId = undefined;
+        session.appdesc = "c:/bardescriptor.xml";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_APPDESC_NOT_FOUND", "c:/bardescriptor.xml"));
+    });
+
+    it("throws an exception when a password [-g] was set with no buildId", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.keystoreCsk = "c:/barsigner.csk";
+        session.keystoreDb = "c:/barsigner.db";
+        session.storepass = "myPassword";
+        configObj.buildId = undefined;
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_BUILDID"));
+    });
+
+    it("throws an exception when --buildId was set with no password [-g]", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.keystoreCsk = "c:/barsigner.csk";
+        session.keystoreDb = "c:/barsigner.db";
+        session.storepass = undefined;
+        session.buildId = "100";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_PASSWORD"));
+    });
+
+    it("generates a warning when the config contains a build id and no password was provided[-g]", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = "c:/author.p12";
+        session.storepass = undefined;
+        session.buildId = undefined;
+        configObj.buildId = "100";
+
+        //Mock the logger
+        spyOn(logger, "warn");
+
+        packagerValidator.validateSession(session, configObj);
+        expect(logger.warn).toHaveBeenCalledWith(localize.translate("WARNING_SIGNING_PASSWORD_EXPECTED"));
+    });
+});
+
+describe("Packager Validator: validateConfig", function () {
+    it("does not remove APIs that do exist from features whitelist", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = {
+                accessList: [{
+                    features: [{
+                        id: "blackberry.identity",
+                        required: true,
+                        version: "1.0.0.0"
+                    }, {
+                        version: "1.0.0.0",
+                        required: true,
+                        id: "blackberry.event"
+                    }],
+                    uri: "WIDGET_LOCAL",
+                    allowSubDomain: true
+                }]
+            };
+
+        spyOn(fs, "existsSync").andCallFake(function () {
+            //since both of these APIs exist, existsSync would return true
+            return true;
+        });
+
+        packagerValidator.validateConfig(session, configObj, extManager);
+        expect(configObj.accessList[0].features.length).toEqual(2);
+
+
+    });
+
+    it("does not crash if user whitelists a feature with no id", function () {
+        var session = testUtilities.cloneObj(testData.session),
+        configObj = {
+            accessList: [{
+                features: [{
+                    id: "blackberry.identity",
+                    required: true,
+                    version: "1.0.0.0"
+                }, {
+                    version: "1.0.0.0",
+                    required: true,
+                }],
+                uri: "WIDGET_LOCAL",
+                allowSubDomain: true
+            }]
+        };
+        spyOn(logger, "warn");
+
+        spyOn(fs, "existsSync").andCallFake(function () {
+            //since both of these APIs exist, existsSync would return true
+            return true;
+        });
+
+        expect(function () {
+            packagerValidator.validateConfig(session, configObj, extManager);
+        }).not.toThrow();
+    });
+
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/session.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/session.js b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/session.js
new file mode 100644
index 0000000..bbf6eb3
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/session.js
@@ -0,0 +1,209 @@
+var session = require(__dirname + "/../../../../../templates/project/cordova/lib/session"),
+    localize = require(__dirname + "/../../../../../templates/project/cordova/lib/localize"),
+    testUtils = require("./test-utilities"),
+    path = require("path"),
+    fs = require("fs"),
+    wrench = require("wrench"),
+    zipLocation = __dirname + "/../../config.xml";
+
+describe("Session", function () {
+    beforeEach(function () {
+        //Do not create the source folder
+        spyOn(wrench, "mkdirSyncRecursive");
+    });
+
+    it("sets the source directory correctly when specified [-s C:/sampleApp/mySource]", function () {
+        testUtils.mockResolve(path);
+
+        var data = {
+            args: [ 'C:/sampleApp/sample.zip' ],
+            output: 'C:/sampleApp/bin',
+            source: 'C:/sampleApp/mySource'//equivalent to [-s C:/sampleApp/mySource]
+        },
+        result = session.initialize(data);
+
+        expect(result.sourceDir).toEqual(path.normalize("C:/sampleApp/mySource/src"));
+    });
+
+    it("sets the source directory correctly when unspecified [-s] and output path set [-o]", function () {
+        testUtils.mockResolve(path);
+
+        var data = {
+            args: [ 'C:/sampleApp/sample.zip' ],
+            output: 'C:/sampleApp/bin',
+            source: true//equivalent to [-s]
+        },
+        result = session.initialize(data);
+
+        //src folder should be created in output directory
+        expect(result.sourceDir).toEqual(path.normalize("C:/sampleApp/bin/src"));
+    });
+
+    it("sets the source directory correctly when unspecified [-s] and no output path is set", function () {
+        testUtils.mockResolve(path);
+
+        var data = {
+            args: [ zipLocation ],
+            source: true//equivalent to [-s]
+        },
+        result = session.initialize(data);
+
+        //src folder should be created in output directory
+        expect(result.sourceDir).toEqual(path.join(path.dirname(zipLocation), "src"));
+    });
+
+    it("sets the password when specified using -g", function () {
+        var data = {
+            args: [ 'C:/sampleApp/sample.zip' ],
+            output: 'C:/sampleApp/bin',
+            source: 'C:/sampleApp/mySource',//equivalent to [-s C:/sampleApp/mySource]
+            password: 'myPassword'
+        },
+        result = session.initialize(data);
+        expect(result.storepass).toEqual('myPassword');
+    });
+
+    it("does not set the password when not a string", function () {
+        //Commander somtimes improperly sets password to a function, when no value provided
+        var data = {
+            args: [ 'C:/sampleApp/sample.zip' ],
+            output: 'C:/sampleApp/bin',
+            source: 'C:/sampleApp/mySource',//equivalent to [-s C:/sampleApp/mySource]
+            password: function () {}
+        },
+        result = session.initialize(data);
+        expect(result.storepass).toBeUndefined();
+    });
+
+    it("sets the buildId when specified [-buildId]", function () {
+        var data = {
+            args: [ 'C:/sampleApp/sample.zip' ],
+            output: 'C:/sampleApp/bin',
+            source: 'C:/sampleApp/mySource',//equivalent to [-s C:/sampleApp/mySource]
+            buildId: '100'
+        },
+        result = session.initialize(data);
+        expect(result.buildId).toEqual('100');
+    });
+
+    it("sets the appdesc correctly when specified [--appdesc C:/path/bardescriptor.xml]", function () {
+        testUtils.mockResolve(path);
+
+        var data = {
+            args: [ 'C:/sampleApp/sample.zip' ],
+            appdesc: 'C:/path/bardescriptor.xml' //equivalent to [--appdesc C:/path/bardescriptor.xml]
+        },
+        result = session.initialize(data);
+
+        expect(result.appdesc).toEqual(path.normalize("C:/path/bardescriptor.xml"));
+    });
+
+    it("sets the appdesc correctly when not specified", function () {
+        testUtils.mockResolve(path);
+
+        var data = {
+            args: [ 'C:/sampleApp/sample.zip' ]
+        },
+        result = session.initialize(data);
+
+        expect(result.appdesc).toBeUndefined();
+    });
+
+    it("sets the output directory correctly when specified with a relative path [-o myOutput]", function () {
+        var bbwpDir = __dirname + "/../../../../../../",
+        data = {
+            args: [ 'C:/sampleApp/sample.zip' ],
+            output: 'myOutput',
+        },
+        result = session.initialize(data);
+
+        //output should be set to bbwp location + outputFolder
+        expect(result.outputDir).toEqual(path.normalize(path.join(bbwpDir, "myOutput")));
+    });
+
+    describe("get params", function () {
+        beforeEach(function () {
+            delete require.cache[require.resolve(__dirname + "/../../../../../templates/project/cordova/lib/session")];
+            session = require(__dirname + "/../../../../../templates/project/cordova/lib/session");
+        });
+
+        it("get params from external file", function () {
+            var data = {
+                    args: [ 'C:/sampleApp/sample.zip' ],
+                    params: "params.json"
+                },
+                result;
+
+            spyOn(path, "resolve").andReturn("bin/test/cordova/unit/params.json");
+            spyOn(fs, "existsSync").andReturn(true);
+
+            result = session.initialize(data);
+
+            expect(result.getParams("blackberry-signer")).toEqual({
+                "-proxyhost": "abc.com",
+                "-proxyport": "80"
+            });
+        });
+
+        it("get params from non-existent file should throw error", function () {
+            var data = {
+                    args: [ 'C:/sampleApp/sample.zip' ],
+                    params: "blah.json"
+                },
+                result;
+
+            spyOn(fs, "existsSync").andReturn(false);
+
+            result = session.initialize(data);
+
+            expect(function () {
+                result.getParams("blackberry-signer");
+            }).toThrow(localize.translate("EXCEPTION_PARAMS_FILE_NOT_FOUND", path.resolve("blah.json")));
+        });
+
+        it("get params from bad JSON file should throw error", function () {
+            var data = {
+                    args: [ 'C:/sampleApp/sample.zip' ],
+                    params: "params-bad.json"
+                },
+                result;
+
+            spyOn(path, "resolve").andReturn("test/params-bad.json");
+            spyOn(fs, "existsSync").andReturn(true);
+
+            result = session.initialize(data);
+
+            expect(function () {
+                result.getParams("blackberry-signer");
+            }).toThrow(localize.translate("EXCEPTION_PARAMS_FILE_ERROR", path.resolve("blah.json")));
+        });
+    });
+
+    describe("when setting the log level", function () {
+        var logger = require(__dirname + "/../../../../../templates/project/cordova/lib/logger");
+
+        beforeEach(function () {
+            spyOn(logger, "level");
+        });
+
+        it("defaults to verbose with no args", function () {
+            session.initialize({args: []});
+            expect(logger.level).toHaveBeenCalledWith("verbose");
+        });
+
+        it("sets level to verbose", function () {
+            session.initialize({args: [], loglevel: 'verbose'});
+            expect(logger.level).toHaveBeenCalledWith("verbose");
+        });
+
+        it("sets level to warn", function () {
+            session.initialize({args: [], loglevel: 'warn'});
+            expect(logger.level).toHaveBeenCalledWith("warn");
+        });
+
+        it("sets level to error", function () {
+            session.initialize({args: [], loglevel: 'error'});
+            expect(logger.level).toHaveBeenCalledWith("error");
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/signing-helper.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/signing-helper.js b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/signing-helper.js
new file mode 100644
index 0000000..ad91726
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/signing-helper.js
@@ -0,0 +1,420 @@
+var testData = require('./test-data'),
+    signingHelper = require(testData.libPath + '/signing-helper'),
+    localize = require(testData.libPath + '/localize'),
+    pkgrUtils = require(testData.libPath + "/packager-utils"),
+    conf = require(testData.libPath + "/conf"),
+    path = require('path'),
+    fs = require('fs'),
+    os = require('os'),
+    childProcess = require("child_process"),
+    properties = {
+        homepath: "",
+        homedrive: ""
+    },
+    session;
+
+describe("signing-helper", function () {
+
+    describe("on windows", function () {
+
+        beforeEach(function () {
+
+            /* Preserve the value of the HOMEPATH and HOMEDRIVE environment
+             * variables if they are defined. If they are not defined, mark
+             * variable for deletion after the test.*/
+            if (typeof process.env.HOMEPATH === 'undefined') {
+                properties.homepath = "delete";
+            } else {
+                properties.homepath = process.env.HOMEPATH;
+            }
+
+            if (typeof process.env.HOMEDRIVE === 'undefined') {
+                properties.homedrive = "delete";
+            } else {
+                properties.homedrive = process.env.HOMEDRIVE;
+            }
+
+            spyOn(os, "type").andReturn("windows");
+        });
+
+        afterEach(function () {
+
+            /* Restore the value of the HOMEPATH and HOMEDRIVE environment
+             * variables if they are defined. If they are not defined, delete
+             * the property if it was defined in the test.*/
+            if (typeof process.env.HOMEPATH === 'string') {
+                if (properties.homepath === 'delete') {
+                    delete process.env.HOMEPATH;
+                } else {
+                    process.env.HOMEPATH = properties.homepath;
+                }
+            }
+
+            if (typeof process.env.HOMEDRIVE === 'string') {
+                if (properties.homedrive === 'delete') {
+                    delete process.env.HOMEDRIVE;
+                } else {
+                    process.env.HOMEDRIVE = properties.homedrive;
+                }
+            }
+        });
+
+        it("can find keys in Local Settings", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find barsigner.csk in Local Settings", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingHelper.getCskPath();
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find barsigner.db in Local Settings", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingHelper.getDbPath();
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find keys in AppData", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find barsigner.csk in AppData", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingHelper.getCskPath();
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find barsigner.db in AppData", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingHelper.getDbPath();
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find keys in home path", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "C:";
+
+            spyOn(fs, "existsSync").andCallFake(function (p) {
+                return p.indexOf("\\Users\\user") !== -1;
+            });
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toContain("\\Users\\user");
+        });
+
+        it("can find keys on C drive", function () {
+
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "C:";
+
+            spyOn(fs, "existsSync").andCallFake(function (p) {
+                return p.indexOf("C:") !== -1;
+            });
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toContain("C:");
+        });
+
+        it("can find keys on a drive other than C", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "D:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1;
+            });
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toContain("D:");
+        });
+
+        it("can find barsigner.csk on a drive other than C", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "D:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1;
+            });
+
+            var result = signingHelper.getCskPath();
+            expect(result).toContain("D:");
+        });
+
+        it("can find barsigner.db on a drive other than C", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "D:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1;
+            });
+
+            var result = signingHelper.getDbPath();
+            expect(result).toContain("D:");
+        });
+
+        it("can find keys in Local Settings on the correct drive", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "C:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("C:") !== -1 &&
+                        path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toContain("C:");
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find barsigner.csk in Local Settings on the correct drive", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "D:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1 &&
+                        path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingHelper.getCskPath();
+            expect(result).toContain("D:");
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find barsigner.db in Local Settings on the correct drive", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "D:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1 &&
+                        path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingHelper.getDbPath();
+            expect(result).toContain("D:");
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find keys in AppData on the correct drive", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "C:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("C:") !== -1 &&
+                        path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toContain("C:");
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find barsigner.csk in AppData on the correct drive", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "D:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1 &&
+                        path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingHelper.getCskPath();
+            expect(result).toContain("D:");
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find barsigner.db in AppData on the correct drive", function () {
+            process.env.HOMEPATH = "\\Users\\user";
+            process.env.HOMEDRIVE = "D:";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1 &&
+                        path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingHelper.getDbPath();
+            expect(result).toContain("D:");
+            expect(result).toContain("\\AppData");
+        });
+
+        it("returns undefined when keys cannot be found", function () {
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toBeUndefined();
+        });
+
+        it("returns undefined when barsigner.csk cannot be found", function () {
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingHelper.getCskPath();
+            expect(result).toBeUndefined();
+        });
+
+        it("returns undefined when barsigner.db cannot be found", function () {
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingHelper.getDbPath();
+            expect(result).toBeUndefined();
+        });
+    });
+
+    describe("on mac", function () {
+
+        beforeEach(function () {
+            spyOn(os, "type").andReturn("darwin");
+        });
+
+        it("can find keys in the Library folder", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("/Library/Research In Motion/") !== -1;
+            });
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toContain("/Library/Research In Motion/");
+        });
+
+        it("can find barsigner.csk in the Library folder", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("/Library/Research In Motion/") !== -1;
+            });
+
+            var result = signingHelper.getCskPath();
+            expect(result).toContain("/Library/Research In Motion/");
+        });
+
+        it("can find barsigner.db in the Library folder", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("/Library/Research In Motion/") !== -1;
+            });
+
+            var result = signingHelper.getDbPath();
+            expect(result).toContain("/Library/Research In Motion/");
+        });
+
+        it("returns undefined when keys cannot be found", function () {
+
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingHelper.getKeyStorePath();
+            expect(result).toBeUndefined();
+        });
+
+        it("returns undefined when barsigner.csk cannot be found", function () {
+
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingHelper.getCskPath();
+            expect(result).toBeUndefined();
+        });
+
+        it("returns undefined when barsigner.db cannot be found", function () {
+
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingHelper.getDbPath();
+            expect(result).toBeUndefined();
+        });
+    });
+
+    describe("Exec blackberry-signer", function () {
+        var stdoutOn = jasmine.createSpy("stdout on"),
+            stderrOn = jasmine.createSpy("stderr on");
+
+        beforeEach(function () {
+            session = testData.session;
+            session.keystore = "/blah/author.p12";
+            session.storepass = "123";
+            session.barPath = path.normalize("c:/%s/" + "Demo.bar");
+
+            spyOn(childProcess, "spawn").andReturn({
+                stdout: {
+                    on: stdoutOn
+                },
+                stderr: {
+                    on: stderrOn
+                },
+                on: jasmine.createSpy("on").andCallFake(function (event, callback) {
+                    if (callback && typeof callback === "function") {
+                        callback(0);
+                    }
+                })
+            });
+        });
+
+        it("exec blackberry-signer without extra params", function () {
+            var callback = jasmine.createSpy("callback"),
+                cmd = "blackberry-signer" + (pkgrUtils.isWindows() ? ".bat" : "");
+
+            if (!pkgrUtils.isWindows()) {
+                cmd = path.normalize(conf.DEPENDENCIES_TOOLS + "/bin/") + cmd;
+            }
+
+            session.getParams = jasmine.createSpy("session getParams").andReturn(null);
+            signingHelper.execSigner(session, "device", callback);
+            expect(childProcess.spawn).toHaveBeenCalledWith(cmd, ["-keystore", session.keystore, "-storepass", session.storepass, path.resolve("c:/device/Demo.bar")], jasmine.any(Object));
+            expect(stdoutOn).toHaveBeenCalledWith("data", pkgrUtils.handleProcessOutput);
+            expect(stderrOn).toHaveBeenCalledWith("data", pkgrUtils.handleProcessOutput);
+            expect(callback).toHaveBeenCalledWith(0);
+        });
+
+        it("exec blackberry-signer with extra params", function () {
+            var callback = jasmine.createSpy("callback"),
+                cmd = "blackberry-signer" + (pkgrUtils.isWindows() ? ".bat" : "");
+
+            if (!pkgrUtils.isWindows()) {
+                cmd = path.normalize(conf.DEPENDENCIES_TOOLS + "/bin/") + cmd;
+            }
+
+            session.getParams = jasmine.createSpy("session getParams").andReturn({
+                "-proxyhost": "abc.com",
+                "-proxyport": "80"
+            });
+            signingHelper.execSigner(session, "device", callback);
+            expect(childProcess.spawn.mostRecentCall.args[0]).toBe(cmd);
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain("-keystore");
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain(session.keystore);
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain("-storepass");
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain(session.storepass);
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain("-proxyport");
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain("80");
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain("-proxyhost");
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain("abc.com");
+            expect(childProcess.spawn.mostRecentCall.args[1]).toContain(path.resolve("c:/device/Demo.bar"));
+            expect(childProcess.spawn.mostRecentCall.args[1].length).toBe(9);
+            expect(stdoutOn).toHaveBeenCalledWith("data", pkgrUtils.handleProcessOutput);
+            expect(stderrOn).toHaveBeenCalledWith("data", pkgrUtils.handleProcessOutput);
+            expect(callback).toHaveBeenCalledWith(0);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/test-data.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/test-data.js b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/test-data.js
new file mode 100644
index 0000000..11ec335
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/test-data.js
@@ -0,0 +1,87 @@
+var path = require("path"),
+    outputDir = path.resolve("../packager.test"),
+    libPath = __dirname + "/../../../../../templates/project/cordova/lib/",
+    barConf = require(libPath + "/bar-conf"),
+    configPath = path.resolve("test") + "/config.xml";
+
+module.exports = {
+    libPath: libPath,
+    configPath: configPath,
+    session: {
+        "barPath": outputDir + "/%s/" + "Demo.bar",
+        "outputDir": outputDir,
+        "sourceDir": path.resolve(outputDir + "/src"),
+        "sourcePaths": {
+            "ROOT": path.resolve(outputDir + "/src"),
+            "CHROME": path.normalize(path.resolve(outputDir + "/src") + barConf.CHROME),
+            "LIB": path.normalize(path.resolve(outputDir + "/src") + barConf.LIB),
+            "UI": path.normalize(path.resolve(outputDir + "/src") + barConf.UI),
+            "EXT": path.normalize(path.resolve(outputDir + "/src") + barConf.EXT),
+            "PLUGINS": path.normalize(path.resolve(outputDir + "/src") + barConf.PLUGINS),
+            "JNEXT_PLUGINS": path.normalize(path.resolve(outputDir + "/src") + barConf.JNEXT_PLUGINS)
+        },
+        "archivePath": path.resolve("bin/test/cordova/unit/test.zip"),
+        "conf": require(path.resolve(libPath + "/conf")),
+        "targets": ["simulator"],
+        isSigningRequired: function () {
+            return false;
+        },
+        getParams: function () {
+            return null;
+        }
+    },
+    config: {
+        "id": 'Demo',
+        "name": { 'default': 'Demo' },
+        "version": '1.0.0',
+        "author": 'Research In Motion Ltd.',
+        "description": { 'default': 'This is a test!' },
+        "image": 'test.png',
+        "autoOrientation": true,
+        "theme": "default"
+    },
+    accessList: [{
+        uri: "http://google.com",
+        allowSubDomain: false,
+        features: [{
+            id: "blackberry.app",
+            required: true,
+            version: "1.0.0"
+        }, {
+            id: "blackberry.system",
+            required:  true,
+            version: "1.0.0"
+        }]
+    }, {
+        uri: "WIDGET_LOCAL",
+        allowSubDomain: false,
+        features: [{
+            id: "blackberry.system",
+            required: true,
+            version: "1.0.0"
+        }]
+    }],
+    xml2jsConfig: {
+        "@": {
+            "xmlns": " http://www.w3.org/ns/widgets",
+            "xmlns:rim": "http://www.blackberry.com/ns/widgets",
+            "version": "1.0.0",
+            "id": "myID",
+            "rim:header" : "RIM-Widget:rim/widget",
+            "rim:userAgent" : "A Test-User-Agent/(Blackberry-Agent)"
+        },
+        "name": "Demo",
+        "content": {
+            "@": {
+                "src": "local:///startPage.html"
+            }
+        },
+        "author": "Research In Motion Ltd.",
+        "license": {
+            "#": "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.",
+            "@": {
+                "href": "http://www.apache.org/licenses/LICENSE-2.0"
+            }
+        }
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/test-utilities.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/test-utilities.js b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/test-utilities.js
new file mode 100644
index 0000000..6f0dbf9
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/cordova/unit/spec/lib/test-utilities.js
@@ -0,0 +1,82 @@
+var xml2js = require("xml2js");
+
+function getObjectByProperty(array, propertyName, propertyValue) {
+    for (var i = 0; i < array.length; i++) {
+        if (propertyValue === array[i][propertyName]) {
+            return array[i];
+        }
+    }
+}
+
+module.exports = {
+    getAccessListForUri: function (accessListArray, uriValue) {
+        return getObjectByProperty(accessListArray, "uri", uriValue);
+    },
+    
+    getFeatureByID: function (featureArray, featureID) {
+        return getObjectByProperty(featureArray, "id", featureID);
+    },
+    
+    mockResolve: function (path) {
+        //Mock resolve because of a weird issue where resolve would return an 
+        //invalid path on Mac if it cannot find the directory (c:/ doesnt exist on mac)
+        spyOn(path, "resolve").andCallFake(function (to) {
+            if (arguments.length === 2) {
+                //Handle optional from attribute
+                return path.normalize(path.join(arguments[0], arguments[1]));
+            } else {
+                return path.normalize(to);
+            }
+        });
+    },
+    
+    cloneObj: function (obj) {
+        var newObj = (obj instanceof Array) ? [] : {}, i;
+        
+        for (i in obj) {
+            if (i === 'clone') continue;
+            
+            if (obj[i] && typeof obj[i] === "object") {
+                newObj[i] = this.cloneObj(obj[i]);
+            } else {
+                newObj[i] = obj[i];
+            }
+        }
+    
+        return newObj;
+    },
+
+    mockParsing: function (data, error) {
+        spyOn(xml2js, "Parser").andReturn({
+            parseString: function (fileData, callback) {
+                //call callback with no error and altered xml2jsConfig data
+                callback(error, data);
+            }
+        });
+    }
+};
+
+describe("test-utilities", function () {
+    var testUtilities = require("./test-utilities");
+    
+    it("can clone objects using cloneObj", function () {
+        var obj = {
+                A: "A",
+                B: "B",
+                C: { 
+                    CA: "CA",
+                    CB: "CB",
+                    CC: {
+                        CCA: "CCA"
+                    }
+                }
+            },
+            clonedObj = testUtilities.cloneObj(obj);
+        
+        //not the same object
+        expect(clonedObj).not.toBe(obj);
+        
+        //has the same data
+        expect(clonedObj).toEqual(obj);
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/cordova/unit/test.zip
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/cordova/unit/test.zip b/lib/cordova-blackberry/bin/test/cordova/unit/test.zip
new file mode 100644
index 0000000..8859a66
Binary files /dev/null and b/lib/cordova-blackberry/bin/test/cordova/unit/test.zip differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/plugins/Accelerometer/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/plugins/Accelerometer/index.js b/lib/cordova-blackberry/bin/test/plugins/Accelerometer/index.js
new file mode 100644
index 0000000..1819ba7
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/plugins/Accelerometer/index.js
@@ -0,0 +1,81 @@
+/*
+* Copyright 2013 Research In Motion Limited.
+*
+* 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.
+*/
+describe("Accelerometer", function () {
+    var _apiDir = __dirname + "./../../../../plugins/Accelerometer/src/blackberry10/",
+        index,
+        callback,
+        result = {
+            ok: jasmine.createSpy(),
+            error: jasmine.createSpy(),
+            noResult: jasmine.createSpy(),
+            callbackOk: jasmine.createSpy()
+        },
+        motion = {
+            timestamp: 0,
+            accelerationIncludingGravity: {
+                x: 0,
+                y: 0,
+                z: 0
+            }
+        };
+
+    beforeEach(function () {
+        index = require(_apiDir + "index");
+        GLOBAL.window = {
+            removeEventListener: jasmine.createSpy("removeEventListener spy"),
+            addEventListener: jasmine.createSpy("addEventListener spy").andCallFake(function (evt, cb) {
+                callback = cb;
+            })
+        };
+        GLOBAL.PluginResult = function () {
+            return result;
+        };
+    });
+
+    afterEach(function () {
+        index = null;
+        delete GLOBAL.window;
+        delete GLOBAL.PluginResult;
+    });
+
+    describe("start", function () {
+        it("calls noResult and keeps callbacks", function () {
+            index.start();
+            expect(window.addEventListener).toHaveBeenCalled();
+            expect(result.noResult).toHaveBeenCalledWith(true);
+        });
+
+        it("callback calls ok and keeps callbacks", function () {
+            callback(motion);
+            expect(result.callbackOk).toHaveBeenCalled();
+        });
+
+        it("does not call error if already started", function () {
+            index.start();
+            expect(window.removeEventListener).toHaveBeenCalled();
+            expect(window.addEventListener).toHaveBeenCalled();
+            expect(result.error).not.toHaveBeenCalled();
+        });
+    });
+
+    describe("stop", function () {
+        it("calls result ok", function () {
+            index.stop();
+            expect(window.removeEventListener).toHaveBeenCalled();
+            expect(result.ok).toHaveBeenCalledWith("removed");
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/plugins/Battery/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/plugins/Battery/index.js b/lib/cordova-blackberry/bin/test/plugins/Battery/index.js
new file mode 100644
index 0000000..ff82a21
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/plugins/Battery/index.js
@@ -0,0 +1,103 @@
+/*
+* Copyright 2013 Research In Motion Limited.
+*
+* 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.
+*/
+
+describe("Battery", function () {
+
+    var _apiDir = __dirname + "./../../../../plugins/Battery/src/blackberry10/",
+        index,
+        callback,
+        mockPluginResult = {
+            ok: jasmine.createSpy(),
+            error: jasmine.createSpy(),
+            noResult: jasmine.createSpy(),
+            callbackOk: jasmine.createSpy()
+        },
+        noop = function () {},
+        args,
+        env = {
+            webview: {
+                id: 42
+            }
+        };
+
+
+    beforeEach(function () {
+        GLOBAL.window = {
+            qnx: {
+                webplatform: {
+                    device: {
+                        addEventListener: jasmine.createSpy("webplatform.device.addEventListener").andCallFake(function (evt, cb) {
+                            callback = cb;
+                        }),
+                        removeEventListener: jasmine.createSpy("webplatform.device.removeEventListener")
+                    }
+                }
+            }
+        };
+        GLOBAL.PluginResult = function () {
+            return mockPluginResult;
+        };
+        index = require(_apiDir + "index");
+    });
+
+    afterEach(function () {
+        delete GLOBAL.window;
+        delete GLOBAL.PluginResult;
+        delete require.cache[require.resolve(_apiDir + "index")];
+    });
+
+    describe("start", function () {
+
+        it("calls noResult and keeps callbacks", function () {
+            index.start(noop, noop, args, env);
+            expect(window.qnx.webplatform.device.removeEventListener).not.toHaveBeenCalled();
+            expect(window.qnx.webplatform.device.addEventListener).toHaveBeenCalled();
+            expect(mockPluginResult.noResult).toHaveBeenCalledWith(true);
+            expect(mockPluginResult.error).not.toHaveBeenCalled();
+        });
+
+        it("callback calls ok and keeps callbacks", function () {
+            callback("OK");
+            expect(mockPluginResult.callbackOk).toHaveBeenCalledWith("OK", true);
+            expect(mockPluginResult.error).not.toHaveBeenCalled();
+        });
+
+        it("does not call error if already started", function () {
+            index.start(noop, noop, args, env);
+            window.qnx.webplatform.device.addEventListener.reset();
+            mockPluginResult.noResult.reset();
+            index.start(noop, noop, args, env);
+            expect(window.qnx.webplatform.device.removeEventListener).toHaveBeenCalled();
+            expect(window.qnx.webplatform.device.addEventListener).toHaveBeenCalled();
+            expect(mockPluginResult.error).not.toHaveBeenCalled();
+            expect(mockPluginResult.noResult).toHaveBeenCalledWith(true);
+        });
+
+
+    });
+
+    describe("stop", function () {
+
+        it("calls noResult and does not keep callbacks", function () {
+            index.start(noop, noop, args, env);
+            window.qnx.webplatform.device.removeEventListener.reset();
+            index.stop(noop, noop, args, env);
+            expect(window.qnx.webplatform.device.removeEventListener).toHaveBeenCalled();
+            expect(mockPluginResult.noResult).toHaveBeenCalledWith(false);
+        });
+
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/plugins/Camera/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/plugins/Camera/index.js b/lib/cordova-blackberry/bin/test/plugins/Camera/index.js
new file mode 100644
index 0000000..3dcee33
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/plugins/Camera/index.js
@@ -0,0 +1,298 @@
+/*
+* Copyright 2013 Research In Motion Limited.
+*
+* 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.
+*/
+describe("Camera", function () {
+    var _apiDir = __dirname + "./../../../../plugins/Camera/src/blackberry10/",
+        index,
+        mockDone,
+        mockCancel,
+        mockError,
+        mockedEnv = {
+            response: {
+                send: jasmine.createSpy()
+            },
+            webview: {
+                executeJavaScript: jasmine.createSpy()
+            }
+        },
+        PictureSourceType = {
+            PHOTOLIBRARY : 0,    // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
+            CAMERA : 1,          // Take picture from camera
+            SAVEDPHOTOALBUM : 2  // Choose image from picture library (same as PHOTOLIBRARY for Android)
+        },
+        DestinationType = {
+            DATA_URL: 0,         // Return base64 encoded string
+            FILE_URI: 1,         // Return file uri (content://media/external/images/media/2 for Android)
+            NATIVE_URI: 2        // Return native uri (eg. asset-library://... for iOS)
+        },
+        readFail,
+        mockBase64Data = "/9j/4QHRw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
+
+    function mockOpen(options, done, cancel, invoked) {
+        if (!mockError) {
+            invoked();
+        }
+
+        if (mockDone) {
+            done(mockDone.path);
+        } else if (mockCancel) {
+            cancel(mockCancel.reason);
+        } else if (mockError) {
+            invoked(mockError.error);
+        }
+    }
+
+    beforeEach(function () {
+        index = require(_apiDir + "index");
+        mockedEnv.response.send.reset();
+        mockedEnv.webview.executeJavaScript.reset();
+    });
+
+    afterEach(function () {
+        index = null;
+        mockDone = null;
+        mockCancel = null;
+        mockError = null;
+        readFail = false;
+    });
+
+    describe("takePicture", function () {
+        beforeEach(function () {
+            GLOBAL.window = {
+                qnx: {
+                    webplatform: {
+                        getApplication: function () {
+                            return {
+                                cards: {
+                                    camera: {
+                                        open: jasmine.createSpy().andCallFake(mockOpen)
+                                    },
+                                    filePicker: {
+                                        open: jasmine.createSpy().andCallFake(mockOpen)
+                                    }
+                                }
+                            };
+                        },
+                        getController: function () {
+                            return {
+                                setFileSystemSandbox: true
+                            };
+                        }
+                    }
+                },
+                webkitRequestFileSystem: jasmine.createSpy().andCallFake(function (type, size, success, error) {
+                    success({
+                        root: {
+                            getFile: jasmine.createSpy().andCallFake(function (path, options, success, error) {
+                                if (readFail) {
+                                    error({
+                                        code: -1
+                                    });
+                                } else {
+                                    success({
+                                        file: jasmine.createSpy().andCallFake(function (cb) {
+                                            cb();
+                                        })
+                                    });
+                                }
+                            })
+                        }
+                    });
+                })
+            };
+
+            GLOBAL.FileReader = function () {
+                return {
+                    onloadend: jasmine.createSpy(),
+                    readAsDataURL: jasmine.createSpy().andCallFake(function (file) {
+                        this.onloadend.apply({
+                            result: "data:image/jpeg;base64," + mockBase64Data
+                        });
+                    })
+                };
+            };
+
+            GLOBAL.FileError = {
+                NOT_FOUND_ERR: 1,
+                NOT_READABLE_ERR: 4,
+                PATH_EXISTS_ERR: 12,
+                TYPE_MISMATCH_ERR: 11
+            };
+
+            GLOBAL.PluginResult = function (args, env) {};
+            GLOBAL.PluginResult.prototype.callbackOk = jasmine.createSpy();
+            GLOBAL.PluginResult.prototype.callbackError = jasmine.createSpy();
+            GLOBAL.PluginResult.prototype.noResult = jasmine.createSpy();
+        });
+
+        afterEach(function () {
+            delete GLOBAL.window;
+            delete GLOBAL.FileReader;
+            delete GLOBAL.PluginResult;
+        });
+
+        it("calls PluginResult.callbackOk if invoke camera is successful and image doesn't need encoding", function () {
+            mockDone = {
+                path: "/foo/bar/abc.jpg"
+            };
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.FILE_URI.toString(),
+                "2": PictureSourceType.CAMERA.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackOk).toHaveBeenCalledWith("file://" + mockDone.path, false);
+        });
+
+        it("calls PluginResult.callbackOk if invoke camera and base64 encode image is successful", function () {
+            mockDone = {
+                path: "/foo/bar/abc.jpg"
+            };
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.DATA_URL.toString(),
+                "2": PictureSourceType.CAMERA.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackOk).toHaveBeenCalledWith(mockBase64Data, false);
+        });
+
+        it("calls PluginResult.callbackError if invoke camera is successful but base64 encode image failed", function () {
+            mockDone = {
+                path: "/foo/bar/abc.jpg"
+            };
+            readFail = true;
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.DATA_URL.toString(),
+                "2": PictureSourceType.CAMERA.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith("An error occured: Unknown Error", false);
+        });
+
+        it("calls PluginResult.callbackError if invoke camera is cancelled by user", function () {
+            mockCancel = {
+                reason: "done"
+            };
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.FILE_URI.toString(),
+                "2": PictureSourceType.CAMERA.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith(mockCancel.reason, false);
+        });
+
+        it("calls PluginResult.callbackError if invoke camera encounters error", function () {
+            mockError = {
+                error: "Camera error"
+            };
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.FILE_URI.toString(),
+                "2": PictureSourceType.CAMERA.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith(mockError.error, false);
+        });
+
+        it("calls PluginResult.callbackOk if invoke file picker is successful and image doesn't need encoding", function () {
+            mockDone = {
+                path: "/foo/bar/abc.jpg"
+            };
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.FILE_URI.toString(),
+                "2": PictureSourceType.PHOTOLIBRARY.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackOk).toHaveBeenCalledWith("file://" + mockDone.path, false);
+        });
+
+        it("calls PluginResult.callbackOk if invoke file picker and base64 encode image is successful", function () {
+            mockDone = {
+                path: "/foo/bar/abc.jpg"
+            };
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.DATA_URL.toString(),
+                "2": PictureSourceType.PHOTOLIBRARY.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackOk).toHaveBeenCalledWith(mockBase64Data, false);
+        });
+
+        it("calls PluginResult.callbackError if invoke file picker is successful but base64 encode image failed", function () {
+            mockDone = {
+                path: "/foo/bar/abc.jpg"
+            };
+            readFail = true;
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.DATA_URL.toString(),
+                "2": PictureSourceType.PHOTOLIBRARY.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith("An error occured: Unknown Error", false);
+        });
+
+        it("calls PluginResult.callbackError if invoke file picker is cancelled by user", function () {
+            mockCancel = {
+                reason: "cancel"
+            };
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.DATA_URL.toString(),
+                "2": PictureSourceType.PHOTOLIBRARY.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith(mockCancel.reason, false);
+        });
+
+        it("calls PluginResult.callbackError if invoke file picker encounters error", function () {
+            mockError = {
+                error: "File picker error"
+            };
+
+            index.takePicture(undefined, undefined, {
+                "1": DestinationType.DATA_URL.toString(),
+                "2": PictureSourceType.PHOTOLIBRARY.toString(),
+                callbackId: "123"
+            }, mockedEnv);
+
+            expect(PluginResult.prototype.noResult).toHaveBeenCalledWith(true);
+            expect(PluginResult.prototype.callbackError).toHaveBeenCalledWith(mockError.error, false);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/plugins/Contacts/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/plugins/Contacts/index.js b/lib/cordova-blackberry/bin/test/plugins/Contacts/index.js
new file mode 100644
index 0000000..a14d89b
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/plugins/Contacts/index.js
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2013 Research In Motion Limited.
+ *
+ * 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.
+ */
+
+describe("Contacts", function () {
+    var _apiDir = __dirname + "./../../../templates/project/plugins/Contacts/src/blackberry10/",
+        index,
+        ContactError = require(_apiDir + "ContactError"),
+        ContactFindOptions = require(_apiDir + "ContactFindOptions"),
+        result = {
+            noResult: jasmine.createSpy("PluginResult.noResult"),
+            error: jasmine.createSpy("PluginResult.error"),
+            ok: jasmine.createSpy("PluginResult.ok"),
+            callbackError: jasmine.createSpy("PluginResult.callbackError"),
+            callbackOk: jasmine.createSpy("PluginResult.callbackOk"),
+            callbackId: "Contacts12345"
+        };
+
+    beforeEach(function () {
+        GLOBAL.JNEXT = {
+            require: jasmine.createSpy("JNEXT.require").andCallFake(function () {
+                return true;
+            }),
+            createObject: jasmine.createSpy("JNEXT.createObject").andCallFake(function () {
+                return 123;
+            }),
+            invoke: jasmine.createSpy("JNEXT.invoke").andCallFake(function () {
+                return JSON.stringify({
+                    _success: true,
+                    contact: { id: "123" }
+                });
+            }),
+            registerEvents: jasmine.createSpy("JNEXT.regsiterEvents")
+        };
+        GLOBAL.PluginResult = function () {
+            return result;
+        };
+        index = require(_apiDir + "index");
+        GLOBAL.window = {
+            parseInt: jasmine.createSpy("window.parseInt"),
+            isNaN: jasmine.createSpy("window.isNaN")
+        };
+    });
+
+    afterEach(function () {
+        index = null;
+        delete GLOBAL.JNEXT;
+        delete GLOBAL.window;
+        delete GLOBAL.PluginResult;
+    });
+
+    describe("index.search", function () {
+        it("correctly parses args to pass down to native (with filter)", function () {
+            var findOptions = new ContactFindOptions("test"),
+                args = {
+                   "0": encodeURIComponent(JSON.stringify(["phoneNumbers", "emails"])),
+                   "1": encodeURIComponent(JSON.stringify(findOptions)),
+                   "callbackId": encodeURIComponent(JSON.stringify("Contacts12345"))
+                },
+                jnextArgs = {
+                    "_eventId": "Contacts12345",
+                    "fields": ["phoneNumbers", "emails"],
+                    "options": {
+                        "filter": [
+                            { "fieldValue": "test" }
+                        ]
+                    }
+            };
+            index.search(function () {}, function () {}, args, {});
+            expect(JNEXT.invoke).toHaveBeenCalledWith(123, 'find ' + JSON.stringify(jnextArgs));
+            expect(result.noResult).toHaveBeenCalledWith(true);
+        });
+
+        it("correctly parses args to pass down to native (with no filter)", function () {
+            var findOptions = new ContactFindOptions(),
+                args = {
+                   "0": encodeURIComponent(JSON.stringify(["phoneNumbers", "emails"])),
+                   "1": encodeURIComponent(JSON.stringify(findOptions)),
+                   "callbackId": encodeURIComponent(JSON.stringify("Contacts12345"))
+                },
+                jnextArgs = {
+                    "_eventId": "Contacts12345",
+                    "fields": ["phoneNumbers", "emails"],
+                    "options": {
+                        "filter": []
+                    }
+            };
+            index.search(function () {}, function () {}, args, {});
+            expect(JNEXT.invoke).toHaveBeenCalledWith(123, 'find ' + JSON.stringify(jnextArgs));
+            expect(result.noResult).toHaveBeenCalledWith(true);
+        });
+    });
+
+    describe("index.save", function () {
+        it("calls JNEXT save with the correct param if contactId provided", function () {
+            var contactProps = {
+                    "id": "123"
+                },
+                args = {
+                    "0": encodeURIComponent(JSON.stringify(contactProps)),
+                    "callbackId": encodeURIComponent(JSON.stringify("Contacts12345"))
+            };
+
+            window.parseInt.andCallFake(function () {
+                return 123;
+            });
+            index.save(function () {}, function () {}, args, {});
+            expect(JNEXT.invoke).toHaveBeenCalledWith(123, 'save ' + JSON.stringify({"id": 123, "_eventId": "Contacts12345"}));
+            expect(result.noResult).toHaveBeenCalledWith(true);
+        });
+
+        it("properly converts birthdays for native", function () {
+            var contactProps = {
+                    birthday: 1367259069028,
+                },
+                args = {
+                    "0": encodeURIComponent(JSON.stringify(contactProps)),
+                    "callbackId": encodeURIComponent(JSON.stringify("Contacts12345"))
+                },
+                processedArgs = {
+                    "birthday": "Mon Apr 29 2013",
+                    "_eventId": "Contacts12345"
+            };
+
+            index.save(function () {}, function () {}, args, {});
+            expect(JNEXT.invoke).toHaveBeenCalledWith(123, 'save ' + JSON.stringify(processedArgs));
+            expect(result.noResult).toHaveBeenCalledWith(true);
+        });
+
+        it("processes emails contactFeild array", function () {
+            var contactProps = {
+                    "emails": [
+                        { "value": "a@c.com" },
+                        { "type" : "home", "value": "a@b.com" }
+                    ]
+                },
+                args = {
+                    "0": encodeURIComponent(JSON.stringify(contactProps)),
+                    "callbackId": encodeURIComponent(JSON.stringify("Contacts12345"))
+                },
+                processedArgs = {
+                    "emails": [
+                        { "type": "home", "value": "a@c.com" },
+                        { "type": "home", "value": "a@b.com" },
+                    ],
+                    "_eventId": "Contacts12345"
+            };
+            index.save(function () {}, function () {}, args, {});
+            expect(JNEXT.invoke).toHaveBeenCalledWith(123, 'save ' + JSON.stringify(processedArgs));
+            expect(result.noResult).toHaveBeenCalledWith(true);
+
+        });
+
+    });
+
+    describe("index.remove", function () {
+        it("calls JNEXT remove with the correct params for valid contactId", function () {
+            var args = {
+                "0": encodeURIComponent(JSON.stringify(123)),
+                "callbackId": encodeURIComponent(JSON.stringify("Contacts12345"))
+            };
+
+            window.parseInt.andCallFake(function () {
+                return 123;
+            });
+            index.remove(function () {}, function () {}, args, {});
+            expect(JNEXT.invoke).toHaveBeenCalledWith(123, 'remove ' + JSON.stringify({"contactId": 123, "_eventId": "Contacts12345"}));
+            expect(result.noResult).toHaveBeenCalledWith(true);
+        });
+
+        it("calls callbackError if invalid ID", function () {
+            var args = {
+                "0": encodeURIComponent(JSON.stringify("asdfas")),
+                "callbackId": encodeURIComponent(JSON.stringify("Contacts12345"))
+            };
+
+            window.isNaN.andCallFake(function() {
+                return true;
+            });
+            index.remove(function () {}, function () {}, args, {});
+            expect(result.error).toHaveBeenCalledWith(ContactError.UNKNOWN_ERROR);
+            expect(result.noResult).toHaveBeenCalledWith(false);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/plugins/Device/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/plugins/Device/index.js b/lib/cordova-blackberry/bin/test/plugins/Device/index.js
new file mode 100644
index 0000000..4c5fb29
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/plugins/Device/index.js
@@ -0,0 +1,76 @@
+/*
+* Copyright 2013 Research In Motion Limited.
+*
+* 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.
+*/
+
+describe("Device", function () {
+
+    var _apiDir = __dirname + "./../../../../plugins/Device/src/blackberry10/",
+        index,
+        result = {
+            ok: jasmine.createSpy()
+        };
+
+    beforeEach(function () {
+        index = require(_apiDir + "index");
+    });
+
+    afterEach(function () {
+        index = null;
+    });
+
+    describe("getDeviceInfo", function () {
+        beforeEach(function () {
+            GLOBAL.window = {
+                qnx: {
+                    webplatform: {
+                        device: {
+                        }
+                    }
+                }
+            };
+            GLOBAL.PluginResult = function () {
+                return result;
+            };
+        });
+
+        afterEach(function () {
+            delete GLOBAL.window;
+            delete GLOBAL.PluginResult;
+        });
+
+        it("calls ok with the Device info", function () {
+            var mockedDevice = {
+                scmBundle: "1.0.0.0",
+                modelName: "q10",
+                devicePin: (new Date()).getTime()
+            };
+
+            result.ok = jasmine.createSpy().andCallFake(function (deviceInfo) {
+                expect(deviceInfo.platform).toEqual("blackberry10");
+                expect(deviceInfo.version).toEqual(mockedDevice.scmBundle);
+                expect(deviceInfo.model).toEqual(mockedDevice.modelName);
+                expect(deviceInfo.name).toEqual(mockedDevice.modelName);
+                expect(deviceInfo.uuid).toEqual(mockedDevice.devicePin);
+                expect(deviceInfo.cordova).toBeDefined();
+            });
+
+            window.qnx.webplatform.device = mockedDevice;
+
+            index.getDeviceInfo();
+
+            expect(result.ok).toHaveBeenCalled();
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/plugins/Logger/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/plugins/Logger/index.js b/lib/cordova-blackberry/bin/test/plugins/Logger/index.js
new file mode 100644
index 0000000..cec4556
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/plugins/Logger/index.js
@@ -0,0 +1,51 @@
+/*
+* Copyright 2013 Research In Motion Limited.
+*
+* 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.
+*/
+
+describe("Logger", function () {
+
+    var _apiDir = __dirname + "./../../../../plugins/Logger/src/blackberry10/",
+        index,
+        result = {
+            noResult: jasmine.createSpy("noResult")
+        };
+
+    beforeEach(function () {
+        index = require(_apiDir + "index");
+    });
+
+    afterEach(function () {
+        index = null;
+    });
+
+    describe("logLevel", function () {
+        beforeEach(function () {
+            spyOn(console, "log");
+            GLOBAL.PluginResult = function () {
+                return result;
+            };
+        });
+
+        afterEach(function () {
+            delete GLOBAL.PluginResult;
+        });
+
+        it("calls console.log", function () {
+            index.logLevel(function () {}, function () {}, ["%22ERROR%22", "%22message%22"]);
+            expect(console.log).toHaveBeenCalledWith("ERROR: message");
+            expect(result.noResult).toHaveBeenCalledWith(false);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/plugins/NetworkStatus/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/plugins/NetworkStatus/index.js b/lib/cordova-blackberry/bin/test/plugins/NetworkStatus/index.js
new file mode 100644
index 0000000..ac87390
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/plugins/NetworkStatus/index.js
@@ -0,0 +1,118 @@
+/*
+* Copyright 2013 Research In Motion Limited.
+*
+* 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.
+*/
+
+
+describe("NetworkStatus", function () {
+    var _apiDir = __dirname + "./../../../../plugins/NetworkStatus/src/blackberry10/",
+        index,
+        result = {
+            ok: jasmine.createSpy(),
+            error: jasmine.createSpy()
+        };
+
+    beforeEach(function () {
+        index = require(_apiDir + "index");
+    });
+
+    afterEach(function () {
+        index = null;
+    });
+
+    describe("getConnectionInfo", function () {
+        beforeEach(function () {
+            GLOBAL.window = {
+                qnx: {
+                    webplatform: {
+                        device: {
+                        }
+                    }
+                }
+            };
+            GLOBAL.PluginResult = function () {
+                return result;
+            };
+        });
+
+        afterEach(function () {
+            delete GLOBAL.window;
+            delete GLOBAL.PluginResult;
+        });
+
+        function testConnection(expectedResult, mockedType, mockedTechnology) {
+            var mockedDevice = {
+                activeConnection: {
+                    type: mockedType,
+                    technology: mockedTechnology
+                }
+            };
+
+            if (mockedType) {
+                window.qnx.webplatform.device = mockedDevice;
+            }
+
+            index.getConnectionInfo();
+
+            expect(result.ok).toHaveBeenCalledWith(expectedResult);
+            expect(result.error).not.toHaveBeenCalled();
+        }
+
+        it("calls success with a wired connection", function () {
+            testConnection("ethernet", "wired");
+        });
+
+        it("calls success with a wifi connection", function () {
+            testConnection("wifi", "wifi");
+        });
+
+        it("calls success with no connection", function () {
+            testConnection("none", "none");
+        });
+
+        it("calls success with a cellular edge connection", function () {
+            testConnection("2g", "cellular", "edge");
+        });
+
+        it("calls success with a cellular gsm connection", function () {
+            testConnection("2g", "cellular", "gsm");
+        });
+
+        it("calls success with a cellular evdo connection", function () {
+            testConnection("3g", "cellular", "evdo");
+        });
+
+        it("calls success with a cellular umts connection", function () {
+            testConnection("3g", "cellular", "umts");
+        });
+
+        it("calls success with a lte connection", function () {
+            testConnection("4g", "cellular", "lte");
+        });
+
+        it("calls success with a cellular connection", function () {
+            testConnection("cellular", "cellular");
+        });
+
+        it("defaults to none if no connection is found", function () {
+            testConnection("none");
+        });
+
+        it("defaults to unknown if connection type doesn't exist", function () {
+            testConnection("unknown", "fakeConnectionType");
+        });
+
+    });
+
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-blackberry/bin/test/plugins/Notification/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/bin/test/plugins/Notification/index.js b/lib/cordova-blackberry/bin/test/plugins/Notification/index.js
new file mode 100644
index 0000000..8d10e25
--- /dev/null
+++ b/lib/cordova-blackberry/bin/test/plugins/Notification/index.js
@@ -0,0 +1,119 @@
+/*
+* Copyright 2013 Research In Motion Limited.
+*
+* 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.
+*/
+
+function mockAndTestDialog(htmlmessage, title, dialogType, buttonLabel) {
+    GLOBAL.qnx = {
+        webplatform: {
+            getWebViews: function () {
+                var webviews = [{}, {},
+                    {//overlayWebview
+                        dialog: {
+                            show: function(messageObj, callback) {
+                                expect(messageObj.title).toEqual(title);
+                                expect(messageObj.htmlmessage).toEqual(htmlmessage);
+                                expect(messageObj.dialogType).toEqual(dialogType);
+                                expect(messageObj.optionalButtons).toEqual(buttonLabel);
+                                expect(typeof callback).toEqual("function");
+                            }
+                        }
+                    }];
+                return webviews;
+            }
+        }
+    };
+
+}
+
+describe("Notification", function () {
+    var _apiDir = __dirname + "./../../../../plugins/Notification/src/blackberry10/",
+    index,
+    success = function() {},
+    fail = function() {},
+    result = {
+        error: jasmine.createSpy(),
+        noResult: jasmine.createSpy()
+    },
+    args = {
+        0: "%22Dialog%20message.%22",
+        1: "%22Dialog%20Title%22",
+        2: "%22Continue%22"
+    };
+
+    beforeEach(function () {
+        index = require(_apiDir + "index");
+
+        GLOBAL.PluginResult = function () {
+            return result;
+        };
+    });
+
+    afterEach(function () {
+        delete require.cache[require.resolve(_apiDir + "index")];
+        delete GLOBAL.qnx;
+        delete GLOBAL.PluginResult;
+    });
+
+    describe("alert", function () {
+        it("fails with invalid number of args", function () {
+            index.alert(success, fail, {}, {});
+            expect(result.error).toHaveBeenCalledWith("Notification action - alert arguments not found.");
+        });
+
+        it("calls dialog.show with correct params", function () {
+            mockAndTestDialog("Dialog message.", "Dialog Title", "CustomAsk", ["Continue"]);
+            index.alert(success, fail, args, {});
+            expect(result.noResult).toHaveBeenCalled();
+        });
+    });
+
+    describe("confirm", function () {
+        it("fails with invalid number of args", function () {
+            index.confirm(success, fail, {}, {});
+            expect(result.error).toHaveBeenCalledWith("Notification action - confirm arguments not found.");
+        });
+
+        it("calls dialog.show with correct params", function () {
+            mockAndTestDialog("Dialog message.", "Dialog Title", "CustomAsk", ["Continue"]);
+            index.confirm(success, fail, args, {});
+            expect(result.noResult).toHaveBeenCalled();
+        });
+
+        it("calls dialog.show with correct params [deprecated buttonArg]", function () {
+            var args = {
+                0: "%22Dialog%20message.%22",
+                1: "%22Dialog%20Title%22",
+                2: "%22Continue,Cancel%22"
+            };
+
+            mockAndTestDialog("Dialog message.", "Dialog Title", "CustomAsk", ["Continue", "Cancel"]);
+            index.confirm(success, fail, args, {});
+            expect(result.noResult).toHaveBeenCalled();
+        });
+    });
+
+    describe("prompt", function () {
+        it("fails with invalid number of args", function () {
+            index.prompt(success, fail, {}, {});
+            expect(result.error).toHaveBeenCalledWith("Notification action - prompt arguments not found.");
+        });
+
+        it("calls dialog.show with correct params", function () {
+            mockAndTestDialog("Dialog message.", "Dialog Title", "JavaScriptPrompt", ["Continue"]);
+            index.prompt(success, fail, args, {});
+            expect(result.noResult).toHaveBeenCalled();
+        });
+    });
+});