Merge lp:~benji/juju-gui/bug-1081205 into lp:juju-gui/experimental

Proposed by Benji York
Status: Merged
Approved by: Benji York
Approved revision: 263
Merged at revision: 256
Proposed branch: lp:~benji/juju-gui/bug-1081205
Merge into: lp:juju-gui/experimental
Diff against target: 1823 lines (+808/-847)
10 files modified
app/app.js (+1/-0)
app/models/charm.js (+2/-1)
app/models/models.js (+1/-0)
app/modules-debug.js (+8/-12)
app/store/charm.js (+1/-0)
test/test_app.js (+176/-191)
test/test_app_hotkeys.js (+52/-52)
test/test_model.js (+18/-25)
test/test_notifications.js (+454/-469)
test/test_notifier_widget.js (+95/-97)
To merge this branch: bzr merge lp:~benji/juju-gui/bug-1081205
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+135952@code.launchpad.net

Description of the change

Remove "requires" properties.

The minification process does not read the "requires" parameters defined in
"all-app-debug.js". This parameter should be defined in each custom yui object
that uses some kind of internal or external requirement. The sole use of
"all-app-debug.js" is to define the fullpath of the file that defines a given
module.

This patch removes all the existing "requires" properties from
"all-app-debug.js" and add then where they are needed.

https://codereview.appspot.com/6845084/

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch has already been reviewed separately, I am just landing it.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/app.js'
2--- app/app.js 2012-11-20 14:51:46 +0000
3+++ app/app.js 2012-11-23 17:25:57 +0000
4@@ -746,6 +746,7 @@
5 'juju-models',
6 'juju-views',
7 'juju-controllers',
8+ 'juju-view-charm-search',
9 'io',
10 'json-parse',
11 'app-base',
12
13=== modified file 'app/models/charm.js'
14--- app/models/charm.js 2012-10-29 11:20:31 +0000
15+++ app/models/charm.js 2012-11-23 17:25:57 +0000
16@@ -226,6 +226,7 @@
17 }, '0.1.0', {
18 requires: [
19 'model',
20- 'model-list'
21+ 'model-list',
22+ 'juju-charm-id'
23 ]
24 });
25
26=== modified file 'app/models/models.js'
27--- app/models/models.js 2012-11-16 08:25:02 +0000
28+++ app/models/models.js 2012-11-23 17:25:57 +0000
29@@ -487,6 +487,7 @@
30 'datasource-jsonschema',
31 'io-base',
32 'json-parse',
33+ 'juju-endpoints',
34 'juju-view-utils',
35 'juju-charm-models'
36 ]
37
38=== modified file 'app/modules-debug.js'
39--- app/modules-debug.js 2012-11-15 15:44:00 +0000
40+++ app/modules-debug.js 2012-11-23 17:25:57 +0000
41@@ -1,6 +1,13 @@
42 // This file is used for development only. In order to use it you should call
43 // the "make debug" command. This command passes the "debug" argument to the
44 // "lib/server.js".
45+//
46+// This file declares which files implement modules, using the
47+// "fullpath" property; and declares the membership of rollup modules, using
48+// the "use" property to specify what the module name aliases.
49+//
50+// The "requires" property should not be used here because the javascript
51+// minimizer will not parse it.
52 var GlobalConfig = {
53 filter: 'debug',
54 // Set "true" for verbose logging of YUI
55@@ -93,19 +100,15 @@
56 },
57
58 'juju-charm-models': {
59- requires: ['juju-charm-id'],
60 fullpath: '/juju-ui/models/charm.js'
61 },
62
63 'juju-models': {
64- requires: [
65- 'model', 'model-list', 'juju-endpoints', 'juju-charm-models'],
66 fullpath: '/juju-ui/models/models.js'
67 },
68
69 // Connectivity
70 'juju-env': {
71- requires: ['reconnecting-websocket'],
72 fullpath: '/juju-ui/store/env.js'
73 },
74
75@@ -114,7 +117,6 @@
76 },
77
78 'juju-charm-store': {
79- requires: ['juju-charm-id'],
80 fullpath: '/juju-ui/store/charm.js'
81 },
82
83@@ -125,13 +127,7 @@
84
85 // App
86 'juju-gui': {
87- fullpath: '/juju-ui/app.js',
88- requires: [
89- 'juju-controllers',
90- 'juju-views',
91- 'juju-models',
92- 'juju-view-charm-search'
93- ]
94+ fullpath: '/juju-ui/app.js'
95 }
96 }
97 }
98
99=== modified file 'app/store/charm.js'
100--- app/store/charm.js 2012-11-05 21:24:42 +0000
101+++ app/store/charm.js 2012-11-23 17:25:57 +0000
102@@ -114,6 +114,7 @@
103
104 }, '0.1.0', {
105 requires: [
106+ 'juju-charm-id',
107 'datasource-io',
108 'json-parse'
109 ]
110
111=== modified file 'test/test_app.js'
112--- test/test_app.js 2012-11-14 14:34:31 +0000
113+++ test/test_app.js 2012-11-23 17:25:57 +0000
114@@ -31,196 +31,181 @@
115 return app;
116 }
117
118-describe('Application basics', function() {
119- var Y, app, container;
120-
121- before(function(done) {
122- Y = YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils'], function(Y) {
123- done();
124- });
125- });
126-
127- beforeEach(function(done) {
128- // XXX Apparently removing a DOM node is asynchronous (on Chrome at least)
129- // and we occasionally lose the race if this code is in the afterEach
130- // function, so instead we do it here, but only if one has previously been
131- // created.
132- if (container) {
133- container.remove(true);
134- }
135- container = Y.one('#main')
136- .appendChild(Y.Node.create('<div/>'))
137- .set('id', 'test-container')
138- .addClass('container')
139- .append(Y.Node.create('<span/>')
140- .set('id', 'environment-name'))
141- .append(Y.Node.create('<span/>')
142- .set('id', 'provider-type'));
143- app = new Y.juju.App(
144- { container: container,
145- viewContainer: container});
146- injectData(app);
147- done();
148- });
149-
150- it('should produce a valid index', function() {
151- var container = app.get('container');
152- app.render();
153- container.getAttribute('id').should.equal('test-container');
154- container.getAttribute('class').should.include('container');
155- });
156-
157- it('should be able to route objects to internal URLs', function() {
158- // take handles to database objects and ensure we can route to the view
159- // needed to show them
160- var wordpress = app.db.services.getById('wordpress'),
161- wp0 = app.db.units.get_units_for_service(wordpress)[0],
162- wp_charm = app.db.charms.add({id: wordpress.get('charm')});
163-
164- // 'service/wordpress/' is the primary and so other URL are not returned
165- app.getModelURL(wordpress).should.equal('/service/wordpress/');
166- // however passing 'intent' can force selection of another
167- app.getModelURL(wordpress, 'config').should.equal(
168- '/service/wordpress/config');
169-
170- // service units use argument rewriting (thus not /u/wp/0)
171- app.getModelURL(wp0).should.equal('/unit/wordpress-0/');
172-
173- // charms also require a mapping but only a name, not a function
174- app.getModelURL(wp_charm).should.equal(
175- '/charms/charms/precise/wordpress-6/json');
176- });
177-
178- it('should display the configured environment name', function() {
179- var environment_name = 'This is the environment name. Deal with it.';
180- app = new Y.juju.App(
181- { container: container,
182- viewContainer: container,
183- environment_name: environment_name});
184- assert.equal(
185- container.one('#environment-name').get('text'),
186- environment_name);
187- });
188-
189- it('should show a generic environment name if none configured', function() {
190- app = new Y.juju.App(
191- { container: container,
192- viewContainer: container});
193- assert.equal(
194- container.one('#environment-name').get('text'),
195- 'Environment');
196- });
197-
198- it('should show the provider type, when available', function() {
199- var providerType = 'excellent provider';
200- // Since no provider type has been set yet, none is displayed.
201- assert.equal(
202- container.one('#provider-type').get('text'),
203- '');
204- app.env.set('providerType', providerType);
205- // The provider type has been displayed.
206- assert.equal(
207- container.one('#provider-type').get('text'),
208- 'on ' + providerType);
209- });
210-
211-});
212-
213-
214-describe('Application Connection State', function() {
215- var Y, container;
216-
217- before(function(done) {
218- Y = YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils'], function(Y) {
219- container = Y.Node.create('<div id="test" class="container"></div>');
220- done();
221- });
222-
223- });
224-
225- it('should be able to handle env connection status changes', function() {
226- var juju = Y.namespace('juju'),
227- conn = new(Y.namespace('juju-tests.utils')).SocketStub(),
228- env = new juju.Environment({conn: conn}),
229- app = new Y.juju.App({env: env, container: container}),
230- reset_called = false,
231- noop = function() {return this;};
232-
233-
234- // mock the db
235- app.db = {
236- // mock out notifications
237- // so app can start normally
238- notifications: {
239- addTarget: noop,
240- after: noop,
241- filter: noop,
242- map: noop,
243- on: noop,
244- size: function() {return 0;}
245- },
246- reset: function() {
247- reset_called = true;
248- }
249- };
250- env.connect();
251- conn.open();
252- reset_called.should.equal(true);
253-
254- // trigger a second time and verify
255- reset_called = false;
256- conn.open();
257- reset_called.should.equal(true);
258- });
259-
260-});
261-
262-describe('Application prefetching', function() {
263- var Y, models, conn, env, app, container, charm_store, data, juju;
264-
265- before(function(done) {
266- Y = YUI(GlobalConfig).use(
267- 'juju-models', 'juju-gui', 'datasource-local', 'juju-tests-utils',
268- 'json-stringify',
269- function(Y) {
270- models = Y.namespace('juju.models');
271- done();
272- });
273- });
274-
275- beforeEach(function() {
276- conn = new (Y.namespace('juju-tests.utils')).SocketStub(),
277- env = new Y.juju.Environment({conn: conn});
278- env.connect();
279- conn.open();
280- container = Y.Node.create('<div id="test" class="container"></div>');
281- data = [];
282- app = new Y.juju.App(
283- { container: container,
284- viewContainer: container,
285- env: env,
286- charm_store: {} });
287-
288- app.updateEndpoints = function() {};
289- env.get_endpoints = function() {};
290- });
291-
292- afterEach(function() {
293- container.destroy();
294- app.destroy();
295- });
296-
297- it('must prefetch charm and service for service pages', function() {
298- injectData(app);
299- var _ = expect(
300- app.db.charms.getById('cs:precise/wordpress-6')).to.not.exist;
301- app.show_service({params: {id: 'wordpress'}, query: {}});
302- // The app made a request of juju for the service info.
303- conn.messages[conn.messages.length - 2].op.should.equal('get_service');
304- // The app also requested juju (not the charm store--see discussion in
305- // app/models/charm.js) for the charm info.
306- conn.last_message().op.should.equal('get_charm');
307- // Tests of the actual load machinery are in the model and env tests, and
308- // so are not repeated here.
309+YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils'], function(Y) {
310+ describe('Application basics', function() {
311+ var app, container;
312+
313+ beforeEach(function() {
314+ if (container) {
315+ container.remove(true);
316+ }
317+ container = Y.one('#main')
318+ .appendChild(Y.Node.create('<div/>'))
319+ .set('id', 'test-container')
320+ .addClass('container')
321+ .append(Y.Node.create('<span/>')
322+ .set('id', 'environment-name'))
323+ .append(Y.Node.create('<span/>')
324+ .set('id', 'provider-type'));
325+ app = new Y.juju.App(
326+ { container: container,
327+ viewContainer: container});
328+ injectData(app);
329+ });
330+
331+ it('should produce a valid index', function() {
332+ var container = app.get('container');
333+ app.render();
334+ container.getAttribute('id').should.equal('test-container');
335+ container.getAttribute('class').should.include('container');
336+ });
337+
338+ it('should be able to route objects to internal URLs', function() {
339+ // take handles to database objects and ensure we can route to the view
340+ // needed to show them
341+ var wordpress = app.db.services.getById('wordpress'),
342+ wp0 = app.db.units.get_units_for_service(wordpress)[0],
343+ wp_charm = app.db.charms.add({id: wordpress.get('charm')});
344+
345+ // 'service/wordpress/' is the primary and so other URL are not returned
346+ app.getModelURL(wordpress).should.equal('/service/wordpress/');
347+ // however passing 'intent' can force selection of another
348+ app.getModelURL(wordpress, 'config').should.equal(
349+ '/service/wordpress/config');
350+
351+ // service units use argument rewriting (thus not /u/wp/0)
352+ app.getModelURL(wp0).should.equal('/unit/wordpress-0/');
353+
354+ // charms also require a mapping but only a name, not a function
355+ app.getModelURL(wp_charm).should.equal(
356+ '/charms/charms/precise/wordpress-6/json');
357+ });
358+
359+ it('should display the configured environment name', function() {
360+ var environment_name = 'This is the environment name. Deal with it.';
361+ app = new Y.juju.App(
362+ { container: container,
363+ viewContainer: container,
364+ environment_name: environment_name});
365+ assert.equal(
366+ container.one('#environment-name').get('text'),
367+ environment_name);
368+ });
369+
370+ it('should show a generic environment name if none configured', function() {
371+ app = new Y.juju.App(
372+ { container: container,
373+ viewContainer: container});
374+ assert.equal(
375+ container.one('#environment-name').get('text'),
376+ 'Environment');
377+ });
378+
379+ it('should show the provider type, when available', function() {
380+ var providerType = 'excellent provider';
381+ // Since no provider type has been set yet, none is displayed.
382+ assert.equal(
383+ container.one('#provider-type').get('text'),
384+ '');
385+ app.env.set('providerType', providerType);
386+ // The provider type has been displayed.
387+ assert.equal(
388+ container.one('#provider-type').get('text'),
389+ 'on ' + providerType);
390+ });
391+
392+ });
393+});
394+
395+YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils'], function(Y) {
396+ describe('Application Connection State', function() {
397+ var container;
398+
399+ before(function() {
400+ container = Y.Node.create('<div id="test" class="container"></div>');
401+ });
402+
403+ it('should be able to handle env connection status changes', function() {
404+ var juju = Y.namespace('juju'),
405+ conn = new(Y.namespace('juju-tests.utils')).SocketStub(),
406+ env = new juju.Environment({conn: conn}),
407+ app = new Y.juju.App({env: env, container: container}),
408+ reset_called = false,
409+ noop = function() {return this;};
410+
411+
412+ // mock the db
413+ app.db = {
414+ // mock out notifications
415+ // so app can start normally
416+ notifications: {
417+ addTarget: noop,
418+ after: noop,
419+ filter: noop,
420+ map: noop,
421+ on: noop,
422+ size: function() {return 0;}
423+ },
424+ reset: function() {
425+ reset_called = true;
426+ }
427+ };
428+ env.connect();
429+ conn.open();
430+ reset_called.should.equal(true);
431+
432+ // trigger a second time and verify
433+ reset_called = false;
434+ conn.open();
435+ reset_called.should.equal(true);
436+ });
437+
438+ });
439+});
440+
441+YUI(GlobalConfig).use(['juju-models', 'juju-gui', 'datasource-local',
442+ 'juju-tests-utils', 'json-stringify'], function(Y) {
443+ describe('Application prefetching', function() {
444+ var models, conn, env, app, container, charm_store, data, juju;
445+
446+ before(function() {
447+ models = Y.namespace('juju.models');
448+ });
449+
450+ beforeEach(function() {
451+ conn = new (Y.namespace('juju-tests.utils')).SocketStub(),
452+ env = new Y.juju.Environment({conn: conn});
453+ env.connect();
454+ conn.open();
455+ container = Y.Node.create('<div id="test" class="container"></div>');
456+ data = [];
457+ app = new Y.juju.App(
458+ { container: container,
459+ viewContainer: container,
460+ env: env,
461+ charm_store: {} });
462+
463+ app.updateEndpoints = function() {};
464+ env.get_endpoints = function() {};
465+ });
466+
467+ afterEach(function() {
468+ container.destroy();
469+ app.destroy();
470+ });
471+
472+ it('must prefetch charm and service for service pages', function() {
473+ injectData(app);
474+ var _ = expect(
475+ app.db.charms.getById('cs:precise/wordpress-6')).to.not.exist;
476+ app.show_service({params: {id: 'wordpress'}, query: {}});
477+ // The app made a request of juju for the service info.
478+ conn.messages[conn.messages.length - 2].op.should.equal('get_service');
479+ // The app also requested juju (not the charm store--see discussion in
480+ // app/models/charm.js) for the charm info.
481+ conn.last_message().op.should.equal('get_charm');
482+ // Tests of the actual load machinery are in the model and env tests, and
483+ // so are not repeated here.
484+ });
485 });
486 });
487
488=== modified file 'test/test_app_hotkeys.js'
489--- test/test_app_hotkeys.js 2012-11-20 15:39:46 +0000
490+++ test/test_app_hotkeys.js 2012-11-23 17:25:57 +0000
491@@ -1,12 +1,11 @@
492 'use strict';
493
494-describe('application hotkeys', function() {
495- var Y, app, container, env, conn, testUtils, windowNode;
496+YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils', 'node-event-simulate'],
497+ function(Y) {
498+ describe('application hotkeys', function() {
499+ var app, container, env, conn, testUtils, windowNode;
500
501- before(function() {
502- Y = YUI(GlobalConfig).use(
503- ['juju-gui', 'juju-tests-utils',
504- 'node-event-simulate'], function(Y) {
505+ before(function() {
506 windowNode = Y.one(window);
507 app = new Y.juju.App({
508 env: env,
509@@ -15,49 +14,50 @@
510 });
511 app.activateHotkeys();
512 });
513- });
514-
515- afterEach(function() {
516- container.remove(true);
517- });
518-
519- beforeEach(function() {
520- container = Y.one('#main').appendChild(Y.Node.create('<div/>')).set('id',
521- 'test-container').append(
522- Y.Node.create('<input />').set('id', 'charm-search-field'));
523- testUtils = Y.namespace('juju-tests.utils');
524- env = {
525- get: function() {
526- },
527- on: function() {
528- },
529- after: function() {
530- }
531- };
532- });
533-
534- it('should listen for alt-S events', function() {
535- app.render();
536- windowNode.simulate('keydown', {
537- keyCode: 83, // "S" key
538- altKey: true
539- });
540- // Did charm-search-field get the focus?
541- assert.equal(Y.one('#charm-search-field'), Y.one(document.activeElement));
542- });
543-
544- it('should listen for alt-E events', function() {
545- var altEtriggered = false;
546- app.on('navigateTo', function(param) {
547- if (param && param.url === '/') {
548- altEtriggered = true;
549- }
550- });
551- app.render();
552- windowNode.simulate('keydown', {
553- keyCode: 69, // "E" key
554- altKey: true
555- });
556- assert.isTrue(altEtriggered);
557- });
558-});
559+
560+ afterEach(function() {
561+ container.remove(true);
562+ });
563+
564+ beforeEach(function() {
565+ container = Y.one('#main').appendChild(Y.Node.create(
566+ '<div/>')).set('id', 'test-container').append(
567+ Y.Node.create('<input />').set('id', 'charm-search-field'));
568+ testUtils = Y.namespace('juju-tests.utils');
569+ env = {
570+ get: function() {
571+ },
572+ on: function() {
573+ },
574+ after: function() {
575+ }
576+ };
577+ });
578+
579+ it('should listen for alt-S events', function() {
580+ app.render();
581+ windowNode.simulate('keydown', {
582+ keyCode: 83, // "S" key
583+ altKey: true
584+ });
585+ // Did charm-search-field get the focus?
586+ assert.equal(Y.one('#charm-search-field'),
587+ Y.one(document.activeElement));
588+ });
589+
590+ it('should listen for alt-E events', function() {
591+ var altEtriggered = false;
592+ app.on('navigateTo', function(param) {
593+ if (param && param.url === '/') {
594+ altEtriggered = true;
595+ }
596+ });
597+ app.render();
598+ windowNode.simulate('keydown', {
599+ keyCode: 69, // "E" key
600+ altKey: true
601+ });
602+ assert.isTrue(altEtriggered);
603+ });
604+ });
605+ });
606
607=== modified file 'test/test_model.js'
608--- test/test_model.js 2012-10-23 20:02:17 +0000
609+++ test/test_model.js 2012-11-23 17:25:57 +0000
610@@ -1,15 +1,11 @@
611 'use strict';
612
613-(function() {
614-
615+YUI(GlobalConfig).use('juju-models', function(Y) {
616 describe('charm normalization', function() {
617- var Y, models;
618+ var models;
619
620- before(function(done) {
621- Y = YUI(GlobalConfig).use('juju-models', function(Y) {
622- models = Y.namespace('juju.models');
623- done();
624- });
625+ before(function() {
626+ models = Y.namespace('juju.models');
627 });
628
629 it('must create derived attributes from official charm id', function() {
630@@ -31,15 +27,14 @@
631 });
632
633 });
634+});
635
636+YUI(GlobalConfig).use('juju-models', function(Y) {
637 describe('juju models', function() {
638- var Y, models;
639+ var models;
640
641- before(function(done) {
642- Y = YUI(GlobalConfig).use('juju-models', function(Y) {
643- models = Y.namespace('juju.models');
644- done();
645- });
646+ before(function() {
647+ models = Y.namespace('juju.models');
648 });
649
650 it('must be able to create charm', function() {
651@@ -355,19 +350,17 @@
652 .should.eql(['relation-2', 'relation-3', 'relation-4']);
653 });
654 });
655+});
656
657+YUI(GlobalConfig).use(['juju-models', 'juju-gui', 'datasource-local',
658+ 'juju-tests-utils', 'json-stringify',
659+ 'juju-charm-store'], function(Y) {
660 describe('juju charm load', function() {
661- var Y, models, conn, env, app, container, charm_store, data, juju;
662+ var models, conn, env, app, container, charm_store, data, juju;
663
664- before(function(done) {
665- Y = YUI(GlobalConfig).use(
666- 'juju-models', 'juju-gui', 'datasource-local', 'juju-tests-utils',
667- 'json-stringify', 'juju-charm-store',
668- function(Y) {
669- models = Y.namespace('juju.models');
670- juju = Y.namespace('juju');
671- done();
672- });
673+ before(function() {
674+ models = Y.namespace('juju.models');
675+ juju = Y.namespace('juju');
676 });
677
678 beforeEach(function() {
679@@ -513,4 +506,4 @@
680 });
681
682 });
683-})();
684+});
685
686=== modified file 'test/test_notifications.js'
687--- test/test_notifications.js 2012-11-16 13:38:21 +0000
688+++ test/test_notifications.js 2012-11-23 17:25:57 +0000
689@@ -1,477 +1,462 @@
690 'use strict';
691
692-describe('notifications', function() {
693- var Y, juju, models, views;
694-
695- var default_env = {
696- 'result': [
697- ['service', 'add', {
698- 'charm': 'cs:precise/wordpress-6',
699- 'id': 'wordpress',
700- 'exposed': false
701- }],
702- ['service', 'add', {
703- 'charm': 'cs:precise/mediawiki-3',
704- 'id': 'mediawiki',
705- 'exposed': false
706- }],
707- ['service', 'add', {
708- 'charm': 'cs:precise/mysql-6',
709- 'id': 'mysql'
710- }],
711- ['relation', 'add', {
712- 'interface': 'reversenginx',
713- 'scope': 'global',
714- 'endpoints':
715- [['wordpress', {'role': 'peer', 'name': 'loadbalancer'}]],
716- 'id': 'relation-0000000000'
717- }],
718- ['relation', 'add', {
719- 'interface': 'mysql',
720- 'scope': 'global',
721- 'endpoints':
722- [['mysql', {'role': 'server', 'name': 'db'}],
723- ['wordpress', {'role': 'client', 'name': 'db'}]],
724- 'id': 'relation-0000000001'
725- }],
726- ['machine', 'add', {
727- 'agent-state': 'running',
728- 'instance-state': 'running',
729- 'id': 0,
730- 'instance-id': 'local',
731- 'dns-name': 'localhost'
732- }],
733- ['unit', 'add', {
734- 'machine': 0,
735- 'agent-state': 'started',
736- 'public-address': '192.168.122.113',
737- 'id': 'wordpress/0'
738- }],
739- ['unit', 'add', {
740- 'machine': 0,
741- 'agent-state': 'error',
742- 'public-address': '192.168.122.222',
743- 'id': 'mysql/0'
744- }]
745- ],
746- 'op': 'delta'
747- };
748-
749-
750- before(function(done) {
751- Y = YUI(GlobalConfig).use([
752- 'juju-models',
753- 'juju-views',
754- 'juju-gui',
755- 'juju-env',
756- 'node-event-simulate',
757- 'juju-tests-utils'],
758-
759+YUI(GlobalConfig).use(['juju-gui', 'node-event-simulate', 'juju-tests-utils'],
760 function(Y) {
761- juju = Y.namespace('juju');
762- models = Y.namespace('juju.models');
763- views = Y.namespace('juju.views');
764- done();
765- });
766- });
767-
768- it('must be able to make notification and lists of notifications',
769- function() {
770- var note1 = new models.Notification({
771- title: 'test1',
772- message: 'Hello'
773- }),
774- note2 = new models.Notification({
775- title: 'test2',
776- message: 'I said goodnight!'
777- }),
778- notifications = new models.NotificationList();
779-
780- notifications.add([note1, note2]);
781- notifications.size().should.equal(2);
782-
783- // timestamp should be generated once
784- var ts = note1.get('timestamp');
785- note1.get('timestamp').should.equal(ts);
786- // force an update so we can test ordering
787- // fast execution can result in same timestamp
788- note2.set('timestamp', ts + 1);
789- note2.get('timestamp').should.be.above(ts);
790-
791- // defaults as expected
792- note1.get('level').should.equal('info');
793- note2.get('level').should.equal('info');
794- // the sort order on the list should be by
795- // timestamp
796- notifications.get('title').should.eql(['test2', 'test1']);
797- });
798-
799- it('must be able to render its view with sample data',
800- function() {
801- var note1 = new models.Notification({
802- title: 'test1', message: 'Hello'}),
803- note2 = new models.Notification({
804- title: 'test2', message: 'I said goodnight!'}),
805- notifications = new models.NotificationList(),
806- container = Y.Node.create('<div id="test">'),
807- env = new juju.Environment(),
808- view = new views.NotificationsView({
809- container: container,
810- notifications: notifications,
811- env: env});
812- view.render();
813- // Verify the expected elements appear in the view
814- container.one('#notify-list').should.not.equal(undefined);
815- container.destroy();
816- });
817-
818- it('must be able to limit the size of notification events',
819- function() {
820- var note1 = new models.Notification({
821- title: 'test1',
822- message: 'Hello'
823- }),
824- note2 = new models.Notification({
825- title: 'test2',
826- message: 'I said goodnight!'
827- }),
828- note3 = new models.Notification({
829- title: 'test3',
830- message: 'Never remember'
831- }),
832- notifications = new models.NotificationList({
833- max_size: 2
834- });
835-
836- notifications.add([note1, note2]);
837- notifications.size().should.equal(2);
838-
839- // Adding a new notification should pop the oldest from the list (we
840- // exceed max_size)
841- notifications.add(note3);
842- notifications.size().should.equal(2);
843- notifications.get('title').should.eql(['test3', 'test2']);
844- });
845-
846- it('must be able to get notifications for a given model',
847- function() {
848- var m = new models.Service({id: 'mediawiki'}),
849- note1 = new models.Notification({
850- title: 'test1',
851- message: 'Hello',
852- modelId: m
853- }),
854- note2 = new models.Notification({
855- title: 'test2',
856- message: 'I said goodnight!'
857- }),
858- notifications = new models.NotificationList();
859-
860- notifications.add([note1, note2]);
861- notifications.size().should.equal(2);
862- notifications.getNotificationsForModel(m).should.eql(
863- [note1]);
864-
865- });
866-
867- it('must be able to include and show object links', function() {
868- var container = Y.Node.create('<div id="test">'),
869- conn = new(Y.namespace('juju-tests.utils')).SocketStub(),
870- env = new juju.Environment({conn: conn}),
871- app = new Y.juju.App({env: env, container: container}),
872- db = app.db,
873- mw = db.services.create({id: 'mediawiki',
874- name: 'mediawiki'}),
875- notifications = db.notifications,
876- view = new views.NotificationsOverview({
877- container: container,
878- notifications: notifications,
879- app: app,
880- env: env}).render();
881- // we use overview here for testing as it defaults
882- // to showing all notices
883-
884- // we can use app's routing table to derive a link
885- notifications.create({title: 'Service Down',
886- message: 'Your service has an error',
887- link: app.getModelURL(mw)
888- });
889- view.render();
890- var link = container.one('.notice').one('a');
891- link.getAttribute('href').should.equal(
892- '/service/mediawiki/');
893- link.getHTML().should.contain('View Details');
894-
895-
896- // create a new notice passing the link_title
897- notifications.create({title: 'Service Down',
898- message: 'Your service has an error',
899- link: app.getModelURL(mw),
900- link_title: 'Resolve this'
901- });
902- view.render();
903- link = container.one('.notice').one('a');
904- link.getAttribute('href').should.equal(
905- '/service/mediawiki/');
906- link.getHTML().should.contain('Resolve this');
907- });
908-
909- it('must be able to evict irrelevant notices', function() {
910- var container = Y.Node.create(
911- '<div id="test" class="container"></div>'),
912- conn = new(Y.namespace('juju-tests.utils')).SocketStub(),
913- env = new juju.Environment({conn: conn}),
914- app = new Y.juju.App({
915- env: env,
916- container: container,
917- viewContainer: container
918- });
919- var environment_delta = default_env;
920-
921- var notifications = app.db.notifications,
922- view = new views.NotificationsView({
923- container: container,
924- notifications: notifications,
925- env: app.env}).render();
926-
927-
928- app.env.dispatch_result(environment_delta);
929-
930-
931- notifications.size().should.equal(7);
932- // we have one unit in error
933- view.getShowable().length.should.equal(1);
934-
935- // now fire another delta event marking that node as
936- // started
937- app.env.dispatch_result({result: [['unit', 'change', {
938- 'machine': 0,
939- 'agent-state': 'started',
940- 'public-address': '192.168.122.222',
941- 'id': 'mysql/0'
942- }]], op: 'delta'});
943- notifications.size().should.equal(8);
944- // This should have evicted the prior notice from seen
945- view.getShowable().length.should.equal(0);
946- });
947-
948- it('must properly construct title and message based on level from ' +
949- 'event data',
950- function() {
951- var container = Y.Node.create(
952- '<div id="test" class="container"></div>'),
953- app = new Y.juju.App({
954- container: container,
955- viewContainer: container
956- });
957- var environment_delta = {
958- 'result': [
959- ['service', 'add', {
960- 'charm': 'cs:precise/wordpress-6',
961- 'id': 'wordpress'
962- }],
963- ['service', 'add', {
964- 'charm': 'cs:precise/mediawiki-3',
965- 'id': 'mediawiki'
966- }],
967- ['service', 'add', {
968- 'charm': 'cs:precise/mysql-6',
969- 'id': 'mysql'
970- }],
971- ['unit', 'add', {
972- 'agent-state': 'install-error',
973- 'id': 'wordpress/0'
974- }],
975- ['unit', 'add', {
976- 'agent-state': 'error',
977- 'public-address': '192.168.122.222',
978- 'id': 'mysql/0'
979- }],
980- ['unit', 'add', {
981- 'public-address': '192.168.122.222',
982- 'id': 'mysql/2'
983- }]
984- ],
985- 'op': 'delta'
986- };
987-
988- var notifications = app.db.notifications,
989- view = new views.NotificationsView({
990- container: container,
991- notifications: notifications,
992- app: app,
993- env: app.env}).render();
994-
995- app.env.dispatch_result(environment_delta);
996-
997- notifications.size().should.equal(6);
998- // we have one unit in error
999- var showable = view.getShowable();
1000- showable.length.should.equal(2);
1001- // The first showable notification should indicate an error.
1002- showable[0].level.should.equal('error');
1003- showable[0].title.should.equal('Error with mysql/0');
1004- showable[0].message.should.equal('Agent-state = error.');
1005- // The second showable notification should also indicate an error.
1006- showable[1].level.should.equal('error');
1007- showable[1].title.should.equal('Error with wordpress/0');
1008- showable[1].message.should.equal('Agent-state = install-error.');
1009- // The first non-error notice should have an 'info' level and less
1010- // severe messaging.
1011- var notice = notifications.item(0);
1012- notice.get('level').should.equal('info');
1013- notice.get('title').should.equal('Problem with mysql/2');
1014- notice.get('message').should.equal('');
1015- });
1016-
1017-
1018- it('should open on click and close on clickoutside', function(done) {
1019- var container = Y.Node.create(
1020- '<div id="test-container" style="display: none" class="container"/>'),
1021- notifications = new models.NotificationList(),
1022- env = new juju.Environment(),
1023- view = new views.NotificationsView({
1024- container: container,
1025- notifications: notifications,
1026- env: env}).render(),
1027- indicator;
1028-
1029- Y.one('body').append(container);
1030- notifications.add({title: 'testing', 'level': 'error'});
1031- indicator = container.one('#notify-indicator');
1032-
1033- indicator.simulate('click');
1034- indicator.ancestor().hasClass('open').should.equal(true);
1035-
1036- Y.one('body').simulate('click');
1037- indicator.ancestor().hasClass('open').should.equal(false);
1038-
1039- container.remove();
1040- done();
1041- });
1042-
1043-});
1044-
1045-
1046-describe('changing notifications to words', function() {
1047- var Y, juju;
1048-
1049- before(function(done) {
1050- Y = YUI(GlobalConfig).use(
1051- ['juju-notification-controller'],
1052- function(Y) {
1053- juju = Y.namespace('juju');
1054- done();
1055- });
1056- });
1057-
1058- it('should correctly translate notification operations into English',
1059- function() {
1060- assert.equal(juju._changeNotificationOpToWords('add'), 'created');
1061- assert.equal(juju._changeNotificationOpToWords('remove'), 'removed');
1062- assert.equal(juju._changeNotificationOpToWords('not-an-op'), 'changed');
1063- });
1064-});
1065-
1066-describe('relation notifications', function() {
1067- var Y, juju;
1068-
1069- before(function(done) {
1070- Y = YUI(GlobalConfig).use(
1071- ['juju-notification-controller'],
1072- function(Y) {
1073- juju = Y.namespace('juju');
1074- done();
1075- });
1076- });
1077-
1078- it('should produce reasonable titles', function() {
1079- assert.equal(
1080- juju._relationNotifications.title(undefined, 'add'),
1081- 'Relation created');
1082- assert.equal(
1083- juju._relationNotifications.title(undefined, 'remove'),
1084- 'Relation removed');
1085- });
1086-
1087- it('should generate messages about two-party relations', function() {
1088- var changeData =
1089- { endpoints:
1090- [['endpoint0', {name: 'relation0'}],
1091- ['endpoint1', {name: 'relation1'}]]};
1092- assert.equal(
1093- juju._relationNotifications.message(undefined, 'add', changeData),
1094- 'Relation between endpoint0 (relation type "relation0") and ' +
1095- 'endpoint1 (relation type "relation1") was created');
1096- });
1097-
1098- it('should generate messages about one-party relations', function() {
1099- var changeData =
1100- { endpoints:
1101- [['endpoint1', {name: 'relation1'}]]};
1102- assert.equal(
1103- juju._relationNotifications.message(undefined, 'add', changeData),
1104- 'Relation with endpoint1 (relation type "relation1") was created');
1105- });
1106-});
1107-
1108-describe('notification visual feedback', function() {
1109- var env, models, notifications, notificationsView, notifierBox, views, Y;
1110-
1111- before(function(done) {
1112- Y = YUI(GlobalConfig).use('juju-env', 'juju-models', 'juju-views',
1113- function(Y) {
1114+ describe('notifications', function() {
1115+ var juju, models, views;
1116+
1117+ var default_env = {
1118+ 'result': [
1119+ ['service', 'add', {
1120+ 'charm': 'cs:precise/wordpress-6',
1121+ 'id': 'wordpress',
1122+ 'exposed': false
1123+ }],
1124+ ['service', 'add', {
1125+ 'charm': 'cs:precise/mediawiki-3',
1126+ 'id': 'mediawiki',
1127+ 'exposed': false
1128+ }],
1129+ ['service', 'add', {
1130+ 'charm': 'cs:precise/mysql-6',
1131+ 'id': 'mysql'
1132+ }],
1133+ ['relation', 'add', {
1134+ 'interface': 'reversenginx',
1135+ 'scope': 'global',
1136+ 'endpoints':
1137+ [['wordpress', {'role': 'peer', 'name': 'loadbalancer'}]],
1138+ 'id': 'relation-0000000000'
1139+ }],
1140+ ['relation', 'add', {
1141+ 'interface': 'mysql',
1142+ 'scope': 'global',
1143+ 'endpoints':
1144+ [['mysql', {'role': 'server', 'name': 'db'}],
1145+ ['wordpress', {'role': 'client', 'name': 'db'}]],
1146+ 'id': 'relation-0000000001'
1147+ }],
1148+ ['machine', 'add', {
1149+ 'agent-state': 'running',
1150+ 'instance-state': 'running',
1151+ 'id': 0,
1152+ 'instance-id': 'local',
1153+ 'dns-name': 'localhost'
1154+ }],
1155+ ['unit', 'add', {
1156+ 'machine': 0,
1157+ 'agent-state': 'started',
1158+ 'public-address': '192.168.122.113',
1159+ 'id': 'wordpress/0'
1160+ }],
1161+ ['unit', 'add', {
1162+ 'machine': 0,
1163+ 'agent-state': 'error',
1164+ 'public-address': '192.168.122.222',
1165+ 'id': 'mysql/0'
1166+ }]
1167+ ],
1168+ 'op': 'delta'
1169+ };
1170+
1171+
1172+ before(function() {
1173+ juju = Y.namespace('juju');
1174+ models = Y.namespace('juju.models');
1175+ views = Y.namespace('juju.views');
1176+ });
1177+
1178+ it('must be able to make notification and lists of notifications',
1179+ function() {
1180+ var note1 = new models.Notification({
1181+ title: 'test1',
1182+ message: 'Hello'
1183+ }),
1184+ note2 = new models.Notification({
1185+ title: 'test2',
1186+ message: 'I said goodnight!'
1187+ }),
1188+ notifications = new models.NotificationList();
1189+
1190+ notifications.add([note1, note2]);
1191+ notifications.size().should.equal(2);
1192+
1193+ // timestamp should be generated once
1194+ var ts = note1.get('timestamp');
1195+ note1.get('timestamp').should.equal(ts);
1196+ // force an update so we can test ordering
1197+ // fast execution can result in same timestamp
1198+ note2.set('timestamp', ts + 1);
1199+ note2.get('timestamp').should.be.above(ts);
1200+
1201+ // defaults as expected
1202+ note1.get('level').should.equal('info');
1203+ note2.get('level').should.equal('info');
1204+ // the sort order on the list should be by
1205+ // timestamp
1206+ notifications.get('title').should.eql(['test2', 'test1']);
1207+ });
1208+
1209+ it('must be able to render its view with sample data',
1210+ function() {
1211+ var note1 = new models.Notification({
1212+ title: 'test1', message: 'Hello'}),
1213+ note2 = new models.Notification({
1214+ title: 'test2', message: 'I said goodnight!'}),
1215+ notifications = new models.NotificationList(),
1216+ container = Y.Node.create('<div id="test">'),
1217+ env = new juju.Environment(),
1218+ view = new views.NotificationsView({
1219+ container: container,
1220+ notifications: notifications,
1221+ env: env});
1222+ view.render();
1223+ // Verify the expected elements appear in the view
1224+ container.one('#notify-list').should.not.equal(undefined);
1225+ container.destroy();
1226+ });
1227+
1228+ it('must be able to limit the size of notification events',
1229+ function() {
1230+ var note1 = new models.Notification({
1231+ title: 'test1',
1232+ message: 'Hello'
1233+ }),
1234+ note2 = new models.Notification({
1235+ title: 'test2',
1236+ message: 'I said goodnight!'
1237+ }),
1238+ note3 = new models.Notification({
1239+ title: 'test3',
1240+ message: 'Never remember'
1241+ }),
1242+ notifications = new models.NotificationList({
1243+ max_size: 2
1244+ });
1245+
1246+ notifications.add([note1, note2]);
1247+ notifications.size().should.equal(2);
1248+
1249+ // Adding a new notification should pop the oldest from the list
1250+ // (we exceed max_size)
1251+ notifications.add(note3);
1252+ notifications.size().should.equal(2);
1253+ notifications.get('title').should.eql(['test3', 'test2']);
1254+ });
1255+
1256+ it('must be able to get notifications for a given model',
1257+ function() {
1258+ var m = new models.Service({id: 'mediawiki'}),
1259+ note1 = new models.Notification({
1260+ title: 'test1',
1261+ message: 'Hello',
1262+ modelId: m
1263+ }),
1264+ note2 = new models.Notification({
1265+ title: 'test2',
1266+ message: 'I said goodnight!'
1267+ }),
1268+ notifications = new models.NotificationList();
1269+
1270+ notifications.add([note1, note2]);
1271+ notifications.size().should.equal(2);
1272+ notifications.getNotificationsForModel(m).should.eql(
1273+ [note1]);
1274+
1275+ });
1276+
1277+ it('must be able to include and show object links', function() {
1278+ var container = Y.Node.create('<div id="test">'),
1279+ conn = new(Y.namespace('juju-tests.utils')).SocketStub(),
1280+ env = new juju.Environment({conn: conn}),
1281+ app = new Y.juju.App({env: env, container: container}),
1282+ db = app.db,
1283+ mw = db.services.create({id: 'mediawiki',
1284+ name: 'mediawiki'}),
1285+ notifications = db.notifications,
1286+ view = new views.NotificationsOverview({
1287+ container: container,
1288+ notifications: notifications,
1289+ app: app,
1290+ env: env}).render();
1291+ // we use overview here for testing as it defaults
1292+ // to showing all notices
1293+
1294+ // we can use app's routing table to derive a link
1295+ notifications.create({title: 'Service Down',
1296+ message: 'Your service has an error',
1297+ link: app.getModelURL(mw)
1298+ });
1299+ view.render();
1300+ var link = container.one('.notice').one('a');
1301+ link.getAttribute('href').should.equal(
1302+ '/service/mediawiki/');
1303+ link.getHTML().should.contain('View Details');
1304+
1305+
1306+ // create a new notice passing the link_title
1307+ notifications.create({title: 'Service Down',
1308+ message: 'Your service has an error',
1309+ link: app.getModelURL(mw),
1310+ link_title: 'Resolve this'
1311+ });
1312+ view.render();
1313+ link = container.one('.notice').one('a');
1314+ link.getAttribute('href').should.equal(
1315+ '/service/mediawiki/');
1316+ link.getHTML().should.contain('Resolve this');
1317+ });
1318+
1319+ it('must be able to evict irrelevant notices', function() {
1320+ var container = Y.Node.create(
1321+ '<div id="test" class="container"></div>'),
1322+ conn = new(Y.namespace('juju-tests.utils')).SocketStub(),
1323+ env = new juju.Environment({conn: conn}),
1324+ app = new Y.juju.App({
1325+ env: env,
1326+ container: container,
1327+ viewContainer: container
1328+ });
1329+ var environment_delta = default_env;
1330+
1331+ var notifications = app.db.notifications,
1332+ view = new views.NotificationsView({
1333+ container: container,
1334+ notifications: notifications,
1335+ env: app.env}).render();
1336+
1337+
1338+ app.env.dispatch_result(environment_delta);
1339+
1340+
1341+ notifications.size().should.equal(7);
1342+ // we have one unit in error
1343+ view.getShowable().length.should.equal(1);
1344+
1345+ // now fire another delta event marking that node as
1346+ // started
1347+ app.env.dispatch_result({result: [['unit', 'change', {
1348+ 'machine': 0,
1349+ 'agent-state': 'started',
1350+ 'public-address': '192.168.122.222',
1351+ 'id': 'mysql/0'
1352+ }]], op: 'delta'});
1353+ notifications.size().should.equal(8);
1354+ // This should have evicted the prior notice from seen
1355+ view.getShowable().length.should.equal(0);
1356+ });
1357+
1358+ it('must properly construct title and message based on level from ' +
1359+ 'event data',
1360+ function() {
1361+ var container = Y.Node.create(
1362+ '<div id="test" class="container"></div>'),
1363+ app = new Y.juju.App({
1364+ container: container,
1365+ viewContainer: container
1366+ });
1367+ var environment_delta = {
1368+ 'result': [
1369+ ['service', 'add', {
1370+ 'charm': 'cs:precise/wordpress-6',
1371+ 'id': 'wordpress'
1372+ }],
1373+ ['service', 'add', {
1374+ 'charm': 'cs:precise/mediawiki-3',
1375+ 'id': 'mediawiki'
1376+ }],
1377+ ['service', 'add', {
1378+ 'charm': 'cs:precise/mysql-6',
1379+ 'id': 'mysql'
1380+ }],
1381+ ['unit', 'add', {
1382+ 'agent-state': 'install-error',
1383+ 'id': 'wordpress/0'
1384+ }],
1385+ ['unit', 'add', {
1386+ 'agent-state': 'error',
1387+ 'public-address': '192.168.122.222',
1388+ 'id': 'mysql/0'
1389+ }],
1390+ ['unit', 'add', {
1391+ 'public-address': '192.168.122.222',
1392+ 'id': 'mysql/2'
1393+ }]
1394+ ],
1395+ 'op': 'delta'
1396+ };
1397+
1398+ var notifications = app.db.notifications,
1399+ view = new views.NotificationsView({
1400+ container: container,
1401+ notifications: notifications,
1402+ app: app,
1403+ env: app.env}).render();
1404+
1405+ app.env.dispatch_result(environment_delta);
1406+
1407+ notifications.size().should.equal(6);
1408+ // we have one unit in error
1409+ var showable = view.getShowable();
1410+ showable.length.should.equal(2);
1411+ // The first showable notification should indicate an error.
1412+ showable[0].level.should.equal('error');
1413+ showable[0].title.should.equal('Error with mysql/0');
1414+ showable[0].message.should.equal('Agent-state = error.');
1415+ // The second showable notification should also indicate an error.
1416+ showable[1].level.should.equal('error');
1417+ showable[1].title.should.equal('Error with wordpress/0');
1418+ showable[1].message.should.equal('Agent-state = install-error.');
1419+ // The first non-error notice should have an 'info' level and less
1420+ // severe messaging.
1421+ var notice = notifications.item(0);
1422+ notice.get('level').should.equal('info');
1423+ notice.get('title').should.equal('Problem with mysql/2');
1424+ notice.get('message').should.equal('');
1425+ });
1426+
1427+
1428+ it('should open on click and close on clickoutside', function(done) {
1429+ var container = Y.Node.create('<div id="test-container" ' +
1430+ 'style="display: none" class="container"/>'),
1431+ notifications = new models.NotificationList(),
1432+ env = new juju.Environment(),
1433+ view = new views.NotificationsView({
1434+ container: container,
1435+ notifications: notifications,
1436+ env: env}).render(),
1437+ indicator;
1438+
1439+ Y.one('body').append(container);
1440+ notifications.add({title: 'testing', 'level': 'error'});
1441+ indicator = container.one('#notify-indicator');
1442+
1443+ indicator.simulate('click');
1444+ indicator.ancestor().hasClass('open').should.equal(true);
1445+
1446+ Y.one('body').simulate('click');
1447+ indicator.ancestor().hasClass('open').should.equal(false);
1448+
1449+ container.remove();
1450+ done();
1451+ });
1452+ });
1453+
1454+ describe('changing notifications to words', function() {
1455+ var juju;
1456+
1457+ before(function() {
1458+ juju = Y.namespace('juju');
1459+ });
1460+
1461+ it('should correctly translate notification operations into English',
1462+ function() {
1463+ assert.equal(juju._changeNotificationOpToWords('add'), 'created');
1464+ assert.equal(juju._changeNotificationOpToWords('remove'),
1465+ 'removed');
1466+ assert.equal(juju._changeNotificationOpToWords('not-an-op'),
1467+ 'changed');
1468+ });
1469+ });
1470+
1471+ describe('relation notifications', function() {
1472+ var juju;
1473+
1474+ before(function() {
1475+ juju = Y.namespace('juju');
1476+ });
1477+
1478+ it('should produce reasonable titles', function() {
1479+ assert.equal(
1480+ juju._relationNotifications.title(undefined, 'add'),
1481+ 'Relation created');
1482+ assert.equal(
1483+ juju._relationNotifications.title(undefined, 'remove'),
1484+ 'Relation removed');
1485+ });
1486+
1487+ it('should generate messages about two-party relations', function() {
1488+ var changeData =
1489+ { endpoints:
1490+ [['endpoint0', {name: 'relation0'}],
1491+ ['endpoint1', {name: 'relation1'}]]};
1492+ assert.equal(
1493+ juju._relationNotifications.message(undefined, 'add',
1494+ changeData), 'Relation between endpoint0 (relation type ' +
1495+ '"relation0") and endpoint1 (relation type "relation1") ' +
1496+ 'was created');
1497+ });
1498+
1499+ it('should generate messages about one-party relations', function() {
1500+ var changeData =
1501+ { endpoints:
1502+ [['endpoint1', {name: 'relation1'}]]};
1503+ assert.equal(
1504+ juju._relationNotifications.message(undefined, 'add',
1505+ changeData), 'Relation with endpoint1 (relation type ' +
1506+ '"relation1") was created');
1507+ });
1508+ });
1509+
1510+ describe('notification visual feedback', function() {
1511+ var env, models, notifications, notificationsView, notifierBox, views;
1512+
1513+ before(function() {
1514 var juju = Y.namespace('juju');
1515 env = new juju.Environment();
1516 models = Y.namespace('juju.models');
1517 views = Y.namespace('juju.views');
1518- done();
1519- });
1520- });
1521-
1522- // Instantiate the notifications model list and view.
1523- // Also create the notifier box and attach it as first element of the body.
1524- beforeEach(function() {
1525- notifications = new models.NotificationList();
1526- notificationsView = new views.NotificationsView({
1527- env: env,
1528- notifications: notifications
1529+ });
1530+
1531+ // Instantiate the notifications model list and view.
1532+ // Also create the notifier box and attach it as first element of the
1533+ // body.
1534+ beforeEach(function() {
1535+ notifications = new models.NotificationList();
1536+ notificationsView = new views.NotificationsView({
1537+ env: env,
1538+ notifications: notifications
1539+ });
1540+ notifierBox = Y.Node.create('<div id="notifier-box"></div>');
1541+ notifierBox.setStyle('display', 'none');
1542+ Y.one('body').prepend(notifierBox);
1543+ });
1544+
1545+ // Destroy the notifier box created in beforeEach.
1546+ afterEach(function() {
1547+ notifierBox.remove();
1548+ notifierBox.destroy(true);
1549+ });
1550+
1551+ // Assert the notifier box contains the expectedNumber of notifiers.
1552+ var assertNumNotifiers = function(expectedNumber) {
1553+ assert.equal(expectedNumber, notifierBox.get('children').size());
1554+ };
1555+
1556+ it('should appear when a new error is notified', function() {
1557+ notifications.add({title: 'mytitle', level: 'error'});
1558+ assertNumNotifiers(1);
1559+ });
1560+
1561+ it('should only appear when the DOM contains the notifier box',
1562+ function() {
1563+ notifierBox.remove();
1564+ notifications.add({title: 'mytitle', level: 'error'});
1565+ assertNumNotifiers(0);
1566+ });
1567+
1568+ it('should not appear when the notification is not an error',
1569+ function() {
1570+ notifications.add({title: 'mytitle', level: 'info'});
1571+ assertNumNotifiers(0);
1572+ });
1573+
1574+ it('should not appear when the notification comes form delta',
1575+ function() {
1576+ notifications.add({title: 'mytitle', level: 'error', isDelta:
1577+ true});
1578+ assertNumNotifiers(0);
1579+ });
1580+
1581+ });
1582 });
1583- notifierBox = Y.Node.create('<div id="notifier-box"></div>');
1584- notifierBox.setStyle('display', 'none');
1585- Y.one('body').prepend(notifierBox);
1586- });
1587-
1588- // Destroy the notifier box created in beforeEach.
1589- afterEach(function() {
1590- notifierBox.remove();
1591- notifierBox.destroy(true);
1592- });
1593-
1594- // Assert the notifier box contains the expectedNumber of notifiers.
1595- var assertNumNotifiers = function(expectedNumber) {
1596- assert.equal(expectedNumber, notifierBox.get('children').size());
1597- };
1598-
1599- it('should appear when a new error is notified', function() {
1600- notifications.add({title: 'mytitle', level: 'error'});
1601- assertNumNotifiers(1);
1602- });
1603-
1604- it('should only appear when the DOM contains the notifier box', function() {
1605- notifierBox.remove();
1606- notifications.add({title: 'mytitle', level: 'error'});
1607- assertNumNotifiers(0);
1608- });
1609-
1610- it('should not appear when the notification is not an error', function() {
1611- notifications.add({title: 'mytitle', level: 'info'});
1612- assertNumNotifiers(0);
1613- });
1614-
1615- it('should not appear when the notification comes form delta', function() {
1616- notifications.add({title: 'mytitle', level: 'error', isDelta: true});
1617- assertNumNotifiers(0);
1618- });
1619-
1620-});
1621
1622=== modified file 'test/test_notifier_widget.js'
1623--- test/test_notifier_widget.js 2012-11-16 13:37:20 +0000
1624+++ test/test_notifier_widget.js 2012-11-23 17:25:57 +0000
1625@@ -1,103 +1,101 @@
1626 'use strict';
1627
1628-describe('notifier widget', function() {
1629- var Notifier, notifierBox, Y;
1630-
1631- before(function(done) {
1632- Y = YUI(GlobalConfig).use('notifier', 'node-event-simulate',
1633- function(Y) {
1634- Notifier = Y.namespace('juju.widgets').Notifier;
1635- done();
1636- });
1637- });
1638-
1639- // Create the notifier box and attach it as first element of the body.
1640- beforeEach(function() {
1641- notifierBox = Y.Node.create('<div id="notifier-box"></div>');
1642- notifierBox.setStyle('display', 'none');
1643- Y.one('body').prepend(notifierBox);
1644- });
1645-
1646- // Destroy the notifier box created in beforeEach.
1647- afterEach(function() {
1648- notifierBox.remove();
1649- notifierBox.destroy(true);
1650- });
1651-
1652- // Factory rendering and returning a notifier instance.
1653- var makeNotifier = function(title, message, timeout) {
1654- var notifier = new Notifier({
1655- title: title || 'mytitle',
1656- message: message || 'mymessage',
1657- timeout: timeout || 10000
1658- });
1659- notifier.render(notifierBox);
1660- return notifier;
1661- };
1662-
1663- // Assert the notifier box contains the expectedNumber of notifiers.
1664- var assertNumNotifiers = function(expectedNumber) {
1665- assert.equal(expectedNumber, notifierBox.get('children').size());
1666- };
1667-
1668- it('should be able to display a notification', function() {
1669- makeNotifier();
1670- assertNumNotifiers(1);
1671- });
1672-
1673- it('should display the given title and message', function() {
1674- makeNotifier('mytitle', 'mymessage');
1675- var notifierNode = notifierBox.one('*');
1676- assert.equal('mytitle', notifierNode.one('h3').getContent());
1677- assert.equal('mymessage', notifierNode.one('div').getContent());
1678- });
1679-
1680- it('should be able to display multiple notifications', function() {
1681- var number = 10;
1682- for (var i = 0; i < number; i += 1) {
1683+YUI(GlobalConfig).use(['notifier', 'node-event-simulate'], function(Y) {
1684+ describe('notifier widget', function() {
1685+ var Notifier, notifierBox;
1686+
1687+ before(function() {
1688+ Notifier = Y.namespace('juju.widgets').Notifier;
1689+ });
1690+
1691+ // Create the notifier box and attach it as first element of the body.
1692+ beforeEach(function() {
1693+ notifierBox = Y.Node.create('<div id="notifier-box"></div>');
1694+ notifierBox.setStyle('display', 'none');
1695+ Y.one('body').prepend(notifierBox);
1696+ });
1697+
1698+ // Destroy the notifier box created in beforeEach.
1699+ afterEach(function() {
1700+ notifierBox.remove();
1701+ notifierBox.destroy(true);
1702+ });
1703+
1704+ // Factory rendering and returning a notifier instance.
1705+ var makeNotifier = function(title, message, timeout) {
1706+ var notifier = new Notifier({
1707+ title: title || 'mytitle',
1708+ message: message || 'mymessage',
1709+ timeout: timeout || 10000
1710+ });
1711+ notifier.render(notifierBox);
1712+ return notifier;
1713+ };
1714+
1715+ // Assert the notifier box contains the expectedNumber of notifiers.
1716+ var assertNumNotifiers = function(expectedNumber) {
1717+ assert.equal(expectedNumber, notifierBox.get('children').size());
1718+ };
1719+
1720+ it('should be able to display a notification', function() {
1721 makeNotifier();
1722- }
1723- assertNumNotifiers(number);
1724- });
1725-
1726- it('should display new notifications on top', function() {
1727- makeNotifier('mytitle1', 'mymessage1');
1728- makeNotifier('mytitle2', 'mymessage2');
1729- var notifierNode = notifierBox.one('*');
1730- assert.equal('mytitle2', notifierNode.one('h3').getContent());
1731- assert.equal('mymessage2', notifierNode.one('div').getContent());
1732- });
1733-
1734- it('should destroy notifications after N milliseconds', function(done) {
1735- makeNotifier('mytitle', 'mymessage', 1);
1736- // A timeout of 250 milliseconds is used so that we ensure the destroying
1737- // animation can be completed.
1738- setTimeout(function() {
1739- assertNumNotifiers(0);
1740- done();
1741- }, 250);
1742- });
1743-
1744- it('should destroy notifications on click', function(done) {
1745- makeNotifier();
1746- notifierBox.one('*').simulate('click');
1747- // A timeout of 250 milliseconds is used so that we ensure the destroying
1748- // animation can be completed.
1749- setTimeout(function() {
1750- assertNumNotifiers(0);
1751- done();
1752- }, 250);
1753- });
1754-
1755- it('should prevent notification removal on mouse enter', function(done) {
1756- makeNotifier('mytitle', 'mymessage', 1);
1757- notifierBox.one('*').simulate('mouseover');
1758- // A timeout of 250 milliseconds is used so that we ensure the node is not
1759- // preserved by the destroying animation.
1760- setTimeout(function() {
1761 assertNumNotifiers(1);
1762- done();
1763- }, 250);
1764+ });
1765+
1766+ it('should display the given title and message', function() {
1767+ makeNotifier('mytitle', 'mymessage');
1768+ var notifierNode = notifierBox.one('*');
1769+ assert.equal('mytitle', notifierNode.one('h3').getContent());
1770+ assert.equal('mymessage', notifierNode.one('div').getContent());
1771+ });
1772+
1773+ it('should be able to display multiple notifications', function() {
1774+ var number = 10;
1775+ for (var i = 0; i < number; i += 1) {
1776+ makeNotifier();
1777+ }
1778+ assertNumNotifiers(number);
1779+ });
1780+
1781+ it('should display new notifications on top', function() {
1782+ makeNotifier('mytitle1', 'mymessage1');
1783+ makeNotifier('mytitle2', 'mymessage2');
1784+ var notifierNode = notifierBox.one('*');
1785+ assert.equal('mytitle2', notifierNode.one('h3').getContent());
1786+ assert.equal('mymessage2', notifierNode.one('div').getContent());
1787+ });
1788+
1789+ it('should destroy notifications after N milliseconds', function(done) {
1790+ makeNotifier('mytitle', 'mymessage', 1);
1791+ // A timeout of 250 milliseconds is used so that we ensure the destroying
1792+ // animation can be completed.
1793+ setTimeout(function() {
1794+ assertNumNotifiers(0);
1795+ done();
1796+ }, 250);
1797+ });
1798+
1799+ it('should destroy notifications on click', function(done) {
1800+ makeNotifier();
1801+ notifierBox.one('*').simulate('click');
1802+ // A timeout of 250 milliseconds is used so that we ensure the destroying
1803+ // animation can be completed.
1804+ setTimeout(function() {
1805+ assertNumNotifiers(0);
1806+ done();
1807+ }, 250);
1808+ });
1809+
1810+ it('should prevent notification removal on mouse enter', function(done) {
1811+ makeNotifier('mytitle', 'mymessage', 1);
1812+ notifierBox.one('*').simulate('mouseover');
1813+ // A timeout of 250 milliseconds is used so that we ensure the node is not
1814+ // preserved by the destroying animation.
1815+ setTimeout(function() {
1816+ assertNumNotifiers(1);
1817+ done();
1818+ }, 250);
1819+ });
1820+
1821 });
1822-
1823 });

Subscribers

People subscribed via source and target branches