/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/

/**
 * ViewProviderSpec. Created by vwoeltje on 11/6/14.
 */
define(
    ["../../src/views/ViewProvider"],
    function (ViewProvider) {
        "use strict";

        describe("The view provider", function () {
            var viewA = {
                    key: "a"
                },
                viewB = {
                    key: "b",
                    needs: [ "someCapability" ]
                },
                viewC = {
                    key: "c",
                    needs: [ "someCapability" ],
                    delegation: true
                },
                capabilities = {},
                delegates = {},
                delegation,
                mockDomainObject = {},
                mockLog,
                provider;

            beforeEach(function () {
                // Simulate the expected API
                mockDomainObject.hasCapability = function (c) {
                    return capabilities[c] !== undefined;
                };
                mockDomainObject.getCapability = function (c) {
                    return capabilities[c];
                };
                mockDomainObject.useCapability = function (c, v) {
                    return capabilities[c] && capabilities[c].invoke(v);
                };
                mockLog = jasmine.createSpyObj("$log", ["warn", "info", "debug"]);

                capabilities = {};
                delegates = {};

                delegation = {
                    doesDelegateCapability: function (c) {
                        return delegates[c] !== undefined;
                    }
                };

                provider = new ViewProvider([viewA, viewB, viewC], mockLog);
            });

            it("reports views provided as extensions", function () {
                capabilities.someCapability = true;
                expect(provider.getViews(mockDomainObject))
                    .toEqual([viewA, viewB, viewC]);
            });

            it("filters views by needed capabilities", function () {
                //capabilities.someCapability = true;
                expect(provider.getViews(mockDomainObject))
                    .toEqual([viewA]);
            });

            it("allows delegation of needed capabilities when specified", function () {
                //capabilities.someCapability = true;
                capabilities.delegation = delegation;
                delegates.someCapability = true;
                expect(provider.getViews(mockDomainObject))
                    .toEqual([viewA, viewC]);
            });

            it("warns if keys are omitted from views", function () {
                // Verify that initial construction issued no warning
                expect(mockLog.warn).not.toHaveBeenCalled();
                // Recreate with no keys; that view should be filtered out
                expect(
                    new ViewProvider(
                        [viewA, { some: "bad view" }],
                        mockLog
                    ).getViews(mockDomainObject)
                ).toEqual([viewA]);
                // We should have also received a warning, to support debugging
                expect(mockLog.warn).toHaveBeenCalledWith(jasmine.any(String));
            });

            it("restricts typed views to matching types", function () {
                var testType = "testType",
                    testView = { key: "x", type: testType },
                    provider = new ViewProvider([testView], mockLog);

                // Include a "type" capability
                capabilities.type = jasmine.createSpyObj(
                    "type",
                    ["instanceOf", "invoke", "getDefinition"]
                );
                capabilities.type.invoke.andReturn(capabilities.type);

                // Should be included when types match
                capabilities.type.instanceOf.andReturn(true);
                expect(provider.getViews(mockDomainObject))
                    .toEqual([testView]);
                expect(capabilities.type.instanceOf)
                    .toHaveBeenCalledWith(testType);

                // ...but not when they don't
                capabilities.type.instanceOf.andReturn(false);
                expect(provider.getViews(mockDomainObject))
                    .toEqual([]);

            });

            it("enforces view restrictions from types", function () {
                var testType = "testType",
                    testView = { key: "x" },
                    provider = new ViewProvider([testView], mockLog);

                // Include a "type" capability
                capabilities.type = jasmine.createSpyObj(
                    "type",
                    ["instanceOf", "invoke", "getDefinition"]
                );
                capabilities.type.invoke.andReturn(capabilities.type);

                // Should be included when view keys match
                capabilities.type.getDefinition
                    .andReturn({ views: [testView.key]});
                expect(provider.getViews(mockDomainObject))
                    .toEqual([testView]);

                // ...but not when they don't
                capabilities.type.getDefinition
                    .andReturn({ views: ["somethingElse"]});
                expect(provider.getViews(mockDomainObject))
                    .toEqual([]);
            });

        });
    }
);