You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by js...@apache.org on 2014/07/07 23:43:50 UTC

[44/51] [partial] CB-7087 Retire blackberry10/ directory

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/i18n-manager.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/i18n-manager.js b/bin/test/cordova/unit/spec/lib/i18n-manager.js
new file mode 100644
index 0000000..91df758
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/i18n-manager.js
@@ -0,0 +1,307 @@
+/*
+ *  Copyright 2012 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.
+ */
+var testData = require("./test-data"),
+    i18nMgr = require(testData.libPath + "/i18n-manager"),
+    session = testData.session,
+    fs = require("fs"),
+    wrench = require("wrench"),
+    pkgrUtils = require(testData.libPath + "/packager-utils");
+
+function mockOSReturnFiles(files) {
+    if (pkgrUtils.isWindows()) {
+        var newFiles = [];
+        files.forEach(function (f) {
+            newFiles.push(session.sourceDir + "\\locales\\" + f.split("/").join("\\"));
+        });
+        return newFiles;
+    } else {
+        return files;
+    }
+}
+
+describe("i18n manager", function () {
+    it("generate correct metadata for icon", function () {
+        var config = {
+                icon: ["logo.png"]
+            },
+            xmlObject = {};
+
+        spyOn(fs, "existsSync").andReturn(true);
+        spyOn(wrench, "readdirSyncRecursive").andReturn(mockOSReturnFiles([
+            'fr',
+            'fr/logo.png'
+        ]));
+
+        i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "icon");
+
+        expect(xmlObject.icon).toBeDefined();
+        expect(xmlObject.icon.image).toBeDefined();
+        expect(xmlObject.icon.image.length).toBe(2);
+        expect(xmlObject.icon.image).toContain({
+            _value: "logo.png"
+        });
+        expect(xmlObject.icon.image).toContain({
+            text: {
+                _attr: {
+                    "xml:lang": "fr"
+                },
+                _value: "locales/fr/logo.png"
+            }
+        });
+    });
+
+    it("generate correct metadata for icon when locales folder does not exist", function () {
+        var config = {
+                icon: ["logo.png"]
+            },
+            xmlObject = {};
+
+        spyOn(fs, "existsSync").andReturn(false);
+
+        i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "icon");
+
+        expect(xmlObject.icon).toBeDefined();
+        expect(xmlObject.icon.image).toBeDefined();
+        expect(xmlObject.icon.image.length).toBe(1);
+        expect(xmlObject.icon.image).toContain({
+            _value: "logo.png"
+        });
+    });
+
+    it("generate correct metadata for icon when locale folder does not contain matching image", function () {
+        var config = {
+                icon: ["logo.png"]
+            },
+            xmlObject = {};
+
+        spyOn(fs, "existsSync").andReturn(true);
+        spyOn(wrench, "readdirSyncRecursive").andReturn(mockOSReturnFiles([
+            'fr',
+            'fr/logo-mismatch.png'
+        ]));
+
+        i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "icon");
+
+        expect(xmlObject.icon).toBeDefined();
+        expect(xmlObject.icon.image).toBeDefined();
+        expect(xmlObject.icon.image.length).toBe(1);
+        expect(xmlObject.icon.image).toContain({
+            _value: "logo.png"
+        });
+    });
+
+    it("generate correct metadata for icon when image is in subfolder", function () {
+        var config = {
+                icon: ["assets\\images\\logo.png"]
+            },
+            xmlObject = {};
+
+        spyOn(fs, "existsSync").andReturn(true);
+        spyOn(wrench, "readdirSyncRecursive").andReturn(mockOSReturnFiles([
+            'fr',
+            'fr/assets/images/logo.png'
+        ]));
+
+        i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "icon");
+
+        expect(xmlObject.icon).toBeDefined();
+        expect(xmlObject.icon.image).toBeDefined();
+        expect(xmlObject.icon.image.length).toBe(2);
+        expect(xmlObject.icon.image).toContain({
+            _value: "assets/images/logo.png"
+        });
+        expect(xmlObject.icon.image).toContain({
+            text: {
+                _attr: {
+                    "xml:lang": "fr"
+                },
+                _value: "locales/fr/assets/images/logo.png"
+            }
+        });
+    });
+
+    it("generate correct metadata for icon when image is in subfolder and OS is windows", function () {
+        if (pkgrUtils.isWindows()) {
+            var config = {
+                    icon: ["assets\\images\\logo.png"]
+                },
+                xmlObject = {};
+
+            spyOn(fs, "existsSync").andReturn(true);
+            spyOn(wrench, "readdirSyncRecursive").andReturn(mockOSReturnFiles([
+                'fr',
+                'fr\\assets\\images\\logo.png'
+            ]));
+
+            i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "icon");
+
+            expect(xmlObject.icon).toBeDefined();
+            expect(xmlObject.icon.image).toBeDefined();
+            expect(xmlObject.icon.image.length).toBe(2);
+            expect(xmlObject.icon.image).toContain({
+                _value: "assets/images/logo.png"
+            });
+            expect(xmlObject.icon.image).toContain({
+                text: {
+                    _attr: {
+                        "xml:lang": "fr"
+                    },
+                    _value: "locales/fr/assets/images/logo.png"
+                }
+            });
+        }
+    });
+
+    it("generate correct metadata for splash and OS is *nx", function () {
+        var config = {
+                "rim:splash": ["splash-1280x768.jpg", "splash-768x1280.jpg"]
+            },
+            xmlObject = {};
+
+        spyOn(pkgrUtils, 'isWindows').andReturn(false);
+        spyOn(fs, "existsSync").andReturn(true);
+        spyOn(wrench, "readdirSyncRecursive").andReturn(mockOSReturnFiles([
+            'fr',
+            'fr/splash-1280x768.jpg',
+            'fr/splash-768x1280.jpg'
+        ]));
+
+        i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "rim:splash");
+
+        expect(xmlObject.splashScreens).toBeDefined();
+        expect(xmlObject.splashScreens.image).toBeDefined();
+        expect(xmlObject.splashScreens.image.length).toBe(4);
+        expect(xmlObject.splashScreens.image).toContain({
+            _value: "splash-1280x768.jpg"
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            _value: "splash-768x1280.jpg"
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            text: {
+                _attr: {
+                    "xml:lang": "fr"
+                },
+                _value: "locales/fr/splash-1280x768.jpg"
+            }
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            text: {
+                _attr: {
+                    "xml:lang": "fr"
+                },
+                _value: "locales/fr/splash-768x1280.jpg"
+            }
+        });
+    });
+
+    it("generate correct metadata for splash when locales folder does not exist", function () {
+        var config = {
+                "rim:splash": ["splash-1280x768.jpg", "splash-768x1280.jpg"]
+            },
+            xmlObject = {};
+
+        spyOn(fs, "existsSync").andReturn(false);
+
+        i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "rim:splash");
+
+        expect(xmlObject.splashScreens).toBeDefined();
+        expect(xmlObject.splashScreens.image).toBeDefined();
+        expect(xmlObject.splashScreens.image.length).toBe(2);
+        expect(xmlObject.splashScreens.image).toContain({
+            _value: "splash-1280x768.jpg"
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            _value: "splash-768x1280.jpg"
+        });
+    });
+
+    it("generate correct metadata for splash when locale folder does not contain matching image", function () {
+        var config = {
+                "rim:splash": ["splash-1280x768.jpg", "splash-768x1280.jpg"]
+            },
+            xmlObject = {};
+
+        spyOn(fs, "existsSync").andReturn(true);
+        spyOn(wrench, "readdirSyncRecursive").andReturn(mockOSReturnFiles([
+            'fr',
+            'fr/splash-1280x768-mismatch.jpg',
+            'fr/splash-768x1280.jpg'
+        ]));
+
+        i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "rim:splash");
+
+        expect(xmlObject.splashScreens).toBeDefined();
+        expect(xmlObject.splashScreens.image).toBeDefined();
+        expect(xmlObject.splashScreens.image.length).toBe(3);
+        expect(xmlObject.splashScreens.image).toContain({
+            _value: "splash-1280x768.jpg"
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            _value: "splash-768x1280.jpg"
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            text: {
+                _attr: {
+                    "xml:lang": "fr"
+                },
+                _value: "locales/fr/splash-768x1280.jpg"
+            }
+        });
+    });
+
+    it("generate correct metadata for splash when image is in subfolder", function () {
+        var config = {
+                "rim:splash": ["assets\\images\\splash-1280x768.jpg", "assets\\images\\splash-768x1280.jpg"]
+            },
+            xmlObject = {};
+
+        spyOn(fs, "existsSync").andReturn(true);
+        spyOn(wrench, "readdirSyncRecursive").andReturn(mockOSReturnFiles([
+            'fr',
+            'fr/assets/images/splash-1280x768.jpg',
+            'fr/assets/images/splash-768x1280.jpg'
+        ]));
+
+        i18nMgr.generateLocalizedMetadata(session, config, xmlObject, "rim:splash");
+
+        expect(xmlObject.splashScreens).toBeDefined();
+        expect(xmlObject.splashScreens.image).toBeDefined();
+        expect(xmlObject.splashScreens.image.length).toBe(4);
+        expect(xmlObject.splashScreens.image).toContain({
+            _value: "assets/images/splash-1280x768.jpg"
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            _value: "assets/images/splash-768x1280.jpg"
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            text: {
+                _attr: {
+                    "xml:lang": "fr"
+                },
+                _value: "locales/fr/assets/images/splash-1280x768.jpg"
+            }
+        });
+        expect(xmlObject.splashScreens.image).toContain({
+            text: {
+                _attr: {
+                    "xml:lang": "fr"
+                },
+                _value: "locales/fr/assets/images/splash-768x1280.jpg"
+            }
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/logger.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/logger.js b/bin/test/cordova/unit/spec/lib/logger.js
new file mode 100644
index 0000000..c934ada
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/logger.js
@@ -0,0 +1,104 @@
+/*
+ *  Copyright 2012 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.
+ */
+
+var srcPath = __dirname + "/../../../../../templates/project/cordova/lib/",
+    logger = require(srcPath + "logger");
+
+describe("logger", function () {
+    describe("when the log level is verbose", function () {
+        beforeEach(function () {
+            spyOn(console, "log");
+            spyOn(console, "error");
+            logger.level('verbose');
+        });
+
+        it("logs info messages", function () {
+            logger.info("cheese is made from milk");
+            expect(console.log).toHaveBeenCalledWith("[INFO]    cheese is made from milk");
+        });
+
+        it("logs error messages", function () {
+            logger.error("PC LOAD LETTER");
+            expect(console.error).toHaveBeenCalledWith("[ERROR]   PC LOAD LETTER");
+        });
+
+        it("logs warning messages", function () {
+            logger.warn("beware the ides of march");
+            expect(console.log).toHaveBeenCalledWith("[WARN]    beware the ides of march");
+        });
+
+        it("logs messages", function () {
+            logger.log("Hulk Smash!");
+            expect(console.log).toHaveBeenCalledWith("[INFO]    Hulk Smash!");
+        });
+    });
+
+    describe("when the log level is warn", function () {
+        beforeEach(function () {
+            spyOn(console, "log");
+            spyOn(console, "error");
+            logger.level('warn');
+        });
+
+        it("doesn't log info messages", function () {
+            logger.info("cheese is made from milk");
+            expect(console.log).not.toHaveBeenCalledWith("[INFO]    cheese is made from milk");
+        });
+
+        it("logs error messages", function () {
+            logger.error("PC LOAD LETTER");
+            expect(console.error).toHaveBeenCalledWith("[ERROR]   PC LOAD LETTER");
+        });
+
+        it("logs warning messages", function () {
+            logger.warn("beware the ides of march");
+            expect(console.log).toHaveBeenCalledWith("[WARN]    beware the ides of march");
+        });
+
+        it("doesn't log messages", function () {
+            logger.log("Hulk Smash!");
+            expect(console.log).not.toHaveBeenCalledWith("[INFO]    Hulk Smash!");
+        });
+    });
+
+    describe("when the log level is error", function () {
+        beforeEach(function () {
+            spyOn(console, "log");
+            spyOn(console, "error");
+            logger.level('error');
+        });
+
+        it("doesn't log info messages", function () {
+            logger.info("cheese is made from milk");
+            expect(console.log).not.toHaveBeenCalledWith("[INFO]    cheese is made from milk");
+        });
+
+        it("logs error messages", function () {
+            logger.error("PC LOAD LETTER");
+            expect(console.error).toHaveBeenCalledWith("[ERROR]   PC LOAD LETTER");
+        });
+
+        it("doesn't log warning messages", function () {
+            logger.warn("beware the ides of march");
+            expect(console.log).not.toHaveBeenCalledWith("[WARN]    beware the ides of march");
+        });
+
+        it("doesn't log messages", function () {
+            logger.log("Hulk Smash!");
+            expect(console.log).not.toHaveBeenCalledWith("[INFO]    Hulk Smash!");
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/native-packager.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/native-packager.js b/bin/test/cordova/unit/spec/lib/native-packager.js
new file mode 100644
index 0000000..6f2359d
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/native-packager.js
@@ -0,0 +1,357 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+var path = require("path"),
+    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 () {
+        process.env.CORDOVA_BBTOOLS = "";
+        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).toHaveBeenCalled();
+    });
+
+    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("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 = "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 = "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-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/packager-utils.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/packager-utils.js b/bin/test/cordova/unit/spec/lib/packager-utils.js
new file mode 100644
index 0000000..493298c
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/packager-utils.js
@@ -0,0 +1,86 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+var 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 exist 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-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/packager-validator.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/packager-validator.js b/bin/test/cordova/unit/spec/lib/packager-validator.js
new file mode 100644
index 0000000..40e3b3c
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/packager-validator.js
@@ -0,0 +1,281 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+var 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"),
+    signingUtils = require(srcPath + "signing-utils"),
+    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 bbidtoken.csk or barsigner.csk was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = signingUtils.getDefaultPath("author.p12");
+        session.keystoreCsk = undefined;
+        session.storepass = "myPassword";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_KEY_FILE", "bbidtoken.csk"));
+    });
+
+    it("does not throw an exception when -g set and barsigner.csk was found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = signingUtils.getDefaultPath("author.p12");
+        session.keystoreCsk = "barsigner.csk";
+        session.keystoreDb = "barsigner.db";
+        session.storepass = "myPassword";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).not.toThrow();
+    });
+
+    it("throws an exception when --buildId set and bbbidtoken.csk was not found", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = signingUtils.getDefaultPath("author.p12");
+        session.keystoreCsk = undefined;
+        session.buildId = "100";
+
+        expect(function () {
+            packagerValidator.validateSession(session, configObj);
+        }).toThrow(localize.translate("EXCEPTION_MISSING_SIGNING_KEY_FILE", "bbidtoken.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 = signingUtils.getDefaultPath("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 = signingUtils.getDefaultPath("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 bbidtoken.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 = signingUtils.getDefaultPath("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", "bbidtoken.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 = signingUtils.getDefaultPath("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 --buildId was set with no password [-g]", function () {
+        var session = testUtilities.cloneObj(testData.session),
+            configObj = testUtilities.cloneObj(testData.config);
+
+        //setup signing parameters
+        session.keystore = signingUtils.getDefaultPath("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"));
+    });
+});
+
+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-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/session.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/session.js b/bin/test/cordova/unit/spec/lib/session.js
new file mode 100644
index 0000000..524b040
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/session.js
@@ -0,0 +1,228 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+var session = require(__dirname + "/../../../../../templates/project/cordova/lib/session"),
+    localize = require(__dirname + "/../../../../../templates/project/cordova/lib/localize"),
+    testUtils = require("./test-utilities"),
+    utils = require(__dirname + "/../../../../../templates/project/cordova/lib/utils"),
+    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");
+        spyOn(utils, "getProperties").andReturn({});
+    });
+
+    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 sometimes 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(path.normalize(__dirname + "../../../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",
+                    keystorepass: "test"
+                };
+
+            spyOn(fs, "existsSync").andReturn(false);
+
+            expect(function () {
+                session.initialize(data);
+            }).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);
+
+            expect(function () {
+                session.initialize(data);
+            }).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-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/signing-helper.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/signing-helper.js b/bin/test/cordova/unit/spec/lib/signing-helper.js
new file mode 100644
index 0000000..8618f21
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/signing-helper.js
@@ -0,0 +1,98 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+var 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("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, "exec").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";
+
+            session.getParams = jasmine.createSpy("session getParams").andReturn(null);
+            signingHelper.execSigner(session, "device", callback);
+            expect(childProcess.exec).toHaveBeenCalledWith([cmd, "-keystore", session.keystore, "-storepass", session.storepass, path.resolve("c:/device/Demo.bar")].join(" "), jasmine.any(Object), callback);
+            expect(stdoutOn).toHaveBeenCalledWith("data", pkgrUtils.handleProcessOutput);
+            expect(stderrOn).toHaveBeenCalledWith("data", pkgrUtils.handleProcessOutput);
+        });
+
+        it("exec blackberry-signer with extra params", function () {
+            var callback = jasmine.createSpy("callback"),
+                cmd = "blackberry-signer";
+
+            session.getParams = jasmine.createSpy("session getParams").andReturn({
+                "-proxyhost": "abc.com",
+                "-proxyport": "80"
+            });
+            signingHelper.execSigner(session, "device", callback);
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain(cmd);
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain("-keystore");
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain(session.keystore);
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain("-storepass");
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain(session.storepass);
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain("-proxyport");
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain("80");
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain("-proxyhost");
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain("abc.com");
+            expect(childProcess.exec.mostRecentCall.args[0]).toContain(path.resolve("c:/device/Demo.bar"));
+            expect(stdoutOn).toHaveBeenCalledWith("data", pkgrUtils.handleProcessOutput);
+            expect(stderrOn).toHaveBeenCalledWith("data", pkgrUtils.handleProcessOutput);
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/signing-utils.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/signing-utils.js b/bin/test/cordova/unit/spec/lib/signing-utils.js
new file mode 100644
index 0000000..1ba5c01
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/signing-utils.js
@@ -0,0 +1,403 @@
+/*
+ * 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.
+ */
+var testData = require('./test-data'),
+    signingUtils = require(testData.libPath + '/signing-utils'),
+    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-utils", function () {
+
+    describe("on windows", function () {
+
+        beforeEach(function () {
+
+            /* Preserve the value of the USERPROFILE environment
+             * variable if it is defined. If it is not defined, mark
+             * the variable for deletion after the test.*/
+            if (typeof process.env.USERPROFILE === 'undefined') {
+                properties.userprofile = "delete";
+            } else {
+                properties.userprofile = process.env.USERPROFILE;
+            }
+
+            spyOn(os, "type").andReturn("windows");
+        });
+
+        afterEach(function () {
+
+            /* Restore the value of the USERPROFILE environment
+             * variable if it is defined. If it is not defined, delete
+             * the property if it was defined in the test.*/
+            if (typeof process.env.USERPROFILE === 'string') {
+                if (properties.userprofile === 'delete') {
+                    delete process.env.USERPROFILE;
+                } else {
+                    process.env.USERPROFILE = properties.userprofile;
+                }
+            }
+        });
+
+        it("can find keys in Local Settings", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePath();
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find bbidtoken.csk in Local Settings", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePathBBID();
+            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 = signingUtils.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 = signingUtils.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 = signingUtils.getKeyStorePath();
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find bbidtoken.csk in AppData", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePathBBID();
+            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 = signingUtils.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 = signingUtils.getDbPath();
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find keys in home path", function () {
+            process.env.USERPROFILE = "C:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (p) {
+                return p.indexOf("\\Users\\user") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePath();
+            expect(result).toContain("\\Users\\user");
+        });
+
+        it("can find keys on C drive", function () {
+            process.env.USERPROFILE = "C:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (p) {
+                return p.indexOf("C:") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePath();
+            expect(result).toContain("C:");
+        });
+
+        it("can find keys on a drive other than C", function () {
+            process.env.USERPROFILE = "D:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePath();
+            expect(result).toContain("D:");
+        });
+
+        it("can find bbidtoken.csk on a drive other than C", function () {
+            process.env.USERPROFILE = "D:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePathBBID();
+            expect(result).toContain("D:");
+        });
+
+        it("can find barsigner.csk on a drive other than C", function () {
+            process.env.USERPROFILE = "D:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1;
+            });
+
+            var result = signingUtils.getCskPath();
+            expect(result).toContain("D:");
+        });
+
+        it("can find barsigner.db on a drive other than C", function () {
+            process.env.USERPROFILE = "D:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1;
+            });
+
+            var result = signingUtils.getDbPath();
+            expect(result).toContain("D:");
+        });
+
+        it("can find keys in Local Settings on the correct drive", function () {
+            process.env.USERPROFILE = "C:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("C:") !== -1 &&
+                        path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePath();
+            expect(result).toContain("C:");
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find bbidtoken.csk in Local Settings on the correct drive", function () {
+            process.env.USERPROFILE = "C:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("C:") !== -1 &&
+                        path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePathBBID();
+            expect(result).toContain("C:");
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find barsigner.csk in Local Settings on the correct drive", function () {
+            process.env.USERPROFILE = "D:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1 &&
+                        path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingUtils.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.USERPROFILE = "D:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1 &&
+                        path.indexOf("\\Local Settings") !== -1;
+            });
+
+            var result = signingUtils.getDbPath();
+            expect(result).toContain("D:");
+            expect(result).toContain("\\Local Settings");
+        });
+
+        it("can find keys in AppData on the correct drive", function () {
+            process.env.USERPROFILE = "C:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("C:") !== -1 &&
+                        path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePath();
+            expect(result).toContain("C:");
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find bbidtoken.csk in AppData on the correct drive", function () {
+            process.env.USERPROFILE = "C:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("C:") !== -1 &&
+                        path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePathBBID();
+            expect(result).toContain("C:");
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find barsigner.csk in AppData on the correct drive", function () {
+            process.env.USERPROFILE = "D:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1 &&
+                        path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingUtils.getCskPath();
+            expect(result).toContain("D:");
+            expect(result).toContain("\\AppData");
+        });
+
+        it("can find barsigner.db in AppData on the correct drive", function () {
+            process.env.USERPROFILE = "D:\\Users\\user";
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("D:") !== -1 &&
+                        path.indexOf("\\AppData") !== -1;
+            });
+
+            var result = signingUtils.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 = signingUtils.getKeyStorePath();
+            expect(result).toBeUndefined();
+        });
+
+        it("returns undefined when barsigner.csk cannot be found", function () {
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingUtils.getCskPath();
+            expect(result).toBeUndefined();
+        });
+
+        it("returns undefined when barsigner.db cannot be found", function () {
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingUtils.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 = signingUtils.getKeyStorePath();
+            expect(result).toContain("/Library/Research In Motion/");
+        });
+
+        it("can find bbidtoken.csk in the Library folder", function () {
+
+            spyOn(fs, "existsSync").andCallFake(function (path) {
+                return path.indexOf("/Library/Research In Motion/") !== -1;
+            });
+
+            var result = signingUtils.getKeyStorePathBBID();
+            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 = signingUtils.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 = signingUtils.getDbPath();
+            expect(result).toContain("/Library/Research In Motion/");
+        });
+
+        it("returns undefined when keys cannot be found", function () {
+
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingUtils.getKeyStorePath();
+            expect(result).toBeUndefined();
+        });
+
+        it("returns undefined when barsigner.csk cannot be found", function () {
+
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingUtils.getCskPath();
+            expect(result).toBeUndefined();
+        });
+
+        it("returns undefined when barsigner.db cannot be found", function () {
+
+            spyOn(fs, "existsSync").andReturn(false);
+
+            var result = signingUtils.getDbPath();
+            expect(result).toBeUndefined();
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/test-data.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/test-data.js b/bin/test/cordova/unit/spec/lib/test-data.js
new file mode 100644
index 0000000..11ec335
--- /dev/null
+++ b/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-blackberry/blob/a6733a83/bin/test/cordova/unit/spec/lib/test-utilities.js
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/spec/lib/test-utilities.js b/bin/test/cordova/unit/spec/lib/test-utilities.js
new file mode 100644
index 0000000..8b53c8e
--- /dev/null
+++ b/bin/test/cordova/unit/spec/lib/test-utilities.js
@@ -0,0 +1,108 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+var 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 = {
+    getAccessList: function (accessListArray, value) {
+        if (accessListArray[0].hasOwnProperty("uri") === true) {
+            return getObjectByProperty(accessListArray, "uri", value);
+        } else {
+            return getObjectByProperty(accessListArray, "origin", value);
+        }
+    },
+
+    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;
+        /* jshint ignore:start */
+        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];
+            }
+        }
+        /* jshint ignore:end */
+
+        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-blackberry/blob/a6733a83/bin/test/cordova/unit/test.zip
----------------------------------------------------------------------
diff --git a/bin/test/cordova/unit/test.zip b/bin/test/cordova/unit/test.zip
new file mode 100644
index 0000000..8859a66
Binary files /dev/null and b/bin/test/cordova/unit/test.zip differ

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/bin/update
----------------------------------------------------------------------
diff --git a/bin/update b/bin/update
new file mode 100755
index 0000000..342984a
--- /dev/null
+++ b/bin/update
@@ -0,0 +1,28 @@
+#! /bin/bash
+<<COMMENT
+    Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+
+COMMENT
+# update a cordova/blackberry10 project
+#
+# USAGE
+#   ./update [projectpath]
+
+BIN_DIR=$(dirname "$0")
+source "$BIN_DIR/init"
+"$CORDOVA_NODE/node" "$BIN_DIR/lib/update.js" "$@"

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/bin/update.bat
----------------------------------------------------------------------
diff --git a/bin/update.bat b/bin/update.bat
new file mode 100644
index 0000000..5a71671
--- /dev/null
+++ b/bin/update.bat
@@ -0,0 +1,21 @@
+@ECHO OFF
+goto comment
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+:comment
+call "%~dp0init"
+"%CORDOVA_NODE%\node.exe" "%~dp0\lib\update.js" %*

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/bin/whereis.cmd
----------------------------------------------------------------------
diff --git a/bin/whereis.cmd b/bin/whereis.cmd
new file mode 100644
index 0000000..372bcef
--- /dev/null
+++ b/bin/whereis.cmd
@@ -0,0 +1,19 @@
+@echo off
+setlocal enabledelayedexpansion
+set var_a=%1
+call :sub %var_a%
+if exist %var_b% if not exist %var_b%\nul goto exit
+for %%i in (.com .exe .cmd .bat) do (
+  call :sub %var_a%%%i
+  if exist !var_b! goto exit
+)
+echo INFO: could not find files for the given pattern(s) 1>&2
+exit /b 1
+
+:sub
+set var_b=%~$PATH:1
+goto :EOF
+
+:exit
+echo %var_b%
+exit /b 0

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/blackberry10/.gitignore
----------------------------------------------------------------------
diff --git a/blackberry10/.gitignore b/blackberry10/.gitignore
deleted file mode 100644
index bd024b8..0000000
--- a/blackberry10/.gitignore
+++ /dev/null
@@ -1,21 +0,0 @@
-# OS X
-
-.DS_Store
-tags
-
-# Eclipse
-
-deliverables/
-.preprocessed/
-
-# Text Editor Tmp
-
-._*
-
-# Generated libraries
-
-build/
-dist/
-bin/templates/project/lib
-example/
-.tmp

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/blackberry10/.jshintignore
----------------------------------------------------------------------
diff --git a/blackberry10/.jshintignore b/blackberry10/.jshintignore
deleted file mode 100644
index 41c3629..0000000
--- a/blackberry10/.jshintignore
+++ /dev/null
@@ -1,6 +0,0 @@
-framework/lib/jnext.js
-bin/test/cordova/unit/params-bad.json
-bin/templates/project/cordova/lib/xml-helpers.js
-bin/templates/project/cordova/third_party/*
-bin/templates/project/lib/*
-bin/templates/project/www/**/*

http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/a6733a83/blackberry10/.jshintrc
----------------------------------------------------------------------
diff --git a/blackberry10/.jshintrc b/blackberry10/.jshintrc
deleted file mode 100644
index 84db82c..0000000
--- a/blackberry10/.jshintrc
+++ /dev/null
@@ -1,73 +0,0 @@
-{
-    "predef": [
-        "escape",
-        "unescape",
-        //Jasmine
-        "jasmine",
-        "it",
-        "describe",
-        "xit",
-        "xdescribe",
-        "beforeEach",
-        "afterEach",
-        "runs",
-        "waits",
-        "waitsFor",
-        "spyOn",
-        "expect",
-        "global",
-        //Webworks
-        "qnx",
-        "chrome",
-        "blackberry",
-        "internal",
-        "JNEXT",
-        "frameworkModules",
-        //Node
-        "__dirname",
-        "GLOBAL",
-        "process",
-        "exports",
-        "module",
-        "NamedNodeMap",
-        "require",
-        "Node",
-        "JSZip",
-        //UI Components
-        "x$",
-        "iris",
-        //Browser
-        "DOMElement",
-        "atob",
-        //automated tests
-        "showOverlay",
-        "hideOverlay"
-    ],
-
-    "node" : false,
-    "browser" : true,
-
-    "boss" : false,
-    "curly": false,
-    "bitwise" : false,
-    "white": true,
-    "indent": 4,
-    "evil": false,
-    "devel": true,
-    "debug": false,
-    "eqeqeq": true,
-    "forin": true,
-    "immed": true,
-    "laxbreak": false,
-    "newcap": true,
-    "noarg": true,
-    "noempty": false,
-    "nonew": false,
-    "nomen": false,
-    "onevar": true,
-    "plusplus": false,
-    "regexp": false,
-    "undef": true,
-    "sub": true,
-    "strict": false
-}