diff --git a/karma.conf.js b/karma.conf.js index 0fa8cb720..e0a6f62c4 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -4,10 +4,10 @@ module.exports = function (config) { config.set({ basePath: '', files: [ + 'test/util.js', 'zone.js', '*-zone.js', //'test/lib/brick.js', - 'test/util.js', 'test/**/*.spec.js', {pattern: 'test/assets/**/*.html', watched: true, served: true, included: false} ], diff --git a/test/util.js b/test/util.js index dfb8d58aa..0deb4c532 100644 --- a/test/util.js +++ b/test/util.js @@ -21,3 +21,6 @@ function ifEnvSupports(test, block) { } }; }; + +// useful for testing mocks +window.__setTimeout = window.setTimeout; diff --git a/test/zone.spec.js b/test/zone.spec.js index 1e1aa8cf4..478bab224 100644 --- a/test/zone.spec.js +++ b/test/zone.spec.js @@ -140,6 +140,32 @@ describe('Zone', function () { }); }); + describe('bindPromiseFn', function () { + var mockPromise = function() { + return { + then: function (a, b) { + window.__setTimeout(a, 0); + return mockPromise(); + } + }; + }; + + it('should return a method that returns promises that run in the correct zone', function (done) { + zone.fork({ mark: 'a' }).run(function () { + var patched = Zone.bindPromiseFn(function() { + return mockPromise(); + }); + + patched().then(function () { + expect(zone.mark).toBe('a'); + }).then(function () { + expect(zone.mark).toBe('a'); + done(); + }); + }); + }); + }); + }); function throwError () { diff --git a/zone.js b/zone.js index 2f80837b6..b45ce9081 100644 --- a/zone.js +++ b/zone.js @@ -221,6 +221,52 @@ Zone.bindArgumentsOnce = function (args) { return args; }; +/* + * patch a fn that returns a promise + */ +Zone.bindPromiseFn = (function() { + // if the browser natively supports Promises, we can just return a native promise + if (window.Promise) { + return function (delegate) { + return function() { + var delegatePromise = delegate.apply(this, arguments); + if (delegatePromise instanceof Promise) { + return delegatePromise; + } else { + return new Promise(function(resolve, reject) { + delegatePromise.then(resolve, reject); + }); + } + }; + }; + } else { + // if the browser does not have native promises, we have to patch each promise instance + return function (delegate) { + return function () { + return patchThenable(delegate.apply(this, arguments)); + }; + }; + + function patchThenable(thenable) { + var then = thenable.then; + thenable.then = function () { + var args = Zone.bindArguments(arguments); + var nextThenable = then.apply(thenable, args); + return patchThenable(nextThenable); + }; + + var ocatch = thenable.catch; + thenable.catch = function () { + var args = Zone.bindArguments(arguments); + var nextThenable = ocatch.apply(thenable, args); + return patchThenable(nextThenable); + }; + return thenable; + } + } +}()); + + Zone.patchableFn = function (obj, fnNames) { fnNames.forEach(function (name) { var delegate = obj[name];