You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by lo...@apache.org on 2013/05/07 17:24:24 UTC

[19/51] [partial] [BlackBerry10] Added support for new platform

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/native-packager.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/native-packager.js b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/native-packager.js
new file mode 100644
index 0000000..a4ab63e
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/native-packager.js
@@ -0,0 +1,347 @@
+var path = require("path"),
+    util = require("util"),
+    fs = require("fs"),
+    childProcess = require("child_process"),
+    wrench = require("wrench"),
+    srcPath = __dirname + "/../../../../../templates/project/cordova/lib/",
+    nativePkgr = require(srcPath + "/native-packager"),
+    pkgrUtils = require(srcPath + "/packager-utils"),
+    testUtils = require("./test-utilities"),
+    testData = require("./test-data"),
+    logger = require(srcPath + "logger"),
+    localize = require(srcPath + "/localize"),
+    conf = require(srcPath + "./conf"),
+    callback,
+    config,
+    session,
+    target,
+    result,
+    orgDebugEnabled,
+    orgDebugTokenPath,
+    NL = pkgrUtils.isWindows() ? "\r\n" : "\n";
+
+describe("Native packager", function () {
+    beforeEach(function () {
+        callback = jasmine.createSpy();
+        config = testData.config;
+        session = testData.session;
+        target = session.targets[0];
+        result = {
+            stdout: {
+                on: jasmine.createSpy()
+            },
+            stderr: {
+                on: jasmine.createSpy()
+            },
+            on: function (eventName, callback) {
+                callback(0);
+            }
+        };
+
+        // Store original debug token setting and later restore them in afterEach
+        // to be able to test positive and negative cases of each.
+        orgDebugEnabled = session.debug;
+        orgDebugTokenPath = session.conf.DEBUG_TOKEN;
+
+        spyOn(wrench, "readdirSyncRecursive").andReturn(["abc", "xyz"]);
+        spyOn(fs, "statSync").andReturn({
+            isDirectory: function () {
+                return false;
+            }
+        });
+        spyOn(fs, "writeFileSync");
+        spyOn(childProcess, "spawn").andReturn(result);
+        spyOn(fs, "existsSync").andCallFake(function (path) {
+            //Return true if this is the bbndk folder check
+            return path.indexOf("bbndk") !== -1;
+        });
+    });
+
+    afterEach(function () {
+        session.debug = orgDebugEnabled;
+        session.conf.DEBUG_TOKEN = orgDebugTokenPath;
+    });
+
+    it("should not display empty messages in logger", function () {
+        spyOn(pkgrUtils, "writeFile");
+        spyOn(logger, "warn");
+        spyOn(logger, "error");
+        spyOn(logger, "info");
+
+        nativePkgr.exec(session, target, testData.config, callback);
+
+        expect(logger.warn).not.toHaveBeenCalledWith("");
+        expect(logger.error).not.toHaveBeenCalledWith("");
+        expect(logger.info).not.toHaveBeenCalledWith("");
+    });
+
+    it("shows debug token warning when path to file is not valid", function () {
+        spyOn(pkgrUtils, "writeFile");
+        spyOn(logger, "warn");
+
+        session.debug = true;
+        //Current time will ensure that the file doesn't exist.
+        session.conf.DEBUG_TOKEN = new Date().getTime() + ".bar";
+
+        nativePkgr.exec(session, target, testData.config, callback);
+
+        expect(logger.warn).toHaveBeenCalledWith(localize.translate("EXCEPTION_DEBUG_TOKEN_NOT_FOUND"));
+    });
+
+    it("won't show debug token warning when -d options wasn't provided", function () {
+        spyOn(pkgrUtils, "writeFile");
+        spyOn(logger, "warn");
+
+        session.debug = false;
+        //Current time will ensure that the file doesn't exist.
+        session.conf.DEBUG_TOKEN = new Date().getTime() + ".bar";
+
+        nativePkgr.exec(session, target, testData.config, callback);
+
+        expect(logger.warn).not.toHaveBeenCalled();
+    });
+
+    it("shows debug token warning when debug token not a .bar file", function () {
+        spyOn(pkgrUtils, "writeFile");
+        spyOn(logger, "warn");
+
+        session.debug = true;
+        //Current time will ensure that the file doesn't exist.
+        session.conf.DEBUG_TOKEN = new Date().getTime() + ".xyz";
+
+        nativePkgr.exec(session, target, testData.config, callback);
+        expect(logger.warn).toHaveBeenCalledWith(localize.translate("EXCEPTION_DEBUG_TOKEN_WRONG_FILE_EXTENSION"));
+    });
+
+    it("exec blackberry-nativepackager", function () {
+        var bbTabletXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+            "<qnx><id>" + config.id + "</id>" +
+            "<versionNumber>" + config.version + "</versionNumber>" +
+            "<author>" + config.author + "</author>" +
+            "<asset entry=\"true\" type=\"qnx/elf\">wwe</asset>" +
+            "<asset>abc</asset>" +
+            "<asset>xyz</asset>" +
+            "<entryPointType>Qnx/WebKit</entryPointType>" +
+            "<cascadesTheme>" + config.theme + "</cascadesTheme>" +
+            "<initialWindow><systemChrome>none</systemChrome><transparent>true</transparent><autoOrients>true</autoOrients></initialWindow>",
+            bbTabletXML2 = "<permission system=\"true\">run_native</permission>" +
+            "<permission system=\"false\">access_internet</permission>" +
+            "<name>" + config.name['default'] + "</name>" +
+            "<description>" + config.description['default'] + "</description></qnx>",
+            cmd = path.normalize(session.conf.DEPENDENCIES_TOOLS + "/bin/blackberry-nativepackager" + (pkgrUtils.isWindows() ? ".bat" : ""));
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (sourceDir, outputDir, data) {
+            expect(sourceDir).toEqual(session.sourceDir);
+            expect(outputDir).toEqual(conf.BAR_DESCRIPTOR);
+
+            //We have to validate the xml data in 2 chucks, because the WEBWORKS_VERSION env variable
+            //has a different value for SCM builds and we can't mock the webworks-info file
+            expect(data).toContain(bbTabletXML);
+            expect(data).toContain(bbTabletXML2);
+        });
+        nativePkgr.exec(session, target, testData.config, callback);
+
+        expect(fs.writeFileSync).toHaveBeenCalledWith(jasmine.any(String), jasmine.any(String));
+        expect(childProcess.spawn).toHaveBeenCalledWith(cmd, ["@options"], {"cwd": session.sourceDir, "env": process.env});
+        expect(callback).toHaveBeenCalledWith(0);
+    });
+
+    it("makes sure slog2 logging is enabled in debug mode", function () {
+        var tabletXMLEntry = "<env value=\"slog2\" var=\"CONSOLE_MODE\"></env>";
+
+        //Silence the logger during unit tests
+        spyOn(logger, "warn").andCallFake(function () { });
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (sourceDir, outputDir, data) {
+            expect(data).toContain(tabletXMLEntry);
+        });
+
+        session.debug = true;
+        nativePkgr.exec(session, target, testData.config, callback);
+    });
+
+    it("makes sure slog2 logging is not enabled when not in debug mode", function () {
+        var tabletXMLEntry = "<env value=\"slog2\" var=\"CONSOLE_MODE\"></env>";
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (sourceDir, outputDir, data) {
+            expect(data).not.toContain(tabletXMLEntry);
+        });
+
+        session.debug = false;
+        nativePkgr.exec(session, target, testData.config, callback);
+    });
+
+    it("can process application name", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.name = {"default": "API Smoke Test"};
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain("<name>API Smoke Test</name>");
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+
+    });
+
+    it("can process localized application name", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.name = {"FR": "API Smoke Test - FR"};
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain('<name><text xml:lang="FR">API Smoke Test - FR</text></name>');
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+    });
+
+    it("can process mutiple application names", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.name = {
+            "default": "API Smoke Test",
+            "EN": "API Smoke Test - EN",
+            "FR": "API Smoke Test - FR"
+        };
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain('<name>API Smoke Test<text xml:lang="EN">API Smoke Test - EN</text><text xml:lang="FR">API Smoke Test - FR</text></name>');
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+    });
+
+    it("can process application description", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.description = {"default": "My app description"};
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain("<description>My app description</description>");
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+
+    });
+
+    it("can process localized application description", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.description = {"FR": "My app description - FR"};
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain('<description><text xml:lang="FR">My app description - FR</text></description>');
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+    });
+
+    it("can process mutiple application descriptions", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.description = {
+            "default": "My app description",
+            "EN": "My app description - EN",
+            "FR": "My app description - FR"
+        };
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain('<description>My app description<text xml:lang="EN">My app description - EN</text><text xml:lang="FR">My app description - FR</text></description>');
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+    });
+
+    it("can process permissions with no attributes", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.permissions = ['read_device_identifying_information'];
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain("<permission>read_device_identifying_information</permission>");
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+
+    });
+
+    it("can process permissions with attributes", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.permissions = [{ '#': 'systemPerm', '@': {"system": "true"}}];
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain("<permission system=\"true\">systemPerm</permission>");
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+
+    });
+
+    it("adds the mandatory permissions for webworks", function () {
+        var config = testUtils.cloneObj(testData.config);
+        config.permissions = [];
+
+        spyOn(pkgrUtils, "writeFile").andCallFake(function (fileLocation, fileName, fileData) {
+            expect(fileData).toContain("<permission system=\"false\">access_internet</permission>");
+            expect(fileData).toContain("<permission system=\"true\">run_native</permission>");
+        });
+
+        nativePkgr.exec(session, target, config, callback);
+
+    });
+
+    it("omits -devMode when signing and specifying -d", function () {
+        testUtils.mockResolve(path);
+        spyOn(pkgrUtils, "writeFile");
+
+        var session = testUtils.cloneObj(testData.session),
+            config = testUtils.cloneObj(testData.config),
+            target = "device",
+            optionsFile = "-package" + NL +
+                "-buildId" + NL +
+                "100" + NL +
+                path.normalize("c:/device/Demo.bar") + NL +
+                "-barVersion" + NL +
+                "1.5" + NL +
+                "-C" + NL +
+                path.normalize("c:/src/") + NL +
+                conf.BAR_DESCRIPTOR + NL +
+                path.normalize("c:/src/abc") + NL +
+                path.normalize("c:/src/xyz") + NL;
+
+        //Set signing params [-g --buildId]
+        session.keystore = path.normalize("c:/author.p12");
+        session.storepass = "password";
+        config.buildId = "100";
+
+        session.barPath = path.normalize("c:/%s/" + "Demo.bar");
+        session.sourceDir = path.normalize("c:/src/");
+        session.isSigningRequired = function () {
+            return true;
+        };
+
+        //Set -d param
+        session.debug = "";
+
+        nativePkgr.exec(session, target, config, callback);
+
+        //options file should NOT contain -devMode
+        expect(fs.writeFileSync).toHaveBeenCalledWith(jasmine.any(String), optionsFile);
+    });
+
+    it("exec blackberry-nativepackager with additional params", function () {
+        var cmd = path.normalize(session.conf.DEPENDENCIES_TOOLS + "/bin/blackberry-nativepackager" + (pkgrUtils.isWindows() ? ".bat" : ""));
+        spyOn(pkgrUtils, "writeFile");
+
+        session.getParams = jasmine.createSpy("session getParams").andReturn({
+            "-installApp": "",
+            "-device": "192.168.1.114",
+            "-password": "abc"
+        });
+
+        nativePkgr.exec(session, "simulator", testData.config, callback);
+
+        expect(fs.writeFileSync.mostRecentCall.args[0]).toBe(path.resolve(session.sourceDir, "options"));
+        expect(fs.writeFileSync.mostRecentCall.args[1]).toContain("-package" + NL);
+        expect(fs.writeFileSync.mostRecentCall.args[1]).toContain("-password" + NL);
+        expect(fs.writeFileSync.mostRecentCall.args[1]).toContain("abc" + NL);
+        expect(fs.writeFileSync.mostRecentCall.args[1]).toContain("-device" + NL);
+        expect(fs.writeFileSync.mostRecentCall.args[1]).toContain("192.168.1.114" + NL);
+        expect(fs.writeFileSync.mostRecentCall.args[1]).toContain("-installApp" + NL);
+        expect(childProcess.spawn).toHaveBeenCalledWith(cmd, ["@options"], {"cwd": session.sourceDir, "env": process.env});
+        expect(callback).toHaveBeenCalledWith(0);
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/packager-utils.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/packager-utils.js b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/packager-utils.js
new file mode 100644
index 0000000..5acad8e
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/packager-validator.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/packager-validator.js b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/packager-validator.js
new file mode 100644
index 0000000..b6ecf4c
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/session.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/session.js b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/session.js
new file mode 100644
index 0000000..bbf6eb3
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/signing-helper.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/signing-helper.js b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/signing-helper.js
new file mode 100644
index 0000000..ad91726
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/test-data.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/test-data.js b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/test-data.js
new file mode 100644
index 0000000..11ec335
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/test-utilities.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/test-utilities.js b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/spec/lib/test-utilities.js
new file mode 100644
index 0000000..6f0dbf9
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/test.zip
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/test.zip b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/test.zip
new file mode 100644
index 0000000..8859a66
Binary files /dev/null and b/lib/cordova-blackberry/blackberry10/bin/test/cordova/unit/test.zip differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/plugins/Accelerometer/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/plugins/Accelerometer/index.js b/lib/cordova-blackberry/blackberry10/bin/test/plugins/Accelerometer/index.js
new file mode 100644
index 0000000..a016219
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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 + "./../../../templates/project/plugins/Accelerometer/",
+        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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/plugins/Battery/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/plugins/Battery/index.js b/lib/cordova-blackberry/blackberry10/bin/test/plugins/Battery/index.js
new file mode 100644
index 0000000..3495629
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/bin/test/plugins/Battery/index.js
@@ -0,0 +1,85 @@
+/*
+* 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 + "./../../../templates/project/plugins/Battery/",
+        index,
+        callback,
+        result = {
+            ok: jasmine.createSpy(),
+            error: jasmine.createSpy(),
+            noResult: jasmine.createSpy(),
+            callbackOk: jasmine.createSpy()
+        };
+
+    beforeEach(function () {
+        index = require(_apiDir + "index");
+        GLOBAL.window = {
+            qnx: {
+                webplatform: {
+                    device: {
+                        addEventListener: jasmine.createSpy().andCallFake(function (evt, cb) {
+                            callback = cb;
+                        }),
+                        removeEventListener: jasmine.createSpy()
+                    }
+                }
+            }
+        };
+        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.qnx.webplatform.device.addEventListener).toHaveBeenCalled();
+            expect(result.noResult).toHaveBeenCalledWith(true);
+        });
+
+        it("callback calls ok and keeps callbacks", function () {
+            callback("OK");
+            expect(result.callbackOk).toHaveBeenCalledWith("OK", true);
+        });
+
+        it("calls error if already started", function () {
+            index.start();
+            expect(window.qnx.webplatform.device.addEventListener).not.toHaveBeenCalled();
+            expect(result.error).toHaveBeenCalled();
+        });
+
+
+    });
+
+    describe("stop", function () {
+
+        it("calls noResult and does not keep callbacks", function () {
+            index.stop();
+            expect(window.qnx.webplatform.device.removeEventListener).toHaveBeenCalled();
+            expect(result.noResult).toHaveBeenCalledWith(false);
+        });
+
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/plugins/Camera/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/plugins/Camera/index.js b/lib/cordova-blackberry/blackberry10/bin/test/plugins/Camera/index.js
new file mode 100644
index 0000000..e629a41
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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 + "./../../../templates/project/plugins/Camera/",
+        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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/plugins/Device/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/plugins/Device/index.js b/lib/cordova-blackberry/blackberry10/bin/test/plugins/Device/index.js
new file mode 100644
index 0000000..9792b09
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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 + "./../../../templates/project/plugins/Device/",
+        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/6831bed4/lib/cordova-blackberry/blackberry10/bin/test/plugins/Logger/index.js
----------------------------------------------------------------------
diff --git a/lib/cordova-blackberry/blackberry10/bin/test/plugins/Logger/index.js b/lib/cordova-blackberry/blackberry10/bin/test/plugins/Logger/index.js
new file mode 100644
index 0000000..e5dc9f9
--- /dev/null
+++ b/lib/cordova-blackberry/blackberry10/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 + "./../../../templates/project/plugins/Logger/",
+        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);
+        });
+    });
+});