Merge lp:~benji/juju-gui/bug-1081205 into lp:juju-gui/experimental
- bug-1081205
- Merge into trunk
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Benji York (community) | code | Approve | |
Review via email: mp+135952@code.launchpad.net |
Commit message
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.
To post a comment you must log in.
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 | }); |
This branch has already been reviewed separately, I am just landing it.