3 describe('Factory: LayoutStorage', function () {
6 beforeEach(function () {
7 angular.module('ui.sortable', []);
10 // load the service's module
11 beforeEach(module('ui.dashboard'));
13 // instantiate service
15 beforeEach(inject(function (_LayoutStorage_) {
16 LayoutStorage = _LayoutStorage_;
19 describe('the constructor', function() {
23 beforeEach(function() {
25 storageId: 'testingStorage',
27 setItem: function(key, value) {
30 getItem: function(key) {
33 removeItem: function(key) {
37 storageHash: 'ds5f9d1f',
38 stringifyStorage: true,
49 settingsModalOptions: {},
50 onSettingsClose: function() {
53 onSettingsDismiss: function() {
57 storage = new LayoutStorage(options);
60 it('should provide an empty implementation of storage if it is not provided', function() {
61 delete options.storage;
62 var stateless = new LayoutStorage(options);
63 var noop = stateless.storage;
64 angular.forEach(['setItem', 'getItem', 'removeItem'], function(method) {
65 expect(typeof noop[method]).toEqual('function');
66 expect(noop[method]).not.toThrow();
71 it('should set a subset of the options directly on the LayoutStorage instance itself', function() {
75 storageHash: 'storageHash',
76 stringifyStorage: 'stringifyStorage',
77 widgetDefinitions: 'widgetDefinitions',
78 defaultLayouts: 'defaultLayouts',
79 widgetButtons: 'widgetButtons',
80 explicitSave: 'explicitSave',
81 settingsModalOptions: 'settingsModalOptions',
82 onSettingsClose: 'onSettingsClose',
83 onSettingsDismiss: 'onSettingsDismiss'
86 angular.forEach(properties, function(val, key) {
87 expect( storage[key] ).toEqual( options[val] );
92 it('should set stringify as true by default', function() {
93 delete options.stringifyStorage;
94 storage = new LayoutStorage(options);
95 expect(storage.stringifyStorage).toEqual(true);
98 it('should allow stringify to be overridden by option', function() {
99 options.stringifyStorage = false;
100 storage = new LayoutStorage(options);
101 expect(storage.stringifyStorage).toEqual(false);
104 it('should create a layouts array and states object', function() {
105 expect(storage.layouts instanceof Array).toEqual(true);
106 expect(typeof storage.states).toEqual('object');
109 it('should call load', function() {
110 spyOn(LayoutStorage.prototype, 'load');
111 storage = new LayoutStorage(options);
112 expect(LayoutStorage.prototype.load).toHaveBeenCalled();
117 describe('the load method', function() {
119 var options, storage;
121 beforeEach(function() {
123 storageId: 'testingStorage',
125 setItem: function(key, value) {
128 getItem: function(key) {
131 removeItem: function(key) {
135 storageHash: 'ds5f9d1f',
136 stringifyStorage: true,
141 {title: 'something'},
142 {title: 'something'},
145 widgetButtons: false,
148 storage = new LayoutStorage(options);
151 it('should use the default layouts if no stored info was found', function() {
152 expect(storage.layouts.length).toEqual(options.defaultLayouts.length);
155 it('should clone default layouts rather than use them directly', function() {
156 expect(storage.layouts.indexOf(options.defaultLayouts[0])).toEqual(-1);
159 it('should use the result from getItem for layouts.', function() {
160 spyOn(options.storage, 'getItem').and.returnValue(JSON.stringify({
161 storageHash: 'ds5f9d1f',
163 { id: 0, title: 'title', defaultWidgets: [], active: true },
164 { id: 1, title: 'title2', defaultWidgets: [], active: false },
165 { id: 2, title: 'title3', defaultWidgets: [], active: false },
166 { id: 3, title: 'custom', defaultWidgets: [], active: false }
175 expect(storage.layouts.map(function(l) {return l.title})).toEqual(['title', 'title2', 'title3', 'custom']);
178 it('should NOT use result from getItem for layouts if the storageHash doesnt match', function() {
179 spyOn(options.storage, 'getItem').and.returnValue(JSON.stringify({
180 storageHash: 'alskdjf02iej',
182 { id: 0, title: 'title', defaultWidgets: [], active: true },
183 { id: 1, title: 'title2', defaultWidgets: [], active: false },
184 { id: 2, title: 'title3', defaultWidgets: [], active: false },
185 { id: 3, title: 'custom', defaultWidgets: [], active: false }
194 expect(storage.layouts.map(function(l) {return l.title})).toEqual(['something', 'something', 'something']);
197 it('should be able to handle async loading via promise', inject(function($rootScope,$q) {
198 var deferred = $q.defer();
199 spyOn(options.storage, 'getItem').and.returnValue(deferred.promise);
201 expect(storage.layouts).toEqual([]);
202 deferred.resolve(JSON.stringify({
203 storageHash: 'ds5f9d1f',
205 { id: 0, title: 'title', defaultWidgets: [], active: true },
206 { id: 1, title: 'title2', defaultWidgets: [], active: false },
207 { id: 2, title: 'title3', defaultWidgets: [], active: false },
208 { id: 3, title: 'custom', defaultWidgets: [], active: false }
217 expect(storage.layouts.map(function(l) {return l.title})).toEqual(['title', 'title2', 'title3', 'custom']);
220 it('should load defaults if the deferred is rejected', inject(function($rootScope,$q) {
221 var deferred = $q.defer();
222 spyOn(options.storage, 'getItem').and.returnValue(deferred.promise);
226 expect(storage.layouts.map(function(l) {return l.title})).toEqual(['something', 'something', 'something']);
229 it('should load defaults if the json is malformed', inject(function($rootScope,$q) {
230 var deferred = $q.defer();
231 spyOn(options.storage, 'getItem').and.returnValue(deferred.promise);
233 expect(storage.layouts).toEqual([]);
234 deferred.resolve(JSON.stringify({
235 storageHash: 'ds5f9d1f',
237 { id: 0, title: 'title', defaultWidgets: [], active: true },
238 { id: 1, title: 'title2', defaultWidgets: [], active: false },
239 { id: 2, title: 'title3', defaultWidgets: [], active: false },
240 { id: 3, title: 'custom', defaultWidgets: [], active: false }
247 }).replace('{','{{'));
249 expect(storage.layouts.map(function(l) {return l.title})).toEqual(['something', 'something', 'something']);
252 it('should not try to JSON.parse the result if stringifyStorage is false.', function() {
253 options.stringifyStorage = false;
254 storage = new LayoutStorage(options);
255 spyOn(options.storage, 'getItem').and.returnValue({
256 storageHash: 'ds5f9d1f',
258 { id: 0, title: 'title', defaultWidgets: [], active: true },
259 { id: 1, title: 'title2', defaultWidgets: [], active: false },
260 { id: 2, title: 'title3', defaultWidgets: [], active: false },
261 { id: 3, title: 'custom', defaultWidgets: [], active: false }
270 expect(storage.layouts.map(function(l) {return l.title})).toEqual(['title', 'title2', 'title3', 'custom']);
275 describe('the add method', function() {
277 var storage, options;
279 beforeEach(function() {
281 storageId: 'testingStorage',
283 setItem: function(key, value) {
286 getItem: function(key) {
289 removeItem: function(key) {
293 storageHash: 'ds5f9d1f',
294 stringifyStorage: true,
299 widgetButtons: false,
303 spyOn(LayoutStorage.prototype, 'load' );
305 storage = new LayoutStorage(options);
309 it('should add to storage.layouts', function() {
310 var newLayout = { title: 'my-layout' };
311 storage.add(newLayout);
312 expect(storage.layouts[0]).toEqual(newLayout);
315 it('should be able to take an array of new layouts', function() {
316 var newLayouts = [ { title: 'my-layout' }, { title: 'my-layout-2' } ];
317 storage.add(newLayouts);
318 expect(storage.layouts.length).toEqual(2);
319 expect(storage.layouts.indexOf(newLayouts[0])).not.toEqual(-1);
320 expect(storage.layouts.indexOf(newLayouts[1])).not.toEqual(-1);
323 it('should look for defaultWidgets on storage options if not supplied on layout definition', function() {
324 options.defaultWidgets = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
325 storage = new LayoutStorage(options);
327 var newLayouts = [ { title: 'my-layout', defaultWidgets: [] }, { title: 'my-layout-2' } ];
328 storage.add(newLayouts);
329 expect(newLayouts[0].dashboard.defaultWidgets === newLayouts[0].defaultWidgets).toEqual(true);
330 expect(newLayouts[1].dashboard.defaultWidgets === options.defaultWidgets).toEqual(true);
333 it('should use defaultWidgets if supplied in the layout definition', function() {
334 options.defaultWidgets = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
335 storage = new LayoutStorage(options);
337 var newLayouts = [ { title: 'my-layout', defaultWidgets: [] }, { title: 'my-layout-2' } ];
338 storage.add(newLayouts);
339 expect(newLayouts[0].dashboard.defaultWidgets).toEqual([]);
340 expect(newLayouts[1].dashboard.defaultWidgets).toEqual(options.defaultWidgets);
343 it('should look for widgetDefinitions on storage options if not supplied on layout definition', function() {
344 options.widgetDefinitions = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
345 storage = new LayoutStorage(options);
347 var newLayouts = [ { title: 'my-layout', widgetDefinitions: [] }, { title: 'my-layout-2' } ];
348 storage.add(newLayouts);
349 expect(newLayouts[0].dashboard.widgetDefinitions === newLayouts[0].widgetDefinitions).toEqual(true);
350 expect(newLayouts[1].dashboard.widgetDefinitions === options.widgetDefinitions).toEqual(true);
353 it('should use widgetDefinitions if supplied in the layout definition', function() {
354 options.widgetDefinitions = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
355 storage = new LayoutStorage(options);
357 var newLayouts = [ { title: 'my-layout', widgetDefinitions: [] }, { title: 'my-layout-2' } ];
358 storage.add(newLayouts);
359 expect(newLayouts[0].dashboard.widgetDefinitions).toEqual([]);
360 expect(newLayouts[1].dashboard.widgetDefinitions).toEqual(options.widgetDefinitions);
365 describe('the remove method', function() {
367 var storage, options;
369 beforeEach(function() {
371 storageId: 'testingStorage',
372 storageHash: 'ds5f9d1f',
373 stringifyStorage: true,
381 { title: '2', active: true },
384 widgetButtons: false,
388 storage = new LayoutStorage(options);
391 it('should remove the supplied layout', function() {
392 var layout = storage.layouts[1];
393 storage.remove(layout);
394 expect(storage.layouts.indexOf(layout)).toEqual(-1);
397 it('should delete the state', function() {
398 var layout = storage.layouts[1];
399 storage.setItem(layout.id, {});
400 storage.remove(layout);
401 expect(storage.states[layout.id]).toBeUndefined();
404 it('should do nothing if layout is not in layouts', function() {
406 var before = storage.layouts.length;
407 storage.remove(layout);
408 var after = storage.layouts.length;
409 expect(before).toEqual(after);
412 it('should set another dashboard to active if the layout removed was active', function() {
413 var layout = storage.layouts[1];
414 storage.remove(layout);
415 expect(storage.layouts[0].active || storage.layouts[1].active).toEqual(true);
418 it('should set the layout at index 0 to active if the removed layout was 0', function() {
419 storage.layouts[1].active = false;
420 storage.layouts[0].active = true;
421 storage.remove(storage.layouts[0]);
422 expect(storage.layouts[0].active).toEqual(true);
425 it('should not change the active layout if it was not the one that got removed', function() {
426 var active = storage.layouts[1];
427 var layout = storage.layouts[0];
428 storage.remove(layout);
429 expect(active.active).toEqual(true);
434 describe('the save method', function() {
436 var options, storage;
438 beforeEach(function() {
440 storageId: 'testingStorage',
442 setItem: function(key, value) {
445 getItem: function(key) {
448 removeItem: function(key) {
452 storageHash: 'ds5f9d1f',
453 stringifyStorage: true,
458 {title: 'something'},
459 {title: 'something'},
462 widgetButtons: false,
465 storage = new LayoutStorage(options);
468 it('should call options.storage.setItem with a stringified object', function() {
469 spyOn(options.storage, 'setItem' );
471 expect(options.storage.setItem).toHaveBeenCalled();
472 expect(options.storage.setItem.calls.argsFor(0)[0]).toEqual(storage.id);
473 expect(typeof options.storage.setItem.calls.argsFor(0)[1]).toEqual('string');
475 JSON.parse(options.storage.setItem.calls.argsFor(0)[1]);
479 it('should save an object that has layouts, states, and storageHash', function() {
480 spyOn(options.storage, 'setItem' );
482 var obj = JSON.parse(options.storage.setItem.calls.argsFor(0)[1]);
483 expect(obj.hasOwnProperty('layouts')).toEqual(true);
484 expect(obj.layouts instanceof Array).toEqual(true);
485 expect(obj.hasOwnProperty('states')).toEqual(true);
486 expect(typeof obj.states).toEqual('object');
487 expect(obj.hasOwnProperty('storageHash')).toEqual(true);
488 expect(typeof obj.storageHash).toEqual('string');
491 it('should call options.storage.setItem with an object when stringifyStorage is false', function() {
492 options.stringifyStorage = false;
493 storage = new LayoutStorage(options);
494 spyOn(options.storage, 'setItem' );
496 expect(options.storage.setItem).toHaveBeenCalled();
497 expect(options.storage.setItem.calls.argsFor(0)[0]).toEqual(storage.id);
498 expect(typeof options.storage.setItem.calls.argsFor(0)[1]).toEqual('object');
503 describe('the setItem method', function() {
505 var options, storage;
507 beforeEach(function() {
509 storageId: 'testingStorage',
511 setItem: function(key, value) {
514 getItem: function(key) {
517 removeItem: function(key) {
521 storageHash: 'ds5f9d1f',
522 stringifyStorage: true,
527 {title: 'something'},
528 {title: 'something'},
531 widgetButtons: false,
534 storage = new LayoutStorage(options);
537 it('should set storage.states[id] to the second argument', function() {
538 var state = { some: 'thing'};
539 storage.setItem('id', state);
540 expect(storage.states.id).toEqual(state);
543 it('should call save', function() {
544 spyOn(storage, 'save');
545 var state = { some: 'thing'};
546 storage.setItem('id', state);
547 expect(storage.save).toHaveBeenCalled();
552 describe('the getItem method', function() {
554 var options, storage;
556 beforeEach(function() {
558 storageId: 'testingStorage',
560 setItem: function(key, value) {
563 getItem: function(key) {
566 removeItem: function(key) {
570 storageHash: 'ds5f9d1f',
571 stringifyStorage: true,
576 {title: 'something'},
577 {title: 'something'},
580 widgetButtons: false,
583 storage = new LayoutStorage(options);
586 it('should return states[id]', function() {
587 storage.states['myId'] = {};
588 var result = storage.getItem('myId');
589 expect(result === storage.states['myId']).toEqual(true);
594 describe('the getActiveLayout method', function() {
595 var options, storage;
597 beforeEach(function() {
599 storageId: 'testingStorage',
601 setItem: function(key, value) {
604 getItem: function(key) {
607 removeItem: function(key) {
611 storageHash: 'ds5f9d1f',
612 stringifyStorage: true,
617 {title: 'i am active', active: true},
619 {title: 'me neither'}
621 widgetButtons: false,
624 storage = new LayoutStorage(options);
627 it('should return the layout with active:true', function() {
628 var layout = storage.getActiveLayout();
629 expect(layout.title).toEqual('i am active');
632 it('should return false if no layout is active', function() {
633 var layout = storage.getActiveLayout();
634 layout.active = false;
635 var result = storage.getActiveLayout();
636 expect(result).toEqual(false);
641 describe('the removeItem', function() {
643 var options, storage;
645 beforeEach(function() {
647 storageId: 'testingStorage',
649 setItem: function(key, value) {
652 getItem: function(key) {
655 removeItem: function(key) {
659 storageHash: 'ds5f9d1f',
660 stringifyStorage: true,
665 {title: 'i am active', active: true},
667 {title: 'me neither'}
669 widgetButtons: false,
672 storage = new LayoutStorage(options);
675 it('should remove states[id]', function() {
677 storage.setItem('1', state);
678 storage.removeItem('1');
679 expect(storage.states['1']).toBeUndefined();
682 it('should call save', function() {
683 spyOn(storage, 'save');
685 storage.setItem('1', state);
686 storage.removeItem('1');
687 expect(storage.save).toHaveBeenCalled();