LEFT | RIGHT |
1 'use strict'; | 1 'use strict'; |
2 | 2 |
3 (function() { | 3 (function() { |
4 describe('juju service view', function() { | 4 describe('juju service view', function() { |
5 var ServiceView, ServiceRelationsView, models, Y, container, service, db, | 5 var models, Y, container, service, db, conn, env, charm, ENTER, ESC, |
6 conn, env, app, charm, ENTER, ESC; | 6 makeServiceView, makeServiceRelationsView, views, unit; |
7 | 7 |
8 before(function(done) { | 8 before(function(done) { |
9 Y = YUI(GlobalConfig).use( | 9 Y = YUI(GlobalConfig).use( |
10 'juju-views', 'juju-models', 'base', 'node', 'json-parse', | 10 'juju-views', 'juju-models', 'base', 'node', 'json-parse', |
11 'juju-env', 'node-event-simulate', 'juju-tests-utils', 'event-key', | 11 'juju-env', 'node-event-simulate', 'juju-tests-utils', 'event-key', |
12 function(Y) { | 12 function(Y) { |
13 ENTER = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.enter; | 13 ENTER = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.enter; |
14 ESC = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.esc; | 14 ESC = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.esc; |
15 models = Y.namespace('juju.models'); | 15 models = Y.namespace('juju.models'); |
16 ServiceView = Y.namespace('juju.views').service; | 16 views = Y.namespace('juju.views'); |
17 ServiceRelationsView = Y.namespace('juju.views').service_relations; | |
18 done(); | 17 done(); |
19 }); | 18 }); |
20 }); | 19 }); |
21 | 20 |
22 beforeEach(function(done) { | 21 beforeEach(function(done) { |
23 conn = new (Y.namespace('juju-tests.utils')).SocketStub(), | 22 conn = new (Y.namespace('juju-tests.utils')).SocketStub(), |
24 env = new (Y.namespace('juju')).Environment({conn: conn}); | 23 env = new (Y.namespace('juju')).Environment({conn: conn}); |
25 env.connect(); | 24 env.connect(); |
26 conn.open(); | 25 conn.open(); |
27 container = Y.Node.create('<div id="test-container" />'); | 26 container = Y.Node.create('<div id="test-container" />'); |
28 Y.one('#main').append(container); | 27 Y.one('#main').append(container); |
29 db = new models.Database(); | 28 db = new models.Database(); |
30 app = { env: env, db: db, | 29 charm = new models.Charm({id: 'cs:precise/mysql-7', description: 'A DB'}); |
31 getModelURL: function(model, intent) { | |
32 return model.get('name'); }}; | |
33 charm = new models.Charm({id: 'cs:precise/mysql', | |
34 description: 'A DB'}); | |
35 db.charms.add([charm]); | 30 db.charms.add([charm]); |
36 // Add units sorted by id as that is what we expect from the server. | 31 // Add units sorted by id as that is what we expect from the server. |
37 db.units.add([{id: 'mysql/0', agent_state: 'pending'}, | 32 db.units.add([{id: 'mysql/0', agent_state: 'pending'}, |
38 {id: 'mysql/1', agent_state: 'pending'}, | 33 {id: 'mysql/1', agent_state: 'pending'}, |
39 {id: 'mysql/2', agent_state: 'pending'} | 34 {id: 'mysql/2', agent_state: 'pending'} |
40 ]); | 35 ]); |
41 service = new models.Service({ | 36 service = new models.Service( |
42 id: 'mysql', | 37 { id: 'mysql', |
43 charm: 'cs:precise/mysql', | 38 charm: 'cs:precise/mysql-7', |
44 unit_count: db.units.size(), | 39 unit_count: db.units.size(), |
45 exposed: false}); | 40 loaded: true, |
| 41 exposed: false}); |
46 | 42 |
47 db.services.add([service]); | 43 db.services.add([service]); |
| 44 var viewMakerMaker = function(ViewPrototype) { |
| 45 return function(querystring) { |
| 46 if (!Y.Lang.isValue(querystring)) { |
| 47 querystring = {}; |
| 48 } |
| 49 return new ViewPrototype( |
| 50 { container: container, |
| 51 model: service, |
| 52 db: db, |
| 53 env: env, |
| 54 getModelURL: function(model, intent) { |
| 55 return model.get('name'); |
| 56 }, |
| 57 querystring: querystring}).render(); |
| 58 }; |
| 59 }; |
| 60 makeServiceView = viewMakerMaker(views.service); |
| 61 makeServiceRelationsView = viewMakerMaker(views.service_relations); |
48 done(); | 62 done(); |
49 }); | 63 }); |
50 | 64 |
51 afterEach(function(done) { | 65 afterEach(function(done) { |
52 container.remove(true); | 66 container.remove(true); |
53 service.destroy(); | 67 service.destroy(); |
54 db.destroy(); | 68 db.destroy(); |
55 env.destroy(); | 69 env.destroy(); |
56 done(); | 70 done(); |
57 }); | 71 }); |
58 | 72 |
59 it('should show controls to modify units by default', function() { | 73 it('should show controls to modify units by default', function() { |
60 var view = new ServiceView( | 74 var view = makeServiceView(); |
61 { container: container, model: service, | |
62 app: app, querystring: {}}).render(); | |
63 container.one('#num-service-units').should.not.equal(null); | 75 container.one('#num-service-units').should.not.equal(null); |
64 }); | 76 }); |
65 | 77 |
66 it('should not show controls if the charm is subordinate', function() { | 78 it('should not show controls if the charm is subordinate', function() { |
67 charm.set('is_subordinate', true); | 79 charm.set('is_subordinate', true); |
68 var view = new ServiceView( | 80 var view = makeServiceView(); |
69 { container: container, service: service, app: app, | |
70 querystring: {}}).render(); | |
71 // "var _ =" makes the linter happy. | 81 // "var _ =" makes the linter happy. |
72 var _ = expect(container.one('#num-service-units')).to.not.exist; | 82 var _ = expect(container.one('#num-service-units')).to.not.exist; |
73 }); | 83 }); |
74 | 84 |
75 it('should show the service units ordered by number', function() { | 85 it('should show the service units ordered by number', function() { |
76 // Note that the units are added in beforeEach in an ordered manner. | 86 // Note that the units are added in beforeEach in an ordered manner. |
77 var view = new ServiceView( | 87 var view = makeServiceView(); |
78 { container: container, model: service, app: app, | |
79 querystring: {}}).render(); | |
80 var rendered_names = container.one( | 88 var rendered_names = container.one( |
81 'ul.thumbnails').all('div.unit').get('id'); | 89 'ul.thumbnails').all('div.unit').get('id'); |
82 var expected_names = db.units.map(function(u) {return u.id;}); | 90 var expected_names = db.units.map(function(u) {return u.id;}); |
83 expected_names.sort(); | 91 expected_names.sort(); |
84 assert.deepEqual(rendered_names, expected_names); | 92 assert.deepEqual(rendered_names, expected_names); |
85 rendered_names.should.eql(expected_names); | 93 rendered_names.should.eql(expected_names); |
86 }); | 94 }); |
87 | 95 |
88 it('should show unit details when a unit is clicked', function() { | 96 it('should show unit details when a unit is clicked', function() { |
89 // Note that the units are added in beforeEach in an ordered manner. | 97 // Note that the units are added in beforeEach in an ordered manner. |
90 var view = new ServiceView( | 98 var view = makeServiceView(), |
91 {container: container, model: service, app: app, | 99 unit = container.one('ul.thumbnails').one('div.unit'), |
92 querystring: {}}).render(); | |
93 var unit = container.one('ul.thumbnails').one('div.unit'), | |
94 showUnitCalled = false; | 100 showUnitCalled = false; |
95 view.on('*:showUnit', function() { | 101 view.on('*:showUnit', function() { |
96 showUnitCalled = true; | 102 showUnitCalled = true; |
97 }); | 103 }); |
98 unit.simulate('click'); | 104 unit.simulate('click'); |
99 assert.isTrue(showUnitCalled); | 105 assert.isTrue(showUnitCalled); |
100 }); | 106 }); |
101 | 107 |
102 it('should use the show_units_large template if required', function() { | 108 it('should use the show_units_large template if required', function() { |
103 // Note that the units are added in beforeEach in an ordered manner. | 109 // Note that the units are added in beforeEach in an ordered manner. |
104 var view = new ServiceView( | 110 var view = makeServiceView(); |
105 {container: container, model: service, app: app, | |
106 querystring: {}}).render(); | |
107 assert.equal('unit-large', container.one('ul.thumbnails').get('id')); | 111 assert.equal('unit-large', container.one('ul.thumbnails').get('id')); |
108 }); | 112 }); |
109 | 113 |
110 var addUnits = function(number, state) { | 114 var addUnits = function(number, state) { |
111 var units = []; | 115 var units = []; |
112 // Starting from the number of already present units. | 116 // Starting from the number of already present units. |
113 var starting_from = db.units.size(); | 117 var starting_from = db.units.size(); |
114 for (var i = starting_from; i < number + starting_from; i += 1) { | 118 for (var i = starting_from; i < number + starting_from; i += 1) { |
115 units.push({id: 'mysql/' + i, agent_state: state || 'pending'}); | 119 units.push({id: 'mysql/' + i, agent_state: state || 'pending'}); |
116 } | 120 } |
117 db.units.add(units); | 121 db.units.add(units); |
118 }; | 122 }; |
119 | 123 |
120 it('should use the show_units_medium template if required', function() { | 124 it('should use the show_units_medium template if required', function() { |
121 // Note that the units are added in beforeEach in an ordered manner. | 125 // Note that the units are added in beforeEach in an ordered manner. |
122 addUnits(30); | 126 addUnits(30); |
123 var view = new ServiceView( | 127 var view = makeServiceView(); |
124 {container: container, model: service, app: app, | |
125 querystring: {}}).render(); | |
126 assert.equal('unit-medium', container.one('ul.thumbnails').get('id')); | 128 assert.equal('unit-medium', container.one('ul.thumbnails').get('id')); |
127 }); | 129 }); |
128 | 130 |
129 it('should use the show_units_small template if required', function() { | 131 it('should use the show_units_small template if required', function() { |
130 // Note that the units are added in beforeEach in an ordered manner. | 132 // Note that the units are added in beforeEach in an ordered manner. |
131 addUnits(60); | 133 addUnits(60); |
132 var view = new ServiceView( | 134 var view = makeServiceView(); |
133 {container: container, model: service, app: app, | |
134 querystring: {}}).render(); | |
135 assert.equal('unit-small', container.one('ul.thumbnails').get('id')); | 135 assert.equal('unit-small', container.one('ul.thumbnails').get('id')); |
136 }); | 136 }); |
137 | 137 |
138 it('should use the show_units_tiny template if required', function() { | 138 it('should use the show_units_tiny template if required', function() { |
139 // Note that the units are added in beforeEach in an ordered manner. | 139 // Note that the units are added in beforeEach in an ordered manner. |
140 addUnits(260); | 140 addUnits(260); |
141 var view = new ServiceView( | 141 var view = makeServiceView(); |
142 {container: container, model: service, app: app, | |
143 querystring: {}}).render(); | |
144 assert.equal('unit-tiny', container.one('ul.thumbnails').get('id')); | 142 assert.equal('unit-tiny', container.one('ul.thumbnails').get('id')); |
145 }); | 143 }); |
146 | 144 |
147 it('should display units based on their agent state', function() { | 145 it('should display units based on their agent state', function() { |
148 // Note that the units are added in beforeEach in an ordered manner | 146 // Note that the units are added in beforeEach in an ordered manner |
149 // with ``pending`` status. | 147 // with ``pending`` status. |
150 addUnits(1, 'started'); | 148 addUnits(1, 'started'); |
151 addUnits(2, 'start-error'); | 149 addUnits(2, 'start-error'); |
152 var view = new ServiceView( | 150 var view = makeServiceView(); |
153 {container: container, model: service, app: app, | |
154 querystring: {}}).render(); | |
155 var thumbnails = container.one('ul.thumbnails'); | 151 var thumbnails = container.one('ul.thumbnails'); |
156 assert.equal(1, thumbnails.all('.state-started').size()); | 152 assert.equal(1, thumbnails.all('.state-started').size()); |
157 assert.equal(2, thumbnails.all('.state-error').size()); | 153 assert.equal(2, thumbnails.all('.state-error').size()); |
158 assert.equal(3, thumbnails.all('.state-pending').size()); | 154 assert.equal(3, thumbnails.all('.state-pending').size()); |
159 }); | 155 }); |
160 | 156 |
161 it('should start with the proper number of units shown in the text field', | 157 it('should start with the proper number of units shown in the text field', |
162 function() { | 158 function() { |
163 var view = new ServiceView( | 159 var view = makeServiceView(); |
164 { container: container, model: service, app: app, | |
165 querystring: {}}).render(); | |
166 var control = container.one('#num-service-units'); | 160 var control = container.one('#num-service-units'); |
167 control.get('value').should.equal('3'); | 161 control.get('value').should.equal('3'); |
168 }); | 162 }); |
169 | 163 |
170 it('should remove multiple units when the text input changes', | 164 it('should remove multiple units when the text input changes', |
171 function() { | 165 function() { |
172 var view = new ServiceView( | 166 var view = makeServiceView(); |
173 { container: container, model: service, app: app, | |
174 querystring: {}}).render(); | |
175 var control = container.one('#num-service-units'); | 167 var control = container.one('#num-service-units'); |
176 control.set('value', 1); | 168 control.set('value', 1); |
177 control.simulate('keydown', { keyCode: ENTER }); // Simulate Enter. | 169 control.simulate('keydown', { keyCode: ENTER }); // Simulate Enter. |
178 var message = conn.last_message(); | 170 var message = conn.last_message(); |
179 message.op.should.equal('remove_units'); | 171 message.op.should.equal('remove_units'); |
180 message.unit_names.should.eql(['mysql/2', 'mysql/1']); | 172 message.unit_names.should.eql(['mysql/2', 'mysql/1']); |
181 }); | 173 }); |
182 | 174 |
183 it('should not do anything if requested is < 1', | 175 it('should not do anything if requested is < 1', |
184 function() { | 176 function() { |
185 var view = new ServiceView( | 177 var view = makeServiceView(); |
186 { container: container, model: service, app: app, | |
187 querystring: {}}).render(); | |
188 var control = container.one('#num-service-units'); | 178 var control = container.one('#num-service-units'); |
189 control.set('value', 0); | 179 control.set('value', 0); |
190 control.simulate('keydown', { keyCode: ENTER }); | 180 control.simulate('keydown', { keyCode: ENTER }); |
191 var _ = expect(conn.last_message()).to.not.exist; | 181 var _ = expect(conn.last_message()).to.not.exist; |
192 control.get('value').should.equal('3'); | 182 control.get('value').should.equal('3'); |
193 }); | 183 }); |
194 | 184 |
195 it('should not do anything if the number of units is <= 1', | 185 it('should not do anything if the number of units is <= 1', |
196 function() { | 186 function() { |
197 service.set('unit_count', 1); | 187 service.set('unit_count', 1); |
198 db.units.remove([1, 2]); | 188 db.units.remove([1, 2]); |
199 var view = new ServiceView( | 189 var view = makeServiceView(); |
200 { container: container, model: service, app: app, | |
201 querystring: {}}).render(); | |
202 var control = container.one('#num-service-units'); | 190 var control = container.one('#num-service-units'); |
203 control.set('value', 0); | 191 control.set('value', 0); |
204 control.simulate('keydown', { keyCode: ENTER }); | 192 control.simulate('keydown', { keyCode: ENTER }); |
205 var _ = expect(conn.last_message()).to.not.exist; | 193 var _ = expect(conn.last_message()).to.not.exist; |
206 control.get('value').should.equal('1'); | 194 control.get('value').should.equal('1'); |
207 }); | 195 }); |
208 | 196 |
209 it('should add the correct number of units when entered via text field', | 197 it('should add the correct number of units when entered via text field', |
210 function() { | 198 function() { |
211 var view = new ServiceView( | 199 var view = makeServiceView(); |
212 { container: container, model: service, app: app, | |
213 querystring: {}}).render(); | |
214 var control = container.one('#num-service-units'); | 200 var control = container.one('#num-service-units'); |
215 control.set('value', 7); | 201 control.set('value', 7); |
216 control.simulate('keydown', { keyCode: ENTER }); | 202 control.simulate('keydown', { keyCode: ENTER }); |
217 var message = conn.last_message(); | 203 var message = conn.last_message(); |
218 message.op.should.equal('add_unit'); | 204 message.op.should.equal('add_unit'); |
219 message.service_name.should.equal('mysql'); | 205 message.service_name.should.equal('mysql'); |
220 message.num_units.should.equal(4); | 206 message.num_units.should.equal(4); |
221 }); | 207 }); |
222 | 208 |
223 it('should add pending units as soon as it gets a reply back ' + | 209 it('should add pending units as soon as it gets a reply back ' + |
224 'from the server', | 210 'from the server', |
225 function() { | 211 function() { |
226 var new_unit_id = 'mysql/5'; | 212 var new_unit_id = 'mysql/5', |
227 var expected_names = db.units.map(function(u) {return u.id;}); | 213 view = makeServiceView(), |
| 214 control = container.one('#num-service-units'), |
| 215 expected_names = db.units.map(function(u) {return u.id;}); |
228 expected_names.push(new_unit_id); | 216 expected_names.push(new_unit_id); |
229 expected_names.sort(); | 217 expected_names.sort(); |
230 var view = new ServiceView( | |
231 { container: container, model: service, app: app, | |
232 querystring: {}}).render(); | |
233 var control = container.one('#num-service-units'); | |
234 control.set('value', 4); | 218 control.set('value', 4); |
235 control.simulate('keydown', { keyCode: ENTER }); | 219 control.simulate('keydown', { keyCode: ENTER }); |
236 var callbacks = Y.Object.values(env._txn_callbacks); | 220 var callbacks = Y.Object.values(env._txn_callbacks); |
237 callbacks.length.should.equal(1); | 221 callbacks.length.should.equal(1); |
238 // Since we don't have an app to listen to this event and tell the | 222 // Since we don't have an app to listen to this event and tell the |
239 // view to re-render, we need to do it ourselves. | 223 // view to re-render, we need to do it ourselves. |
240 db.on('update', view.render, view); | 224 db.on('update', view.render, view); |
241 callbacks[0]({result: [new_unit_id]}); | 225 callbacks[0]({result: [new_unit_id]}); |
242 var db_names = db.units.map(function(u) {return u.id;}); | 226 var db_names = db.units.map(function(u) {return u.id;}); |
243 db_names.sort(); | 227 db_names.sort(); |
244 db_names.should.eql(expected_names); | 228 db_names.should.eql(expected_names); |
245 service.get('unit_count').should.equal(4); | 229 service.get('unit_count').should.equal(4); |
246 var rendered_names = container.one( | 230 var rendered_names = container.one( |
247 'ul.thumbnails').all('div.unit').get('id'); | 231 'ul.thumbnails').all('div.unit').get('id'); |
248 assert.deepEqual(rendered_names, expected_names); | 232 assert.deepEqual(rendered_names, expected_names); |
249 }); | 233 }); |
250 | 234 |
251 it('should remove units as soon as it gets a ' + | 235 it('should remove units as soon as it gets a ' + |
252 'reply back from the server', | 236 'reply back from the server', |
253 function() { | 237 function() { |
254 var view = new ServiceView( | 238 var view = makeServiceView(); |
255 { container: container, model: service, app: app, | |
256 querystring: {}}).render(); | |
257 var control = container.one('#num-service-units'); | 239 var control = container.one('#num-service-units'); |
258 control.set('value', 2); | 240 control.set('value', 2); |
259 control.simulate('keydown', { keyCode: ENTER }); | 241 control.simulate('keydown', { keyCode: ENTER }); |
260 var callbacks = Y.Object.values(env._txn_callbacks); | 242 var callbacks = Y.Object.values(env._txn_callbacks); |
261 callbacks.length.should.equal(1); | 243 callbacks.length.should.equal(1); |
262 callbacks[0]({unit_names: ['mysql/2']}); | 244 callbacks[0]({unit_names: ['mysql/2']}); |
263 var _ = expect(db.units.getById('mysql/2')).to.not.exist; | 245 var _ = expect(db.units.getById('mysql/2')).to.not.exist; |
264 }); | 246 }); |
265 | 247 |
266 it('should reset values on the control when you press escape', | 248 it('should reset values on the control when you press escape', |
267 function() { | 249 function() { |
268 var view = new ServiceView( | 250 var view = makeServiceView(); |
269 { container: container, model: service, app: app, | |
270 querystring: {}}).render(); | |
271 var control = container.one('#num-service-units'); | 251 var control = container.one('#num-service-units'); |
272 control.set('value', 2); | 252 control.set('value', 2); |
273 control.simulate('keydown', { keyCode: ESC }); | 253 control.simulate('keydown', { keyCode: ESC }); |
274 control.get('value').should.equal('3'); | 254 control.get('value').should.equal('3'); |
275 }); | 255 }); |
276 | 256 |
277 it('should reset values on the control when you change focus', | 257 it('should reset values on the control when you change focus', |
278 function() { | 258 function() { |
279 var view = new ServiceView( | 259 var view = makeServiceView(); |
280 { container: container, model: service, app: app, | |
281 querystring: {}}).render(); | |
282 var control = container.one('#num-service-units'); | 260 var control = container.one('#num-service-units'); |
283 control.set('value', 2); | 261 control.set('value', 2); |
284 control.simulate('blur'); | 262 control.simulate('blur'); |
285 control.get('value').should.equal('3'); | 263 control.get('value').should.equal('3'); |
286 }); | 264 }); |
287 | 265 |
288 it('should reset values on the control when you type invalid value', | 266 it('should reset values on the control when you type invalid value', |
289 function() { | 267 function() { |
290 var view = new ServiceView( | 268 var view = makeServiceView(); |
291 { container: container, model: service, app: app, | |
292 querystring: {}}).render(); | |
293 var control = container.one('#num-service-units'); | 269 var control = container.one('#num-service-units'); |
294 | 270 |
295 var pressKey = function(key) { | 271 var pressKey = function(key) { |
296 control.set('value', key); | 272 control.set('value', key); |
297 control.simulate('keydown', { keyCode: ENTER }); | 273 control.simulate('keydown', { keyCode: ENTER }); |
298 control.get('value').should.equal('3'); | 274 control.get('value').should.equal('3'); |
299 }; | 275 }; |
300 pressKey('a'); | 276 pressKey('a'); |
301 pressKey('2w'); | 277 pressKey('2w'); |
302 pressKey('w2'); | 278 pressKey('w2'); |
303 }); | 279 }); |
304 | 280 |
305 // Test for destroying services. | 281 // Test for destroying services. |
306 it('should destroy the service when "Destroy Service" is clicked', | 282 it('should destroy the service when "Destroy Service" is clicked', |
307 function() { | 283 function() { |
308 var view = new ServiceView( | 284 var view = makeServiceView(); |
309 { container: container, model: service, app: app, | |
310 querystring: {}}).render(); | |
311 var control = container.one('#destroy-service'); | 285 var control = container.one('#destroy-service'); |
312 control.simulate('click'); | 286 control.simulate('click'); |
313 var destroy = container.one('#destroy-modal-panel .btn-danger'); | 287 var destroy = container.one('#destroy-modal-panel .btn-danger'); |
314 destroy.simulate('click'); | 288 destroy.simulate('click'); |
315 var message = conn.last_message(); | 289 var message = conn.last_message(); |
316 message.op.should.equal('destroy_service'); | 290 message.op.should.equal('destroy_service'); |
317 destroy.get('disabled').should.equal(true); | 291 destroy.get('disabled').should.equal(true); |
318 }); | 292 }); |
319 | 293 |
320 it('should remove the service from the db after server ack', | 294 it('should remove the service from the db after server ack', |
321 function() { | 295 function() { |
322 var view = new ServiceView( | 296 var view = makeServiceView(); |
323 { container: container, model: service, app: app, | |
324 querystring: {}}).render(); | |
325 db.relations.add( | 297 db.relations.add( |
326 [new models.Relation({id: 'relation-0000000000', | 298 [new models.Relation({id: 'relation-0000000000', |
327 endpoints: [['mysql', {}], ['wordpress', {}]]}), | 299 endpoints: [['mysql', {}], ['wordpress', {}]]}), |
328 new models.Relation({id: 'relation-0000000001', | 300 new models.Relation({id: 'relation-0000000001', |
329 endpoints: [['squid', {}], ['apache', {}]]})]); | 301 endpoints: [['squid', {}], ['apache', {}]]})]); |
330 var control = container.one('#destroy-service'); | 302 var control = container.one('#destroy-service'); |
331 control.simulate('click'); | 303 control.simulate('click'); |
332 var destroy = container.one('#destroy-modal-panel .btn-danger'); | 304 var destroy = container.one('#destroy-modal-panel .btn-danger'); |
333 destroy.simulate('click'); | 305 destroy.simulate('click'); |
334 var called = false; | 306 var called = false; |
335 view.on('showEnvironment', function(ev) { | 307 view.on('showEnvironment', function(ev) { |
336 called = true; | 308 called = true; |
337 }); | 309 }); |
338 var callbacks = Y.Object.values(env._txn_callbacks); | 310 var callbacks = Y.Object.values(env._txn_callbacks); |
339 callbacks.length.should.equal(1); | 311 callbacks.length.should.equal(1); |
340 // Since we don't have an app to listen to this event and tell the | 312 // Since we don't have an app to listen to this event and tell the |
341 // view to re-render, we need to do it ourselves. | 313 // view to re-render, we need to do it ourselves. |
342 db.on('update', view.render, view); | 314 db.on('update', view.render, view); |
343 callbacks[0]({result: true}); | 315 callbacks[0]({result: true}); |
344 var _ = expect(db.services.getById(service.get('id'))).to.not.exist; | 316 var _ = expect(db.services.getById(service.get('id'))).to.not.exist; |
345 db.relations.map(function(u) {return u.get('id');}) | 317 db.relations.map(function(u) {return u.get('id');}) |
346 .should.eql(['relation-0000000001']); | 318 .should.eql(['relation-0000000001']); |
347 // Catch show environment event. | 319 // Catch show environment event. |
348 called.should.equal(true); | 320 called.should.equal(true); |
349 }); | 321 }); |
350 | 322 |
351 it('should send an expose RPC call when exposeService is invoked', | 323 it('should send an expose RPC call when exposeService is invoked', |
352 function() { | 324 function() { |
353 var view = new ServiceView({ | 325 var view = makeServiceView(); |
354 container: container, model: service, app: app, | |
355 querystring: {}}); | |
356 | 326 |
357 view.exposeService(); | 327 view.exposeService(); |
358 conn.last_message().op.should.equal('expose'); | 328 conn.last_message().op.should.equal('expose'); |
359 }); | 329 }); |
360 | 330 |
361 it('should send an unexpose RPC call when unexposeService is invoked', | 331 it('should send an unexpose RPC call when unexposeService is invoked', |
362 function() { | 332 function() { |
363 var view = new ServiceView({ | 333 var view = makeServiceView(); |
364 container: container, model: service, app: app, | |
365 querystring: {}}); | |
366 | 334 |
367 view.unexposeService(); | 335 view.unexposeService(); |
368 conn.last_message().op.should.equal('unexpose'); | 336 conn.last_message().op.should.equal('unexpose'); |
369 }); | 337 }); |
370 | 338 |
371 it('should invoke callback when expose RPC returns', | 339 it('should invoke callback when expose RPC returns', |
372 function() { | 340 function() { |
373 var view = new ServiceView({ | 341 var view = makeServiceView(); |
374 container: container, model: service, app: app, | |
375 querystring: {}}).render(); | |
376 | 342 |
377 var test = function(selectorBefore, selectorAfter, callback) { | 343 var test = function(selectorBefore, selectorAfter, callback) { |
378 console.log('Service is exposed: ' + service.get('exposed')); | 344 console.log('Service is exposed: ' + service.get('exposed')); |
379 console.log('selectorBefore: ' + selectorBefore); | 345 console.log('selectorBefore: ' + selectorBefore); |
380 console.log('selectorAfter: ' + selectorAfter); | 346 console.log('selectorAfter: ' + selectorAfter); |
381 | 347 |
382 assert.isNotNull(container.one(selectorBefore)); | 348 assert.isNotNull(container.one(selectorBefore)); |
383 assert.isNull(container.one(selectorAfter)); | 349 assert.isNull(container.one(selectorAfter)); |
384 | 350 |
385 var dbUpdated = false; | 351 var dbUpdated = false; |
(...skipping 11 matching lines...) Expand all Loading... |
397 assert.isNull(container.one(selectorBefore)); | 363 assert.isNull(container.one(selectorBefore)); |
398 }; | 364 }; |
399 | 365 |
400 test('.exposeService', '.unexposeService', | 366 test('.exposeService', '.unexposeService', |
401 Y.bind(view._exposeServiceCallback, view)); | 367 Y.bind(view._exposeServiceCallback, view)); |
402 test('.unexposeService', '.exposeService', | 368 test('.unexposeService', '.exposeService', |
403 Y.bind(view._unexposeServiceCallback, view)); | 369 Y.bind(view._unexposeServiceCallback, view)); |
404 }); | 370 }); |
405 | 371 |
406 it('should show proper tabs initially', function() { | 372 it('should show proper tabs initially', function() { |
407 var view = new ServiceView( | 373 var view = makeServiceView(), |
408 { container: container, model: service, app: app, | |
409 querystring: {}}).render(), | |
410 active_navtabs = []; | 374 active_navtabs = []; |
411 container.all('.state-title').each( | 375 container.all('.state-title').each( |
412 function(n) { | 376 function(n) { |
413 active_navtabs.push([n.get('text').trim(), | 377 active_navtabs.push([n.get('text').trim(), |
414 n.hasClass('active')]); | 378 n.hasClass('active')]); |
415 }); | 379 }); |
416 active_navtabs.should.eql( | 380 active_navtabs.should.eql( |
417 [['All', true], | 381 [['All', true], |
418 ['Running', false], | 382 ['Running', false], |
419 ['Pending', false], | 383 ['Pending', false], |
420 ['Error', false]]); | 384 ['Error', false]]); |
421 }); | 385 }); |
422 | 386 |
423 it('should show zero running units when filtered', function() { | 387 it('should show zero running units when filtered', function() { |
424 // All units are pending. | 388 // All units are pending. |
425 var view = new ServiceView( | 389 var view = makeServiceView({state: 'running'}), |
426 { container: container, model: service, app: app, | |
427 querystring: {state: 'running'}}).render(), | |
428 active_navtabs = []; | 390 active_navtabs = []; |
429 container.all('.state-title').each( | 391 container.all('.state-title').each( |
430 function(n) { | 392 function(n) { |
431 active_navtabs.push([n.get('text').trim(), | 393 active_navtabs.push([n.get('text').trim(), |
432 n.hasClass('active')]); | 394 n.hasClass('active')]); |
433 }); | 395 }); |
434 active_navtabs.should.eql( | 396 active_navtabs.should.eql( |
435 [['All', false], | 397 [['All', false], |
436 ['Running', true], | 398 ['Running', true], |
437 ['Pending', false], | 399 ['Pending', false], |
438 ['Error', false]]); | 400 ['Error', false]]); |
439 container.all('div.thumbnail').get('id').length.should.equal(0); | 401 container.all('div.thumbnail').get('id').length.should.equal(0); |
440 }); | 402 }); |
441 | 403 |
442 it('should show some running units when filtered', function() { | 404 it('should show some running units when filtered', function() { |
443 db.units.getById('mysql/0').agent_state = 'started'; | 405 db.units.getById('mysql/0').agent_state = 'started'; |
444 // 1 is pending. | 406 // 1 is pending. |
445 db.units.getById('mysql/2').agent_state = 'started'; | 407 db.units.getById('mysql/2').agent_state = 'started'; |
446 var view = new ServiceView( | 408 var view = makeServiceView({state: 'running'}); |
447 { container: container, model: service, app: app, | |
448 querystring: {state: 'running'}}).render(); | |
449 var rendered_names = container.one( | 409 var rendered_names = container.one( |
450 'ul.thumbnails').all('div.unit').get('id'); | 410 'ul.thumbnails').all('div.unit').get('id'); |
451 rendered_names.should.eql(['mysql/0', 'mysql/2']); | 411 rendered_names.should.eql(['mysql/0', 'mysql/2']); |
452 }); | 412 }); |
453 | 413 |
454 it('should show zero pending units when filtered', function() { | 414 it('should show zero pending units when filtered', function() { |
455 db.units.getById('mysql/0').agent_state = 'install-error'; | 415 db.units.getById('mysql/0').agent_state = 'install-error'; |
456 db.units.getById('mysql/1').agent_state = 'error'; | 416 db.units.getById('mysql/1').agent_state = 'error'; |
457 db.units.getById('mysql/2').agent_state = 'started'; | 417 db.units.getById('mysql/2').agent_state = 'started'; |
458 var view = new ServiceView( | 418 var view = makeServiceView({state: 'pending'}), |
459 { container: container, model: service, app: app, | |
460 querystring: {state: 'pending'}}).render(), | |
461 active_navtabs = []; | 419 active_navtabs = []; |
462 container.all('.state-title').each( | 420 container.all('.state-title').each( |
463 function(n) { | 421 function(n) { |
464 active_navtabs.push([n.get('text').trim(), | 422 active_navtabs.push([n.get('text').trim(), |
465 n.hasClass('active')]); | 423 n.hasClass('active')]); |
466 }); | 424 }); |
467 active_navtabs.should.eql( | 425 active_navtabs.should.eql( |
468 [['All', false], | 426 [['All', false], |
469 ['Running', false], | 427 ['Running', false], |
470 ['Pending', true], | 428 ['Pending', true], |
471 ['Error', false]]); | 429 ['Error', false]]); |
472 container.all('div.thumbnail').get('id').length.should.equal(0); | 430 container.all('div.thumbnail').get('id').length.should.equal(0); |
473 }); | 431 }); |
474 | 432 |
475 it('should show some pending units when filtered', function() { | 433 it('should show some pending units when filtered', function() { |
476 // 0 is pending already. | 434 // 0 is pending already. |
477 db.units.getById('mysql/1').agent_state = 'started'; | 435 db.units.getById('mysql/1').agent_state = 'started'; |
478 // We include installed with pending. | 436 // We include installed with pending. |
479 db.units.getById('mysql/2').agent_state = 'installed'; | 437 db.units.getById('mysql/2').agent_state = 'installed'; |
480 var view = new ServiceView( | 438 var view = makeServiceView({state: 'pending'}); |
481 { container: container, model: service, app: app, | |
482 querystring: {state: 'pending'}}).render(); | |
483 var rendered_names = container.one( | 439 var rendered_names = container.one( |
484 'ul.thumbnails').all('div.unit').get('id'); | 440 'ul.thumbnails').all('div.unit').get('id'); |
485 rendered_names.should.eql(['mysql/0', 'mysql/2']); | 441 rendered_names.should.eql(['mysql/0', 'mysql/2']); |
486 }); | 442 }); |
487 | 443 |
488 it('should show zero error units when filtered', function() { | 444 it('should show zero error units when filtered', function() { |
489 var view = new ServiceView( | 445 var view = makeServiceView({state: 'error'}), |
490 { container: container, model: service, app: app, | |
491 querystring: {state: 'error'}}).render(), | |
492 active_navtabs = []; | 446 active_navtabs = []; |
493 container.all('.state-title').each( | 447 container.all('.state-title').each( |
494 function(n) { | 448 function(n) { |
495 active_navtabs.push([n.get('text').trim(), | 449 active_navtabs.push([n.get('text').trim(), |
496 n.hasClass('active')]); | 450 n.hasClass('active')]); |
497 }); | 451 }); |
498 active_navtabs.should.eql( | 452 active_navtabs.should.eql( |
499 [['All', false], | 453 [['All', false], |
500 ['Running', false], | 454 ['Running', false], |
501 ['Pending', false], | 455 ['Pending', false], |
502 ['Error', true]]); | 456 ['Error', true]]); |
503 container.all('div.thumbnail').get('id').length.should.equal(0); | 457 container.all('div.thumbnail').get('id').length.should.equal(0); |
504 }); | 458 }); |
505 | 459 |
506 it('should show some error units when filtered', function() { | 460 it('should show some error units when filtered', function() { |
507 // Any -error is included. | 461 // Any -error is included. |
508 db.units.getById('mysql/0').agent_state = 'install-error'; | 462 db.units.getById('mysql/0').agent_state = 'install-error'; |
509 // 1 is pending. | 463 // 1 is pending. |
510 db.units.getById('mysql/2').agent_state = 'foo-error'; | 464 db.units.getById('mysql/2').agent_state = 'foo-error'; |
511 var view = new ServiceView( | 465 var view = makeServiceView({state: 'error'}), |
512 { container: container, model: service, app: app, | 466 rendered_names = container.one( |
513 querystring: {state: 'error'}}).render(); | |
514 var rendered_names = container.one( | |
515 'ul.thumbnails').all('div.unit').get('id'); | 467 'ul.thumbnails').all('div.unit').get('id'); |
516 rendered_names.should.eql(['mysql/0', 'mysql/2']); | 468 rendered_names.should.eql(['mysql/0', 'mysql/2']); |
517 }); | 469 }); |
518 | 470 |
519 it('should remove the relation when requested', | 471 it('should remove the relation when requested', |
520 function() { | 472 function() { |
521 | |
522 var service_name = service.get('id'), | 473 var service_name = service.get('id'), |
523 rel0 = new models.Relation( | 474 rel0 = new models.Relation( |
524 { id: 'relation-0', | 475 { id: 'relation-0', |
525 endpoints: | 476 endpoints: |
526 [[service_name, {name: 'db', role: 'source'}], | 477 [[service_name, {name: 'db', role: 'source'}], |
527 ['squid', {name: 'cache', role: 'front'}]], | 478 ['squid', {name: 'cache', role: 'front'}]], |
528 'interface': 'cache', | 479 'interface': 'cache', |
529 scope: 'global' | 480 scope: 'global' |
530 }), | 481 }), |
531 rel1 = new models.Relation( | 482 rel1 = new models.Relation( |
532 { id: 'relation-1', | 483 { id: 'relation-1', |
533 endpoints: | 484 endpoints: |
534 [[service_name, {name: 'db', role: 'peer'}]], | 485 [[service_name, {name: 'db', role: 'peer'}]], |
535 'interface': 'db', | 486 'interface': 'db', |
536 scope: 'global' | 487 scope: 'global' |
537 }); | 488 }); |
538 | 489 |
539 db.relations.add([rel0, rel1]); | 490 db.relations.add([rel0, rel1]); |
540 | 491 |
541 var view = new ServiceRelationsView( | 492 var view = makeServiceRelationsView(), |
542 { container: container, model: service, app: app, | 493 control = container.one('#relation-0'); |
543 querystring: {}}).render(); | |
544 | |
545 var control = container.one('#relation-0'); | |
546 control.simulate('click'); | 494 control.simulate('click'); |
547 var remove = container.one('#remove-modal-panel .btn-danger'); | 495 var remove = container.one('#remove-modal-panel .btn-danger'); |
548 remove.simulate('click'); | 496 remove.simulate('click'); |
549 var message = conn.last_message(); | 497 var message = conn.last_message(); |
550 message.op.should.equal('remove_relation'); | 498 message.op.should.equal('remove_relation'); |
551 remove.get('disabled').should.equal(true); | 499 remove.get('disabled').should.equal(true); |
552 }); | 500 }); |
553 | 501 |
554 it('should remove peer relations when requested', | 502 it('should remove peer relations when requested', |
555 function() { | 503 function() { |
(...skipping 10 matching lines...) Expand all Loading... |
566 rel1 = new models.Relation( | 514 rel1 = new models.Relation( |
567 { id: 'relation-1', | 515 { id: 'relation-1', |
568 endpoints: | 516 endpoints: |
569 [[service_name, {name: 'db', role: 'peer'}]], | 517 [[service_name, {name: 'db', role: 'peer'}]], |
570 'interface': 'db', | 518 'interface': 'db', |
571 scope: 'global' | 519 scope: 'global' |
572 }); | 520 }); |
573 | 521 |
574 db.relations.add([rel0, rel1]); | 522 db.relations.add([rel0, rel1]); |
575 | 523 |
576 var view = new ServiceRelationsView( | 524 var view = makeServiceRelationsView(), |
577 { container: container, model: service, app: app, | 525 control = container.one('#relation-1'); |
578 querystring: {}}).render(); | |
579 | |
580 var control = container.one('#relation-1'); | |
581 control.simulate('click'); | 526 control.simulate('click'); |
582 var remove = container.one('#remove-modal-panel .btn-danger'); | 527 var remove = container.one('#remove-modal-panel .btn-danger'); |
583 remove.simulate('click'); | 528 remove.simulate('click'); |
584 var message = conn.last_message(); | 529 var message = conn.last_message(); |
585 message.op.should.equal('remove_relation'); | 530 message.op.should.equal('remove_relation'); |
586 remove.get('disabled').should.equal(true); | 531 remove.get('disabled').should.equal(true); |
587 }); | 532 }); |
588 | 533 |
589 it('should remove two consecutive relations when requested', | 534 it('should remove two consecutive relations when requested', |
590 function() { | 535 function() { |
(...skipping 13 matching lines...) Expand all Loading... |
604 endpoints: | 549 endpoints: |
605 [[service_name, {name: 'db', role: 'peer'}]], | 550 [[service_name, {name: 'db', role: 'peer'}]], |
606 'interface': 'db', | 551 'interface': 'db', |
607 scope: 'global' | 552 scope: 'global' |
608 }); | 553 }); |
609 | 554 |
610 db.relations.add([rel0, rel1]); | 555 db.relations.add([rel0, rel1]); |
611 db.relations.get_relations_for_service( | 556 db.relations.get_relations_for_service( |
612 service).length.should.equal(2); | 557 service).length.should.equal(2); |
613 | 558 |
614 var view = new ServiceRelationsView( | 559 var view = makeServiceRelationsView(), |
615 { container: container, model: service, app: app, | 560 control = container.one('#relation-0'); |
616 querystring: {}}).render(); | |
617 | |
618 var control = container.one('#relation-0'); | |
619 control.simulate('click'); | 561 control.simulate('click'); |
620 var remove = container.one('#remove-modal-panel .btn-danger'); | 562 var remove = container.one('#remove-modal-panel .btn-danger'); |
621 remove.simulate('click'); | 563 remove.simulate('click'); |
622 env.dispatch_result(conn.last_message()); | 564 env.dispatch_result(conn.last_message()); |
623 db.relations.get_relations_for_service( | 565 db.relations.get_relations_for_service( |
624 service).length.should.equal(1); | 566 service).length.should.equal(1); |
625 | 567 |
626 control = container.one('#relation-1'); | 568 control = container.one('#relation-1'); |
627 control.simulate('click'); | 569 control.simulate('click'); |
628 remove = container.one('#remove-modal-panel .btn-danger'); | 570 remove = container.one('#remove-modal-panel .btn-danger'); |
(...skipping 17 matching lines...) Expand all Loading... |
646 rel1 = new models.Relation( | 588 rel1 = new models.Relation( |
647 { id: 'relation-1', | 589 { id: 'relation-1', |
648 endpoints: | 590 endpoints: |
649 [[service_name, {name: 'db', role: 'peer'}]], | 591 [[service_name, {name: 'db', role: 'peer'}]], |
650 'interface': 'db', | 592 'interface': 'db', |
651 scope: 'global' | 593 scope: 'global' |
652 }); | 594 }); |
653 | 595 |
654 db.relations.add([rel0, rel1]); | 596 db.relations.add([rel0, rel1]); |
655 | 597 |
656 var view = new ServiceRelationsView( | 598 var view = makeServiceRelationsView({rel_id: 'relation-0'}), |
657 { container: container, model: service, app: app, | 599 row = container.one('.highlighted'); |
658 querystring: {rel_id: 'relation-0'}}).render(); | |
659 | |
660 var row = container.one('.highlighted'); | |
661 row.one('a').getHTML().should.equal('squid'); | 600 row.one('a').getHTML().should.equal('squid'); |
662 row.one('.btn').get('disabled').should.equal(false); | 601 row.one('.btn').get('disabled').should.equal(false); |
663 }); | 602 }); |
664 | 603 |
665 it('should handle errors properly in the callback', | 604 it('should handle errors properly in the callback', |
666 function() { | 605 function() { |
667 var service_name = service.get('id'), | 606 var service_name = service.get('id'), |
668 rel0 = new models.Relation( | 607 rel0 = new models.Relation( |
669 { id: 'relation-0', | 608 { id: 'relation-0', |
670 endpoints: | 609 endpoints: |
671 [[service_name, {name: 'db', role: 'source'}], | 610 [[service_name, {name: 'db', role: 'source'}], |
672 ['squid', {name: 'cache', role: 'front'}]], | 611 ['squid', {name: 'cache', role: 'front'}]], |
673 'interface': 'cache', | 612 'interface': 'cache', |
674 scope: 'global' | 613 scope: 'global' |
675 }), | 614 }), |
676 rel1 = new models.Relation( | 615 rel1 = new models.Relation( |
677 { id: 'relation-1', | 616 { id: 'relation-1', |
678 endpoints: | 617 endpoints: |
679 [[service_name, {name: 'db', role: 'peer'}]], | 618 [[service_name, {name: 'db', role: 'peer'}]], |
680 'interface': 'db', | 619 'interface': 'db', |
681 scope: 'global' | 620 scope: 'global' |
682 }); | 621 }); |
683 db.relations.add([rel0, rel1]); | 622 db.relations.add([rel0, rel1]); |
684 var view = new ServiceRelationsView( | 623 var view = makeServiceRelationsView(), |
685 { container: container, model: service, app: app, | 624 control = container.one('#relation-0'); |
686 querystring: {}}); | |
687 view.render(); | |
688 var control = container.one('#relation-0'); | |
689 control.simulate('click'); | 625 control.simulate('click'); |
690 var remove = container.one('#remove-modal-panel .btn-danger'); | 626 var remove = container.one('#remove-modal-panel .btn-danger'); |
691 remove.simulate('click'); | 627 remove.simulate('click'); |
692 | 628 |
693 var callbacks = Y.Object.values(env._txn_callbacks); | 629 var callbacks = Y.Object.values(env._txn_callbacks); |
694 callbacks.length.should.equal(1); | 630 callbacks.length.should.equal(1); |
695 var existing_notice_count = db.notifications.size(); | 631 var existing_notice_count = db.notifications.size(); |
696 callbacks[0]( | 632 callbacks[0]( |
697 { err: true, endpoint_a: service_name, | 633 { err: true, endpoint_a: service_name, |
698 endpoint_b: 'squid'}); | 634 endpoint_b: 'squid'}); |
(...skipping 18 matching lines...) Expand all Loading... |
717 } | 653 } |
718 }, { | 654 }, { |
719 agent_state: 'pending', | 655 agent_state: 'pending', |
720 relation_errors: { | 656 relation_errors: { |
721 a: '', | 657 a: '', |
722 b: '' | 658 b: '' |
723 } | 659 } |
724 }, { | 660 }, { |
725 testKey: 'error2', | 661 testKey: 'error2', |
726 agent_state: 'error' | 662 agent_state: 'error' |
727 }], filtered = ServiceView.prototype.filterUnits('error', units); | 663 }], filtered = views.service.prototype.filterUnits('error', units); |
728 | 664 |
729 assert.equal(2, filtered.length); | 665 assert.equal(2, filtered.length); |
730 assert.equal('error1', filtered[0].testKey); | 666 assert.equal('error1', filtered[0].testKey); |
731 assert.equal('error2', filtered[1].testKey); | 667 assert.equal('error2', filtered[1].testKey); |
732 }); | 668 }); |
733 | 669 |
734 }); | 670 }); |
735 }) (); | 671 }) (); |
LEFT | RIGHT |