'use strict'; describe('ngMock', function() { var noop = angular.noop; describe('TzDate', function() { function minutes(min) { return min*60*1000; } it('should look like a Date', function() { var date = new angular.mock.TzDate(0,0); expect(angular.isDate(date)).toBe(true); }); it('should take millis as constructor argument', function() { expect(new angular.mock.TzDate(0, 0).getTime()).toBe(0); expect(new angular.mock.TzDate(0, 1283555108000).getTime()).toBe(1283555108000); }); it('should take dateString as constructor argument', function() { expect(new angular.mock.TzDate(0, '1970-01-01T00:00:00.000Z').getTime()).toBe(0); expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.023Z').getTime()).toBe(1283555108023); }); it('should fake getLocalDateString method', function() { //0 in -3h var t0 = new angular.mock.TzDate(-3, 0); expect(t0.toLocaleDateString()).toMatch('1970'); //0 in +0h var t1 = new angular.mock.TzDate(0, 0); expect(t1.toLocaleDateString()).toMatch('1970'); //0 in +3h var t2 = new angular.mock.TzDate(3, 0); expect(t2.toLocaleDateString()).toMatch('1969'); }); it('should fake toISOString method', function() { var date = new angular.mock.TzDate(-1, '2009-10-09T01:02:03.027Z'); if (new Date().toISOString) { expect(date.toISOString()).toEqual('2009-10-09T01:02:03.027Z'); } else { expect(date.toISOString).toBeUndefined(); } }); it('should fake getHours method', function() { //0 in -3h var t0 = new angular.mock.TzDate(-3, 0); expect(t0.getHours()).toBe(3); //0 in +0h var t1 = new angular.mock.TzDate(0, 0); expect(t1.getHours()).toBe(0); //0 in +3h var t2 = new angular.mock.TzDate(3, 0); expect(t2.getHours()).toMatch(21); }); it('should fake getMinutes method', function() { //0:15 in -3h var t0 = new angular.mock.TzDate(-3, minutes(15)); expect(t0.getMinutes()).toBe(15); //0:15 in -3.25h var t0a = new angular.mock.TzDate(-3.25, minutes(15)); expect(t0a.getMinutes()).toBe(30); //0 in +0h var t1 = new angular.mock.TzDate(0, minutes(0)); expect(t1.getMinutes()).toBe(0); //0:15 in +0h var t1a = new angular.mock.TzDate(0, minutes(15)); expect(t1a.getMinutes()).toBe(15); //0:15 in +3h var t2 = new angular.mock.TzDate(3, minutes(15)); expect(t2.getMinutes()).toMatch(15); //0:15 in +3.25h var t2a = new angular.mock.TzDate(3.25, minutes(15)); expect(t2a.getMinutes()).toMatch(0); }); it('should fake getSeconds method', function() { //0 in -3h var t0 = new angular.mock.TzDate(-3, 0); expect(t0.getSeconds()).toBe(0); //0 in +0h var t1 = new angular.mock.TzDate(0, 0); expect(t1.getSeconds()).toBe(0); //0 in +3h var t2 = new angular.mock.TzDate(3, 0); expect(t2.getSeconds()).toMatch(0); }); it('should fake getMilliseconds method', function() { expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.003Z').getMilliseconds()).toBe(3); expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.023Z').getMilliseconds()).toBe(23); expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.123Z').getMilliseconds()).toBe(123); }); it('should create a date representing new year in Bratislava', function() { var newYearInBratislava = new angular.mock.TzDate(-1, '2009-12-31T23:00:00.000Z'); expect(newYearInBratislava.getTimezoneOffset()).toBe(-60); expect(newYearInBratislava.getFullYear()).toBe(2010); expect(newYearInBratislava.getMonth()).toBe(0); expect(newYearInBratislava.getDate()).toBe(1); expect(newYearInBratislava.getHours()).toBe(0); expect(newYearInBratislava.getMinutes()).toBe(0); expect(newYearInBratislava.getSeconds()).toBe(0); }); it('should delegate all the UTC methods to the original UTC Date object', function() { //from when created from string var date1 = new angular.mock.TzDate(-1, '2009-12-31T23:00:00.000Z'); expect(date1.getUTCFullYear()).toBe(2009); expect(date1.getUTCMonth()).toBe(11); expect(date1.getUTCDate()).toBe(31); expect(date1.getUTCHours()).toBe(23); expect(date1.getUTCMinutes()).toBe(0); expect(date1.getUTCSeconds()).toBe(0); //from when created from millis var date2 = new angular.mock.TzDate(-1, date1.getTime()); expect(date2.getUTCFullYear()).toBe(2009); expect(date2.getUTCMonth()).toBe(11); expect(date2.getUTCDate()).toBe(31); expect(date2.getUTCHours()).toBe(23); expect(date2.getUTCMinutes()).toBe(0); expect(date2.getUTCSeconds()).toBe(0); }); it('should throw error when no third param but toString called', function() { expect(function() { new angular.mock.TzDate(0,0).toString(); }). toThrow('Method \'toString\' is not implemented in the TzDate mock'); }); }); describe('$log', function() { angular.forEach([true, false], function(debugEnabled) { describe('debug ' + debugEnabled, function() { beforeEach(module(function($logProvider) { $logProvider.debugEnabled(debugEnabled); })); afterEach(inject(function($log){ $log.reset(); })); it("should skip debugging output if disabled (" + debugEnabled + ")", inject(function($log) { $log.log('fake log'); $log.info('fake log'); $log.warn('fake log'); $log.error('fake log'); $log.debug('fake log'); expect($log.log.logs).toContain(['fake log']); expect($log.info.logs).toContain(['fake log']); expect($log.warn.logs).toContain(['fake log']); expect($log.error.logs).toContain(['fake log']); if (debugEnabled) { expect($log.debug.logs).toContain(['fake log']); } else { expect($log.debug.logs).toEqual([]); } })); }); }); describe('debug enabled (default)', function() { var $log; beforeEach(inject(['$log', function(log) { $log = log; }])); afterEach(inject(function($log){ $log.reset(); })); it('should provide the log method', function() { expect(function() { $log.log(''); }).not.toThrow(); }); it('should provide the info method', function() { expect(function() { $log.info(''); }).not.toThrow(); }); it('should provide the warn method', function() { expect(function() { $log.warn(''); }).not.toThrow(); }); it('should provide the error method', function() { expect(function() { $log.error(''); }).not.toThrow(); }); it('should provide the debug method', function() { expect(function() { $log.debug(''); }).not.toThrow(); }); it('should store log messages', function() { $log.log('fake log'); expect($log.log.logs).toContain(['fake log']); }); it('should store info messages', function() { $log.info('fake log'); expect($log.info.logs).toContain(['fake log']); }); it('should store warn messages', function() { $log.warn('fake log'); expect($log.warn.logs).toContain(['fake log']); }); it('should store error messages', function() { $log.error('fake log'); expect($log.error.logs).toContain(['fake log']); }); it('should store debug messages', function() { $log.debug('fake log'); expect($log.debug.logs).toContain(['fake log']); }); it('should assertEmpty', function(){ try { $log.error(Error('MyError')); $log.warn(Error('MyWarn')); $log.info(Error('MyInfo')); $log.log(Error('MyLog')); $log.debug(Error('MyDebug')); $log.assertEmpty(); } catch (error) { error = error.message || error; expect(error).toMatch(/Error: MyError/m); expect(error).toMatch(/Error: MyWarn/m); expect(error).toMatch(/Error: MyInfo/m); expect(error).toMatch(/Error: MyLog/m); expect(error).toMatch(/Error: MyDebug/m); } finally { $log.reset(); } }); it('should reset state', function(){ $log.error(Error('MyError')); $log.warn(Error('MyWarn')); $log.info(Error('MyInfo')); $log.log(Error('MyLog')); $log.reset(); var passed = false; try { $log.assertEmpty(); // should not throw error! passed = true; } catch (e) { passed = e; } expect(passed).toBe(true); }); }); }); describe('$interval', function() { it('should run tasks repeatedly', inject(function($interval) { var counter = 0; $interval(function() { counter++; }, 1000); expect(counter).toBe(0); $interval.flush(1000); expect(counter).toBe(1); $interval.flush(1000); expect(counter).toBe(2); })); it('should call $apply after each task is executed', inject(function($interval, $rootScope) { var applySpy = spyOn($rootScope, '$apply').andCallThrough(); $interval(noop, 1000); expect(applySpy).not.toHaveBeenCalled(); $interval.flush(1000); expect(applySpy).toHaveBeenCalledOnce(); applySpy.reset(); $interval(noop, 1000); $interval(noop, 1000); $interval.flush(1000); expect(applySpy.callCount).toBe(3); })); it('should NOT call $apply if invokeApply is set to false', inject(function($interval, $rootScope) { var applySpy = spyOn($rootScope, '$apply').andCallThrough(); $interval(noop, 1000, 0, false); expect(applySpy).not.toHaveBeenCalled(); $interval.flush(2000); expect(applySpy).not.toHaveBeenCalled(); })); it('should allow you to specify the delay time', inject(function($interval) { var counter = 0; $interval(function() { counter++; }, 123); expect(counter).toBe(0); $interval.flush(122); expect(counter).toBe(0); $interval.flush(1); expect(counter).toBe(1); })); it('should allow you to specify a number of iterations', inject(function($interval) { var counter = 0; $interval(function() {counter++}, 1000, 2); $interval.flush(1000); expect(counter).toBe(1); $interval.flush(1000); expect(counter).toBe(2); $interval.flush(1000); expect(counter).toBe(2); })); describe('flush', function() { it('should move the clock forward by the specified time', inject(function($interval) { var counterA = 0; var counterB = 0; $interval(function() { counterA++; }, 100); $interval(function() { counterB++; }, 401); $interval.flush(200); expect(counterA).toEqual(2); $interval.flush(201); expect(counterA).toEqual(4); expect(counterB).toEqual(1); })); }); it('should return a promise which will be updated with the count on each iteration', inject(function($interval) { var log = [], promise = $interval(function() { log.push('tick'); }, 1000); promise.then(function(value) { log.push('promise success: ' + value); }, function(err) { log.push('promise error: ' + err); }, function(note) { log.push('promise update: ' + note); }); expect(log).toEqual([]); $interval.flush(1000); expect(log).toEqual(['tick', 'promise update: 0']); $interval.flush(1000); expect(log).toEqual(['tick', 'promise update: 0', 'tick', 'promise update: 1']); })); it('should return a promise which will be resolved after the specified number of iterations', inject(function($interval) { var log = [], promise = $interval(function() { log.push('tick'); }, 1000, 2); promise.then(function(value) { log.push('promise success: ' + value); }, function(err) { log.push('promise error: ' + err); }, function(note) { log.push('promise update: ' + note); }); expect(log).toEqual([]); $interval.flush(1000); expect(log).toEqual(['tick', 'promise update: 0']); $interval.flush(1000); expect(log).toEqual([ 'tick', 'promise update: 0', 'tick', 'promise update: 1', 'promise success: 2']); })); describe('exception handling', function() { beforeEach(module(function($exceptionHandlerProvider) { $exceptionHandlerProvider.mode('log'); })); it('should delegate exception to the $exceptionHandler service', inject( function($interval, $exceptionHandler) { $interval(function() { throw "Test Error"; }, 1000); expect($exceptionHandler.errors).toEqual([]); $interval.flush(1000); expect($exceptionHandler.errors).toEqual(["Test Error"]); $interval.flush(1000); expect($exceptionHandler.errors).toEqual(["Test Error", "Test Error"]); })); it('should call $apply even if an exception is thrown in callback', inject( function($interval, $rootScope) { var applySpy = spyOn($rootScope, '$apply').andCallThrough(); $interval(function() { throw "Test Error"; }, 1000); expect(applySpy).not.toHaveBeenCalled(); $interval.flush(1000); expect(applySpy).toHaveBeenCalled(); })); it('should still update the interval promise when an exception is thrown', inject(function($interval) { var log = [], promise = $interval(function() { throw "Some Error"; }, 1000); promise.then(function(value) { log.push('promise success: ' + value); }, function(err) { log.push('promise error: ' + err); }, function(note) { log.push('promise update: ' + note); }); $interval.flush(1000); expect(log).toEqual(['promise update: 0']); })); }); describe('cancel', function() { it('should cancel tasks', inject(function($interval) { var task1 = jasmine.createSpy('task1', 1000), task2 = jasmine.createSpy('task2', 1000), task3 = jasmine.createSpy('task3', 1000), promise1, promise3; promise1 = $interval(task1, 200); $interval(task2, 1000); promise3 = $interval(task3, 333); $interval.cancel(promise3); $interval.cancel(promise1); $interval.flush(1000); expect(task1).not.toHaveBeenCalled(); expect(task2).toHaveBeenCalledOnce(); expect(task3).not.toHaveBeenCalled(); })); it('should cancel the promise', inject(function($interval, $rootScope) { var promise = $interval(noop, 1000), log = []; promise.then(function(value) { log.push('promise success: ' + value); }, function(err) { log.push('promise error: ' + err); }, function(note) { log.push('promise update: ' + note); }); expect(log).toEqual([]); $interval.flush(1000); $interval.cancel(promise); $interval.flush(1000); $rootScope.$apply(); // For resolving the promise - // necessary since q uses $rootScope.evalAsync. expect(log).toEqual(['promise update: 0', 'promise error: canceled']); })); it('should return true if a task was successfully canceled', inject(function($interval) { var task1 = jasmine.createSpy('task1'), task2 = jasmine.createSpy('task2'), promise1, promise2; promise1 = $interval(task1, 1000, 1); $interval.flush(1000); promise2 = $interval(task2, 1000, 1); expect($interval.cancel(promise1)).toBe(false); expect($interval.cancel(promise2)).toBe(true); })); it('should not throw a runtime exception when given an undefined promise', inject(function($interval) { var task1 = jasmine.createSpy('task1'), promise1; promise1 = $interval(task1, 1000, 1); expect($interval.cancel()).toBe(false); })); }); }); describe('defer', function() { var browser, log; beforeEach(inject(function($browser) { browser = $browser; log = ''; })); function logFn(text){ return function() { log += text +';'; }; } it('should flush', function() { browser.defer(logFn('A')); expect(log).toEqual(''); browser.defer.flush(); expect(log).toEqual('A;'); }); it('should flush delayed', function() { browser.defer(logFn('A')); browser.defer(logFn('B'), 10); browser.defer(logFn('C'), 20); expect(log).toEqual(''); expect(browser.defer.now).toEqual(0); browser.defer.flush(0); expect(log).toEqual('A;'); browser.defer.flush(); expect(log).toEqual('A;B;C;'); }); it('should defer and flush over time', function() { browser.defer(logFn('A'), 1); browser.defer(logFn('B'), 2); browser.defer(logFn('C'), 3); browser.defer.flush(0); expect(browser.defer.now).toEqual(0); expect(log).toEqual(''); browser.defer.flush(1); expect(browser.defer.now).toEqual(1); expect(log).toEqual('A;'); browser.defer.flush(2); expect(browser.defer.now).toEqual(3); expect(log).toEqual('A;B;C;'); }); it('should throw an exception if there is nothing to be flushed', function() { expect(function() {browser.defer.flush();}).toThrow('No deferred tasks to be flushed'); }); }); describe('$exceptionHandler', function() { it('should rethrow exceptions', inject(function($exceptionHandler) { expect(function() { $exceptionHandler('myException'); }).toThrow('myException'); })); it('should log exceptions', module(function($exceptionHandlerProvider){ $exceptionHandlerProvider.mode('log'); var $exceptionHandler = $exceptionHandlerProvider.$get(); $exceptionHandler('MyError'); expect($exceptionHandler.errors).toEqual(['MyError']); $exceptionHandler('MyError', 'comment'); expect($exceptionHandler.errors[1]).toEqual(['MyError', 'comment']); })); it('should throw on wrong argument', module(function($exceptionHandlerProvider) { expect(function() { $exceptionHandlerProvider.mode('XXX'); }).toThrow("Unknown mode 'XXX', only 'log'/'rethrow' modes are allowed!"); })); }); describe('$timeout', function() { it('should expose flush method that will flush the pending queue of tasks', inject( function($timeout) { var logger = [], logFn = function(msg) { return function() { logger.push(msg) }}; $timeout(logFn('t1')); $timeout(logFn('t2'), 200); $timeout(logFn('t3')); expect(logger).toEqual([]); $timeout.flush(); expect(logger).toEqual(['t1', 't3', 't2']); })); it('should throw an exception when not flushed', inject(function($timeout){ $timeout(noop); var expectedError = 'Deferred tasks to flush (1): {id: 0, time: 0}'; expect(function() {$timeout.verifyNoPendingTasks();}).toThrow(expectedError); })); it('should do nothing when all tasks have been flushed', inject(function($timeout) { $timeout(noop); $timeout.flush(); expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow(); })); it('should check against the delay if provided within timeout', inject(function($timeout) { $timeout(noop, 100); $timeout.flush(100); expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow(); $timeout(noop, 1000); $timeout.flush(100); expect(function() {$timeout.verifyNoPendingTasks();}).toThrow(); $timeout.flush(900); expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow(); })); it('should assert against the delay value', inject(function($timeout) { var count = 0; var iterate = function() { count++; }; $timeout(iterate, 100); $timeout(iterate, 123); $timeout.flush(100); expect(count).toBe(1); $timeout.flush(123); expect(count).toBe(2); })); }); describe('angular.mock.dump', function(){ var d = angular.mock.dump; it('should serialize primitive types', function(){ expect(d(undefined)).toEqual('undefined'); expect(d(1)).toEqual('1'); expect(d(null)).toEqual('null'); expect(d('abc')).toEqual('abc'); }); it('should serialize element', function(){ var e = angular.element('
abc
xyz'); expect(d(e).toLowerCase()).toEqual('
abc
xyz'); expect(d(e[0]).toLowerCase()).toEqual('
abc
'); }); it('should serialize scope', inject(function($rootScope){ $rootScope.obj = {abc:'123'}; expect(d($rootScope)).toMatch(/Scope\(.*\): \{/); expect(d($rootScope)).toMatch(/{"abc":"123"}/); })); it('should serialize scope that has overridden "hasOwnProperty"', inject(function($rootScope, $sniffer){ // MS IE8 just doesn't work for this kind of thing, since "for ... in" doesn't return // things like hasOwnProperty even if it is explicitly defined on the actual object! if ($sniffer.msie<=8) return; $rootScope.hasOwnProperty = 'X'; expect(d($rootScope)).toMatch(/Scope\(.*\): \{/); expect(d($rootScope)).toMatch(/hasOwnProperty: "X"/); })); }); describe('angular.mock.clearDataCache', function() { function keys(obj) { var keys = []; for(var key in obj) { if (obj.hasOwnProperty(key)) keys.push(key); } return keys.sort(); } function browserTrigger(element, eventType) { element = element[0]; if (document.createEvent) { var event = document.createEvent('MouseEvents'); event.initMouseEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element); element.dispatchEvent(event); } else { element.fireEvent('on' + eventType); } } it('should remove data', function() { expect(angular.element.cache).toEqual({}); var div = angular.element('
'); div.data('name', 'angular'); expect(keys(angular.element.cache)).not.toEqual([]); angular.mock.clearDataCache(); expect(keys(angular.element.cache)).toEqual([]); }); it('should deregister event handlers', function() { expect(keys(angular.element.cache)).toEqual([]); var log = ''; var div = angular.element('
'); // crazy IE9 requires div to be connected to render DOM for click event to work // mousemove works even when not connected. This is a heisen-bug since stepping // through the code makes the test pass. Viva IE!!! angular.element(document.body).append(div) div.on('click', function() { log += 'click1;'}); div.on('click', function() { log += 'click2;'}); div.on('mousemove', function() { log += 'mousemove;'}); browserTrigger(div, 'click'); browserTrigger(div, 'mousemove'); expect(log).toEqual('click1;click2;mousemove;'); log = ''; angular.mock.clearDataCache(); browserTrigger(div, 'click'); browserTrigger(div, 'mousemove'); expect(log).toEqual(''); expect(keys(angular.element.cache)).toEqual([]); div.remove(); }); }); describe('jasmine module and inject', function(){ var log; beforeEach(function(){ log = ''; }); describe('module', function() { describe('object literal format', function() { var mock = { log: 'module' }; beforeEach(function() { module({ 'service': mock, 'other': { some: 'replacement'} }, 'ngResource', function ($provide) { $provide.value('example', 'win'); } ); }); it('should inject the mocked module', function() { inject(function(service) { expect(service).toEqual(mock); }); }); it('should support multiple key value pairs', function() { inject(function(service, other) { expect(other.some).toEqual('replacement'); expect(service).toEqual(mock); }); }); it('should integrate with string and function', function() { inject(function(service, $resource, example) { expect(service).toEqual(mock); expect($resource).toBeDefined(); expect(example).toEqual('win'); }); }); }); describe('in DSL', function() { it('should load module', module(function() { log += 'module'; })); afterEach(function() { inject(); expect(log).toEqual('module'); }); }); describe('inline in test', function() { it('should load module', function() { module(function() { log += 'module'; }); inject(); }); afterEach(function() { expect(log).toEqual('module'); }); }); }); describe('inject', function() { describe('in DSL', function() { it('should load module', inject(function() { log += 'inject'; })); afterEach(function() { expect(log).toEqual('inject'); }); }); describe('inline in test', function() { it('should load module', function() { inject(function() { log += 'inject'; }); }); afterEach(function() { expect(log).toEqual('inject'); }); }); describe('module with inject', function() { beforeEach(module(function(){ log += 'module;'; })); it('should inject', inject(function() { log += 'inject;'; })); afterEach(function() { expect(log).toEqual('module;inject;') }); }); describe('this', function() { it('should set `this` to be the jasmine context', inject(function() { expect(this instanceof jasmine.Spec).toBe(true); })); it('should set `this` to be the jasmine context when inlined in a test', function() { var tested = false; inject(function() { expect(this instanceof jasmine.Spec).toBe(true); tested = true; }); expect(tested).toBe(true); }); }); // We don't run the following tests on IE8. // IE8 throws "Object does not support this property or method." error, // when thrown from a function defined on window (which `inject` is). it('should not change thrown Errors', inject(function($sniffer) { if ($sniffer.msie <= 8) return; expect(function() { inject(function() { throw new Error('test message'); }); }).toThrow('test message'); })); it('should not change thrown strings', inject(function($sniffer) { if ($sniffer.msie <= 8) return; expect(function() { inject(function() { throw 'test message'; }); }).toThrow('test message'); })); }); }); describe('$httpBackend', function() { var hb, callback, realBackendSpy; beforeEach(inject(function($httpBackend) { callback = jasmine.createSpy('callback'); hb = $httpBackend; })); it('should respond with first matched definition', function() { hb.when('GET', '/url1').respond(200, 'content', {}); hb.when('GET', '/url1').respond(201, 'another', {}); callback.andCallFake(function(status, response) { expect(status).toBe(200); expect(response).toBe('content'); }); hb('GET', '/url1', null, callback); expect(callback).not.toHaveBeenCalled(); hb.flush(); expect(callback).toHaveBeenCalledOnce(); }); it('should respond with a copy of the mock data', function() { var mockObject = {a: 'b'}; hb.when('GET', '/url1').respond(200, mockObject, {}); callback.andCallFake(function(status, response) { expect(status).toBe(200); expect(response).toEqual({a: 'b'}); expect(response).not.toBe(mockObject); response.a = 'c'; }); hb('GET', '/url1', null, callback); hb.flush(); expect(callback).toHaveBeenCalledOnce(); // Fire it again and verify that the returned mock data has not been // modified. callback.reset(); hb('GET', '/url1', null, callback); hb.flush(); expect(callback).toHaveBeenCalledOnce(); expect(mockObject).toEqual({a: 'b'}); }); it('should throw error when unexpected request', function() { hb.when('GET', '/url1').respond(200, 'content'); expect(function() { hb('GET', '/xxx'); }).toThrow('Unexpected request: GET /xxx\nNo more request expected'); }); it('should match headers if specified', function() { hb.when('GET', '/url', null, {'X': 'val1'}).respond(201, 'content1'); hb.when('GET', '/url', null, {'X': 'val2'}).respond(202, 'content2'); hb.when('GET', '/url').respond(203, 'content3'); hb('GET', '/url', null, function(status, response) { expect(status).toBe(203); expect(response).toBe('content3'); }); hb('GET', '/url', null, function(status, response) { expect(status).toBe(201); expect(response).toBe('content1'); }, {'X': 'val1'}); hb('GET', '/url', null, function(status, response) { expect(status).toBe(202); expect(response).toBe('content2'); }, {'X': 'val2'}); hb.flush(); }); it('should match data if specified', function() { hb.when('GET', '/a/b', '{a: true}').respond(201, 'content1'); hb.when('GET', '/a/b').respond(202, 'content2'); hb('GET', '/a/b', '{a: true}', function(status, response) { expect(status).toBe(201); expect(response).toBe('content1'); }); hb('GET', '/a/b', null, function(status, response) { expect(status).toBe(202); expect(response).toBe('content2'); }); hb.flush(); }); it('should match data object if specified', function() { hb.when('GET', '/a/b', {a: 1, b: 2}).respond(201, 'content1'); hb.when('GET', '/a/b').respond(202, 'content2'); hb('GET', '/a/b', '{"a":1,"b":2}', function(status, response) { expect(status).toBe(201); expect(response).toBe('content1'); }); hb('GET', '/a/b', '{"b":2,"a":1}', function(status, response) { expect(status).toBe(201); expect(response).toBe('content1'); }); hb('GET', '/a/b', null, function(status, response) { expect(status).toBe(202); expect(response).toBe('content2'); }); hb.flush(); }); it('should match only method', function() { hb.when('GET').respond(202, 'c'); callback.andCallFake(function(status, response) { expect(status).toBe(202); expect(response).toBe('c'); }); hb('GET', '/some', null, callback, {}); hb('GET', '/another', null, callback, {'X-Fake': 'Header'}); hb('GET', '/third', 'some-data', callback, {}); hb.flush(); expect(callback).toHaveBeenCalled(); }); it('should preserve the order of requests', function() { hb.when('GET', '/url1').respond(200, 'first'); hb.when('GET', '/url2').respond(201, 'second'); hb('GET', '/url2', null, callback); hb('GET', '/url1', null, callback); hb.flush(); expect(callback.callCount).toBe(2); expect(callback.argsForCall[0]).toEqual([201, 'second', '']); expect(callback.argsForCall[1]).toEqual([200, 'first', '']); }); describe('respond()', function() { it('should take values', function() { hb.expect('GET', '/url1').respond(200, 'first', {'header': 'val'}); hb('GET', '/url1', undefined, callback); hb.flush(); expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val'); }); it('should take function', function() { hb.expect('GET', '/some').respond(function(m, u, d, h) { return [301, m + u + ';' + d + ';a=' + h.a, {'Connection': 'keep-alive'}]; }); hb('GET', '/some', 'data', callback, {a: 'b'}); hb.flush(); expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some;data;a=b', 'Connection: keep-alive'); }); it('should default status code to 200', function() { callback.andCallFake(function(status, response) { expect(status).toBe(200); expect(response).toBe('some-data'); }); hb.expect('GET', '/url1').respond('some-data'); hb.expect('GET', '/url2').respond('some-data', {'X-Header': 'true'}); hb('GET', '/url1', null, callback); hb('GET', '/url2', null, callback); hb.flush(); expect(callback).toHaveBeenCalled(); expect(callback.callCount).toBe(2); }); it('should default response headers to ""', function() { hb.expect('GET', '/url1').respond(200, 'first'); hb.expect('GET', '/url2').respond('second'); hb('GET', '/url1', null, callback); hb('GET', '/url2', null, callback); hb.flush(); expect(callback.callCount).toBe(2); expect(callback.argsForCall[0]).toEqual([200, 'first', '']); expect(callback.argsForCall[1]).toEqual([200, 'second', '']); }); }); describe('expect()', function() { it('should require specified order', function() { hb.expect('GET', '/url1').respond(200, ''); hb.expect('GET', '/url2').respond(200, ''); expect(function() { hb('GET', '/url2', null, noop, {}); }).toThrow('Unexpected request: GET /url2\nExpected GET /url1'); }); it('should have precedence over when()', function() { callback.andCallFake(function(status, response) { expect(status).toBe(300); expect(response).toBe('expect'); }); hb.when('GET', '/url').respond(200, 'when'); hb.expect('GET', '/url').respond(300, 'expect'); hb('GET', '/url', null, callback, {}); hb.flush(); expect(callback).toHaveBeenCalledOnce(); }); it ('should throw exception when only headers differs from expectation', function() { hb.when('GET').respond(200, '', {}); hb.expect('GET', '/match', undefined, {'Content-Type': 'application/json'}); expect(function() { hb('GET', '/match', null, noop, {}); }).toThrow('Expected GET /match with different headers\n' + 'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}'); }); it ('should throw exception when only data differs from expectation', function() { hb.when('GET').respond(200, '', {}); hb.expect('GET', '/match', 'some-data'); expect(function() { hb('GET', '/match', 'different', noop, {}); }).toThrow('Expected GET /match with different data\n' + 'EXPECTED: some-data\nGOT: different'); }); it ('should not throw an exception when parsed body is equal to expected body object', function() { hb.when('GET').respond(200, '', {}); hb.expect('GET', '/match', {a: 1, b: 2}); expect(function() { hb('GET', '/match', '{"a":1,"b":2}', noop, {}); }).not.toThrow(); hb.expect('GET', '/match', {a: 1, b: 2}); expect(function() { hb('GET', '/match', '{"b":2,"a":1}', noop, {}); }).not.toThrow(); }); it ('should throw exception when only parsed body differs from expected body object', function() { hb.when('GET').respond(200, '', {}); hb.expect('GET', '/match', {a: 1, b: 2}); expect(function() { hb('GET', '/match', '{"a":1,"b":3}', noop, {}); }).toThrow('Expected GET /match with different data\n' + 'EXPECTED: {"a":1,"b":2}\nGOT: {"a":1,"b":3}'); }); it("should use when's respond() when no expect() respond is defined", function() { callback.andCallFake(function(status, response) { expect(status).toBe(201); expect(response).toBe('data'); }); hb.when('GET', '/some').respond(201, 'data'); hb.expect('GET', '/some'); hb('GET', '/some', null, callback); hb.flush(); expect(callback).toHaveBeenCalled(); expect(function() { hb.verifyNoOutstandingExpectation(); }).not.toThrow(); }); }); describe('flush()', function() { it('flush() should flush requests fired during callbacks', function() { hb.when('GET').respond(200, ''); hb('GET', '/some', null, function() { hb('GET', '/other', null, callback); }); hb.flush(); expect(callback).toHaveBeenCalled(); }); it('should flush given number of pending requests', function() { hb.when('GET').respond(200, ''); hb('GET', '/some', null, callback); hb('GET', '/some', null, callback); hb('GET', '/some', null, callback); hb.flush(2); expect(callback).toHaveBeenCalled(); expect(callback.callCount).toBe(2); }); it('should throw exception when flushing more requests than pending', function() { hb.when('GET').respond(200, ''); hb('GET', '/url', null, callback); expect(function() {hb.flush(2);}).toThrow('No more pending request to flush !'); expect(callback).toHaveBeenCalledOnce(); }); it('should throw exception when no request to flush', function() { expect(function() {hb.flush();}).toThrow('No pending request to flush !'); hb.when('GET').respond(200, ''); hb('GET', '/some', null, callback); hb.flush(); expect(function() {hb.flush();}).toThrow('No pending request to flush !'); }); it('should throw exception if not all expectations satisfied', function() { hb.expect('GET', '/url1').respond(); hb.expect('GET', '/url2').respond(); hb('GET', '/url1', null, angular.noop); expect(function() {hb.flush();}).toThrow('Unsatisfied requests: GET /url2'); }); }); it('should abort requests when timeout promise resolves', function() { hb.expect('GET', '/url1').respond(200); var canceler, then = jasmine.createSpy('then').andCallFake(function(fn) { canceler = fn; }); hb('GET', '/url1', null, callback, null, {then: then}); expect(typeof canceler).toBe('function'); canceler(); // simulate promise resolution expect(callback).toHaveBeenCalledWith(-1, undefined, ''); hb.verifyNoOutstandingExpectation(); hb.verifyNoOutstandingRequest(); }); it('should throw an exception if no response defined', function() { hb.when('GET', '/test'); expect(function() { hb('GET', '/test', null, callback); }).toThrow('No response defined !'); }); it('should throw an exception if no response for exception and no definition', function() { hb.expect('GET', '/url'); expect(function() { hb('GET', '/url', null, callback); }).toThrow('No response defined !'); }); it('should respond undefined when JSONP method', function() { hb.when('JSONP', '/url1').respond(200); hb.expect('JSONP', '/url2').respond(200); expect(hb('JSONP', '/url1')).toBeUndefined(); expect(hb('JSONP', '/url2')).toBeUndefined(); }); it('should not have passThrough method', function() { expect(hb.passThrough).toBeUndefined(); }); describe('verifyExpectations', function() { it('should throw exception if not all expectations were satisfied', function() { hb.expect('POST', '/u1', 'ddd').respond(201, '', {}); hb.expect('GET', '/u2').respond(200, '', {}); hb.expect('POST', '/u3').respond(201, '', {}); hb('POST', '/u1', 'ddd', noop, {}); expect(function() {hb.verifyNoOutstandingExpectation();}). toThrow('Unsatisfied requests: GET /u2, POST /u3'); }); it('should do nothing when no expectation', function() { hb.when('DELETE', '/some').respond(200, ''); expect(function() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); }); it('should do nothing when all expectations satisfied', function() { hb.expect('GET', '/u2').respond(200, '', {}); hb.expect('POST', '/u3').respond(201, '', {}); hb.when('DELETE', '/some').respond(200, ''); hb('GET', '/u2', noop); hb('POST', '/u3', noop); expect(function() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); }); }); describe('verifyRequests', function() { it('should throw exception if not all requests were flushed', function() { hb.when('GET').respond(200); hb('GET', '/some', null, noop, {}); expect(function() { hb.verifyNoOutstandingRequest(); }).toThrow('Unflushed requests: 1'); }); }); describe('resetExpectations', function() { it('should remove all expectations', function() { hb.expect('GET', '/u2').respond(200, '', {}); hb.expect('POST', '/u3').respond(201, '', {}); hb.resetExpectations(); expect(function() {hb.verifyNoOutstandingExpectation();}).not.toThrow(); }); it('should remove all pending responses', function() { var cancelledClb = jasmine.createSpy('cancelled'); hb.expect('GET', '/url').respond(200, ''); hb('GET', '/url', null, cancelledClb); hb.resetExpectations(); hb.expect('GET', '/url').respond(300, ''); hb('GET', '/url', null, callback, {}); hb.flush(); expect(callback).toHaveBeenCalledOnce(); expect(cancelledClb).not.toHaveBeenCalled(); }); it('should not remove definitions', function() { var cancelledClb = jasmine.createSpy('cancelled'); hb.when('GET', '/url').respond(200, 'success'); hb('GET', '/url', null, cancelledClb); hb.resetExpectations(); hb('GET', '/url', null, callback, {}); hb.flush(); expect(callback).toHaveBeenCalledOnce(); expect(cancelledClb).not.toHaveBeenCalled(); }); }); describe('expect/when shortcuts', function() { angular.forEach(['expect', 'when'], function(prefix) { angular.forEach(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'JSONP'], function(method) { var shortcut = prefix + method; it('should provide ' + shortcut + ' shortcut method', function() { hb[shortcut]('/foo').respond('bar'); hb(method, '/foo', undefined, callback); hb.flush(); expect(callback).toHaveBeenCalledOnceWith(200, 'bar', ''); }); }); }); }); describe('MockHttpExpectation', function() { it('should accept url as regexp', function() { var exp = new MockHttpExpectation('GET', /^\/x/); expect(exp.match('GET', '/x')).toBe(true); expect(exp.match('GET', '/xxx/x')).toBe(true); expect(exp.match('GET', 'x')).toBe(false); expect(exp.match('GET', 'a/x')).toBe(false); }); it('should accept url as function', function() { var urlValidator = function(url) { return url !== '/not-accepted'; }; var exp = new MockHttpExpectation('POST', urlValidator); expect(exp.match('POST', '/url')).toBe(true); expect(exp.match('POST', '/not-accepted')).toBe(false); }); it('should accept data as regexp', function() { var exp = new MockHttpExpectation('POST', '/url', /\{.*?\}/); expect(exp.match('POST', '/url', '{"a": "aa"}')).toBe(true); expect(exp.match('POST', '/url', '{"one": "two"}')).toBe(true); expect(exp.match('POST', '/url', '{"one"')).toBe(false); }); it('should accept data as function', function() { var dataValidator = function(data) { var json = angular.fromJson(data); return !!json.id && json.status === 'N'; }; var exp = new MockHttpExpectation('POST', '/url', dataValidator); expect(exp.matchData({})).toBe(false); expect(exp.match('POST', '/url', '{"id": "xxx", "status": "N"}')).toBe(true); expect(exp.match('POST', '/url', {"id": "xxx", "status": "N"})).toBe(true); }); it('should ignore data only if undefined (not null or false)', function() { var exp = new MockHttpExpectation('POST', '/url', null); expect(exp.matchData(null)).toBe(true); expect(exp.matchData('some-data')).toBe(false); exp = new MockHttpExpectation('POST', '/url', undefined); expect(exp.matchData(null)).toBe(true); expect(exp.matchData('some-data')).toBe(true); }); it('should accept headers as function', function() { var exp = new MockHttpExpectation('GET', '/url', undefined, function(h) { return h['Content-Type'] == 'application/json'; }); expect(exp.matchHeaders({})).toBe(false); expect(exp.matchHeaders({'Content-Type': 'application/json', 'X-Another': 'true'})).toBe(true); }); }); }); describe('$rootElement', function() { it('should create mock application root', inject(function($rootElement) { expect($rootElement.text()).toEqual(''); })); }); }); describe('ngMockE2E', function() { describe('$httpBackend', function() { var hb, realHttpBackend, callback; beforeEach(function() { module(function($provide) { callback = jasmine.createSpy('callback'); realHttpBackend = jasmine.createSpy('real $httpBackend'); $provide.value('$httpBackend', realHttpBackend); $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); }); inject(function($injector) { hb = $injector.get('$httpBackend'); }); }); describe('passThrough()', function() { it('should delegate requests to the real backend when passThrough is invoked', function() { hb.when('GET', /\/passThrough\/.*/).passThrough(); hb('GET', '/passThrough/23', null, callback, {}, null, true); expect(realHttpBackend).toHaveBeenCalledOnceWith( 'GET', '/passThrough/23', null, callback, {}, null, true); }); }); describe('autoflush', function() { it('should flush responses via $browser.defer', inject(function($browser) { hb.when('GET', '/foo').respond('bar'); hb('GET', '/foo', null, callback); expect(callback).not.toHaveBeenCalled(); $browser.defer.flush(); expect(callback).toHaveBeenCalledOnce(); })); }); }); });