├── .editorconfig
├── .gitignore
├── .travis.yml
├── Gruntfile.js
├── README.md
├── bower.json
├── package.json
├── promise-tracker-http-interceptor.js
├── promise-tracker.js
└── test
├── karma.conf.js
└── unit
├── interceptor.spec.js
└── provider.spec.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | npm-debug.log
3 | .DS_Store
4 | demo
5 | bower_components
6 | node_modules
7 | dist
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: node_js
3 | node_js:
4 | - 0.10
5 | env:
6 |
7 | before_script:
8 | - export DISPLAY=:99.0
9 | - sh -e /etc/init.d/xvfb start
10 | - npm install -g grunt-cli bower
11 | - npm link
12 | - bower install
13 |
14 | script:
15 | - grunt
16 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 |
3 | require('load-grunt-tasks')(grunt);
4 |
5 | grunt.initConfig({
6 | dist: 'dist',
7 | pkgFile: 'bower.json',
8 | pkg: grunt.file.readJSON('bower.json'),
9 |
10 | watch: {
11 | scripts: {
12 | files: ['src/**/*.js', 'test/unit/**/*.js'],
13 | tasks: ['karma:watch:run']
14 | },
15 | gruntfile: {
16 | files: ['Gruntfile.js'],
17 | tasks: ['jshint']
18 | }
19 | },
20 |
21 | jshint: {
22 | all: ['Gruntfile.js', 'src/**/*.js'],
23 | options: {
24 | eqeqeq: true,
25 | globals: {
26 | angular: true
27 | }
28 | }
29 | },
30 |
31 | clean: ['demo/**/*'],
32 |
33 | karma: {
34 | watch: {
35 | configFile: 'test/karma.conf.js',
36 | background: true,
37 | },
38 | single: {
39 | configFile: 'test/karma.conf.js',
40 | singleRun: true,
41 | browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
42 | }
43 | },
44 |
45 | changelog: {
46 | options: {
47 | dest: 'CHANGELOG.md'
48 | }
49 | },
50 | });
51 |
52 | grunt.registerTask('dev', ['karma:watch', 'watch']);
53 |
54 | grunt.registerTask('default', ['jshint', 'test']);
55 | grunt.registerTask('test', ['karma:single']);
56 | };
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | angular-promise-tracker
2 | =======================
3 |
4 | > **Version**: 2.0
5 |
6 | (note to users using version 1.x: upgrading has *many* breaking changes, see [the CHANGELOG](https://github.com/ajoslin/angular-promise-tracker/tree/master/CHANGELOG.md).)
7 |
8 | [](https://travis-ci.org/ajoslin/angular-promise-tracker)
9 |
10 | Small, feature filled library used to easily add spinners or general promise/request tracking to your angular app.
11 |
12 | * [Quick Start](#quick-start)
13 | * [API Documentation](#api-documentation)
14 | * [Changes](https://github.com/ajoslin/angular-promise-tracker/tree/master/CHANGELOG.md)
15 | * [License](#license)
16 |
17 | ## Quick Start
18 |
19 | The basic idea: each time we add one or more promises to an instance of a `promiseTracker`, that instance's `active()` method will return true until all added promises are resolved. A common use case is showing some sort of loading spinner while some http requests are loading.
20 |
21 | [Play with this example on plunkr](http://plnkr.co/edit/PrO2ou9b1uANbeGoX6eB?p=preview)
22 |
23 | ```sh
24 | $ bower install angular-promise-tracker
25 | ```
26 | ```html
27 |
28 |
29 | Loading...
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | ```
41 | ```js
42 | angular.module('myApp', ['ajoslin.promise-tracker'])
43 | .controller('MainCtrl', function($scope, $http, $timeout, promiseTracker) {
44 | //Create a new tracker
45 | $scope.loadingTracker = promiseTracker();
46 |
47 | //use `addPromise` to add any old promise to our tracker
48 | $scope.delaySomething = function() {
49 | var promise = $timeout(function() {
50 | alert('Delayed something!');
51 | }, 1000);
52 | $scope.loadingTracker.addPromise(promise);
53 | };
54 |
55 | //use `tracker:` shortcut in $http config to link our http promise to a tracker
56 | //This shortcut is included in promise-tracker-http-interceptor.js
57 | $scope.fetchSomething = function(id) {
58 | return $http.get('/something', {
59 | tracker: $scope.loadingTracker
60 | }).then(function(response) {
61 | alert('Fetched something! ' + response.data);
62 | });
63 | };
64 | });
65 | ```
66 |
67 | ## API Documentation
68 |
69 | ### Service `promiseTracker`
70 |
71 | * **`tracker` promiseTracker([options])**
72 |
73 | Creates and returns a new promiseTracker.
74 |
75 | Options can be given as an object, with the following allowed values:
76 |
77 | - `activationDelay` `{Number}` - Number of milliseconds that an added promise needs to be pending before this tracker is active.
78 | * Usage example: You have some http calls that sometimes return too quickly for a loading spinner to look good. You only want to show the tracker if a promise is pending for over 500ms. You put `{activationDelay: 500}` in options.
79 | - `minDuration` `{Number}` - Minimum number of milliseconds that a tracker will stay active.
80 | * Usage example: You want a loading spinner to always show up for at least 750ms. You put `{minDuration: 750}` in options.
81 |
82 | Often you want a global promiseTracker (eg to show a loading screen); one easy way is to put the tracker on your $rootScope:
83 |
84 | ```js
85 | app.run(function($rootScope, promiseTracker) {
86 | $rootScope.loadingTracker = promiseTracker();
87 | });
88 | ```
89 |
90 | ### Instantiated promiseTracker
91 |
92 | Example: `var myTracker = promiseTracker({ activationDelay: 500, minDuration: 750 });`
93 |
94 | * **`boolean` tracker.active()**
95 |
96 | Returns whether this tracker is currently active. That is, whether any of the promises added to/created by this tracker are still pending. Note: if the `activationDelay` has not elapsed yet, this will return false.
97 |
98 | * **`boolean` tracker.tracking()**
99 |
100 | Returns whether this tracker is currently tracking a request. That is, whether any of the promises added to/created by this tracker are still pending. This method has no regard for `activationDelay`.
101 |
102 | * **`number` tracker.trackingCount()**
103 |
104 | The count of promises currently being tracked.
105 |
106 | * **`promise` tracker.addPromise(promise)**
107 |
108 | Add any arbitrary promise to tracker. `tracker.active()` will be true until `promise` is resolved or rejected.
109 |
110 | - `promise` `{object}` - Promise to add
111 |
112 | Usage Example:
113 |
114 | ```js
115 | var promise = $timeout(doSomethingCool, 1000);
116 | myTracker.addPromise(promise);
117 | console.log(myTracker.active()); // => true
118 | //1000 milliseconds later...
119 | console.log(myTracker.active()); // => false
120 | ```
121 |
122 | * **`promise` tracker.createPromise()**
123 |
124 | Creates and returns a new deferred object that is tracked by our promiseTracker.
125 |
126 | Usage Example:
127 |
128 | ```js
129 | var deferred = myTracker.createPromise()
130 | console.log(myTracker.active()); // => true
131 | deferred.resolve();
132 | console.log(myTracker.active()); // => false
133 | ```
134 |
135 | * **`void` tracker.cancel()**
136 |
137 | Causes a tracker to immediately become inactive and stop tracking all current promises.
138 |
139 | ### **`$http` Sugar**
140 |
141 | **Requires promise-tracker-http-interceptor.js**
142 |
143 | * **Any $http call's `config` parameter can have a `tracker` field. Examples:**
144 |
145 | ```js
146 | //Add $http promise to tracker with id 'myTracker'
147 | $http('/banana', { tracker: myPromiseTrackerInstance })
148 | ```
149 | ```js
150 | //Add $http promise to both 'tracker1' and 'tracker2'
151 | $http.post('/elephant', {some: 'data'}, { tracker: [myFirstTracker, mySecondTracker] })
152 | ```
153 |
154 | ## More Examples
155 |
156 | * Do something whenever the tracker's active state changes
157 |
158 | ```js
159 | angular.module('app', ['ajoslin.promise-tracker'])
160 |
161 | .factory('myTracker', function (promiseTracker) {
162 | return promiseTracker();
163 | })
164 |
165 | .controller('AppCtrl', function ($rootScope, myTracker) {
166 | $rootScope.$watch(myTracker.active, function (isActive) {
167 | //doSomething()
168 | });
169 | });
170 | ```
171 |
172 | ## Development
173 |
174 | * Install karma & grunt with `npm install -g karma grunt-cli` to build & test
175 | * Install local dependencies with `bower install && npm install`
176 | * Run `grunt` to lint, test, build the code, and build the docs site
177 | * Run `grunt dev` to watch and re-test on changes
178 |
179 | #### New Versions
180 |
181 | ## License
182 |
183 | >
angular-promise-tracker by Andy Joslin is free of known copyright restrictions.
184 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "Andy Joslin",
3 | "name": "promise-tracker",
4 | "description": "Easily add spinners or general request tracking to your angular app.",
5 | "version": "2.1.0",
6 | "homepage": "http://github.com/ajoslin/angular-promise-tracker",
7 | "repository": {
8 | "type": "git",
9 | "url": "git://github.com/ajoslin/angular-promise-tracker"
10 | },
11 | "license": "Public Domain",
12 | "main": "./promise-tracker.js",
13 | "ignore": [
14 | "CHANGELOG.md",
15 | "Gruntfile.js",
16 | "bower.json",
17 | "bower_components",
18 | "dist",
19 | "node_modules",
20 | "package.json",
21 | "src",
22 | "test"
23 | ],
24 | "files": [
25 | "promise-tracker.js",
26 | "promise-tracker-http-interceptor.js"
27 | ],
28 | "dependencies": {
29 | "angular": ">=1.3.0"
30 | },
31 | "devDependencies": {
32 | "angular-mocks": ">=1.3.0",
33 | "angular-resource": ">=1.3.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-promise-tracker",
3 | "version": "2.2.2",
4 | "main": "promise-tracker.js",
5 | "author": {
6 | "name": "Andy Joslin"
7 | },
8 | "repository": {
9 | "url": "git://github.com/ajoslin/angular-promise-tracker.git"
10 | },
11 | "homepage": "https://github.com/ajoslin/angular-promise-tracker",
12 | "dependencies": {
13 | "angular": ">=1.3.0"
14 | },
15 | "devDependencies": {
16 | "load-grunt-tasks": "~0.2.1",
17 | "semver": "~2.2.1",
18 | "grunt-contrib-clean": "~0.5.0",
19 | "grunt-contrib-jshint": "~0.7.2",
20 | "grunt": "~0.4.2",
21 | "grunt-contrib-watch": "~0.5.3",
22 | "grunt-contrib-uglify": "~0.2.7",
23 | "karma-script-launcher": "~0.1.0",
24 | "karma-chrome-launcher": "~0.1.2",
25 | "karma-html2js-preprocessor": "~0.1.0",
26 | "karma-firefox-launcher": "~0.1.2",
27 | "karma-jasmine": "~0.1.5",
28 | "requirejs": "~2.1.9",
29 | "karma-requirejs": "~0.2.1",
30 | "karma-coffee-preprocessor": "~0.1.1",
31 | "karma-phantomjs-launcher": "~0.1.1",
32 | "karma": "~0.10.8",
33 | "grunt-karma": "~0.6.2",
34 | "grunt-conventional-changelog": "~1.1.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/promise-tracker-http-interceptor.js:
--------------------------------------------------------------------------------
1 | /*
2 | * promise-tracker - v2.1.0 - 2014-11-15
3 | * http://github.com/ajoslin/angular-promise-tracker
4 | * Created by Andy Joslin; Licensed under Public Domain
5 | */
6 |
7 | (function() {
8 |
9 | angular.module('ajoslin.promise-tracker')
10 | .config(['$httpProvider', function($httpProvider) {
11 | $httpProvider.interceptors.push(['$q', 'promiseTracker', function($q, promiseTracker) {
12 | return {
13 | request: function(config) {
14 | if (config.tracker) {
15 | if (!angular.isArray(config.tracker)) {
16 | config.tracker = [config.tracker];
17 | }
18 | config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];
19 |
20 | angular.forEach(config.tracker, function(tracker) {
21 | var deferred = tracker.createPromise();
22 | config.$promiseTrackerDeferred.push(deferred);
23 | });
24 | }
25 | return $q.when(config);
26 | },
27 | response: function(response) {
28 | if (response.config && response.config.$promiseTrackerDeferred) {
29 | angular.forEach(response.config.$promiseTrackerDeferred, function(deferred) {
30 | deferred.resolve(response);
31 | });
32 | }
33 | return $q.when(response);
34 | },
35 | responseError: function(response) {
36 | if (response.config && response.config.$promiseTrackerDeferred) {
37 | angular.forEach(response.config.$promiseTrackerDeferred, function(deferred) {
38 | deferred.reject(response);
39 | });
40 | }
41 | return $q.reject(response);
42 | }
43 | };
44 | }]);
45 | }]);
46 |
47 | }());
--------------------------------------------------------------------------------
/promise-tracker.js:
--------------------------------------------------------------------------------
1 | angular.module('ajoslin.promise-tracker', [])
2 |
3 | .provider('promiseTracker', function() {
4 | var trackers = {};
5 |
6 | this.$get = ['$q', '$timeout', function($q, $timeout) {
7 | function cancelTimeout(promise) {
8 | if (promise) {
9 | $timeout.cancel(promise);
10 | }
11 | }
12 |
13 | return function PromiseTracker(options) {
14 | options = options || {};
15 |
16 | //Array of promises being tracked
17 | var tracked = [];
18 | var self = {};
19 |
20 | //Allow an optional "minimum duration" that the tracker has to stay active for.
21 | var minDuration = options.minDuration;
22 | //Allow a delay that will stop the tracker from activating until that time is reached
23 | var activationDelay = options.activationDelay;
24 |
25 | var minDurationPromise;
26 | var activationDelayPromise;
27 |
28 | self.active = function() {
29 | //Even if we have a promise in our tracker, we aren't active until delay is elapsed
30 | if (activationDelayPromise) {
31 | return false;
32 | }
33 | return tracked.length > 0;
34 | };
35 |
36 | self.tracking = function() {
37 | //Even if we aren't active, we could still have a promise in our tracker
38 | return tracked.length > 0;
39 | };
40 |
41 | self.trackingCount = function() {
42 | return tracked.length;
43 | };
44 |
45 | self.destroy = self.cancel = function() {
46 | minDurationPromise = cancelTimeout(minDurationPromise);
47 | activationDelayPromise = cancelTimeout(activationDelayPromise);
48 | for (var i=tracked.length-1; i>=0; i--) {
49 | tracked[i].resolve();
50 | }
51 | tracked.length = 0;
52 | };
53 |
54 | //Create a promise that will make our tracker active until it is resolved.
55 | // @return deferred - our deferred object that is being tracked
56 | self.createPromise = function() {
57 | var deferred = $q.defer();
58 | tracked.push(deferred);
59 |
60 | //If the tracker was just inactive and this the first in the list of
61 | //promises, we reset our delay and minDuration
62 | //again.
63 | if (tracked.length === 1) {
64 | if (activationDelay) {
65 | activationDelayPromise = $timeout(function() {
66 | activationDelayPromise = cancelTimeout(activationDelayPromise);
67 | startMinDuration();
68 | }, activationDelay);
69 | } else {
70 | startMinDuration();
71 | }
72 | }
73 |
74 | deferred.promise.then(onDone(false), onDone(true));
75 |
76 | return deferred;
77 |
78 | function startMinDuration() {
79 | if (minDuration) {
80 | minDurationPromise = $timeout(angular.noop, minDuration);
81 | }
82 | }
83 |
84 | //Create a callback for when this promise is done. It will remove our
85 | //tracked promise from the array if once minDuration is complete
86 | function onDone(isError) {
87 | return function(value) {
88 | (minDurationPromise || $q.when()).then(function() {
89 | var index = tracked.indexOf(deferred);
90 | tracked.splice(index, 1);
91 |
92 | //If this is the last promise, cleanup the timeouts
93 | //for activationDelay
94 | if (tracked.length === 0) {
95 | activationDelayPromise = cancelTimeout(activationDelayPromise);
96 | }
97 | });
98 | };
99 | }
100 | };
101 |
102 | self.addPromise = function(promise) {
103 | if (Array.isArray(promise)) {
104 | return $q.all(promise.map(self.addPromise));
105 | }
106 |
107 | promise = promise && (promise.$promise || promise) || {};
108 | if (!promise.then) {
109 | throw new Error("promiseTracker#addPromise expects a promise object!");
110 | }
111 |
112 | var deferred = self.createPromise();
113 |
114 | //When given promise is done, resolve our created promise
115 | //Allow $then for angular-resource objects
116 | promise.then(function success(value) {
117 | deferred.resolve(value);
118 | return value;
119 | }, function error(value) {
120 | deferred.reject(value);
121 | return $q.reject(value);
122 | });
123 |
124 | return deferred;
125 | };
126 |
127 | return self;
128 | };
129 | }];
130 | });
131 |
--------------------------------------------------------------------------------
/test/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Mon Dec 23 2013 08:15:21 GMT-0500 (EST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path, that will be used to resolve files and exclude
8 | basePath: '../',
9 |
10 |
11 | // frameworks to use
12 | frameworks: ['jasmine'],
13 |
14 |
15 | // list of files / patterns to load in the browser
16 | files: [
17 | 'bower_components/angular/angular.js',
18 | 'bower_components/angular-mocks/angular-mocks.js',
19 | 'bower_components/angular-resource/angular-resource.js',
20 | 'promise-tracker.js',
21 | 'promise-tracker-http-interceptor.js',
22 | 'test/unit/**/*.js'
23 | ],
24 |
25 |
26 | // list of files to exclude
27 | exclude: [
28 |
29 | ],
30 |
31 |
32 | // test results reporter to use
33 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
34 | reporters: ['dots'],
35 |
36 |
37 | // web server port
38 | port: 9876,
39 |
40 |
41 | // enable / disable colors in the output (reporters and logs)
42 | colors: true,
43 |
44 |
45 | // level of logging
46 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
47 | logLevel: config.LOG_INFO,
48 |
49 |
50 | // enable / disable watching file and executing tests whenever any file changes
51 | autoWatch: true,
52 |
53 |
54 | // Start these browsers, currently available:
55 | // - Chrome
56 | // - ChromeCanary
57 | // - Firefox
58 | // - Opera (has to be installed with `npm install karma-opera-launcher`)
59 | // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
60 | // - PhantomJS
61 | // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
62 | browsers: ['Chrome'],
63 |
64 |
65 | // If browser does not capture in given timeout [ms], kill it
66 | captureTimeout: 60000,
67 |
68 |
69 | // Continuous Integration mode
70 | // if true, it capture browsers, run tests and exit
71 | singleRun: false
72 | });
73 | };
74 |
--------------------------------------------------------------------------------
/test/unit/interceptor.spec.js:
--------------------------------------------------------------------------------
1 | describe('http interceptor', function() {
2 |
3 | beforeEach(module('ajoslin.promise-tracker'));
4 |
5 | var http, promiseTracker, backend, q;
6 | beforeEach(inject(function($http, _promiseTracker_, $httpBackend, $q) {
7 | http = $http;
8 | promiseTracker = _promiseTracker_;
9 | $httpBackend.whenGET('/ok').respond(200);
10 | $httpBackend.whenGET('/error').respond(404);
11 | backend = $httpBackend;
12 | q = $q;
13 | }));
14 |
15 | function digest() {
16 | inject(function($rootScope) { $rootScope.$digest(); });
17 | }
18 |
19 | it('should add a promise to tracking with http config option', function() {
20 | var tracker = promiseTracker();
21 | var tracker2 = promiseTracker();
22 | spyOn(tracker, 'createPromise').andCallThrough();
23 | spyOn(tracker2, 'createPromise').andCallThrough();
24 |
25 | http.get('/ok', { tracker: tracker });
26 | digest();
27 | expect(tracker.createPromise).toHaveBeenCalled();
28 |
29 | tracker.createPromise.reset();
30 | http.get('/ok', { tracker: [tracker,tracker2] });
31 | digest();
32 | expect(tracker.createPromise).toHaveBeenCalled();
33 | expect(tracker2.createPromise).toHaveBeenCalled();
34 | });
35 |
36 | it('should resolve on good response', function(){
37 | var tracker = promiseTracker();
38 | var deferred = q.defer();
39 | spyOn(tracker, 'createPromise').andCallFake(function() {
40 | return deferred;
41 | });
42 | spyOn(deferred, 'resolve');
43 |
44 | http.get('/ok', { tracker: tracker });
45 | digest();
46 | backend.flush();
47 | expect(deferred.resolve).toHaveBeenCalled();
48 | expect(deferred.resolve.mostRecentCall.args[0].status).toBe(200);
49 | });
50 |
51 | it('should reject on error response', function(){
52 | var tracker = promiseTracker();
53 | var deferred = q.defer();
54 | spyOn(tracker, 'createPromise').andCallFake(function() {
55 | return deferred;
56 | });
57 | spyOn(deferred, 'reject');
58 |
59 | http.get('/error', { tracker: tracker });
60 | digest();
61 | backend.flush();
62 | expect(deferred.reject).toHaveBeenCalled();
63 | expect(deferred.reject.mostRecentCall.args[0].status).toBe(404);
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/test/unit/provider.spec.js:
--------------------------------------------------------------------------------
1 | describe('promiseTracker provider', function() {
2 | beforeEach(module('ajoslin.promise-tracker'));
3 |
4 | var promiseTracker, timeout, q;
5 | beforeEach(inject(function(_promiseTracker_, $timeout, $q) {
6 | promiseTracker = _promiseTracker_;
7 | timeout = $timeout;
8 | q = $q;
9 | }));
10 |
11 | function digest() {
12 | inject(function($rootScope) { $rootScope.$digest(); });
13 | }
14 |
15 | it('should create a tracker with api', function() {
16 | var tracker = new promiseTracker();
17 | expect(typeof tracker.addPromise).toBe('function');
18 | expect(typeof tracker.createPromise).toBe('function');
19 | expect(typeof tracker.destroy).toBe('function');
20 | expect(typeof tracker.cancel).toBe('function');
21 | });
22 |
23 | it('should create a tracker even if no `new`', function() {
24 | var tracker = promiseTracker();
25 | expect(typeof tracker.addPromise).toBe('function');
26 | });
27 |
28 | it('should not be active by default', function() {
29 | expect(promiseTracker().active()).toBe(false);
30 | });
31 |
32 | it('should not be tracking by default', function() {
33 | expect(promiseTracker().tracking()).toBe(false);
34 | });
35 |
36 | describe('addPromise', function() {
37 |
38 | it('should error with object', function() {
39 | expect(function() { promiseTracker().addPromise({}); }).toThrow();
40 | });
41 |
42 | it('should error with deferred', function() {
43 | expect(function() { promiseTracker().addPromise(q.defer()); }).toThrow();
44 | });
45 |
46 | it('should not error with then, $then, $promise.then', function() {
47 | promiseTracker().addPromise(q.defer().promise);
48 | promiseTracker().addPromise({ $promise: q.defer().promise } );
49 | });
50 |
51 | it('should return promise from createPromise', function() {
52 | var tracker = promiseTracker();
53 | var promise = q.defer().promise;
54 | var created = q.defer();
55 | spyOn(tracker, 'createPromise').andCallFake(function() {
56 | return created;
57 | });
58 | var ret = tracker.addPromise(promise);
59 | expect(ret).toBe(created);
60 | });
61 |
62 | it('should resolve returned promise when passed in promise is resolved', function() {
63 | var tracker = promiseTracker();
64 | var deferred = q.defer();
65 | var trackerPromise = tracker.addPromise(deferred.promise);
66 | spyOn(trackerPromise, 'resolve');
67 | deferred.resolve(1);
68 | digest();
69 | expect(trackerPromise.resolve).toHaveBeenCalledWith(1);
70 | });
71 |
72 | it('should reject returned promise when passed in promise is rejected', function() {
73 | var tracker = promiseTracker();
74 | var deferred = q.defer();
75 | var trackerPromise = tracker.addPromise(deferred.promise);
76 | spyOn(trackerPromise, 'reject');
77 | deferred.reject(2);
78 | digest();
79 | expect(trackerPromise.reject).toHaveBeenCalledWith(2);
80 | });
81 |
82 | it('should start tracking with then, $then, $promise.then', function() {
83 | var tracker = promiseTracker();
84 | tracker.addPromise(q.defer().promise);
85 | expect(tracker.tracking()).toBe(true);
86 |
87 | tracker = promiseTracker();
88 | tracker.addPromise(q.defer().promise);
89 | expect(tracker.tracking()).toBe(true);
90 |
91 | tracker = promiseTracker();
92 | tracker.addPromise({ $promise: q.defer().promise });
93 | expect(tracker.tracking()).toBe(true);
94 | });
95 |
96 | });
97 |
98 | describe('createPromise', function() {
99 |
100 | it('should return a deferred', function() {
101 | expect(promiseTracker().createPromise().promise.then).toBeTruthy();
102 | });
103 |
104 | it('should set active to true when promise is added', function() {
105 | var tracker = promiseTracker();
106 | tracker.createPromise();
107 | expect(tracker.active()).toBe(true);
108 | });
109 |
110 | it('should set active to true when promises are added', function() {
111 | var tracker = promiseTracker();
112 | tracker.createPromise();
113 | tracker.createPromise();
114 | expect(tracker.active()).toBe(true);
115 | });
116 |
117 | it('should set active to false when promises are added and resolved/rejected', function() {
118 | var tracker = promiseTracker();
119 | var p1 = tracker.createPromise();
120 | var p2 = tracker.createPromise();
121 | expect(tracker.active()).toBe(true);
122 | p1.resolve();
123 | digest();
124 | expect(tracker.active()).toBe(true);
125 | p2.reject();
126 | digest();
127 | expect(tracker.active()).toBe(false);
128 | });
129 |
130 | it('should set tracking to true when promise is added', function() {
131 | var tracker = promiseTracker();
132 | tracker.createPromise();
133 | expect(tracker.tracking()).toBe(true);
134 | });
135 |
136 | it('should set tracking to true when promises are added', function() {
137 | var tracker = promiseTracker();
138 | tracker.createPromise();
139 | tracker.createPromise();
140 | expect(tracker.tracking()).toBe(true);
141 | });
142 |
143 | it('should set tracking to false when promises are added and resolved/rejected', function() {
144 | var tracker = promiseTracker();
145 | var p1 = tracker.createPromise();
146 | var p2 = tracker.createPromise();
147 | expect(tracker.tracking()).toBe(true);
148 | p1.resolve();
149 | digest();
150 | expect(tracker.tracking()).toBe(true);
151 | p2.reject();
152 | digest();
153 | expect(tracker.tracking()).toBe(false);
154 | });
155 |
156 | });
157 |
158 | it('cancel should deactivate and resolve all promises', function() {
159 | var tracker = promiseTracker();
160 | var p1 = tracker.createPromise();
161 | expect(tracker.active()).toBe(true);
162 | spyOn(p1, 'resolve');
163 | tracker.cancel();
164 | expect(p1.resolve).toHaveBeenCalled();
165 | expect(tracker.active()).toBe(false);
166 | expect(tracker.tracking()).toBe(false);
167 | });
168 |
169 | it('destroy should be cancel', function() {
170 | var tracker = promiseTracker();
171 | expect(tracker.destroy).toBe(tracker.cancel);
172 | });
173 |
174 | describe('activationDelay', function() {
175 |
176 | it('should not be active() until delay is over', function() {
177 | var tracker = promiseTracker({ activationDelay: 1000 });
178 | tracker.createPromise();
179 |
180 | //Should not be active due to delay
181 | expect(tracker.active()).toBe(false);
182 | tracker.createPromise();
183 | expect(tracker.active()).toBe(false);
184 |
185 | //Flush, it should be active
186 | timeout.flush();
187 | expect(tracker.active()).toBe(true);
188 | });
189 |
190 | it('should be tracking irrespective of the activation delay', function() {
191 | var tracker = promiseTracker({ activationDelay: 1000 });
192 | tracker.createPromise();
193 |
194 | //Should be tracking
195 | expect(tracker.tracking()).toBe(true);
196 | tracker.createPromise();
197 | expect(tracker.tracking()).toBe(true);
198 |
199 | //Flush, it should be tracking
200 | timeout.flush();
201 | expect(tracker.tracking()).toBe(true);
202 | });
203 |
204 | });
205 |
206 | describe('minDuration', function() {
207 |
208 | it('should be active() for at least minDuration', function() {
209 | var tracker = promiseTracker({ minDuration: 1000 });
210 | var p1 = tracker.createPromise();
211 | expect(tracker.active()).toBe(true);
212 | p1.resolve();
213 | digest();
214 | //Should still be active until minDuration timeout elapses
215 | expect(tracker.active()).toBe(true);
216 | timeout.flush();
217 | expect(tracker.active()).toBe(false);
218 | });
219 |
220 | it('should not deactivate if there is still another promise active', function() {
221 | var tracker = promiseTracker({ minDuration: 1000 });
222 | var p1 = tracker.createPromise();
223 | expect(tracker.active()).toBe(true);
224 | p1.resolve();
225 | digest();
226 | //Should still be active until minDuration timeout elapses
227 | expect(tracker.active()).toBe(true);
228 | var p2 = tracker.createPromise();
229 | timeout.flush();
230 | expect(tracker.active()).toBe(true);
231 | p2.resolve();
232 | digest();
233 | expect(tracker.active()).toBe(false);
234 | });
235 |
236 | it('should be tracking for at least minDuration', function() {
237 | var tracker = promiseTracker({ minDuration: 1000 });
238 | var p1 = tracker.createPromise();
239 | expect(tracker.tracking()).toBe(true);
240 | p1.resolve();
241 | digest();
242 | //Should still be tracking until minDuration timeout elapses
243 | expect(tracker.tracking()).toBe(true);
244 | timeout.flush();
245 | expect(tracker.tracking()).toBe(false);
246 | });
247 |
248 | it('should continue tracking if there is still another promise active', function() {
249 | var tracker = promiseTracker({ minDuration: 1000 });
250 | var p1 = tracker.createPromise();
251 | expect(tracker.tracking()).toBe(true);
252 | p1.resolve();
253 | digest();
254 | //Should still be tracking until minDuration timeout elapses
255 | expect(tracker.tracking()).toBe(true);
256 | var p2 = tracker.createPromise();
257 | timeout.flush();
258 | expect(tracker.tracking()).toBe(true);
259 | p2.resolve();
260 | digest();
261 | expect(tracker.tracking()).toBe(false);
262 | });
263 |
264 | });
265 |
266 | describe('minDuration + activationDelay', function() {
267 |
268 | it('should delay, be active, wait until duration, then be inactive', function() {
269 | var tracker = promiseTracker({ minDuration: 500, activationDelay: 250 });
270 | var p1 = tracker.createPromise();
271 | expect(tracker.active()).toBe(false);
272 | timeout.flush();
273 | expect(tracker.active()).toBe(true);
274 | p1.resolve();
275 | digest();
276 | expect(tracker.active()).toBe(true);
277 | timeout.flush();
278 | expect(tracker.active()).toBe(false);
279 | });
280 |
281 | it('should delay, be tracking, wait until duration, then be not tracking', function() {
282 | var tracker = promiseTracker({ minDuration: 500, activationDelay: 250 });
283 | expect(tracker.tracking()).toBe(false);
284 | var p1 = tracker.createPromise();
285 | expect(tracker.tracking()).toBe(true);
286 | timeout.flush();
287 | expect(tracker.tracking()).toBe(true);
288 | p1.resolve();
289 | digest();
290 | expect(tracker.tracking()).toBe(true);
291 | timeout.flush();
292 | expect(tracker.tracking()).toBe(false);
293 | });
294 |
295 | });
296 |
297 | });
298 |
--------------------------------------------------------------------------------