root / snf-cyclades-app / synnefo / ui / static / snf / js / tests.js @ 270b11f9
History | View | Annotate | Download (22.2 kB)
1 |
// Copyright 2011 GRNET S.A. All rights reserved.
|
---|---|
2 |
//
|
3 |
// Redistribution and use in source and binary forms, with or
|
4 |
// without modification, are permitted provided that the following
|
5 |
// conditions are met:
|
6 |
//
|
7 |
// 1. Redistributions of source code must retain the above
|
8 |
// copyright notice, this list of conditions and the following
|
9 |
// disclaimer.
|
10 |
//
|
11 |
// 2. Redistributions in binary form must reproduce the above
|
12 |
// copyright notice, this list of conditions and the following
|
13 |
// disclaimer in the documentation and/or other materials
|
14 |
// provided with the distribution.
|
15 |
//
|
16 |
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 |
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 |
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 |
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 |
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 |
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 |
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 |
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 |
// POSSIBILITY OF SUCH DAMAGE.
|
28 |
//
|
29 |
// The views and conclusions contained in the software and
|
30 |
// documentation are those of the authors and should not be
|
31 |
// interpreted as representing official policies, either expressed
|
32 |
// or implied, of GRNET S.A.
|
33 |
//
|
34 |
|
35 |
$(document).ready(function(){ |
36 |
|
37 |
// shortcuts
|
38 |
snf = synnefo; |
39 |
models = snf.models; |
40 |
util = snf.utils; |
41 |
views = snf.views; |
42 |
bb = Backbone; |
43 |
vms = snf.storage.vms; |
44 |
nets = snf.storage.networks; |
45 |
|
46 |
synnefo.config.api_urls = { |
47 |
'compute': '/api/v1.1', |
48 |
'glance':'/images/v1.1' |
49 |
}; |
50 |
|
51 |
// what messages to display based on vm status
|
52 |
synnefo.config.diagnostics_status_messages_map = { |
53 |
'BUILD': ['image-helper-task-start', 'image-info'], |
54 |
'ERROR': ['image-error'] |
55 |
}; |
56 |
synnefo.config.diagnostic_messages_tpls = { |
57 |
'image-helper-task-start': "Running 'MESSAGE'" |
58 |
}; |
59 |
|
60 |
snf.user = {'token': 'TESTTOKEN'} |
61 |
|
62 |
module("VM Model")
|
63 |
|
64 |
test("vm diagnostics", function() { |
65 |
synnefo.storage.images.add({ |
66 |
id:1, |
67 |
size: 100, |
68 |
metadata:{
|
69 |
values: {
|
70 |
size: 100 |
71 |
} |
72 |
} |
73 |
}); |
74 |
synnefo.storage.vms.add({id:1, imageRef:1}); |
75 |
var vm = synnefo.storage.vms.at(0); |
76 |
var diagnostics = [{
|
77 |
source:'image-helper-task-start', |
78 |
message:'SSHCopyKey' |
79 |
}]; |
80 |
synnefo.storage.vms.update([{id:1, |
81 |
progress: 0, |
82 |
status: "BUILD"}], |
83 |
{silent:false}); |
84 |
equal(vm.get('status_message'), "init", |
85 |
"Show initial progress message");
|
86 |
synnefo.storage.vms.update([{id:1, |
87 |
progress: 10, |
88 |
status: "BUILD"}], |
89 |
{silent:false}); |
90 |
equal(vm.get('status_message'), "10.00 MB, 100.00 MB, 10", |
91 |
"Construct message based on image size");
|
92 |
synnefo.storage.vms.update([{id:1, |
93 |
progress: 99, |
94 |
status: "BUILD"}], |
95 |
{silent:false}); |
96 |
equal(vm.get('status_message'), "99.00 MB, 100.00 MB, 99", |
97 |
"Calculate 99% of image size and display image");
|
98 |
synnefo.storage.vms.update([{id:1, |
99 |
progress: 99, |
100 |
status: "ERROR"}], |
101 |
{silent:false}); |
102 |
equal(vm.get('status_message'), null); |
103 |
synnefo.storage.vms.update([{id:1, |
104 |
progress: 100, |
105 |
status: "BUILD"}], |
106 |
{silent:false}); |
107 |
equal(vm.get('status_message'), "final", |
108 |
"Progress is 100, show finializing progress message");
|
109 |
synnefo.storage.vms.update([{id:1, |
110 |
progress: 100, |
111 |
diagnostics: _.clone(diagnostics),
|
112 |
status: "BUILD"}], |
113 |
{silent:false}); |
114 |
|
115 |
synnefo.storage.vms.update([{id:1, |
116 |
progress: 100, |
117 |
diagnostics: _.clone(diagnostics),
|
118 |
status: "BUILD"}], |
119 |
{silent:false}); |
120 |
equal(vm.get('status_message'), "Running 'SSHCopyKey'", |
121 |
"Diagnostics added, show first building diagnostic message");
|
122 |
|
123 |
diagnostics.unshift({source:'unknown-source', message:'H&^^ACJJ'}); |
124 |
synnefo.storage.vms.update([{id:1, |
125 |
progress: 100, |
126 |
diagnostics: _.clone(diagnostics),
|
127 |
status: "BUILD"}], |
128 |
{silent:false}); |
129 |
equal(vm.get('status_message'), "Running 'SSHCopyKey'", |
130 |
"Unwanted diagnostics get filtered out, progress message remains");
|
131 |
|
132 |
synnefo.storage.vms.update([{id:1, |
133 |
progress: 100, |
134 |
diagnostics: _.clone(diagnostics),
|
135 |
status: "ERROR"}], |
136 |
{silent:false}); |
137 |
equal(vm.get('status_message'), null, |
138 |
"In error state since no error diagnostic is set we get null");
|
139 |
|
140 |
diagnostics.unshift({source:'image-error', message:'Image error'}); |
141 |
synnefo.storage.vms.update([{id:1, |
142 |
progress: 100, |
143 |
diagnostics: _.clone(diagnostics),
|
144 |
status: "BUILD"}], |
145 |
{silent:false}); |
146 |
equal(vm.get('status_message'), "Running 'SSHCopyKey'"); |
147 |
|
148 |
diagnostics.unshift({source:'image-error', message:'Image error'}); |
149 |
synnefo.storage.vms.update([{id:1, |
150 |
progress: 100, |
151 |
diagnostics: _.clone(diagnostics),
|
152 |
status: "ERROR"}], |
153 |
{silent:false}); |
154 |
equal(vm.get('status_message'), "Image error"); |
155 |
|
156 |
synnefo.storage.vms.update([{id:1, |
157 |
progress: 100, |
158 |
diagnostics: _.clone(diagnostics),
|
159 |
status: "ACTIVE"}], |
160 |
{silent:false}); |
161 |
equal(vm.get('status_message'), null); |
162 |
|
163 |
}); |
164 |
|
165 |
|
166 |
test("model change events", function(){ |
167 |
expect(8);
|
168 |
|
169 |
synnefo.storage.images.add({id:1,metadata:{values:{size:100}}}); |
170 |
var v1 = new models.VM({'imageRef':1}); |
171 |
v1.bind("change", function(){ |
172 |
ok(1, "change event triggered") |
173 |
equal(v1.get("status"), "BUILD") |
174 |
equal(v1.get("state"), "BUILD_COPY") |
175 |
}) |
176 |
v1.set({'status':'BUILD', 'progress':80, 'imageRef': 1}); |
177 |
v1.unbind(); |
178 |
|
179 |
v1.bind("change", function(){ |
180 |
ok(1, "change event triggered") |
181 |
equal(v1.get("status"), "BUILD") |
182 |
equal(v1.get("state"), "DESTROY") |
183 |
}) |
184 |
v1.set({'state':'DESTROY'}); |
185 |
v1.unbind(); |
186 |
|
187 |
v1.bind("change", function() { |
188 |
ok(1, "change event triggered") |
189 |
equal(v1.get("status"), "BUILD") |
190 |
equal(v1.get("state"), "BUILD_COPY") |
191 |
}) |
192 |
v1.set({'status':'BUILD', 'progress':80, 'imageRef': 1}); |
193 |
equal(v1.get("status"), "BUILD") |
194 |
equal(v1.get("state"), "DESTROY") |
195 |
v1.unbind(); |
196 |
}) |
197 |
|
198 |
test("model state transitions", function(){ |
199 |
var vm = models.VM;
|
200 |
|
201 |
var v1 = new vm(); |
202 |
v1.set({status: 'BUILD_COPY'}) |
203 |
equal(v1.get("state"), 'BUILD_COPY', "State is set"); |
204 |
v1.set({status: 'DESTROY'}) |
205 |
equal(v1.get("state"), 'DESTROY', "From buld to destroy"); |
206 |
v1.set({status: 'BUILD'}) |
207 |
equal(v1.get("state"), 'DESTROY', "Keep destroy state"); |
208 |
|
209 |
v1 = new vm();
|
210 |
v1.set({status: 'ACTIVE'}) |
211 |
equal(v1.get("state"), 'ACTIVE', "State is set"); |
212 |
|
213 |
v1.set({status: 'SHUTDOWN'}) |
214 |
equal(v1.get("state"), 'SHUTDOWN', "From active to shutdown (should change)"); |
215 |
|
216 |
v1.set({status: 'ACTIVE'}) |
217 |
equal(v1.get("state"), 'SHUTDOWN', "From shutdown to active (should not change)"); |
218 |
|
219 |
v1.set({status: 'STOPPED'}) |
220 |
equal(v1.get("state"), 'STOPPED', "From shutdown to stopped (should change)"); |
221 |
|
222 |
v1.set({status: 'ACTIVE'}) |
223 |
equal(v1.get("state"), 'ACTIVE', "From stopped to active (should change)"); |
224 |
v1.set({'status': 'STOPPED'}) |
225 |
equal(v1.get('state'), 'STOPPED', "From shutdown to stopped should change"); |
226 |
|
227 |
v1.set({'status': 'DESTROY'}) |
228 |
equal(v1.get('state'), 'DESTROY', "From stopped to destory should set state to DESTROY"); |
229 |
v1.set({'status': 'ACTIVE'}) |
230 |
equal(v1.get('state'), 'DESTROY', "From destroy to active should keep state to DESTROY"); |
231 |
v1.set({'status': 'REBOOT'}) |
232 |
equal(v1.get('state'), 'DESTROY', "From destroy to active should keep state to DESTROY"); |
233 |
v1.set({'status': 'DELETED'}) |
234 |
equal(v1.get('state'), 'DELETED', "Destroy should be kept until DELETE or ERROR"); |
235 |
|
236 |
v1 = new vm({status:'BUILD'}); |
237 |
equal(v1.get('state'), 'BUILD', "new vm with build as initial status") |
238 |
equal(v1.get('status'), 'BUILD', "new vm with build as initial status") |
239 |
v1.set({status:'ACTIVE'}) |
240 |
equal(v1.get('state'), 'ACTIVE', "active state has been set") |
241 |
equal(v1.get('status'), 'ACTIVE', "active status has been set") |
242 |
}) |
243 |
|
244 |
|
245 |
test("building states", function(){ |
246 |
synnefo.storage.images.add({id:1,metadata:{values:{size:100}}}); |
247 |
var vm = models.VM;
|
248 |
var v1 = new vm({'status':'BUILD','progress':0, 'imageRef':1}); |
249 |
equal(v1.get('state'), 'BUILD_INIT', "progress 0 sets state to BUILD_INIT"); |
250 |
equal(v1.get('status'), 'BUILD', "progress 0 sets status to BUILD"); |
251 |
equal(v1.get('progress_message'), 'init', "message 'init'"); |
252 |
v1.set({status:'BUILD', progress:50}); |
253 |
equal(v1.get('state'), 'BUILD_COPY', "progress 50 sets state to BUILD_COPY"); |
254 |
equal(v1.get('status'), 'BUILD', "progress 50 sets status to BUILD"); |
255 |
equal(v1.get('progress_message'), '50.00 MB, 100.00 MB, 50', "message: 'final'"); |
256 |
v1.set({status:'BUILD', progress:100}); |
257 |
equal(v1.get('state'), 'BUILD_FINAL', "progress 100 sets state to BUILD_FINAL"); |
258 |
equal(v1.get('status'), 'BUILD', "progress 100 sets status to BUILD"); |
259 |
v1.set({status:'ACTIVE', progress:100}); |
260 |
equal(v1.get('state'), 'ACTIVE', "ACTIVE set transition to ACTIVE"); |
261 |
equal(v1.get('status'), 'ACTIVE', "ACTIVE set transition to ACTIVE"); |
262 |
equal(v1.get('progress_message'), 'final', "message: 'final'"); |
263 |
}) |
264 |
|
265 |
test("active inactive states", function(){ |
266 |
|
267 |
var vm = models.VM;
|
268 |
var v1 = new vm(); |
269 |
var v = {}
|
270 |
var active = ['ACTIVE', 'BUILD', 'REBOOT']; |
271 |
for (v in active) { |
272 |
v = active[v]; |
273 |
v1.set({status: v})
|
274 |
equal(v1.is_active(), true, v + " status is active") |
275 |
} |
276 |
|
277 |
var v1 = new vm(); |
278 |
var inactive = ['STOPPED', 'ERROR', 'UNKNOWN']; |
279 |
for (v in inactive) { |
280 |
v = inactive[v]; |
281 |
v1.set({status: v})
|
282 |
equal(v1.is_active(), false, v1.state() + " status is not active") |
283 |
} |
284 |
|
285 |
}) |
286 |
|
287 |
test("transition event", function(){ |
288 |
expect(9);
|
289 |
|
290 |
var vm = new models.VM({status:'BUILD'}); |
291 |
vm.bind("transition", function(data) { |
292 |
ok(true, "Transition triggered"); |
293 |
equal(data.from, "BUILD")
|
294 |
equal(data.to, "ACTIVE");
|
295 |
}) |
296 |
// trigger 1 time
|
297 |
vm.set({status:'BUILD'}); |
298 |
vm.set({status:'ACTIVE'}); |
299 |
vm.unbind(); |
300 |
|
301 |
// from build to active
|
302 |
vm = new models.VM({status:'BUILD'}); |
303 |
vm.bind("transition", function(data) { |
304 |
ok(true, "Transition triggered"); |
305 |
equal(data.from, "BUILD")
|
306 |
equal(data.to, "ACTIVE");
|
307 |
}) |
308 |
// trigger 1 time
|
309 |
vm.set({status:'ACTIVE'}); |
310 |
vm.unbind(); |
311 |
|
312 |
// from active to shutdown
|
313 |
vm = new models.VM({status:'SHUTDOWN'}); |
314 |
vm.bind("transition", function(data) { |
315 |
ok(true, "Transition triggered"); |
316 |
equal(data.from, "SHUTDOWN")
|
317 |
equal(data.to, "STOPPED");
|
318 |
}) |
319 |
// trigger 1 time
|
320 |
vm.set({status:'STOPPED'}); |
321 |
}) |
322 |
|
323 |
module("Collections");
|
324 |
|
325 |
test("status model remove events", function(){ |
326 |
vms.unbind(); |
327 |
expect(1)
|
328 |
|
329 |
vms.bind("change", function(){ |
330 |
ok(-1, "change event should not get triggered"); |
331 |
}) |
332 |
|
333 |
vms.bind("remove", function(){ |
334 |
ok(1, "remove event triggered") |
335 |
}) |
336 |
|
337 |
var vm = new models.VM({id:1, status:"ACTIVE", name:"oldname"}); |
338 |
vms.add(vm); |
339 |
|
340 |
// NO change/delete just DELETE event triggered
|
341 |
vms.update([{id:1, status:"DELETED", name:"newname"}]) |
342 |
}); |
343 |
|
344 |
test("collection reset events", function() { |
345 |
expect(9);
|
346 |
|
347 |
var testCollection = models.Collection.extend({
|
348 |
url: '/testObject' |
349 |
}); |
350 |
var collection = new testCollection(); |
351 |
|
352 |
|
353 |
// reset on new entry after empty
|
354 |
$.mockjax({
|
355 |
url: '/testObject', |
356 |
responseTime: 50, |
357 |
responseText: [
|
358 |
{id:1, attr1: 1, attr2: 2} |
359 |
] |
360 |
}); |
361 |
// THIS SHOULD NOT FIRE, since we force update method
|
362 |
collection.bind("reset", function() { |
363 |
ok(1, "NOT EXPECTED: reset triggered on new entry while collection was empty"); |
364 |
}); |
365 |
collection.bind("add", function() { |
366 |
ok(1, "1: add triggered on new entry while collection was empty"); |
367 |
}); |
368 |
// THIS SHOULD NOT FIRE, model was added, not changed
|
369 |
collection.bind("change", function() { |
370 |
ok(1, "NOT EXPECTED: change triggered on new entry while collection was empty"); |
371 |
}); |
372 |
collection.fetch({'async': false}); |
373 |
equal(collection.length, 1, "2: collection contains 1 model"); |
374 |
collection.unbind(); |
375 |
$.mockjaxClear();
|
376 |
|
377 |
// reset is called on change
|
378 |
$.mockjax({
|
379 |
url: '/testObject', |
380 |
responseTime: 50, |
381 |
responseText: [
|
382 |
{id:1, attr1: 4, attr2: 2} |
383 |
] |
384 |
}); |
385 |
collection.bind("reset", function() { |
386 |
ok(1, "NOT EXPECTED: reset triggered on new entry while collection was empty"); |
387 |
}); |
388 |
collection.bind("add", function() { |
389 |
ok(1, "NOT EXPECTED: add triggered on new entry while collection was empty"); |
390 |
}); |
391 |
// THIS SHOULD NOT FIRE, model was added, not changed
|
392 |
collection.bind("change", function() { |
393 |
ok(1, "3: change triggered on new entry while collection was empty"); |
394 |
}); |
395 |
collection.fetch({'async': false, refresh:true}); |
396 |
equal(collection.length, 1, "4 collection contains 1 model"); |
397 |
collection.unbind(); |
398 |
$.mockjaxClear();
|
399 |
|
400 |
// reset on second entry
|
401 |
$.mockjax({
|
402 |
url: '/testObject', |
403 |
responseTime: 50, |
404 |
responseText: [
|
405 |
{id:1, attr1: 4, attr2: 2}, |
406 |
{id:2, attr1: 1, attr2: 2} |
407 |
] |
408 |
}); |
409 |
collection.bind("reset", function() { |
410 |
ok(1, "NOT EXPECTED: reset triggered when new model arrived"); |
411 |
}) |
412 |
collection.bind("add", function() { |
413 |
ok(1, "5: add triggered when new model arrived"); |
414 |
}) |
415 |
collection.bind("change", function() { |
416 |
ok(1, "NOT EXPECTED: change triggered when new model arrived"); |
417 |
}) |
418 |
collection.fetch({async:false, refresh:true}); |
419 |
equal(collection.length, 2, "6 new model added"); |
420 |
collection.unbind(); |
421 |
$.mockjaxClear();
|
422 |
|
423 |
// reset does not remove
|
424 |
$.mockjax({
|
425 |
url: '/testObject', |
426 |
responseTime: 50, |
427 |
responseText: [
|
428 |
{id:2, attr1: 1, attr2: 2} |
429 |
] |
430 |
}); |
431 |
collection.bind("reset", function() { |
432 |
ok(1, "NOT EXPECTED: reset triggered when model removed"); |
433 |
}) |
434 |
collection.bind("remove", function() { |
435 |
ok(1, "7: remove triggered when model removed"); |
436 |
}) |
437 |
collection.bind("change", function() { |
438 |
ok(1, "NOT EXPECTED: change triggered when model removed"); |
439 |
}) |
440 |
|
441 |
collection.fetch({async:false, refresh:true}); |
442 |
equal(collection.length, 1, "8 one model removed"); |
443 |
collection.unbind(); |
444 |
$.mockjaxClear();
|
445 |
|
446 |
// reset is not called on 304
|
447 |
$.mockjax({
|
448 |
url: '/testObject', |
449 |
responseTime: 50, |
450 |
status: 304, |
451 |
responseText: undefined |
452 |
}); |
453 |
// NO event is triggered on 304
|
454 |
collection.bind("reset", function() { |
455 |
ok(1, "WRONG: reset triggered on 304"); |
456 |
}); |
457 |
collection.bind("remove", function() { |
458 |
ok(1, "WRONG: remove triggered on 304"); |
459 |
}); |
460 |
collection.bind("change", function() { |
461 |
ok(1, "WRONG: remove triggered on 304"); |
462 |
}); |
463 |
collection.fetch({async:false, refresh:true}); |
464 |
equal(collection.length, 1, "9 one model removed"); |
465 |
collection.unbind(); |
466 |
$.mockjaxClear();
|
467 |
}) |
468 |
|
469 |
|
470 |
module("network vm connections")
|
471 |
test("network vm connections", function() { |
472 |
function _net(id, ip) { |
473 |
return {
|
474 |
id: id,
|
475 |
name: "net " + id, |
476 |
values:[{version:4, addr: ip}, {version:6, addr:ip}] |
477 |
} |
478 |
} |
479 |
vms.add({id:1, name:"s1", linked_to_nets:[_net("p", "127")]}); |
480 |
var vm = vms.at(0); |
481 |
|
482 |
nets.add({id:"p", nid:"p", name:"n1", linked_to:[1]}); |
483 |
var n = nets.at(0); |
484 |
}) |
485 |
|
486 |
module("images/flavors")
|
487 |
test("Test DELETE state image retrieval", function() { |
488 |
snf.storage.images.reset(); |
489 |
snf.storage.vms.reset(); |
490 |
|
491 |
var images = snf.storage.images;
|
492 |
var vms = snf.storage.vms;
|
493 |
|
494 |
var img = images.add({name:"image 1", id:1}).last(); |
495 |
var vm1 = vms.add({imageRef:1, name:"vm1"}).last(); |
496 |
var vm2 = vms.add({imageRef:2, name:"vm2"}).last(); |
497 |
|
498 |
vm1.get_image(function(i){
|
499 |
equal(i, img); |
500 |
}); |
501 |
|
502 |
// reset is not called on 304
|
503 |
$.mockjax({
|
504 |
url: '/api/v1.1/images/2', |
505 |
responseTime: 50, |
506 |
status: 200, |
507 |
responseText: {image:{name:"image 2", id:2}} |
508 |
}); |
509 |
|
510 |
|
511 |
equal(images.length, 1, "1 image exists"); |
512 |
vm2.get_image(function(i){
|
513 |
equal(images.get(2).get("name"), "image 2", "image data parsed"); |
514 |
equal(images.length, 2);
|
515 |
}); |
516 |
}) |
517 |
|
518 |
test("Test DELETE state flavor retrieval", function() { |
519 |
snf.storage.flavors.reset(); |
520 |
snf.storage.vms.reset(); |
521 |
|
522 |
var flavors = snf.storage.flavors;
|
523 |
var vms = snf.storage.vms;
|
524 |
|
525 |
var flv = flavors.add({id:1, cpu:1, disk:1, ram:1024}).last(); |
526 |
var vm1 = vms.add({flavorRef:1, name:"vm1"}).last(); |
527 |
var vm2 = vms.add({flavorRef:2, name:"vm2"}).last(); |
528 |
|
529 |
equal(flv, vm1.get_flavor()); |
530 |
|
531 |
// reset is not called on 304
|
532 |
$.mockjax({
|
533 |
url: '/api/v1.1/flavors/2', |
534 |
responseTime: 50, |
535 |
status: 200, |
536 |
responseText: {flavor:{cpu:1, ram:2048, disk:100, id:2}} |
537 |
}); |
538 |
|
539 |
|
540 |
equal(flavors.length, 1, "1 flavor exists"); |
541 |
vm2.get_flavor(); |
542 |
equal(flavors.get(2).get("ram"), 2048, "flavor data parsed"); |
543 |
equal(flavors.length, 2);
|
544 |
}) |
545 |
|
546 |
test("actions list object", function(){ |
547 |
var m = new models.Image(); |
548 |
var l = new models.ParamsList(m, "actions"); |
549 |
var count = 0; |
550 |
|
551 |
l.add("destroy");
|
552 |
equal(l.has_action("destroy"), true); |
553 |
equal(l.contains("destroy"), true); |
554 |
|
555 |
l.add("destroy", 1, {}); |
556 |
equal(l.has_action("destroy"), true); |
557 |
equal(l.contains("destroy", 1, {}), true); |
558 |
|
559 |
l.remove("destroy", 1, {}); |
560 |
equal(l.contains("destroy", 1, {}), false); |
561 |
|
562 |
m.bind("change:actions", function() { count ++}); |
563 |
l.add("destroy");
|
564 |
|
565 |
equal(count, 0);
|
566 |
l.add("destroy", 1, {}); |
567 |
equal(count, 1);
|
568 |
}); |
569 |
|
570 |
module("update handlers")
|
571 |
test("update handlers", function() { |
572 |
// this test is based on multiple timeouts
|
573 |
// so the results might differ between different browsers
|
574 |
// or browser load
|
575 |
stop(); |
576 |
|
577 |
var counter = 0; |
578 |
var cb = function() { |
579 |
counter++; |
580 |
} |
581 |
|
582 |
var opts = {
|
583 |
callback:cb,
|
584 |
interval: 10, |
585 |
fast: 5, |
586 |
increase: 5, |
587 |
max: 15, |
588 |
increase_after_calls: 3, |
589 |
initial_call: false |
590 |
} |
591 |
|
592 |
var h = new snf.api.updateHandler(opts); |
593 |
h.start(); |
594 |
|
595 |
var add = $.browser.msie ? 8 : 0; |
596 |
|
597 |
window.setTimeout(function(){
|
598 |
h.stop(); |
599 |
start(); |
600 |
// 4 calls, limit reached
|
601 |
equal(counter, 4, "normal calls"); |
602 |
equal(h.interval, opts.max, "limit reached");
|
603 |
|
604 |
stop(); |
605 |
h.start(false);
|
606 |
h.faster(); |
607 |
window.setTimeout(function(){
|
608 |
// 11 calls, limit reached
|
609 |
start(); |
610 |
equal(counter, 11, "faster calls"); |
611 |
equal(h.interval, opts.max, "limit reached");
|
612 |
h.stop(); |
613 |
stop(); |
614 |
window.setTimeout(function(){
|
615 |
// no additional calls because we stopped it
|
616 |
start(); |
617 |
equal(counter, 11, "no additional calls") |
618 |
}, 50 + add)
|
619 |
}, 50 + add)
|
620 |
}, 43 + add)
|
621 |
}) |
622 |
}) |