Repository: ambari
Updated Branches:
  refs/heads/branch-1.7.0 9f373723f -> 6260332ea


AMBARI-7927. Slider view: Integrate unit test with maven and doc the commands 
to run unit test. (onechiporenko)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/6260332e
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/6260332e
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/6260332e

Branch: refs/heads/branch-1.7.0
Commit: 6260332eaff7a2e70a2c78dcb209e18c5478c897
Parents: 9f37372
Author: Oleg Nechiporenko <onechipore...@apache.org>
Authored: Fri Oct 24 16:58:32 2014 +0300
Committer: Oleg Nechiporenko <onechipore...@apache.org>
Committed: Fri Oct 24 16:58:32 2014 +0300

----------------------------------------------------------------------
 contrib/views/slider/docs/index.md              |   2 +-
 contrib/views/slider/pom.xml                    |   8 +-
 .../ui/app/assets/javascripts/jquery.mockjax.js | 692 +++++++++++++++++++
 .../src/main/resources/ui/app/assets/tests.html |   1 +
 .../slider/src/main/resources/ui/package.json   |   3 +-
 .../slider/src/main/resources/ui/runner.js      | 136 ++++
 .../slider/src/main/resources/ui/test/index.md  |  28 +
 .../processes/create_new_app_test.js            |  17 +-
 pom.xml                                         |   1 +
 9 files changed, 877 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/contrib/views/slider/docs/index.md
----------------------------------------------------------------------
diff --git a/contrib/views/slider/docs/index.md 
b/contrib/views/slider/docs/index.md
index bd23d14..ce09e28 100644
--- a/contrib/views/slider/docs/index.md
+++ b/contrib/views/slider/docs/index.md
@@ -101,7 +101,7 @@ kadmin.local -q "addprinc -randkey slider-u...@example.com"
 Next, extract keytab file 
 
 ```
-kadmin.local -q "xst -k /path/to/keytab/slider-user.headless.keytab 
view-princi...@example.com"
+kadmin.local -q "xst -k /path/to/keytab/slider-user.headless.keytab 
slider-u...@example.com"
 ```
 The keytab file should then be copied over to the keytabs location on the host 
where the view is hosted.
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/contrib/views/slider/pom.xml
----------------------------------------------------------------------
diff --git a/contrib/views/slider/pom.xml b/contrib/views/slider/pom.xml
index cb07dd4..0fd7de9 100644
--- a/contrib/views/slider/pom.xml
+++ b/contrib/views/slider/pom.xml
@@ -480,9 +480,11 @@
                                                        <goal>run</goal>
                                                </goals>
                                                <configuration>
-                                                       <target 
name="slider-ui-test">
-                                                               <!-- <exec 
dir="${ui.directory}" executable="brunch" failonerror="false">
-                                                                       <arg 
value="test"/> </exec> -->
+                                                       <target 
name="slider-ui-test" unless="${skipTests}">
+                <exec dir="${ui.directory}" executable="npm" 
failonerror="true">
+                  <env key="PYTHON" 
value="${basedir}/../ambari-common/src/main/unix/ambari-python-wrap" />
+                  <arg value="test"/>
+                </exec>
                                                        </target>
                                                </configuration>
                                        </execution>

http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/jquery.mockjax.js
----------------------------------------------------------------------
diff --git 
a/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/jquery.mockjax.js
 
b/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/jquery.mockjax.js
new file mode 100644
index 0000000..54fa8ec
--- /dev/null
+++ 
b/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/jquery.mockjax.js
@@ -0,0 +1,692 @@
+/*!
+ * MockJax - jQuery Plugin to Mock Ajax requests
+ *
+ * Version:  1.6.0
+ * Released:
+ * Home:   https://github.com/jakerella/jquery-mockjax
+ * Author:   Jonathan Sharp (http://jdsharp.com)
+ * License:  MIT,GPL
+ *
+ * Copyright (c) 2014 appendTo, Jordan Kasper
+ * NOTE: This repository was taken over by Jordan Kasper (@jakerella) October, 
2014
+ *
+ * Dual licensed under the MIT or GPL licenses.
+ * http://opensource.org/licenses/MIT OR 
http://www.gnu.org/licenses/gpl-2.0.html
+ */
+(function($) {
+  var _ajax = $.ajax,
+    mockHandlers = [],
+    mockedAjaxCalls = [],
+    unmockedAjaxCalls = [],
+    CALLBACK_REGEX = /=\?(&|$)/,
+    jsc = (new Date()).getTime();
+
+
+  // Parse the given XML string.
+  function parseXML(xml) {
+    if ( window.DOMParser == undefined && window.ActiveXObject ) {
+      DOMParser = function() { };
+      DOMParser.prototype.parseFromString = function( xmlString ) {
+        var doc = new ActiveXObject('Microsoft.XMLDOM');
+        doc.async = 'false';
+        doc.loadXML( xmlString );
+        return doc;
+      };
+    }
+
+    try {
+      var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
+      if ( $.isXMLDoc( xmlDoc ) ) {
+        var err = $('parsererror', xmlDoc);
+        if ( err.length == 1 ) {
+          throw new Error('Error: ' + $(xmlDoc).text() );
+        }
+      } else {
+        throw new Error('Unable to parse XML');
+      }
+      return xmlDoc;
+    } catch( e ) {
+      var msg = ( e.name == undefined ? e : e.name + ': ' + e.message );
+      $(document).trigger('xmlParseError', [ msg ]);
+      return undefined;
+    }
+  }
+
+  // Check if the data field on the mock handler and the request match. This
+  // can be used to restrict a mock handler to being used only when a certain
+  // set of data is passed to it.
+  function isMockDataEqual( mock, live ) {
+    var identical = true;
+    // Test for situations where the data is a querystring (not an object)
+    if (typeof live === 'string') {
+      // Querystring may be a regex
+      return $.isFunction( mock.test ) ? mock.test(live) : mock == live;
+    }
+    $.each(mock, function(k) {
+      if ( live[k] === undefined ) {
+        identical = false;
+        return identical;
+      } else {
+        if ( typeof live[k] === 'object' && live[k] !== null ) {
+          if ( identical && $.isArray( live[k] ) ) {
+            identical = $.isArray( mock[k] ) && live[k].length === 
mock[k].length;
+          }
+          identical = identical && isMockDataEqual(mock[k], live[k]);
+        } else {
+          if ( mock[k] && $.isFunction( mock[k].test ) ) {
+            identical = identical && mock[k].test(live[k]);
+          } else {
+            identical = identical && ( mock[k] == live[k] );
+          }
+        }
+      }
+    });
+
+    return identical;
+  }
+
+  // See if a mock handler property matches the default settings
+  function isDefaultSetting(handler, property) {
+    return handler[property] === $.mockjaxSettings[property];
+  }
+
+  // Check the given handler should mock the given request
+  function getMockForRequest( handler, requestSettings ) {
+    // If the mock was registered with a function, let the function decide if 
we
+    // want to mock this request
+    if ( $.isFunction(handler) ) {
+      return handler( requestSettings );
+    }
+
+    // Inspect the URL of the request and check if the mock handler's url
+    // matches the url for this ajax request
+    if ( $.isFunction(handler.url.test) ) {
+      // The user provided a regex for the url, test it
+      if ( !handler.url.test( requestSettings.url ) ) {
+        return null;
+      }
+    } else {
+      // Look for a simple wildcard '*' or a direct URL match
+      var star = handler.url.indexOf('*');
+      if (handler.url !== requestSettings.url && star === -1 ||
+        !new RegExp(handler.url.replace(/[-[\]{}()+?.,\\^$|#\s]/g, 
"\\$&").replace(/\*/g, '.+')).test(requestSettings.url)) {
+        return null;
+      }
+    }
+
+    // Inspect the data submitted in the request (either POST body or GET 
query string)
+    if ( handler.data ) {
+      if ( ! requestSettings.data || !isMockDataEqual(handler.data, 
requestSettings.data) ) {
+        // They're not identical, do not mock this request
+        return null;
+      }
+    }
+    // Inspect the request type
+    if ( handler && handler.type &&
+      handler.type.toLowerCase() != requestSettings.type.toLowerCase() ) {
+      // The request type doesn't match (GET vs. POST)
+      return null;
+    }
+
+    return handler;
+  }
+
+  function parseResponseTimeOpt(responseTime) {
+    if ($.isArray(responseTime)) {
+      var min = responseTime[0];
+      var max = responseTime[1];
+      return (typeof min === 'number' && typeof max === 'number') ? 
Math.floor(Math.random() * (max - min)) + min : null;
+    } else {
+      return (typeof responseTime === 'number') ? responseTime: null;
+    }
+  }
+
+  // Process the xhr objects send operation
+  function _xhrSend(mockHandler, requestSettings, origSettings) {
+
+    // This is a substitute for < 1.4 which lacks $.proxy
+    var process = (function(that) {
+      return function() {
+        return (function() {
+          // The request has returned
+          this.status     = mockHandler.status;
+          this.statusText = mockHandler.statusText;
+          this.readyState      = 1;
+
+          var finishRequest = function () {
+            this.readyState    = 4;
+
+            var onReady;
+            // Copy over our mock to our xhr object before passing control 
back to
+            // jQuery's onreadystatechange callback
+            if ( requestSettings.dataType == 'json' && ( typeof 
mockHandler.responseText == 'object' ) ) {
+              this.responseText = JSON.stringify(mockHandler.responseText);
+            } else if ( requestSettings.dataType == 'xml' ) {
+              if ( typeof mockHandler.responseXML == 'string' ) {
+                this.responseXML = parseXML(mockHandler.responseXML);
+                //in jQuery 1.9.1+, responseXML is processed differently and 
relies on responseText
+                this.responseText = mockHandler.responseXML;
+              } else {
+                this.responseXML = mockHandler.responseXML;
+              }
+            } else if (typeof mockHandler.responseText === 'object' && 
mockHandler.responseText !== null) {
+              // since jQuery 1.9 responseText type has to match contentType
+              mockHandler.contentType = 'application/json';
+              this.responseText = JSON.stringify(mockHandler.responseText);
+            } else {
+              this.responseText = mockHandler.responseText;
+            }
+            if( typeof mockHandler.status == 'number' || typeof 
mockHandler.status == 'string' ) {
+              this.status = mockHandler.status;
+            }
+            if( typeof mockHandler.statusText === "string") {
+              this.statusText = mockHandler.statusText;
+            }
+            // jQuery 2.0 renamed onreadystatechange to onload
+            onReady = this.onreadystatechange || this.onload;
+
+            // jQuery < 1.4 doesn't have onreadystate change for xhr
+            if ( $.isFunction( onReady ) ) {
+              if( mockHandler.isTimeout) {
+                this.status = -1;
+              }
+              onReady.call( this, mockHandler.isTimeout ? 'timeout' : 
undefined );
+            } else if ( mockHandler.isTimeout ) {
+              // Fix for 1.3.2 timeout to keep success from firing.
+              this.status = -1;
+            }
+          };
+
+          // We have an executable function, call it to give
+          // the mock handler a chance to update it's data
+          if ( $.isFunction(mockHandler.response) ) {
+            // Wait for it to finish
+            if ( mockHandler.response.length === 2 ) {
+              mockHandler.response(origSettings, function () {
+                finishRequest.call(that);
+              });
+              return;
+            } else {
+              mockHandler.response(origSettings);
+            }
+          }
+
+          finishRequest.call(that);
+        }).apply(that);
+      };
+    })(this);
+
+    if ( mockHandler.proxy ) {
+      // We're proxying this request and loading in an external file instead
+      _ajax({
+        global: false,
+        url: mockHandler.proxy,
+        type: mockHandler.proxyType,
+        data: mockHandler.data,
+        dataType: requestSettings.dataType === "script" ? "text/plain" : 
requestSettings.dataType,
+        complete: function(xhr) {
+          mockHandler.responseXML = xhr.responseXML;
+          mockHandler.responseText = xhr.responseText;
+          // Don't override the handler status/statusText if it's specified by 
the config
+          if (isDefaultSetting(mockHandler, 'status')) {
+            mockHandler.status = xhr.status;
+          }
+          if (isDefaultSetting(mockHandler, 'statusText')) {
+            mockHandler.statusText = xhr.statusText;
+          }
+          this.responseTimer = setTimeout(process, 
parseResponseTimeOpt(mockHandler.responseTime) || 0);
+        }
+      });
+    } else {
+      // type == 'POST' || 'GET' || 'DELETE'
+      if ( requestSettings.async === false ) {
+        // TODO: Blocking delay
+        process();
+      } else {
+        this.responseTimer = setTimeout(process, 
parseResponseTimeOpt(mockHandler.responseTime) || 50);
+      }
+    }
+  }
+
+  // Construct a mocked XHR Object
+  function xhr(mockHandler, requestSettings, origSettings, origHandler) {
+    // Extend with our default mockjax settings
+    mockHandler = $.extend(true, {}, $.mockjaxSettings, mockHandler);
+
+    if (typeof mockHandler.headers === 'undefined') {
+      mockHandler.headers = {};
+    }
+    if (typeof requestSettings.headers === 'undefined') {
+      requestSettings.headers = {};
+    }
+    if ( mockHandler.contentType ) {
+      mockHandler.headers['content-type'] = mockHandler.contentType;
+    }
+
+    return {
+      status: mockHandler.status,
+      statusText: mockHandler.statusText,
+      readyState: 1,
+      open: function() { },
+      send: function() {
+        origHandler.fired = true;
+        _xhrSend.call(this, mockHandler, requestSettings, origSettings);
+      },
+      abort: function() {
+        clearTimeout(this.responseTimer);
+      },
+      setRequestHeader: function(header, value) {
+        requestSettings.headers[header] = value;
+      },
+      getResponseHeader: function(header) {
+        // 'Last-modified', 'Etag', 'content-type' are all checked by jQuery
+        if ( mockHandler.headers && mockHandler.headers[header] ) {
+          // Return arbitrary headers
+          return mockHandler.headers[header];
+        } else if ( header.toLowerCase() == 'last-modified' ) {
+          return mockHandler.lastModified || (new Date()).toString();
+        } else if ( header.toLowerCase() == 'etag' ) {
+          return mockHandler.etag || '';
+        } else if ( header.toLowerCase() == 'content-type' ) {
+          return mockHandler.contentType || 'text/plain';
+        }
+      },
+      getAllResponseHeaders: function() {
+        var headers = '';
+        // since jQuery 1.9 responseText type has to match contentType
+        if (mockHandler.contentType) {
+          mockHandler.headers['Content-Type'] = mockHandler.contentType;
+        }
+        $.each(mockHandler.headers, function(k, v) {
+          headers += k + ': ' + v + "\n";
+        });
+        return headers;
+      }
+    };
+  }
+
+  // Process a JSONP mock request.
+  function processJsonpMock( requestSettings, mockHandler, origSettings ) {
+    // Handle JSONP Parameter Callbacks, we need to replicate some of the 
jQuery core here
+    // because there isn't an easy hook for the cross domain script tag of 
jsonp
+
+    processJsonpUrl( requestSettings );
+
+    requestSettings.dataType = "json";
+    if(requestSettings.data && CALLBACK_REGEX.test(requestSettings.data) || 
CALLBACK_REGEX.test(requestSettings.url)) {
+      createJsonpCallback(requestSettings, mockHandler, origSettings);
+
+      // We need to make sure
+      // that a JSONP style response is executed properly
+
+      var rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+        parts = rurl.exec( requestSettings.url ),
+        remote = parts && (parts[1] && parts[1] !== location.protocol || 
parts[2] !== location.host);
+
+      requestSettings.dataType = "script";
+      if(requestSettings.type.toUpperCase() === "GET" && remote ) {
+        var newMockReturn = processJsonpRequest( requestSettings, mockHandler, 
origSettings );
+
+        // Check if we are supposed to return a Deferred back to the mock 
call, or just
+        // signal success
+        if(newMockReturn) {
+          return newMockReturn;
+        } else {
+          return true;
+        }
+      }
+    }
+    return null;
+  }
+
+  // Append the required callback parameter to the end of the request URL, for 
a JSONP request
+  function processJsonpUrl( requestSettings ) {
+    if ( requestSettings.type.toUpperCase() === "GET" ) {
+      if ( !CALLBACK_REGEX.test( requestSettings.url ) ) {
+        requestSettings.url += (/\?/.test( requestSettings.url ) ? "&" : "?") +
+          (requestSettings.jsonp || "callback") + "=?";
+      }
+    } else if ( !requestSettings.data || 
!CALLBACK_REGEX.test(requestSettings.data) ) {
+      requestSettings.data = (requestSettings.data ? requestSettings.data + 
"&" : "") + (requestSettings.jsonp || "callback") + "=?";
+    }
+  }
+
+  // Process a JSONP request by evaluating the mocked response text
+  function processJsonpRequest( requestSettings, mockHandler, origSettings ) {
+    // Synthesize the mock request for adding a script tag
+    var callbackContext = origSettings && origSettings.context || 
requestSettings,
+      newMock = null;
+
+
+    // If the response handler on the moock is a function, call it
+    if ( mockHandler.response && $.isFunction(mockHandler.response) ) {
+      mockHandler.response(origSettings);
+    } else {
+
+      // Evaluate the responseText javascript in a global context
+      if( typeof mockHandler.responseText === 'object' ) {
+        $.globalEval( '(' + JSON.stringify( mockHandler.responseText ) + ')');
+      } else {
+        $.globalEval( '(' + mockHandler.responseText + ')');
+      }
+    }
+
+    // Successful response
+    setTimeout(function() {
+      jsonpSuccess( requestSettings, callbackContext, mockHandler );
+      jsonpComplete( requestSettings, callbackContext, mockHandler );
+    }, parseResponseTimeOpt(mockHandler.responseTime) || 0);
+
+    // If we are running under jQuery 1.5+, return a deferred object
+    if($.Deferred){
+      newMock = new $.Deferred();
+      if(typeof mockHandler.responseText == "object"){
+        newMock.resolveWith( callbackContext, [mockHandler.responseText] );
+      }
+      else{
+        newMock.resolveWith( callbackContext, [$.parseJSON( 
mockHandler.responseText )] );
+      }
+    }
+    return newMock;
+  }
+
+
+  // Create the required JSONP callback function for the request
+  function createJsonpCallback( requestSettings, mockHandler, origSettings ) {
+    var callbackContext = origSettings && origSettings.context || 
requestSettings;
+    var jsonp = requestSettings.jsonpCallback || ("jsonp" + jsc++);
+
+    // Replace the =? sequence both in the query string and the data
+    if ( requestSettings.data ) {
+      requestSettings.data = (requestSettings.data + 
"").replace(CALLBACK_REGEX, "=" + jsonp + "$1");
+    }
+
+    requestSettings.url = requestSettings.url.replace(CALLBACK_REGEX, "=" + 
jsonp + "$1");
+
+
+    // Handle JSONP-style loading
+    window[ jsonp ] = window[ jsonp ] || function( tmp ) {
+      data = tmp;
+      jsonpSuccess( requestSettings, callbackContext, mockHandler );
+      jsonpComplete( requestSettings, callbackContext, mockHandler );
+      // Garbage collect
+      window[ jsonp ] = undefined;
+
+      try {
+        delete window[ jsonp ];
+      } catch(e) {}
+
+      if ( head ) {
+        head.removeChild( script );
+      }
+    };
+  }
+
+  // The JSONP request was successful
+  function jsonpSuccess(requestSettings, callbackContext, mockHandler) {
+    // If a local callback was specified, fire it and pass it the data
+    if ( requestSettings.success ) {
+      requestSettings.success.call( callbackContext, mockHandler.responseText 
|| "", status, {} );
+    }
+
+    // Fire the global callback
+    if ( requestSettings.global ) {
+      (requestSettings.context ? $(requestSettings.context) : 
$.event).trigger("ajaxSuccess", [{}, requestSettings]);
+    }
+  }
+
+  // The JSONP request was completed
+  function jsonpComplete(requestSettings, callbackContext) {
+    // Process result
+    if ( requestSettings.complete ) {
+      requestSettings.complete.call( callbackContext, {} , status );
+    }
+
+    // The request was completed
+    if ( requestSettings.global ) {
+      (requestSettings.context ? $(requestSettings.context) : 
$.event).trigger("ajaxComplete", [{}, requestSettings]);
+    }
+
+    // Handle the global AJAX counter
+    if ( requestSettings.global && ! --$.active ) {
+      $.event.trigger( "ajaxStop" );
+    }
+  }
+
+
+  // The core $.ajax replacement.
+  function handleAjax( url, origSettings ) {
+    var mockRequest, requestSettings, mockHandler, overrideCallback;
+
+    // If url is an object, simulate pre-1.5 signature
+    if ( typeof url === "object" ) {
+      origSettings = url;
+      url = undefined;
+    } else {
+      // work around to support 1.5 signature
+      origSettings = origSettings || {};
+      origSettings.url = url;
+    }
+
+    // Extend the original settings for the request
+    requestSettings = $.extend(true, {}, $.ajaxSettings, origSettings);
+
+    // Generic function to override callback methods for use with
+    // callback options (onAfterSuccess, onAfterError, onAfterComplete)
+    overrideCallback = function(action, mockHandler) {
+      var origHandler = origSettings[action.toLowerCase()];
+      return function() {
+        if ( $.isFunction(origHandler) ) {
+          origHandler.apply(this, [].slice.call(arguments));
+        }
+        mockHandler['onAfter' + action]();
+      };
+    };
+
+    // Iterate over our mock handlers (in registration order) until we find
+    // one that is willing to intercept the request
+    for(var k = 0; k < mockHandlers.length; k++) {
+      if ( !mockHandlers[k] ) {
+        continue;
+      }
+
+      mockHandler = getMockForRequest( mockHandlers[k], requestSettings );
+      if(!mockHandler) {
+        // No valid mock found for this request
+        continue;
+      }
+
+      mockedAjaxCalls.push(requestSettings);
+
+      // If logging is enabled, log the mock to the console
+      $.mockjaxSettings.log( mockHandler, requestSettings );
+
+
+      if ( requestSettings.dataType && requestSettings.dataType.toUpperCase() 
=== 'JSONP' ) {
+        if ((mockRequest = processJsonpMock( requestSettings, mockHandler, 
origSettings ))) {
+          // This mock will handle the JSONP request
+          return mockRequest;
+        }
+      }
+
+
+      // Removed to fix #54 - keep the mocking data object intact
+      //mockHandler.data = requestSettings.data;
+
+      mockHandler.cache = requestSettings.cache;
+      mockHandler.timeout = requestSettings.timeout;
+      mockHandler.global = requestSettings.global;
+
+      // In the case of a timeout, we just need to ensure
+      // an actual jQuery timeout (That is, our reponse won't)
+      // return faster than the timeout setting.
+      if ( mockHandler.isTimeout ) {
+        if ( mockHandler.responseTime > 1 ) {
+          origSettings.timeout = mockHandler.responseTime - 1;
+        } else {
+          mockHandler.responseTime = 2;
+          origSettings.timeout = 1;
+        }
+        mockHandler.isTimeout = false;
+      }
+
+      // Set up onAfter[X] callback functions
+      if ( $.isFunction( mockHandler.onAfterSuccess ) ) {
+        origSettings.success = overrideCallback('Success', mockHandler);
+      }
+      if ( $.isFunction( mockHandler.onAfterError ) ) {
+        origSettings.error = overrideCallback('Error', mockHandler);
+      }
+      if ( $.isFunction( mockHandler.onAfterComplete ) ) {
+        origSettings.complete = overrideCallback('Complete', mockHandler);
+      }
+
+      copyUrlParameters(mockHandler, origSettings);
+
+      (function(mockHandler, requestSettings, origSettings, origHandler) {
+
+        mockRequest = _ajax.call($, $.extend(true, {}, origSettings, {
+          // Mock the XHR object
+          xhr: function() { return xhr( mockHandler, requestSettings, 
origSettings, origHandler ); }
+        }));
+      })(mockHandler, requestSettings, origSettings, mockHandlers[k]);
+
+      return mockRequest;
+    }
+
+    // We don't have a mock request
+    unmockedAjaxCalls.push(origSettings);
+    if($.mockjaxSettings.throwUnmocked === true) {
+      throw new Error('AJAX not mocked: ' + origSettings.url);
+    }
+    else { // trigger a normal request
+      return _ajax.apply($, [origSettings]);
+    }
+  }
+
+  /**
+   * Copies URL parameter values if they were captured by a regular expression
+   * @param {Object} mockHandler
+   * @param {Object} origSettings
+   */
+  function copyUrlParameters(mockHandler, origSettings) {
+    //parameters aren't captured if the URL isn't a RegExp
+    if (!(mockHandler.url instanceof RegExp)) {
+      return;
+    }
+    //if no URL params were defined on the handler, don't attempt a capture
+    if (!mockHandler.hasOwnProperty('urlParams')) {
+      return;
+    }
+    var captures = mockHandler.url.exec(origSettings.url);
+    //the whole RegExp match is always the first value in the capture results
+    if (captures.length === 1) {
+      return;
+    }
+    captures.shift();
+    //use handler params as keys and capture resuts as values
+    var i = 0,
+      capturesLength = captures.length,
+      paramsLength = mockHandler.urlParams.length,
+    //in case the number of params specified is less than actual captures
+      maxIterations = Math.min(capturesLength, paramsLength),
+      paramValues = {};
+    for (i; i < maxIterations; i++) {
+      var key = mockHandler.urlParams[i];
+      paramValues[key] = captures[i];
+    }
+    origSettings.urlParams = paramValues;
+  }
+
+
+  // Public
+
+  $.extend({
+    ajax: handleAjax
+  });
+
+  $.mockjaxSettings = {
+    //url:        null,
+    //type:       'GET',
+    log:          function( mockHandler, requestSettings ) {
+      if ( mockHandler.logging === false ||
+        ( typeof mockHandler.logging === 'undefined' && 
$.mockjaxSettings.logging === false ) ) {
+        return;
+      }
+      if ( window.console && console.log ) {
+        var message = 'MOCK ' + requestSettings.type.toUpperCase() + ': ' + 
requestSettings.url;
+        var request = $.extend({}, requestSettings);
+
+        if (typeof console.log === 'function') {
+          console.log(message, request);
+        } else {
+          try {
+            console.log( message + ' ' + JSON.stringify(request) );
+          } catch (e) {
+            console.log(message);
+          }
+        }
+      }
+    },
+    logging:       true,
+    status:        200,
+    statusText:    "OK",
+    responseTime:  500,
+    isTimeout:     false,
+    throwUnmocked: false,
+    contentType:   'text/plain',
+    response:      '',
+    responseText:  '',
+    responseXML:   '',
+    proxy:         '',
+    proxyType:     'GET',
+
+    lastModified:  null,
+    etag:          '',
+    headers: {
+      etag: 'IJF@H#@923uf8023hFO@I#H#',
+      'content-type' : 'text/plain'
+    }
+  };
+
+  $.mockjax = function(settings) {
+    var i = mockHandlers.length;
+    mockHandlers[i] = settings;
+    return i;
+  };
+  $.mockjax.clear = function(i) {
+    if ( arguments.length == 1 ) {
+      mockHandlers[i] = null;
+    } else {
+      mockHandlers = [];
+    }
+    mockedAjaxCalls = [];
+    unmockedAjaxCalls = [];
+  };
+  // support older, deprecated version
+  $.mockjaxClear = function(i) {
+    window.console && window.console.warn && window.console.warn( 'DEPRECATED: 
The $.mockjaxClear() method has been deprecated in 1.6.0. Please use 
$.mockjax.clear() as the older function will be removed soon!' );
+    $.mockjax.clear();
+  };
+  $.mockjax.handler = function(i) {
+    if ( arguments.length == 1 ) {
+      return mockHandlers[i];
+    }
+  };
+  $.mockjax.mockedAjaxCalls = function() {
+    return mockedAjaxCalls;
+  };
+  $.mockjax.unfiredHandlers = function() {
+    var results = [];
+    for (var i=0, len=mockHandlers.length; i<len; i++) {
+      var handler = mockHandlers[i];
+      if (handler !== null && !handler.fired) {
+        results.push(handler);
+      }
+    }
+    return results;
+  };
+  $.mockjax.unmockedAjaxCalls = function() {
+    return unmockedAjaxCalls;
+  };
+})(jQuery);

http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/contrib/views/slider/src/main/resources/ui/app/assets/tests.html
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/assets/tests.html 
b/contrib/views/slider/src/main/resources/ui/app/assets/tests.html
index 70da2a1..6c08bff 100644
--- a/contrib/views/slider/src/main/resources/ui/app/assets/tests.html
+++ b/contrib/views/slider/src/main/resources/ui/app/assets/tests.html
@@ -28,6 +28,7 @@
 <div id="qunit-fixture"></div>
 <script src="javascripts/qunit.js"></script>
 <script src="javascripts/vendor.js"></script>
+<script src="javascripts/jquery.mockjax.js"></script>
 <script src="javascripts/ember-qunit.js"></script>
 <script src="javascripts/app.js"></script>
 <script>

http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/contrib/views/slider/src/main/resources/ui/package.json
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/package.json 
b/contrib/views/slider/src/main/resources/ui/package.json
index daf0943..03a3310 100755
--- a/contrib/views/slider/src/main/resources/ui/package.json
+++ b/contrib/views/slider/src/main/resources/ui/package.json
@@ -12,7 +12,7 @@
     "node": "~0.6.10 || 0.8 || 0.9"
   },
   "scripts": {
-    "test": "./node_modules/karma/bin/karma start --single-run --browsers 
PhantomJS",
+    "test": "node_modules/phantomjs/bin/phantomjs runner.js public/tests.html",
     "start": "brunch watch --server",
     "build": "brunch build",
     "build:production": "brunch build --production",
@@ -29,6 +29,7 @@
     "ember-handlebars-brunch": 
"git://github.com/fuseelements/ember-handlebars-brunch#fix/ember-1.3.0"
   },
   "devDependencies": {
+    "phantomjs": "^1.9.2",
     "karma": "*",
     "karma-qunit": "*",
     "karma-phantomjs-launcher": "~0.1.2"

http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/contrib/views/slider/src/main/resources/ui/runner.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/runner.js 
b/contrib/views/slider/src/main/resources/ui/runner.js
new file mode 100644
index 0000000..4fd7894
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/runner.js
@@ -0,0 +1,136 @@
+/*
+ * PhantomJS Runner QUnit Plugin (List Tests) 1.2.0
+ *
+ * PhantomJS binaries: http://phantomjs.org/download.html
+ * Requires PhantomJS 1.6+ (1.7+ recommended)
+ *
+ * Run with:
+ *   phantomjs runner-list.js [url-of-your-qunit-testsuite]
+ *
+ * e.g.
+ *   phantomjs runner-list.js http://localhost/qunit/test/index.html
+ */
+
+/*global phantom:false, require:false, console:false, window:false, 
QUnit:false */
+
+(function() {
+  'use strict';
+
+  var url, page, timeout,
+    args = require('system').args;
+
+  // arg[0]: scriptName, args[1...]: arguments
+  if (args.length < 2 || args.length > 3) {
+    console.error('Usage:\n  phantomjs runner-list.js 
[url-of-your-qunit-testsuite] [timeout-in-seconds]');
+    phantom.exit(1);
+  }
+
+  url = args[1];
+  page = require('webpage').create();
+  if (args[2] !== undefined) {
+    timeout = parseInt(args[2], 10);
+  }
+
+  // Route `console.log()` calls from within the Page context to the main 
Phantom context (i.e. current `this`)
+  page.onConsoleMessage = function(msg) {
+    console.log(msg);
+  };
+
+  page.onInitialized = function() {
+    page.evaluate(addLogging);
+  };
+
+  page.onCallback = function(message) {
+    var result,
+      failed;
+
+    if (message) {
+      if (message.name === 'QUnit.done') {
+        result = message.data;
+        failed = !result || !result.total || result.failed;
+
+        if (!result.total) {
+          console.error('No tests were executed. Are you loading tests 
asynchronously?');
+        }
+
+        phantom.exit(failed ? 1 : 0);
+      }
+    }
+  };
+
+  page.open(url, function(status) {
+    if (status !== 'success') {
+      console.error('Unable to access network: ' + status);
+      phantom.exit(1);
+    } else {
+      // Cannot do this verification with the 'DOMContentLoaded' handler 
because it
+      // will be too late to attach it if a page does not have any script tags.
+      var qunitMissing = page.evaluate(function() { return (typeof QUnit === 
'undefined' || !QUnit); });
+      if (qunitMissing) {
+        console.error('The `QUnit` object is not present on this page.');
+        phantom.exit(1);
+      }
+
+      // Set a timeout on the test running, otherwise tests with async 
problems will hang forever
+      if (typeof timeout === 'number') {
+        setTimeout(function() {
+          console.error('The specified timeout of ' + timeout + ' seconds has 
expired. Aborting...');
+          phantom.exit(1);
+        }, timeout * 1000);
+      }
+
+      // Do nothing... the callback mechanism will handle everything!
+    }
+  });
+
+  function addLogging() {
+    window.document.addEventListener('DOMContentLoaded', function() {
+      var currentTestAssertions = [];
+
+      QUnit.log(function(details) {
+        var response;
+
+        console.log((details.result ? "? ": "? ") + details.message);
+
+        if (!details.result) {
+          response = details.message || '';
+
+          if (typeof details.expected !== 'undefined') {
+            if (response) {
+              response += ', ';
+            }
+
+            response += 'expected: ' + details.expected + ', but was: ' + 
details.actual;
+          }
+
+          if (details.source) {
+            response += '\n' + details.source;
+          }
+
+          console.log('    Failed assertion: ' + response);
+        }
+      });
+
+      QUnit.moduleStart(function( details ) {
+        if (details.name) {
+          console.log('\n' + details.name);
+        }
+      });
+
+      QUnit.testStart(function(result) {
+        console.log('\n' + result.name);
+      });
+
+      QUnit.done(function(result) {
+        console.log('\n' + 'Took ' + result.runtime +  'ms to run ' + 
result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' 
failed.');
+
+        if (typeof window.callPhantom === 'function') {
+          window.callPhantom({
+            'name': 'QUnit.done',
+            'data': result
+          });
+        }
+      });
+    }, false);
+  }
+})();

http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/contrib/views/slider/src/main/resources/ui/test/index.md
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/index.md 
b/contrib/views/slider/src/main/resources/ui/test/index.md
new file mode 100644
index 0000000..d62a3e7
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/test/index.md
@@ -0,0 +1,28 @@
+<!---
+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](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.
+-->
+
+# Slider UI Unit Tests
+
+Go to slider-ui folder - `/contrib/views/slider/src/main/resources/ui/`
+Execute:
+
+1. `npm i`
+2. `brunch w -s`
+
+Open in browser `http://localhost:3333/tests.html`
+
+OR
+
+Execute in console `node_modules/phantomjs/bin/phantomjs runner.js 
public/tests.html`

http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
----------------------------------------------------------------------
diff --git 
a/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
 
b/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
index ddef342..ac984b8 100644
--- 
a/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
+++ 
b/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
@@ -107,6 +107,15 @@ var selectors = {
 QUnit.module('integration/processes - Create New App', {
 
   setup: function () {
+
+    $.mockjax({
+      type: 'GET',
+      url: '*',
+      status: '200',
+      dataType: 'json',
+      responseText: {}
+    });
+
     Em.run(App, App.advanceReadiness);
     Em.run(function () {
       App.set('viewEnabled', true); // Important!
@@ -116,6 +125,7 @@ QUnit.module('integration/processes - Create New App', {
 
   teardown: function () {
     App.reset();
+    $.mockjax.clear();
   }
 
 });
@@ -174,11 +184,6 @@ test('basic (no errors - just valid data)', function () {
       ok(find('#step4').text().indexOf('HBASE_REGIONSERVER: ' + 
newApp.components.HBASE_REGIONSERVER) > -1, 'HBASE_REGIONSERVER count exists');
       ok(find('pre').text().indexOf('"' + newApp.newConfig.name + '":"' + 
newApp.newConfig.value + '"') > -1, 'Custom property exists');
 
-      click('button.btn-success');
-      andThen(function () {
-        /* FINISHED */
-        equal(currentURL(), '/', 'After click "Finish" user is sent to index 
page');
-      });
     });
   });
 
@@ -347,4 +352,4 @@ test('check step3 back', function () {
     });
   });
 
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/6260332e/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 7862e84..d31876f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -240,6 +240,7 @@
             
<exclude>contrib/views/slider/src/main/resources/ui/app/assets/stylesheets/**</exclude>
             
<exclude>contrib/views/slider/src/main/resources/ui/app/assets/javascripts/**</exclude>
             
<exclude>contrib/views/slider/src/main/resources/ui/bower_components/**</exclude>
+            
<exclude>contrib/views/slider/src/main/resources/ui/runner.js</exclude>
             
<exclude>contrib/addons/package/deb/nagios_addon_deb_control</exclude>
             
<exclude>contrib/addons/src/addOns/nagios/conf.d/hdp_mon_nagios_addons.conf</exclude>
             <exclude>contrib/views/*/.classpath</exclude>

Reply via email to