diff --git a/src/components/services/predictiveCache.js b/src/components/services/predictiveCache.js index 5711c8f1..ae7491aa 100644 --- a/src/components/services/predictiveCache.js +++ b/src/components/services/predictiveCache.js @@ -1,8 +1,31 @@ angular .module("bawApp.services.predictiveCache", []) .factory( + "predictiveCacheInterceptor", + [ + function () { + var _listeners = []; + return { + listeners: function() { + return _listeners; + }, + request: function pciRequest(httpConfig) { + + _listeners.forEach(function) + + return httpConfig; + } + /* + requestError: , + response: , + responseError: + */ + }; + } + ]) + .factory( "predictiveCache", - ["$http", function ($http) { + ["$http", "predictiveCacheInterceptor", function ($http, predictiveCacheInterceptor) { var defaults = { name: null, @@ -16,6 +39,8 @@ angular var defaultProgression = 1; var unnamedProfiles = 0; + var profiles = {}; + function validateProfile(settings) { settings = angular.extend({}, defaults, settings); @@ -63,7 +88,7 @@ angular throw new Error("progression must be an array of numbers/functions"); } - if(!angular.isNumber(settings.count) || settings.count < 0) { + if (!angular.isNumber(settings.count) || settings.count < 0) { throw new Error("count must be a positive integer"); } @@ -74,6 +99,9 @@ angular return settings; } + function createInterceptor() { + }; + return function predictiveCache(profile) { if (!angular.isObject(profile)) { throw new Error("A profile is required"); @@ -81,7 +109,11 @@ angular var settings = validateProfile(profile); + profiles[settings.name] = settings; + return settings; }; - }] -); \ No newline at end of file + }]) + .config(['$httpProvider', function($httpProvider) { + $httpProvider.interceptors.push("predictiveCacheInterceptor"); + }]); \ No newline at end of file diff --git a/src/components/services/predictiveCache.spec.js b/src/components/services/predictiveCache.spec.js index 8b84f4a9..f9b90baf 100644 --- a/src/components/services/predictiveCache.spec.js +++ b/src/components/services/predictiveCache.spec.js @@ -1,15 +1,19 @@ describe("The predictiveCache service", function () { - var predictiveCache; - var testProfile; - beforeEach(module("bawApp.services")); + var predictiveCache, + testProfile, + $httpProvider; + beforeEach(module("bawApp.services", function (_$httpProvider_) { + $httpProvider = _$httpProvider_; - beforeEach(inject(["predictiveCache", function (_predictiveCache) { - predictiveCache = _predictiveCache; + })); + + beforeEach(inject(function (_predictiveCache_) { + predictiveCache = _predictiveCache_; testProfile = { name: "Media cache ahead", - match: /google\.com\?page=(.*)&skip=(.*)/, + match: /google\.com\?page=(.*)&size=(.*)/, request: ["one url", "another url"], progression: [ 30.0, @@ -26,142 +30,243 @@ describe("The predictiveCache service", function () { count: 10, method: "HEAD" }; - }])); + })); - it("requires an object to function", function () { - expect(function () { - predictiveCache(); - }).toThrowError(Error, "A profile is required"); + describe("The predictive cache http interceptor", function () { + var predictiveCacheInterceptor, $httpBackend; - }); + beforeEach(inject(function (_predictiveCacheInterceptor_, $injector) { + predictiveCacheInterceptor = _predictiveCacheInterceptor_; + $httpBackend = $injector.get("$httpBackend"); + })); - it("returns the profile that was passed into it", function () { - var profile = predictiveCache(testProfile); + it("ensure the interceptor implements the expected methods", function () { + expect(predictiveCacheInterceptor).toImplement({ + request: null, + listeners: null + }); + }); - var pj = JSON.stringify(profile), - tj = JSON.stringify(testProfile); + it("requires the interceptor be registered!", function () { + var index = $httpProvider.interceptors.indexOf("predictiveCacheInterceptor"); + expect(index >= 0).toBeTrue(); + }); - expect(pj).toBe(tj); - }); + it("ensures the request interceptor calls the correct method on every listener when a request is made", function () { + predictiveCacheInterceptor.listeners().push(); + spyOn(predictiveCache, "interceptorCallback"); - it("expects the profile to accept the http verbs", function () { - var verbs = ["GET", "HEAD", "POST", "PUT", "DELETE"]; + var mockHttpConfig = { + url: "some fake url" + }; - // should occur without exception - verbs.forEach(function (verb) { - testProfile.method = verb; - predictiveCache(testProfile); + predictiveCacheInterceptor.request(mockHttpConfig); + + expect(predictiveCache.interceptorCallback).toHaveBeenCalled(); + expect(predictiveCache.interceptorCallback).toHaveBeenCalledWith("some fake url"); }); - expect(function () { - testProfile.method = "anything else"; - predictiveCache(testProfile); - }).toThrowError(Error, "A valid http method is required"); - }); + it("returns the http config unmodified", inject(["$http", function ($http) { + spyOn(predictiveCacheInterceptor, "request").and.callThrough(); - it("sets the default http verb to HEAD if omitted", function () { - delete testProfile.method; - var profile = predictiveCache(testProfile); + $httpBackend.expectGET("www.url.com").respond(200); + var reqConfig; + $http.get("www.url.com").success(function(d,s,h,c,st) { + reqConfig = c; + }); + $httpBackend.flush(); + + expect(predictiveCacheInterceptor.request).toHaveBeenCalled(); + var interceptorInput = predictiveCacheInterceptor.request.calls.argsFor(0); + + var e = JSON.stringify(interceptorInput[0]), + a = JSON.stringify(reqConfig); - expect(profile.method).toBe("HEAD"); + expect(a).toBe(e); + }])); }); - it("requires name to be a string", function () { - testProfile.name = 33; + describe("The functionality of the cacher", function () { + var configuredProfile, $http, + exampleUrl = "http://www.google.com?page=1&size=10"; + + beforeEach(inject(function ($injector) { + $httpBackend = $injector.get("$httpBackend"); + + // create .when for requests common to all tests + + // grab a reference to $http + $http = $injector.get("$http"); + + // set up the cache service (register everything) + configuredProfile = predictiveCache(testProfile); + })); - expect(function () { - predictiveCache(testProfile); - }).toThrowError("The provided name must be a string"); + afterEach(function () { + $httpBackend.verifyNoOutstandingExpectation(); + $httpBackend.verifyNoOutstandingRequest(); + }); + + it("makes the required requests when the url is matched", function () { + + $httpBackend.expectGET(exampleUrl).respond(200); + $httpBackend.expectHEAD("one url").respond(200); + $httpBackend.expectHEAD("another url").respond(200); + $http.get(exampleUrl); + + $httpBackend.flush(); + }); }); - it("will provide an automatic name if one is not supplied", function () { - delete testProfile.name; + it("registers a new http interceptor when a profile is added", function () { + var interceptorCount = $httpProvider.interceptors.length; + var profile = predictiveCache(testProfile); - expect(profile.name).toBe("UnnamedProfile1"); - profile = predictiveCache(testProfile); - expect(profile.name).toBe("UnnamedProfile2"); + expect($httpProvider.interceptors.length).toBe(interceptorCount + 1); }); - it("will fail if the supplied match regular expression is missing", function () { - delete testProfile.match; - expect(function () { - predictiveCache(testProfile); - }).toThrowError("A value for match must be provided"); - }); + describe("Validating the input profile", function () { + it("requires an object to function", function () { + expect(function () { + predictiveCache(); + }).toThrowError(Error, "A profile is required"); - it("will fail if the supplied match regular expression is not a RegExp", function () { - testProfile.match = "test"; + }); - expect(function () { - predictiveCache(testProfile); - }).toThrowError("The value for match must be a regular expression"); - }); + it("returns the profile that was passed into it", function () { + var profile = predictiveCache(testProfile); + + var pj = JSON.stringify(profile), + tj = JSON.stringify(testProfile); + + expect(pj).toBe(tj); + }); - it("ensures the request value is an array of strings", function () { - var failValues = [null, [], 33, [33]]; + it("expects the profile to accept the http verbs", function () { + var verbs = ["GET", "HEAD", "POST", "PUT", "DELETE"]; - failValues.forEach(function (item) { - testProfile.request = item; + // should occur without exception + verbs.forEach(function (verb) { + testProfile.method = verb; + predictiveCache(testProfile); + }); expect(function () { + testProfile.method = "anything else"; predictiveCache(testProfile); - }).toThrowError("requests must be an array of strings"); + }).toThrowError(Error, "A valid http method is required"); }); - }); - [ - {key: "null", in: null, out: 1}, - {key: "undefined", in: undefined, out: 1}, - {key: "number", in: 3, out: 3}, - {key: "array of numbers or functions", in: [1, function (i) {return i;}]} - ].forEach(function (progressionTest) { - it("allows the progression value to be " + progressionTest.key, function () { - testProfile.progression = progressionTest.in; - - var profile = predictiveCache(testProfile); - expect(profile.progression).toBe(progressionTest.out || progressionTest.in); - }); + it("sets the default http verb to HEAD if omitted", function () { + delete testProfile.method; + var profile = predictiveCache(testProfile); + + expect(profile.method).toBe("HEAD"); + }); + + it("requires name to be a string", function () { + testProfile.name = 33; + + expect(function () { + predictiveCache(testProfile); + }).toThrowError("The provided name must be a string"); + }); + + it("will provide an automatic name if one is not supplied", function () { + delete testProfile.name; + var profile = predictiveCache(testProfile); + expect(profile.name).toBe("UnnamedProfile1"); + + profile = predictiveCache(testProfile); + expect(profile.name).toBe("UnnamedProfile2"); + }); + + it("will fail if the supplied match regular expression is missing", function () { + delete testProfile.match; + + expect(function () { + predictiveCache(testProfile); + }).toThrowError("A value for match must be provided"); + }); + + it("will fail if the supplied match regular expression is not a RegExp", function () { + testProfile.match = "test"; + + expect(function () { + predictiveCache(testProfile); + }).toThrowError("The value for match must be a regular expression"); }); - [ - {key: "a string", in: "test"}, - {key: "empty array", in: []}, - {key: "array not of numbers or functions, strings", in: ["testing"]}, - {key: "array not of numbers or functions, null/undefined", in: [null, undefined, 3.0]}, - {key: "array not of numbers or functions, object", in: [{}]} - ].forEach(function (progressionTest) { - it("disallows the progression value to be " + progressionTest.key, function () { - testProfile.progression = progressionTest.in; + it("ensures the request value is an array of strings", function () { + var failValues = [null, [], 33, [33]]; + + failValues.forEach(function (item) { + testProfile.request = item; - expect(function() { - var profile = predictiveCache(testProfile); - }).toThrowError("progression must be an array of numbers/functions"); + expect(function () { + predictiveCache(testProfile); + }).toThrowError("requests must be an array of strings"); }); }); - it("ensures count is a number", function() { - var profile = predictiveCache(testProfile); - expect(profile.count).toBe(testProfile.count); + [ + {key: "null", in: null, out: 1}, + {key: "undefined", in: undefined, out: 1}, + {key: "number", in: 3, out: 3}, + { + key: "array of numbers or functions", in: [1, function (i) { + return i; + }] + } + ].forEach(function (progressionTest) { + it("allows the progression value to be " + progressionTest.key, function () { + testProfile.progression = progressionTest.in; + + var profile = predictiveCache(testProfile); + expect(profile.progression).toBe(progressionTest.out || progressionTest.in); + }); + }); - testProfile.count = "fkasnfiabfias"; - expect(function() { - predictiveCache(testProfile) - }).toThrowError("count must be a positive integer"); + [ + {key: "a string", in: "test"}, + {key: "empty array", in: []}, + {key: "array not of numbers or functions, strings", in: ["testing"]}, + {key: "array not of numbers or functions, null/undefined", in: [null, undefined, 3.0]}, + {key: "array not of numbers or functions, object", in: [{}]} + ].forEach(function (progressionTest) { + it("disallows the progression value to be " + progressionTest.key, function () { + testProfile.progression = progressionTest.in; + + expect(function () { + var profile = predictiveCache(testProfile); + }).toThrowError("progression must be an array of numbers/functions"); + }); + }); - testProfile.count = -1; - expect(function() { - predictiveCache(testProfile) - }).toThrowError("count must be a positive integer"); - }); + it("ensures count is a number", function () { + var profile = predictiveCache(testProfile); + expect(profile.count).toBe(testProfile.count); - it("allows a default value to be used for count", function() { - delete testProfile.count; + testProfile.count = "fkasnfiabfias"; + expect(function () { + predictiveCache(testProfile); + }).toThrowError("count must be a positive integer"); - var profile = predictiveCache(testProfile); - expect(profile.count).toBe(10); + testProfile.count = -1; + expect(function () { + predictiveCache(testProfile); + }).toThrowError("count must be a positive integer"); + }); - }) + it("allows a default value to be used for count", function () { + delete testProfile.count; + + var profile = predictiveCache(testProfile); + expect(profile.count).toBe(10); + + }); + }); }); \ No newline at end of file