<commit_before>// !!!!!!!!!!!!!!!!!!!!!!!!!!
// Depends on basic-utils.js
// !!!!!!!!!!!!!!!!!!!!!!!!!!

/**
 * Creates a child window, including a document with an HTML 5 doctype, UFT-8 charset, head, title, and body tags.
 * Returns the handle, or undefined if window creation fails.
 *
 * Optionally accepts a jQuery Deferred. The deferred is resolved when the document in the child window is ready and the
 * window has expanded to its intended size. If the child window can't be created, the deferred is rejected. (For this,
 * jQuery needs to be loaded, obviously.)
 *
 * The size can also be specified. If so, the new window is opened with minimal browser chrome (no menu, location, and
 * status bars) and is positioned at the top left of the viewport. By default, the window is opened with the default
 * settings of the browser (usually with browser chrome, and not exactly in the top left corner).
 *
 * If the child window can't be created, a pop-up blocker usually prevents it. Pop-up blockers are active by default in
 * most browsers - Chrome, for instance.
 *
 * @param   {jQuery.Deferred} [readyDfd]
 * @param   {Object|string}   [size]          "parent" (same size as parent window), or size object
 * @param   {number}          [size.width]
 * @param   {number}          [size.height]
 *
 * @returns {Window|undefined}                the window handle
 */
function createChildWindow ( readyDfd, size ) {
    var childWindow, width, height,
        sizedDefaultProps = ",top=0,left=0,location=no,menubar=no,status=no,toolbar=no,resizeable=yes,scrollbars=yes";

    if ( size ) {

        width = size === "parent" ? window.document.documentElement.clientWidth : size.width;
        height = size === "parent" ? window.document.documentElement.clientHeight : size.height;

        childWindow = window.open( "", "", "width=" + width + ",height=" + height + sizedDefaultProps );

    } else {

        childWindow = window.open();

    }

    if ( childWindow ) {

        // Setting the document content (using plain JS - jQuery can't write an entire HTML document, including the
        // doctype and  tags).
        childWindow.document.open();
        childWindow.document.write( '<!DOCTYPE html>\n\n\n<meta charset="UTF-8">\n<title></title>\n\n\n\n' );
        childWindow.document.close();

    }

    if ( readyDfd ) {
        if ( ! varExists( $ ) ) throw new Error( "`$` variable is not available. For using a readyDfd, jQuery (or a compatible library) must be loaded" );

        if (  childWindow && childWindow.document ) {
            $( childWindow.document ).ready ( function () {
                windowSizeReady( childWindow, readyDfd );
            } );
        } else {
            readyDfd.reject();
        }
    }

    return childWindow;
}

/**
 * Creates an iframe with an HTML5 doctype and UTF-8 encoding. Appends it to the body, or to another specified parent
 * element. Alternatively, the iframe can be prepended to the parent.
 *
 * The iframe element can be styled as it is created, before it is added to the DOM, e.g. to keep it out of view.
 * Likewise, styles can be written into the iframe document as it is created, providing it with defaults from the
 * get-go.
 *
 * @param   {Object}             [opts]
 * @param   {HTMLElement|jQuery} [opts.parent=document.body]  the parent element to which the iframe is appended
 * @param   {boolean}            [opts.prepend=false]         if true, the iframe gets prepended to the parent, rather than appended
 * @param   {string}             [opts.elementStyles]         cssText string, styles the iframe _element_
 * @param   {string}             [opts.documentStyles]        cssText string of entire rules, styles the iframe document, e.g.
 *                                                            "html, body { overflow: hidden; } div.foo { margin: 2em; }"
 * @returns {HTMLIFrameElement}
 */
function createIframe ( opts ) {
    var parent = ( opts && opts.parent ) ? ( varExists( $ ) && opts.parent instanceof $ ) ? opts.parent[0] : opts.parent : document.body,
        _document = parent.ownerDocument,
        iframe = _document.createElement( "iframe" );

    opts || ( opts = {} );

    if ( opts.elementStyles ) iframe.style.cssText = ensureTrailingSemicolon( opts.elementStyles );
    iframe.frameborder = "0";

    if ( opts.prepend ) {
        parent.insertBefore( iframe, parent.firstChild );
    } else {
        parent.appendChild( iframe );
    }

    iframe.src = 'about:blank';
    createIframeDocument( iframe, opts.documentStyles );

    return iframe;
}

/**
 * Creates an iframe document with an HTML5 doctype and UTF-8 encoding.
 *
 * The iframe element MUST have been appended to the DOM by the time this function is called, and it must be a
 * descendant of the body element. A document inside an iframe can only be created when these conditions are met.
 *
 * @param   {HTMLIFrameElement|jQuery}  iframe
 * @param   {string}                    [documentStyles]  cssText string of CSS rules, styles the iframe document, e.g.
 *                                                        "html, body { overflow: hidden; } div.foo { margin: 2em; }"
 * @returns {Document}
 */
function createIframeDocument ( iframe, documentStyles ) {
    if ( varExists( $ ) && iframe instanceof $ ) iframe = iframe[0];

    if ( ! iframe.ownerDocument.body.contains( iframe ) ) throw new Error( "The iframe has not been appended to the DOM, or is not a descendant of the body element. Can't create an iframe content document." );
    if ( ! iframe.contentDocument ) throw new Error( "Cannot access the iframe content document. Check for cross-domain policy restrictions." );

    if ( documentStyles ) documentStyles = '\n';
    iframe.contentDocument.write( '<!DOCTYPE html>\n\n\n<meta charset="UTF-8">\n<title></title>\n' + documentStyles + '\n\n\n' );

    return iframe.contentDocument;
}

/**
 * Waits for the size of a window to become stable, in case it is undergoing a change. Returns a deferred which resolves
 * when the window size is stable.
 *
 * Optionally accepts an external jQuery deferred to act on, which is then returned instead.
 *
 * This check can be used to determine when the process of resizing a window has ended. It should also be used when a
 * new window is created.
 *
 * Technique
 * ---------
 *
 * In most cases, it would be enough to set a timeout of 0 and then resolve the deferred. The timeout frees the UI and
 * allows the window to assume its eventual size before the deferred is resolved.
 *
 * Unfortunately, the success rate of this approach is close to, but not quite, 100%. So instead, we check the reported
 * window size in regular intervals, so we know for sure when it is stable.
 *
 * @param {Window|jQuery}   queriedWindow   the window to observe, also accepted inside a jQuery `$( window )` wrapper
 * @param {jQuery.Deferred} [readyDfd]
 * @param {number}          [interval=100]  the interval for checking the window size, in ms
 *
 * @returns {jQuery.Deferred}
 */
function windowSizeReady ( queriedWindow, readyDfd, interval ) {

    if ( !varExists( $ ) ) throw new Error( "This method uses jQuery deferreds, but the $ variable is not available" );

    if ( queriedWindow instanceof $ ) queriedWindow = queriedWindow[0];

    readyDfd || ( readyDfd = $.Deferred() );

    $( queriedWindow.document ).ready( function () {

        var documentElement = queriedWindow.document.documentElement,
            lastSize = { width: documentElement.clientWidth, height: documentElement.clientHeight },

            repeater = setInterval( function () {

                var width = documentElement.clientWidth,
                    height = documentElement.clientHeight,
                    isStable = width > 0 && height > 0 && width === lastSize.width && height === lastSize.height;

                if ( isStable ) {
                    clearInterval( repeater );
                    readyDfd.resolve();
                } else {
                    lastSize = { width: width, height: height };
                }

            }, interval || 100 );

    } );

    return readyDfd;

}

/**
 * Makes sure a window is as at least as large as the specified minimum. If the window is too small, an error
 * is thrown.
 *
 * Optionally, it can be validated that the window matches the expected size exactly.
 *
 * @param {Object}  expected
 * @param {number}  expected.width
 * @param {number}  expected.height
 * @param {Object}  [opts]
 * @param {Object}  [opts.window=window]  a window handle, defaults to the global `window`
 * @param {boolean} [opts.exactly=false]
 */
function validateWindowSize ( expected, opts ) {
    var msg = "",
        documentElement = ( opts && opts.window || window ).document.documentElement,
        width = documentElement.clientWidth,
        height = documentElement.clientHeight;

    if ( opts && opts.exactly ) {
        if ( width !== expected.width ) msg = " Window width is " + width + "px (expected: " + expected.width + "px).";
        if ( height !== expected.height ) msg += " Window height is " + height + "px (expected: " + expected.height + "px).";
    } else {
        if ( width < expected.width ) msg = " Window width is " + width + "px (expected minimum: " + expected.width + "px).";
        if ( height < expected.height ) msg += " Window height is " + height + "px (expected minimum: " + expected.height + "px).";
    }

    if ( msg !== "" ) throw new Error( "The browser window does not match the expected size." + msg );
}

/**
 * Feature-tests that an iframe expands to show its content, even if given an explicit width and height. This is the
 * case on iOS.
 *
 * For some background on expanding iframes in iOS, see
 * http://dev.magnolia-cms.com/blog/2012/05/strategies-for-the-iframe-on-the-ipad-problem/
 *
 * @returns {boolean}
 */
function testIframeExpands () {
    var _document, _documentElement, expands,
        iframe = createIframe();

    iframe.style.cssText = "width: 50px; height: 50px; padding: 0px; border: none; margin: 0px;";

    _document = iframe.contentDocument;
    _documentElement = _document.documentElement;

    _document.body.style.cssText = "width: 100px; height: 100px;";

    expands = parseFloat( _documentElement.clientWidth ) > 50 || parseFloat( _documentElement.clientHeight ) > 50;

    document.body.removeChild( iframe );

    return expands;
}

/**
 * Forces a reflow for a given element, in case it doesn't happen automatically.
 *
 * For the technique, see http://stackoverflow.com/a/14382251/508355
 *
 * For some background, see e.g. http://apmblog.dynatrace.com/2009/12/12/understanding-internet-explorer-rendering-behaviour/
 *
 * @param {HTMLElement|jQuery} element
 */
function forceReflow ( element ) {
    if ( !varExists( $ ) ) throw new Error( "This method uses jQuery, but the $ variable is not available" );

    var $element =  element instanceof $ ? element : $( element );

    $element.css( { display: "none" } ).height();
    $element.css( { display: "block" } );
}

/**
 * Detects if the browser is on iOS. Works for Safari as well as other browsers, say, Chrome on iOS.
 *
 * Required for some iOS behaviour which can't be feature-detected in any way.
 *
 * @returns {boolean}
 */
function isIOS () {
    return /iPad|iPhone|iPod/g.test( navigator.userAgent );
}

/**
 * Detects IE.
 *
 * Can use a version requirement. A range can also be specified, e.g. with an option like { gte: 8, lt: 11 }.
 *
 * @param {Object} [opts]
 * @param {number} [opts.eq]   the IE version must be as specified
 * @param {number} [opts.lt]   the IE version must be less than the one specified
 * @param {number} [opts.lte]  the IE version must be less than or equal to the one specified
 * @param {number} [opts.gt]   the IE version must be greater than the one specified
 * @param {number} [opts.gte]  the IE version must be greater than or equal to the one specified
 */
function isIE ( opts ) {
    var ver = getIEVersion(),
        isMatch = ver !== 0;

    opts || ( opts = {} );

    if ( isMatch && opts.eq ) isMatch = ver === opts.eq;
    if ( isMatch && opts.lt ) isMatch = ver < opts.lt;
    if ( isMatch && opts.lte ) isMatch = ver <= opts.lte;
    if ( isMatch && opts.gt ) isMatch = ver > opts.gt;
    if ( isMatch && opts.gte ) isMatch = ver >= opts.gte;

    return isMatch;
}

/**
 * Detects the IE version. Returns the major version number, or 0 if the browser is not IE.
 *
 * Simple solution, solely based on UA sniffing. In a better implementation, conditional comments would be used to
 * detect IE6 to IE9 - see https://gist.github.com/cowboy/542301 for an example. UA sniffing would only serve as a
 * fallback to detect IE > 9. There are also other solutions to infer the version of IE > 9. For inspiration, see
 * http://stackoverflow.com/q/17907445/508355.
 */
function getIEVersion () {
    var ieMatch = /MSIE (\d+)/.exec( navigator.userAgent ) || /Trident\/.+? rv:(\d+)/.exec( navigator.userAgent );
    return ( ieMatch && ieMatch.length ) ? parseFloat( ieMatch[1] ) : 0;
}

function isPhantomJs () {
    return /PhantomJS/.test( navigator.userAgent );
}

// Legacy name, still around in old test code.
function inPhantomJs () {
    return isPhantomJs();
}

<commit_msg>Fixed bug in iframe test helper<commit_after>// !!!!!!!!!!!!!!!!!!!!!!!!!!
// Depends on basic-utils.js
// !!!!!!!!!!!!!!!!!!!!!!!!!!

/**
 * Creates a child window, including a document with an HTML 5 doctype, UFT-8 charset, head, title, and body tags.
 * Returns the handle, or undefined if window creation fails.
 *
 * Optionally accepts a jQuery Deferred. The deferred is resolved when the document in the child window is ready and the
 * window has expanded to its intended size. If the child window can't be created, the deferred is rejected. (For this,
 * jQuery needs to be loaded, obviously.)
 *
 * The size can also be specified. If so, the new window is opened with minimal browser chrome (no menu, location, and
 * status bars) and is positioned at the top left of the viewport. By default, the window is opened with the default
 * settings of the browser (usually with browser chrome, and not exactly in the top left corner).
 *
 * If the child window can't be created, a pop-up blocker usually prevents it. Pop-up blockers are active by default in
 * most browsers - Chrome, for instance.
 *
 * @param   {jQuery.Deferred} [readyDfd]
 * @param   {Object|string}   [size]          "parent" (same size as parent window), or size object
 * @param   {number}          [size.width]
 * @param   {number}          [size.height]
 *
 * @returns {Window|undefined}                the window handle
 */
function createChildWindow ( readyDfd, size ) {
    var childWindow, width, height,
        sizedDefaultProps = ",top=0,left=0,location=no,menubar=no,status=no,toolbar=no,resizeable=yes,scrollbars=yes";

    if ( size ) {

        width = size === "parent" ? window.document.documentElement.clientWidth : size.width;
        height = size === "parent" ? window.document.documentElement.clientHeight : size.height;

        childWindow = window.open( "", "", "width=" + width + ",height=" + height + sizedDefaultProps );

    } else {

        childWindow = window.open();

    }

    if ( childWindow ) {

        // Setting the document content (using plain JS - jQuery can't write an entire HTML document, including the
        // doctype and  tags).
        childWindow.document.open();
        childWindow.document.write( '<!DOCTYPE html>\n\n\n<meta charset="UTF-8">\n<title></title>\n\n\n\n' );
        childWindow.document.close();

    }

    if ( readyDfd ) {
        if ( ! varExists( $ ) ) throw new Error( "`$` variable is not available. For using a readyDfd, jQuery (or a compatible library) must be loaded" );

        if (  childWindow && childWindow.document ) {
            $( childWindow.document ).ready ( function () {
                windowSizeReady( childWindow, readyDfd );
            } );
        } else {
            readyDfd.reject();
        }
    }

    return childWindow;
}

/**
 * Creates an iframe with an HTML5 doctype and UTF-8 encoding. Appends it to the body, or to another specified parent
 * element. Alternatively, the iframe can be prepended to the parent.
 *
 * The iframe element can be styled as it is created, before it is added to the DOM, e.g. to keep it out of view.
 * Likewise, styles can be written into the iframe document as it is created, providing it with defaults from the
 * get-go.
 *
 * @param   {Object}             [opts]
 * @param   {HTMLElement|jQuery} [opts.parent=document.body]  the parent element to which the iframe is appended
 * @param   {boolean}            [opts.prepend=false]         if true, the iframe gets prepended to the parent, rather than appended
 * @param   {string}             [opts.elementStyles]         cssText string, styles the iframe _element_
 * @param   {string}             [opts.documentStyles]        cssText string of entire rules, styles the iframe document, e.g.
 *                                                            "html, body { overflow: hidden; } div.foo { margin: 2em; }"
 * @returns {HTMLIFrameElement}
 */
function createIframe ( opts ) {
    var parent = ( opts && opts.parent ) ? ( varExists( $ ) && opts.parent instanceof $ ) ? opts.parent[0] : opts.parent : document.body,
        _document = parent.ownerDocument,
        iframe = _document.createElement( "iframe" );

    opts || ( opts = {} );

    if ( opts.elementStyles ) iframe.style.cssText = ensureTrailingSemicolon( opts.elementStyles );
    iframe.frameborder = "0";

    if ( opts.prepend ) {
        parent.insertBefore( iframe, parent.firstChild );
    } else {
        parent.appendChild( iframe );
    }

    iframe.src = 'about:blank';
    createIframeDocument( iframe, opts.documentStyles );

    return iframe;
}

/**
 * Creates an iframe document with an HTML5 doctype and UTF-8 encoding.
 *
 * The iframe element MUST have been appended to the DOM by the time this function is called, and it must be a
 * descendant of the body element. A document inside an iframe can only be created when these conditions are met.
 *
 * @param   {HTMLIFrameElement|jQuery}  iframe
 * @param   {string}                    [documentStyles]  cssText string of CSS rules, styles the iframe document, e.g.
 *                                                        "html, body { overflow: hidden; } div.foo { margin: 2em; }"
 * @returns {Document}
 */
function createIframeDocument ( iframe, documentStyles ) {
    if ( varExists( $ ) && iframe instanceof $ ) iframe = iframe[0];

    if ( ! iframe.ownerDocument.body.contains( iframe ) ) throw new Error( "The iframe has not been appended to the DOM, or is not a descendant of the body element. Can't create an iframe content document." );
    if ( ! iframe.contentDocument ) throw new Error( "Cannot access the iframe content document. Check for cross-domain policy restrictions." );

    documentStyles = documentStyles ? '\n' : "";
    iframe.contentDocument.write( '<!DOCTYPE html>\n\n\n<meta charset="UTF-8">\n<title></title>\n' + documentStyles + '\n\n\n' );

    return iframe.contentDocument;
}

/**
 * Waits for the size of a window to become stable, in case it is undergoing a change. Returns a deferred which resolves
 * when the window size is stable.
 *
 * Optionally accepts an external jQuery deferred to act on, which is then returned instead.
 *
 * This check can be used to determine when the process of resizing a window has ended. It should also be used when a
 * new window is created.
 *
 * Technique
 * ---------
 *
 * In most cases, it would be enough to set a timeout of 0 and then resolve the deferred. The timeout frees the UI and
 * allows the window to assume its eventual size before the deferred is resolved.
 *
 * Unfortunately, the success rate of this approach is close to, but not quite, 100%. So instead, we check the reported
 * window size in regular intervals, so we know for sure when it is stable.
 *
 * @param {Window|jQuery}   queriedWindow   the window to observe, also accepted inside a jQuery `$( window )` wrapper
 * @param {jQuery.Deferred} [readyDfd]
 * @param {number}          [interval=100]  the interval for checking the window size, in ms
 *
 * @returns {jQuery.Deferred}
 */
function windowSizeReady ( queriedWindow, readyDfd, interval ) {

    if ( !varExists( $ ) ) throw new Error( "This method uses jQuery deferreds, but the $ variable is not available" );

    if ( queriedWindow instanceof $ ) queriedWindow = queriedWindow[0];

    readyDfd || ( readyDfd = $.Deferred() );

    $( queriedWindow.document ).ready( function () {

        var documentElement = queriedWindow.document.documentElement,
            lastSize = { width: documentElement.clientWidth, height: documentElement.clientHeight },

            repeater = setInterval( function () {

                var width = documentElement.clientWidth,
                    height = documentElement.clientHeight,
                    isStable = width > 0 && height > 0 && width === lastSize.width && height === lastSize.height;

                if ( isStable ) {
                    clearInterval( repeater );
                    readyDfd.resolve();
                } else {
                    lastSize = { width: width, height: height };
                }

            }, interval || 100 );

    } );

    return readyDfd;

}

/**
 * Makes sure a window is as at least as large as the specified minimum. If the window is too small, an error
 * is thrown.
 *
 * Optionally, it can be validated that the window matches the expected size exactly.
 *
 * @param {Object}  expected
 * @param {number}  expected.width
 * @param {number}  expected.height
 * @param {Object}  [opts]
 * @param {Object}  [opts.window=window]  a window handle, defaults to the global `window`
 * @param {boolean} [opts.exactly=false]
 */
function validateWindowSize ( expected, opts ) {
    var msg = "",
        documentElement = ( opts && opts.window || window ).document.documentElement,
        width = documentElement.clientWidth,
        height = documentElement.clientHeight;

    if ( opts && opts.exactly ) {
        if ( width !== expected.width ) msg = " Window width is " + width + "px (expected: " + expected.width + "px).";
        if ( height !== expected.height ) msg += " Window height is " + height + "px (expected: " + expected.height + "px).";
    } else {
        if ( width < expected.width ) msg = " Window width is " + width + "px (expected minimum: " + expected.width + "px).";
        if ( height < expected.height ) msg += " Window height is " + height + "px (expected minimum: " + expected.height + "px).";
    }

    if ( msg !== "" ) throw new Error( "The browser window does not match the expected size." + msg );
}

/**
 * Feature-tests that an iframe expands to show its content, even if given an explicit width and height. This is the
 * case on iOS.
 *
 * For some background on expanding iframes in iOS, see
 * http://dev.magnolia-cms.com/blog/2012/05/strategies-for-the-iframe-on-the-ipad-problem/
 *
 * @returns {boolean}
 */
function testIframeExpands () {
    var _document, _documentElement, expands,
        iframe = createIframe();

    iframe.style.cssText = "width: 50px; height: 50px; padding: 0px; border: none; margin: 0px;";

    _document = iframe.contentDocument;
    _documentElement = _document.documentElement;

    _document.body.style.cssText = "width: 100px; height: 100px;";

    expands = parseFloat( _documentElement.clientWidth ) > 50 || parseFloat( _documentElement.clientHeight ) > 50;

    document.body.removeChild( iframe );

    return expands;
}

/**
 * Forces a reflow for a given element, in case it doesn't happen automatically.
 *
 * For the technique, see http://stackoverflow.com/a/14382251/508355
 *
 * For some background, see e.g. http://apmblog.dynatrace.com/2009/12/12/understanding-internet-explorer-rendering-behaviour/
 *
 * @param {HTMLElement|jQuery} element
 */
function forceReflow ( element ) {
    if ( !varExists( $ ) ) throw new Error( "This method uses jQuery, but the $ variable is not available" );

    var $element =  element instanceof $ ? element : $( element );

    $element.css( { display: "none" } ).height();
    $element.css( { display: "block" } );
}

/**
 * Detects if the browser is on iOS. Works for Safari as well as other browsers, say, Chrome on iOS.
 *
 * Required for some iOS behaviour which can't be feature-detected in any way.
 *
 * @returns {boolean}
 */
function isIOS () {
    return /iPad|iPhone|iPod/g.test( navigator.userAgent );
}

/**
 * Detects IE.
 *
 * Can use a version requirement. A range can also be specified, e.g. with an option like { gte: 8, lt: 11 }.
 *
 * @param {Object} [opts]
 * @param {number} [opts.eq]   the IE version must be as specified
 * @param {number} [opts.lt]   the IE version must be less than the one specified
 * @param {number} [opts.lte]  the IE version must be less than or equal to the one specified
 * @param {number} [opts.gt]   the IE version must be greater than the one specified
 * @param {number} [opts.gte]  the IE version must be greater than or equal to the one specified
 */
function isIE ( opts ) {
    var ver = getIEVersion(),
        isMatch = ver !== 0;

    opts || ( opts = {} );

    if ( isMatch && opts.eq ) isMatch = ver === opts.eq;
    if ( isMatch && opts.lt ) isMatch = ver < opts.lt;
    if ( isMatch && opts.lte ) isMatch = ver <= opts.lte;
    if ( isMatch && opts.gt ) isMatch = ver > opts.gt;
    if ( isMatch && opts.gte ) isMatch = ver >= opts.gte;

    return isMatch;
}

/**
 * Detects the IE version. Returns the major version number, or 0 if the browser is not IE.
 *
 * Simple solution, solely based on UA sniffing. In a better implementation, conditional comments would be used to
 * detect IE6 to IE9 - see https://gist.github.com/cowboy/542301 for an example. UA sniffing would only serve as a
 * fallback to detect IE > 9. There are also other solutions to infer the version of IE > 9. For inspiration, see
 * http://stackoverflow.com/q/17907445/508355.
 */
function getIEVersion () {
    var ieMatch = /MSIE (\d+)/.exec( navigator.userAgent ) || /Trident\/.+? rv:(\d+)/.exec( navigator.userAgent );
    return ( ieMatch && ieMatch.length ) ? parseFloat( ieMatch[1] ) : 0;
}

function isPhantomJs () {
    return /PhantomJS/.test( navigator.userAgent );
}

// Legacy name, still around in old test code.
function inPhantomJs () {
    return isPhantomJs();
}

<|endoftext|>


<commit_before>const firebase = require('firebase');
const config = require('./config');

firebase.initializeApp(config);

var ref = firebase.database().ref().child("servers");

ref.child(config.device).set(new Date().getTime());

ref.once('value', function (snapshot) {
    process.exit();
});
<commit_msg>update project<commit_after>const fb = require('./fb')

var ref = fb.child("servers")

ref.child(process.env.DEVICE).set(new Date().getTime())

ref.once('value', function (snapshot) {
    process.exit()
})<|endoftext|>


<commit_before>'use strict';
<commit_msg>Update number-words.spec.js<commit_after>'use strict';

var numberWords = require('../src/number-words');
<|endoftext|>


<commit_before>describe("m.postgrest.paginationVM", function(){
  var vm = null;
  var apiPrefix = "http://api.foo.com/v1";
  var model = null;
  var header = {'Prefer': 'count=exact'};

  beforeEach(function(){
    m.postgrest.init(apiPrefix);
    model = m.postgrest.model('foo');
  });

  describe("when fetch fails", function(){
    beforeEach(function(){
      vm = m.postgrest.paginationVM(model, null, false);
      jasmine.Ajax.stubRequest(/foo.*/).andReturn({
        'status' : 401,
        'responseText' : 'Invalid user'
      });
    });

    it("should be initialized with a getter returning an empty array", function(){
      expect(vm.collection()).toBeEmptyArray();
    });

    it("should receive error message and let array empty", function(){
      var error;
      vm.nextPage().then(null, function(e){
        error = e;
      });
      expect(error).toEqual({hint: null, details: null, code: 0, message: 'Invalid user'});
      expect(vm.collection()).toEqual([]);
    });
  });

  describe("when fetch is successful", function(){
    beforeEach(function(){
      spyOn(model, "getPage").and.callThrough();
      vm = m.postgrest.paginationVM(model, null, false);
      jasmine.Ajax.stubRequest(/foo.*/).andReturn({
        'responseText' : '["items"]',
      });
    });

    it("should add count=exact header", function(){
      var lastRequest = jasmine.Ajax.requests.mostRecent();
      expect(lastRequest.requestHeaders.Prefer).toEqual(header.Prefer);
    });

    describe("#collection", function() {
      it("should be initialized with a getter returning an empty array", function(){
        expect(vm.collection()).toBeEmptyArray();
      });

      it("should receive more itens from the fetched pages", function(){
        vm.nextPage();
        expect(vm.collection()).toEqual(['items']);
      });
    });

    describe("#isLoading", function() {
      it("should be a function", function(){
        expect(vm.isLoading).toBeFunction();
      });
    });

    describe("#firstPage", function() {
      it("should be a function", function(){
        var lastRequest = jasmine.Ajax.requests.mostRecent();
        expect(vm.firstPage).toBeFunction();
      });

      it("should call the getPage without incrementing the page number and only with default order if no parameters are passed", function(){
        vm.firstPage({id: 'eq.0'});
        vm.firstPage();
        expect(model.getPage).toHaveBeenCalledWith({order: 'id.desc'}, 1, {background: true, extract: jasmine.any(Function)}, header);
      });

      it("should call the getPage without incrementing the page number and with filters passed as parameters", function(){
        vm.firstPage({id: 'eq.0'});
        expect(model.getPage).toHaveBeenCalledWith({id: 'eq.0', order: 'id.desc'}, 1, {background: true, extract: jasmine.any(Function)}, header);
      });
    });

    describe("#isLastPage", function() {
      const mockRequest = (range) => {
        jasmine.Ajax.stubRequest(/foo.*/).andReturn({
          responseHeaders: [{name: 'Content-Range', value: range}],
          responseText : '["items"]'
        });
      };

      beforeEach(function(){
        vm = m.postgrest.paginationVM(model, null, false);
      });

      it("should return true when is the last page", function() {
        mockRequest('0-1/2');
        vm.firstPage();
        expect(vm.isLastPage()).toEqual(true);
      });

      it("should return false when is not the last page", function() {
        mockRequest('0-9/11');
        vm.firstPage();
        expect(vm.isLastPage()).toEqual(false);
      });
    });

    describe("#nextPage", function() {
      it("should be a function", function(){
        expect(vm.nextPage).toBeFunction();
      });

      it("should call the getPage incrementing the page number and with default filters", function(){
        vm.nextPage();
        expect(model.getPage).toHaveBeenCalledWith({order: 'id.desc'}, 2, {background: true, extract: jasmine.any(Function)}, header);
      });
    });
  });
});

<commit_msg>Indentation fix<commit_after>describe("m.postgrest.paginationVM", function(){
    var vm = null;
    var apiPrefix = "http://api.foo.com/v1";
    var model = null;
    var header = {'Prefer': 'count=exact'};

    beforeEach(function(){
        m.postgrest.init(apiPrefix);
        model = m.postgrest.model('foo');
    });

    describe("when fetch fails", function(){
        beforeEach(function(){
            vm = m.postgrest.paginationVM(model, null, false);
            jasmine.Ajax.stubRequest(/foo.*/).andReturn({
                'status' : 401,
                'responseText' : 'Invalid user'
            });
        });

        it("should be initialized with a getter returning an empty array", function(){
            expect(vm.collection()).toBeEmptyArray();
        });

        it("should receive error message and let array empty", function(){
            var error;
            vm.nextPage().then(null, function(e){
                error = e;
            });
            expect(error).toEqual({hint: null, details: null, code: 0, message: 'Invalid user'});
            expect(vm.collection()).toEqual([]);
        });
    });

    describe("when fetch is successful", function(){
        beforeEach(function(){
            spyOn(model, "getPage").and.callThrough();
            vm = m.postgrest.paginationVM(model, null, false);
            jasmine.Ajax.stubRequest(/foo.*/).andReturn({
                'responseText' : '["items"]',
            });
        });

        it("should add count=exact header", function(){
            var lastRequest = jasmine.Ajax.requests.mostRecent();
            expect(lastRequest.requestHeaders.Prefer).toEqual(header.Prefer);
        });

        describe("#collection", function() {
            it("should be initialized with a getter returning an empty array", function(){
                expect(vm.collection()).toBeEmptyArray();
            });

            it("should receive more itens from the fetched pages", function(){
                vm.nextPage();
                expect(vm.collection()).toEqual(['items']);
            });
        });

        describe("#isLoading", function() {
            it("should be a function", function(){
                expect(vm.isLoading).toBeFunction();
            });
        });

        describe("#firstPage", function() {
            it("should be a function", function(){
                var lastRequest = jasmine.Ajax.requests.mostRecent();
                expect(vm.firstPage).toBeFunction();
            });

            it("should call the getPage without incrementing the page number and only with default order if no parameters are passed", function(){
                vm.firstPage({id: 'eq.0'});
                vm.firstPage();
                expect(model.getPage).toHaveBeenCalledWith({order: 'id.desc'}, 1, {background: true, extract: jasmine.any(Function)}, header);
            });

            it("should call the getPage without incrementing the page number and with filters passed as parameters", function(){
                vm.firstPage({id: 'eq.0'});
                expect(model.getPage).toHaveBeenCalledWith({id: 'eq.0', order: 'id.desc'}, 1, {background: true, extract: jasmine.any(Function)}, header);
            });
        });

        describe("#isLastPage", function() {
            const mockRequest = (range) => {
                jasmine.Ajax.stubRequest(/foo.*/).andReturn({
                    responseHeaders: [{name: 'Content-Range', value: range}],
                    responseText : '["items"]'
                });
            };

            beforeEach(function(){
                vm = m.postgrest.paginationVM(model, null, false);
            });

            it("should return true when is the last page", function() {
                mockRequest('0-1/2');
                vm.firstPage();
                expect(vm.isLastPage()).toEqual(true);
            });

            it("should return false when is not the last page", function() {
                mockRequest('0-9/11');
                vm.firstPage();
                expect(vm.isLastPage()).toEqual(false);
            });
        });

        describe("#nextPage", function() {
            it("should be a function", function(){
                expect(vm.nextPage).toBeFunction();
            });

            it("should call the getPage incrementing the page number and with default filters", function(){
                vm.nextPage();
                expect(model.getPage).toHaveBeenCalledWith({order: 'id.desc'}, 2, {background: true, extract: jasmine.any(Function)}, header);
            });
        });
    });
});

<|endoftext|>


<commit_before>/* global describe, beforeEach, afterEach, expect, Cocktail */
describe('Cocktail', function() {
    var A, B, C, D, ViewClass, ViewClass2, calls;

    beforeEach(function() {
        calls = [];

        A = {
            events: {
                'click .A': 'clickA'
            },

            zoo: {
                aardvark: 'george',
                elephant: 'edgar',
                organutan: 'fred'
            },

            initialize: function() {
                this.$el.append('

');
            },

            theFunc: function() {
                calls.push('theFunc');
                return 'func!';
            },

            clickA: function() {
                calls.push('clickA');
            },

            render: function() {
                calls.push('renderA');
            },

            awesomeSauce: function() {
                calls.push('awesomeA');
                return 'the sauce';
            },

            fooBar: function() {
                calls.push('fooBarA');
                return true;
            },

            cosmology: {
                omegaM: 0.3
            }
        };

        B = {
            events: {
                'click .B': 'clickB'
            },

            zoo : {
                antelope: 'bouregard',
                elephant: 'edward',
                zebra: 'brittania'
            },

            initialize: function() {
                this.$el.append('

');
            },

            sublime: function() {
                calls.push('sublime');
                return 'sublemon';
            },

            clickB: function() {
                calls.push('clickB');
            },

            beforeTearDown: function() {
                calls.push('beforeTearDownB');
            },

            fooBar: function() {
                calls.push('fooBarB');
                return false;
            },

            cosmology: function() {
                calls.push('cosmology');
            }
        };

        C = {
            url: function() {
                return '/sprockets';
            }
        };

        D = {
            urlRoot: '/thingamajigs',
            defaults: function() {
                return null;
            }
        };

        Cocktail.mixins.A = A;
        Cocktail.mixins.B = B;
        Cocktail.mixins.C = C;
        Cocktail.mixins.D = D;
    });

    describe('Cocktail.mixin', function() {
        beforeEach(function() {
            ViewClass = Backbone.View.extend({
                mixins: [A, 'B'],
                //should be ignored as the patch is not installed
                fooBar: function() {
                    calls.push('fooBarOriginal');
                }
            });

            var aMixin = {
                mixinmethod: function() {
                    return 'mixin';
                },

                otherMixinMethod: function() {
                    return 'other';
                },

                sampleArray: [1, 2, 3]
            };

            // Works with named mixins too
            Cocktail.mixins.fooBar = {
                fooBar: function() {
                    calls.push('fooBarInclude');
                }
            };

            Cocktail.mixin(ViewClass, aMixin, 'fooBar');

            ViewClass2 = Backbone.View.extend({
                initialize: function () {
                  Cocktail.mixin(this, aMixin, 'fooBar');
                },

                //should be ignored as the patch is not installed
                fooBar: function() {
                    calls.push('fooBarOriginal');
                }
            });

        });

        it('should allow mixing in mixins after the sub class has been built (useful for coffeescript)', function() {
            var view = new ViewClass();
            expect(view.mixinmethod()).toEqual('mixin');
            expect(view.otherMixinMethod()).toEqual('other');
            view.fooBar();
            expect(calls).toEqual(['fooBarOriginal', 'fooBarInclude']);
        });

        it('should allow mixing into the instance after the subclass has been instantiated', function() {
            var view = new ViewClass2();
            expect(view.mixinmethod()).toEqual('mixin');
            expect(view.otherMixinMethod()).toEqual('other');
            view.fooBar();
            expect(calls).toEqual(['fooBarOriginal', 'fooBarInclude']);
        });

        it('should not mixin the same function reference more than once', function () {
            var A = {
                foo: function () {
                    console.log('foo');
                }
            };

            var B = {};

            var C = {
                foo: function () {
                    console.log('foo');
                }
            };

            Cocktail.mixin(B, A);
            expect(B.foo === A.foo).toBeTruthy();

            // An accidental mixin of the same base
            Cocktail.mixin(B, A);
            expect(B.foo === A.foo).toBeTruthy();

            // Expect the collision wrapper
            Cocktail.mixin(B, C);
            expect(B.foo === A.foo).toBeFalsy();
            expect(B.foo === C.foo).toBeFalsy();
        });

        it('should not convert an array attribute of a mixin into an object', function () {
            var view = new ViewClass();
            console.log(view)
            console.log('array', view.sampleArray)
            expect(_.isArray(view.sampleArray)).toBeTruthy();
        });
    });

    describe('when patching backbone', function() {
        var ViewClass, CollectionClass, ModelClass;

        beforeEach(function() {
            Cocktail.patch(Backbone);

            ViewClass = Backbone.View.extend({
                mixins: ['A', B],

                events: {
                    'click .view': 'clickView'
                },

                zoo : {
                    aardvark: 'bob',
                    penguin: 'melvin',
                    zebra: 'zoe'
                },

                initialize: function() {
                    this.$el.append('

');
                },

                clickView: function() {
                    calls.push('clickView');
                },

                render: function() {
                    calls.push('renderView');
                    return this;
                },

                beforeTearDown: function() {
                    calls.push('beforeTearDownView');
                },

                awesomeSauce: function() {
                    calls.push('awesomeView');
                },

                cosmology: {
                    omegaL: 0.7
                }
            }),

            CollectionClass = Backbone.Collection.extend({
                mixins: ['C'],
                url: '/widgets'
            }),

            ModelClass = Backbone.Model.extend({
                mixins: [D],
                urlRoot: function() {
                    return '/gizmos';
                },
                defaults: {
                    foo: 'bar'
                }
            });
        });

        afterEach(function() {
            Cocktail.unpatch(Backbone);
        });

        it('should allow coffeescript syntax', function() {
            ViewClass.mixin(C);
            var view = new ViewClass();
            expect(view.url()).toEqual('/sprockets');
        });

        describe('mixing in mixins', function() {
            it('should mixin all the mixin methods for all the mixins', function() {
                var view = new ViewClass();
                view.theFunc();
                expect(calls).toContain('theFunc');
                view.sublime();
                expect(calls).toContain('sublime');
            });

            describe('hashes', function() {
                it('should merge in the events hash', function() {
                    var view = new ViewClass();

                    $('#content').append(view.$el);

                    $('.A').click();
                    $('.B').click();
                    $('.view').click();

                    expect(calls).toEqual(['clickA', 'clickB', 'clickView']);
                });

                it('should merge together hashes, favoring values defined by the instance over mixins, and mixins on a first in will win basis', function() {
                    var view = new ViewClass();
                    expect(view.zoo).toEqual({
                        aardvark: 'bob',
                        antelope: 'bouregard',
                        elephant: 'edgar',
                        organutan: 'fred',
                        penguin: 'melvin',
                        zebra: 'zoe'
                    });
                });
            });
        });

        describe('handling method collisions', function() {
            it('should call all the methods involved in the collision in the correct order', function() {
                var view = new ViewClass();
                view.render();
                view.awesomeSauce();
                view.fooBar();
                $('#content').append(view.$el);
                expect($('.A')[0]).toBeTruthy();
                expect($('.B')[0]).toBeTruthy();
                expect($('.view')[0]).toBeTruthy();
                view.beforeTearDown();

                expect(calls).toEqual(['renderView', 'renderA', 'awesomeView', 'awesomeA', 'fooBarA', 'fooBarB', 'beforeTearDownView', 'beforeTearDownB']);
            });

            it('should return the last return value in the collision chain', function() {
                var view = new ViewClass();
                expect(view.render()).toEqual(view);
                expect(view.theFunc()).toEqual('func!');
                expect(view.sublime()).toEqual('sublemon');
                expect(view.fooBar()).toEqual(false);
                expect(view.awesomeSauce()).toEqual('the sauce');
            });
        });

        describe('handling functional override of non-function property', function() {
            it('should return the last truthy defined return value in the collision chain', function() {
                var collection = new CollectionClass();
                expect(collection.url()).toEqual('/sprockets');
            });
            it('should return the last falsy defined return value in the collision chain', function() {
                var model = new ModelClass();
                expect(model.defaults()).toBeNull();
            });
            it('should set a function returning the base value if no mixin function has defined return', function() {
                var view = new ViewClass();
                expect(view.cosmology()).toEqual({
                    omegaM:0.3,
                    omegaL:0.7
                });
                expect(calls).toEqual(['cosmology']);
            });
        });

        describe('handling primitive override of non-events property', function() {
            describe('when class specifies prop', function() {
              it('should use the class property', function() {
                var model = new ModelClass();
                expect(model.url()).toEqual('/gizmos');
              });
            });
            describe('when class does not specify prop', function() {
              it('should use the class property', function() {
                var ModelWithoutUrlRoot = Backbone.Model.extend({
                  mixins: [D]
                }), model = new ModelWithoutUrlRoot();
                expect(model.url()).toEqual('/thingamajigs');
              });
            });
        });

        describe('when mixins are applied in the context of super/subclasses', function() {
            var BaseClass, SubClass, SubClassWithMixin;
            beforeEach(function() {
                BaseClass = Backbone.View.extend({
                    mixins: [A],
                    fooBar: function() {
                        calls.push('BaseClassFoo');
                    }
                });

                SubClass = BaseClass.extend({
                    fooBar: function() {
                        SubClass.__super__.fooBar.apply(this);
                        calls.push('SubClassFoo');
                    }
                });

                SubClassWithMixin = BaseClass.extend({
                    mixins: [B],

                    fooBar: function() {
                        SubClassWithMixin.__super__.fooBar.apply(this);
                        calls.push('SubClassWithMixinFoo');
                    }
                });
            });

            it('should behave correctly for the base class', function() {
                var baseInstance = new BaseClass();

                $('#content').append(baseInstance.$el);
                $('.A').click();
                baseInstance.fooBar();

                expect(calls).toEqual(['clickA', 'BaseClassFoo', 'fooBarA']);
            });

            it('should behave correctly for the sub class that has no mixins', function() {
                var subInstance = new SubClass();

                $('#content').html(subInstance.$el);
                $('.A').click();
                subInstance.fooBar();

                expect(calls).toEqual(['clickA', 'BaseClassFoo', 'fooBarA', 'SubClassFoo']);
            });

            it('should behave correctly for the sub class that has mixins', function() {
                var subInstance = new SubClassWithMixin();

                $('#content').html(subInstance.$el);
                $('.A').click();
                $('.B').click();
                subInstance.fooBar();

                expect(calls).toEqual(['clickA', 'clickB', 'BaseClassFoo', 'fooBarA', 'SubClassWithMixinFoo', 'fooBarB']);
            });
        });
    });
});<commit_msg>Remove debug logging from tests<commit_after>/* global describe, beforeEach, afterEach, expect, Cocktail */
describe('Cocktail', function() {
    var A, B, C, D, ViewClass, ViewClass2, calls;

    beforeEach(function() {
        calls = [];

        A = {
            events: {
                'click .A': 'clickA'
            },

            zoo: {
                aardvark: 'george',
                elephant: 'edgar',
                organutan: 'fred'
            },

            initialize: function() {
                this.$el.append('

');
            },

            theFunc: function() {
                calls.push('theFunc');
                return 'func!';
            },

            clickA: function() {
                calls.push('clickA');
            },

            render: function() {
                calls.push('renderA');
            },

            awesomeSauce: function() {
                calls.push('awesomeA');
                return 'the sauce';
            },

            fooBar: function() {
                calls.push('fooBarA');
                return true;
            },

            cosmology: {
                omegaM: 0.3
            }
        };

        B = {
            events: {
                'click .B': 'clickB'
            },

            zoo : {
                antelope: 'bouregard',
                elephant: 'edward',
                zebra: 'brittania'
            },

            initialize: function() {
                this.$el.append('

');
            },

            sublime: function() {
                calls.push('sublime');
                return 'sublemon';
            },

            clickB: function() {
                calls.push('clickB');
            },

            beforeTearDown: function() {
                calls.push('beforeTearDownB');
            },

            fooBar: function() {
                calls.push('fooBarB');
                return false;
            },

            cosmology: function() {
                calls.push('cosmology');
            }
        };

        C = {
            url: function() {
                return '/sprockets';
            }
        };

        D = {
            urlRoot: '/thingamajigs',
            defaults: function() {
                return null;
            }
        };

        Cocktail.mixins.A = A;
        Cocktail.mixins.B = B;
        Cocktail.mixins.C = C;
        Cocktail.mixins.D = D;
    });

    describe('Cocktail.mixin', function() {
        beforeEach(function() {
            ViewClass = Backbone.View.extend({
                mixins: [A, 'B'],
                //should be ignored as the patch is not installed
                fooBar: function() {
                    calls.push('fooBarOriginal');
                }
            });

            var aMixin = {
                mixinmethod: function() {
                    return 'mixin';
                },

                otherMixinMethod: function() {
                    return 'other';
                },

                sampleArray: [1, 2, 3]
            };

            // Works with named mixins too
            Cocktail.mixins.fooBar = {
                fooBar: function() {
                    calls.push('fooBarInclude');
                }
            };

            Cocktail.mixin(ViewClass, aMixin, 'fooBar');

            ViewClass2 = Backbone.View.extend({
                initialize: function () {
                  Cocktail.mixin(this, aMixin, 'fooBar');
                },

                //should be ignored as the patch is not installed
                fooBar: function() {
                    calls.push('fooBarOriginal');
                }
            });

        });

        it('should allow mixing in mixins after the sub class has been built (useful for coffeescript)', function() {
            var view = new ViewClass();
            expect(view.mixinmethod()).toEqual('mixin');
            expect(view.otherMixinMethod()).toEqual('other');
            view.fooBar();
            expect(calls).toEqual(['fooBarOriginal', 'fooBarInclude']);
        });

        it('should allow mixing into the instance after the subclass has been instantiated', function() {
            var view = new ViewClass2();
            expect(view.mixinmethod()).toEqual('mixin');
            expect(view.otherMixinMethod()).toEqual('other');
            view.fooBar();
            expect(calls).toEqual(['fooBarOriginal', 'fooBarInclude']);
        });

        it('should not mixin the same function reference more than once', function () {
            var A = {
                foo: function () {
                    console.log('foo');
                }
            };

            var B = {};

            var C = {
                foo: function () {
                    console.log('foo');
                }
            };

            Cocktail.mixin(B, A);
            expect(B.foo === A.foo).toBeTruthy();

            // An accidental mixin of the same base
            Cocktail.mixin(B, A);
            expect(B.foo === A.foo).toBeTruthy();

            // Expect the collision wrapper
            Cocktail.mixin(B, C);
            expect(B.foo === A.foo).toBeFalsy();
            expect(B.foo === C.foo).toBeFalsy();
        });

        it('should not convert an array attribute of a mixin into an object', function () {
            var view = new ViewClass();
            expect(_.isArray(view.sampleArray)).toBeTruthy();
        });
    });

    describe('when patching backbone', function() {
        var ViewClass, CollectionClass, ModelClass;

        beforeEach(function() {
            Cocktail.patch(Backbone);

            ViewClass = Backbone.View.extend({
                mixins: ['A', B],

                events: {
                    'click .view': 'clickView'
                },

                zoo : {
                    aardvark: 'bob',
                    penguin: 'melvin',
                    zebra: 'zoe'
                },

                initialize: function() {
                    this.$el.append('

');
                },

                clickView: function() {
                    calls.push('clickView');
                },

                render: function() {
                    calls.push('renderView');
                    return this;
                },

                beforeTearDown: function() {
                    calls.push('beforeTearDownView');
                },

                awesomeSauce: function() {
                    calls.push('awesomeView');
                },

                cosmology: {
                    omegaL: 0.7
                }
            }),

            CollectionClass = Backbone.Collection.extend({
                mixins: ['C'],
                url: '/widgets'
            }),

            ModelClass = Backbone.Model.extend({
                mixins: [D],
                urlRoot: function() {
                    return '/gizmos';
                },
                defaults: {
                    foo: 'bar'
                }
            });
        });

        afterEach(function() {
            Cocktail.unpatch(Backbone);
        });

        it('should allow coffeescript syntax', function() {
            ViewClass.mixin(C);
            var view = new ViewClass();
            expect(view.url()).toEqual('/sprockets');
        });

        describe('mixing in mixins', function() {
            it('should mixin all the mixin methods for all the mixins', function() {
                var view = new ViewClass();
                view.theFunc();
                expect(calls).toContain('theFunc');
                view.sublime();
                expect(calls).toContain('sublime');
            });

            describe('hashes', function() {
                it('should merge in the events hash', function() {
                    var view = new ViewClass();

                    $('#content').append(view.$el);

                    $('.A').click();
                    $('.B').click();
                    $('.view').click();

                    expect(calls).toEqual(['clickA', 'clickB', 'clickView']);
                });

                it('should merge together hashes, favoring values defined by the instance over mixins, and mixins on a first in will win basis', function() {
                    var view = new ViewClass();
                    expect(view.zoo).toEqual({
                        aardvark: 'bob',
                        antelope: 'bouregard',
                        elephant: 'edgar',
                        organutan: 'fred',
                        penguin: 'melvin',
                        zebra: 'zoe'
                    });
                });
            });
        });

        describe('handling method collisions', function() {
            it('should call all the methods involved in the collision in the correct order', function() {
                var view = new ViewClass();
                view.render();
                view.awesomeSauce();
                view.fooBar();
                $('#content').append(view.$el);
                expect($('.A')[0]).toBeTruthy();
                expect($('.B')[0]).toBeTruthy();
                expect($('.view')[0]).toBeTruthy();
                view.beforeTearDown();

                expect(calls).toEqual(['renderView', 'renderA', 'awesomeView', 'awesomeA', 'fooBarA', 'fooBarB', 'beforeTearDownView', 'beforeTearDownB']);
            });

            it('should return the last return value in the collision chain', function() {
                var view = new ViewClass();
                expect(view.render()).toEqual(view);
                expect(view.theFunc()).toEqual('func!');
                expect(view.sublime()).toEqual('sublemon');
                expect(view.fooBar()).toEqual(false);
                expect(view.awesomeSauce()).toEqual('the sauce');
            });
        });

        describe('handling functional override of non-function property', function() {
            it('should return the last truthy defined return value in the collision chain', function() {
                var collection = new CollectionClass();
                expect(collection.url()).toEqual('/sprockets');
            });
            it('should return the last falsy defined return value in the collision chain', function() {
                var model = new ModelClass();
                expect(model.defaults()).toBeNull();
            });
            it('should set a function returning the base value if no mixin function has defined return', function() {
                var view = new ViewClass();
                expect(view.cosmology()).toEqual({
                    omegaM:0.3,
                    omegaL:0.7
                });
                expect(calls).toEqual(['cosmology']);
            });
        });

        describe('handling primitive override of non-events property', function() {
            describe('when class specifies prop', function() {
              it('should use the class property', function() {
                var model = new ModelClass();
                expect(model.url()).toEqual('/gizmos');
              });
            });
            describe('when class does not specify prop', function() {
              it('should use the class property', function() {
                var ModelWithoutUrlRoot = Backbone.Model.extend({
                  mixins: [D]
                }), model = new ModelWithoutUrlRoot();
                expect(model.url()).toEqual('/thingamajigs');
              });
            });
        });

        describe('when mixins are applied in the context of super/subclasses', function() {
            var BaseClass, SubClass, SubClassWithMixin;
            beforeEach(function() {
                BaseClass = Backbone.View.extend({
                    mixins: [A],
                    fooBar: function() {
                        calls.push('BaseClassFoo');
                    }
                });

                SubClass = BaseClass.extend({
                    fooBar: function() {
                        SubClass.__super__.fooBar.apply(this);
                        calls.push('SubClassFoo');
                    }
                });

                SubClassWithMixin = BaseClass.extend({
                    mixins: [B],

                    fooBar: function() {
                        SubClassWithMixin.__super__.fooBar.apply(this);
                        calls.push('SubClassWithMixinFoo');
                    }
                });
            });

            it('should behave correctly for the base class', function() {
                var baseInstance = new BaseClass();

                $('#content').append(baseInstance.$el);
                $('.A').click();
                baseInstance.fooBar();

                expect(calls).toEqual(['clickA', 'BaseClassFoo', 'fooBarA']);
            });

            it('should behave correctly for the sub class that has no mixins', function() {
                var subInstance = new SubClass();

                $('#content').html(subInstance.$el);
                $('.A').click();
                subInstance.fooBar();

                expect(calls).toEqual(['clickA', 'BaseClassFoo', 'fooBarA', 'SubClassFoo']);
            });

            it('should behave correctly for the sub class that has mixins', function() {
                var subInstance = new SubClassWithMixin();

                $('#content').html(subInstance.$el);
                $('.A').click();
                $('.B').click();
                subInstance.fooBar();

                expect(calls).toEqual(['clickA', 'clickB', 'BaseClassFoo', 'fooBarA', 'SubClassWithMixinFoo', 'fooBarB']);
            });
        });
    });
});<|endoftext|>


<commit_before>import h from 'spec/spec_helper';
import { _, path, lazy_require } from 'azk';
import { Q } from 'azk';

var lazy = lazy_require({
  Watcher: ['azk/sync/watcher'],
  Sync   : ['azk/sync'],
  qfs    : 'q-io/fs',
  semver : 'semver'
});

describe("Azk sync, Watcher module", function() {
  var watcher;
  var example_fixtures = h.fixture_path('sync/test_1/');

  function make_copy() {
    return Q.all([
      h.copyToTmp(example_fixtures),
      h.tmp_dir()
    ]);
  }

  before(() => {
    watcher = new lazy.Watcher();
  });

  afterEach(() => {
    watcher.close();
    h.expect(_.keys(watcher.workers)).to.length(0);
  });

  it("should sync two folders", function* () {
    var wait_msg = h.wait_msg("sync.watcher.sync");

    var [origin, dest] = yield make_copy();
    yield watcher.watch(origin, dest);
    h.expect(_.keys(watcher.workers)).to.length(1);

    var msg = yield wait_msg;
    h.expect(msg).to.have.property('op', 'sync');
    h.expect(msg).to.have.property('status', 'done');

    var result = yield h.diff(origin, dest);
    h.expect(result).to.have.property('deviation', 3);
  });

  describe("with called to watch a two folders", function() {
    var origin, dest;

    beforeEach(function* () {
      [origin, dest] = yield make_copy();

      yield watcher.watch(origin, dest);
      h.expect(_.keys(watcher.workers)).to.length(1);

      var result = yield h.diff(origin, dest);
      h.expect(result).to.have.property('deviation', 3);
    });

    it("should sync two folders and watch", function* () {
      var file = "foo/Moe.txt";
      var origin_file = path.join(origin, file);
      var dest_file   = path.join(origin, file);

      var wait_msg = h.wait_msg("sync.watcher.*");
      yield lazy.qfs.write(origin_file, "foobar");
      var msg = yield wait_msg;

      h.expect(msg).to.have.deep.property('op', 'change');
      h.expect(msg).to.have.deep.property('filepath', file);
      h.expect(msg).to.have.deep.property('status', 'done');

      var content = yield lazy.qfs.read(dest_file);
      h.expect(content).to.equal("foobar");
    });

    it("should reuse a watcher", function* () {
      var msg = yield h.wait_msg("sync.watcher.init", null, () => {
        watcher.watch(origin, dest);
      });
      h.expect(msg).to.have.property('status', 'exists');
    });

    it("should remove a watcher", function* () {
      h.expect(_.keys(watcher.workers)).to.length(1);
      var msg = yield h.wait_msg("sync.watcher.finish", null, () => {
        watcher.unwatch(origin, dest);
      });

      h.expect(_.keys(watcher.workers)).to.length(0);
      h.expect(msg).to.have.property('status', 'done');
    });

    it("should respawn sync process if worker killed", function* () {
      var filter = (msg, msgs) => { msg; return msgs.length >= 3; };
      var msgs = yield h.wait_msgs("sync.watcher.*", filter, () => {
        process.kill(watcher.get_worker(origin, dest).child.childData.pid);
      });

      h.expect(msgs).to.include.something.that.deep.eql(
        {"op": "restart", "status": "init"}
      );
      h.expect(msgs).to.include.something.that.deep.eql(
        {"op": "sync", "status": "done"}
      );
      h.expect(msgs).to.include.something.that.deep.eql(
        {"op": "watch", "status": "ready"}
      );

      var file = "foo/Moe.txt";
      var origin_file = path.join(origin, file);
      var dest_file   = path.join(origin, file);
      var wait_msgs   = h.wait_msg('sync.watcher.*');

      yield lazy.qfs.write(origin_file, "foobar");

      var msg_change = yield wait_msgs;
      h.expect(msg_change).to.have.property('op', 'change');
      h.expect(msg_change).to.have.property('filepath', file);
      h.expect(msg_change).to.have.property('status', 'done');

      var content = yield lazy.qfs.read(dest_file);
      h.expect(content).to.equal("foobar");
    });
  });

  it("should return a error if initial sync fails", function* () {
    var origin = yield h.tmp_dir();
    var dest   = path.join(yield h.tmp_dir(), "foo", "bar");

    var filter    = (msg) => msg.op == "finish";
    var wait_msgs = h.wait_msgs('sync.watcher.*', filter);

    var rsync_version = yield lazy.Sync.version();
    var promise = watcher.watch(origin, dest);

    if (lazy.semver.cmp(rsync_version, '>=', '3.1.0')) {
      yield h.expect(promise).to.be.rejected.and.eventually.have.property('code', 3);
    } else {
      yield h.expect(promise).to.be.rejected.and.eventually.have.property('code', 12);
    }

    h.expect(_.keys(watcher.workers)).to.length(0);

    var msgs = yield wait_msgs;
    h.expect(msgs).to.containSubset([{ "op": "sync", "status": "fail" }]);
    h.expect(msgs).to.containSubset([{ "op": "finish", "status": "done" }]);
  });
});
<commit_msg>[fix_file_watcher_excludes] Fixing sync.watcher_spec<commit_after>import h from 'spec/spec_helper';
import { _, path, lazy_require } from 'azk';
import { Q } from 'azk';

var lazy = lazy_require({
  Watcher: ['azk/sync/watcher'],
  Sync   : ['azk/sync'],
  qfs    : 'q-io/fs',
  semver : 'semver'
});

describe("Azk sync, Watcher module", function() {
  var watcher;
  var example_fixtures = h.fixture_path('sync/test_1/');

  function make_copy() {
    return Q.all([
      h.copyToTmp(example_fixtures),
      h.tmp_dir()
    ]);
  }

  before(() => {
    watcher = new lazy.Watcher();
  });

  afterEach(() => {
    watcher.close();
    h.expect(_.keys(watcher.workers)).to.length(0);
  });

  it("should sync two folders", function* () {
    var wait_msg = h.wait_msg("sync.watcher.sync");

    var [origin, dest] = yield make_copy();
    yield watcher.watch(origin, dest);
    h.expect(_.keys(watcher.workers)).to.length(1);

    var msg = yield wait_msg;
    h.expect(msg).to.have.property('op', 'sync');
    h.expect(msg).to.have.property('status', 'done');

    var result = yield h.diff(origin, dest);
    h.expect(result).to.have.property('deviation', 3);
  });

  describe("with called to watch a two folders", function() {
    var origin, dest;

    beforeEach(function* () {
      [origin, dest] = yield make_copy();

      yield watcher.watch(origin, dest);
      h.expect(_.keys(watcher.workers)).to.length(1);

      var result = yield h.diff(origin, dest);
      h.expect(result).to.have.property('deviation', 3);
    });

    it("should sync two folders and watch", function* () {
      var file = "bar/Fred.txt";
      var origin_file = path.join(origin, file);
      var dest_file   = path.join(origin, file);

      var wait_msg = h.wait_msg("sync.watcher.*");
      yield lazy.qfs.write(origin_file, "foobar");
      var msg = yield wait_msg;

      h.expect(msg).to.have.deep.property('op', 'change');
      h.expect(msg).to.have.deep.property('filepath', file);
      h.expect(msg).to.have.deep.property('status', 'done');

      var content = yield lazy.qfs.read(dest_file);
      h.expect(content).to.equal("foobar");
    });

    it("should reuse a watcher", function* () {
      var msg = yield h.wait_msg("sync.watcher.init", null, () => {
        watcher.watch(origin, dest);
      });
      h.expect(msg).to.have.property('status', 'exists');
    });

    it("should remove a watcher", function* () {
      h.expect(_.keys(watcher.workers)).to.length(1);
      var msg = yield h.wait_msg("sync.watcher.finish", null, () => {
        watcher.unwatch(origin, dest);
      });

      h.expect(_.keys(watcher.workers)).to.length(0);
      h.expect(msg).to.have.property('status', 'done');
    });

    it("should respawn sync process if worker killed", function* () {
      var filter = (msg, msgs) => { msg; return msgs.length >= 3; };
      var msgs = yield h.wait_msgs("sync.watcher.*", filter, () => {
        process.kill(watcher.get_worker(origin, dest).child.childData.pid);
      });

      h.expect(msgs).to.include.something.that.deep.eql(
        {"op": "restart", "status": "init"}
      );
      h.expect(msgs).to.include.something.that.deep.eql(
        {"op": "sync", "status": "done"}
      );
      h.expect(msgs).to.include.something.that.deep.eql(
        {"op": "watch", "status": "ready"}
      );

      var file = "bar/Fred.txt";
      var origin_file = path.join(origin, file);
      var dest_file   = path.join(origin, file);
      var wait_msgs   = h.wait_msg('sync.watcher.*');

      yield lazy.qfs.write(origin_file, "foobar");

      var msg_change = yield wait_msgs;
      h.expect(msg_change).to.have.property('op', 'change');
      h.expect(msg_change).to.have.property('filepath', file);
      h.expect(msg_change).to.have.property('status', 'done');

      var content = yield lazy.qfs.read(dest_file);
      h.expect(content).to.equal("foobar");
    });
  });

  it("should return a error if initial sync fails", function* () {
    var origin = yield h.tmp_dir();
    var dest   = path.join(yield h.tmp_dir(), "foo", "bar");

    var filter    = (msg) => msg.op == "finish";
    var wait_msgs = h.wait_msgs('sync.watcher.*', filter);

    var rsync_version = yield lazy.Sync.version();
    var promise = watcher.watch(origin, dest);

    if (lazy.semver.cmp(rsync_version, '>=', '3.1.0')) {
      yield h.expect(promise).to.be.rejected.and.eventually.have.property('code', 3);
    } else {
      yield h.expect(promise).to.be.rejected.and.eventually.have.property('code', 12);
    }

    h.expect(_.keys(watcher.workers)).to.length(0);

    var msgs = yield wait_msgs;
    h.expect(msgs).to.containSubset([{ "op": "sync", "status": "fail" }]);
    h.expect(msgs).to.containSubset([{ "op": "finish", "status": "done" }]);
  });
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import showLoadingIndicator from '../utils/show-loading-indicator';
import cdptRequest from '../utils/cdpt-request';

export default Ember.Controller.extend({
  needs: ['application'],
  isSignedInBinding: 'controllers.application.isUserSignedInProperty',
  workshops: [],
  selectedWorkshops: [],

  showPurchaseNotCompletedInfo: function () {
    return this.get('isSignedIn') && !this.get('purchaseComplete');
  }.property('isSignedIn', 'purchaseComplete'),

  canSelectWorkshops: function () {
    return this.get('isSignedIn') && this.get('purchaseComplete');
  }.property('isSignedIn', 'purchaseComplete'),

  refreshSelectedWorkshops: function () {
    showLoadingIndicator(true);
    var controller = this;
    var userId = this.get('controllers.application.userData').user.get('id');
    var url = '/api/users/' + userId + '/workshops/';
    cdptRequest(url, 'GET')
      .then(function (result) {
        controller.get('workshops').forEach(function(workshop){
          workshop.set('isSelected', false);
        });
        var workshops = result.workshops.map(function (rawWorkshop) {
          return controller.store.getById('workshop', rawWorkshop.id);
        });
        workshops.forEach(function(workshop){
          workshop.set('isSelected', true);
        });
        controller.set('selectedWorkshops', workshops);
      })
      .always(function () {
        showLoadingIndicator(false);
      });
  },

  canSelectWorkshopsChanged: function () {
    if (this.get('canSelectWorkshops')) {
      this.refreshSelectedWorkshops();
    }
  }.observes('canSelectWorkshops'),

  filterWorkshopsByDay: function (day) {
    var workshops = this.get('workshops').filter(function (workshop) {
      var timeSlots = workshop.get('timeSlots').filter(function (timeSlot) {
        return timeSlot.get('day') === day;
      });
      return timeSlots.length > 0;
    });
    return workshops;
  },

  noWorkshopsFoundDay1: function () {
    return !this.filterWorkshopsByDay('FIRST').length;
  }.property('workshops'),

  noWorkshopsFoundDay2: function () {
    return !this.filterWorkshopsByDay('SECOND').length;
  }.property('workshops'),

  searchTextChanged: function () {
    Ember.run.debounce(this, this.filterWorkshops, 500);
  }.observes('searchText'),

  filteredWorkshopsOnly: function () {
    return !!this.get('searchText');
  }.property('workshops'),

  filterWorkshops: function () {
    ga('send', 'event', 'seach', 'performed', this.get('searchText'));
    this.target.send('filterWorkshops', this, this.get('searchText'));
  },

  actions: {
    filterWorkshops: function () {
      this.filterWorkshops();
    },

    selectWorkshop: function (workshop) {
      var collidingWorkshop = this.get('selectedWorkshops').filter(function (selectedWorkshop) {
        return selectedWorkshop.collidesWithWorkshop(workshop);
      })[0];

      if (collidingWorkshop) {
        var confirmed = confirm('Are you sure? This workshop overlaps: "' + collidingWorkshop.get('title') + '". You will be signed out of this workshop.');
        if (confirmed) {
          this.leaveWorkshopAndThenSelectNewOne(collidingWorkshop, workshop);
        }
        return;
      }

      showLoadingIndicator(true);
      var controller = this;
      var userId = this.get('controllers.application.userData').user.get('id');
      var url = '/api/users/' + userId + '/workshops/';
      var data = {
        workshopId: parseInt(workshop.get('id'))
      };
      cdptRequest(url, 'POST', data)
        .then(function () {
          $('.reveal-modal.open').foundation('reveal', 'close');
          controller.refreshSelectedWorkshops();
        })
        .always(function () {
          showLoadingIndicator(false);
        });
    },

    leaveWorkshop: function (workshop) {
      showLoadingIndicator(true);
      var controller = this;
      var userId = this.get('controllers.application.userData').user.get('id');
      var url = '/api/users/' + userId + '/workshops/' + workshop.get('id') + '/';
      cdptRequest(url, 'DELETE')
        .then(function () {
          $('.reveal-modal.open').foundation('reveal', 'close');
          controller.refreshSelectedWorkshops();
        })
        .always(function () {
          showLoadingIndicator(false);
        });
    }
  },

  leaveWorkshopAndThenSelectNewOne: function (oldWorkshop, newWorkshop) {
    showLoadingIndicator(true);
    var controller = this;
    var userId = this.get('controllers.application.userData').user.get('id');
    var url = '/api/users/' + userId + '/workshops/' + oldWorkshop.get('id') + '/';
    cdptRequest(url, 'DELETE')
      .then(function () {
        var url = '/api/users/' + userId + '/workshops/';
        var data = {
          workshopId: parseInt(newWorkshop.get('id'))
        };
        cdptRequest(url, 'POST', data)
          .then(function () {
            $('.reveal-modal.open').foundation('reveal', 'close');
            controller.refreshSelectedWorkshops();
          })
          .always(function () {
            showLoadingIndicator(false);
          });
      });
  },

  firstDaySelectedWorkshops: function () {
    return this.get('selectedWorkshops').filter(function(workshop){
      return workshop.get('day') === 'FIRST';
    });
  }.property('selectedWorkshops'),

  secondDaySelectedWorkshops: function () {
    return this.get('selectedWorkshops').filter(function(workshop){
      return workshop.get('day') === 'SECOND';
    });
  }.property('selectedWorkshops')
});
<commit_msg>GET -> POST<commit_after>import Ember from 'ember';
import showLoadingIndicator from '../utils/show-loading-indicator';
import cdptRequest from '../utils/cdpt-request';

export default Ember.Controller.extend({
  needs: ['application'],
  isSignedInBinding: 'controllers.application.isUserSignedInProperty',
  workshops: [],
  selectedWorkshops: [],

  showPurchaseNotCompletedInfo: function () {
    return this.get('isSignedIn') && !this.get('purchaseComplete');
  }.property('isSignedIn', 'purchaseComplete'),

  canSelectWorkshops: function () {
    return this.get('isSignedIn') && this.get('purchaseComplete');
  }.property('isSignedIn', 'purchaseComplete'),

  refreshSelectedWorkshops: function () {
    showLoadingIndicator(true);
    var controller = this;
    var userId = this.get('controllers.application.userData').user.get('id');
    var url = '/api/users/' + userId + '/workshops/' + '?nocache=' + new Date().getTime();
    cdptRequest(url, 'POST')
      .then(function (result) {
        controller.get('workshops').forEach(function(workshop){
          workshop.set('isSelected', false);
        });
        var workshops = result.workshops.map(function (rawWorkshop) {
          return controller.store.getById('workshop', rawWorkshop.id);
        });
        workshops.forEach(function(workshop){
          workshop.set('isSelected', true);
        });
        controller.set('selectedWorkshops', workshops);
      })
      .always(function () {
        showLoadingIndicator(false);
      });
  },

  canSelectWorkshopsChanged: function () {
    if (this.get('canSelectWorkshops')) {
      this.refreshSelectedWorkshops();
    }
  }.observes('canSelectWorkshops'),

  filterWorkshopsByDay: function (day) {
    var workshops = this.get('workshops').filter(function (workshop) {
      var timeSlots = workshop.get('timeSlots').filter(function (timeSlot) {
        return timeSlot.get('day') === day;
      });
      return timeSlots.length > 0;
    });
    return workshops;
  },

  noWorkshopsFoundDay1: function () {
    return !this.filterWorkshopsByDay('FIRST').length;
  }.property('workshops'),

  noWorkshopsFoundDay2: function () {
    return !this.filterWorkshopsByDay('SECOND').length;
  }.property('workshops'),

  searchTextChanged: function () {
    Ember.run.debounce(this, this.filterWorkshops, 500);
  }.observes('searchText'),

  filteredWorkshopsOnly: function () {
    return !!this.get('searchText');
  }.property('workshops'),

  filterWorkshops: function () {
    ga('send', 'event', 'seach', 'performed', this.get('searchText'));
    this.target.send('filterWorkshops', this, this.get('searchText'));
  },

  actions: {
    filterWorkshops: function () {
      this.filterWorkshops();
    },

    selectWorkshop: function (workshop) {
      var collidingWorkshop = this.get('selectedWorkshops').filter(function (selectedWorkshop) {
        return selectedWorkshop.collidesWithWorkshop(workshop);
      })[0];

      if (collidingWorkshop) {
        var confirmed = confirm('Are you sure? This workshop overlaps: "' + collidingWorkshop.get('title') + '". You will be signed out of this workshop.');
        if (confirmed) {
          this.leaveWorkshopAndThenSelectNewOne(collidingWorkshop, workshop);
        }
        return;
      }

      showLoadingIndicator(true);
      var controller = this;
      var userId = this.get('controllers.application.userData').user.get('id');
      var url = '/api/users/' + userId + '/workshops/sign/';
      var data = {
        workshopId: parseInt(workshop.get('id'))
      };
      cdptRequest(url, 'POST', data)
        .then(function () {
          $('.reveal-modal.open').foundation('reveal', 'close');
        })
        .always(function () {
          showLoadingIndicator(false);
          controller.refreshSelectedWorkshops();
        });
    },

    leaveWorkshop: function (workshop) {
      showLoadingIndicator(true);
      var controller = this;
      var userId = this.get('controllers.application.userData').user.get('id');
      var url = '/api/users/' + userId + '/workshops/' + workshop.get('id') + '/';
      cdptRequest(url, 'DELETE')
        .then(function () {
          $('.reveal-modal.open').foundation('reveal', 'close');
        })
        .always(function () {
          showLoadingIndicator(false);
          controller.refreshSelectedWorkshops();
        });
    }
  },

  leaveWorkshopAndThenSelectNewOne: function (oldWorkshop, newWorkshop) {
    showLoadingIndicator(true);
    var controller = this;
    var userId = this.get('controllers.application.userData').user.get('id');
    var url = '/api/users/' + userId + '/workshops/' + oldWorkshop.get('id') + '/';
    cdptRequest(url, 'DELETE')
      .then(function () {
        var url = '/api/users/' + userId + '/workshops/';
        var data = {
          workshopId: parseInt(newWorkshop.get('id'))
        };
        cdptRequest(url, 'POST', data)
          .then(function () {
            $('.reveal-modal.open').foundation('reveal', 'close');
          })
          .always(function () {
            showLoadingIndicator(false);
            controller.refreshSelectedWorkshops();
          });
      });
  },

  firstDaySelectedWorkshops: function () {
    return this.get('selectedWorkshops').filter(function(workshop){
      return workshop.get('day') === 'FIRST';
    });
  }.property('selectedWorkshops'),

  secondDaySelectedWorkshops: function () {
    return this.get('selectedWorkshops').filter(function(workshop){
      return workshop.get('day') === 'SECOND';
    });
  }.property('selectedWorkshops')
});
<|endoftext|>