8533a2110342d0e5dfa169c953fb85d866383787
[portal/sdk.git] /
1 /*
2  * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 'use strict';
18
19 describe('Directive: dashboard-layouts', function () {
20
21   var $rootScope, element, options, childScope, DashboardState, LayoutStorage, $mockModal, $mockTimeout, toFn;
22
23   // mock UI Sortable
24   beforeEach(function () {
25     angular.module('ui.sortable', []);
26   });
27
28   // load the directive's module
29   beforeEach(module('ui.dashboard', function($provide) {
30     $mockModal = {
31       open: function() {}
32     };
33     $mockTimeout = function(fn, delay) {
34       toFn = fn;
35     };
36     $provide.value('$uibModal', $mockModal);
37     $provide.value('$timeout', $mockTimeout);
38   }));
39
40   beforeEach(inject(function ($compile, _$rootScope_, _DashboardState_, _LayoutStorage_) {
41     // services
42     $rootScope = _$rootScope_;
43     DashboardState = _DashboardState_;
44     LayoutStorage = _LayoutStorage_;
45
46     // options
47     var widgetDefinitions = [
48       {
49         name: 'wt-one',
50         template: '<div class="wt-one-value">{{2 + 2}}</div>'
51       },
52       {
53         name: 'wt-two',
54         template: '<span class="wt-two-value">{{value}}</span>'
55       }
56     ];
57     var defaultWidgets = _.clone(widgetDefinitions);
58     $rootScope.dashboardOptions = options = {
59       widgetButtons: true,
60       widgetDefinitions: widgetDefinitions,
61       defaultLayouts: [
62         {
63           title: 'first',
64           active: true,
65           defaultWidgets: defaultWidgets
66         },
67         {
68           title: 'second',
69           active: false,
70           defaultWidgets: defaultWidgets
71         }
72       ],
73       defaultWidgets: defaultWidgets,
74       storage: {
75         setItem: function(key, val) {
76
77         },
78         getItem: function(key) {
79
80         },
81         removeItem: function(key) {
82
83         }
84       }
85     };
86     $rootScope.value = 10;
87
88     // element setup 
89     element = $compile('<div dashboard-layouts="dashboardOptions"></div>')($rootScope);
90     $rootScope.$digest();
91     childScope = element.scope();
92   }));
93
94   it('should not require storage', inject(function($compile) {
95     delete $rootScope.dashboardOptions.storage;
96     expect(function() {
97       var noStorageEl = $compile('<div dashboard-layouts="dashboardOptions"></div>')($rootScope);
98       $rootScope.$digest();
99     }).not.toThrow();
100
101   }));
102
103   it('should be able to use a different dashboard-layouts template', inject(function($compile, $templateCache) {
104     $templateCache.put(
105       'myCustomTemplate.html',
106       '<ul class="my-custom-tabs layout-tabs">' +
107       '<li ng-repeat="layout in layouts" ng-class="{ active: layout.active }">' +
108       '<a ng-click="makeLayoutActive(layout)">' +
109       '<span ng-dblclick="editTitle(layout)" ng-show="!layout.editingTitle">{{layout.title}}</span>' +
110       '<form action="" class="layout-title" ng-show="layout.editingTitle" ng-submit="saveTitleEdit(layout)">' +
111       '<input type="text" ng-model="layout.title" class="form-control" data-layout="{{layout.id}}">' +
112       '</form>' +
113       '<span ng-click="removeLayout(layout)" class="glyphicon glyphicon-remove icon-erase"></span>' +
114       '<!-- <span class="glyphicon glyphicon-pencil"></span> -->' +
115       '<!-- <span class="glyphicon glyphicon-remove"></span> -->' +
116       '</a>' +
117       '</li>' +
118       '<li>' +
119       '<a ng-click="createNewLayout()">' +
120       '<span class="glyphicon glyphicon-plus"></span>' +
121       '</a>' +
122       '</li>' +
123       '</ul>' +
124       '<div ng-repeat="layout in layouts | filter:isActive" dashboard="layout.dashboard" templateUrl="template/dashboard.html"></div>'
125     );
126     var customElement = $compile('<div dashboard-layouts="dashboardOptions" template-url="myCustomTemplate.html"></div>')($rootScope);
127     $rootScope.$digest();
128     expect(customElement.find('ul.my-custom-tabs').length).toEqual(1);
129
130   }));
131
132   it('should set the first dashboard to active if there is not one already active', inject(function($compile) {
133     options.defaultLayouts[0].active = options.defaultLayouts[1].active = false;
134     element = $compile('<div dashboard-layouts="dashboardOptions"></div>')($rootScope);
135     $rootScope.$digest();
136     childScope = element.scope();
137     
138     var layouts = childScope.layouts;
139     var active;
140     for (var i = 0; i < layouts.length; i++) {
141       if (layouts[i].active) {
142         active = layouts[i];
143         break;
144       }
145     };
146     expect(active).not.toBeUndefined();
147   }));
148
149   describe('the createNewLayout method', function() {
150
151     it('should call the add and save methods of LayoutStorage', function() {
152       spyOn(LayoutStorage.prototype, 'add');
153       spyOn(LayoutStorage.prototype, 'save');
154
155       childScope.createNewLayout();
156       expect(LayoutStorage.prototype.add).toHaveBeenCalled();
157       expect(LayoutStorage.prototype.save).toHaveBeenCalled();
158     });
159
160     it('should return the newly created layout object', function() {
161       var result = childScope.createNewLayout();
162       expect(typeof result).toEqual('object');
163     });
164
165     it('should set active=true on the newly created layout', function() {
166       var result = childScope.createNewLayout();
167       expect(result.active).toEqual(true);
168     });
169
170     it('should set defaultWidgets to dashboardOptions.defaultWidgets if it is present', function() {
171       var result = childScope.createNewLayout();
172       expect(result.defaultWidgets === options.defaultWidgets).toEqual(true);
173     });
174
175     it('should set defaultWidgets to an empty array if dashboardOptions.defaultWidgets is not present', inject(function($compile) {
176       delete options.defaultWidgets;
177       element = $compile('<div dashboard-layouts="dashboardOptions"></div>')($rootScope);
178       $rootScope.$digest();
179       childScope = element.scope();
180       var result = childScope.createNewLayout();
181       expect(result.defaultWidgets).toEqual([]);
182     }));
183
184   });
185
186   describe('the removeLayout method', function() {
187     
188     it('should call the remove and save methods of LayoutStorage', function() {
189       spyOn(LayoutStorage.prototype, 'remove');
190       spyOn(LayoutStorage.prototype, 'save');
191
192       childScope.removeLayout(childScope.layouts[0]);
193       expect(LayoutStorage.prototype.remove).toHaveBeenCalled();
194       expect(LayoutStorage.prototype.save).toHaveBeenCalled();
195     });
196
197     it('should call remove with the layout it was passed', function() {
198       spyOn(LayoutStorage.prototype, 'remove');
199       var layout = childScope.layouts[0];
200       childScope.removeLayout(layout);
201       expect(LayoutStorage.prototype.remove.calls.argsFor(0)[0]).toEqual(layout);
202     });
203
204   });
205
206   describe('the makeLayoutActive method', function() {
207
208     it('should call _makeLayoutActive if there is not a currently active dashboard with unsaved changes', function() {
209       spyOn(childScope, '_makeLayoutActive');
210       var layout = childScope.layouts[1];
211       childScope.makeLayoutActive(layout);
212       expect(childScope._makeLayoutActive).toHaveBeenCalled();
213     });
214
215     describe('when there are unsaved changes on the current dashboard', function() {
216       
217       var current, options, successCb, errorCb, layout;
218
219       beforeEach(function() {
220         current = childScope.layouts[0];
221         current.dashboard.unsavedChangeCount = 1;
222
223         spyOn($mockModal, 'open').and.callFake(function(arg) {
224           options = arg;
225           return {
226             result: {
227               then: function(success, error) {
228                 successCb = success;
229                 errorCb = error;
230               }
231             }
232           }
233         });
234
235         layout = childScope.layouts[1];
236         childScope.makeLayoutActive(layout);
237       });
238
239       it('should create a modal', function() {
240         expect($mockModal.open).toHaveBeenCalled();
241       });
242
243       it('should resolve layout to the layout to be made active', function() {
244         expect(options.resolve.layout()).toEqual(layout);
245       });
246
247       it('should provide a success callback that saves the current dashboard and then calls _makeLayoutActive', function() {
248         spyOn(current.dashboard, 'saveDashboard');
249         spyOn(childScope, '_makeLayoutActive');
250         successCb();
251         expect(current.dashboard.saveDashboard).toHaveBeenCalled();
252         expect(childScope._makeLayoutActive).toHaveBeenCalled();
253         expect(childScope._makeLayoutActive.calls.argsFor(0)[0]).toEqual(layout);
254       });
255
256       it('should provide an error callback that only calls _makeLayoutActive', function() {
257         spyOn(current.dashboard, 'saveDashboard');
258         spyOn(childScope, '_makeLayoutActive');
259         errorCb();
260         expect(current.dashboard.saveDashboard).not.toHaveBeenCalled();
261         expect(childScope._makeLayoutActive).toHaveBeenCalled();
262         expect(childScope._makeLayoutActive.calls.argsFor(0)[0]).toEqual(layout);
263       });
264
265     });
266
267   });
268
269   describe('the editTitle method', function() {
270
271     it('should set the editingTitle attribute to true on the layout it is passed', function() {
272       var layout = { id: '1' };
273       childScope.editTitle(layout);
274       $rootScope.$digest();
275       expect(layout.editingTitle).toEqual(true);
276       toFn();
277     });
278
279   });
280
281   describe('the saveTitleEdit method', function() {
282
283     it('should set editingTitle to false', function() {
284       var layout = { id: '1' };
285       childScope.saveTitleEdit(layout);
286       expect(layout.editingTitle).toEqual(false);
287     });
288
289     it('should call layoutStorage.save', function() {
290       var layout = { id: '1' };
291       spyOn(LayoutStorage.prototype, 'save').and.callThrough();
292       childScope.saveTitleEdit(layout);
293       expect(LayoutStorage.prototype.save).toHaveBeenCalled();
294     });
295
296   });
297
298   describe('the saveLayouts method', function() {
299
300     it('should call LayoutStorage.save', function() {
301       spyOn(LayoutStorage.prototype, 'save').and.callThrough();
302       $rootScope.dashboardOptions.saveLayouts();
303       expect(LayoutStorage.prototype.save).toHaveBeenCalled();
304     });
305
306     it('should call LayoutStorage.save with true as the first arg', function() {
307       spyOn(LayoutStorage.prototype, 'save').and.callThrough();
308       $rootScope.dashboardOptions.saveLayouts();
309       expect(LayoutStorage.prototype.save.calls.argsFor(0)[0]).toEqual(true);
310     });
311
312   });
313   describe('the proxy methods to active layout', function() {
314
315     var mockDash, galSpy;
316
317     beforeEach(function() {
318       mockDash = {
319         active: true,
320         dashboard: {
321           addWidget: function() {},
322           loadWidgets: function() {},
323           saveDashboard: function() {}
324         }
325       };
326       spyOn(mockDash.dashboard, 'addWidget');
327       spyOn(mockDash.dashboard, 'loadWidgets');
328       spyOn(mockDash.dashboard, 'saveDashboard');
329       galSpy = spyOn(LayoutStorage.prototype, 'getActiveLayout').and;
330       galSpy.returnValue(mockDash);
331     });
332     
333     describe('the addWidget method', function() {
334
335       it('should call dashboard.addWidget method of the active layout', function() {
336         options.addWidget(1,2,3);
337         expect(mockDash.dashboard.addWidget).toHaveBeenCalled();
338         var firstCall = mockDash.dashboard.addWidget.calls.first();
339         expect(firstCall.object).toEqual(mockDash.dashboard);
340         expect(firstCall.args).toEqual([1,2,3]);
341       });
342
343       it('should do nothing if there is no active layout', function() {
344         galSpy.returnValue(null);
345         expect(function() {
346           options.addWidget();
347         }).not.toThrow();
348       });
349
350     });
351
352     describe('the loadWidgets method', function() {
353
354       it('should call dashboard.loadWidgets of the current layout', function() {
355         options.loadWidgets(1,2,3);
356         expect(mockDash.dashboard.loadWidgets).toHaveBeenCalled();
357         var firstCall = mockDash.dashboard.loadWidgets.calls.first();
358         expect(firstCall.object).toEqual(mockDash.dashboard);
359         expect(firstCall.args).toEqual([1,2,3]);
360       });
361
362       it('should do nothing if there is no active layout', function() {
363         galSpy.returnValue(null);
364         expect(function() {
365           options.loadWidgets();
366         }).not.toThrow();
367       });
368
369     });
370
371     describe('the saveDashboard method', function() {
372
373       it('should call dashboard.saveDashboard of the current layout', function() {
374         options.saveDashboard(1,2,3);
375         expect(mockDash.dashboard.saveDashboard).toHaveBeenCalled();
376         var firstCall = mockDash.dashboard.saveDashboard.calls.first();
377         expect(firstCall.object).toEqual(mockDash.dashboard);
378         expect(firstCall.args).toEqual([1,2,3]);
379       });
380
381       it('should do nothing if there is no active layout', function() {
382         galSpy.returnValue(null);
383         expect(function() {
384           options.saveDashboard();
385         }).not.toThrow();
386       });
387
388     });
389
390   });
391
392 });