vendor/assets/javascripts/angular-mocks.js in angularjs-rails-1.0.8 vs vendor/assets/javascripts/angular-mocks.js in angularjs-rails-1.2.0.rc1

- old
+ new

@@ -1,7 +1,7 @@ /** - * @license AngularJS v1.0.8 + * @license AngularJS v1.2.0rc1 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT * * TODO(vojta): wrap whole file into closure during build */ @@ -116,11 +116,27 @@ while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) { self.deferredFns.shift().fn(); } }; + /** + * @name ngMock.$browser#defer.flushNext + * @methodOf ngMock.$browser + * + * @description + * Flushes next pending request and compares it to the provided delay + * + * @param {number=} expectedDelay the delay value that will be asserted against the delay of the next timeout function + */ + self.defer.flushNext = function(expectedDelay) { + var tick = self.deferredFns.shift(); + expect(tick.time).toEqual(expectedDelay); + tick.fn(); + }; + + /** * @name ngMock.$browser#defer.now * @propertyOf ngMock.$browser * * @description * Current milliseconds mock time. @@ -291,22 +307,36 @@ * (one array per logging level). These arrays are exposed as `logs` property of each of the * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`. * */ angular.mock.$LogProvider = function() { + var debug = true; function concat(array1, array2, index) { return array1.concat(Array.prototype.slice.call(array2, index)); } + this.debugEnabled = function(flag) { + if (isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } + }; this.$get = function () { var $log = { log: function() { $log.log.logs.push(concat([], arguments, 0)); }, warn: function() { $log.warn.logs.push(concat([], arguments, 0)); }, info: function() { $log.info.logs.push(concat([], arguments, 0)); }, - error: function() { $log.error.logs.push(concat([], arguments, 0)); } + error: function() { $log.error.logs.push(concat([], arguments, 0)); }, + debug: function() { + if (debug) { + $log.debug.logs.push(concat([], arguments, 0)); + } + } }; /** * @ngdoc method * @name ngMock.$log#reset @@ -331,38 +361,38 @@ * </pre> */ $log.log.logs = []; /** * @ngdoc property - * @name ngMock.$log#warn.logs + * @name ngMock.$log#info.logs * @propertyOf ngMock.$log * * @description - * Array of messages logged using {@link ngMock.$log#warn}. + * Array of messages logged using {@link ngMock.$log#info}. * * @example * <pre> - * $log.warn('Some Warning'); - * var first = $log.warn.logs.unshift(); + * $log.info('Some Info'); + * var first = $log.info.logs.unshift(); * </pre> */ - $log.warn.logs = []; + $log.info.logs = []; /** * @ngdoc property - * @name ngMock.$log#info.logs + * @name ngMock.$log#warn.logs * @propertyOf ngMock.$log * * @description - * Array of messages logged using {@link ngMock.$log#info}. + * Array of messages logged using {@link ngMock.$log#warn}. * * @example * <pre> - * $log.info('Some Info'); - * var first = $log.info.logs.unshift(); + * $log.warn('Some Warning'); + * var first = $log.warn.logs.unshift(); * </pre> */ - $log.info.logs = []; + $log.warn.logs = []; /** * @ngdoc property * @name ngMock.$log#error.logs * @propertyOf ngMock.$log * @@ -374,10 +404,25 @@ * $log.log('Some Error'); * var first = $log.error.logs.unshift(); * </pre> */ $log.error.logs = []; + /** + * @ngdoc property + * @name ngMock.$log#debug.logs + * @propertyOf ngMock.$log + * + * @description + * Array of messages logged using {@link ngMock.$log#debug}. + * + * @example + * <pre> + * $log.debug('Some Error'); + * var first = $log.debug.logs.unshift(); + * </pre> + */ + $log.debug.logs = [] }; /** * @ngdoc method * @name ngMock.$log#assertEmpty @@ -386,11 +431,11 @@ * @description * Assert that the all of the logging methods have no logged messages. If messages present, an exception is thrown. */ $log.assertEmpty = function() { var errors = []; - angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) { + angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) { angular.forEach($log[logLevel].logs, function(log) { angular.forEach(log, function (logItem) { errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || '')); }); }); @@ -478,10 +523,11 @@ * newYearInBratislava.getFullYear() => 2010; * newYearInBratislava.getMonth() => 0; * newYearInBratislava.getDate() => 1; * newYearInBratislava.getHours() => 0; * newYearInBratislava.getMinutes() => 0; + * newYearInBratislava.getSeconds() => 0; * </pre> * */ angular.mock.TzDate = function (offset, timestamp) { var self = new Date(0); @@ -534,10 +580,14 @@ self.getSeconds = function() { return self.date.getSeconds(); }; + self.getMilliseconds = function() { + return self.date.getMilliseconds(); + }; + self.getTimezoneOffset = function() { return offset * 60; }; self.getUTCFullYear = function() { @@ -584,11 +634,11 @@ padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z' } } //hide all methods not implemented in this mock that the Date prototype exposes - var unimplementedMethods = ['getMilliseconds', 'getUTCDay', + var unimplementedMethods = ['getUTCDay', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; @@ -604,11 +654,48 @@ //make "tzDateInstance instanceof Date" return true angular.mock.TzDate.prototype = Date.prototype; })(); +angular.mock.animate = angular.module('mock.animate', ['ng']) + .config(['$provide', function($provide) { + + $provide.decorator('$animate', function($delegate) { + var animate = { + queue : [], + enabled : $delegate.enabled, + flushNext : function(name) { + var tick = animate.queue.shift(); + expect(tick.method).toBe(name); + tick.fn(); + return tick; + } + }; + + forEach(['enter','leave','move','addClass','removeClass'], function(method) { + animate[method] = function() { + var params = arguments; + animate.queue.push({ + method : method, + params : params, + element : angular.isElement(params[0]) && params[0], + parent : angular.isElement(params[1]) && params[1], + after : angular.isElement(params[2]) && params[2], + fn : function() { + $delegate[method].apply($delegate, params); + } + }); + }; + }); + + return animate; + }); + + }]); + + /** * @ngdoc function * @name angular.mock.dump * @description * @@ -644,10 +731,11 @@ if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) { out = serializeScope(object); } else if (object instanceof Error) { out = object.stack || ('' + object.name + ': ' + object.message); } else { + // TODO(i): this prevents methods to be logged, we should have a better way to serialize objects out = angular.toJson(object, true); } } else { out = String(object); } @@ -868,11 +956,11 @@ }); }); </pre> */ angular.mock.$HttpBackendProvider = function() { - this.$get = [createHttpBackendMock]; + this.$get = ['$rootScope', createHttpBackendMock]; }; /** * General factory function for $httpBackend mock. * Returns instance for unit testing (when no arguments specified): @@ -885,11 +973,11 @@ * * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified) * @param {Object=} $browser Auto-flushing enabled if specified * @return {Object} Instance of $httpBackend mock */ -function createHttpBackendMock($delegate, $browser) { +function createHttpBackendMock($rootScope, $delegate, $browser) { var definitions = [], expectations = [], responses = [], responsesPush = angular.bind(responses, responses.push); @@ -902,64 +990,77 @@ : [200, status, data]; }; } // TODO(vojta): change params to: method, url, data, headers, callback - function $httpBackend(method, url, data, callback, headers) { + function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) { var xhr = new MockXhr(), expectation = expectations[0], wasExpected = false; function prettyPrint(data) { return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) ? data : angular.toJson(data); } + function wrapResponse(wrapped) { + if (!$browser && timeout && timeout.then) timeout.then(handleTimeout); + + return handleResponse; + + function handleResponse() { + var response = wrapped.response(method, url, data, headers); + xhr.$$respHeaders = response[2]; + callback(response[0], response[1], xhr.getAllResponseHeaders()); + } + + function handleTimeout() { + for (var i = 0, ii = responses.length; i < ii; i++) { + if (responses[i] === handleResponse) { + responses.splice(i, 1); + callback(-1, undefined, ''); + break; + } + } + } + } + if (expectation && expectation.match(method, url)) { if (!expectation.matchData(data)) - throw Error('Expected ' + expectation + ' with different data\n' + + throw new Error('Expected ' + expectation + ' with different data\n' + 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); if (!expectation.matchHeaders(headers)) - throw Error('Expected ' + expectation + ' with different headers\n' + - 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + - prettyPrint(headers)); + throw new Error('Expected ' + expectation + ' with different headers\n' + + 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + prettyPrint(headers)); expectations.shift(); if (expectation.response) { - responses.push(function() { - var response = expectation.response(method, url, data, headers); - xhr.$$respHeaders = response[2]; - callback(response[0], response[1], xhr.getAllResponseHeaders()); - }); + responses.push(wrapResponse(expectation)); return; } wasExpected = true; } var i = -1, definition; while ((definition = definitions[++i])) { if (definition.match(method, url, data, headers || {})) { if (definition.response) { // if $browser specified, we do auto flush all requests - ($browser ? $browser.defer : responsesPush)(function() { - var response = definition.response(method, url, data, headers); - xhr.$$respHeaders = response[2]; - callback(response[0], response[1], xhr.getAllResponseHeaders()); - }); + ($browser ? $browser.defer : responsesPush)(wrapResponse(definition)); } else if (definition.passThrough) { - $delegate(method, url, data, callback, headers); + $delegate(method, url, data, callback, headers, timeout, withCredentials); } else throw Error('No response defined !'); return; } } throw wasExpected ? - Error('No response defined !') : - Error('Unexpected request: ' + method + ' ' + url + '\n' + - (expectation ? 'Expected ' + expectation : 'No more request expected')); + new Error('No response defined !') : + new Error('Unexpected request: ' + method + ' ' + url + '\n' + + (expectation ? 'Expected ' + expectation : 'No more request expected')); } /** * @ngdoc method * @name ngMock.$httpBackend#when @@ -967,11 +1068,12 @@ * @description * Creates a new backend definition. * * @param {string} method HTTP method. * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. + * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives + * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header * object and returns true if the headers match the current definition. * @returns {requestHandler} Returns an object with `respond` method that control how a matched * request is handled. * @@ -1043,11 +1145,12 @@ * @methodOf ngMock.$httpBackend * @description * Creates a new backend definition for POST requests. For more info see `when()`. * * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. + * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives + * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers. * @returns {requestHandler} Returns an object with `respond` method that control how a matched * request is handled. */ @@ -1057,11 +1160,12 @@ * @methodOf ngMock.$httpBackend * @description * Creates a new backend definition for PUT requests. For more info see `when()`. * * @param {string|RegExp} url HTTP url. - * @param {(string|RegExp)=} data HTTP request body. + * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives + * data string and returns true if the data is as expected. * @param {(Object|function(Object))=} headers HTTP headers. * @returns {requestHandler} Returns an object with `respond` method that control how a matched * request is handled. */ @@ -1223,10 +1327,11 @@ * @param {number=} count Number of responses to flush (in the order they arrived). If undefined, * all pending requests will be flushed. If there are no pending requests when the flush method * is called an exception is thrown (as this typically a sign of programming error). */ $httpBackend.flush = function(count) { + $rootScope.$digest(); if (!responses.length) throw Error('No pending request to flush !'); if (angular.isDefined(count)) { while (count--) { if (!responses.length) throw Error('No more pending request to flush !'); @@ -1255,12 +1360,13 @@ * <pre> * afterEach($httpBackend.verifyNoOutstandingExpectation); * </pre> */ $httpBackend.verifyNoOutstandingExpectation = function() { + $rootScope.$digest(); if (expectations.length) { - throw Error('Unsatisfied requests: ' + expectations.join(', ')); + throw new Error('Unsatisfied requests: ' + expectations.join(', ')); } }; /** @@ -1342,10 +1448,11 @@ }; this.matchData = function(d) { if (angular.isUndefined(data)) return true; if (data && angular.isFunction(data.test)) return data.test(d); + if (data && angular.isFunction(data)) return data(d); if (data && !angular.isString(data)) return angular.toJson(data) == d; return data == d; }; this.toString = function() { @@ -1407,22 +1514,70 @@ * @ngdoc function * @name ngMock.$timeout * @description * * This service is just a simple decorator for {@link ng.$timeout $timeout} service - * that adds a "flush" method. + * that adds a "flush" and "verifyNoPendingTasks" methods. */ -/** - * @ngdoc method - * @name ngMock.$timeout#flush - * @methodOf ngMock.$timeout - * @description - * - * Flushes the queue of pending tasks. - */ +angular.mock.$TimeoutDecorator = function($delegate, $browser) { + /** + * @ngdoc method + * @name ngMock.$timeout#flush + * @methodOf ngMock.$timeout + * @description + * + * Flushes the queue of pending tasks. + * + * @param {number=} delay maximum timeout amount to flush up until + */ + $delegate.flush = function(delay) { + $browser.defer.flush(delay); + }; + + /** + * @ngdoc method + * @name ngMock.$timeout#flushNext + * @methodOf ngMock.$timeout + * @description + * + * Flushes the next timeout in the queue and compares it to the provided delay + * + * @param {number=} expectedDelay the delay value that will be asserted against the delay of the next timeout function + */ + $delegate.flushNext = function(expectedDelay) { + $browser.defer.flushNext(expectedDelay); + }; + + /** + * @ngdoc method + * @name ngMock.$timeout#verifyNoPendingTasks + * @methodOf ngMock.$timeout + * @description + * + * Verifies that there are no pending tasks that need to be flushed. + */ + $delegate.verifyNoPendingTasks = function() { + if ($browser.deferredFns.length) { + throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' + + formatPendingTasksAsString($browser.deferredFns)); + } + }; + + function formatPendingTasksAsString(tasks) { + var result = []; + angular.forEach(tasks, function(task) { + result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}'); + }); + + return result.join(', '); + } + + return $delegate; +}; + /** * */ angular.mock.$RootElementProvider = function() { this.$get = function() { @@ -1443,19 +1598,13 @@ $exceptionHandler: angular.mock.$ExceptionHandlerProvider, $log: angular.mock.$LogProvider, $httpBackend: angular.mock.$HttpBackendProvider, $rootElement: angular.mock.$RootElementProvider }).config(function($provide) { - $provide.decorator('$timeout', function($delegate, $browser) { - $delegate.flush = function(delay) { - $browser.defer.flush(delay); - }; - return $delegate; - }); + $provide.decorator('$timeout', angular.mock.$TimeoutDecorator); }); - /** * @ngdoc overview * @name ngMockE2E * @description * @@ -1630,40 +1779,46 @@ * @param {string|RegExp} url HTTP url. * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that * control how a matched request is handled. */ angular.mock.e2e = {}; -angular.mock.e2e.$httpBackendDecorator = ['$delegate', '$browser', createHttpBackendMock]; +angular.mock.e2e.$httpBackendDecorator = ['$rootScope', '$delegate', '$browser', createHttpBackendMock]; angular.mock.clearDataCache = function() { var key, cache = angular.element.cache; for(key in cache) { if (cache.hasOwnProperty(key)) { var handle = cache[key].handle; - handle && angular.element(handle.elem).unbind(); + handle && angular.element(handle.elem).off(); delete cache[key]; } } }; -window.jasmine && (function(window) { +(window.jasmine || window.mocha) && (function(window) { + var currentSpec = null; + + beforeEach(function() { + currentSpec = this; + }); + afterEach(function() { - var spec = getCurrentSpec(); - var injector = spec.$injector; + var injector = currentSpec.$injector; - spec.$injector = null; - spec.$modules = null; + currentSpec.$injector = null; + currentSpec.$modules = null; + currentSpec = null; if (injector) { - injector.get('$rootElement').unbind(); + injector.get('$rootElement').off(); injector.get('$browser').pollFns.length = 0; } angular.mock.clearDataCache(); @@ -1678,26 +1833,20 @@ delete angular.callbacks[key]; }); angular.callbacks.counter = 0; }); - function getCurrentSpec() { - return jasmine.getEnv().currentSpec; - } - function isSpecRunning() { - var spec = getCurrentSpec(); - return spec && spec.queue.running; + return currentSpec && (window.mocha || currentSpec.queue.running); } /** * @ngdoc function * @name angular.mock.module * @description * * *NOTE*: This function is also published on window for easy access.<br> - * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}. * * This function registers a module configuration code. It collects the configuration information * which will be used when the injector is created by {@link angular.mock.inject inject}. * * See {@link angular.mock.inject inject} for usage example @@ -1709,15 +1858,14 @@ window.module = angular.mock.module = function() { var moduleFns = Array.prototype.slice.call(arguments, 0); return isSpecRunning() ? workFn() : workFn; ///////////////////// function workFn() { - var spec = getCurrentSpec(); - if (spec.$injector) { + if (currentSpec.$injector) { throw Error('Injector already created, can not register a module!'); } else { - var modules = spec.$modules || (spec.$modules = []); + var modules = currentSpec.$modules || (currentSpec.$modules = []); angular.forEach(moduleFns, function(module) { modules.push(module); }); } } @@ -1727,11 +1875,10 @@ * @ngdoc function * @name angular.mock.inject * @description * * *NOTE*: This function is also published on window for easy access.<br> - * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}. * * The inject function wraps a function into an injectable function. The inject() creates new * instance of {@link AUTO.$injector $injector} per test, which is then used for * resolving references. * @@ -1780,16 +1927,16 @@ var blockFns = Array.prototype.slice.call(arguments, 0); var errorForStack = new Error('Declaration Location'); return isSpecRunning() ? workFn() : workFn; ///////////////////// function workFn() { - var spec = getCurrentSpec(); - var modules = spec.$modules || []; + var modules = currentSpec.$modules || []; + modules.unshift('ngMock'); modules.unshift('ng'); - var injector = spec.$injector; + var injector = currentSpec.$injector; if (!injector) { - injector = spec.$injector = angular.injector(modules); + injector = currentSpec.$injector = angular.injector(modules); } for(var i = 0, ii = blockFns.length; i < ii; i++) { try { injector.invoke(blockFns[i] || angular.noop, this); } catch (e) {