From 529fa935c9a4e40710ea73db83996d6de904dcac Mon Sep 17 00:00:00 2001 From: Joe Fiorini Date: Fri, 20 Mar 2015 17:18:29 -0400 Subject: [PATCH] Extract library for tests --- awx/ui/static/js/controllers/Users.js | 2 +- awx/ui/tests/karma.conf.js | 28 +- awx/ui/tests/phantomjs-polyfill.js | 28 ++ awx/ui/tests/unit/describe-module.js | 274 ++++++++++++++++++ .../unit/directives/job-status-graph-test.js | 149 +++++----- awx/ui/tests/unit/rest-stub.js | 62 ++++ .../services/host-count-graph-data-test.js | 148 +++------- .../services/job-status-graph-data-test.js | 156 ++++------ 8 files changed, 547 insertions(+), 300 deletions(-) create mode 100644 awx/ui/tests/phantomjs-polyfill.js create mode 100644 awx/ui/tests/unit/describe-module.js create mode 100644 awx/ui/tests/unit/rest-stub.js diff --git a/awx/ui/static/js/controllers/Users.js b/awx/ui/static/js/controllers/Users.js index 8fc6934992..bea627584e 100644 --- a/awx/ui/static/js/controllers/Users.js +++ b/awx/ui/static/js/controllers/Users.js @@ -481,4 +481,4 @@ export function UsersEdit($scope, $rootScope, $compile, $location, $log, $routeP UsersEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'UserForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'GetBasePath', 'Prompt', 'CheckAccess', 'ResetForm', 'Wait', 'Stream' -]; \ No newline at end of file +]; diff --git a/awx/ui/tests/karma.conf.js b/awx/ui/tests/karma.conf.js index 2021c8170e..62321c9ba0 100644 --- a/awx/ui/tests/karma.conf.js +++ b/awx/ui/tests/karma.conf.js @@ -1,24 +1,48 @@ // Karma configuration // Generated on Mon Aug 04 2014 21:17:04 GMT-0400 (EDT) +var path = require('path'); + module.exports = function(config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '', + autoWatchBatchDelay: 2000, + // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha', 'chai', 'sinon-chai', 'chai-as-promised'], + + frameworks: + [ 'mocha', + 'chai', + 'sinon-chai', + 'chai-as-promised' + ], + + preprocessors: + { '../dist/**/*.html': ['ng-html2js'] + }, + // list of files / patterns to load in the browser files: [ + '../tests/phantomjs-polyfill.js', '../dist/tower.concat.js', '../static/lib/angular-mocks/angular-mocks.js', '../static/lib/ember-cli-test-loader/test-loader.js', '../dist/tests/**/*.js', - '../tests/unit.js' + '../tests/unit.js', + '../dist/partials/**/*.html', + '../dist/js/**/*.html' ], + ngHtml2JsPreprocessor: { + stripPrefix: path.join(process.cwd(), 'awx/ui/dist'), + prependPrefix: '/static', + moduleName: 'templates' + }, + // list of files to exclude exclude: [ diff --git a/awx/ui/tests/phantomjs-polyfill.js b/awx/ui/tests/phantomjs-polyfill.js new file mode 100644 index 0000000000..46c4204283 --- /dev/null +++ b/awx/ui/tests/phantomjs-polyfill.js @@ -0,0 +1,28 @@ +// Phantom.js is missing the standard Function.prototype.bind +// function. See https://code.google.com/p/phantomjs/issues/detail?id=522 +// for more details. +// +if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; +} diff --git a/awx/ui/tests/unit/describe-module.js b/awx/ui/tests/unit/describe-module.js new file mode 100644 index 0000000000..47b7845a74 --- /dev/null +++ b/awx/ui/tests/unit/describe-module.js @@ -0,0 +1,274 @@ +import RestStub from 'tests/unit/rest-stub'; + +var $provide; + +function wrapInjected(dslFn) { + // wrapInjected(before(inject(..., function() { + // })); + return function(fn) { + dslFn.apply(this, + [inject( + [ '$injector', + function($injector) { + var $compile = $injector.get('$compile'); + var $httpBackend = $injector.get('$httpBackend'); + var $rootScope = $injector.get('$rootScope'); + + return fn.apply(this, [$httpBackend, $compile, $rootScope]); + }.bind(this) + ])]); + }; +}; + +function TestModule(name, deps) { + + window.localStorage.setItem('zones', []); + return { + mockedProviders: {}, + registerPreHooks: function() { + + var self = this; + beforeEach("tower module", module('Tower')); + beforeEach(name + " module", module(name)); + beforeEach("templates module", module('templates')); + beforeEach("mock app setup", module(['$provide', function(_provide_) { + + var getBasePath = function(path) { + return '/' + path + '/'; + } + + $provide = _provide_; + $provide.value('LoadBasePaths', angular.noop); + $provide.value('GetBasePath', getBasePath); + + for (var name in self.mockedProviders) { + $provide.value(name, self.mockedProviders[name]); + } + + }])); + + wrapInjected(beforeEach)(function($httpBackend) { + + $httpBackend + .expectGET('/static/js/local_config.js') + .respond({}); + }); + }, + mockProvider: function(name, value) { + this.mockedProviders[name] = value; + }, + describe: function(name, describeFn) { + + describe(name, function() { + describeFn.apply(this); + }); + }, + registerPostHooks: function() { + afterEach(inject(['$httpBackend', function($httpBackend) { + $httpBackend.verifyNoOutstandingExpectation(); + $httpBackend.verifyNoOutstandingRequest(); + }])); + } + }; +}; + +function TestService(name) { + var restStub = new RestStub(); + + afterEach(function() { + restStub.reset(); + }); + + return { + withService: function(fn) { + beforeEach(name + " service", inject([name, function() { + var service = arguments[0]; + fn(service); + }])); + }, + restStub: restStub, + }; +}; + +// Note: if you need a compile step for your directive you +// must either: +// +// 1. Use a before/after compile hook, which also allows +// you to modify the scope before compiling +// 2. If you don't use a hook, call `registerCompile` +// prior to the first `it` in your tests +function TestDirective(name, deps) { + + return { name: name, + // Hooks that need to run after any hooks registered + // by the test + withScope: function(fn) { + var self = this; + beforeEach("capture outer $scope", inject(['$rootScope', function($rootScope) { + var $scope = self.$scope = self.$scope || $rootScope.$new(); + // `this` refers to mocha test suite + fn.apply(this, [$scope]); + }])); + }, + withIsolateScope: function(fn) { + var self = this; + beforeEach("capture isolate scope", inject(['$rootScope', function($rootScope) { + // `this` refers to mocha test suite + fn.apply(this, [self.$element.isolateScope()]); + }])); + }, + beforeCompile: function(fn) { + + var self = this; + + // Run before compile step by passing in the + // outer scope, allowing for modifications + // prior to compiling + self.withScope(fn); + + this.registerCompile(); + }, + afterCompile: function(fn) { + + var self = this; + + // Make sure compile step gets setup first + if (!this._compileRegistered) { + this.registerCompile(); + } + + // Then pre-apply the function with the outer scope + self.withScope(function($scope) { + // `this` refers to mocha test suite + fn = fn.bind(this, $scope); + }); + + // Finally, have it called by the isolate scope + // hook, which will pass in both the outer + // scope (since it was pre-applied) and the + // isolate scope (if one exists) + // + self.withIsolateScope(function($scope) { + // `this` refers to mocha test suite + fn.apply(this, [$scope]); + }); + + }, + registerCompile: function(deps) { + + var self = this; + + // Only setup compile step once + if (this._compileRegistered) { + return; + } + + beforeEach("compile directive element", + inject(['$compile', '$httpBackend', function($compile, $httpBackend) { + + self.$element = $compile(self.element)(self.$scope); + $(self.$element).appendTo('body'); + + self.$scope.$digest(); + + $httpBackend.flush(); + + }])); + + afterEach("cleanup directive element", function() { + self.$element.trigger('$destroy'); + self.$element.remove(); + }); + + this._compileRegistered = true; + + }, + withController: function(fn) { + var self = this; + beforeEach(function() { + self._ensureCompiled(); + fn(self.$element.controller(self.name)); + }); + }, + use: function(elem) { + this.element = angular.element(elem); + }, + provideTemplate: function(url, template) { + var $scope = this.$scope; + beforeEach("mock template endpoint", inject(['$httpBackend', function($httpBackend) { + $httpBackend + .whenGET(url) + .respond(template); + }])); + } + }; +} + +function ModuleDescriptor(name, deps) { + + var moduleTests = []; + var testModule = + Object.create(TestModule(name, deps)); + + var proto = + { mockProvider: function(name, value) { + testModule.mockProvider(name, value); + return this; + }, + testService: function(name, test) { + testModule.describe(name, function() { + var testService = Object.create(TestService(name)); + + testModule.mockProvider('Rest', testService.restStub); + testModule.mockProvider('$cookieStore', { get: angular.noop }); + testModule.registerPreHooks(); + + beforeEach("$q", inject(['$q', function($q) { + testService.restStub.$q = $q; + }])); + + test.apply(null, [testService, testService.restStub]); + }); + }, + testDirective: function(name, test) { + + testModule.describe(name, function(deps) { + var directiveDeps = _.clone(deps); + + var testDirective = + Object.create(TestDirective(name)); + + // Hand in testDirective object & injected + // dependencies to the test as separate arguments + // + var args = [testDirective].concat(_.values(directiveDeps)); + var testObj = + // Using Function#bind to create a new function + // with the arguments pre-applied (go search + // the web for "partial application" to know more) + // + { run: test.bind(null, testDirective, args), + name: name + }; + + testModule.registerPreHooks(); + + // testDirective.registerCompile(); + + testObj.run(); + + // testDirective.registerPostHooks(); + }); + } + }; + + return proto; +} + +export function describeModule(name) { + var descriptor = null + descriptor = Object.create(ModuleDescriptor(name)); + + + return descriptor; +}; diff --git a/awx/ui/tests/unit/directives/job-status-graph-test.js b/awx/ui/tests/unit/directives/job-status-graph-test.js index 573613e1c3..5f53d3681a 100644 --- a/awx/ui/tests/unit/directives/job-status-graph-test.js +++ b/awx/ui/tests/unit/directives/job-status-graph-test.js @@ -1,90 +1,73 @@ import Tower from 'tower/app'; +import {describeModule} from 'tests/unit/describe-module'; -describe('Job Status Graph Directive', function() { - var element, scope, httpBackend; +var resizeHandler = sinon.spy(); - var resizeHandler = sinon.spy(); +describeModule('DashboardGraphs') + .mockProvider('adjustGraphSize', resizeHandler) + .testDirective('jobStatusGraph', function(directive) { - beforeEach(module('Tower')); - beforeEach(module(['$provide', function($provide) { - $provide.value('LoadBasePaths', angular.noop); - $provide.value('adjustGraphSize', resizeHandler); - }])); + directive.provideTemplate( + '/static/partials/job_status_graph.html', + "
"); + + directive.use(''); + + directive.beforeCompile(function($scope) { + + // Takes jobs grouped by result (successful or failure + // Then looks at each array of arrays, where index 0 is the timestamp & index 1 is the count of jobs with that status + $scope.data = + { jobs: + { successful: [[1, 0], [2, 0], [3,0], [4,0], [5,0]], + failed: [[1,0],[2,0],[3,0],[4,0],[5,0]] + } + }; + + }); + + function filterDataSeries(key, data) { + return data.map(function(datum) { + return datum.values; + })[key]; + } + + it('uses successes & failures from scope', function() { + var chartContainer = d3.select(directive.$element.find('svg')[0]); + var lineData = chartContainer.datum(); + + var successfulSeries = filterDataSeries(0, lineData); + var failedSeries = filterDataSeries(1, lineData); + + expect(successfulSeries).to.eql( + [ {x: 1, y: 0, series: 0}, + {x: 2, y: 0, series: 0}, + {x: 3, y: 0, series: 0}, + {x: 4, y: 0, series: 0}, + {x: 5, y: 0, series: 0} + ]); + + expect(failedSeries).to.eql( + [ {x: 1, y: 0, series: 1}, + {x: 2, y: 0, series: 1}, + {x: 3, y: 0, series: 1}, + {x: 4, y: 0, series: 1}, + {x: 5, y: 0, series: 1} + ]); + + }); + + it('cleans up external bindings', function() { + directive.$element.trigger('$destroy'); + + resizeHandler.reset(); + + inject(['$window', function($window) { + angular.element($window).trigger('resize'); + }]); + + expect(resizeHandler).not.to.have.been.called; + }); - beforeEach(inject(['$rootScope', '$compile', '$httpBackend', function($rootScope, $compile, $httpBackend) { - httpBackend = $httpBackend; - $httpBackend.expectGET('/static/js/local_config.js').respond({ }); - - $httpBackend.whenGET('/static/partials/job_status_graph.html') - .respond("
"); - - scope = $rootScope.$new(); - - element = ''; - - // Takes jobs grouped by result (successful or failure - // Then looks at each array of arrays, where index 0 is the timestamp & index 1 is the count of jobs with that status - scope.data = - { jobs: - { successful: [[1, 0], [2, 0], [3,0], [4,0], [5,0]], - failed: [[1,0],[2,0],[3,0],[4,0],[5,0]] - } - }; - - element = $compile(element)(scope); - scope.$digest(); - - $httpBackend.flush(); - - }])); - - afterEach(function() { - element.trigger('$destroy'); - httpBackend.verifyNoOutstandingExpectation(); - httpBackend.verifyNoOutstandingRequest(); - }); - - function filterDataSeries(key, data) { - return data.map(function(datum) { - return datum.values; - })[key]; - } - - it('uses successes & failures from scope', function() { - var chartContainer = d3.select(element.find('svg')[0]); - var lineData = chartContainer.datum(); - - var successfulSeries = filterDataSeries(0, lineData); - var failedSeries = filterDataSeries(1, lineData); - - expect(successfulSeries).to.eql( - [ {x: 1, y: 0, series: 0}, - {x: 2, y: 0, series: 0}, - {x: 3, y: 0, series: 0}, - {x: 4, y: 0, series: 0}, - {x: 5, y: 0, series: 0}]); - - expect(failedSeries).to.eql( - [ {x: 1, y: 0, series: 1}, - {x: 2, y: 0, series: 1}, - {x: 3, y: 0, series: 1}, - {x: 4, y: 0, series: 1}, - {x: 5, y: 0, series: 1}]); - }); - - it('cleans up external bindings', function() { - element.trigger('$destroy'); - - resizeHandler.reset(); - - inject(['$window', function($window) { - angular.element($window).trigger('resize'); - }]); - - expect(resizeHandler).not.to.have.been.called; - }); - -}); - diff --git a/awx/ui/tests/unit/rest-stub.js b/awx/ui/tests/unit/rest-stub.js new file mode 100644 index 0000000000..be91f1afe7 --- /dev/null +++ b/awx/ui/tests/unit/rest-stub.js @@ -0,0 +1,62 @@ +function assertUrlDeferred(url, obj) { + if (angular.isUndefined(obj[url]) || + angular.isUndefined(obj[url].then) && + angular.isUndefined(obj[url].promise.then)) { + var urls = []; + + for (key in obj) { + if (/\//.test(key)) { + urls.push(key); + } + } + + var registered = urls.map(function(url) { + return "\t\"" + url + "\""; + }).join("\n"); + + throw "Could not find a thenable registered for url \"" + url + "\". Registered URLs include:\n\n" + registered + "\n\nPerhaps you typo'd the URL?\n" + } +} + +function RestStub() { +} + +RestStub.prototype = + { setUrl: function(url) { + this[url] = this.$q.defer(); + this.currentUrl = url; + }, + reset: function() { + delete this.deferred; + }, + get: function() { + // allow a single deferred on this in case we don't need URL + this.deferred = this[this.currentUrl]; + + return this.deferred.promise; + }, + succeedAt: function(url, value) { + assertUrlDeferred(url, this); + this[url].resolve(value); + }, + succeed: function(value) { + this.deferred.resolve(value); + }, + failAt: function(url, value) { + assertUrlDeferred(url, this); + this[url].reject(value); + }, + fail: function(value) { + this.deferred.reject(value); + }, + flush: function() { + window.setTimeout(function() { + inject(['$rootScope', function($rootScope) { + $rootScope.$apply(); + }], 1000); + }); + } + }; + + +export default RestStub; diff --git a/awx/ui/tests/unit/services/host-count-graph-data-test.js b/awx/ui/tests/unit/services/host-count-graph-data-test.js index 16cfa525ed..7769bf7a3f 100644 --- a/awx/ui/tests/unit/services/host-count-graph-data-test.js +++ b/awx/ui/tests/unit/services/host-count-graph-data-test.js @@ -1,132 +1,56 @@ -describe('Host Count Graph Data Service', function() { +import {describeModule} from 'tests/unit/describe-module'; - var q; +var processErrors = sinon.spy(); - var hostCountGraphData, httpBackend, rootScope, timeout; +describeModule('DashboardGraphs') + .mockProvider('ProcessErrors', processErrors) + .testService('hostCountGraphData', function(test, restStub) { - var processErrors = sinon.spy(); + var q; + var service; - var getBasePath = function(path) { - return '/' + path + '/'; - } + beforeEach(inject(['$q', function($q) { + q = $q; + }])); - function flushPromises() { - window.setTimeout(function() { - inject(['$rootScope', function($rootScope) { - $rootScope.$apply(); - }], 1000); + + test.withService(function(_service_) { + service = _service_; }); - } - function assertUrlDeferred(url, obj) { - if (angular.isUndefined(obj[url]) || - angular.isUndefined(obj[url].then) && - angular.isUndefined(obj[url].promise.then)) { - var urls = []; + it('returns a promise to be fulfilled when data comes in', function() { + var license = "license"; + var hostData = "hosts"; - for (key in obj) { - if (/\//.test(key)) { - urls.push(key); + var result = service.get(); + + restStub.succeedAt('/config/', { data: { + license_info: { + instance_count: license + } } - } + }); - var registered = urls.map(function(url) { - return "\t\"" + url + "\""; - }).join("\n"); + restStub.succeedAt('/dashboard/graphs/inventory/', { data: hostData }); - throw "Could not find a thenable registered for url \"" + url + "\". Registered URLs include:\n\n" + registered + "\n\nPerhaps you typo'd the URL?\n" - } - } + restStub.flush(); - var restStub = { - setUrl: function(url) { - restStub[url] = q.defer(); - restStub.currentUrl = url; - }, - reset: function() { - delete restStub.deferred; - }, - get: function() { - // allow a single deferred on restStub in case we don't need URL - restStub.deferred = restStub[restStub.currentUrl]; - - return restStub.deferred.promise; - }, - succeedAt: function(url, value) { - assertUrlDeferred(url, restStub); - restStub[url].resolve(value); - }, - succeed: function(value) { - restStub.deferred.resolve(value); - }, - failAt: function(url, value) { - assertUrlDeferred(url, restStub); - restStub[url].reject(value); - }, - fail: function(value) { - restStub.deferred.reject(value); - } - }; - - beforeEach(module("Tower")); - - beforeEach(module(['$provide', function($provide) { - - $provide.value("$cookieStore", { get: angular.noop }); - - $provide.value('ProcessErrors', processErrors); - $provide.value('Rest', restStub); - $provide.value('GetBasePath', getBasePath); - }])); - - afterEach(function() { - restStub.reset(); - }); - - beforeEach(inject(['hostCountGraphData', '$httpBackend', '$q', '$rootScope', '$timeout', function(_hostCountGraphData_, $httpBackend, $q, $rootScope, $timeout) { - hostCountGraphData = _hostCountGraphData_; - httpBackend = $httpBackend; - rootScope = $rootScope; - timeout = $timeout; - $httpBackend.expectGET('/static/js/local_config.js').respond({ - }); - q = $q; - }])); - - it('returns a promise to be fulfilled when data comes in', function() { - var license = "license"; - var hostData = "hosts"; - - var result = hostCountGraphData.get(); - - restStub.succeedAt('/config/', { data: { - license_info: { - instance_count: license - } - } + return expect(result).to.eventually.eql({ license: license, hosts: hostData });; }); - restStub.succeedAt('/dashboard/graphs/inventory/', { data: hostData }); + it('processes errors through error handler', function() { + var expected = { data: "blah", status: "bad" }; + var actual = service.get(); - flushPromises(); + restStub.failAt('/config/', expected); - return expect(result).to.eventually.eql({ license: license, hosts: hostData });; - }); + restStub.flush(); - it('processes errors through error handler', function() { - var expected = { data: "blah", status: "bad" }; - var actual = hostCountGraphData.get(); + return actual.catch(function() { + expect(processErrors).to + .have.been.calledWith(null, expected.data, expected.status); + }); - restStub.failAt('/config/', expected); - - flushPromises(); - - return actual.catch(function() { - expect(processErrors).to - .have.been.calledWith(null, expected.data, expected.status); + }); }); - }); - -}); - diff --git a/awx/ui/tests/unit/services/job-status-graph-data-test.js b/awx/ui/tests/unit/services/job-status-graph-data-test.js index dd10540aa0..9ec9e3f9be 100644 --- a/awx/ui/tests/unit/services/job-status-graph-data-test.js +++ b/awx/ui/tests/unit/services/job-status-graph-data-test.js @@ -1,124 +1,76 @@ -describe('Job Status Graph Data Service', function() { +import {describeModule} from 'tests/unit/describe-module'; - var q; +var processErrors = sinon.spy(); - var jobStatusGraphData, httpBackend, rootScope, timeout; +describeModule('DashboardGraphs') + .mockProvider('ProcessErrors', processErrors) + .testService('jobStatusGraphData', function(test, restStub) { + var q; + var service; - var jobStatusChange = { - $on: sinon.spy(), - }; + var jobStatusChange = { + $on: sinon.spy(), + }; - var processErrors = sinon.spy(); + beforeEach(inject(['$q', function($q) { + q = $q; + }])); - var getBasePath = function(path) { - return '/' + path + '/'; - } + test.withService(function(_service) { + service = _service; + }); - function flushPromises() { - window.setTimeout(function() { - inject(['$rootScope', function($rootScope) { - $rootScope.$apply(); - }]); - }); - } + it('returns a promise to be fulfilled when data comes in', function() { + var firstResult = "result"; - var restStub = { - setUrl: angular.noop, - reset: function() { - delete restStub.deferred; - }, - get: function() { - if (angular.isUndefined(restStub.deferred)) { - restStub.deferred = q.defer(); - } + var result = service.get('', ''); - return restStub.deferred.promise; - }, - succeed: function(value) { - restStub.deferred.resolve(value); - }, - fail: function(value) { - restStub.deferred.reject(value); - } - }; + restStub.succeed({ data: firstResult }); - beforeEach(module("Tower")); + restStub.flush(); - beforeEach(module(['$provide', function($provide) { + return expect(result).to.eventually.equal(firstResult);; + }); - $provide.value("$cookieStore", { get: angular.noop }); + it('processes errors through error handler', function() { + var expected = { data: "blah", status: "bad" }; + var actual = service.get().catch(function() { + return processErrors; + }); - $provide.value('ProcessErrors', processErrors); - $provide.value('Rest', restStub); - $provide.value('GetBasePath', getBasePath); - }])); + restStub.fail(expected); - afterEach(function() { - restStub.reset(); - }); + restStub.flush(); - beforeEach(inject(['jobStatusGraphData', '$httpBackend', '$q', '$rootScope', '$timeout', function(_jobStatusGraphData_, $httpBackend, $q, $rootScope, $timeout) { - jobStatusGraphData = _jobStatusGraphData_; - httpBackend = $httpBackend; - rootScope = $rootScope; - timeout = $timeout; - $httpBackend.expectGET('/static/js/local_config.js').respond({ - }); - q = $q; - }])); + return actual.catch(function() { + expect(processErrors).to + .have.been.calledWith(null, expected.data, expected.status); + }); - it('returns a promise to be fulfilled when data comes in', function() { - var firstResult = "result"; + }); - var result = jobStatusGraphData.get('', ''); + it('broadcasts event when data is received', function() { + var expected = "value"; + var result = q.defer(); + service.setupWatcher(); - restStub.succeed({ data: firstResult }); + inject(['$rootScope', function($rootScope) { + $rootScope.$on('DataReceived:JobStatusGraph', function(e, data) { + result.resolve(data); + }); + $rootScope.$emit('JobStatusChange'); + restStub.succeed({ data: expected }); + restStub.flush(); + }]); - flushPromises(); + return expect(result.promise).to.eventually.equal(expected); + }); - return expect(result).to.eventually.equal(firstResult);; - }); + it('requests data with given period and jobType', function() { + restStub.setUrl = sinon.spy(); - it('processes errors through error handler', function() { - var expected = { data: "blah", status: "bad" }; - var actual = jobStatusGraphData.get().catch(function() { - return processErrors; - }); - - restStub.fail(expected); - - flushPromises(); - - return actual.catch(function() { - expect(processErrors).to - .have.been.calledWith(null, expected.data, expected.status); - }); - - }); - - it('broadcasts event when data is received', function() { - var expected = "value"; - var result = q.defer(); - jobStatusGraphData.setupWatcher(); - - inject(['$rootScope', function($rootScope) { - $rootScope.$on('DataReceived:JobStatusGraph', function(e, data) { - result.resolve(data); - }); - $rootScope.$emit('JobStatusChange'); - restStub.succeed({ data: expected }); - flushPromises(); - }]); - - return expect(result.promise).to.eventually.equal(expected); - }); - - it('requests data with given period and jobType', function() { - restStub.setUrl = sinon.spy(); - - jobStatusGraphData.get('1', '2'); - - expect(restStub.setUrl).to.have.been.calledWith('/dashboard/graphs/jobs/?period=1&job_type=2'); - }); + service.get('1', '2'); + expect(restStub.setUrl).to.have.been.calledWith('/dashboard/graphs/jobs/?period=1&job_type=2'); + }); });