describe("Jelly", function() { var our_token; beforeEach(function() { spyOn($, 'ajax'); our_token = "authenticity token"; window._token = our_token; Jelly.init(); }); describe(".add", function() { afterEach(function() { delete Jelly.Pages.all["test-name"]; }); it("instantiates a Page with the passed-in name and attaches the set of passed-in functions to the Page object", function() { expect(Jelly.Pages.all["test-name"]).toEqual(undefined); var showFn = function() { }; var newFn = function() { }; var indexFn = function() { }; var newPage = Jelly.Pages.add("test-name", {show: showFn}, {'new': newFn}, {index: indexFn}); expect(Jelly.Pages.all["test-name"]).toNotEqual(undefined); expect(newPage).toEqual(Jelly.Pages.all["test-name"]); expect(newPage.show).toEqual(showFn); expect(newPage['new']).toEqual(newFn); expect(newPage.index).toEqual(indexFn); }); }); describe(".run", function() { describe("attach", function() { describe("when the component does not respond to init", function() { describe("when the component is referenced as a String", function() { beforeEach(function() { window.MyComponent = { }; }); afterEach(function() { delete window.MyComponent; }); it("attaches the component to Jelly.observers", function() { Jelly.run(["attach", "MyComponent"]); expect(Jelly.observers).toContain(MyComponent); }); }); describe("when the component is referenced as itself", function() { it("attaches the component to Jelly.observers", function() { var component = {}; Jelly.run(["attach", component]); expect(Jelly.observers).toContain(component); }); }); }) }); }); describe(".attach", function() { describe("when the component does not respond to init", function() { describe("when the component is referenced as a String", function() { beforeEach(function() { window.MyComponent = { }; }); afterEach(function() { delete window.MyComponent; }); it("attaches the component to Jelly.observers", function() { Jelly.attach("MyComponent"); expect(Jelly.observers).toContain(MyComponent); }); }); describe("when the component is referenced as itself", function() { it("attaches the component to Jelly.observers", function() { var component = {}; Jelly.attach(component); expect(Jelly.observers).toContain(component); }); }); }); describe("when component responds to init", function() { describe("when the component's init method returns undefined", function() { describe("when the component is referenced as a String", function() { beforeEach(function() { window.MyComponent = { init: function() { } }; }); afterEach(function() { delete window.MyComponent; }); it("calls the init method on the component and attaches the component to Jelly.observers", function() { spyOn(MyComponent, 'init'); Jelly.attach(MyComponent, 1, 2); expect(MyComponent.init).wasCalledWith(1, 2); expect(Jelly.observers).toContain(MyComponent); }); }); describe("when the component is referenced as itself", function() { var component; beforeEach(function() { component = { init: function() { } }; }); it("calls the init method on the component and attaches the component to Jelly.observers", function() { spyOn(component, 'init'); Jelly.attach(component, 1, 2); expect(component.init).wasCalledWith(1, 2); expect(Jelly.observers).toContain(component); }); }); }); describe("when the component's init method returns false", function() { var component; beforeEach(function() { component = { init: function() { component.initCalled = true; return false; } }; }); it("calls the init method on the component and does not attaches an observer to Jelly.observers", function() { var originalObserversLength = Jelly.observers.length; Jelly.attach(component, 1, 2); expect(component.initCalled).toBeTruthy(); expect(Jelly.observers.length).toEqual(originalObserversLength); expect(Jelly.observers).toNotContain(component); }); }); describe("when the component's init method returns null", function() { var component; beforeEach(function() { component = { init: function() { component.initCalled = true; return null; } }; }); it("calls the init method on the component and does not attaches an observer to Jelly.observers", function() { var originalObserversLength = Jelly.observers.length; Jelly.attach(component, 1, 2); expect(component.initCalled).toBeTruthy(); expect(Jelly.observers.length).toEqual(originalObserversLength); expect(Jelly.observers).toNotContain(component); }); }); describe("when the component's init method returns an object", function() { it("attaches the returned object (instead of the component) to Jelly.observers", function() { var observer = new Object(); var component = { init: function() { return observer; } }; Jelly.attach(component, 1, 2); expect(Jelly.observers).toContain(observer); expect(Jelly.observers).toNotContain(component); }); }); }); }); describe(".Observers.notify", function() { beforeEach(function() { Jelly.Pages.add("MyPage", { on_my_method : function() { } }); Jelly.attach("Jelly.Page", "MyPage", "index"); }); describe("when bound to the default Jelly.observers collection", function() { describe("the active page object", function() { describe("when the notify method is defined on the page", function() { it("should call the notify method on the page", function() { spyOn(page, 'on_my_method'); Jelly.notifyObservers("on_my_method", "arg1", "arg2"); expect(page.on_my_method).wasCalled(); expect(page.on_my_method).wasCalledWith('arg1', 'arg2'); }); describe("when there are attached components", function() { it("calls the notify methods in the order of the attached components", function() { var component = { on_my_method: function() { } }; Jelly.attach(component); var functionsCalledInOrder = []; spyOn(page, 'on_my_method').andCallFake(function() { functionsCalledInOrder.push("page"); }); spyOn(component, 'on_my_method').andCallFake(function() { functionsCalledInOrder.push("component"); }); Jelly.notifyObservers("on_my_method", "arg1", "arg2"); expect(page.on_my_method).wasCalled(); expect(page.on_my_method).wasCalledWith('arg1', 'arg2'); expect(component.on_my_method).wasCalled(); expect(component.on_my_method).wasCalledWith('arg1', 'arg2'); expect(functionsCalledInOrder).toEqual(["page", "component"]); }); }); }); describe("when the page object does not define the notify method", function() { it("does not blow up", function() { expect(page.on_my_undefined_method).toBe(undefined); Jelly.notifyObservers("on_my_undefined_method", "arg1", "arg2"); }); }); }); describe("when the 'on' parameter is present", function() { beforeEach(function() { GlobalObject = {on_my_method: function() { }}; GlobalObject.secondObject = {on_my_method: function() { }}; }); afterEach(function() { delete GlobalObject; }); }); }); describe("when bound to an array of custom observers", function() { it("notifies the given observers and not the existing Jelly.observers, unless in the list of observers", function() { var component = { on_my_method: function() { } }; Jelly.attach("Jelly.Page", "MyPage", "index"); Jelly.attach(component); spyOn(page, 'on_my_method'); spyOn(component, 'on_my_method'); var customObserver1 = {on_my_method: function() { }}; spyOn(customObserver1, 'on_my_method'); var customObserver2 = {on_my_method: function() { }}; spyOn(customObserver2, 'on_my_method'); Jelly.notifyObservers.call([customObserver1, customObserver2], "on_my_method", "arg1", "arg2"); expect(page.on_my_method).wasNotCalled(); expect(component.on_my_method).wasNotCalled(); expect(customObserver1.on_my_method).wasCalled(); expect(customObserver1.on_my_method).wasCalledWith('arg1', 'arg2'); expect(customObserver2.on_my_method).wasCalled(); expect(customObserver2.on_my_method).wasCalledWith('arg1', 'arg2'); }); }); describe("an observer listening to on_notify", function() { it("receives a notify instruction", function() { var observer = { on_notify: function() { } }; spyOn(observer, 'on_notify'); Jelly.notifyObservers.call([observer], "on_my_method", "arg1", "arg2"); expect(observer.on_notify).wasCalledWith("on_my_method", "arg1", "arg2"); }); }); describe("an observer listening to the notify method", function() { var observer; beforeEach(function() { observer = { on_my_method: function() { } }; Jelly.attach(observer); expect(Jelly.observers).toContain(observer); }); describe("when the observer does not have a detach method defined", function() { beforeEach(function() { expect(observer.detach).toBe(undefined); }); it("leaves the observer in Jelly.observers and calls the notify method on the observer", function() { spyOn(observer, "on_my_method"); Jelly.notifyObservers("on_my_method"); expect(Jelly.observers).toContain(observer); expect(observer.on_my_method).wasCalled(); }); }); describe("when the observer a detach method defined", function() { describe("when the detach method is truthy", function() { var anotherObserver; beforeEach(function() { observer.detach = function() { return true; }; anotherObserver = { on_my_method: function() { } }; Jelly.attach(anotherObserver); }); it("removes observer in Jelly.observers, does not call the notify method on the observer, and calls the other observers", function() { spyOn(observer, "on_my_method"); spyOn(anotherObserver, "on_my_method"); Jelly.notifyObservers("on_my_method"); expect(Jelly.observers).toNotContain(observer); expect(observer.on_my_method).wasNotCalled(); expect(anotherObserver.on_my_method).wasCalled(); }); }); describe("when the detach method is falsy", function() { beforeEach(function() { observer.detach = function() { return undefined; } }); it("leaves the observer in Jelly.observers and calls the notify method on the observer", function() { spyOn(observer, "on_my_method"); Jelly.notifyObservers("on_my_method"); expect(Jelly.observers).toContain(observer); expect(observer.on_my_method).wasCalled(); }); }); }); }); }); }); describe("Jelly.Page", function() { var our_token; beforeEach(function() { spyOn($, 'ajax'); our_token = "authenticity token"; window._token = our_token; Jelly.init(); }); describe(".init", function() { beforeEach(function() { Jelly.Pages.add("DefinedComponent", { baz : function() { }, all : function() { }, show: function() { } }); spyOn(Jelly.Pages.all["DefinedComponent"], "show"); }); afterEach(function() { delete Jelly.Pages.all["DefinedComponent"]; }); describe("when the passed-in controllerName is defined", function() { describe("when the actionName is defined", function() { it("invokes the page-specific method", function() { var foobar = Jelly.Pages.all["DefinedComponent"]; expect(foobar.show).wasNotCalled(); Jelly.Page.init("DefinedComponent", "show"); expect(foobar.show).wasCalled(); }); describe("when the 'all' method is defined", function() { var invokedMethods; beforeEach(function() { invokedMethods = []; spyOn(Jelly.Pages.all["DefinedComponent"], "all").andCallFake(function() { invokedMethods.push("all"); }); spyOn(Jelly.Pages.all["DefinedComponent"], "baz").andCallFake(function() { invokedMethods.push("baz"); }); }); it("invokes the all method before invoking the page-specific method", function() { expect(invokedMethods).toEqual([]); Jelly.Page.init("DefinedComponent", "baz"); expect(invokedMethods).toEqual(['all', 'baz']); }); }); }); describe("when the actionName is not defined", function() { it("does not blow up", function() { expect(Jelly.Pages.all["DefinedComponent"].easterBunny).toEqual(undefined); Jelly.Page.init("DefinedComponent", "easterBunny"); }); describe("when the 'all' method is defined", function() { var invokedMethods; beforeEach(function() { invokedMethods = []; Jelly.Pages.all["DefinedComponent"].all = function() { invokedMethods.push("all"); }; }); it("invokes the all method", function() { expect(Jelly.Pages.all["DefinedComponent"].easterBunny).toEqual(undefined); expect(invokedMethods).toEqual([]); Jelly.Page.init("DefinedComponent", "easterBunny"); expect(invokedMethods).toEqual(['all']); }); }); }); }); describe("when the passed-in controllerName is not defined", function() { it("does nothing and does not cause an error", function() { expect(Jelly.Pages.all["UndefinedComponent"]).toEqual(undefined); Jelly.Page.init("UndefinedComponent", "easterBunny"); }); }); }); }); describe("Jelly.Location", function() { var our_token, originalTop; beforeEach(function() { spyOn($, 'ajax'); our_token = "authenticity token"; window._token = our_token; Jelly.init(); originalTop = window.top; }); afterEach(function() { window.top = originalTop; }); describe(".on_redirect", function() { it("sets top.location.href to the given location", function() { var window = {top: {location: {}}}; spyOn(Jelly.Location, "window").andReturn(window); Jelly.Location.on_redirect("http://mars.com"); expect(window.top.location.href).toEqual("http://mars.com"); }); }); });