Merge lp:~benji/juju-gui/tweak-show_environment-2 into lp:juju-gui/experimental

Proposed by Benji York
Status: Merged
Approved by: Kapil Thangavelu
Approved revision: 204
Merged at revision: 211
Proposed branch: lp:~benji/juju-gui/tweak-show_environment-2
Merge into: lp:juju-gui/experimental
Diff against target: 2644 lines (+652/-722)
15 files modified
app/app.js (+24/-23)
app/templates/service-footer-common-controls.partial (+2/-2)
app/views/charm-search.js (+92/-17)
app/views/environment.js (+4/-5)
app/views/service.js (+84/-69)
app/views/unit.js (+10/-10)
app/views/utils.js (+3/-3)
test/test_application_notifications.js (+269/-298)
test/test_charm_configuration.js (+26/-35)
test/test_charm_search.js (+3/-2)
test/test_environment_view.js (+1/-1)
test/test_notifications.js (+0/-1)
test/test_service_config_view.js (+17/-45)
test/test_service_view.js (+75/-139)
test/test_unit_view.js (+42/-72)
To merge this branch: bzr merge lp:~benji/juju-gui/tweak-show_environment-2
Reviewer Review Type Date Requested Status
Kapil Thangavelu (community) Approve
Review via email: mp+130552@code.launchpad.net

Description of the change

Stop sending the app object to views.

This refactoring is intended to flatten the namespace views use and make the
values available to views both more constrained and better specified.

https://codereview.appspot.com/6735048/

To post a comment you must log in.
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

mostly procedural, looks good to me.

https://codereview.appspot.com/6735048/

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

noticing a few errors in practice. changing units is broken. still
investigating for others.

https://codereview.appspot.com/6735048/

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

On 2012/10/19 19:44:34, hazmat wrote:
> noticing a few errors in practice. changing units is broken. still
investigating
> for others.

most of the app seems to be broken with this branch, adding relations,
modifying services. etc.

https://codereview.appspot.com/6735048/

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

looks good to me

review: Approve

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-10-19 01:44:45 +0000
3+++ app/app.js 2012-10-25 10:13:22 +0000
4@@ -273,7 +273,7 @@
5 'unit',
6 // The querystring is used to handle highlighting relation rows in
7 // links from notifications about errors.
8- { unit: unit, db: this.db, env: this.env, app: this,
9+ { unit: unit, db: this.db, env: this.env,
10 querystring: req.query });
11 },
12
13@@ -287,7 +287,7 @@
14 if (Y.Lang.isValue(service)) {
15 if (!service.get('loaded')) {
16 this.env.get_service(
17- service.get('id'), Y.bind(this.load_service, this));
18+ service.get('id'), Y.bind(this.loadService, this));
19 }
20 var charm_id = service.get('charm'),
21 self = this;
22@@ -308,7 +308,9 @@
23 this._prefetch_service(service);
24 this.showView(viewName, {
25 model: service,
26- app: this,
27+ db: this.db,
28+ env: this.env,
29+ getModelURL: Y.bind(this.getModelURL, this),
30 querystring: req.query
31 }, {}, function(view) {
32 // If the view contains a method call fitToWindow,
33@@ -355,7 +357,6 @@
34
35 show_notifications_overview: function(req) {
36 this.showView('notifications_overview', {
37- app: this,
38 env: this.env,
39 notifications: this.db.notifications});
40 },
41@@ -373,7 +374,6 @@
42 if (!instance) {
43 view.instance = new views.NotificationsView(
44 {container: Y.one('#notifications'),
45- app: this,
46 env: this.env,
47 notifications: this.db.notifications});
48 view.instance.render();
49@@ -397,14 +397,17 @@
50
51 show_environment: function(req, res, next) {
52 var view = this.getViewInfo('environment'),
53- instance = view.instance;
54+ instance = view.instance,
55+ self = this;
56 if (!instance) {
57 console.log('new env view');
58- this.showView('environment', {
59- app: this,
60- db: this.db,
61- env: this.env},
62- {render: true});
63+ this.showView('environment',
64+ { getModelURL: Y.bind(this.getModelURL, this),
65+ getServiceEndpoints: function() {return self.serviceEndpoints;},
66+ loadService: this.loadService,
67+ db: this.db,
68+ env: this.env},
69+ {render: true});
70 } else {
71 /* The current impl makes extensive use of
72 * event handlers which are not being properly rebound
73@@ -412,23 +415,21 @@
74 * to enable this but we have to land the basics of this branch
75 * first.
76 */
77- this.showView('environment', {app: this,
78- db: this.db,
79- env: this.env}, {
80- update: false,
81- render: true,
82- callback: function(view) {
83- //view.attachView();
84- view.postRender();
85- //view.updateCanvas();
86- }
87- });
88+ this.showView('environment',
89+ { getModelURL: Y.bind(this.getModelURL, this),
90+ getServiceEndpoints: function() {return self.serviceEndpoints;},
91+ loadService: this.loadService,
92+ db: this.db,
93+ env: this.env},
94+ { update: false,
95+ render: true,
96+ callback: function(view) {view.postRender();}});
97 }
98 next();
99 },
100
101 // Model interactions -> move to db layer
102- load_service: function(evt) {
103+ loadService: function(evt) {
104 console.log('load service', evt);
105 if (evt.err) {
106 this.db.notifications.add(
107
108=== modified file 'app/templates/service-footer-common-controls.partial'
109--- app/templates/service-footer-common-controls.partial 2012-10-22 11:57:15 +0000
110+++ app/templates/service-footer-common-controls.partial 2012-10-25 10:13:22 +0000
111@@ -4,7 +4,7 @@
112 <div class="inline"><span>Unit count</span></div>
113 <div class="inline">
114 <input type="text" id="num-service-units" value="{{service.unit_count}}">
115- <img class="divider"
116+ <img class="divider"
117 src="/juju-ui/assets/images/bottom_bar_small_div.png" />
118 </div>
119 </div>
120@@ -25,4 +25,4 @@
121 {{/if}}
122 </div>
123 </div>
124-</div>
125\ No newline at end of file
126+</div>
127
128=== modified file 'app/views/charm-search.js'
129--- app/views/charm-search.js 2012-10-25 07:38:57 +0000
130+++ app/views/charm-search.js 2012-10-25 10:13:22 +0000
131@@ -101,9 +101,80 @@
132 { name: 'configuration',
133 charmId: ev.currentTarget.getData('url')});
134 },
135+ // Create a data structure friendly to the view
136+ normalizeCharms: function(charms) {
137+ var hash = {},
138+ defaultSeries = this.get('defaultSeries');
139+ Y.each(charms, function(charm) {
140+ charm.url = charm.series + '/' + charm.name;
141+ if (charm.owner === 'charmers') {
142+ charm.owner = null;
143+ } else {
144+ charm.url = '~' + charm.owner + '/' + charm.url;
145+ }
146+ charm.url = 'cs:' + charm.url;
147+ if (!Y.Lang.isValue(hash[charm.series])) {
148+ hash[charm.series] = [];
149+ }
150+ hash[charm.series].push(charm);
151+ });
152+ var series_names = Y.Object.keys(hash);
153+ series_names.sort(function(a, b) {
154+ if ((a === defaultSeries && b !== defaultSeries) || a > b) {
155+ return -1;
156+ } else if ((a !== defaultSeries && b === defaultSeries) || a < b) {
157+ return 1;
158+ } else {
159+ return 0;
160+ }
161+ });
162+ return Y.Array.map(series_names, function(name) {
163+ var charms = hash[name];
164+ charms.sort(function(a, b) {
165+ // If !a.owner, that means it is owned by charmers.
166+ if ((!a.owner && b.owner) || (a.owner < b.owner)) {
167+ return -1;
168+ } else if ((a.owner && !b.owner) || (a.owner > b.owner)) {
169+ return 1;
170+ } else if (a.name < b.name) {
171+ return -1;
172+ } else if (a.name > b.name) {
173+ return 1;
174+ } else {
175+ return 0;
176+ }
177+ });
178+ return {series: name, charms: hash[name]};
179+ });
180+ },
181+ findCharms: function(query, callback) {
182+ var charmStore = this.get('charmStore'),
183+ db = this.get('db');
184+ charmStore.sendRequest({
185+ request: 'search/json?search_text=' + query,
186+ callback: {
187+ 'success': Y.bind(function(io_request) {
188+ // To see an example of what is being obtained, look at
189+ // http://jujucharms.com/search/json?search_text=mysql .
190+ var result_set = Y.JSON.parse(
191+ io_request.response.results[0].responseText);
192+ console.log('results update', result_set);
193+ callback(this.normalizeCharms(result_set.results));
194+ }, this),
195+ 'failure': function er(e) {
196+ console.error(e.error);
197+ db.notifications.add(
198+ new models.Notification({
199+ title: 'Could not retrieve charms',
200+ message: e.error,
201+ level: 'error'
202+ })
203+ );
204+ }}});
205+ },
206 _showErrors: function(e) {
207 console.error(e.error);
208- this.get('app').db.notifications.add(
209+ this.get('db').notifications.add(
210 new models.Notification({
211 title: 'Could not retrieve charms',
212 message: e.error,
213@@ -268,8 +339,8 @@
214 // Some file read errors don't go through the error handler as
215 // expected but instead return an empty string. Warn the user if
216 // this happens.
217- var app = this.get('app');
218- app.db.notifications.add(
219+ var db = this.get('db');
220+ db.notifications.add(
221 new models.Notification({
222 title: 'Configuration file error',
223 message: 'The configuration file loaded is empty. ' +
224@@ -298,8 +369,8 @@
225 msg = 'An error occurred reading this file.';
226 }
227 if (msg) {
228- var app = this.get('app');
229- app.db.notifications.add(
230+ var db = this.get('db');
231+ db.notifications.add(
232 new models.Notification({
233 title: 'Error reading configuration file',
234 message: msg,
235@@ -314,7 +385,8 @@
236 },
237 onCharmDeployClicked: function(evt) {
238 var container = this.get('container'),
239- app = this.get('app'),
240+ db = this.get('db'),
241+ env = this.get('env'),
242 serviceName = container.one('#service-name').get('value'),
243 numUnits = container.one('#number-units').get('value'),
244 charm = this.get('model'),
245@@ -323,11 +395,11 @@
246 '#service-config .config-field');
247 // The service names must be unique. It is an error to deploy a
248 // service with same name.
249- var existing_service = app.db.services.getById(serviceName);
250+ var existing_service = db.services.getById(serviceName);
251 if (Y.Lang.isValue(existing_service)) {
252 console.log('Attempting to add service of the same name: ' +
253 serviceName);
254- app.db.notifications.add(
255+ db.notifications.add(
256 new models.Notification({
257 title: 'Attempting to deploy service ' + serviceName,
258 message: 'A service with that name already exists.',
259@@ -339,11 +411,11 @@
260 config = null;
261 }
262 numUnits = parseInt(numUnits, 10);
263- app.env.deploy(url, serviceName, config, this.configFileContent,
264- numUnits, function(ev) {
265+ env.deploy(url, serviceName, config, this.configFileContent,
266+ numUnits, function(ev) {
267 if (ev.err) {
268 console.log(url + ' deployment failed');
269- app.db.notifications.add(
270+ db.notifications.add(
271 new models.Notification({
272 title: 'Error deploying ' + serviceName,
273 message: 'Could not deploy the requested service.',
274@@ -351,7 +423,7 @@
275 }));
276 } else {
277 console.log(url + ' deployed');
278- app.db.notifications.add(
279+ db.notifications.add(
280 new models.Notification({
281 title: 'Deployed ' + serviceName,
282 message: 'Successfully deployed the requested service.',
283@@ -367,9 +439,9 @@
284 loaded: false,
285 config: config
286 });
287- app.db.services.add([service]);
288+ db.services.add([service]);
289 // Force refresh.
290- app.db.fire('update');
291+ db.fire('update');
292 }
293 });
294 this.goBack(evt);
295@@ -400,16 +472,19 @@
296 charmsSearchPanelNode = Y.Node.create(),
297 charmsSearchPanel = new CharmCollectionView(
298 { container: charmsSearchPanelNode,
299- app: app,
300+ env: app.env,
301+ db: app.db,
302 charmStore: charmStore }),
303 descriptionPanelNode = Y.Node.create(),
304 descriptionPanel = new CharmDescriptionView(
305 { container: descriptionPanelNode,
306- app: app }),
307+ env: app.env,
308+ db: app.db}),
309 configurationPanelNode = Y.Node.create(),
310 configurationPanel = new CharmConfigurationView(
311 { container: configurationPanelNode,
312- app: app}),
313+ env: app.env,
314+ db: app.db}),
315 panels =
316 { charms: charmsSearchPanel,
317 description: descriptionPanel,
318
319=== modified file 'app/views/environment.js'
320--- app/views/environment.js 2012-10-20 18:16:19 +0000
321+++ app/views/environment.js 2012-10-25 10:13:22 +0000
322@@ -415,7 +415,6 @@
323 updateData: function() {
324 //model data
325 var vis = this.vis,
326- app = this.get('app'),
327 db = this.get('db'),
328 relations = db.relations.toArray(),
329 services = db.services.map(views.toBoundingBox);
330@@ -1369,7 +1368,7 @@
331 },
332
333 _destroyCallback: function(service, view, btn, ev) {
334- var app = view.get('app'),
335+ var getModelURL = view.get('getModelURL'),
336 db = view.get('db');
337 if (ev.err) {
338 db.notifications.add(
339@@ -1377,7 +1376,7 @@
340 title: 'Error destroying service',
341 message: 'Service name: ' + ev.service_name,
342 level: 'error',
343- link: app.getModelURL(service),
344+ link: getModelURL(service),
345 modelId: service
346 })
347 );
348@@ -1402,10 +1401,10 @@
349 view.show(view.vis.selectAll('.service'));
350
351 var db = view.get('db'),
352- app = view.get('app'),
353+ getServiceEndpoints = view.get('getServiceEndpoints'),
354 service = view.serviceForBox(m),
355 endpoints = models.getEndpoints(
356- service, app.serviceEndpoints, db),
357+ service, getServiceEndpoints(), db),
358
359 /* Transform endpoints into a list of
360 * relatable services (to the service in m)
361
362=== modified file 'app/views/service.js'
363--- app/views/service.js 2012-10-22 11:57:15 +0000
364+++ app/views/service.js 2012-10-25 10:13:22 +0000
365@@ -53,7 +53,7 @@
366 var service = this.get('model'),
367 unit_count = service.get('unit_count'),
368 field = this.get('container').one('#num-service-units'),
369- env = this.get('app').env;
370+ env = this.get('env');
371
372 if (requested_unit_count < 1) {
373 console.log('You must have at least one unit');
374@@ -69,7 +69,7 @@
375 Y.bind(this._addUnitCallback, this));
376 } else if (delta < 0) {
377 delta = Math.abs(delta);
378- var units = this.get('app').db.units.get_units_for_service(service),
379+ var units = this.get('db').units.get_units_for_service(service),
380 unit_ids_to_remove = [];
381
382 for (var i = units.length - 1;
383@@ -87,8 +87,8 @@
384
385 _addUnitCallback: function(ev) {
386 var service = this.get('model'),
387- app = this.get('app'),
388- db = this.get('app').db,
389+ getModelURL = this.get('getModelURL'),
390+ db = this.get('db'),
391 unit_names = ev.result || [];
392 if (ev.err) {
393 db.notifications.add(
394@@ -96,7 +96,7 @@
395 title: 'Error adding unit',
396 message: ev.num_units + ' units',
397 level: 'error',
398- link: app.getModelURL(service),
399+ link: getModelURL(service),
400 modelId: service
401 })
402 );
403@@ -115,8 +115,8 @@
404
405 _removeUnitCallback: function(ev) {
406 var service = this.get('model'),
407- app = this.get('app'),
408- db = this.get('app').db,
409+ getModelURL = this.get('getModelURL'),
410+ db = this.get('db'),
411 unit_names = ev.unit_names;
412 console.log('_removeUnitCallback with: ', arguments);
413
414@@ -139,7 +139,7 @@
415 return 'Unit name: ' + ev.unit_names[0];
416 })(),
417 level: 'error',
418- link: app.getModelURL(service),
419+ link: getModelURL(service),
420 modelId: service
421 })
422 );
423@@ -181,7 +181,7 @@
424
425 destroyService: function(ev) {
426 ev.preventDefault();
427- var env = this.get('app').env,
428+ var env = this.get('env'),
429 service = this.get('model');
430 ev.target.set('disabled', true);
431 env.destroy_service(
432@@ -189,8 +189,8 @@
433 },
434
435 _destroyCallback: function(ev) {
436- var db = this.get('app').db,
437- app = this.get('app'),
438+ var db = this.get('db'),
439+ getModelURL = this.get('getModelURL'),
440 service = this.get('model'),
441 service_id = service.get('id');
442
443@@ -200,7 +200,7 @@
444 title: 'Error destroying service',
445 message: 'Service name: ' + ev.service_name,
446 level: 'error',
447- link: app.getModelURL(service),
448+ link: getModelURL(service),
449 modelId: service
450 })
451 );
452@@ -229,22 +229,22 @@
453
454 unexposeService: function() {
455 var service = this.get('model'),
456- env = this.get('app').env;
457+ env = this.get('env');
458 env.unexpose(service.get('id'),
459 Y.bind(this._unexposeServiceCallback, this));
460 },
461
462 _unexposeServiceCallback: function(ev) {
463 var service = this.get('model'),
464- db = this.get('app').db,
465- app = this.get('app');
466+ db = this.get('db'),
467+ getModelURL = this.get('getModelURL');
468 if (ev.err) {
469 db.notifications.add(
470 new models.Notification({
471 title: 'Error un-exposing service',
472 message: 'Service name: ' + ev.service_name,
473 level: 'error',
474- link: app.getModelURL(service),
475+ link: getModelURL(service),
476 modelId: service
477 })
478 );
479@@ -256,22 +256,22 @@
480
481 exposeService: function() {
482 var service = this.get('model'),
483- env = this.get('app').env;
484+ env = this.get('env');
485 env.expose(service.get('id'),
486 Y.bind(this._exposeServiceCallback, this));
487 },
488
489 _exposeServiceCallback: function(ev) {
490 var service = this.get('model'),
491- db = this.get('app').db,
492- app = this.get('app');
493+ db = this.get('db'),
494+ getModelURL = this.get('getModelURL');
495 if (ev.err) {
496 db.notifications.add(
497 new models.Notification({
498 title: 'Error exposing service',
499 message: 'Service name: ' + ev.service_name,
500 level: 'error',
501- link: app.getModelURL(service),
502+ link: getModelURL(service),
503 modelId: service
504 })
505 );
506@@ -298,11 +298,11 @@
507 },
508
509 getServiceTabs: function(href) {
510- var app = this.get('app'),
511+ var db = this.get('db'),
512 service = this.get('model'),
513 charmId = service.get('charm'),
514- charm = app.db.charms.getById(charmId),
515- charmUrl = (charm ? app.getModelURL(charm) : '#');
516+ charm = db.charms.getById(charmId),
517+ charmUrl = (charm ? this.get('getModelURL')(charm) : '#');
518
519 var tabs = [{
520 href: '.',
521@@ -372,28 +372,32 @@
522
523 render: function() {
524 var container = this.get('container'),
525- app = this.get('app'),
526+ getModelURL = this.get('getModelURL'),
527 service = this.get('model'),
528+ db = this.get('db'),
529 querystring = this.get('querystring');
530- if (!service) {
531+ if (!service || !service.get('loaded')) {
532 container.setHTML('<div class="alert">Loading...</div>');
533 console.log('waiting on service data');
534 return this;
535 }
536- var relation_data = utils.getRelationDataForService(app.db, service);
537+ var relation_data = utils.getRelationDataForService(db, service);
538 Y.each(relation_data, function(rel) {
539 if (rel.relation_id === querystring.rel_id) {
540 rel.highlight = true;
541 }
542 });
543-
544+ var charm_id = service.get('charm'),
545+ charm = db.charms.getById(charm_id),
546+ charm_attrs = charm ? charm.getAttrs() : undefined;
547 container.setHTML(this.template(
548 { viewName: 'relations',
549 tabs: this.getServiceTabs('relations'),
550 service: service.getAttrs(),
551 relations: relation_data,
552- charm: this.renderable_charm(service.get('charm'), app)}
553- ));
554+ charm: charm_attrs,
555+ charm_id: charm_id}));
556+
557 },
558
559 confirmRemoved: function(ev) {
560@@ -421,9 +425,10 @@
561 doRemoveRelation: function(button, ev) {
562 ev.preventDefault();
563 var rel_id = button.get('value'),
564- app = this.get('app'),
565+ db = this.get('db'),
566+ env = this.get('env'),
567 service = this.get('model'),
568- relation = app.db.relations.getById(rel_id),
569+ relation = db.relations.getById(rel_id),
570 endpoints = relation.get('endpoints'),
571 endpoint_a = endpoints[0][0] + ':' + endpoints[0][1].name,
572 endpoint_b;
573@@ -437,7 +442,7 @@
574
575 ev.target.set('disabled', true);
576
577- app.env.remove_relation(
578+ env.remove_relation(
579 endpoint_a,
580 endpoint_b,
581 Y.bind(this._removeRelationCallback, this,
582@@ -446,23 +451,24 @@
583
584 _removeRelationCallback: function(relation, rm_button,
585 confirm_button, ev) {
586- var app = this.get('app'),
587+ var db = this.get('db'),
588+ getModelURL = this.get('getModelURL'),
589 service = this.get('model');
590 views.highlightRow(rm_button.ancestor('tr'), ev.err);
591 if (ev.err) {
592- app.db.notifications.add(
593+ db.notifications.add(
594 new models.Notification({
595 title: 'Error deleting relation',
596 message: 'Relation ' + ev.endpoint_a + ' to ' + ev.endpoint_b,
597 level: 'error',
598- link: app.getModelURL(service) + 'relations?rel_id=' +
599+ link: getModelURL(service) + 'relations?rel_id=' +
600 relation.get('id'),
601 modelId: relation
602 })
603 );
604 } else {
605- app.db.relations.remove(relation);
606- app.db.fire('update');
607+ db.relations.remove(relation);
608+ db.fire('update');
609 }
610 confirm_button.set('disabled', false);
611 this.remove_panel.hide();
612@@ -483,7 +489,7 @@
613 updateConstraints: function() {
614 var service = this.get('model'),
615 container = this.get('container'),
616- env = this.get('app').env;
617+ env = this.get('env');
618
619 var values = (function() {
620 var result = [],
621@@ -508,9 +514,10 @@
622
623 _setConstraintsCallback: function(container, ev) {
624 var service = this.get('model'),
625- env = this.get('app').env,
626- app = this.get('app'),
627- db = this.get('app').db;
628+ env = this.get('env'),
629+ getModelURL = this.get('getModelURL'),
630+ loadService = this.get('loadService'),
631+ db = this.get('db');
632
633 if (ev.err) {
634 db.notifications.add(
635@@ -518,7 +525,7 @@
636 title: 'Error setting service constraints',
637 message: 'Service name: ' + ev.service_name,
638 level: 'error',
639- link: app.getModelURL(service) + 'constraints',
640+ link: getModelURL(service) + 'constraints',
641 modelId: service
642 })
643 );
644@@ -527,7 +534,7 @@
645
646 } else {
647 env.get_service(
648- service.get('id'), Y.bind(app.load_service, app));
649+ service.get('id'), loadService);
650
651 // The usual result of a successful request is a page refresh.
652 // Therefore, we need to set this delay in order to show the
653@@ -540,11 +547,11 @@
654
655 render: function() {
656 var container = this.get('container'),
657- app = this.get('app'),
658- service = this.get('model');
659-
660- var constraints = service.get('constraints');
661- var display_constraints = [];
662+ service = this.get('model'),
663+ getModelURL = this.get('getModelURL'),
664+ db = this.get('db'),
665+ constraints = service.get('constraints'),
666+ display_constraints = [];
667
668 //these are read-only values
669 var readOnlyConstraints = {
670@@ -601,7 +608,7 @@
671
672 render: function() {
673 var container = this.get('container'),
674- app = this.get('app'),
675+ db = this.get('db'),
676 service = this.get('model');
677
678 if (!service || !service.get('loaded')) {
679@@ -613,8 +620,9 @@
680 console.log('config', service.get('config'));
681
682 // combine the charm schema and the service values for display.
683- var charm = app.db.charms.getById(service.get('charm')),
684+ var charm = db.charms.getById(service.get('charm')),
685 config = service.get('config'),
686+ getModelURL = this.get('getModelURL'),
687 charm_config = charm.get('config'),
688 schema = charm_config && charm_config.options,
689 settings = [],
690@@ -698,10 +706,12 @@
691 },
692
693 saveConfig: function() {
694- var app = this.get('app'),
695+ var env = this.get('env'),
696+ db = this.get('db'),
697+ getModelURL = this.get('getModelURL'),
698 service = this.get('model'),
699 charm_url = service.get('charm'),
700- charm = app.db.charms.getById(charm_url),
701+ charm = db.charms.getById(charm_url),
702 charm_config = charm.get('config'),
703 schema = charm_config && charm_config.options,
704 container = this.get('container');
705@@ -714,7 +724,7 @@
706 errors = utils.validate(new_values, schema);
707
708 if (Y.Object.isEmpty(errors)) {
709- app.env.set_config(
710+ env.set_config(
711 service.get('id'),
712 new_values,
713 Y.bind(this._setConfigCallback, this, container)
714@@ -727,9 +737,10 @@
715
716 _setConfigCallback: function(container, ev) {
717 var service = this.get('model'),
718- env = this.get('app').env,
719- app = this.get('app'),
720- db = this.get('app').db;
721+ env = this.get('env'),
722+ getModelURL = this.get('getModelURL'),
723+ loadService = this.get('loadService'),
724+ db = this.get('db');
725
726 if (ev.err) {
727 db.notifications.add(
728@@ -737,7 +748,7 @@
729 title: 'Error setting service config',
730 message: 'Service name: ' + ev.service_name,
731 level: 'error',
732- link: app.getModelURL(service) + 'config',
733+ link: getModelURL(service) + 'config',
734 modelId: service
735 })
736 );
737@@ -745,7 +756,7 @@
738 .removeAttribute('disabled');
739
740 } else {
741- env.get_service(service.get('id'), Y.bind(app.load_service, app));
742+ env.get_service(service.get('id'), loadService);
743
744 // The usual result of a successful request is a page refresh.
745 // Therefore, we need to set this delay in order to show the
746@@ -791,7 +802,8 @@
747 console.log('service view render');
748
749 var container = this.get('container'),
750- app = this.get('app'),
751+ getModelURL = this.get('getModelURL'),
752+ db = this.get('db'),
753 service = this.get('model'),
754 filter_state = this.get('querystring').state;
755
756@@ -801,7 +813,7 @@
757 return this;
758 }
759
760- var units = app.db.units.get_units_for_service(service),
761+ var units = db.units.get_units_for_service(service),
762 state_data = [{
763 title: 'All',
764 link: '.',
765@@ -817,15 +829,18 @@
766 count: this.filterUnits(lower, units).length,
767 link: '?state=' + lower});
768 }, this);
769- container.setHTML(this.template({
770- viewName: 'units',
771- tabs: this.getServiceTabs('.'),
772- service: service.getAttrs(),
773- charm_id: service.get('charm'),
774- state: filter_state,
775- units: this.filterUnits(filter_state, units),
776- states: state_data
777- }));
778+ var charm_id = service.get('charm'),
779+ charm = db.charms.getById(charm_id),
780+ charm_attrs = charm ? charm.getAttrs() : undefined;
781+ container.setHTML(this.template(
782+ { viewName: 'units',
783+ tabs: this.getServiceTabs('.'),
784+ service: service.getAttrs(),
785+ charm_id: charm_id,
786+ charm: charm_attrs,
787+ state: filter_state,
788+ units: this.filterUnits(filter_state, units),
789+ states: state_data}));
790 return this;
791 },
792
793
794=== modified file 'app/views/unit.js'
795--- app/views/unit.js 2012-10-05 20:39:08 +0000
796+++ app/views/unit.js 2012-10-25 10:13:22 +0000
797@@ -123,7 +123,7 @@
798 _resolvedUnitCallback: function(button, ev) {
799 var unit = this.get('unit'),
800 db = this.get('db'),
801- app = this.get('app'),
802+ getModelURL = this.get('getModelURL'),
803 service = db.services.getById(unit.service);
804 if (ev.err) {
805 db.notifications.add(
806@@ -131,7 +131,7 @@
807 title: 'Error resolving unit',
808 message: 'Unit name: ' + ev.unit_name,
809 level: 'error',
810- link: app.getModelURL(unit),
811+ link: getModelURL(unit),
812 modelId: unit
813 })
814 );
815@@ -172,7 +172,7 @@
816 _removeUnitCallback: function(btn, ev) {
817 var unit = this.get('unit'),
818 db = this.get('db'),
819- app = this.get('app'),
820+ getModelURL = this.get('getModelURL'),
821 service = db.services.getById(unit.service),
822 unit_name = ev.unit_names[0];
823
824@@ -182,7 +182,7 @@
825 title: 'Error removing unit',
826 message: 'Unit name: ' + unit_name,
827 level: 'error',
828- link: app.getModelURL(unit),
829+ link: getModelURL(unit),
830 modelId: unit
831 })
832 );
833@@ -201,7 +201,7 @@
834 var env = this.get('env'),
835 unit = this.get('unit'),
836 db = this.get('db'),
837- app = this.get('app'),
838+ getModelURL = this.get('getModelURL'),
839 service = db.services.getById(unit.service),
840 button = ev.target;
841 button.set('disabled', true);
842@@ -213,7 +213,7 @@
843 title: 'Error retrying unit',
844 message: 'Unit name: ' + ev.unit_name,
845 level: 'error',
846- link: app.getModelURL(unit),
847+ link: getModelURL(unit),
848 modelId: unit
849 })
850 );
851@@ -257,7 +257,7 @@
852 views.highlightRow(button.ancestor('tr'), ev.err);
853 if (ev.err) {
854 var db = this.get('db'),
855- app = this.get('app'),
856+ getModelURL = this.get('getModelURL'),
857 unit = this.get('unit'),
858 relation_id = button.ancestor('tr').get('id'),
859 relation = db.relations.getById(relation_id);
860@@ -266,7 +266,7 @@
861 title: 'Error resolving relation',
862 message: 'Could not resolve a unit relation',
863 level: 'error',
864- link: app.getModelURL(unit) + '?rel_id=' + relation_id,
865+ link: getModelURL(unit) + '?rel_id=' + relation_id,
866 modelId: relation
867 })
868 );
869@@ -280,7 +280,7 @@
870 var env = this.get('env'),
871 unit = this.get('unit'),
872 db = this.get('db'),
873- app = this.get('app'),
874+ getModelURL = this.get('getModelURL'),
875 button = ev.target,
876 relation_name = button.ancestor('form').get('id');
877 button.set('disabled', true);
878@@ -296,7 +296,7 @@
879 title: 'Error retrying relation',
880 message: 'Could not retry a unit relation',
881 level: 'error',
882- link: app.getModelURL(unit) + '?rel_id=' + relation_id,
883+ link: getModelURL(unit) + '?rel_id=' + relation_id,
884 modelId: relation
885 })
886 );
887
888=== modified file 'app/views/utils.js'
889--- app/views/utils.js 2012-10-20 18:17:23 +0000
890+++ app/views/utils.js 2012-10-25 10:13:22 +0000
891@@ -130,11 +130,11 @@
892 this.after(['*:change', '*:load'], this.render, this);
893 },
894
895- renderable_charm: function(charm_name, app) {
896- var charm = app.db.charms.getById(charm_name);
897+ renderable_charm: function(charm_name, db, getModelURL) {
898+ var charm = db.charms.getById(charm_name);
899 if (charm) {
900 var result = charm.getAttrs();
901- result.app_url = app.getModelURL(charm);
902+ result.app_url = getModelURL(charm);
903 return result;
904 }
905 return null;
906
907=== modified file 'test/test_application_notifications.js'
908--- test/test_application_notifications.js 2012-10-19 17:33:36 +0000
909+++ test/test_application_notifications.js 2012-10-25 10:13:22 +0000
910@@ -50,20 +50,20 @@
911
912 db = new models.Database();
913
914- var notificationsView = new views.NotificationsView({
915- container: notificationsContainer,
916- app: {},
917- env: {
918- on: NO_OP,
919- get: function(key) {
920- if (key === 'connected') {
921- return true;
922- }
923- return null;
924- }
925- },
926- notifications: db.notifications
927- });
928+ var notificationsView = new views.NotificationsView(
929+ { container: notificationsContainer,
930+ db: db,
931+ env: {
932+ on: NO_OP,
933+ get: function(key) {
934+ if (key === 'connected') {
935+ return true;
936+ }
937+ return null;
938+ }
939+ },
940+ notifications: db.notifications
941+ });
942
943 notificationsView.render();
944
945@@ -90,20 +90,18 @@
946
947 it('should show notification for "add_unit" and "remove_units" exceptions' +
948 ' (service view)', function() {
949- var view = new views.service({
950- container: viewContainer,
951- app: {
952- getModelURL: function() {
953- return 'my url';
954+ var view = new views.service(
955+ { container: viewContainer,
956+ getModelURL: function() {
957+ return 'my url';
958+ },
959+ db: db,
960+ env: {
961+ add_unit: function(serviceId, delta, callback) {
962+ callback(ERR_EV);
963 },
964- db: db,
965- env: {
966- add_unit: function(serviceId, delta, callback) {
967- callback(ERR_EV);
968- },
969- remove_units: function(param, callback) {
970- callback(ERR_EV);
971- }
972+ remove_units: function(param, callback) {
973+ callback(ERR_EV);
974 }
975 },
976 model: {
977@@ -116,8 +114,7 @@
978 return null;
979 }
980 },
981- querystring: {}
982- }).render();
983+ querystring: {}}).render();
984
985 db.units.get_units_for_service = function() {
986 return [{
987@@ -135,118 +132,109 @@
988 });
989
990 it('should show notification for "remove_units" and "resolved" exceptions' +
991- ' (unit view)', function() {
992- var view = new views.unit({
993- container: viewContainer,
994- app: {
995- getModelURL: function() {
996- return 'my url';
997- }
998- },
999+ ' (unit view)', function()
1000+ {
1001+ var view = new views.unit(
1002+ { container: viewContainer,
1003+ getModelURL: function() {return 'my url';},
1004 db: db,
1005 env: {
1006 remove_units: function(param, callback) {
1007- callback({
1008- err: true,
1009- unit_names: ['aaa']
1010- });
1011+ callback(
1012+ { err: true,
1013+ unit_names: ['aaa']});
1014 },
1015 resolved: function(unit_name, relation_name, retry, callback) {
1016 callback(ERR_EV);
1017- }
1018- },
1019+ }},
1020 unit: {},
1021 querystring: {}
1022 });
1023
1024- // Used by "unit.js" inside the "render" function
1025- db.services.getById = function() {
1026- // Mock service
1027- return {
1028- get: function(key) {
1029- if (key === 'loaded') {
1030- return true;
1031- }
1032- return null;
1033- }
1034- };
1035- };
1036-
1037- view.render();
1038-
1039- view.confirmRemoved({
1040- preventDefault: NO_OP
1041- });
1042-
1043- view.remove_panel.footerNode.one('.btn-danger').simulate('click');
1044- view.remove_panel.destroy();
1045-
1046- assertNotificationNumber('1');
1047-
1048- // Fake relation
1049- db.relations.getById = function() {
1050- return {name: ''};
1051- };
1052-
1053- view.retryRelation({
1054- preventDefault: NO_OP,
1055-
1056- // This is a mock object of the relation button
1057- target: {
1058- ancestor: function() {
1059- return {get: NO_OP};
1060- },
1061- set: NO_OP
1062- }
1063- });
1064-
1065- assertNotificationNumber('2');
1066- });
1067+ // Used by "unit.js" inside the "render" function
1068+ db.services.getById = function() {
1069+ // Mock service
1070+ return {
1071+ get: function(key) {
1072+ if (key === 'loaded') {
1073+ return true;
1074+ }
1075+ return null;
1076+ }
1077+ };
1078+ };
1079+
1080+ view.render();
1081+
1082+ view.confirmRemoved({
1083+ preventDefault: NO_OP
1084+ });
1085+
1086+ view.remove_panel.footerNode.one('.btn-danger').simulate('click');
1087+ view.remove_panel.destroy();
1088+
1089+ assertNotificationNumber('1');
1090+
1091+ // Fake relation
1092+ db.relations.getById = function() {
1093+ return {name: ''};
1094+ };
1095+
1096+ view.retryRelation({
1097+ preventDefault: NO_OP,
1098+
1099+ // This is a mock object of the relation button
1100+ target: {
1101+ ancestor: function() {
1102+ return {get: NO_OP};
1103+ },
1104+ set: NO_OP
1105+ }
1106+ });
1107+
1108+ assertNotificationNumber('2');
1109+ });
1110
1111 it('should show notification for "add_relation" and "remove_relation"' +
1112- ' exceptions (environment view)', function() {
1113- var view = new views.environment({
1114- db: db,
1115- container: viewContainer});
1116- view.render();
1117- db.relations.remove = NO_OP;
1118+ ' exceptions (environment view)', function() {
1119+ var view = new views.environment({
1120+ db: db,
1121+ container: viewContainer});
1122+ view.render();
1123+ db.relations.remove = NO_OP;
1124
1125- view.service_click_actions._addRelationCallback.apply(view,
1126+ view.service_click_actions._addRelationCallback.apply(view,
1127 [view, 'relation_id', ERR_EV]);
1128
1129- assertNotificationNumber('1');
1130-
1131- //view, relationElement, relationId, confirmButton, ev
1132- view._removeRelationCallback.apply(view, [{
1133- get: function() {return {hide: NO_OP};},
1134- removeSVGClass: NO_OP
1135- }, {}, '', {
1136- set: NO_OP
1137- }, ERR_EV]);
1138-
1139- assertNotificationNumber('2');
1140- });
1141+ assertNotificationNumber('1');
1142+
1143+ //view, relationElement, relationId, confirmButton, ev
1144+ view._removeRelationCallback.apply(view, [{
1145+ get: function() {return {hide: NO_OP};},
1146+ removeSVGClass: NO_OP
1147+ }, {}, '', {
1148+ set: NO_OP
1149+ }, ERR_EV]);
1150+
1151+ assertNotificationNumber('2');
1152+ });
1153
1154 it('should show notification for "add_relation" and "destroy_service"' +
1155- ' exceptions (environment view)', function() {
1156- var fakeLink = (function() {
1157- var link = [{}, {}];
1158- link.enter = function() {
1159- return {
1160- insert: function() {
1161- return {
1162- attr: NO_OP
1163- };
1164- }
1165- };
1166- };
1167- return link;
1168- })(),
1169- app = {
1170- getModelURL: NO_OP,
1171- updateEndpoints: NO_OP
1172- },
1173- env = {
1174+ ' exceptions (environment view)', function() {
1175+ var fakeLink = (function() {
1176+ var link = [{}, {}];
1177+ link.enter = function() {
1178+ return {
1179+ insert: function() {
1180+ return {
1181+ attr: NO_OP
1182+ };
1183+ }
1184+ };
1185+ };
1186+ return link;
1187+ })(),
1188+ env = {
1189 destroy_service: function(service, callback) {
1190 callback(ERR_EV);
1191 },
1192@@ -273,8 +261,11 @@
1193 },
1194 env: env,
1195 get: function(key) {
1196- if ('app' === key) {
1197- return app;
1198+ if ('getModelURL' === key) {
1199+ return NO_OP;
1200+ }
1201+ if ('updateEndpoints' === key) {
1202+ return NO_OP;
1203 }
1204 if ('env' === key) {
1205 return env;
1206@@ -309,71 +300,64 @@
1207 }
1208 };
1209
1210- views.environment.prototype.service_click_actions.addRelationEnd
1211+ views.environment.prototype.service_click_actions.addRelationEnd
1212 .apply(view, [
1213 [
1214 ['s1', {name: 'n', role: 'client'}],
1215 ['s2', {name: 'n', role: 'server'}]],
1216 view]);
1217
1218- assertNotificationNumber('1');
1219+ assertNotificationNumber('1');
1220
1221- views.environment.prototype.service_click_actions.destroyService.apply(
1222+ views.environment.prototype.service_click_actions.destroyService.apply(
1223 //destroyService function signature > (m, view, btn)
1224 view, [{}, view, {set: NO_OP}]);
1225
1226- assertNotificationNumber('2');
1227- });
1228+ assertNotificationNumber('2');
1229+ });
1230
1231 it('should show notification for "get_service" exceptions' +
1232- ' (service constraints view)', function() {
1233-
1234- var view = new views.service_constraints({
1235- model: {
1236- getAttrs: NO_OP,
1237- get: function(key) {
1238- if ('constraints' === key) {
1239- return {};
1240- }
1241- return null;
1242- }
1243- },
1244- app: {
1245- getModelURL: NO_OP,
1246- db: db,
1247- env: {
1248- set_constraints: function(id, values, callback) {
1249- callback(ERR_EV);
1250- }
1251- }
1252- },
1253-
1254- container: viewContainer}).render();
1255-
1256- view.updateConstraints();
1257-
1258- assertNotificationNumber('1');
1259- });
1260+ ' (service constraints view)', function() {
1261+
1262+ var view = new views.service_constraints(
1263+ { model:
1264+ { getAttrs: NO_OP,
1265+ get: function(key) {
1266+ if ('constraints' === key) {
1267+ return {};
1268+ }
1269+ return null;
1270+ }},
1271+ getModelURL: NO_OP,
1272+ db: db,
1273+ env:
1274+ { set_constraints: function(id, values, callback) {
1275+ callback(ERR_EV);
1276+ }},
1277+ container: viewContainer}).render();
1278+
1279+ view.updateConstraints();
1280+
1281+ assertNotificationNumber('1');
1282+ });
1283
1284 it('should show notification for "get_service", "expose" and "unexpose"' +
1285- ' exceptions (service config view)', function() {
1286+ ' exceptions (service config view)', function() {
1287
1288- var view = new views.service_config({
1289- app: {
1290- db: db,
1291- env: {
1292- set_config: function(id, newValues, callback) {
1293- callback(ERR_EV);
1294- },
1295- expose: function(id, callback) {
1296- callback(ERR_EV);
1297- },
1298- unexpose: function(id, callback) {
1299- callback({err: true, service_name: '1234'});
1300- }
1301- },
1302- getModelURL: NO_OP
1303+ var view = new views.service_config(
1304+ { db: db,
1305+ env: {
1306+ set_config: function(id, newValues, callback) {
1307+ callback(ERR_EV);
1308+ },
1309+ expose: function(id, callback) {
1310+ callback(ERR_EV);
1311+ },
1312+ unexpose: function(id, callback) {
1313+ callback({err: true, service_name: '1234'});
1314+ }
1315 },
1316+ getModelURL: NO_OP,
1317 model: {
1318 getAttrs: NO_OP,
1319 get: function(key) {
1320@@ -384,84 +368,81 @@
1321 return {};
1322 }
1323 return null;
1324- }
1325- },
1326+ }},
1327 container: viewContainer});
1328
1329- db.services.getById = NO_OP;
1330- db.charms.getById = function() {
1331- return {
1332- getAttrs: function() {
1333- return {};
1334- },
1335- get: function(key) {
1336- if ('config' === key) {
1337- return {};
1338- }
1339- return null;
1340- }
1341- };
1342- };
1343- view.render();
1344-
1345- view.saveConfig();
1346- assertNotificationNumber('1');
1347-
1348- view.exposeService();
1349- assertNotificationNumber('2');
1350-
1351- view.unexposeService();
1352- assertNotificationNumber('3');
1353- });
1354+ db.services.getById = NO_OP;
1355+ db.charms.getById = function() {
1356+ return {
1357+ getAttrs: function() {
1358+ return {};
1359+ },
1360+ get: function(key) {
1361+ if ('config' === key) {
1362+ return {};
1363+ }
1364+ return null;
1365+ }
1366+ };
1367+ };
1368+ view.render();
1369+
1370+ view.saveConfig();
1371+ assertNotificationNumber('1');
1372+
1373+ view.exposeService();
1374+ assertNotificationNumber('2');
1375+
1376+ view.unexposeService();
1377+ assertNotificationNumber('3');
1378+ });
1379
1380 it('should show notification for "remove_relation"' +
1381- ' exceptions (service relations view)', function() {
1382+ ' exceptions (service relations view)', function() {
1383
1384- var view = new views.service_relations({
1385- app: {
1386- db: db,
1387- env: {
1388- remove_relation: function(id, newValues, callback) {
1389- callback(ERR_EV);
1390- }
1391- },
1392- getModelURL: NO_OP
1393+ var view = new views.service_relations(
1394+ { db: db,
1395+ env: {
1396+ remove_relation: function(id, newValues, callback) {
1397+ callback(ERR_EV);
1398+ }
1399 },
1400+ getModelURL: NO_OP,
1401 container: viewContainer});
1402
1403- db.relations.getById = function() {
1404- return {
1405- get: function(key) {
1406- if ('endpoints' === key) {
1407- return [
1408- [{}, {name: ''}]
1409- ];
1410- }
1411- return null;
1412- }
1413- };
1414- };
1415-
1416- view.render();
1417-
1418- view.confirmRemoved({
1419- preventDefault: NO_OP,
1420-
1421- // This is a mock object of the relation button
1422- target: {
1423- ancestor: NO_OP,
1424- get: NO_OP
1425- }
1426- });
1427- view.remove_panel.footerNode.one('.btn-danger').simulate('click');
1428- view.remove_panel.destroy();
1429-
1430- assertNotificationNumber('1');
1431- });
1432+ db.relations.getById = function() {
1433+ return {
1434+ get: function(key) {
1435+ if ('endpoints' === key) {
1436+ return [
1437+ [{}, {name: ''}]
1438+ ];
1439+ }
1440+ return null;
1441+ }
1442+ };
1443+ };
1444+
1445+ view.render();
1446+
1447+ view.confirmRemoved({
1448+ preventDefault: NO_OP,
1449+
1450+ // This is a mock object of the relation button
1451+ target: {
1452+ ancestor: NO_OP,
1453+ get: NO_OP
1454+ }
1455+ });
1456+ view.remove_panel.footerNode.one('.btn-danger').simulate('click');
1457+ view.remove_panel.destroy();
1458+
1459+ assertNotificationNumber('1');
1460+ });
1461
1462 it('should show notification for "deploy" exceptions (charm view)',
1463- function() {
1464- var notified = false,
1465+ function() {
1466+ var notified = false,
1467 db = {
1468 notifications: {
1469 add: function() {
1470@@ -510,66 +491,56 @@
1471 }
1472 };
1473
1474- views.charm.prototype.on_charm_deploy.apply(mockView, [ERR_EV]);
1475- assert.isTrue(notified);
1476- });
1477+ views.charm.prototype.on_charm_deploy.apply(mockView, [ERR_EV]);
1478+ assert.isTrue(notified);
1479+ });
1480
1481 it('should show errors for no unit, one unit and multiple ' +
1482- 'units (service view)', function() {
1483-
1484- //_removeUnitCallback
1485- var mockView = {
1486- get: function(key) {
1487- if ('app' === key) {
1488- return {
1489- getModelURL: NO_OP,
1490- db: {
1491- fire: NO_OP,
1492- notifications: {
1493- add: function(notification) {
1494- messages.push(notification.get('message'));
1495- titles.push(notification.get('title'));
1496- }
1497- }
1498- }
1499- };
1500- }
1501- return null;
1502- }
1503- },
1504+ 'units (service view)', function() {
1505+ //_removeUnitCallback
1506+ var mockView =
1507+ { get: function(key) {
1508+ return {
1509+ getModelURL: NO_OP,
1510+ db:
1511+ { fire: NO_OP,
1512+ notifications:
1513+ { add: function(notification) {
1514+ messages.push(notification.get('message'));
1515+ titles.push(notification.get('title'));
1516+ }}}}[key];
1517+ }},
1518 messages = [],
1519 titles = [],
1520 baseView = new views.serviceBase({});
1521
1522-
1523-
1524- baseView._removeUnitCallback.apply(mockView, [{
1525- err: true,
1526- unit_names: null
1527- }]);
1528-
1529- baseView._removeUnitCallback.apply(mockView, [{
1530- err: true,
1531- unit_names: []
1532- }]);
1533-
1534- baseView._removeUnitCallback.apply(mockView, [{
1535- err: true,
1536- unit_names: ['a']
1537- }]);
1538-
1539- baseView._removeUnitCallback.apply(mockView, [{
1540- err: true,
1541- unit_names: ['b', 'c']
1542- }]);
1543-
1544- function assertTrace(expected, trace) {
1545- assert.equal(expected.join(';'), trace.join(';'));
1546- }
1547-
1548- assertTrace(['Error removing unit', 'Error removing unit',
1549- 'Error removing unit', 'Error removing units'], titles);
1550- assertTrace(['', '', 'Unit name: a', 'Unit names: b, c'], messages);
1551- });
1552+ baseView._removeUnitCallback.apply(mockView, [{
1553+ err: true,
1554+ unit_names: null
1555+ }]);
1556+
1557+ baseView._removeUnitCallback.apply(mockView, [{
1558+ err: true,
1559+ unit_names: []
1560+ }]);
1561+
1562+ baseView._removeUnitCallback.apply(mockView, [{
1563+ err: true,
1564+ unit_names: ['a']
1565+ }]);
1566+
1567+ baseView._removeUnitCallback.apply(mockView, [{
1568+ err: true,
1569+ unit_names: ['b', 'c']
1570+ }]);
1571+
1572+ function assertTrace(expected, trace) {
1573+ assert.equal(expected.join(';'), trace.join(';'));
1574+ }
1575+
1576+ assertTrace(['Error removing unit', 'Error removing unit',
1577+ 'Error removing unit', 'Error removing units'], titles);
1578+ assertTrace(['', '', 'Unit name: a', 'Unit names: b, c'], messages);
1579+ });
1580
1581 });
1582
1583=== modified file 'test/test_charm_configuration.js'
1584--- test/test_charm_configuration.js 2012-10-20 18:17:23 +0000
1585+++ test/test_charm_configuration.js 2012-10-25 10:13:22 +0000
1586@@ -1,7 +1,7 @@
1587 'use strict';
1588
1589 describe('charm configuration', function() {
1590- var Y, juju, app, db, models, views, container,
1591+ var Y, juju, db, models, views, makeView, container,
1592 charmConfig =
1593 { config:
1594 { options:
1595@@ -43,24 +43,29 @@
1596 container = Y.Node.create('<div id="juju-search-charm-panel" />');
1597 Y.one('#main').append(container);
1598 db = new models.Database();
1599- app = { db: db };
1600+ makeView = function(charm, env) {
1601+ return new views.CharmConfigurationView(
1602+ { container: container,
1603+ db: db,
1604+ env: env,
1605+ model: charm,
1606+ tooltipDelay: 0 }).render();
1607+ };
1608 });
1609+
1610 afterEach(function() {
1611 container.remove(true);
1612 });
1613
1614 it('must show loading message if the charm is not loaded', function() {
1615- var view = new views.CharmConfigurationView({container: container});
1616- view.render();
1617+ var view = makeView();
1618 container.one('div.alert').get('text').trim().should.equal(
1619 'Waiting on charm data...');
1620 });
1621
1622 it('must have inputs for service and number of units', function() {
1623 var charm = new models.Charm({id: 'precise/mysql-7'}),
1624- view = new views.CharmConfigurationView(
1625- { container: container,
1626- model: charm});
1627+ view = makeView(charm);
1628 charm.loaded = true;
1629 // If the charm has no config options it is still handled.
1630 assert.isTrue(!Y.Lang.isValue(charm.config));
1631@@ -92,11 +97,7 @@
1632 received_service_name = service_name;
1633 }},
1634 charm = new models.Charm({id: 'precise/mysql-7'}),
1635- view = new views.CharmConfigurationView(
1636- { container: container,
1637- model: charm,
1638- app: app});
1639- app.env = env;
1640+ view = makeView(charm, env);
1641 charm.loaded = true;
1642 view.render();
1643 container.one('#service-name').get('value').should.equal('mysql');
1644@@ -120,11 +121,7 @@
1645 received_num_units = num_units;
1646 }},
1647 charm = new models.Charm({id: 'precise/mysql-7'}),
1648- view = new views.CharmConfigurationView(
1649- { container: container,
1650- model: charm,
1651- app: app});
1652- app.env = env;
1653+ view = makeView(charm, env);
1654 charm.setAttrs(
1655 { config:
1656 { options:
1657@@ -149,17 +146,14 @@
1658 it('must not deploy a charm with same name as an existing service',
1659 function() {
1660 var deployed = false,
1661- env = {deploy: function(charm_url, service_name, config, config_raw,
1662- num_units) {
1663+ env =
1664+ { deploy:
1665+ function(charm_url, service_name, config, config_raw, num_units) {
1666 deployed = true;
1667 }},
1668 charm = new models.Charm({id: 'precise/mysql-7'}),
1669- view = new views.CharmConfigurationView(
1670- { container: container,
1671- model: charm,
1672- app: app});
1673+ view = makeView(charm, env);
1674 db.services.add([{id: 'wordpress'}]);
1675- app.env = env;
1676 charm.loaded = true;
1677 view.render();
1678 container.one('#service-name').set('value', 'wordpress');
1679@@ -352,17 +346,14 @@
1680 var received_config,
1681 received_config_raw,
1682 charm = new models.Charm({id: 'precise/mysql-7'}),
1683- app = {db: {services: {getById: function(name) {return null;}}},
1684- env: {deploy: function(charm_url, service_name, config, config_raw,
1685- num_units) {
1686- received_config = config;
1687- received_config_raw = config_raw;
1688- }}},
1689- view = new views.CharmConfigurationView(
1690- { container: container,
1691- model: charm,
1692- app: app,
1693- tooltipDelay: 0 });
1694+ db = {services: {getById: function(name) {return null;}}},
1695+ env =
1696+ { deploy:
1697+ function(charm_url, service_name, config, config_raw, num_units) {
1698+ received_config = config;
1699+ received_config_raw = config_raw;
1700+ }},
1701+ view = makeView(charm, env, db);
1702 charm.setAttrs(
1703 { config:
1704 { options:
1705
1706=== modified file 'test/test_charm_search.js'
1707--- test/test_charm_search.js 2012-10-19 01:53:03 +0000
1708+++ test/test_charm_search.js 2012-10-25 10:13:22 +0000
1709@@ -46,7 +46,7 @@
1710
1711 it('must be able to show and hide the panel', function() {
1712 var panel = Y.namespace('juju.views').CharmSearchPopup
1713- .getInstance({testing: true}),
1714+ .getInstance({testing: true, app: {}}),
1715 container = panel.node;
1716 container.getStyle('display').should.equal('none');
1717 panel.show();
1718@@ -75,7 +75,8 @@
1719 });
1720 }
1721 }}),
1722- testing: true
1723+ testing: true,
1724+ app: {}
1725 }),
1726 node = panel.node;
1727 panel.show(true);
1728
1729=== modified file 'test/test_environment_view.js'
1730--- test/test_environment_view.js 2012-10-20 18:16:19 +0000
1731+++ test/test_environment_view.js 2012-10-25 10:13:22 +0000
1732@@ -301,7 +301,7 @@
1733 function() {
1734 var view = new views.environment({
1735 container: container,
1736- app: {serviceEndpoints: {}},
1737+ getServiceEndpoints: function() {return {};},
1738 db: db,
1739 env: env
1740 }).render();
1741
1742=== modified file 'test/test_notifications.js'
1743--- test/test_notifications.js 2012-10-05 13:31:20 +0000
1744+++ test/test_notifications.js 2012-10-25 10:13:22 +0000
1745@@ -232,7 +232,6 @@
1746 view = new views.NotificationsView({
1747 container: container,
1748 notifications: notifications,
1749- app: app,
1750 env: app.env}).render();
1751
1752
1753
1754=== modified file 'test/test_service_config_view.js'
1755--- test/test_service_config_view.js 2012-10-19 01:44:45 +0000
1756+++ test/test_service_config_view.js 2012-10-25 10:13:22 +0000
1757@@ -3,7 +3,7 @@
1758 (function() {
1759 describe('juju service config view', function() {
1760 var ServiceConfigView, models, Y, container, service, db, conn,
1761- env, charm, ENTER, utils, app, testUtils;
1762+ env, charm, ENTER, utils, testUtils, makeView;
1763
1764 before(function(done) {
1765 Y = YUI(GlobalConfig).use('juju-views', 'juju-models', 'base',
1766@@ -16,6 +16,14 @@
1767 ServiceConfigView = Y.namespace('juju.views').service_config;
1768 utils = Y.namespace('juju.views.utils');
1769 testUtils = Y.namespace('juju-tests.utils');
1770+ makeView = function() {
1771+ return new ServiceConfigView(
1772+ { container: container,
1773+ model: service,
1774+ db: db,
1775+ env: env,
1776+ getModelURL: function() {}}).render();
1777+ };
1778 done();
1779 });
1780 });
1781@@ -61,9 +69,6 @@
1782 type: 'float'}}}});
1783
1784 db.charms.add([charm]);
1785- app = { env: env, db: db,
1786- getModelURL: function(model, intent) {
1787- return model.get('name'); }};
1788 service = new models.Service({
1789 id: 'mysql',
1790 charm: 'cs:precise/mysql-7',
1791@@ -90,14 +95,8 @@
1792 });
1793
1794 it('should display the configuration', function() {
1795- var view = new ServiceConfigView({
1796- container: container,
1797- model: service,
1798- app: app
1799- });
1800- view.render();
1801-
1802- var config = service.get('config'),
1803+ var view = makeView(),
1804+ config = service.get('config'),
1805 serviceConfigDiv = container.one('#service-config');
1806
1807 Y.Object.each(config, function(value, name) {
1808@@ -116,12 +115,7 @@
1809 });
1810
1811 it('should let the user change a configuration value', function() {
1812- var view = new ServiceConfigView({
1813- container: container,
1814- model: service,
1815- app: app
1816- }).render();
1817-
1818+ var view = makeView();
1819 container.one('#input-option0').set('value', 'new value');
1820 // In tests the events are not wired up right so we will call this
1821 // event handler manually.
1822@@ -144,14 +138,7 @@
1823 };
1824
1825 var ev = {err: true},
1826- view = new ServiceConfigView({
1827- container: container,
1828- model: service,
1829- app: (function() {
1830- app.getModelURL = function() {};
1831- return app;
1832- })()
1833- }).render();
1834+ view = makeView();
1835
1836 // Clicking on the "Update" button disables it until the RPC
1837 // callback returns, then it is re-enabled.
1838@@ -162,17 +149,12 @@
1839
1840 it('should display a message when a server error occurs', function() {
1841 var ev = {err: true},
1842+ view = makeView(),
1843 alert_ = container.one('#message-area>.alert');
1844 env.set_config = function(service, config, callback) {
1845 callback(ev);
1846 };
1847
1848- var view = new ServiceConfigView({
1849- container: container,
1850- model: service,
1851- app: app
1852- }).render();
1853-
1854 // Before an erroneous event is processed, no alert exists.
1855 var _ = expect(alert_).to.not.exist;
1856 // Handle the error event.
1857@@ -186,13 +168,8 @@
1858
1859 it('should display an error when addErrorMessage is called',
1860 function() {
1861- var view = new ServiceConfigView({
1862- container: container,
1863- model: service,
1864- app: app
1865- }).render();
1866-
1867- var error_message = utils.SERVER_ERROR_MESSAGE,
1868+ var view = makeView(),
1869+ error_message = utils.SERVER_ERROR_MESSAGE,
1870 alert_ = container.one('#message-area>.alert');
1871
1872 // Before an erroneous event is processed, no alert exists.
1873@@ -213,16 +190,11 @@
1874 // Mock function
1875 // view.saveConfig() calls it as part of its internal
1876 // "success" callback
1877- app.load_service = function() {};
1878 env.set_config = function(service, config, callback) {
1879 callback(ev);
1880 };
1881 var ev = {err: false},
1882- view = new ServiceConfigView({
1883- app: app,
1884- container: container,
1885- model: service
1886- }).render();
1887+ view = makeView();
1888
1889 container.one('#input-' + key).set('value', value);
1890
1891
1892=== modified file 'test/test_service_view.js'
1893--- test/test_service_view.js 2012-10-12 18:27:36 +0000
1894+++ test/test_service_view.js 2012-10-25 10:13:22 +0000
1895@@ -2,8 +2,8 @@
1896
1897 (function() {
1898 describe('juju service view', function() {
1899- var ServiceView, ServiceRelationsView, models, Y, container, service, db,
1900- conn, env, app, charm, ENTER, ESC;
1901+ var models, Y, container, service, db, conn, env, charm, ENTER, ESC,
1902+ makeServiceView, makeServiceRelationsView, views, unit;
1903
1904 before(function(done) {
1905 Y = YUI(GlobalConfig).use(
1906@@ -13,8 +13,7 @@
1907 ENTER = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.enter;
1908 ESC = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.esc;
1909 models = Y.namespace('juju.models');
1910- ServiceView = Y.namespace('juju.views').service;
1911- ServiceRelationsView = Y.namespace('juju.views').service_relations;
1912+ views = Y.namespace('juju.views');
1913 done();
1914 });
1915 });
1916@@ -27,24 +26,39 @@
1917 container = Y.Node.create('<div id="test-container" />');
1918 Y.one('#main').append(container);
1919 db = new models.Database();
1920- app = { env: env, db: db,
1921- getModelURL: function(model, intent) {
1922- return model.get('name'); }};
1923- charm = new models.Charm({id: 'cs:precise/mysql',
1924- description: 'A DB'});
1925+ charm = new models.Charm({id: 'cs:precise/mysql-7', description: 'A DB'});
1926 db.charms.add([charm]);
1927 // Add units sorted by id as that is what we expect from the server.
1928 db.units.add([{id: 'mysql/0', agent_state: 'pending'},
1929 {id: 'mysql/1', agent_state: 'pending'},
1930 {id: 'mysql/2', agent_state: 'pending'}
1931 ]);
1932- service = new models.Service({
1933- id: 'mysql',
1934- charm: 'cs:precise/mysql',
1935- unit_count: db.units.size(),
1936- exposed: false});
1937+ service = new models.Service(
1938+ { id: 'mysql',
1939+ charm: 'cs:precise/mysql-7',
1940+ unit_count: db.units.size(),
1941+ loaded: true,
1942+ exposed: false});
1943
1944 db.services.add([service]);
1945+ var viewMakerMaker = function(ViewPrototype) {
1946+ return function(querystring) {
1947+ if (!Y.Lang.isValue(querystring)) {
1948+ querystring = {};
1949+ }
1950+ return new ViewPrototype(
1951+ { container: container,
1952+ model: service,
1953+ db: db,
1954+ env: env,
1955+ getModelURL: function(model, intent) {
1956+ return model.get('name');
1957+ },
1958+ querystring: querystring}).render();
1959+ };
1960+ };
1961+ makeServiceView = viewMakerMaker(views.service);
1962+ makeServiceRelationsView = viewMakerMaker(views.service_relations);
1963 done();
1964 });
1965
1966@@ -57,26 +71,20 @@
1967 });
1968
1969 it('should show controls to modify units by default', function() {
1970- var view = new ServiceView(
1971- { container: container, model: service,
1972- app: app, querystring: {}}).render();
1973+ var view = makeServiceView();
1974 container.one('#num-service-units').should.not.equal(null);
1975 });
1976
1977 it('should not show controls if the charm is subordinate', function() {
1978 charm.set('is_subordinate', true);
1979- var view = new ServiceView(
1980- { container: container, service: service, app: app,
1981- querystring: {}}).render();
1982+ var view = makeServiceView();
1983 // "var _ =" makes the linter happy.
1984 var _ = expect(container.one('#num-service-units')).to.not.exist;
1985 });
1986
1987 it('should show the service units ordered by number', function() {
1988 // Note that the units are added in beforeEach in an ordered manner.
1989- var view = new ServiceView(
1990- { container: container, model: service, app: app,
1991- querystring: {}}).render();
1992+ var view = makeServiceView();
1993 var rendered_names = container.one(
1994 'ul.thumbnails').all('div.unit').get('id');
1995 var expected_names = db.units.map(function(u) {return u.id;});
1996@@ -87,10 +95,8 @@
1997
1998 it('should show unit details when a unit is clicked', function() {
1999 // Note that the units are added in beforeEach in an ordered manner.
2000- var view = new ServiceView(
2001- {container: container, model: service, app: app,
2002- querystring: {}}).render();
2003- var unit = container.one('ul.thumbnails').one('div.unit'),
2004+ var view = makeServiceView(),
2005+ unit = container.one('ul.thumbnails').one('div.unit'),
2006 showUnitCalled = false;
2007 view.on('*:showUnit', function() {
2008 showUnitCalled = true;
2009@@ -101,9 +107,7 @@
2010
2011 it('should use the show_units_large template if required', function() {
2012 // Note that the units are added in beforeEach in an ordered manner.
2013- var view = new ServiceView(
2014- {container: container, model: service, app: app,
2015- querystring: {}}).render();
2016+ var view = makeServiceView();
2017 assert.equal('unit-large', container.one('ul.thumbnails').get('id'));
2018 });
2019
2020@@ -120,27 +124,21 @@
2021 it('should use the show_units_medium template if required', function() {
2022 // Note that the units are added in beforeEach in an ordered manner.
2023 addUnits(30);
2024- var view = new ServiceView(
2025- {container: container, model: service, app: app,
2026- querystring: {}}).render();
2027+ var view = makeServiceView();
2028 assert.equal('unit-medium', container.one('ul.thumbnails').get('id'));
2029 });
2030
2031 it('should use the show_units_small template if required', function() {
2032 // Note that the units are added in beforeEach in an ordered manner.
2033 addUnits(60);
2034- var view = new ServiceView(
2035- {container: container, model: service, app: app,
2036- querystring: {}}).render();
2037+ var view = makeServiceView();
2038 assert.equal('unit-small', container.one('ul.thumbnails').get('id'));
2039 });
2040
2041 it('should use the show_units_tiny template if required', function() {
2042 // Note that the units are added in beforeEach in an ordered manner.
2043 addUnits(260);
2044- var view = new ServiceView(
2045- {container: container, model: service, app: app,
2046- querystring: {}}).render();
2047+ var view = makeServiceView();
2048 assert.equal('unit-tiny', container.one('ul.thumbnails').get('id'));
2049 });
2050
2051@@ -149,9 +147,7 @@
2052 // with ``pending`` status.
2053 addUnits(1, 'started');
2054 addUnits(2, 'start-error');
2055- var view = new ServiceView(
2056- {container: container, model: service, app: app,
2057- querystring: {}}).render();
2058+ var view = makeServiceView();
2059 var thumbnails = container.one('ul.thumbnails');
2060 assert.equal(1, thumbnails.all('.state-started').size());
2061 assert.equal(2, thumbnails.all('.state-error').size());
2062@@ -160,18 +156,14 @@
2063
2064 it('should start with the proper number of units shown in the text field',
2065 function() {
2066- var view = new ServiceView(
2067- { container: container, model: service, app: app,
2068- querystring: {}}).render();
2069+ var view = makeServiceView();
2070 var control = container.one('#num-service-units');
2071 control.get('value').should.equal('3');
2072 });
2073
2074 it('should remove multiple units when the text input changes',
2075 function() {
2076- var view = new ServiceView(
2077- { container: container, model: service, app: app,
2078- querystring: {}}).render();
2079+ var view = makeServiceView();
2080 var control = container.one('#num-service-units');
2081 control.set('value', 1);
2082 control.simulate('keydown', { keyCode: ENTER }); // Simulate Enter.
2083@@ -182,9 +174,7 @@
2084
2085 it('should not do anything if requested is < 1',
2086 function() {
2087- var view = new ServiceView(
2088- { container: container, model: service, app: app,
2089- querystring: {}}).render();
2090+ var view = makeServiceView();
2091 var control = container.one('#num-service-units');
2092 control.set('value', 0);
2093 control.simulate('keydown', { keyCode: ENTER });
2094@@ -196,9 +186,7 @@
2095 function() {
2096 service.set('unit_count', 1);
2097 db.units.remove([1, 2]);
2098- var view = new ServiceView(
2099- { container: container, model: service, app: app,
2100- querystring: {}}).render();
2101+ var view = makeServiceView();
2102 var control = container.one('#num-service-units');
2103 control.set('value', 0);
2104 control.simulate('keydown', { keyCode: ENTER });
2105@@ -208,9 +196,7 @@
2106
2107 it('should add the correct number of units when entered via text field',
2108 function() {
2109- var view = new ServiceView(
2110- { container: container, model: service, app: app,
2111- querystring: {}}).render();
2112+ var view = makeServiceView();
2113 var control = container.one('#num-service-units');
2114 control.set('value', 7);
2115 control.simulate('keydown', { keyCode: ENTER });
2116@@ -223,14 +209,12 @@
2117 it('should add pending units as soon as it gets a reply back ' +
2118 'from the server',
2119 function() {
2120- var new_unit_id = 'mysql/5';
2121- var expected_names = db.units.map(function(u) {return u.id;});
2122+ var new_unit_id = 'mysql/5',
2123+ view = makeServiceView(),
2124+ control = container.one('#num-service-units'),
2125+ expected_names = db.units.map(function(u) {return u.id;});
2126 expected_names.push(new_unit_id);
2127 expected_names.sort();
2128- var view = new ServiceView(
2129- { container: container, model: service, app: app,
2130- querystring: {}}).render();
2131- var control = container.one('#num-service-units');
2132 control.set('value', 4);
2133 control.simulate('keydown', { keyCode: ENTER });
2134 var callbacks = Y.Object.values(env._txn_callbacks);
2135@@ -251,9 +235,7 @@
2136 it('should remove units as soon as it gets a ' +
2137 'reply back from the server',
2138 function() {
2139- var view = new ServiceView(
2140- { container: container, model: service, app: app,
2141- querystring: {}}).render();
2142+ var view = makeServiceView();
2143 var control = container.one('#num-service-units');
2144 control.set('value', 2);
2145 control.simulate('keydown', { keyCode: ENTER });
2146@@ -265,9 +247,7 @@
2147
2148 it('should reset values on the control when you press escape',
2149 function() {
2150- var view = new ServiceView(
2151- { container: container, model: service, app: app,
2152- querystring: {}}).render();
2153+ var view = makeServiceView();
2154 var control = container.one('#num-service-units');
2155 control.set('value', 2);
2156 control.simulate('keydown', { keyCode: ESC });
2157@@ -276,9 +256,7 @@
2158
2159 it('should reset values on the control when you change focus',
2160 function() {
2161- var view = new ServiceView(
2162- { container: container, model: service, app: app,
2163- querystring: {}}).render();
2164+ var view = makeServiceView();
2165 var control = container.one('#num-service-units');
2166 control.set('value', 2);
2167 control.simulate('blur');
2168@@ -287,9 +265,7 @@
2169
2170 it('should reset values on the control when you type invalid value',
2171 function() {
2172- var view = new ServiceView(
2173- { container: container, model: service, app: app,
2174- querystring: {}}).render();
2175+ var view = makeServiceView();
2176 var control = container.one('#num-service-units');
2177
2178 var pressKey = function(key) {
2179@@ -305,9 +281,7 @@
2180 // Test for destroying services.
2181 it('should destroy the service when "Destroy Service" is clicked',
2182 function() {
2183- var view = new ServiceView(
2184- { container: container, model: service, app: app,
2185- querystring: {}}).render();
2186+ var view = makeServiceView();
2187 var control = container.one('#destroy-service');
2188 control.simulate('click');
2189 var destroy = container.one('#destroy-modal-panel .btn-danger');
2190@@ -319,9 +293,7 @@
2191
2192 it('should remove the service from the db after server ack',
2193 function() {
2194- var view = new ServiceView(
2195- { container: container, model: service, app: app,
2196- querystring: {}}).render();
2197+ var view = makeServiceView();
2198 db.relations.add(
2199 [new models.Relation({id: 'relation-0000000000',
2200 endpoints: [['mysql', {}], ['wordpress', {}]]}),
2201@@ -350,9 +322,7 @@
2202
2203 it('should send an expose RPC call when exposeService is invoked',
2204 function() {
2205- var view = new ServiceView({
2206- container: container, model: service, app: app,
2207- querystring: {}});
2208+ var view = makeServiceView();
2209
2210 view.exposeService();
2211 conn.last_message().op.should.equal('expose');
2212@@ -360,9 +330,7 @@
2213
2214 it('should send an unexpose RPC call when unexposeService is invoked',
2215 function() {
2216- var view = new ServiceView({
2217- container: container, model: service, app: app,
2218- querystring: {}});
2219+ var view = makeServiceView();
2220
2221 view.unexposeService();
2222 conn.last_message().op.should.equal('unexpose');
2223@@ -370,9 +338,7 @@
2224
2225 it('should invoke callback when expose RPC returns',
2226 function() {
2227- var view = new ServiceView({
2228- container: container, model: service, app: app,
2229- querystring: {}}).render();
2230+ var view = makeServiceView();
2231
2232 var test = function(selectorBefore, selectorAfter, callback) {
2233 console.log('Service is exposed: ' + service.get('exposed'));
2234@@ -404,9 +370,7 @@
2235 });
2236
2237 it('should show proper tabs initially', function() {
2238- var view = new ServiceView(
2239- { container: container, model: service, app: app,
2240- querystring: {}}).render(),
2241+ var view = makeServiceView(),
2242 active_navtabs = [];
2243 container.all('.state-btn').each(
2244 function(n) {
2245@@ -422,9 +386,7 @@
2246
2247 it('should show zero running units when filtered', function() {
2248 // All units are pending.
2249- var view = new ServiceView(
2250- { container: container, model: service, app: app,
2251- querystring: {state: 'running'}}).render(),
2252+ var view = makeServiceView({state: 'running'}),
2253 active_navtabs = [];
2254 container.all('.state-btn').each(
2255 function(n) {
2256@@ -443,9 +405,7 @@
2257 db.units.getById('mysql/0').agent_state = 'started';
2258 // 1 is pending.
2259 db.units.getById('mysql/2').agent_state = 'started';
2260- var view = new ServiceView(
2261- { container: container, model: service, app: app,
2262- querystring: {state: 'running'}}).render();
2263+ var view = makeServiceView({state: 'running'});
2264 var rendered_names = container.one(
2265 'ul.thumbnails').all('div.unit').get('id');
2266 rendered_names.should.eql(['mysql/0', 'mysql/2']);
2267@@ -455,9 +415,7 @@
2268 db.units.getById('mysql/0').agent_state = 'install-error';
2269 db.units.getById('mysql/1').agent_state = 'error';
2270 db.units.getById('mysql/2').agent_state = 'started';
2271- var view = new ServiceView(
2272- { container: container, model: service, app: app,
2273- querystring: {state: 'pending'}}).render(),
2274+ var view = makeServiceView({state: 'pending'}),
2275 active_navtabs = [];
2276 container.all('.state-btn').each(
2277 function(n) {
2278@@ -477,18 +435,14 @@
2279 db.units.getById('mysql/1').agent_state = 'started';
2280 // We include installed with pending.
2281 db.units.getById('mysql/2').agent_state = 'installed';
2282- var view = new ServiceView(
2283- { container: container, model: service, app: app,
2284- querystring: {state: 'pending'}}).render();
2285+ var view = makeServiceView({state: 'pending'});
2286 var rendered_names = container.one(
2287 'ul.thumbnails').all('div.unit').get('id');
2288 rendered_names.should.eql(['mysql/0', 'mysql/2']);
2289 });
2290
2291 it('should show zero error units when filtered', function() {
2292- var view = new ServiceView(
2293- { container: container, model: service, app: app,
2294- querystring: {state: 'error'}}).render(),
2295+ var view = makeServiceView({state: 'error'}),
2296 active_navtabs = [];
2297 container.all('.state-btn').each(
2298 function(n) {
2299@@ -508,17 +462,14 @@
2300 db.units.getById('mysql/0').agent_state = 'install-error';
2301 // 1 is pending.
2302 db.units.getById('mysql/2').agent_state = 'foo-error';
2303- var view = new ServiceView(
2304- { container: container, model: service, app: app,
2305- querystring: {state: 'error'}}).render();
2306- var rendered_names = container.one(
2307+ var view = makeServiceView({state: 'error'}),
2308+ rendered_names = container.one(
2309 'ul.thumbnails').all('div.unit').get('id');
2310 rendered_names.should.eql(['mysql/0', 'mysql/2']);
2311 });
2312
2313 it('should remove the relation when requested',
2314 function() {
2315-
2316 var service_name = service.get('id'),
2317 rel0 = new models.Relation(
2318 { id: 'relation-0',
2319@@ -538,11 +489,8 @@
2320
2321 db.relations.add([rel0, rel1]);
2322
2323- var view = new ServiceRelationsView(
2324- { container: container, model: service, app: app,
2325- querystring: {}}).render();
2326-
2327- var control = container.one('#relation-0');
2328+ var view = makeServiceRelationsView(),
2329+ control = container.one('#relation-0');
2330 control.simulate('click');
2331 var remove = container.one('#remove-modal-panel .btn-danger');
2332 remove.simulate('click');
2333@@ -573,11 +521,8 @@
2334
2335 db.relations.add([rel0, rel1]);
2336
2337- var view = new ServiceRelationsView(
2338- { container: container, model: service, app: app,
2339- querystring: {}}).render();
2340-
2341- var control = container.one('#relation-1');
2342+ var view = makeServiceRelationsView(),
2343+ control = container.one('#relation-1');
2344 control.simulate('click');
2345 var remove = container.one('#remove-modal-panel .btn-danger');
2346 remove.simulate('click');
2347@@ -611,11 +556,8 @@
2348 db.relations.get_relations_for_service(
2349 service).length.should.equal(2);
2350
2351- var view = new ServiceRelationsView(
2352- { container: container, model: service, app: app,
2353- querystring: {}}).render();
2354-
2355- var control = container.one('#relation-0');
2356+ var view = makeServiceRelationsView(),
2357+ control = container.one('#relation-0');
2358 control.simulate('click');
2359 var remove = container.one('#remove-modal-panel .btn-danger');
2360 remove.simulate('click');
2361@@ -653,11 +595,8 @@
2362
2363 db.relations.add([rel0, rel1]);
2364
2365- var view = new ServiceRelationsView(
2366- { container: container, model: service, app: app,
2367- querystring: {rel_id: 'relation-0'}}).render();
2368-
2369- var row = container.one('.highlighted');
2370+ var view = makeServiceRelationsView({rel_id: 'relation-0'}),
2371+ row = container.one('.highlighted');
2372 row.one('a').getHTML().should.equal('squid');
2373 row.one('.btn').get('disabled').should.equal(false);
2374 });
2375@@ -681,11 +620,8 @@
2376 scope: 'global'
2377 });
2378 db.relations.add([rel0, rel1]);
2379- var view = new ServiceRelationsView(
2380- { container: container, model: service, app: app,
2381- querystring: {}});
2382- view.render();
2383- var control = container.one('#relation-0');
2384+ var view = makeServiceRelationsView(),
2385+ control = container.one('#relation-0');
2386 control.simulate('click');
2387 var remove = container.one('#remove-modal-panel .btn-danger');
2388 remove.simulate('click');
2389@@ -724,7 +660,7 @@
2390 }, {
2391 testKey: 'error2',
2392 agent_state: 'error'
2393- }], filtered = ServiceView.prototype.filterUnits('error', units);
2394+ }], filtered = views.service.prototype.filterUnits('error', units);
2395
2396 assert.equal(2, filtered.length);
2397 assert.equal('error1', filtered[0].testKey);
2398
2399=== modified file 'test/test_unit_view.js'
2400--- test/test_unit_view.js 2012-10-19 01:44:45 +0000
2401+++ test/test_unit_view.js 2012-10-25 10:13:22 +0000
2402@@ -2,8 +2,8 @@
2403
2404 (function() {
2405 describe('juju unit view', function() {
2406- var UnitView, views, machine, models, Y, container, service, unit, db,
2407- conn, juju, env, charm, testUtils;
2408+ var views, machine, models, Y, container, service, unit, db,
2409+ conn, juju, env, charm, testUtils, makeView;
2410
2411 before(function(done) {
2412 Y = YUI(GlobalConfig).use(
2413@@ -12,13 +12,24 @@
2414 function(Y) {
2415 views = Y.namespace('juju.views');
2416 models = Y.namespace('juju.models');
2417- UnitView = views.unit;
2418 testUtils = Y.namespace('juju-tests.utils');
2419 conn = new testUtils.SocketStub();
2420 juju = Y.namespace('juju');
2421 env = new juju.Environment({conn: conn});
2422 env.connect();
2423 conn.open();
2424+ makeView = function(querystring) {
2425+ if (!Y.Lang.isValue(querystring)) {
2426+ querystring = {};
2427+ }
2428+ return new views.unit(
2429+ { container: container,
2430+ unit: unit,
2431+ db: db,
2432+ env: env,
2433+ getModelURL: function(u) { return u.id; },
2434+ querystring: querystring}).render();
2435+ };
2436 done();
2437 });
2438 });
2439@@ -75,30 +86,22 @@
2440 });
2441
2442 it('should include unit identifier', function() {
2443- var view = new UnitView(
2444- { container: container, unit: unit, db: db, env: env,
2445- querystring: {}}).render();
2446+ var view = makeView();
2447 container.one('#unit-id').getHTML().should.contain('mysql/0');
2448 });
2449
2450 it('should include charm URI', function() {
2451- var view = new UnitView(
2452- { container: container, unit: unit, db: db, env: env,
2453- querystring: {}}).render();
2454+ var view = makeView();
2455 container.one('#charm-uri').getHTML().should.contain('cs:precise/mysql');
2456 });
2457
2458 it('should include unit status', function() {
2459- var view = new UnitView(
2460- { container: container, unit: unit, db: db, env: env,
2461- querystring: {}}).render();
2462+ var view = makeView();
2463 container.one('#unit-status').getHTML().should.contain('pending');
2464 });
2465
2466 it('should include machine info', function() {
2467- var view = new UnitView(
2468- { container: container, unit: unit, db: db, env: env,
2469- querystring: {}}).render();
2470+ var view = makeView();
2471 container.one('#machine-name').getHTML().should.contain(
2472 'Machine machine-0');
2473 container.one('#machine-agent-state').getHTML().should.contain(
2474@@ -112,9 +115,7 @@
2475 });
2476
2477 it('should include relation info', function() {
2478- var view = new UnitView(
2479- { container: container, unit: unit, db: db, env: env,
2480- querystring: {}}).render();
2481+ var view = makeView();
2482 container.one('#relations').getHTML().should.contain('Relations');
2483 container.one('.relation-ident').get('text').trim().should.equal('db:2');
2484 container.one('.relation-endpoint').get('text').trim().should.equal(
2485@@ -128,19 +129,15 @@
2486
2487 it('should not display Retry and Resolved buttons when ' +
2488 'there is no error', function() {
2489- var view = new UnitView(
2490- { container: container, unit: unit, db: db, env: env,
2491- querystring: {}}).render();
2492- var _ = expect(container.one('#retry-unit-button')).to.not.exist;
2493- _ = expect(container.one('#resolved-unit-button')).to.not.exist;
2494+ var view = makeView();
2495+ var _ = expect(container.one('#retry-unit-button')).to.not.exist;
2496+ _ = expect(container.one('#resolved-unit-button')).to.not.exist;
2497 });
2498
2499 it('should display Retry and Resolved buttons when ' +
2500 'there is an error', function() {
2501 unit.agent_state = 'foo-error';
2502- var view = new UnitView(
2503- { container: container, unit: unit, db: db, env: env,
2504- querystring: {}}).render();
2505+ var view = makeView();
2506 container.one('#retry-unit-button').getHTML().should.equal('Retry');
2507 container.one('#resolved-unit-button').getHTML().should.equal(
2508 'Resolved');
2509@@ -148,33 +145,26 @@
2510 });
2511
2512 it('should always display Remove button', function() {
2513- var view = new UnitView(
2514- { container: container, unit: unit, db: db, env: env,
2515- querystring: {}}).render(),
2516+ var view = makeView(),
2517 button = container.one('#remove-unit-button');
2518 button.getHTML().should.equal('Remove');
2519 // Control is disabled with only one unit for the given service.
2520 button.get('disabled').should.equal(true);
2521 });
2522
2523- it('should send resolved op when confirmation button clicked',
2524- function() {
2525- unit.agent_state = 'foo-error';
2526- var view = new UnitView(
2527- { container: container, unit: unit, db: db, env: env,
2528- querystring: {}}).render();
2529- container.one('#resolved-unit-button').simulate('click');
2530- container.one('#resolved-modal-panel .btn-danger').simulate('click');
2531- var msg = conn.last_message();
2532- msg.op.should.equal('resolved');
2533- msg.retry.should.equal(false);
2534- });
2535+ it('should send resolved op when confirmation button clicked', function() {
2536+ unit.agent_state = 'foo-error';
2537+ var view = makeView();
2538+ container.one('#resolved-unit-button').simulate('click');
2539+ container.one('#resolved-modal-panel .btn-danger').simulate('click');
2540+ var msg = conn.last_message();
2541+ msg.op.should.equal('resolved');
2542+ msg.retry.should.equal(false);
2543+ });
2544
2545 it('should send resolved op with retry when retry clicked', function() {
2546 unit.agent_state = 'foo-error';
2547- var view = new UnitView(
2548- { container: container, unit: unit, db: db, env: env,
2549- querystring: {}}).render();
2550+ var view = makeView();
2551 container.one('#retry-unit-button').simulate('click');
2552 var msg = conn.last_message();
2553 msg.op.should.equal('resolved');
2554@@ -184,9 +174,7 @@
2555 it('should send remove_units op when confirmation clicked', function() {
2556 // Enable removal button by giving the service more than one unit.
2557 service.set('unit_count', 2);
2558- var view = new UnitView(
2559- { container: container, unit: unit, db: db, env: env,
2560- querystring: {}}).render();
2561+ var view = makeView();
2562 container.one('#remove-unit-button').simulate('click');
2563 container.one('#remove-modal-panel .btn-danger').simulate('click');
2564 var msg = conn.last_message();
2565@@ -196,9 +184,7 @@
2566 it('should clear out the database after a unit is removed', function() {
2567 // Enable removal button by giving the service more than one unit.
2568 service.set('unit_count', 2);
2569- var view = new UnitView(
2570- { container: container, unit: unit, db: db, env: env,
2571- querystring: {}}).render();
2572+ var view = makeView();
2573 container.one('#remove-unit-button').simulate('click');
2574 container.one('#remove-modal-panel .btn-danger')
2575 .simulate('click');
2576@@ -215,9 +201,7 @@
2577
2578 it('should show unit errors on the page with action buttons', function() {
2579 unit.relation_errors = {'db': ['mediawiki']};
2580- var view = new UnitView(
2581- { container: container, unit: unit, db: db, env: env,
2582- querystring: {}}).render();
2583+ var view = makeView();
2584 container.one('.relation-status').get('text').trim().should.contain(
2585 'error');
2586 container.one('.relation-status').all('button').get('text')
2587@@ -225,18 +209,14 @@
2588 });
2589
2590 it('should show highlighted relation rows', function() {
2591- var view = new UnitView(
2592- { container: container, unit: unit, db: db, env: env,
2593- querystring: {rel_id: 'relation-0000000002'}}).render();
2594+ var view = makeView({rel_id: 'relation-0000000002'});
2595 container.one('#relations tbody tr').hasClass('highlighted')
2596 .should.equal(true);
2597 });
2598
2599 it('should be able to send a resolve relation message', function() {
2600 unit.relation_errors = {'db': ['mediawiki']};
2601- var view = new UnitView(
2602- { container: container, unit: unit, db: db, env: env,
2603- querystring: {}}).render();
2604+ var view = makeView();
2605 container.one('.resolved-relation-button').simulate('click');
2606 container.one('#resolved-relation-modal-panel .btn-danger')
2607 .simulate('click');
2608@@ -249,9 +229,7 @@
2609
2610 it('should be able to send a retry relation message', function() {
2611 unit.relation_errors = {'db': ['mediawiki']};
2612- var view = new UnitView(
2613- { container: container, unit: unit, db: db, env: env,
2614- querystring: {}}).render();
2615+ var view = makeView();
2616 container.one('.retry-relation-button').simulate('click');
2617 var msg = conn.last_message();
2618 msg.op.should.equal('resolved');
2619@@ -262,11 +240,7 @@
2620
2621 it('should create an error notification if a resolve fails', function() {
2622 unit.relation_errors = {'db': ['mediawiki']};
2623- var view = new UnitView(
2624- { container: container, unit: unit, db: db, env: env,
2625- querystring: {},
2626- app: {getModelURL: function(u) { return u.id; }}})
2627- .render();
2628+ var view = makeView();
2629 container.one('.resolved-relation-button').simulate('click');
2630 container.one('#resolved-relation-modal-panel .btn-danger')
2631 .simulate('click');
2632@@ -282,11 +256,7 @@
2633
2634 it('should create an error notification if a retry fails', function() {
2635 unit.relation_errors = {'db': ['mediawiki']};
2636- var view = new UnitView(
2637- { container: container, unit: unit, db: db, env: env,
2638- querystring: {},
2639- app: {getModelURL: function(u) { return u.id; }}})
2640- .render();
2641+ var view = makeView();
2642 container.one('.retry-relation-button').simulate('click');
2643 var msg = conn.last_message();
2644 msg.err = true;

Subscribers

People subscribed via source and target branches