diff --git a/platform/core/bundle.json b/platform/core/bundle.json index dcfbaaeb24..6cb0f329f6 100644 --- a/platform/core/bundle.json +++ b/platform/core/bundle.json @@ -67,7 +67,7 @@ "provides": "viewService", "type": "provider", "implementation": "views/ViewProvider.js", - "depends": [ "views[]" ] + "depends": [ "views[]", "$log" ] } ], "types": [ diff --git a/platform/core/src/views/ViewProvider.js b/platform/core/src/views/ViewProvider.js index bc6bd36348..f6caec903a 100644 --- a/platform/core/src/views/ViewProvider.js +++ b/platform/core/src/views/ViewProvider.js @@ -37,7 +37,25 @@ define( * @constructor * @param {View[]} an array of view definitions */ - function ViewProvider(views) { + function ViewProvider(views, $log) { + + // Views without defined keys cannot be used in the user + // interface, and can result in unexpected behavior. These + // are filtered out using this function. + function validate(view) { + var key = view.key; + + // Leave a log message to support detection of this issue. + if (!key) { + $log.warn([ + "No key specified for view in ", + (view.bundle || {}).path, + "; omitting this view." + ].join("")); + } + + return key; + } // Check if an object has all capabilities designated as `needs` // for a view. Exposing a capability via delegation is taken to @@ -79,6 +97,9 @@ define( }); } + // Filter out any key-less views + views = views.filter(validate); + return { /** * Get all views which are applicable to this domain object. diff --git a/platform/core/test/views/ViewProviderSpec.js b/platform/core/test/views/ViewProviderSpec.js index 7c70f7c937..47d44d0058 100644 --- a/platform/core/test/views/ViewProviderSpec.js +++ b/platform/core/test/views/ViewProviderSpec.js @@ -25,6 +25,7 @@ define( delegates = {}, delegation, mockDomainObject = {}, + mockLog, provider; beforeEach(function () { @@ -38,6 +39,7 @@ define( mockDomainObject.useCapability = function (c, v) { return capabilities[c] && capabilities[c].invoke(v); }; + mockLog = jasmine.createSpyObj("$log", ["warn", "info", "debug"]); capabilities = {}; delegates = {}; @@ -48,7 +50,7 @@ define( } }; - provider = new ViewProvider([viewA, viewB, viewC]); + provider = new ViewProvider([viewA, viewB, viewC], mockLog); }); it("reports views provided as extensions", function () { @@ -71,6 +73,20 @@ define( .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)); + }); + }); } ); \ No newline at end of file diff --git a/platform/features/plot/bundle.json b/platform/features/plot/bundle.json index 8f5e134e57..b9d21894bb 100644 --- a/platform/features/plot/bundle.json +++ b/platform/features/plot/bundle.json @@ -4,6 +4,7 @@ "views": [ { "name": "Plot", + "key": "plot", "templateUrl": "templates/plot.html", "needs": [ "telemetry" ], "delegation": true