<commit_before>/*
 *
 * 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.
 *
*/
exports.defineAutoTests = function () {
    var fail = function (done, context, message) {
            // prevents done() to be called several times
            if (context) {
                if (context.done) return;
                context.done = true;
            }

            if (message) {
                expect(false).toBe(true, message);
            } else {
                expect(false).toBe(true);
            }

            // watchPosition could call its callback sync (before returning the value)
            // so we invoke done async to make sure we know watcher id to .clear in afterEach
            setTimeout(function () {
                done();
            });
        },
        succeed = function (done, context) {
            // prevents done() to be called several times
            if (context) {
                if (context.done) return;
                context.done = true;
            }

            expect(true).toBe(true);

            // watchPosition could call its callback sync (before returning the value)
            // so we invoke done async to make sure we know watcher id to .clear in afterEach
            setTimeout(function () {
                done();
            });
        },
        isWindowsStore = (cordova.platformId == "windows8") || (cordova.platformId == "windows" && !WinJS.Utilities.isPhone);

    describe('Geolocation (navigator.geolocation)', function () {

        it("geolocation.spec.1 should exist", function () {
            expect(navigator.geolocation).toBeDefined();
        });

        it("geolocation.spec.2 should contain a getCurrentPosition function", function () {
            expect(typeof navigator.geolocation.getCurrentPosition).toBeDefined();
            expect(typeof navigator.geolocation.getCurrentPosition == 'function').toBe(true);
        });

        it("geolocation.spec.3 should contain a watchPosition function", function () {
            expect(typeof navigator.geolocation.watchPosition).toBeDefined();
            expect(typeof navigator.geolocation.watchPosition == 'function').toBe(true);
        });

        it("geolocation.spec.4 should contain a clearWatch function", function () {
            expect(typeof navigator.geolocation.clearWatch).toBeDefined();
            expect(typeof navigator.geolocation.clearWatch == 'function').toBe(true);
        });

    });

    describe('getCurrentPosition method', function () {

        describe('error callback', function () {

            it("geolocation.spec.5 should be called if we set timeout to 0 and maximumAge to a very small number", function (done) {
                // this test asks for using geolocation and interrupts autotests running.
                // That's why we have to pending that for Windows Store 8.0/8.1 apps
                if (isWindowsStore) {
                    pending();
                }

                navigator.geolocation.getCurrentPosition(
                    fail.bind(null, done),
                    succeed.bind(null, done),
                    {
                        maximumAge: 0,
                        timeout: 0
                    });
            });

        });

        describe('success callback', function () {

            it("geolocation.spec.6 should be called with a Position object", function (done) {
                // this test asks for using geolocation and interrupts autotests running.
                // That's why we have to pending that for Windows Store 8.0/8.1 apps
                if (isWindowsStore) {
                    pending();
                }

                navigator.geolocation.getCurrentPosition(function (p) {
                    expect(p.coords).toBeDefined();
                    expect(p.timestamp).toBeDefined();
                    done();
                },
                fail.bind(null, done),
                {
                    maximumAge: (5 * 60 * 1000) // 5 minutes maximum age of cached position
                });
            }, 25000); // first geolocation call can take several seconds on some devices
        });

    });

    describe('watchPosition method', function () {

        beforeEach(function(done) {
            // This timeout is set to lessen the load on platform's geolocation services
            // which were causing occasional test failures
            setTimeout(function() {
                done();
            }, 100);
        });

        describe('error callback', function () {

            var errorWatch = null;
            afterEach(function () {
                navigator.geolocation.clearWatch(errorWatch);
            });

            it("geolocation.spec.7 should be called if we set timeout to 0 and maximumAge to a very small number", function (done) {
                // this test asks for using geolocation and interrupts autotests running.
                // That's why we have to pending that for Windows Store 8.0/8.1 apps
                if (isWindowsStore) {
                    pending();
                }

                var context = this;
                errorWatch = navigator.geolocation.watchPosition(
                    fail.bind(null, done, context, 'Unexpected win'),
                    succeed.bind(null, done, context),
                    {
                        maximumAge: 0,
                        timeout: 0
                    });
            });

        });

        describe('success callback', function () {

            var successWatch = null;
            afterEach(function () {
                navigator.geolocation.clearWatch(successWatch);
            });

            it("geolocation.spec.8 should be called with a Position object", function (done) {
                // this test asks for using geolocation and interrupts autotests running.
                // That's why we have to pending that for Windows Store 8.0/8.1 apps
                if (isWindowsStore) {
                    pending();
                }

                var context = this;
                successWatch = navigator.geolocation.watchPosition(
                    function (p) {
                        // prevents done() to be called several times
                        if (context.done) return;
                        context.done = true;

                        expect(p.coords).toBeDefined();
                        expect(p.timestamp).toBeDefined();
                        // callback could be called sync so we invoke done async to make sure we know watcher id to .clear in afterEach
                        setTimeout(function () {
                            done();
                        });
                    },
                    fail.bind(null, done, context, 'Unexpected fail callback'),
                    {
                        maximumAge: (5 * 60 * 1000) // 5 minutes maximum age of cached position
                    });
            });

        });

    });

};

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

exports.defineManualTests = function (contentEl, createActionButton) {
    var newGeolocation = navigator.geolocation;
    var origGeolocation = cordova.require('cordova/modulemapper').getOriginalSymbol(window, 'navigator.geolocation');
    if (!origGeolocation) {
        origGeolocation = newGeolocation;
        newGeolocation = null;
    }

    var watchLocationId = null;

    /**
     * Start watching location
     */
    var watchLocation = function (usePlugin) {
        console.log("watchLocation()");
        var geo = usePlugin ? newGeolocation : origGeolocation;
        if (!geo) {
            alert('geolocation object is missing. usePlugin = ' + usePlugin);
            return;
        }

        // Success callback
        var success = function (p) {
            setLocationDetails(p);
        };

        // Fail callback
        var fail = function (e) {
            console.log("watchLocation fail callback with error code " + e);
            stopLocation(geo);
        };

        // Get location
        watchLocationId = geo.watchPosition(success, fail, { enableHighAccuracy: true });
        setLocationStatus("Running");
    };

    /**
     * Stop watching the location
     */
    var stopLocation = function (usePlugin) {
        console.log("stopLocation()");
        var geo = usePlugin ? newGeolocation : origGeolocation;
        if (!geo) {
            alert('geolocation object is missing. usePlugin = ' + usePlugin);
            return;
        }
        setLocationStatus("Stopped");
        if (watchLocationId) {
            geo.clearWatch(watchLocationId);
            watchLocationId = null;
        }
    };

    /**
     * Get current location
     */
    var getLocation = function (usePlugin, opts) {
        console.log("getLocation()");
        var geo = usePlugin ? newGeolocation : origGeolocation;
        if (!geo) {
            alert('geolocation object is missing. usePlugin = ' + usePlugin);
            return;
        }

        // Stop location if running
        stopLocation(geo);

        // Success callback
        var success = function (p) {
            setLocationDetails(p);
            setLocationStatus("Done");
        };

        // Fail callback
        var fail = function (e) {
            console.log("getLocation fail callback with error code " + e.code);
            setLocationStatus("Error: " + e.code);
        };

        setLocationStatus("Retrieving location...");

        // Get location
        geo.getCurrentPosition(success, fail, opts || { enableHighAccuracy: true }); //, {timeout: 10000});

    };

    /**
     * Set location status
     */
    var setLocationStatus = function (status) {
        document.getElementById('location_status').innerHTML = status;
    };
    var setLocationDetails = function (p) {
        var date = (new Date(p.timestamp));
        document.getElementById('latitude').innerHTML = p.coords.latitude;
        document.getElementById('longitude').innerHTML = p.coords.longitude;
        document.getElementById('altitude').innerHTML = p.coords.altitude;
        document.getElementById('accuracy').innerHTML = p.coords.accuracy;
        document.getElementById('heading').innerHTML = p.coords.heading;
        document.getElementById('speed').innerHTML = p.coords.speed;
        document.getElementById('altitude_accuracy').innerHTML = p.coords.altitudeAccuracy;
        document.getElementById('timestamp').innerHTML = date.toDateString() + " " + date.toTimeString();
    }

    /******************************************************************************/

    var location_div = '
' +
            'Status: Stopped' +
            '
',
        latitude = '
' +
            'Latitude:	' +
            ' 	' +
            '(decimal degrees) geographic coordinate [#ref]	' +
            '
',
        longitude = '
' +
            'Longitude:	' +
            ' 	' +
            '(decimal degrees) geographic coordinate [#ref]	' +
            '
',
        altitude = '
' +
            'Altitude:	' +
            ' 	' +
            'null if not supported;
' +
            '(meters) height above the [WGS84] ellipsoid. [#ref]	' +
            '
',
        accuracy = '
' +
            'Accuracy:	' +
            ' 	' +
            '(meters; non-negative; 95% confidence level) the accuracy level of the latitude and longitude coordinates. [#ref]	' +
            '
',
        heading = '
' +
            'Heading:	' +
            ' 	' +
            'null if not supported;
' +
            'NaN if speed == 0;
' +
            '(degrees; 0° ≤ heading < 360°) direction of travel of the hosting device- counting clockwise relative to the true north. [#ref]	' +
            '
',
        speed = '
' +
            'Speed:	' +
            ' 	' +
            'null if not supported;
' +
            '(meters per second; non-negative) magnitude of the horizontal component of the hosting device current velocity. [#ref]	' +
            '
',
        altitude_accuracy = '
' +
            'Altitude Accuracy:	' +
            ' 	' +
            'null if not supported;
(meters; non-negative; 95% confidence level) the accuracy level of the altitude. [#ref]	' +
            '
',
        time = '
' +
            'Time:	' +
            ' 	' +
            '(DOMTimeStamp) when the position was acquired [#ref]	' +
            '
' +
            '
' +
            '
',
        actions =
            '
Use Built-in WebView navigator.geolocation
' +
            '

' +
            'Expected result: Will update all applicable values in status box for current location. Status will read Retrieving Location (may not see this if location is retrieved immediately) then Done.' +
            '

' +
            'Expected result: Will update all applicable values in status box for current location and update as location changes. Status will read Running.' +
            '

' +
            'Expected result: Will stop watching the location so values will not be updated. Status will read Stopped.' +
            '

' +
            'Expected result: Will update location values with a cached position that is up to 30 seconds old. Verify with time value. Status will read Done.' +
            '
Use Cordova Geolocation Plugin
' +
            '

' +
            'Expected result: Will update all applicable values in status box for current location. Status will read Retrieving Location (may not see this if location is retrieved immediately) then Done.' +
            '

' +
            'Expected result: Will update all applicable values in status box for current location and update as location changes. Status will read Running.' +
            '

' +
            'Expected result: Will stop watching the location so values will not be updated. Status will read Stopped.' +
            '

' +
            'Expected result: Will update location values with a cached position that is up to 30 seconds old. Verify with time value. Status will read Done.',
        values_info =
            '
Details about each value are listed below in the status box
',
        note =
            '
Allow use of current location, if prompted
';

    contentEl.innerHTML = values_info + location_div + latitude + longitude + altitude + accuracy + heading + speed
        + altitude_accuracy + time + note + actions;

    createActionButton('Get Location', function () {
        getLocation(false);
    }, 'built-in-getLocation');

    createActionButton('Start Watching Location', function () {
        watchLocation(false);
    }, 'built-in-watchLocation');

    createActionButton('Stop Watching Location', function () {
        stopLocation(false);
    }, 'built-in-stopLocation');

    createActionButton('Get Location Up to 30 Sec Old', function () {
        getLocation(false, { maximumAge: 30000 });
    }, 'built-in-getOld');

    createActionButton('Get Location', function () {
        getLocation(true);
    }, 'cordova-getLocation');

    createActionButton('Start Watching Location', function () {
        watchLocation(true);
    }, 'cordova-watchLocation');

    createActionButton('Stop Watching Location', function () {
        stopLocation(true);
    }, 'cordova-stopLocation');

    createActionButton('Get Location Up to 30 Sec Old', function () {
        getLocation(true, { maximumAge: 30000 });
    }, 'cordova-getOld');
};
<commit_msg>CB-8845 Pended tests for Android<commit_after>/*
 *
 * 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.
 *
*/
exports.defineAutoTests = function () {
    var fail = function (done, context, message) {
            // prevents done() to be called several times
            if (context) {
                if (context.done) return;
                context.done = true;
            }

            if (message) {
                expect(false).toBe(true, message);
            } else {
                expect(false).toBe(true);
            }

            // watchPosition could call its callback sync (before returning the value)
            // so we invoke done async to make sure we know watcher id to .clear in afterEach
            setTimeout(function () {
                done();
            });
        },
        succeed = function (done, context) {
            // prevents done() to be called several times
            if (context) {
                if (context.done) return;
                context.done = true;
            }

            expect(true).toBe(true);

            // watchPosition could call its callback sync (before returning the value)
            // so we invoke done async to make sure we know watcher id to .clear in afterEach
            setTimeout(function () {
                done();
            });
        },
        isWindowsStore = (cordova.platformId == "windows8") || (cordova.platformId == "windows" && !WinJS.Utilities.isPhone),
        isAndroid = cordova.platformId == "android";

    describe('Geolocation (navigator.geolocation)', function () {

        it("geolocation.spec.1 should exist", function () {
            expect(navigator.geolocation).toBeDefined();
        });

        it("geolocation.spec.2 should contain a getCurrentPosition function", function () {
            expect(typeof navigator.geolocation.getCurrentPosition).toBeDefined();
            expect(typeof navigator.geolocation.getCurrentPosition == 'function').toBe(true);
        });

        it("geolocation.spec.3 should contain a watchPosition function", function () {
            expect(typeof navigator.geolocation.watchPosition).toBeDefined();
            expect(typeof navigator.geolocation.watchPosition == 'function').toBe(true);
        });

        it("geolocation.spec.4 should contain a clearWatch function", function () {
            expect(typeof navigator.geolocation.clearWatch).toBeDefined();
            expect(typeof navigator.geolocation.clearWatch == 'function').toBe(true);
        });

    });

    describe('getCurrentPosition method', function () {

        describe('error callback', function () {

            it("geolocation.spec.5 should be called if we set timeout to 0 and maximumAge to a very small number", function (done) {
                // On Windows, this test prompts user for permission to use geolocation and interrupts autotests running.
                // On Android, HTML5 native geolocation is used instead of geolocation plugin. This is not our goal to test it.
                if (isWindowsStore || isAndroid) {
                    pending();
                }

                navigator.geolocation.getCurrentPosition(
                    fail.bind(null, done),
                    succeed.bind(null, done),
                    {
                        maximumAge: 0,
                        timeout: 0
                    });
            });

        });

        describe('success callback', function () {

            it("geolocation.spec.6 should be called with a Position object", function (done) {
                // On Windows, this test prompts user for permission to use geolocation and interrupts autotests running.
                // On Android, HTML5 native geolocation is used instead of geolocation plugin. This is not our goal to test it.
                if (isWindowsStore || isAndroid) {
                    pending();
                }

                navigator.geolocation.getCurrentPosition(function (p) {
                    expect(p.coords).toBeDefined();
                    expect(p.timestamp).toBeDefined();
                    done();
                },
                fail.bind(null, done),
                {
                    maximumAge: (5 * 60 * 1000) // 5 minutes maximum age of cached position
                });
            }, 25000); // first geolocation call can take several seconds on some devices
        });

    });

    describe('watchPosition method', function () {

        beforeEach(function(done) {
            // This timeout is set to lessen the load on platform's geolocation services
            // which were causing occasional test failures
            setTimeout(function() {
                done();
            }, 100);
        });

        describe('error callback', function () {

            var errorWatch = null;
            afterEach(function () {
                navigator.geolocation.clearWatch(errorWatch);
            });

            it("geolocation.spec.7 should be called if we set timeout to 0 and maximumAge to a very small number", function (done) {
                // On Windows, this test prompts user for permission to use geolocation and interrupts autotests running.
                // On Android, HTML5 native geolocation is used instead of geolocation plugin. This is not our goal to test it.
                if (isWindowsStore || isAndroid) {
                    pending();
                }

                var context = this;
                errorWatch = navigator.geolocation.watchPosition(
                    fail.bind(null, done, context, 'Unexpected win'),
                    succeed.bind(null, done, context),
                    {
                        maximumAge: 0,
                        timeout: 0
                    });
            });

        });

        describe('success callback', function () {

            var successWatch = null;
            afterEach(function () {
                navigator.geolocation.clearWatch(successWatch);
            });

            it("geolocation.spec.8 should be called with a Position object", function (done) {
                // On Windows, this test prompts user for permission to use geolocation and interrupts autotests running.
                // On Android, HTML5 native geolocation is used instead of geolocation plugin. This is not our goal to test it.
                if (isWindowsStore || isAndroid) {
                    pending();
                }

                var context = this;
                successWatch = navigator.geolocation.watchPosition(
                    function (p) {
                        // prevents done() to be called several times
                        if (context.done) return;
                        context.done = true;

                        expect(p.coords).toBeDefined();
                        expect(p.timestamp).toBeDefined();
                        // callback could be called sync so we invoke done async to make sure we know watcher id to .clear in afterEach
                        setTimeout(function () {
                            done();
                        });
                    },
                    fail.bind(null, done, context, 'Unexpected fail callback'),
                    {
                        maximumAge: (5 * 60 * 1000) // 5 minutes maximum age of cached position
                    });
            });

        });

    });

};

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

exports.defineManualTests = function (contentEl, createActionButton) {
    var newGeolocation = navigator.geolocation;
    var origGeolocation = cordova.require('cordova/modulemapper').getOriginalSymbol(window, 'navigator.geolocation');
    if (!origGeolocation) {
        origGeolocation = newGeolocation;
        newGeolocation = null;
    }

    var watchLocationId = null;

    /**
     * Start watching location
     */
    var watchLocation = function (usePlugin) {
        console.log("watchLocation()");
        var geo = usePlugin ? newGeolocation : origGeolocation;
        if (!geo) {
            alert('geolocation object is missing. usePlugin = ' + usePlugin);
            return;
        }

        // Success callback
        var success = function (p) {
            setLocationDetails(p);
        };

        // Fail callback
        var fail = function (e) {
            console.log("watchLocation fail callback with error code " + e);
            stopLocation(geo);
        };

        // Get location
        watchLocationId = geo.watchPosition(success, fail, { enableHighAccuracy: true });
        setLocationStatus("Running");
    };

    /**
     * Stop watching the location
     */
    var stopLocation = function (usePlugin) {
        console.log("stopLocation()");
        var geo = usePlugin ? newGeolocation : origGeolocation;
        if (!geo) {
            alert('geolocation object is missing. usePlugin = ' + usePlugin);
            return;
        }
        setLocationStatus("Stopped");
        if (watchLocationId) {
            geo.clearWatch(watchLocationId);
            watchLocationId = null;
        }
    };

    /**
     * Get current location
     */
    var getLocation = function (usePlugin, opts) {
        console.log("getLocation()");
        var geo = usePlugin ? newGeolocation : origGeolocation;
        if (!geo) {
            alert('geolocation object is missing. usePlugin = ' + usePlugin);
            return;
        }

        // Stop location if running
        stopLocation(geo);

        // Success callback
        var success = function (p) {
            setLocationDetails(p);
            setLocationStatus("Done");
        };

        // Fail callback
        var fail = function (e) {
            console.log("getLocation fail callback with error code " + e.code);
            setLocationStatus("Error: " + e.code);
        };

        setLocationStatus("Retrieving location...");

        // Get location
        geo.getCurrentPosition(success, fail, opts || { enableHighAccuracy: true }); //, {timeout: 10000});

    };

    /**
     * Set location status
     */
    var setLocationStatus = function (status) {
        document.getElementById('location_status').innerHTML = status;
    };
    var setLocationDetails = function (p) {
        var date = (new Date(p.timestamp));
        document.getElementById('latitude').innerHTML = p.coords.latitude;
        document.getElementById('longitude').innerHTML = p.coords.longitude;
        document.getElementById('altitude').innerHTML = p.coords.altitude;
        document.getElementById('accuracy').innerHTML = p.coords.accuracy;
        document.getElementById('heading').innerHTML = p.coords.heading;
        document.getElementById('speed').innerHTML = p.coords.speed;
        document.getElementById('altitude_accuracy').innerHTML = p.coords.altitudeAccuracy;
        document.getElementById('timestamp').innerHTML = date.toDateString() + " " + date.toTimeString();
    }

    /******************************************************************************/

    var location_div = '
' +
            'Status: Stopped' +
            '
',
        latitude = '
' +
            'Latitude:	' +
            ' 	' +
            '(decimal degrees) geographic coordinate [#ref]	' +
            '
',
        longitude = '
' +
            'Longitude:	' +
            ' 	' +
            '(decimal degrees) geographic coordinate [#ref]	' +
            '
',
        altitude = '
' +
            'Altitude:	' +
            ' 	' +
            'null if not supported;
' +
            '(meters) height above the [WGS84] ellipsoid. [#ref]	' +
            '
',
        accuracy = '
' +
            'Accuracy:	' +
            ' 	' +
            '(meters; non-negative; 95% confidence level) the accuracy level of the latitude and longitude coordinates. [#ref]	' +
            '
',
        heading = '
' +
            'Heading:	' +
            ' 	' +
            'null if not supported;
' +
            'NaN if speed == 0;
' +
            '(degrees; 0° ≤ heading < 360°) direction of travel of the hosting device- counting clockwise relative to the true north. [#ref]	' +
            '
',
        speed = '
' +
            'Speed:	' +
            ' 	' +
            'null if not supported;
' +
            '(meters per second; non-negative) magnitude of the horizontal component of the hosting device current velocity. [#ref]	' +
            '
',
        altitude_accuracy = '
' +
            'Altitude Accuracy:	' +
            ' 	' +
            'null if not supported;
(meters; non-negative; 95% confidence level) the accuracy level of the altitude. [#ref]	' +
            '
',
        time = '
' +
            'Time:	' +
            ' 	' +
            '(DOMTimeStamp) when the position was acquired [#ref]	' +
            '
' +
            '
' +
            '
',
        actions =
            '
Use Built-in WebView navigator.geolocation
' +
            '

' +
            'Expected result: Will update all applicable values in status box for current location. Status will read Retrieving Location (may not see this if location is retrieved immediately) then Done.' +
            '

' +
            'Expected result: Will update all applicable values in status box for current location and update as location changes. Status will read Running.' +
            '

' +
            'Expected result: Will stop watching the location so values will not be updated. Status will read Stopped.' +
            '

' +
            'Expected result: Will update location values with a cached position that is up to 30 seconds old. Verify with time value. Status will read Done.' +
            '
Use Cordova Geolocation Plugin
' +
            '

' +
            'Expected result: Will update all applicable values in status box for current location. Status will read Retrieving Location (may not see this if location is retrieved immediately) then Done.' +
            '

' +
            'Expected result: Will update all applicable values in status box for current location and update as location changes. Status will read Running.' +
            '

' +
            'Expected result: Will stop watching the location so values will not be updated. Status will read Stopped.' +
            '

' +
            'Expected result: Will update location values with a cached position that is up to 30 seconds old. Verify with time value. Status will read Done.',
        values_info =
            '
Details about each value are listed below in the status box
',
        note =
            '
Allow use of current location, if prompted
';

    contentEl.innerHTML = values_info + location_div + latitude + longitude + altitude + accuracy + heading + speed
        + altitude_accuracy + time + note + actions;

    createActionButton('Get Location', function () {
        getLocation(false);
    }, 'built-in-getLocation');

    createActionButton('Start Watching Location', function () {
        watchLocation(false);
    }, 'built-in-watchLocation');

    createActionButton('Stop Watching Location', function () {
        stopLocation(false);
    }, 'built-in-stopLocation');

    createActionButton('Get Location Up to 30 Sec Old', function () {
        getLocation(false, { maximumAge: 30000 });
    }, 'built-in-getOld');

    createActionButton('Get Location', function () {
        getLocation(true);
    }, 'cordova-getLocation');

    createActionButton('Start Watching Location', function () {
        watchLocation(true);
    }, 'cordova-watchLocation');

    createActionButton('Stop Watching Location', function () {
        stopLocation(true);
    }, 'cordova-stopLocation');

    createActionButton('Get Location Up to 30 Sec Old', function () {
        getLocation(true, { maximumAge: 30000 });
    }, 'cordova-getOld');
};
<|endoftext|>


<commit_before>/**
 * Unit tests for mesostic function
 */

QUnit.test('Startup Test', function(assert) {
    assert.ok(1 == "1", "QUnit Started Up");
});

QUnit.test( "createSourceArray", function( assert ) {
    var testFunc = mesostic._createSourceArray;
    var sourceText;
    var sourceArray;
    var expectedResult = ['Here', 'is', 'the', 'source', 'text'];
    var options = {
        stripPunctuation: true,
    };

    //test vanilla example
    sourceText = 'Here is the source text';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'test vanilla')

    //test some double spaces
    sourceText = ' Here   is     the source  text ';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'ignore double spaces');

    //test some line breaks
    sourceText = 'Here\nis the \n text\n\nsource\n';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'ignore line breaks');

    //test some tabs
    sourceText = '\tHere\tis the \t text\n\t\nsource\n';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'ignore tabs');

    //test some punctuation
    sourceText = 'Here! is. the?  text "source"';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'strip punctuation');

    //Try leaving in an apostrophe
    expectedResult = ['Here', 'is', 'Bob\'s', 'text'];
    sourceText = 'Here is Bob\'s text!';
    sourceArray= testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'leave an apostrophe');

    //try leaving in punctuation
    expectedResult = ['Here!', 'is,', 'Bob\'s,', '!text'];
    sourceText = 'Here! is, Bob\'s, !text'];
    sourceArray= testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'leave punctuation');
});

QUnit.test( "hello test2", function( assert ) {
  assert.ok( 1 == "1", "Passed!" );
});
<commit_msg>added a bunch of tests<commit_after>/**
 * Unit tests for mesostic function
 */

QUnit.test('Startup Test', function(assert) {
    assert.ok(1 == "1", "QUnit Started Up");
});
//#########################createSourceArray testing ##############
QUnit.test( "createSourceArray", function( assert ) {
    var testFunc = mesostic._createSourceArray;
    var sourceText;
    var sourceArray;
    var expectedResult = ['here', 'is', 'the', 'source', 'text'];
    var options = {
        stripPunctuation: true,
    };

    //test vanilla example
    sourceText = 'here is the source text';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'test vanilla')

    //test capitalization example
    sourceText = 'here Is the SouRce text';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'test capitalization')

    //test some double spaces
    sourceText = ' Here   is     the source  text ';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'ignore double spaces');

    //test some line breaks
    sourceText = 'Here\nis the \n text\n\nsource\n';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'ignore line breaks');

    //test some tabs
    sourceText = '\tHere\tis the \t text\n\t\nsource\n';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'ignore tabs');

    //test some punctuation
    sourceText = 'Here! is. the?  text "source"';
    sourceArray = testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'strip punctuation');

    //Try leaving in an apostrophe
    expectedResult = ['Here', 'is', 'Bob\'s', 'text'];
    sourceText = 'Here is Bob\'s text!';
    sourceArray= testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'leave an apostrophe');

    //try leaving in punctuation
    options.stripPunctuation = false;
    expectedResult = ['here!', 'is,', 'bob\'s,', '!text'];
    sourceText = 'Here! is, Bob\'s, !text';
    sourceArray= testFunc(sourceText, options);
    assert.deepEqual(sourceArray, expectedResult, 'don\'t strip punctuation');
});

//#########################createSpineArray Testing ######################Y
QUnit.test( "createSpineArray", function( assert ) {

    var testFunc = mesostic._createSpineArray;
    var spineWord;
    var spineArray;
    var sourceArray;
    var expectedResult;
    var options = {
        stripPunctuation: true,
        rule: "basic",
        spineLoops: 1
    };

    spineWord = 'slot';
    sourceArray = [
        'Here',       //0
        'is',         //1 - 's' for basic, 50, 100
        'a',          //2
        'lovey',      //3 'l' for basic
        'silver',     //4 'l' for 50
        'alive',      //5 'l' for  100
        'source',     //6 'o' for basic, 50, 100
        'array',      //7
        'that',       //8 't' for basic, 50
        'itches',     //9 't' for 100 (1 loop)
        'into',       //10 't' for 100 (2 loops)
        'the',        //11
        'summer',     //12
        'nightlife',  //13
        'over',       //14
        'cancun'      //15
    ];

    //basic one loop
    spineArray = testFunc(sourceArray, spineWord, options);
    expectedResult = {parsed: true, spineArray: [
        {spineLetter: 's', index:1, pre:1, post:0},//is
        {spineLetter: 'l', index:3, pre:0, post:4},//lovely
        {spineLetter: 'o', index:6, pre:1, post:4},//source
        {spineLetter: 't', index:8, pre:0, post:3},//that
    ]};
    assert.deepEqual(spineArray, expectedResult, 'basic 1 loop');

    //50% one loop
    options.rule = "50";
    spineArray = testFunc(sourceArray, spineWord, options);
    expectedResult = {parsed: true, spineArray: [
        {spineLetter: 's', index:1, pre:1, post:0},//is
        {spineLetter: 'l', index:4, pre:2, post:3},//silver
        {spineLetter: 'o', index:6, pre:1, post:4},//source
        {spineLetter: 't', index:8, pre:0, post:3},//that
    ]};
    assert.deepEqual(spineArray, expectedResult, '50% 1 loop');

    //100% one loop
    options.rule = "100";
    spineArray = testFunc(sourceArray, spineWord, options);
    expectedResult = {parsed: true, spineArray: [
        {spineLetter: 's', index:1, pre:1, post:0},//is
        {spineLetter: 'l', index:5, pre:1, post:3},//alive
        {spineLetter: 'o', index:6, pre:1, post:4},//source
        {spineLetter: 't', index:9, pre:1, post:4},//itches
    ]};
    assert.deepEqual(spineArray, expectedResult, '100% 1 loop');

});

//#########################Parse Line Words Testing ######################Y
QUnit.test('parseLineWords', function(assert) {
    var testFunc = mesostic._parseLineWords;

    var parsed;
    var spineArray = [
        {spineLetter: 's', index:1, pre:1, post:0},//is
        {spineLetter: 'l', index:5, pre:1, post:3},//alive
        {spineLetter: 'o', index:6, pre:1, post:4},//source
        {spineLetter: 't', index:9, pre:1, post:4},//itches
    ];
    var sourceArray = [
        'here',       //0
        'is',         //1 - 's' for basic, 50, 100
        'a',          //2
        'lovey',      //3 'l' for basic
        'silver',     //4 'l' for 50
        'alive',      //5 'l' for  100
        'source',     //6 'o' for basic, 50, 100
        'array',      //7
        'rattle',     //8 't' for basic, 50
        'itches',     //9 't' for 100 (1 loop)
        'into',       //10 't' for 100 (2 loops)
        'the',        //11
        'summer',     //12
        'nightlife',  //13
        'over',       //14
        'cancun'      //15
    ];
    var options = {
        rule: "basic",
        wingTextSparsity: "normal"
    };

    parsed = testFunc(2,3,spineArray, sourceArray, options);
    assert.equal(parsed.startSpineLetter, 'O','check start spine letter');
    assert.equal(parsed.endSpineLetter, 'T', 'check end spine letter');
    assert.equal(parsed.preLineBreak.substr(0,4), 'urce', 'check preline break');

    assert.equal(parsed.postLineBreak.substr(parsed.postLineBreak.length - 2), 'ra', 'check postline break');
});
<|endoftext|>


<commit_before>// Set up the Jasmine environment
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;

var htmlReporter = new jasmine.HtmlReporter();

jasmineEnv.addReporter(htmlReporter);

jasmineEnv.specFilter = function(spec) {
    return htmlReporter.specFilter(spec);
};

// Configure the tests
describe('EventEmitter.fn.getListeners', function() {
    var ee;

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('initialises the event object and a listener array', function() {
        ee.getListeners('foo');
        expect(ee._events).toEqual({
            foo: []
        });
    });

    it('does not overwrite listener arrays', function() {
        var listeners = ee.getListeners('foo');
        listeners.push('bar');

        expect(ee._events).toEqual({
            foo: ['bar']
        });

        ee.getListeners('foo');

        expect(ee._events).toEqual({
            foo: ['bar']
        });
    });
});

describe('EventEmitter.fn.indexOfListener', function() {
    var ee,
        fn = function(){};

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('returns -1 on non existent listeners', function() {
        expect(ee.indexOfListener(fn, ee.getListeners('foo'))).toEqual(-1);

        // Testing again to make sure it does not work on init of storage only
        expect(ee.indexOfListener(fn, ee.getListeners('foo'))).toEqual(-1);
    });

    it('returns the index of an existing listener', function() {
        var listeners = ee.getListeners('foo');
        listeners.push('a');
        listeners.push('b');
        listeners.push(fn);
        listeners.push('c');

        expect(ee.indexOfListener(fn, listeners)).toEqual(2);
    });
});

describe('EventEmitter.fn.addListener', function() {
    var ee,
        fn1 = function(){},
        fn2 = function(){};

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('adds a listener to the specified event', function() {
        ee.addListener('foo', fn1);
        expect(ee.getListeners('foo')).toEqual([fn1]);
    });

    it('does not allow duplicate listeners', function() {
        ee.addListener('bar', fn1);
        expect(ee.getListeners('bar')).toEqual([fn1]);

        ee.addListener('bar', fn2);
        expect(ee.getListeners('bar')).toEqual([fn1, fn2]);

        ee.addListener('bar', fn1);
        expect(ee.getListeners('bar')).toEqual([fn1, fn2]);
    });
});

describe('EventEmitter.fn.removeListener', function() {
    var ee,
        fn1 = function(){},
        fn2 = function(){},
        fn3 = function(){},
        fn4 = function(){},
        fnX = function(){};

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('does nothing when the listener is not found', function() {
        var orig = ee.getListeners('foo').length;
        ee.removeListener('foo', fn1);
        expect(ee.getListeners('foo').length).toEqual(orig);
    });

    it('removes listeners', function() {
        var listeners = ee.getListeners('bar');

        ee.addListener('bar', fn1);
        ee.addListener('bar', fn2);
        ee.addListener('bar', fn3);
        ee.addListener('bar', fn3); // Make sure doubling up does nothing
        ee.addListener('bar', fn4);
        expect(listeners).toEqual([fn1, fn2, fn3, fn4]);

        ee.removeListener('bar', fn3);
        expect(listeners).toEqual([fn1, fn2, fn4]);

        ee.removeListener('bar', fnX);
        expect(listeners).toEqual([fn1, fn2, fn4]);

        ee.removeListener('bar', fn1);
        expect(listeners).toEqual([fn2, fn4]);

        ee.removeListener('bar', fn4);
        expect(listeners).toEqual([fn2]);

        ee.removeListener('bar', fn2);
        expect(ee._events.bar).not.toBeDefined();
    });
});

describe('EventEmitter.fn.removeEvent', function() {
    var ee,
        fn1 = function(){},
        fn2 = function(){},
        fn3 = function(){},
        fn4 = function(){},
        fn5 = function(){};

    beforeEach(function() {
        ee = new EventEmitter();

        ee.addListener('foo', fn1);
        ee.addListener('foo', fn2);
        ee.addListener('bar', fn3);
        ee.addListener('bar', fn4);
        ee.addListener('baz', fn5);
        expect(ee.getListeners('foo')).toEqual([fn1, fn2]);
        expect(ee.getListeners('bar')).toEqual([fn3, fn4]);
        expect(ee.getListeners('baz')).toEqual([fn5]);
    });

    it('removes all listeners for the specified event', function() {
        ee.removeEvent('bar');
        expect(ee.getListeners('foo')).toEqual([fn1, fn2]);
        expect(ee.getListeners('bar')).toEqual([]);
        expect(ee.getListeners('baz')).toEqual([fn5]);

        ee.removeEvent('baz');
        expect(ee.getListeners('foo')).toEqual([fn1, fn2]);
        expect(ee.getListeners('bar')).toEqual([]);
        expect(ee.getListeners('baz')).toEqual([]);
    });

    it('removes all events when no event is specified', function() {
        ee.removeEvent();
        expect(ee.getListeners('foo')).toEqual([]);
        expect(ee.getListeners('bar')).toEqual([]);
        expect(ee.getListeners('baz')).toEqual([]);
    });
});

describe('EventEmitter.fn.emitEvent', function() {
    var ee;

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('executes attached listeners', function() {
        var run = false;

        ee.addListener('foo', function() {
            run = true;
        });
        ee.emitEvent('foo');

        expect(run).toEqual(true);
    });

    it('executes attached with arguments', function() {
        var key = null;

        ee.addListener('bar', function(passedKey) {
            key = passedKey;
        });
        ee.emitEvent('bar', [42]);

        expect(key).toEqual(42);
    });

    it('executes multiple listeners', function() {
        var count = 0;

        ee.addListener('baz', function() { count++; });
        ee.addListener('baz', function() { count++; });
        ee.addListener('baz', function() { count++; });
        ee.addListener('baz', function() { count++; });
        ee.addListener('baz', function() { count++; });

        ee.emitEvent('baz');

        expect(count).toEqual(5);
    });
});

describe('EventEmitter.fn.manipulateListeners', function() {
    var ee;
    function fn1(){}
    function fn2(){}
    function fn3(){}
    function fn4(){}
    function fn5(){}

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('manipulates multiple with an array', function() {
        ee.manipulateListeners(false, 'foo', [fn1, fn2, fn3, fn4, fn5]);
        expect(ee.getListeners('foo')).toEqual([fn5, fn4, fn3, fn2, fn1]);

        ee.manipulateListeners(true, 'foo', [fn1, fn2]);
        expect(ee.getListeners('foo')).toEqual([fn5, fn4, fn3]);

        ee.manipulateListeners(true, 'foo', [fn3, fn5]);
        ee.manipulateListeners(false, 'foo', [fn4, fn1]);
        expect(ee.getListeners('foo')).toEqual([fn4, fn1]);

        ee.manipulateListeners(true, 'foo', [fn4, fn1]);
        expect(ee.getListeners('foo')).toEqual([]);
    });
});

// Run Jasmine
jasmineEnv.execute();<commit_msg>Finished the manipulation tests.<commit_after>// Set up the Jasmine environment
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;

var htmlReporter = new jasmine.HtmlReporter();

jasmineEnv.addReporter(htmlReporter);

jasmineEnv.specFilter = function(spec) {
    return htmlReporter.specFilter(spec);
};

// Configure the tests
describe('EventEmitter.fn.getListeners', function() {
    var ee;

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('initialises the event object and a listener array', function() {
        ee.getListeners('foo');
        expect(ee._events).toEqual({
            foo: []
        });
    });

    it('does not overwrite listener arrays', function() {
        var listeners = ee.getListeners('foo');
        listeners.push('bar');

        expect(ee._events).toEqual({
            foo: ['bar']
        });

        ee.getListeners('foo');

        expect(ee._events).toEqual({
            foo: ['bar']
        });
    });
});

describe('EventEmitter.fn.indexOfListener', function() {
    var ee,
        fn = function(){};

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('returns -1 on non existent listeners', function() {
        expect(ee.indexOfListener(fn, ee.getListeners('foo'))).toEqual(-1);

        // Testing again to make sure it does not work on init of storage only
        expect(ee.indexOfListener(fn, ee.getListeners('foo'))).toEqual(-1);
    });

    it('returns the index of an existing listener', function() {
        var listeners = ee.getListeners('foo');
        listeners.push('a');
        listeners.push('b');
        listeners.push(fn);
        listeners.push('c');

        expect(ee.indexOfListener(fn, listeners)).toEqual(2);
    });
});

describe('EventEmitter.fn.addListener', function() {
    var ee,
        fn1 = function(){},
        fn2 = function(){};

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('adds a listener to the specified event', function() {
        ee.addListener('foo', fn1);
        expect(ee.getListeners('foo')).toEqual([fn1]);
    });

    it('does not allow duplicate listeners', function() {
        ee.addListener('bar', fn1);
        expect(ee.getListeners('bar')).toEqual([fn1]);

        ee.addListener('bar', fn2);
        expect(ee.getListeners('bar')).toEqual([fn1, fn2]);

        ee.addListener('bar', fn1);
        expect(ee.getListeners('bar')).toEqual([fn1, fn2]);
    });
});

describe('EventEmitter.fn.removeListener', function() {
    var ee,
        fn1 = function(){},
        fn2 = function(){},
        fn3 = function(){},
        fn4 = function(){},
        fnX = function(){};

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('does nothing when the listener is not found', function() {
        var orig = ee.getListeners('foo').length;
        ee.removeListener('foo', fn1);
        expect(ee.getListeners('foo').length).toEqual(orig);
    });

    it('removes listeners', function() {
        var listeners = ee.getListeners('bar');

        ee.addListener('bar', fn1);
        ee.addListener('bar', fn2);
        ee.addListener('bar', fn3);
        ee.addListener('bar', fn3); // Make sure doubling up does nothing
        ee.addListener('bar', fn4);
        expect(listeners).toEqual([fn1, fn2, fn3, fn4]);

        ee.removeListener('bar', fn3);
        expect(listeners).toEqual([fn1, fn2, fn4]);

        ee.removeListener('bar', fnX);
        expect(listeners).toEqual([fn1, fn2, fn4]);

        ee.removeListener('bar', fn1);
        expect(listeners).toEqual([fn2, fn4]);

        ee.removeListener('bar', fn4);
        expect(listeners).toEqual([fn2]);

        ee.removeListener('bar', fn2);
        expect(ee._events.bar).not.toBeDefined();
    });
});

describe('EventEmitter.fn.removeEvent', function() {
    var ee,
        fn1 = function(){},
        fn2 = function(){},
        fn3 = function(){},
        fn4 = function(){},
        fn5 = function(){};

    beforeEach(function() {
        ee = new EventEmitter();

        ee.addListener('foo', fn1);
        ee.addListener('foo', fn2);
        ee.addListener('bar', fn3);
        ee.addListener('bar', fn4);
        ee.addListener('baz', fn5);
        expect(ee.getListeners('foo')).toEqual([fn1, fn2]);
        expect(ee.getListeners('bar')).toEqual([fn3, fn4]);
        expect(ee.getListeners('baz')).toEqual([fn5]);
    });

    it('removes all listeners for the specified event', function() {
        ee.removeEvent('bar');
        expect(ee.getListeners('foo')).toEqual([fn1, fn2]);
        expect(ee.getListeners('bar')).toEqual([]);
        expect(ee.getListeners('baz')).toEqual([fn5]);

        ee.removeEvent('baz');
        expect(ee.getListeners('foo')).toEqual([fn1, fn2]);
        expect(ee.getListeners('bar')).toEqual([]);
        expect(ee.getListeners('baz')).toEqual([]);
    });

    it('removes all events when no event is specified', function() {
        ee.removeEvent();
        expect(ee.getListeners('foo')).toEqual([]);
        expect(ee.getListeners('bar')).toEqual([]);
        expect(ee.getListeners('baz')).toEqual([]);
    });
});

describe('EventEmitter.fn.emitEvent', function() {
    var ee;

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('executes attached listeners', function() {
        var run = false;

        ee.addListener('foo', function() {
            run = true;
        });
        ee.emitEvent('foo');

        expect(run).toEqual(true);
    });

    it('executes attached with arguments', function() {
        var key = null;

        ee.addListener('bar', function(passedKey) {
            key = passedKey;
        });
        ee.emitEvent('bar', [42]);

        expect(key).toEqual(42);
    });

    it('executes multiple listeners', function() {
        var count = 0;

        ee.addListener('baz', function() { count++; });
        ee.addListener('baz', function() { count++; });
        ee.addListener('baz', function() { count++; });
        ee.addListener('baz', function() { count++; });
        ee.addListener('baz', function() { count++; });

        ee.emitEvent('baz');

        expect(count).toEqual(5);
    });
});

describe('EventEmitter.fn.manipulateListeners', function() {
    var ee;
    function fn1(){}
    function fn2(){}
    function fn3(){}
    function fn4(){}
    function fn5(){}

    beforeEach(function() {
        ee = new EventEmitter();
    });

    it('manipulates multiple with an array', function() {
        ee.manipulateListeners(false, 'foo', [fn1, fn2, fn3, fn4, fn5]);
        expect(ee.getListeners('foo')).toEqual([fn5, fn4, fn3, fn2, fn1]);

        ee.manipulateListeners(true, 'foo', [fn1, fn2]);
        expect(ee.getListeners('foo')).toEqual([fn5, fn4, fn3]);

        ee.manipulateListeners(true, 'foo', [fn3, fn5]);
        ee.manipulateListeners(false, 'foo', [fn4, fn1]);
        expect(ee.getListeners('foo')).toEqual([fn4, fn1]);

        ee.manipulateListeners(true, 'foo', [fn4, fn1]);
        expect(ee.getListeners('foo')).toEqual([]);
    });

    it('manipulates with an object', function() {
        ee.manipulateListeners(false, {
            foo: [fn1, fn2, fn3],
            bar: fn4
        });

        ee.manipulateListeners(false, {
            bar: [fn5, fn1]
        });

        expect(ee.getListeners('foo')).toEqual([fn3, fn2, fn1]);
        expect(ee.getListeners('bar')).toEqual([fn4, fn1, fn5]);

        ee.manipulateListeners(true, {
            foo: fn1,
            bar: [fn5, fn4]
        });

        expect(ee.getListeners('foo')).toEqual([fn3, fn2]);
        expect(ee.getListeners('bar')).toEqual([fn1]);

        ee.manipulateListeners(true, {
            foo: [fn3, fn2],
            bar: fn1
        });

        expect(ee.getListeners('foo')).toEqual([]);
        expect(ee.getListeners('bar')).toEqual([]);
    });
});

// Run Jasmine
jasmineEnv.execute();<|endoftext|>


<commit_before>var models = require("bolt-internal-models");
var utils = require("bolt-internal-utils");

var AWS = require('aws-sdk');
var bodyParser = require('body-parser');
var exphbs = require('express-handlebars');
var express = require("express");
var fs = require("fs");
var path = require("path");
var session = require("client-sessions"/*"express-session"*/);
var Showdown = require("showdown");
var s3 = require('s3');

var __publicdir = path.join(__dirname + './../../public');
var __sysdir = path.join(__dirname + './../../sys');

const X_BOLT_USER_TOKEN = 'X-Bolt-User-Token';
const X_BOLT_USER_NAME = 'X-Bolt-User-Name';

var multer = require('multer'),
	upload = multer({ dest : 'public/bolt/uploads/'});

module.exports = function(app) {
	app.use(bodyParser.json({limit: '100mb'}));
	app.use(bodyParser.urlencoded({limit: '100mb', extended: true}));

	app.use(function (request, response, next) {
	  response.header('Access-Control-Allow-Origin', '*');
	  response.header('Access-Control-Allow-Headers',
	  	'Origin, X-Requested-With, Content-Type, Accept, X-Bolt-App-Token, X-Bolt-Locale, X-Bolt-API-Ver, X-Bolt-Perm-Token, X-Bolt-User-Token');
	  response.header('Access-Control-Allow-Methods', 'DELETE, GET, POST, PUT');

	  next();
	});

	app.use(session({
		cookieName: 'session',
		secret: process.env.BOLT_SESSION_SECRET,
		duration: 24 * 60 * 60 * 1000,
		activeDuration: 24 * 60 * 60 * 1000,
		//httpOnly: false

		/*saveUninitialized: true, 	//for express-session
		resave: true*/				//for express-session
	}));
	app.use(function(request, response, next) {
		if (!utils.Misc.isNullOrUndefined(request.get(X_BOLT_USER_NAME))) {
			var username = request.get(X_BOLT_USER_NAME);

			models.user.findOne({
				name: username
			}, function(error, user){
				if(!utils.Misc.isNullOrUndefined(user)){
					request.user = utils.Misc.sanitizeUser(user);
				}
				next();
			});
		}
		else if (!utils.Misc.isNullOrUndefined(request.get(X_BOLT_USER_TOKEN))) {
			var token = request.get(X_BOLT_USER_TOKEN);

			superagent
				.get(process.env.BOLT_ADDRESS + '/api/tokens/' + encodeURIComponent(token))
				.end(function(tokenError, tokenResponse){
					var realResponse = tokenResponse.body;
					if (!utils.Misc.isNullOrUndefined(realResponse) && !utils.Misc.isNullOrUndefined(realResponse.body)) {
						var userid = realResponse.body;
						models.user.findOne({
							_id: userid
						}, function(error, user){
							if(!utils.Misc.isNullOrUndefined(user)){
								request.user = utils.Misc.sanitizeUser(user);
							}
							next();
						});
					}
					else {
						next();
					}
				});
		}
		else if (!utils.Misc.isNullOrUndefined(request.session) && !utils.Misc.isNullOrUndefined(request.session.user)) {
			models.user.findOne({ name: request.session.user.name }, function(error, user) {
				if (!utils.Misc.isNullOrUndefined(user)) {
					if (user.isBlocked) { //TODO: test this
						request.session.reset();
					}
					else {
						request.user = utils.Misc.sanitizeUser(user);
						request.session.user = request.user;  //refresh the session value
						response.locals.user = request.user;  //make available to UI template engines
					}
				}
				next();
			});
		}
		else {
			next();
		}
	});

	app.use('/api', function (request, response, next) {
	  response.set('Content-Type', 'application/json');
	  next();
	});

	app.post('/public/upload', upload.any(), function (request, response) {
		var files = [];
		var results = [];
		var fileNames = [];

		if (!utils.Misc.isNullOrUndefined(request.file)) files.push(request.file);
		else if (!utils.Misc.isNullOrUndefined(request.files)) files = request.files;

		function loopThroughFiles (index) {
			if (index >= files.length) {
				response
					.set('Content-Type', 'application/json')
					.send(utils.Misc.createResponse(results));
				return;
			}

			var file = files[index];
			var fileName = "", filePath = "";

			function uploadToAWS(fileName, filePath) {
				var bucket = process.env.S3_BUCKET;
				var region = process.env.S3_REGION;

				AWS.config.update({ accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
					region: region });

				fs.readFile(filePath, function (err, data) {
					if (err) {
						results.push({original: file.originalname, url: process.env.BOLT_ADDRESS + '/' + filePath, error: err});
						loopThroughFiles(index + 1);
						return;
					}

					var base64data = new Buffer(data, 'binary');

					var s3 = new AWS.S3({signatureVersion: 'v4'});
					s3.putObject({
						Bucket: bucket,
						Key: fileName,
						Body: base64data,
						ACL: 'public-read'
					}, function (err, resp) {
						if (err) {
							//return the local file path (perhaps the user can retry)
							results.push({original: file.originalname, url: process.env.BOLT_ADDRESS + '/' + filePath, error: err});
							//fileNames.push(process.env.BOLT_ADDRESS + '/' + filePath);
						}
						else {
							results.push({original: file.originalname, url: 'https://s3.' + region + '.amazonaws.com/' + bucket + '/' + fileName});
							//fileNames.push('https://s3.' + region + '.amazonaws.com/' + bucket + '/' + fileName);
							fs.unlink(filePath, function(unlinkError){});
						}
						loopThroughFiles(index + 1);
					});

				});
			}

			//since multer seems not to add extensions, I'm doing it manually here
			var tempPath = path.resolve(file.path),
				targetPath = path.resolve(file.path + path.extname(file.originalname));
			fs.rename(tempPath, targetPath, function(renameError){
				//I can easily use targetPath (file.path + ext) but file.path uses '\' (instead of '/') as path separator,
				//with which Mozilla doesn't work well sometimes
				if(!utils.Misc.isNullOrUndefined(renameError)) { //if the file could not be renamed just use the original name
					fileName = file.filename;
					filePath = file.destination + file.filename;
				}
				else {
					fileName = file.filename + path.extname(file.originalname);
					filePath = file.destination + file.filename + path.extname(file.originalname);
				}

				if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY &&
					process.env.S3_BUCKET && process.env.S3_REGION) {
					uploadToAWS(fileName, filePath);
				}
				else {
					results.push({original: file.originalname, url: process.env.BOLT_ADDRESS + '/' + filePath});
					//fileNames.push(process.env.BOLT_ADDRESS + '/' + filePath);
					loopThroughFiles(index + 1);
				}
			});
		}

		loopThroughFiles(0);
	});

	app.delete('/public/upload/:file', function (request, response) {
		var filename = request.params.file;

		//delete local copy
		fs.unlink(path.resolve('public/bolt/uploads/' + filename), function(unlinkError){});

		//delete AWS S3 copy
		if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY &&
			process.env.S3_BUCKET && process.env.S3_REGION) {
			var bucket = process.env.S3_BUCKET;
			var region = process.env.S3_REGION;

			AWS.config.update({ accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
				region: region });

			var s3 = new AWS.S3();

			var params = {
				Bucket: bucket,
				Delete: {
					Objects: [
						{
							Key: filename
						}
					]
				}
			};

			s3.deleteObjects(params, function(err, data) {console.log("deleting: ", filename)
				//if (err) console.log(err, err.stack); // an error occurred
				//else     console.log(data);           // successful response
			});
		}

		response
			.set('Content-Type', 'application/json')
			.send(JSON.stringify({}));
	});

	app.use('/public', express.static(__publicdir));

	app.set('views', __sysdir + '/views');
	app.engine('html', exphbs.create({
		defaultLayout: 'main.html',
		layoutsDir: app.get('views') + '/layouts',
		partialsDir: [app.get('views') + '/partials'],
		helpers: {
			markdownToHtml: function(obj) {
				if (!obj) return "";

				var converter;
				try{
					converter = new Showdown.converter(); //lowercase 'C'
				}
				catch (e) {
					converter = new Showdown.Converter(); //uppercase 'C'
				}
				var content = converter.makeHtml(obj);
				return content;
			},
			markdownToHtmlString: function(obj) {
				if (!obj) return "";

				var converter;
				try{
					converter = new Showdown.converter(); //lowercase 'C'
				}
				catch (e) {
					converter = new Showdown.Converter(); //uppercase 'C'
				}
				var content = converter.makeHtml(obj);
				content = content;
				content = content.split("\n");
				content = content.join();
				content = content.split("'");
				content = content.join("\"");
				return content;
			},
			json: function(obj) {
				return JSON.stringify(obj);
			}
		}
	}).engine);
	app.set('view engine', 'html');

	return app;
};<commit_msg>Implemented /@<username> endpoint<commit_after>var models = require("bolt-internal-models");
var utils = require("bolt-internal-utils");

var AWS = require('aws-sdk');
var bodyParser = require('body-parser');
var exphbs = require('express-handlebars');
var express = require("express");
var fs = require("fs");
var path = require("path");
var session = require("client-sessions"/*"express-session"*/);
var Showdown = require("showdown");
var s3 = require('s3');

var __publicdir = path.join(__dirname + './../../public');
var __sysdir = path.join(__dirname + './../../sys');

const X_BOLT_USER_TOKEN = 'X-Bolt-User-Token';
const X_BOLT_USER_NAME = 'X-Bolt-User-Name';

var multer = require('multer'),
	upload = multer({ dest : 'public/bolt/uploads/'});

module.exports = function(app) {
	app.use(bodyParser.json({limit: '100mb'}));
	app.use(bodyParser.urlencoded({limit: '100mb', extended: true}));

	app.use(function (request, response, next) {
	  response.header('Access-Control-Allow-Origin', '*');
	  response.header('Access-Control-Allow-Headers',
	  	'Origin, X-Requested-With, Content-Type, Accept, X-Bolt-App-Token, X-Bolt-Locale, X-Bolt-API-Ver, X-Bolt-Perm-Token, X-Bolt-User-Token');
	  response.header('Access-Control-Allow-Methods', 'DELETE, GET, POST, PUT');

	  next();
	});

	app.use(session({
		cookieName: 'session',
		secret: process.env.BOLT_SESSION_SECRET,
		duration: 24 * 60 * 60 * 1000,
		activeDuration: 24 * 60 * 60 * 1000,
		//httpOnly: false

		/*saveUninitialized: true, 	//for express-session
		resave: true*/				//for express-session
	}));
	app.use(function(request, response, next) {
		if (!utils.Misc.isNullOrUndefined(request.get(X_BOLT_USER_NAME))) {
			var username = request.get(X_BOLT_USER_NAME);

			models.user.findOne({
				name: username
			}, function(error, user){
				if(!utils.Misc.isNullOrUndefined(user)){
					request.user = utils.Misc.sanitizeUser(user);
				}
				next();
			});
		}
		else if (!utils.Misc.isNullOrUndefined(request.get(X_BOLT_USER_TOKEN))) {
			var token = request.get(X_BOLT_USER_TOKEN);

			superagent
				.get(process.env.BOLT_ADDRESS + '/api/tokens/' + encodeURIComponent(token))
				.end(function(tokenError, tokenResponse){
					var realResponse = tokenResponse.body;
					if (!utils.Misc.isNullOrUndefined(realResponse) && !utils.Misc.isNullOrUndefined(realResponse.body)) {
						var userid = realResponse.body;
						models.user.findOne({
							_id: userid
						}, function(error, user){
							if(!utils.Misc.isNullOrUndefined(user)){
								request.user = utils.Misc.sanitizeUser(user);
							}
							next();
						});
					}
					else {
						next();
					}
				});
		}
		else if (!utils.Misc.isNullOrUndefined(request.session) && !utils.Misc.isNullOrUndefined(request.session.user)) {
			models.user.findOne({ name: request.session.user.name }, function(error, user) {
				if (!utils.Misc.isNullOrUndefined(user)) {
					if (user.isBlocked) { //TODO: test this
						request.session.reset();
					}
					else {
						request.user = utils.Misc.sanitizeUser(user);
						request.session.user = request.user;  //refresh the session value
						response.locals.user = request.user;  //make available to UI template engines
					}
				}
				next();
			});
		}
		else {
			next();
		}
	});

	app.use('/api', function (request, response, next) {
	  response.set('Content-Type', 'application/json');
	  next();
	});

	app.post('/public/upload', upload.any(), function (request, response) {
		var files = [];
		var results = [];
		var fileNames = [];

		if (!utils.Misc.isNullOrUndefined(request.file)) files.push(request.file);
		else if (!utils.Misc.isNullOrUndefined(request.files)) files = request.files;

		function loopThroughFiles (index) {
			if (index >= files.length) {
				response
					.set('Content-Type', 'application/json')
					.send(utils.Misc.createResponse(results));
				return;
			}

			var file = files[index];
			var fileName = "", filePath = "";

			function uploadToAWS(fileName, filePath) {
				var bucket = process.env.S3_BUCKET;
				var region = process.env.S3_REGION;

				AWS.config.update({ accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
					region: region });

				fs.readFile(filePath, function (err, data) {
					if (err) {
						results.push({original: file.originalname, url: process.env.BOLT_ADDRESS + '/' + filePath, error: err});
						loopThroughFiles(index + 1);
						return;
					}

					var base64data = new Buffer(data, 'binary');

					var s3 = new AWS.S3({signatureVersion: 'v4'});
					s3.putObject({
						Bucket: bucket,
						Key: fileName,
						Body: base64data,
						ACL: 'public-read'
					}, function (err, resp) {
						if (err) {
							//return the local file path (perhaps the user can retry)
							results.push({original: file.originalname, url: process.env.BOLT_ADDRESS + '/' + filePath, error: err});
							//fileNames.push(process.env.BOLT_ADDRESS + '/' + filePath);
						}
						else {
							results.push({original: file.originalname, url: 'https://s3.' + region + '.amazonaws.com/' + bucket + '/' + fileName});
							//fileNames.push('https://s3.' + region + '.amazonaws.com/' + bucket + '/' + fileName);
							fs.unlink(filePath, function(unlinkError){});
						}
						loopThroughFiles(index + 1);
					});

				});
			}

			//since multer seems not to add extensions, I'm doing it manually here
			var tempPath = path.resolve(file.path),
				targetPath = path.resolve(file.path + path.extname(file.originalname));
			fs.rename(tempPath, targetPath, function(renameError){
				//I can easily use targetPath (file.path + ext) but file.path uses '\' (instead of '/') as path separator,
				//with which Mozilla doesn't work well sometimes
				if(!utils.Misc.isNullOrUndefined(renameError)) { //if the file could not be renamed just use the original name
					fileName = file.filename;
					filePath = file.destination + file.filename;
				}
				else {
					fileName = file.filename + path.extname(file.originalname);
					filePath = file.destination + file.filename + path.extname(file.originalname);
				}

				if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY &&
					process.env.S3_BUCKET && process.env.S3_REGION) {
					uploadToAWS(fileName, filePath);
				}
				else {
					results.push({original: file.originalname, url: process.env.BOLT_ADDRESS + '/' + filePath});
					//fileNames.push(process.env.BOLT_ADDRESS + '/' + filePath);
					loopThroughFiles(index + 1);
				}
			});
		}

		loopThroughFiles(0);
	});

	app.delete('/public/upload/:file', function (request, response) {
		var filename = request.params.file;

		//delete local copy
		fs.unlink(path.resolve('public/bolt/uploads/' + filename), function(unlinkError){});

		//delete AWS S3 copy
		if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY &&
			process.env.S3_BUCKET && process.env.S3_REGION) {
			var bucket = process.env.S3_BUCKET;
			var region = process.env.S3_REGION;

			AWS.config.update({ accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
				region: region });

			var s3 = new AWS.S3();

			var params = {
				Bucket: bucket,
				Delete: {
					Objects: [
						{
							Key: filename
						}
					]
				}
			};

			s3.deleteObjects(params, function(err, data) {
				//if (err) console.log(err, err.stack); // an error occurred
				//else     console.log(data);           // successful response
			});
		}

		response
			.set('Content-Type', 'application/json')
			.send(JSON.stringify({}));
	});

	app.use('/public', express.static(__publicdir));

	app.set('views', __sysdir + '/views');
	app.engine('html', exphbs.create({
		defaultLayout: 'main.html',
		layoutsDir: app.get('views') + '/layouts',
		partialsDir: [app.get('views') + '/partials'],
		helpers: {
			markdownToHtml: function(obj) {
				if (!obj) return "";

				var converter;
				try{
					converter = new Showdown.converter(); //lowercase 'C'
				}
				catch (e) {
					converter = new Showdown.Converter(); //uppercase 'C'
				}
				var content = converter.makeHtml(obj);
				return content;
			},
			markdownToHtmlString: function(obj) {
				if (!obj) return "";

				var converter;
				try{
					converter = new Showdown.converter(); //lowercase 'C'
				}
				catch (e) {
					converter = new Showdown.Converter(); //uppercase 'C'
				}
				var content = converter.makeHtml(obj);
				content = content;
				content = content.split("\n");
				content = content.join();
				content = content.split("'");
				content = content.join("\"");
				return content;
			},
			json: function(obj) {
				return JSON.stringify(obj);
			}
		}
	}).engine);
	app.set('view engine', 'html');

	return app;
};<|endoftext|>


<commit_before>'use strict';

var wd = require('wd');
var SauceTunnel = require('sauce-tunnel');
var _ = require('grunt').util._;
var async = require('async');
var runner = require('./lib/mocha-runner');
var phantom = require('phantomjs');
var childProcess = require('child_process');

/*
 * grunt-mocha-sauce
 * https://github.com/jmreidy/grunt-mocha-sauce
 *
 * Copyright (c) 2013 Justin Reidy
 * Licensed under the MIT license
 */
module.exports = function (grunt) {
  grunt.registerMultiTask('mochaWebdriver', 'Run mocha tests against PhantomJS and SauceLabs', function () {

    var opts = this.options({
      username: process.env.SAUCE_USERNAME,
      key: process.env.SAUCE_ACCESS_KEY,
      identifier: Math.floor((new Date()).getTime() / 1000 - 1230768000).toString(),
      concurrency: 1,
      tunnelTimeout: 120,
      testName: "",
      testTags: []
    });

    grunt.util.async.forEachSeries(this.files, function (fileGroup, next) {
      if (opts.usePhantom) {
        runTestsOnPhantom(fileGroup, opts, next);
      }
      else {
        runTestsOnSaucelabs(fileGroup, opts, next);
      }
    }, this.async());
  });

  function runTestsForBrowser(opts, fileGroup, browser, next) {
    var onTestFinish = function(err) {
      browser.quit();
      next(err);
    };
    runner(opts, fileGroup, browser, grunt, onTestFinish);
  }

  function configureLogEvents(tunnel) {
    var methods = ['write', 'writeln', 'error', 'ok', 'debug'];
    methods.forEach(function (method) {
      tunnel.on('log:'+method, function (text) {
        grunt.log[method](text);
      });
      tunnel.on('verbose:'+method, function (text) {
        grunt.verbose[method](text);
      });
    });
  }

  function runTestsOnPhantom(fileGroup, opts, next) {
    var browser;
    var phantomPort = opts.phantomPort? opts.phantomPort : 4444;

    if (opts.usePromises) {
      browser = wd.promiseChainRemote({port: phantomPort});
    }
    else {
      browser = wd.remote({port: phantomPort});
    }
    grunt.log.writeln('Running webdriver tests against PhantomJS.');

    startPhantom(phantomPort, opts.ignoreSslErrors, function (err, phantomProc) {
      if (err) { return next(err); }
      browser.init({}, function () {
        runTestsForBrowser(opts, fileGroup, browser, function (err) {
          phantomProc.on('close', function () { next(err); });
          phantomProc.kill();
        });
      });
    });

  }

  function startPhantom(port, ignoreSslErrors, next) {
    var phantomOpts = ['--webdriver', port];
    if (ignoreSslErrors) {
      phantomOpts.push('--ignore-ssl-errors', 'yes');
    }
    var process = childProcess.execFile(phantom.path, phantomOpts);
    process.stdout.setEncoding('utf8');
    var onPhantomData = function (data) {
      if (data.match(/running/i)) {
        grunt.log.writeln('PhantomJS started.');
        process.stdout.removeListener('data', onPhantomData);
        next(process);
      }
      else if (data.match(/error/i)) {
        grunt.log.error('Error starting PhantomJS');
        next(new Error(data));
      }
    };
    process.stdout.on('data', onPhantomData);
  }

  function runTestsOnSaucelabs(fileGroup, opts, next) {
    if (opts.browsers) {
      var tunnel = new SauceTunnel(opts.username, opts.key, opts.identifier, true, opts.tunnelTimeout);
      configureLogEvents(tunnel);

      grunt.log.writeln("=> Connecting to Saucelabs ...");

      tunnel.start(function(isCreated) {
        if (!isCreated) {
          return next(new Error('Failed to create Sauce tunnel.'));
        }
        grunt.log.ok("Connected to Saucelabs.");

        var browser_failed = false;
        var testQueue = async.queue(function (browserOpts, cb) {
          var browser;
          if (opts.usePromises) {
            browser = wd.promiseRemote('ondemand.saucelabs.com', 80, opts.username, opts.key);
          }
          else {
            browser = wd.remote('ondemand.saucelabs.com', 80, opts.username, opts.key);
          }
          browser.browserTitle = browserOpts.browserTitle;
          browserOpts = _.extend(browserOpts, {
            name: opts.testName,
            tags: opts.testTags,
            'tunnel-identifier': opts.identifier
          });

          browser.init(browserOpts, function (err) {
            if (err) {
              grunt.log.error("Could not initialize browser on Saucelabs");
              return cb(false);
            }
            runTestsForBrowser(opts, fileGroup, browser, cb);
          });
        }, opts.concurrency);

        opts.browsers.forEach(function (browserOpts) {
          var browserTitle = ''+browserOpts.browserName + ' ' + browserOpts.version + ' on ' + browserOpts.platform;
          browserOpts.browserTitle = browserTitle;
          grunt.log.verbose.writeln('Queueing ' + browserTitle + ' on Saucelabs.');
          testQueue.push(browserOpts, function (err) {
            if (err) {
              browser_failed = true;
            }
            grunt.log.verbose.writeln('%s test complete, %s tests remaining', browserTitle, testQueue.length());
          });
        });

        testQueue.drain = function () {
          var err;
          if (browser_failed) {
            err = new Error('One or more tests on Sauce Labs failed.');
          }
          tunnel.stop(function () {
            next(err);
          });
        };
      });
    }
    else {
      grunt.log.writeln('No browsers configured for running on Saucelabs.');
    }
  }

};

//wd.js monkey patch for clearer errors
var _newError = wd.webdriver.prototype._newError;
wd.webdriver.prototype._newError = function (opts) {
  var err = _newError(opts);
  try {
    err = new Error(err.cause.value.message
      .match(/([\s\S]*) caused/)[1]
      .match(/'([\s\S]*)'\n/)[1]
    );
  }
  catch (e) {}
  return err;
};
<commit_msg>add phantom exited logger statement<commit_after>'use strict';

var wd = require('wd');
var SauceTunnel = require('sauce-tunnel');
var _ = require('grunt').util._;
var async = require('async');
var runner = require('./lib/mocha-runner');
var phantom = require('phantomjs');
var childProcess = require('child_process');

/*
 * grunt-mocha-sauce
 * https://github.com/jmreidy/grunt-mocha-sauce
 *
 * Copyright (c) 2013 Justin Reidy
 * Licensed under the MIT license
 */
module.exports = function (grunt) {
  grunt.registerMultiTask('mochaWebdriver', 'Run mocha tests against PhantomJS and SauceLabs', function () {

    var opts = this.options({
      username: process.env.SAUCE_USERNAME,
      key: process.env.SAUCE_ACCESS_KEY,
      identifier: Math.floor((new Date()).getTime() / 1000 - 1230768000).toString(),
      concurrency: 1,
      tunnelTimeout: 120,
      testName: "",
      testTags: []
    });

    grunt.util.async.forEachSeries(this.files, function (fileGroup, next) {
      if (opts.usePhantom) {
        runTestsOnPhantom(fileGroup, opts, next);
      }
      else {
        runTestsOnSaucelabs(fileGroup, opts, next);
      }
    }, this.async());
  });

  function runTestsForBrowser(opts, fileGroup, browser, next) {
    var onTestFinish = function(err) {
      browser.quit();
      next(err);
    };
    runner(opts, fileGroup, browser, grunt, onTestFinish);
  }

  function configureLogEvents(tunnel) {
    var methods = ['write', 'writeln', 'error', 'ok', 'debug'];
    methods.forEach(function (method) {
      tunnel.on('log:'+method, function (text) {
        grunt.log[method](text);
      });
      tunnel.on('verbose:'+method, function (text) {
        grunt.verbose[method](text);
      });
    });
  }

  function runTestsOnPhantom(fileGroup, opts, next) {
    var browser;
    var phantomPort = opts.phantomPort? opts.phantomPort : 4444;

    if (opts.usePromises) {
      browser = wd.promiseChainRemote({port: phantomPort});
    }
    else {
      browser = wd.remote({port: phantomPort});
    }
    grunt.log.writeln('Running webdriver tests against PhantomJS.');

    startPhantom(phantomPort, opts.ignoreSslErrors, function (err, phantomProc) {
      if (err) { return next(err); }
      browser.init({}, function () {
        runTestsForBrowser(opts, fileGroup, browser, function (err) {
          phantomProc.on('close', function () {
            grunt.log.writeln('Phantom exited.');
            next(err);
          });
          phantomProc.kill();
        });
      });
    });

  }

  function startPhantom(port, ignoreSslErrors, next) {
    var phantomOpts = ['--webdriver', port];
    if (ignoreSslErrors) {
      phantomOpts.push('--ignore-ssl-errors', 'yes');
    }
    var process = childProcess.execFile(phantom.path, phantomOpts);
    process.stdout.setEncoding('utf8');
    var onPhantomData = function (data) {
      if (data.match(/running/i)) {
        grunt.log.writeln('PhantomJS started.');
        process.stdout.removeListener('data', onPhantomData);
        next(process);
      }
      else if (data.match(/error/i)) {
        grunt.log.error('Error starting PhantomJS');
        next(new Error(data));
      }
    };
    process.stdout.on('data', onPhantomData);
  }

  function runTestsOnSaucelabs(fileGroup, opts, next) {
    if (opts.browsers) {
      var tunnel = new SauceTunnel(opts.username, opts.key, opts.identifier, true, opts.tunnelTimeout);
      configureLogEvents(tunnel);

      grunt.log.writeln("=> Connecting to Saucelabs ...");

      tunnel.start(function(isCreated) {
        if (!isCreated) {
          return next(new Error('Failed to create Sauce tunnel.'));
        }
        grunt.log.ok("Connected to Saucelabs.");

        var browser_failed = false;
        var testQueue = async.queue(function (browserOpts, cb) {
          var browser;
          if (opts.usePromises) {
            browser = wd.promiseRemote('ondemand.saucelabs.com', 80, opts.username, opts.key);
          }
          else {
            browser = wd.remote('ondemand.saucelabs.com', 80, opts.username, opts.key);
          }
          browser.browserTitle = browserOpts.browserTitle;
          browserOpts = _.extend(browserOpts, {
            name: opts.testName,
            tags: opts.testTags,
            'tunnel-identifier': opts.identifier
          });

          browser.init(browserOpts, function (err) {
            if (err) {
              grunt.log.error("Could not initialize browser on Saucelabs");
              return cb(false);
            }
            runTestsForBrowser(opts, fileGroup, browser, cb);
          });
        }, opts.concurrency);

        opts.browsers.forEach(function (browserOpts) {
          var browserTitle = ''+browserOpts.browserName + ' ' + browserOpts.version + ' on ' + browserOpts.platform;
          browserOpts.browserTitle = browserTitle;
          grunt.log.verbose.writeln('Queueing ' + browserTitle + ' on Saucelabs.');
          testQueue.push(browserOpts, function (err) {
            if (err) {
              browser_failed = true;
            }
            grunt.log.verbose.writeln('%s test complete, %s tests remaining', browserTitle, testQueue.length());
          });
        });

        testQueue.drain = function () {
          var err;
          if (browser_failed) {
            err = new Error('One or more tests on Sauce Labs failed.');
          }
          tunnel.stop(function () {
            next(err);
          });
        };
      });
    }
    else {
      grunt.log.writeln('No browsers configured for running on Saucelabs.');
    }
  }

};

//wd.js monkey patch for clearer errors
var _newError = wd.webdriver.prototype._newError;
wd.webdriver.prototype._newError = function (opts) {
  var err = _newError(opts);
  try {
    err = new Error(err.cause.value.message
      .match(/([\s\S]*) caused/)[1]
      .match(/'([\s\S]*)'\n/)[1]
    );
  }
  catch (e) {}
  return err;
};
<|endoftext|>


<commit_before>/*
 *
 * 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.
 *
*/

/*
 * This probably should live in plugman/cli world
 * Transoforms old require calls to new node-style require calls
 * the whole thing is fucking bullshit and needs to disappear ASAP
 */

var fs = require('fs');
var path = require('path');
var util = require('util');
var through = require('through');
var UglifyJS = require('uglify-js');
var root = fs.realpathSync(path.join(__dirname, '..', '..'));

var requireTr = {

  transform: function(file) {
    var data = '';

    function write(buf) {
      data += buf;
    }

    function end() {
        // SOME BS pre-transforms
      if(data.match(/clobbers\('cordova\/plugin\/android\/app/)) {
        // Checking for '\' from the windows path
        root = root.replace(/\\/g, '/');

        if(file.match(/android\/platform.js$/)) {
          data = data.replace(/modulemapper\.clobbers.*\n/,
                              util.format('navigator.app = require("%s/src/android/plugin/android/app")', root));
        } else if (file.match(/amazon-fireos\/platform.js$/)) {
          data = data.replace(/modulemapper\.clobbers.*\n/,
                              util.format('navigator.app = require("%s/src/amazon-fireos/plugin/android/app")', root));
        }
      }

      if(data.match(/clobbers\('cordova\/exec\/proxy/)) {
        // Checking for '\' from the windows path
        root = root.replace(/\\/g, '/');

        data = data.replace(/modulemapper\.clobbers.*\n/,
                            util.format('cordova.commandProxy = require("%s/src/common/exec/proxy");', root));
      }

      if(file.match(/FileReader.js$/)) {
        data = data.replace(/getOriginalSymbol\(this/,
                            'getOriginalSymbol(window');
      }

      this.queue(_updateRequires(data));
      this.queue(null);
    }

    return through(write, end);
  },
  getModules: function() {
    return this.modules;
  },
  addModule: function(module) {
    if(!module || !module.symbol || !module.path) {
      throw new Error("Can't add module without a symbol and a path");
    }
    this.modules.push(module);
  },
  platform: null,
  modules: []

}

/*
 * visits AST and modifies all the require('cordova/*') and require('org.apache.cordova.*')
 */
function _updateRequires(code) {
  var ast = UglifyJS.parse(code);

  var before = new UglifyJS.TreeTransformer(function(node, descend) {

    // check all function calls
    if(node instanceof UglifyJS.AST_Call) {
      // check if function call is a require('module') call
      if(node.expression.name === "require" && node.args.length === 1) {

        // Uglify is not able to recognize Windows style paths using '\' instead of '/'
        // So replacing all of the '/' back to Windows '\'

        // FIXME: need to better handle cases of modulemapper replace
        if (node.args[0].value !== undefined && node.args[0].value.indexOf(root) !== -1 && process.platform === 'win32') {
            node.args[0].value = node.args[0].value.replace(/\//g, '\\');
        }

        var module = node.args[0].value;

        // make sure require only has one argument and that it starts with cordova (old style require.js)
        if(module !== undefined &&
           module.indexOf("cordova") === 0) {

          // adding symbolList bullcrap
          if(requireTr.symbolList && requireTr.symbolList.indexOf(module) === -1) {
            requireTr.symbolList.push(module);
          }

          // require('cordova') -> cordova.js
          if(module === "cordova") {
            node.args[0].value = path.join(root, "src", "cordova_b");
          // require('cordova/init') -> common/init
          }  else if(module.match(/cordova\/init/)) {
            node.args[0].value = module.replace(/cordova\/init/,
                                    path.join(root, "src", "common", "init_b"));
          // android and amazon-fireos have some special require's
          } else if(module.match(/cordova\/(android|amazon-fireos)\/(.+)/)) {
            node.args[0].value = module.replace(/cordova\/(android|amazon-fireos)\/(.+)/,
                                    path.join(root, "src", "$1", "android", "$2"));
          // require('cordova/exec') and require('cordova/platform') -> platform's exec/platform
          } else if(module.match(/cordova\/(platform|exec)$/)) {
            node.args[0].value = module.replace(/cordova\/(platform|exec)/,
                                                path.join(root, "src", requireTr.platform, "$1"));
          // require('cordova/anything') should be under common/
          } else if(module.match(/cordova\/(.+)/)) {
            node.args[0].value = module.replace(/cordova\/(.+)/,
                                    path.join(root, "src", "common", "$1"));
          }
        }
        else if(module !== undefined && ( module.indexOf("org.apache.cordova") !== -1 ||
                                          module.indexOf("./") === 0 || module.indexOf("../") === 0 ) ) {
          var modules = requireTr.getModules();

          if(module.indexOf("../") === 0){
            module = module.replace('../', '');
          }
          if(module.indexOf("./") === 0 ) {
            module = module.replace('./', '');
          }

          for(var i = 0, j = modules.length ; i < j ; i++) {
            var regx = new RegExp ("\\."+ module + "$");
            if(module === modules[i].symbol || modules[i].symbol.search(regx) != -1) {
                node.args[0].value = modules[i].path;
                break;
            }
          }
        }
        descend(node, this);
        return node;
      }
    }
  });

  ast.transform(before, null);

  var stream = UglifyJS.OutputStream({beautify:true});

  ast.print(stream);

  return stream.toString();
}

module.exports = requireTr;
<commit_msg>Checking for windows style path<commit_after>/*
 *
 * 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.
 *
*/

/*
 * This probably should live in plugman/cli world
 * Transoforms old require calls to new node-style require calls
 * the whole thing is fucking bullshit and needs to disappear ASAP
 */

var fs = require('fs');
var path = require('path');
var util = require('util');
var through = require('through');
var UglifyJS = require('uglify-js');
var root = fs.realpathSync(path.join(__dirname, '..', '..'));

var requireTr = {

  transform: function(file) {
    var data = '';

    function write(buf) {
      data += buf;
    }

    function end() {
        // SOME BS pre-transforms
      if(data.match(/clobbers\('cordova\/plugin\/android\/app/)) {
        // Checking for '\' from the windows path
        root = root.replace(/\\/g, '/');

        if(file.match(/android\/platform.js$/) || file.match(/android\\platform.js$/)) {
          data = data.replace(/modulemapper\.clobbers.*\n/,
                              util.format('navigator.app = require("%s/src/android/plugin/android/app")', root));
        } else if (file.match(/amazon-fireos\/platform.js$/) || file.match(/amazon-fireos\\platform.js$/)) {
          data = data.replace(/modulemapper\.clobbers.*\n/,
                              util.format('navigator.app = require("%s/src/amazon-fireos/plugin/android/app")', root));
        }
      }

      if(data.match(/clobbers\('cordova\/exec\/proxy/)) {
        // Checking for '\' from the windows path
        root = root.replace(/\\/g, '/');

        data = data.replace(/modulemapper\.clobbers.*\n/,
                            util.format('cordova.commandProxy = require("%s/src/common/exec/proxy");', root));
      }

      if(file.match(/FileReader.js$/)) {
        data = data.replace(/getOriginalSymbol\(this/,
                            'getOriginalSymbol(window');
      }

      this.queue(_updateRequires(data));
      this.queue(null);
    }

    return through(write, end);
  },
  getModules: function() {
    return this.modules;
  },
  addModule: function(module) {
    if(!module || !module.symbol || !module.path) {
      throw new Error("Can't add module without a symbol and a path");
    }
    this.modules.push(module);
  },
  platform: null,
  modules: []

}

/*
 * visits AST and modifies all the require('cordova/*') and require('org.apache.cordova.*')
 */
function _updateRequires(code) {
  var ast = UglifyJS.parse(code);

  var before = new UglifyJS.TreeTransformer(function(node, descend) {

    // check all function calls
    if(node instanceof UglifyJS.AST_Call) {
      // check if function call is a require('module') call
      if(node.expression.name === "require" && node.args.length === 1) {

        // Uglify is not able to recognize Windows style paths using '\' instead of '/'
        // So replacing all of the '/' back to Windows '\'

        // FIXME: need to better handle cases of modulemapper replace
        if (node.args[0].value !== undefined && node.args[0].value.indexOf(root) !== -1 && process.platform === 'win32') {
            node.args[0].value = node.args[0].value.replace(/\//g, '\\');
        }

        var module = node.args[0].value;

        // make sure require only has one argument and that it starts with cordova (old style require.js)
        if(module !== undefined &&
           module.indexOf("cordova") === 0) {

          // adding symbolList bullcrap
          if(requireTr.symbolList && requireTr.symbolList.indexOf(module) === -1) {
            requireTr.symbolList.push(module);
          }

          // require('cordova') -> cordova.js
          if(module === "cordova") {
            node.args[0].value = path.join(root, "src", "cordova_b");
          // require('cordova/init') -> common/init
          }  else if(module.match(/cordova\/init/)) {
            node.args[0].value = module.replace(/cordova\/init/,
                                    path.join(root, "src", "common", "init_b"));
          // android and amazon-fireos have some special require's
          } else if(module.match(/cordova\/(android|amazon-fireos)\/(.+)/)) {
            node.args[0].value = module.replace(/cordova\/(android|amazon-fireos)\/(.+)/,
                                    path.join(root, "src", "$1", "android", "$2"));
          // require('cordova/exec') and require('cordova/platform') -> platform's exec/platform
          } else if(module.match(/cordova\/(platform|exec)$/)) {
            node.args[0].value = module.replace(/cordova\/(platform|exec)/,
                                                path.join(root, "src", requireTr.platform, "$1"));
          // require('cordova/anything') should be under common/
          } else if(module.match(/cordova\/(.+)/)) {
            node.args[0].value = module.replace(/cordova\/(.+)/,
                                    path.join(root, "src", "common", "$1"));
          }
        }
        else if(module !== undefined && ( module.indexOf("org.apache.cordova") !== -1 ||
                                          module.indexOf("./") === 0 || module.indexOf("../") === 0 ) ) {
          var modules = requireTr.getModules();

          if(module.indexOf("../") === 0){
            module = module.replace('../', '');
          }
          if(module.indexOf("./") === 0 ) {
            module = module.replace('./', '');
          }

          for(var i = 0, j = modules.length ; i < j ; i++) {
            var regx = new RegExp ("\\."+ module + "$");
            if(module === modules[i].symbol || modules[i].symbol.search(regx) != -1) {
                node.args[0].value = modules[i].path;
                break;
            }
          }
        }
        descend(node, this);
        return node;
      }
    }
  });

  ast.transform(before, null);

  var stream = UglifyJS.OutputStream({beautify:true});

  ast.print(stream);

  return stream.toString();
}

module.exports = requireTr;
<|endoftext|>