brisket/brisket

View on GitHub
spec/client/viewing/CloseableViewSpec.js

Summary

Maintainability
C
7 hrs
Test Coverage
"use strict";

describe("CloseableView", function() {
    var CloseableView = require("../../../lib/viewing/CloseableView");
    var HasChildViews = require("../../../lib/viewing/HasChildViews");
    var Backbone = require("../../../lib/application/Backbone");
    var _ = require("underscore");

    var ViewThatCloses;
    var view;

    describe("#close", function() {

        beforeEach(function() {
            ViewThatCloses = Backbone.View.extend(_.extend({}, CloseableView));
            view = new ViewThatCloses();

            spyOn(view, "onClose");
            spyOn(view, "trigger");
            spyOn(view, "remove");
            spyOn(view, "unbind");

            view.close();
        });

        it("removes the view from the DOM", function() {
            expect(view.remove).toHaveBeenCalled();
        });

        it("unbinds all events that were listening to the view", function() {
            expect(view.unbind).toHaveBeenCalled();
        });

        it("alerts listeners that close is happening", function() {
            expect(view.trigger).toHaveBeenCalledWith("close");
        });

        it("calls view's onClose handler", function() {
            expect(view.onClose).toHaveBeenCalled();
        });

        describe("when the view does NOT extend HasChildViews", function() {

            beforeEach(function() {
                view = new ViewThatCloses();
            });

            it("does NOT have the method hasChildViews", function() {
                expect(view.hasChildViews).not.toBeDefined();
            });

        });

        describe("when the view extends HasChildViews", function() {

            beforeEach(function() {
                var ParentView = ViewThatCloses.extend(HasChildViews);
                view = new ParentView();
                spyOn(view, "closeChildViews").and.callThrough();
            });

            describe("when the view has child views", function() {

                beforeEach(function() {
                    view.createChildView(ViewThatCloses);
                });

                it("ensures that all child views will be closed", function() {
                    view.close();

                    expect(view.closeChildViews).toHaveBeenCalled();
                });

                describe("when closeChildViews was called explicitly", function() {

                    beforeEach(function() {
                        view.closeChildViews();
                    });

                    it("avoids calling closeChildViews again", function() {
                        view.close();

                        expect(view.closeChildViews.calls.count()).toBe(1);
                    });

                });

            });

            describe("when the view does NOT have any child view", function() {

                beforeEach(function() {
                    view.close();
                });

                it("does NOT call closeChildViews", function() {
                    expect(view.closeChildViews).not.toHaveBeenCalled();
                });

            });

        });

        describe("when there is an error in onClose", function() {
            var error;
            var catchError;

            beforeEach(function() {
                error = new Error();
                catchError = jasmine.createSpy();

                view.onClose.and.callFake(function() {
                    throw error;
                });

                spyOn(console, "error");

                try {
                    view.close();
                } catch (e) {
                    catchError(e);
                }
            });

            it("still removes the view from the DOM", function() {
                expect(view.remove).toHaveBeenCalled();
            });

            it("still unbinds all events that were listening to the view", function() {
                expect(view.unbind).toHaveBeenCalled();
            });

            it("reports the error to the console", function() {
                expect(console.error).toHaveBeenCalledWith(
                    "Error: There is an error in an onClose callback.\n" +
                    "View with broken onClose is: " + view
                );
            });

            it("rethrows error", function() {
                expect(catchError).toHaveBeenCalledWith(error);
            });

        });

    });

    describe("#closeAsChild", function() {

        beforeEach(function() {
            ViewThatCloses = Backbone.View.extend(_.extend({}, CloseableView));
            view = new ViewThatCloses();

            spyOn(view, "onClose");
            spyOn(view, "trigger");
            spyOn(view, "remove");
            spyOn(view, "unbind");
            spyOn(view, "stopListening");
            spyOn(view.$el, "off");
            spyOn(view.$el, "removeData");

            view.closeAsChild();
        });

        it("does NOT remove the view from the DOM", function() {
            expect(view.remove).not.toHaveBeenCalled();
        });

        it("unbinds all events that were listening to the view", function() {
            expect(view.unbind).toHaveBeenCalled();
        });

        it("alerts listeners that close is happening", function() {
            expect(view.trigger).toHaveBeenCalledWith("close");
        });

        it("calls view's onClose handler", function() {
            expect(view.onClose).toHaveBeenCalled();
        });

        it("stops listening to new events", function() {
            expect(view.stopListening).toHaveBeenCalled();
        });

        it("unbinds all the view's $el's events", function() {
            expect(view.$el.off).toHaveBeenCalled();
        });

        it("removes all jquery data associated with view's el", function() {
            expect(view.$el.removeData).toHaveBeenCalled();
        });

        describe("when there is an error in onClose", function() {
            var error;
            var catchError;

            beforeEach(function() {
                error = new Error();
                catchError = jasmine.createSpy();

                view.onClose.and.callFake(function() {
                    throw error;
                });

                spyOn(console, "error");

                try {
                    view.closeAsChild();
                } catch (e) {
                    catchError(e);
                }
            });

            it("does NOT remove the view from the DOM", function() {
                expect(view.remove).not.toHaveBeenCalled();
            });

            it("still unbinds all events that were listening to the view", function() {
                expect(view.unbind).toHaveBeenCalled();
            });

            it("stops listening to new events", function() {
                expect(view.stopListening).toHaveBeenCalled();
            });

            it("reports the error to the console", function() {
                expect(console.error).toHaveBeenCalledWith(
                    "Error: There is an error in an onClose callback.\n" +
                    "View with broken onClose is: " + view
                );
            });

            it("rethrows error", function() {
                expect(catchError).toHaveBeenCalledWith(error);
            });

        });

    });

});

// ----------------------------------------------------------------------------
// Copyright (C) 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ----------------------------- END-OF-FILE ----------------------------------