Merge lp:~hazmat/juju-gui/reliable-test into lp:juju-gui/experimental
- reliable-test
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~hazmat/juju-gui/reliable-test |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
1717 lines (+781/-641) (has conflicts) 15 files modified
app/app.js (+16/-5) app/modules-debug.js (+6/-1) app/templates/overview.handlebars (+1/-1) app/views/charm-panel.js (+3/-2) app/views/environment.js (+4/-7) app/views/topology/mega.js (+6/-2) app/views/topology/panzoom.js (+3/-2) app/views/utils.js (+5/-2) package.json (+4/-0) test/index.html (+75/-10) test/test_app.js (+44/-14) test/test_app_hotkeys.js (+47/-45) test/test_d3_components.js (+0/-1) test/test_notifications.js (+469/-454) test/test_notifier_widget.js (+98/-95) Text conflict in app/app.js Text conflict in package.json Text conflict in test/index.html Text conflict in test/test_app.js |
To merge this branch: | bzr merge lp:~hazmat/juju-gui/reliable-test |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email:
|
Commit message
Description of the change
Make tests more reliable.
- Disable async loading for yui to ensure app code is loaded before tests.
- Yank yui loader closures around tests, this is an anti-pattern.
- Any test file can be run in isolation now.
- Re-order test loading into alphabetical order to detect/correct collisions.
- Enable using firefox for tests via test loader fix.
- WIP enable using phantomjs for running tests on cli.
- Make topology-mega.js more foregiving on its (there's a event subscription leak here.)
- Fix event subscription leak in charm-panel code.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Benjamin Saller (bcsaller) wrote : | # |
This looks good, and looks like a real improvement, I made some changes
below that I think help as well.
Not sure why Y.use around the tests is an anti-pattern. With this style
of loading it all up front we could almost get away with use('*') which
would bind all the modules to the Y object but that gives up something
about scoped dependencies in the tests. Happy with the improvement.
https:/
File test/index.html (left):
https:/
test/index.html:44: <script>
I changed to a pre-load global config object and added use of the
delayUntil option. I think this makes this already nice improvement a
little nicer.
=== modified file 'test/index.html'
--- test/index.html 2012-12-24 03:17:11 +0000
+++ test/index.html 2012-12-24 18:21:50 +0000
@@ -53,29 +53,29 @@
<script>
- YUI({'async': false}).use('node', 'event', function(Y) {
- Y.on('domready', function() {
-
- var config = GlobalConfig;
-
- config.async = false;
- config.
-
- for (group in config.groups) {
+ YUI_config = {
+ async: false,
+ consoleEnabled: true,
+ delayUntil: 'domready'
+ };
+
+ YUI().use(['node', 'event'], function(Y) {
+ var config = GlobalConfig;
+
+ for (group in config.groups) {
var group = config.
- for (m in group.modules) {
- var resource = group.modules[m];
- if (!m || !resource.fullpath) {
- continue
- }
- resource.fullpath = resource.
- '/juju-ui/', '../juju-ui/');
- }
- }
- // Run the tests.
- if (window.
- else { mocha.run(); }
- });
+ for (m in group.modules) {
+ var resource = group.modules[m];
+ if (!m || !resource.fullpath) {
+ continue
+ }
+ resource.fullpath = resource.
+ '/juju-ui/', '../juju-ui/');
+ }
+ }
+ // Run the tests.
+ if (window.
+ else { mocha.run(); }
});
</script>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brad Crittenden (bac) wrote : | # |
These changes look good and should help standardize the way the tests
are run. Like Ben I'm curious as to the new pattern for Y.use in the
tests.
https:/
File app/app.js (right):
https:/
app/app.js:754: // This alias doesn't seem to work, including refs by
hand.
It might be clear to say
The juju-controllers alias...
And if it doesn't work can we get rid of it?
https:/
File app/modules-
https:/
app/modules-
Why are we combining for the development use? Won't that make debugging
more difficult?
https:/
File app/views/
https:/
app/views/
Just curious why you did the re-ordering. Is it required? Or are you
just grouping by hierarchy?
https:/
File test/index.html (right):
https:/
test/index.html:31: <!-- Tests (Alphabetical)-->
Thanks!
https:/
test/index.html:61: config.async = false;
Is setting async in the YUI() call and here in the config necessary? A
comment explaining why the redundancy is required might be helpful.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Kapil Thangavelu (hazmat) wrote : | # |
On 2012/12/24 18:26:11, bcsaller wrote:
> This looks good, and looks like a real improvement, I made some
changes below
> that I think help as well.
> Not sure why Y.use around the tests is an anti-pattern. With this
style of
> loading it all up front we could almost get away with use('*') which
would bind
> all the modules to the Y object but that gives up something about
scoped
> dependencies in the tests. Happy with the improvement.
in practice Y.use around the tests made test loading unreliable. mocha
scan would often complete before the Y.use had finished resulting in no
tests founds for a module.
> https:/
> File test/index.html (left):
https:/
> test/index.html:44: <script>
> I changed to a pre-load global config object and added use of the
delayUntil
> option. I think this makes this already nice improvement a little
nicer.
> === modified file 'test/index.html'
> --- test/index.html 2012-12-24 03:17:11 +0000
> +++ test/index.html 2012-12-24 18:21:50 +0000
> @@ -53,29 +53,29 @@
> <script>
> - YUI({'async': false}).use('node', 'event', function(Y) {
> - Y.on('domready', function() {
> -
> - var config = GlobalConfig;
> -
> - config.async = false;
> - config.
> -
> - for (group in config.groups) {
> + YUI_config = {
> + async: false,
> + consoleEnabled: true,
> + delayUntil: 'domready'
> + };
> +
> + YUI().use(['node', 'event'], function(Y) {
> + var config = GlobalConfig;
> +
> + for (group in config.groups) {
> var group = config.
> - for (m in group.modules) {
> - var resource = group.modules[m];
> - if (!m || !resource.fullpath) {
> - continue
> - }
> - resource.fullpath = resource.
> - '/juju-ui/', '../juju-ui/');
> - }
> - }
> - // Run the tests.
> - if (window.
> - else { mocha.run(); }
> - });
> + for (m in group.modules) {
> + var resource = group.modules[m];
> + if (!m || !resource.fullpath) {
> + continue
> + }
> + resource.fullpath = resource.
> + '/juju-ui/', '../juju-ui/');
> + }
> + }
> + // Run the tests.
> + if (window.
> + else { mocha.run(); }
> });
> </script>
looks nice, thanks. i'll give it a shot.
- 299. By Kapil Thangavelu
-
disable rollups in debug, yank yeti
Unmerged revisions
- 299. By Kapil Thangavelu
-
disable rollups in debug, yank yeti
Preview Diff
1 | === modified file 'app/app.js' |
2 | --- app/app.js 2013-01-07 21:38:42 +0000 |
3 | +++ app/app.js 2013-01-09 15:35:25 +0000 |
4 | @@ -793,19 +793,30 @@ |
5 | |
6 | }, '0.5.2', { |
7 | requires: [ |
8 | + 'juju-charm-models', |
9 | + 'juju-charm-panel', |
10 | + 'juju-charm-store', |
11 | 'juju-models', |
12 | +<<<<<<< TREE |
13 | 'juju-notification-controller', |
14 | 'juju-charm-models', |
15 | +======= |
16 | + 'juju-notifications', |
17 | + |
18 | + // This alias doesn't seem to work, including refs by hand. |
19 | + 'juju-controllers', |
20 | + 'juju-notification-controller', |
21 | + 'juju-env', |
22 | + |
23 | +>>>>>>> MERGE-SOURCE |
24 | 'juju-views', |
25 | - 'juju-controllers', |
26 | - 'juju-view-charm-search', |
27 | + 'juju-templates', |
28 | + 'handlebars', |
29 | 'io', |
30 | 'json-parse', |
31 | 'app-base', |
32 | 'app-transitions', |
33 | 'base', |
34 | 'node', |
35 | - 'model', |
36 | - 'juju-charm-panel', |
37 | - 'juju-charm-store'] |
38 | + 'model'] |
39 | }); |
40 | |
41 | === modified file 'app/modules-debug.js' |
42 | --- app/modules-debug.js 2012-12-21 20:34:38 +0000 |
43 | +++ app/modules-debug.js 2013-01-09 15:35:25 +0000 |
44 | @@ -12,7 +12,9 @@ |
45 | filter: 'debug', |
46 | // Set "true" for verbose logging of YUI |
47 | debug: false, |
48 | + |
49 | base: '/juju-ui/assets/javascripts/yui/', |
50 | + |
51 | // Use Rollups |
52 | combine: false, |
53 | |
54 | @@ -80,6 +82,7 @@ |
55 | 'juju-topology': { |
56 | fullpath: '/juju-ui/views/topology/topology.js' |
57 | }, |
58 | + |
59 | 'juju-view-utils': { |
60 | fullpath: '/juju-ui/views/utils.js' |
61 | }, |
62 | @@ -163,7 +166,9 @@ |
63 | }, |
64 | |
65 | 'juju-controllers': { |
66 | - use: ['juju-env', 'juju-charm-store', |
67 | + use: [ |
68 | + 'juju-env', |
69 | + 'juju-charm-store', |
70 | 'juju-notification-controller'] |
71 | }, |
72 | |
73 | |
74 | === modified file 'app/templates/overview.handlebars' |
75 | --- app/templates/overview.handlebars 2012-12-17 15:39:40 +0000 |
76 | +++ app/templates/overview.handlebars 2013-01-09 15:35:25 +0000 |
77 | @@ -1,5 +1,5 @@ |
78 | <div class="topology"> |
79 | - <div class="topology-canvas crosshatch-background"> |
80 | + <div class="crosshatch-background topology-canvas"> |
81 | <div class="environment-menu" id="service-menu"> |
82 | <div class="triangle"> </div> |
83 | <ul> |
84 | |
85 | === modified file 'app/views/charm-panel.js' |
86 | --- app/views/charm-panel.js 2012-12-14 20:25:16 +0000 |
87 | +++ app/views/charm-panel.js 2013-01-09 15:35:25 +0000 |
88 | @@ -1056,11 +1056,11 @@ |
89 | setPanel({name: 'charms'}); |
90 | |
91 | // Update position if we resize the window. |
92 | - Y.on('windowresize', function(e) { |
93 | + subscriptions.push(Y.on('windowresize', function(e) { |
94 | if (isPanelVisible) { |
95 | updatePanelPosition(); |
96 | } |
97 | - }); |
98 | + })); |
99 | |
100 | /** |
101 | * Hide the charm panel. |
102 | @@ -1229,6 +1229,7 @@ |
103 | requires: [ |
104 | 'view', |
105 | 'juju-view-utils', |
106 | + 'juju-templates', |
107 | 'node', |
108 | 'handlebars', |
109 | 'event-hover', |
110 | |
111 | === modified file 'app/views/environment.js' |
112 | --- app/views/environment.js 2012-12-20 17:28:17 +0000 |
113 | +++ app/views/environment.js 2013-01-09 15:35:25 +0000 |
114 | @@ -34,10 +34,10 @@ |
115 | |
116 | //If we need the initial HTML template |
117 | // take care of that. |
118 | - if (!this.svg) { |
119 | + if (!this.rendered) { |
120 | EnvironmentView.superclass.render.apply(this, arguments); |
121 | container.setHTML(Templates.overview()); |
122 | - this.svg = container.one('.topology'); |
123 | + this.rendered = true; |
124 | } |
125 | |
126 | if (!topo) { |
127 | @@ -79,13 +79,10 @@ |
128 | requires: ['juju-templates', |
129 | 'juju-view-utils', |
130 | 'juju-models', |
131 | - 'd3', |
132 | - 'd3-components', |
133 | + 'juju-topology', |
134 | + 'svg-layouts', |
135 | 'base-build', |
136 | 'handlebars-base', |
137 | 'node', |
138 | - 'svg-layouts', |
139 | - 'event-resize', |
140 | - 'slider', |
141 | 'view'] |
142 | }); |
143 | |
144 | === modified file 'app/views/topology/mega.js' |
145 | --- app/views/topology/mega.js 2013-01-08 16:08:41 +0000 |
146 | +++ app/views/topology/mega.js 2013-01-09 15:35:25 +0000 |
147 | @@ -721,6 +721,12 @@ |
148 | svg = container.one('svg'), |
149 | canvas = container.one('.topology-canvas'); |
150 | |
151 | + |
152 | + // Test band-aid due to lack of cleanup. |
153 | + if (!canvas) { |
154 | + return; |
155 | + } |
156 | + |
157 | topo.fire('beforePageSizeRecalculation'); |
158 | // Get the canvas out of the way so we can calculate the size |
159 | // correctly (the canvas contains the svg). We want it to be the |
160 | @@ -894,8 +900,6 @@ |
161 | 'd3', |
162 | 'd3-components', |
163 | 'juju-templates', |
164 | - 'node', |
165 | - 'event', |
166 | 'juju-models', |
167 | 'juju-env' |
168 | ] |
169 | |
170 | === modified file 'app/views/topology/panzoom.js' |
171 | --- app/views/topology/panzoom.js 2012-12-21 20:46:01 +0000 |
172 | +++ app/views/topology/panzoom.js 2013-01-09 15:35:25 +0000 |
173 | @@ -180,10 +180,11 @@ |
174 | views.PanZoomModule = PanZoomModule; |
175 | }, '0.1.0', { |
176 | requires: [ |
177 | + 'node', |
178 | + 'event', |
179 | + 'slider', |
180 | 'd3', |
181 | 'd3-components', |
182 | - 'node', |
183 | - 'event', |
184 | 'juju-models', |
185 | 'juju-env' |
186 | ] |
187 | |
188 | === modified file 'app/views/utils.js' |
189 | --- app/views/utils.js 2012-12-20 21:59:21 +0000 |
190 | +++ app/views/utils.js 2013-01-09 15:35:25 +0000 |
191 | @@ -118,6 +118,8 @@ |
192 | time: noop, |
193 | timeEnd: noop, |
194 | log: noop, |
195 | + info: noop, |
196 | + error: noop, |
197 | debug: noop |
198 | }; |
199 | |
200 | @@ -970,12 +972,13 @@ |
201 | }); |
202 | |
203 | }, '0.1.0', { |
204 | - requires: ['base-build', |
205 | + requires: [ |
206 | 'handlebars', |
207 | 'node', |
208 | 'view', |
209 | 'panel', |
210 | 'json-stringify', |
211 | 'gallery-markdown', |
212 | - 'datatype-date-format'] |
213 | + 'datatype-date-format', |
214 | + 'base-build',] |
215 | }); |
216 | |
217 | === modified file 'package.json' |
218 | --- package.json 2013-01-02 12:57:09 +0000 |
219 | +++ package.json 2013-01-09 15:35:25 +0000 |
220 | @@ -15,7 +15,11 @@ |
221 | "cryptojs": ">= 2.5.3" |
222 | }, |
223 | "devDependencies": { |
224 | +<<<<<<< TREE |
225 | "d3": "2.10.x", |
226 | +======= |
227 | + "d3": "<3.0.0", |
228 | +>>>>>>> MERGE-SOURCE |
229 | "yui": ">=3.7.0", |
230 | "mocha": "1.5.x", |
231 | "express": "3.x", |
232 | |
233 | === modified file 'test/index.html' |
234 | --- test/index.html 2013-01-07 21:38:42 +0000 |
235 | +++ test/index.html 2013-01-09 15:35:25 +0000 |
236 | @@ -3,15 +3,30 @@ |
237 | <head> |
238 | <meta charset="utf-8"> |
239 | <link rel="stylesheet" href="assets/mocha.css"> |
240 | + |
241 | + |
242 | + <!-- Load test runner/environment --> |
243 | + <script src="assets/chai.js"></script> |
244 | + <script src="assets/mocha.js"></script> |
245 | + <script> |
246 | + var assert = chai.assert, |
247 | + expect = chai.expect; |
248 | + |
249 | + var should = chai.should(); |
250 | + console.log('mocha setup'); |
251 | + mocha.setup({'ui': 'bdd', 'ignoreLeaks': false, 'timeout': 20000}) |
252 | + console.log('mocha setup done'); |
253 | + </script> |
254 | + |
255 | + <!-- Load up YUI base, app modules, and test utils --> |
256 | + <!-- Since only the tests depend on these files and the prod tests disable |
257 | + the YUI loader, we have to include them manually here. --> |
258 | <script src="/juju-ui/assets/modules.js"></script> |
259 | <script src="/juju-ui/assets/all-yui.js"></script> |
260 | - <!-- Since only the tests depend on these files and the prod tests disable |
261 | - the YUI loader, we have to include them manually here. --> |
262 | <script src="/juju-ui/assets/event-simulate.js"></script> |
263 | <script src="/juju-ui/assets/node-event-simulate.js"></script> |
264 | - <script src="assets/chai.js"></script> |
265 | - <script src="assets/mocha.js"></script> |
266 | <script src="utils.js"></script> |
267 | +<<<<<<< TREE |
268 | <script> |
269 | noLogin = true; |
270 | var assert = chai.assert, |
271 | @@ -43,20 +58,38 @@ |
272 | }); |
273 | </script> |
274 | |
275 | +======= |
276 | + |
277 | + |
278 | + <!-- Tests (Alphabetical)--> |
279 | + <script src="test_app.js"></script> |
280 | + <script src="test_app_hotkeys.js"></script> |
281 | + <script src="test_application_notifications.js"></script> |
282 | + <script src="test_charm_collection_view.js"></script> |
283 | + <script src="test_charm_configuration.js"></script> |
284 | + <script src="test_charm_panel.js"></script> |
285 | + <script src="test_charm_store.js"></script> |
286 | + <script src="test_charm_view.js"></script> |
287 | + <script src="test_console.js"></script> |
288 | +>>>>>>> MERGE-SOURCE |
289 | <script src="test_d3_components.js"></script> |
290 | +<<<<<<< TREE |
291 | <script src="test_topology.js"></script> |
292 | <script src="test_panzoom.js"></script> |
293 | +======= |
294 | + <script src="test_environment_view.js"></script> |
295 | +>>>>>>> MERGE-SOURCE |
296 | <script src="test_env.js"></script> |
297 | + <script src="test_endpoints.js"></script> |
298 | <script src="test_model.js"></script> |
299 | <script src="test_notifications.js"></script> |
300 | - <script src="test_app.js"></script> |
301 | - <script src="test_unit_view.js"></script> |
302 | - <script src="test_charm_collection_view.js"></script> |
303 | - <script src="test_charm_view.js"></script> |
304 | - <script src="test_environment_view.js"></script> |
305 | + <script src="test_notifier_widget.js"></script> |
306 | + <script src="test_topology.js"></script> |
307 | <script src="test_service_config_view.js"></script> |
308 | <script src="test_service_view.js"></script> |
309 | + <script src="test_unit_view.js"></script> |
310 | <script src="test_utils.js"></script> |
311 | +<<<<<<< TREE |
312 | <script src="test_login.js"></script> |
313 | <script src="test_charm_panel.js"></script> |
314 | <script src="test_charm_configuration.js"></script> |
315 | @@ -66,7 +99,39 @@ |
316 | <script src="test_charm_store.js"></script> |
317 | <script src="test_app_hotkeys.js"></script> |
318 | <script src="test_notifier_widget.js"></script> |
319 | - |
320 | +======= |
321 | + |
322 | +>>>>>>> MERGE-SOURCE |
323 | + |
324 | +<<<<<<< TREE |
325 | +======= |
326 | + <script> |
327 | + YUI({'async': false}).use('node', 'event', function(Y) { |
328 | + Y.on('domready', function() { |
329 | + |
330 | + var config = GlobalConfig; |
331 | + |
332 | + config.async = false; |
333 | + config.consoleEnabled = true; |
334 | + |
335 | + for (group in config.groups) { |
336 | + var group = config.groups[group]; |
337 | + for (m in group.modules) { |
338 | + var resource = group.modules[m]; |
339 | + if (!m || !resource.fullpath) { |
340 | + continue |
341 | + } |
342 | + resource.fullpath = resource.fullpath.replace( |
343 | + '/juju-ui/', '../juju-ui/'); |
344 | + } |
345 | + } |
346 | + // Run the tests. |
347 | + if (window.mochaPhantomJS) { mochaPhantomJS.run(); } |
348 | + else { mocha.run(); } |
349 | + }); |
350 | + }); |
351 | + </script> |
352 | +>>>>>>> MERGE-SOURCE |
353 | |
354 | </head> |
355 | |
356 | |
357 | === modified file 'test/test_app.js' |
358 | --- test/test_app.js 2013-01-02 20:39:49 +0000 |
359 | +++ test/test_app.js 2013-01-09 15:35:25 +0000 |
360 | @@ -31,9 +31,18 @@ |
361 | return app; |
362 | } |
363 | |
364 | -YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils'], function(Y) { |
365 | +(function() { |
366 | + |
367 | describe('Application basics', function() { |
368 | - var app, container; |
369 | + var Y, app, container; |
370 | + |
371 | + before(function(done) { |
372 | + Y = YUI(GlobalConfig).use( |
373 | + ['juju-gui', 'juju-tests-utils'], |
374 | + function(Y) { |
375 | + done(); |
376 | + }); |
377 | + }); |
378 | |
379 | beforeEach(function() { |
380 | container = Y.one('#main') |
381 | @@ -116,15 +125,29 @@ |
382 | }); |
383 | |
384 | }); |
385 | +<<<<<<< TREE |
386 | }); |
387 | |
388 | YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils'], function(Y) { |
389 | |
390 | +======= |
391 | +})(); |
392 | + |
393 | + |
394 | + |
395 | +(function() { |
396 | + |
397 | +>>>>>>> MERGE-SOURCE |
398 | describe('Application Connection State', function() { |
399 | - var container; |
400 | + var container, Y; |
401 | |
402 | - before(function() { |
403 | - container = Y.Node.create('<div id="test" class="container"></div>'); |
404 | + before(function(done) { |
405 | + Y = YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils'], |
406 | + function(Y) { |
407 | + container = Y.Node.create( |
408 | + '<div id="test" class="container"></div>'); |
409 | + done(); |
410 | + }); |
411 | }); |
412 | |
413 | it('should be able to handle env connection status changes', function() { |
414 | @@ -135,7 +158,6 @@ |
415 | reset_called = false, |
416 | noop = function() {return this;}; |
417 | |
418 | - |
419 | // mock the db |
420 | app.db = { |
421 | // mock out notifications |
422 | @@ -166,15 +188,23 @@ |
423 | }); |
424 | |
425 | }); |
426 | -}); |
427 | - |
428 | -YUI(GlobalConfig).use(['juju-models', 'juju-gui', 'datasource-local', |
429 | - 'juju-tests-utils', 'json-stringify'], function(Y) { |
430 | +})(); |
431 | + |
432 | + |
433 | +(function() { |
434 | + |
435 | describe('Application prefetching', function() { |
436 | - var models, conn, env, app, container, charm_store, data, juju; |
437 | + var Y, models, conn, env, app, container, charm_store, data, juju; |
438 | |
439 | - before(function() { |
440 | - models = Y.namespace('juju.models'); |
441 | + before(function(done) { |
442 | + console.log('Loading App prefetch test code'); |
443 | + Y = YUI(GlobalConfig).use( |
444 | + ['juju-gui', 'datasource-local', |
445 | + 'juju-views', 'juju-templates', |
446 | + 'juju-tests-utils', 'json-stringify'], function(Y) { |
447 | + models = Y.namespace('juju.models'); |
448 | + done(); |
449 | + }); |
450 | }); |
451 | |
452 | beforeEach(function() { |
453 | @@ -243,4 +273,4 @@ |
454 | get_endpoints_count.should.equal(2); |
455 | }); |
456 | }); |
457 | -}); |
458 | +})(); |
459 | |
460 | === modified file 'test/test_app_hotkeys.js' |
461 | --- test/test_app_hotkeys.js 2013-01-07 20:10:00 +0000 |
462 | +++ test/test_app_hotkeys.js 2013-01-09 15:35:25 +0000 |
463 | @@ -1,11 +1,11 @@ |
464 | 'use strict'; |
465 | |
466 | -YUI(GlobalConfig).use(['juju-gui', 'juju-tests-utils', 'node-event-simulate'], |
467 | - function(Y) { |
468 | - describe('application hotkeys', function() { |
469 | - var app, container, windowNode; |
470 | +describe('application hotkeys', function() { |
471 | + var app, container, windowNode, Y; |
472 | |
473 | - before(function(done) { |
474 | + before(function(done) { |
475 | + Y = YUI(GlobalConfig).use( |
476 | + ['juju-gui', 'juju-tests-utils', 'node-event-simulate'], function(Y) { |
477 | var env = { |
478 | after: function() {}, |
479 | get: function() {}, |
480 | @@ -22,43 +22,45 @@ |
481 | done(); |
482 | }); |
483 | |
484 | - beforeEach(function() { |
485 | - container = Y.Node.create('<div/>'); |
486 | - Y.one('#main').append(container); |
487 | - app.render(); |
488 | - }); |
489 | - |
490 | - afterEach(function() { |
491 | - container.remove(true); |
492 | - }); |
493 | - |
494 | - it('should listen for alt-S events', function() { |
495 | - var searchInput = Y.Node.create('<input/>'); |
496 | - searchInput.set('id', 'charm-search-field'); |
497 | - container.append(searchInput); |
498 | - windowNode.simulate('keydown', { |
499 | - keyCode: 83, // "S" key. |
500 | - altKey: true |
501 | - }); |
502 | - // Did charm-search-field get the focus? |
503 | - assert.equal(searchInput, Y.one(document.activeElement)); |
504 | - }); |
505 | - |
506 | - it('should listen for alt-E events', function() { |
507 | - var altEtriggered = false; |
508 | - app.on('navigateTo', function(ev) { |
509 | - if (ev && ev.url === '/') { |
510 | - altEtriggered = true; |
511 | - } |
512 | - // Avoid URL change performed by additional listeners. |
513 | - ev.stopImmediatePropagation(); |
514 | - }); |
515 | - windowNode.simulate('keydown', { |
516 | - keyCode: 69, // "E" key. |
517 | - altKey: true |
518 | - }); |
519 | - assert.isTrue(altEtriggered); |
520 | - }); |
521 | - |
522 | - }); |
523 | - }); |
524 | + }); |
525 | + |
526 | + beforeEach(function() { |
527 | + container = Y.Node.create('<div/>'); |
528 | + Y.one('#main').append(container); |
529 | + app.render(); |
530 | + }); |
531 | + |
532 | + afterEach(function() { |
533 | + container.remove(true); |
534 | + }); |
535 | + |
536 | + it('should listen for alt-S events', function() { |
537 | + var searchInput = Y.Node.create('<input/>'); |
538 | + searchInput.set('id', 'charm-search-field'); |
539 | + container.append(searchInput); |
540 | + windowNode.simulate('keydown', { |
541 | + keyCode: 83, // "S" key. |
542 | + altKey: true |
543 | + }); |
544 | + // Did charm-search-field get the focus? |
545 | + assert.equal(searchInput, Y.one(document.activeElement)); |
546 | + }); |
547 | + |
548 | + it('should listen for alt-E events', function() { |
549 | + var altEtriggered = false; |
550 | + app.on('navigateTo', function(ev) { |
551 | + if (ev && ev.url === '/') { |
552 | + altEtriggered = true; |
553 | + } |
554 | + // Avoid URL change performed by additional listeners. |
555 | + ev.stopImmediatePropagation(); |
556 | + }); |
557 | + windowNode.simulate('keydown', { |
558 | + keyCode: 69, // "E" key. |
559 | + altKey: true |
560 | + }); |
561 | + assert.isTrue(altEtriggered); |
562 | + }); |
563 | + |
564 | +}); |
565 | + |
566 | |
567 | === modified file 'test/test_d3_components.js' |
568 | --- test/test_d3_components.js 2012-12-19 13:45:10 +0000 |
569 | +++ test/test_d3_components.js 2013-01-09 15:35:25 +0000 |
570 | @@ -32,7 +32,6 @@ |
571 | state.cancelled = true; |
572 | } |
573 | }); |
574 | - |
575 | done(); |
576 | }); |
577 | }); |
578 | |
579 | === modified file 'test/test_notifications.js' |
580 | --- test/test_notifications.js 2012-11-23 16:21:32 +0000 |
581 | +++ test/test_notifications.js 2013-01-09 15:35:25 +0000 |
582 | @@ -1,462 +1,477 @@ |
583 | 'use strict'; |
584 | |
585 | -YUI(GlobalConfig).use(['juju-gui', 'node-event-simulate', 'juju-tests-utils'], |
586 | +describe('notifications', function() { |
587 | + var Y, juju, models, views; |
588 | + |
589 | + var default_env = { |
590 | + 'result': [ |
591 | + ['service', 'add', { |
592 | + 'charm': 'cs:precise/wordpress-6', |
593 | + 'id': 'wordpress', |
594 | + 'exposed': false |
595 | + }], |
596 | + ['service', 'add', { |
597 | + 'charm': 'cs:precise/mediawiki-3', |
598 | + 'id': 'mediawiki', |
599 | + 'exposed': false |
600 | + }], |
601 | + ['service', 'add', { |
602 | + 'charm': 'cs:precise/mysql-6', |
603 | + 'id': 'mysql' |
604 | + }], |
605 | + ['relation', 'add', { |
606 | + 'interface': 'reversenginx', |
607 | + 'scope': 'global', |
608 | + 'endpoints': |
609 | + [['wordpress', {'role': 'peer', 'name': 'loadbalancer'}]], |
610 | + 'id': 'relation-0000000000' |
611 | + }], |
612 | + ['relation', 'add', { |
613 | + 'interface': 'mysql', |
614 | + 'scope': 'global', |
615 | + 'endpoints': |
616 | + [['mysql', {'role': 'server', 'name': 'db'}], |
617 | + ['wordpress', {'role': 'client', 'name': 'db'}]], |
618 | + 'id': 'relation-0000000001' |
619 | + }], |
620 | + ['machine', 'add', { |
621 | + 'agent-state': 'running', |
622 | + 'instance-state': 'running', |
623 | + 'id': 0, |
624 | + 'instance-id': 'local', |
625 | + 'dns-name': 'localhost' |
626 | + }], |
627 | + ['unit', 'add', { |
628 | + 'machine': 0, |
629 | + 'agent-state': 'started', |
630 | + 'public-address': '192.168.122.113', |
631 | + 'id': 'wordpress/0' |
632 | + }], |
633 | + ['unit', 'add', { |
634 | + 'machine': 0, |
635 | + 'agent-state': 'error', |
636 | + 'public-address': '192.168.122.222', |
637 | + 'id': 'mysql/0' |
638 | + }] |
639 | + ], |
640 | + 'op': 'delta' |
641 | + }; |
642 | + |
643 | + |
644 | + before(function(done) { |
645 | + Y = YUI(GlobalConfig).use([ |
646 | + 'juju-models', |
647 | + 'juju-views', |
648 | + 'juju-gui', |
649 | + 'juju-env', |
650 | + 'node-event-simulate', |
651 | + 'juju-tests-utils'], |
652 | + |
653 | function(Y) { |
654 | - describe('notifications', function() { |
655 | - var juju, models, views; |
656 | - |
657 | - var default_env = { |
658 | - 'result': [ |
659 | - ['service', 'add', { |
660 | - 'charm': 'cs:precise/wordpress-6', |
661 | - 'id': 'wordpress', |
662 | - 'exposed': false |
663 | - }], |
664 | - ['service', 'add', { |
665 | - 'charm': 'cs:precise/mediawiki-3', |
666 | - 'id': 'mediawiki', |
667 | - 'exposed': false |
668 | - }], |
669 | - ['service', 'add', { |
670 | - 'charm': 'cs:precise/mysql-6', |
671 | - 'id': 'mysql' |
672 | - }], |
673 | - ['relation', 'add', { |
674 | - 'interface': 'reversenginx', |
675 | - 'scope': 'global', |
676 | - 'endpoints': |
677 | - [['wordpress', {'role': 'peer', 'name': 'loadbalancer'}]], |
678 | - 'id': 'relation-0000000000' |
679 | - }], |
680 | - ['relation', 'add', { |
681 | - 'interface': 'mysql', |
682 | - 'scope': 'global', |
683 | - 'endpoints': |
684 | - [['mysql', {'role': 'server', 'name': 'db'}], |
685 | - ['wordpress', {'role': 'client', 'name': 'db'}]], |
686 | - 'id': 'relation-0000000001' |
687 | - }], |
688 | - ['machine', 'add', { |
689 | - 'agent-state': 'running', |
690 | - 'instance-state': 'running', |
691 | - 'id': 0, |
692 | - 'instance-id': 'local', |
693 | - 'dns-name': 'localhost' |
694 | - }], |
695 | - ['unit', 'add', { |
696 | - 'machine': 0, |
697 | - 'agent-state': 'started', |
698 | - 'public-address': '192.168.122.113', |
699 | - 'id': 'wordpress/0' |
700 | - }], |
701 | - ['unit', 'add', { |
702 | - 'machine': 0, |
703 | - 'agent-state': 'error', |
704 | - 'public-address': '192.168.122.222', |
705 | - 'id': 'mysql/0' |
706 | - }] |
707 | - ], |
708 | - 'op': 'delta' |
709 | - }; |
710 | - |
711 | - |
712 | - before(function() { |
713 | - juju = Y.namespace('juju'); |
714 | - models = Y.namespace('juju.models'); |
715 | - views = Y.namespace('juju.views'); |
716 | - }); |
717 | - |
718 | - it('must be able to make notification and lists of notifications', |
719 | - function() { |
720 | - var note1 = new models.Notification({ |
721 | - title: 'test1', |
722 | - message: 'Hello' |
723 | - }), |
724 | - note2 = new models.Notification({ |
725 | - title: 'test2', |
726 | - message: 'I said goodnight!' |
727 | - }), |
728 | - notifications = new models.NotificationList(); |
729 | - |
730 | - notifications.add([note1, note2]); |
731 | - notifications.size().should.equal(2); |
732 | - |
733 | - // timestamp should be generated once |
734 | - var ts = note1.get('timestamp'); |
735 | - note1.get('timestamp').should.equal(ts); |
736 | - // force an update so we can test ordering |
737 | - // fast execution can result in same timestamp |
738 | - note2.set('timestamp', ts + 1); |
739 | - note2.get('timestamp').should.be.above(ts); |
740 | - |
741 | - // defaults as expected |
742 | - note1.get('level').should.equal('info'); |
743 | - note2.get('level').should.equal('info'); |
744 | - // the sort order on the list should be by |
745 | - // timestamp |
746 | - notifications.get('title').should.eql(['test2', 'test1']); |
747 | - }); |
748 | - |
749 | - it('must be able to render its view with sample data', |
750 | - function() { |
751 | - var note1 = new models.Notification({ |
752 | - title: 'test1', message: 'Hello'}), |
753 | - note2 = new models.Notification({ |
754 | - title: 'test2', message: 'I said goodnight!'}), |
755 | - notifications = new models.NotificationList(), |
756 | - container = Y.Node.create('<div id="test">'), |
757 | - env = new juju.Environment(), |
758 | - view = new views.NotificationsView({ |
759 | - container: container, |
760 | - notifications: notifications, |
761 | - env: env}); |
762 | - view.render(); |
763 | - // Verify the expected elements appear in the view |
764 | - container.one('#notify-list').should.not.equal(undefined); |
765 | - container.destroy(); |
766 | - }); |
767 | - |
768 | - it('must be able to limit the size of notification events', |
769 | - function() { |
770 | - var note1 = new models.Notification({ |
771 | - title: 'test1', |
772 | - message: 'Hello' |
773 | - }), |
774 | - note2 = new models.Notification({ |
775 | - title: 'test2', |
776 | - message: 'I said goodnight!' |
777 | - }), |
778 | - note3 = new models.Notification({ |
779 | - title: 'test3', |
780 | - message: 'Never remember' |
781 | - }), |
782 | - notifications = new models.NotificationList({ |
783 | - max_size: 2 |
784 | - }); |
785 | - |
786 | - notifications.add([note1, note2]); |
787 | - notifications.size().should.equal(2); |
788 | - |
789 | - // Adding a new notification should pop the oldest from the list |
790 | - // (we exceed max_size) |
791 | - notifications.add(note3); |
792 | - notifications.size().should.equal(2); |
793 | - notifications.get('title').should.eql(['test3', 'test2']); |
794 | - }); |
795 | - |
796 | - it('must be able to get notifications for a given model', |
797 | - function() { |
798 | - var m = new models.Service({id: 'mediawiki'}), |
799 | - note1 = new models.Notification({ |
800 | - title: 'test1', |
801 | - message: 'Hello', |
802 | - modelId: m |
803 | - }), |
804 | - note2 = new models.Notification({ |
805 | - title: 'test2', |
806 | - message: 'I said goodnight!' |
807 | - }), |
808 | - notifications = new models.NotificationList(); |
809 | - |
810 | - notifications.add([note1, note2]); |
811 | - notifications.size().should.equal(2); |
812 | - notifications.getNotificationsForModel(m).should.eql( |
813 | - [note1]); |
814 | - |
815 | - }); |
816 | - |
817 | - it('must be able to include and show object links', function() { |
818 | - var container = Y.Node.create('<div id="test">'), |
819 | - conn = new(Y.namespace('juju-tests.utils')).SocketStub(), |
820 | - env = new juju.Environment({conn: conn}), |
821 | - app = new Y.juju.App({env: env, container: container}), |
822 | - db = app.db, |
823 | - mw = db.services.create({id: 'mediawiki', |
824 | - name: 'mediawiki'}), |
825 | - notifications = db.notifications, |
826 | - view = new views.NotificationsOverview({ |
827 | - container: container, |
828 | - notifications: notifications, |
829 | - app: app, |
830 | - env: env}).render(); |
831 | - // we use overview here for testing as it defaults |
832 | - // to showing all notices |
833 | - |
834 | - // we can use app's routing table to derive a link |
835 | - notifications.create({title: 'Service Down', |
836 | - message: 'Your service has an error', |
837 | - link: app.getModelURL(mw) |
838 | - }); |
839 | - view.render(); |
840 | - var link = container.one('.notice').one('a'); |
841 | - link.getAttribute('href').should.equal( |
842 | - '/service/mediawiki/'); |
843 | - link.getHTML().should.contain('View Details'); |
844 | - |
845 | - |
846 | - // create a new notice passing the link_title |
847 | - notifications.create({title: 'Service Down', |
848 | - message: 'Your service has an error', |
849 | - link: app.getModelURL(mw), |
850 | - link_title: 'Resolve this' |
851 | - }); |
852 | - view.render(); |
853 | - link = container.one('.notice').one('a'); |
854 | - link.getAttribute('href').should.equal( |
855 | - '/service/mediawiki/'); |
856 | - link.getHTML().should.contain('Resolve this'); |
857 | - }); |
858 | - |
859 | - it('must be able to evict irrelevant notices', function() { |
860 | - var container = Y.Node.create( |
861 | - '<div id="test" class="container"></div>'), |
862 | - conn = new(Y.namespace('juju-tests.utils')).SocketStub(), |
863 | - env = new juju.Environment({conn: conn}), |
864 | - app = new Y.juju.App({ |
865 | - env: env, |
866 | - container: container, |
867 | - viewContainer: container |
868 | - }); |
869 | - var environment_delta = default_env; |
870 | - |
871 | - var notifications = app.db.notifications, |
872 | - view = new views.NotificationsView({ |
873 | - container: container, |
874 | - notifications: notifications, |
875 | - env: app.env}).render(); |
876 | - |
877 | - |
878 | - app.env.dispatch_result(environment_delta); |
879 | - |
880 | - |
881 | - notifications.size().should.equal(7); |
882 | - // we have one unit in error |
883 | - view.getShowable().length.should.equal(1); |
884 | - |
885 | - // now fire another delta event marking that node as |
886 | - // started |
887 | - app.env.dispatch_result({result: [['unit', 'change', { |
888 | - 'machine': 0, |
889 | - 'agent-state': 'started', |
890 | - 'public-address': '192.168.122.222', |
891 | - 'id': 'mysql/0' |
892 | - }]], op: 'delta'}); |
893 | - notifications.size().should.equal(8); |
894 | - // This should have evicted the prior notice from seen |
895 | - view.getShowable().length.should.equal(0); |
896 | - }); |
897 | - |
898 | - it('must properly construct title and message based on level from ' + |
899 | - 'event data', |
900 | - function() { |
901 | - var container = Y.Node.create( |
902 | - '<div id="test" class="container"></div>'), |
903 | - app = new Y.juju.App({ |
904 | - container: container, |
905 | - viewContainer: container |
906 | - }); |
907 | - var environment_delta = { |
908 | - 'result': [ |
909 | - ['service', 'add', { |
910 | - 'charm': 'cs:precise/wordpress-6', |
911 | - 'id': 'wordpress' |
912 | - }], |
913 | - ['service', 'add', { |
914 | - 'charm': 'cs:precise/mediawiki-3', |
915 | - 'id': 'mediawiki' |
916 | - }], |
917 | - ['service', 'add', { |
918 | - 'charm': 'cs:precise/mysql-6', |
919 | - 'id': 'mysql' |
920 | - }], |
921 | - ['unit', 'add', { |
922 | - 'agent-state': 'install-error', |
923 | - 'id': 'wordpress/0' |
924 | - }], |
925 | - ['unit', 'add', { |
926 | - 'agent-state': 'error', |
927 | - 'public-address': '192.168.122.222', |
928 | - 'id': 'mysql/0' |
929 | - }], |
930 | - ['unit', 'add', { |
931 | - 'public-address': '192.168.122.222', |
932 | - 'id': 'mysql/2' |
933 | - }] |
934 | - ], |
935 | - 'op': 'delta' |
936 | - }; |
937 | - |
938 | - var notifications = app.db.notifications, |
939 | - view = new views.NotificationsView({ |
940 | - container: container, |
941 | - notifications: notifications, |
942 | - app: app, |
943 | - env: app.env}).render(); |
944 | - |
945 | - app.env.dispatch_result(environment_delta); |
946 | - |
947 | - notifications.size().should.equal(6); |
948 | - // we have one unit in error |
949 | - var showable = view.getShowable(); |
950 | - showable.length.should.equal(2); |
951 | - // The first showable notification should indicate an error. |
952 | - showable[0].level.should.equal('error'); |
953 | - showable[0].title.should.equal('Error with mysql/0'); |
954 | - showable[0].message.should.equal('Agent-state = error.'); |
955 | - // The second showable notification should also indicate an error. |
956 | - showable[1].level.should.equal('error'); |
957 | - showable[1].title.should.equal('Error with wordpress/0'); |
958 | - showable[1].message.should.equal('Agent-state = install-error.'); |
959 | - // The first non-error notice should have an 'info' level and less |
960 | - // severe messaging. |
961 | - var notice = notifications.item(0); |
962 | - notice.get('level').should.equal('info'); |
963 | - notice.get('title').should.equal('Problem with mysql/2'); |
964 | - notice.get('message').should.equal(''); |
965 | - }); |
966 | - |
967 | - |
968 | - it('should open on click and close on clickoutside', function(done) { |
969 | - var container = Y.Node.create('<div id="test-container" ' + |
970 | - 'style="display: none" class="container"/>'), |
971 | - notifications = new models.NotificationList(), |
972 | - env = new juju.Environment(), |
973 | - view = new views.NotificationsView({ |
974 | - container: container, |
975 | - notifications: notifications, |
976 | - env: env}).render(), |
977 | - indicator; |
978 | - |
979 | - Y.one('body').append(container); |
980 | - notifications.add({title: 'testing', 'level': 'error'}); |
981 | - indicator = container.one('#notify-indicator'); |
982 | - |
983 | - indicator.simulate('click'); |
984 | - indicator.ancestor().hasClass('open').should.equal(true); |
985 | - |
986 | - Y.one('body').simulate('click'); |
987 | - indicator.ancestor().hasClass('open').should.equal(false); |
988 | - |
989 | - container.remove(); |
990 | - done(); |
991 | - }); |
992 | - }); |
993 | - |
994 | - describe('changing notifications to words', function() { |
995 | - var juju; |
996 | - |
997 | - before(function() { |
998 | - juju = Y.namespace('juju'); |
999 | - }); |
1000 | - |
1001 | - it('should correctly translate notification operations into English', |
1002 | - function() { |
1003 | - assert.equal(juju._changeNotificationOpToWords('add'), 'created'); |
1004 | - assert.equal(juju._changeNotificationOpToWords('remove'), |
1005 | - 'removed'); |
1006 | - assert.equal(juju._changeNotificationOpToWords('not-an-op'), |
1007 | - 'changed'); |
1008 | - }); |
1009 | - }); |
1010 | - |
1011 | - describe('relation notifications', function() { |
1012 | - var juju; |
1013 | - |
1014 | - before(function() { |
1015 | - juju = Y.namespace('juju'); |
1016 | - }); |
1017 | - |
1018 | - it('should produce reasonable titles', function() { |
1019 | - assert.equal( |
1020 | - juju._relationNotifications.title(undefined, 'add'), |
1021 | - 'Relation created'); |
1022 | - assert.equal( |
1023 | - juju._relationNotifications.title(undefined, 'remove'), |
1024 | - 'Relation removed'); |
1025 | - }); |
1026 | - |
1027 | - it('should generate messages about two-party relations', function() { |
1028 | - var changeData = |
1029 | - { endpoints: |
1030 | - [['endpoint0', {name: 'relation0'}], |
1031 | - ['endpoint1', {name: 'relation1'}]]}; |
1032 | - assert.equal( |
1033 | - juju._relationNotifications.message(undefined, 'add', |
1034 | - changeData), 'Relation between endpoint0 (relation type ' + |
1035 | - '"relation0") and endpoint1 (relation type "relation1") ' + |
1036 | - 'was created'); |
1037 | - }); |
1038 | - |
1039 | - it('should generate messages about one-party relations', function() { |
1040 | - var changeData = |
1041 | - { endpoints: |
1042 | - [['endpoint1', {name: 'relation1'}]]}; |
1043 | - assert.equal( |
1044 | - juju._relationNotifications.message(undefined, 'add', |
1045 | - changeData), 'Relation with endpoint1 (relation type ' + |
1046 | - '"relation1") was created'); |
1047 | - }); |
1048 | - }); |
1049 | - |
1050 | - describe('notification visual feedback', function() { |
1051 | - var env, models, notifications, notificationsView, notifierBox, views; |
1052 | - |
1053 | - before(function() { |
1054 | + juju = Y.namespace('juju'); |
1055 | + models = Y.namespace('juju.models'); |
1056 | + views = Y.namespace('juju.views'); |
1057 | + done(); |
1058 | + }); |
1059 | + }); |
1060 | + |
1061 | + it('must be able to make notification and lists of notifications', |
1062 | + function() { |
1063 | + var note1 = new models.Notification({ |
1064 | + title: 'test1', |
1065 | + message: 'Hello' |
1066 | + }), |
1067 | + note2 = new models.Notification({ |
1068 | + title: 'test2', |
1069 | + message: 'I said goodnight!' |
1070 | + }), |
1071 | + notifications = new models.NotificationList(); |
1072 | + |
1073 | + notifications.add([note1, note2]); |
1074 | + notifications.size().should.equal(2); |
1075 | + |
1076 | + // timestamp should be generated once |
1077 | + var ts = note1.get('timestamp'); |
1078 | + note1.get('timestamp').should.equal(ts); |
1079 | + // force an update so we can test ordering |
1080 | + // fast execution can result in same timestamp |
1081 | + note2.set('timestamp', ts + 1); |
1082 | + note2.get('timestamp').should.be.above(ts); |
1083 | + |
1084 | + // defaults as expected |
1085 | + note1.get('level').should.equal('info'); |
1086 | + note2.get('level').should.equal('info'); |
1087 | + // the sort order on the list should be by |
1088 | + // timestamp |
1089 | + notifications.get('title').should.eql(['test2', 'test1']); |
1090 | + }); |
1091 | + |
1092 | + it('must be able to render its view with sample data', |
1093 | + function() { |
1094 | + var note1 = new models.Notification({ |
1095 | + title: 'test1', message: 'Hello'}), |
1096 | + note2 = new models.Notification({ |
1097 | + title: 'test2', message: 'I said goodnight!'}), |
1098 | + notifications = new models.NotificationList(), |
1099 | + container = Y.Node.create('<div id="test">'), |
1100 | + env = new juju.Environment(), |
1101 | + view = new views.NotificationsView({ |
1102 | + container: container, |
1103 | + notifications: notifications, |
1104 | + env: env}); |
1105 | + view.render(); |
1106 | + // Verify the expected elements appear in the view |
1107 | + container.one('#notify-list').should.not.equal(undefined); |
1108 | + container.destroy(); |
1109 | + }); |
1110 | + |
1111 | + it('must be able to limit the size of notification events', |
1112 | + function() { |
1113 | + var note1 = new models.Notification({ |
1114 | + title: 'test1', |
1115 | + message: 'Hello' |
1116 | + }), |
1117 | + note2 = new models.Notification({ |
1118 | + title: 'test2', |
1119 | + message: 'I said goodnight!' |
1120 | + }), |
1121 | + note3 = new models.Notification({ |
1122 | + title: 'test3', |
1123 | + message: 'Never remember' |
1124 | + }), |
1125 | + notifications = new models.NotificationList({ |
1126 | + max_size: 2 |
1127 | + }); |
1128 | + |
1129 | + notifications.add([note1, note2]); |
1130 | + notifications.size().should.equal(2); |
1131 | + |
1132 | + // Adding a new notification should pop the oldest from the list (we |
1133 | + // exceed max_size) |
1134 | + notifications.add(note3); |
1135 | + notifications.size().should.equal(2); |
1136 | + notifications.get('title').should.eql(['test3', 'test2']); |
1137 | + }); |
1138 | + |
1139 | + it('must be able to get notifications for a given model', |
1140 | + function() { |
1141 | + var m = new models.Service({id: 'mediawiki'}), |
1142 | + note1 = new models.Notification({ |
1143 | + title: 'test1', |
1144 | + message: 'Hello', |
1145 | + modelId: m |
1146 | + }), |
1147 | + note2 = new models.Notification({ |
1148 | + title: 'test2', |
1149 | + message: 'I said goodnight!' |
1150 | + }), |
1151 | + notifications = new models.NotificationList(); |
1152 | + |
1153 | + notifications.add([note1, note2]); |
1154 | + notifications.size().should.equal(2); |
1155 | + notifications.getNotificationsForModel(m).should.eql( |
1156 | + [note1]); |
1157 | + |
1158 | + }); |
1159 | + |
1160 | + it('must be able to include and show object links', function() { |
1161 | + var container = Y.Node.create('<div id="test">'), |
1162 | + conn = new(Y.namespace('juju-tests.utils')).SocketStub(), |
1163 | + env = new juju.Environment({conn: conn}), |
1164 | + app = new Y.juju.App({env: env, container: container}), |
1165 | + db = app.db, |
1166 | + mw = db.services.create({id: 'mediawiki', |
1167 | + name: 'mediawiki'}), |
1168 | + notifications = db.notifications, |
1169 | + view = new views.NotificationsOverview({ |
1170 | + container: container, |
1171 | + notifications: notifications, |
1172 | + app: app, |
1173 | + env: env}).render(); |
1174 | + // we use overview here for testing as it defaults |
1175 | + // to showing all notices |
1176 | + |
1177 | + // we can use app's routing table to derive a link |
1178 | + notifications.create({title: 'Service Down', |
1179 | + message: 'Your service has an error', |
1180 | + link: app.getModelURL(mw) |
1181 | + }); |
1182 | + view.render(); |
1183 | + var link = container.one('.notice').one('a'); |
1184 | + link.getAttribute('href').should.equal( |
1185 | + '/service/mediawiki/'); |
1186 | + link.getHTML().should.contain('View Details'); |
1187 | + |
1188 | + |
1189 | + // create a new notice passing the link_title |
1190 | + notifications.create({title: 'Service Down', |
1191 | + message: 'Your service has an error', |
1192 | + link: app.getModelURL(mw), |
1193 | + link_title: 'Resolve this' |
1194 | + }); |
1195 | + view.render(); |
1196 | + link = container.one('.notice').one('a'); |
1197 | + link.getAttribute('href').should.equal( |
1198 | + '/service/mediawiki/'); |
1199 | + link.getHTML().should.contain('Resolve this'); |
1200 | + }); |
1201 | + |
1202 | + it('must be able to evict irrelevant notices', function() { |
1203 | + var container = Y.Node.create( |
1204 | + '<div id="test" class="container"></div>'), |
1205 | + conn = new(Y.namespace('juju-tests.utils')).SocketStub(), |
1206 | + env = new juju.Environment({conn: conn}), |
1207 | + app = new Y.juju.App({ |
1208 | + env: env, |
1209 | + container: container, |
1210 | + viewContainer: container |
1211 | + }); |
1212 | + var environment_delta = default_env; |
1213 | + |
1214 | + var notifications = app.db.notifications, |
1215 | + view = new views.NotificationsView({ |
1216 | + container: container, |
1217 | + notifications: notifications, |
1218 | + env: app.env}).render(); |
1219 | + |
1220 | + |
1221 | + app.env.dispatch_result(environment_delta); |
1222 | + |
1223 | + |
1224 | + notifications.size().should.equal(7); |
1225 | + // we have one unit in error |
1226 | + view.getShowable().length.should.equal(1); |
1227 | + |
1228 | + // now fire another delta event marking that node as |
1229 | + // started |
1230 | + app.env.dispatch_result({result: [['unit', 'change', { |
1231 | + 'machine': 0, |
1232 | + 'agent-state': 'started', |
1233 | + 'public-address': '192.168.122.222', |
1234 | + 'id': 'mysql/0' |
1235 | + }]], op: 'delta'}); |
1236 | + notifications.size().should.equal(8); |
1237 | + // This should have evicted the prior notice from seen |
1238 | + view.getShowable().length.should.equal(0); |
1239 | + }); |
1240 | + |
1241 | + it('must properly construct title and message based on level from ' + |
1242 | + 'event data', |
1243 | + function() { |
1244 | + var container = Y.Node.create( |
1245 | + '<div id="test" class="container"></div>'), |
1246 | + app = new Y.juju.App({ |
1247 | + container: container, |
1248 | + viewContainer: container |
1249 | + }); |
1250 | + var environment_delta = { |
1251 | + 'result': [ |
1252 | + ['service', 'add', { |
1253 | + 'charm': 'cs:precise/wordpress-6', |
1254 | + 'id': 'wordpress' |
1255 | + }], |
1256 | + ['service', 'add', { |
1257 | + 'charm': 'cs:precise/mediawiki-3', |
1258 | + 'id': 'mediawiki' |
1259 | + }], |
1260 | + ['service', 'add', { |
1261 | + 'charm': 'cs:precise/mysql-6', |
1262 | + 'id': 'mysql' |
1263 | + }], |
1264 | + ['unit', 'add', { |
1265 | + 'agent-state': 'install-error', |
1266 | + 'id': 'wordpress/0' |
1267 | + }], |
1268 | + ['unit', 'add', { |
1269 | + 'agent-state': 'error', |
1270 | + 'public-address': '192.168.122.222', |
1271 | + 'id': 'mysql/0' |
1272 | + }], |
1273 | + ['unit', 'add', { |
1274 | + 'public-address': '192.168.122.222', |
1275 | + 'id': 'mysql/2' |
1276 | + }] |
1277 | + ], |
1278 | + 'op': 'delta' |
1279 | + }; |
1280 | + |
1281 | + var notifications = app.db.notifications, |
1282 | + view = new views.NotificationsView({ |
1283 | + container: container, |
1284 | + notifications: notifications, |
1285 | + app: app, |
1286 | + env: app.env}).render(); |
1287 | + |
1288 | + app.env.dispatch_result(environment_delta); |
1289 | + |
1290 | + notifications.size().should.equal(6); |
1291 | + // we have one unit in error |
1292 | + var showable = view.getShowable(); |
1293 | + showable.length.should.equal(2); |
1294 | + // The first showable notification should indicate an error. |
1295 | + showable[0].level.should.equal('error'); |
1296 | + showable[0].title.should.equal('Error with mysql/0'); |
1297 | + showable[0].message.should.equal('Agent-state = error.'); |
1298 | + // The second showable notification should also indicate an error. |
1299 | + showable[1].level.should.equal('error'); |
1300 | + showable[1].title.should.equal('Error with wordpress/0'); |
1301 | + showable[1].message.should.equal('Agent-state = install-error.'); |
1302 | + // The first non-error notice should have an 'info' level and less |
1303 | + // severe messaging. |
1304 | + var notice = notifications.item(0); |
1305 | + notice.get('level').should.equal('info'); |
1306 | + notice.get('title').should.equal('Problem with mysql/2'); |
1307 | + notice.get('message').should.equal(''); |
1308 | + }); |
1309 | + |
1310 | + |
1311 | + it('should open on click and close on clickoutside', function(done) { |
1312 | + var container = Y.Node.create( |
1313 | + '<div id="test-container" style="display: none" class="container"/>'), |
1314 | + notifications = new models.NotificationList(), |
1315 | + env = new juju.Environment(), |
1316 | + view = new views.NotificationsView({ |
1317 | + container: container, |
1318 | + notifications: notifications, |
1319 | + env: env}).render(), |
1320 | + indicator; |
1321 | + |
1322 | + Y.one('body').append(container); |
1323 | + notifications.add({title: 'testing', 'level': 'error'}); |
1324 | + indicator = container.one('#notify-indicator'); |
1325 | + |
1326 | + indicator.simulate('click'); |
1327 | + indicator.ancestor().hasClass('open').should.equal(true); |
1328 | + |
1329 | + Y.one('body').simulate('click'); |
1330 | + indicator.ancestor().hasClass('open').should.equal(false); |
1331 | + |
1332 | + container.remove(); |
1333 | + done(); |
1334 | + }); |
1335 | + |
1336 | +}); |
1337 | + |
1338 | + |
1339 | +describe('changing notifications to words', function() { |
1340 | + var Y, juju; |
1341 | + |
1342 | + before(function(done) { |
1343 | + Y = YUI(GlobalConfig).use( |
1344 | + ['juju-notification-controller'], |
1345 | + function(Y) { |
1346 | + juju = Y.namespace('juju'); |
1347 | + done(); |
1348 | + }); |
1349 | + }); |
1350 | + |
1351 | + it('should correctly translate notification operations into English', |
1352 | + function() { |
1353 | + assert.equal(juju._changeNotificationOpToWords('add'), 'created'); |
1354 | + assert.equal(juju._changeNotificationOpToWords('remove'), 'removed'); |
1355 | + assert.equal(juju._changeNotificationOpToWords('not-an-op'), 'changed'); |
1356 | + }); |
1357 | +}); |
1358 | + |
1359 | +describe('relation notifications', function() { |
1360 | + var Y, juju; |
1361 | + |
1362 | + before(function(done) { |
1363 | + Y = YUI(GlobalConfig).use( |
1364 | + ['juju-notification-controller'], |
1365 | + function(Y) { |
1366 | + juju = Y.namespace('juju'); |
1367 | + done(); |
1368 | + }); |
1369 | + }); |
1370 | + |
1371 | + it('should produce reasonable titles', function() { |
1372 | + assert.equal( |
1373 | + juju._relationNotifications.title(undefined, 'add'), |
1374 | + 'Relation created'); |
1375 | + assert.equal( |
1376 | + juju._relationNotifications.title(undefined, 'remove'), |
1377 | + 'Relation removed'); |
1378 | + }); |
1379 | + |
1380 | + it('should generate messages about two-party relations', function() { |
1381 | + var changeData = |
1382 | + { endpoints: |
1383 | + [['endpoint0', {name: 'relation0'}], |
1384 | + ['endpoint1', {name: 'relation1'}]]}; |
1385 | + assert.equal( |
1386 | + juju._relationNotifications.message(undefined, 'add', changeData), |
1387 | + 'Relation between endpoint0 (relation type "relation0") and ' + |
1388 | + 'endpoint1 (relation type "relation1") was created'); |
1389 | + }); |
1390 | + |
1391 | + it('should generate messages about one-party relations', function() { |
1392 | + var changeData = |
1393 | + { endpoints: |
1394 | + [['endpoint1', {name: 'relation1'}]]}; |
1395 | + assert.equal( |
1396 | + juju._relationNotifications.message(undefined, 'add', changeData), |
1397 | + 'Relation with endpoint1 (relation type "relation1") was created'); |
1398 | + }); |
1399 | +}); |
1400 | + |
1401 | +describe('notification visual feedback', function() { |
1402 | + var env, models, notifications, notificationsView, notifierBox, views, Y; |
1403 | + |
1404 | + before(function(done) { |
1405 | + Y = YUI(GlobalConfig).use('juju-env', 'juju-models', 'juju-views', |
1406 | + function(Y) { |
1407 | var juju = Y.namespace('juju'); |
1408 | env = new juju.Environment(); |
1409 | models = Y.namespace('juju.models'); |
1410 | views = Y.namespace('juju.views'); |
1411 | - }); |
1412 | - |
1413 | - // Instantiate the notifications model list and view. |
1414 | - // Also create the notifier box and attach it as first element of the |
1415 | - // body. |
1416 | - beforeEach(function() { |
1417 | - notifications = new models.NotificationList(); |
1418 | - notificationsView = new views.NotificationsView({ |
1419 | - env: env, |
1420 | - notifications: notifications |
1421 | - }); |
1422 | - notifierBox = Y.Node.create('<div id="notifier-box"></div>'); |
1423 | - notifierBox.setStyle('display', 'none'); |
1424 | - Y.one('body').prepend(notifierBox); |
1425 | - }); |
1426 | - |
1427 | - // Destroy the notifier box created in beforeEach. |
1428 | - afterEach(function() { |
1429 | - notifierBox.remove(); |
1430 | - notifierBox.destroy(true); |
1431 | - }); |
1432 | - |
1433 | - // Assert the notifier box contains the expectedNumber of notifiers. |
1434 | - var assertNumNotifiers = function(expectedNumber) { |
1435 | - assert.equal(expectedNumber, notifierBox.get('children').size()); |
1436 | - }; |
1437 | - |
1438 | - it('should appear when a new error is notified', function() { |
1439 | - notifications.add({title: 'mytitle', level: 'error'}); |
1440 | - assertNumNotifiers(1); |
1441 | - }); |
1442 | - |
1443 | - it('should only appear when the DOM contains the notifier box', |
1444 | - function() { |
1445 | - notifierBox.remove(); |
1446 | - notifications.add({title: 'mytitle', level: 'error'}); |
1447 | - assertNumNotifiers(0); |
1448 | - }); |
1449 | - |
1450 | - it('should not appear when the notification is not an error', |
1451 | - function() { |
1452 | - notifications.add({title: 'mytitle', level: 'info'}); |
1453 | - assertNumNotifiers(0); |
1454 | - }); |
1455 | - |
1456 | - it('should not appear when the notification comes form delta', |
1457 | - function() { |
1458 | - notifications.add({title: 'mytitle', level: 'error', isDelta: |
1459 | - true}); |
1460 | - assertNumNotifiers(0); |
1461 | - }); |
1462 | - |
1463 | - }); |
1464 | + done(); |
1465 | + }); |
1466 | + }); |
1467 | + |
1468 | + // Instantiate the notifications model list and view. |
1469 | + // Also create the notifier box and attach it as first element of the body. |
1470 | + beforeEach(function() { |
1471 | + notifications = new models.NotificationList(); |
1472 | + notificationsView = new views.NotificationsView({ |
1473 | + env: env, |
1474 | + notifications: notifications |
1475 | }); |
1476 | + notifierBox = Y.Node.create('<div id="notifier-box"></div>'); |
1477 | + notifierBox.setStyle('display', 'none'); |
1478 | + Y.one('body').prepend(notifierBox); |
1479 | + }); |
1480 | + |
1481 | + // Destroy the notifier box created in beforeEach. |
1482 | + afterEach(function() { |
1483 | + notifierBox.remove(); |
1484 | + notifierBox.destroy(true); |
1485 | + }); |
1486 | + |
1487 | + // Assert the notifier box contains the expectedNumber of notifiers. |
1488 | + var assertNumNotifiers = function(expectedNumber) { |
1489 | + assert.equal(expectedNumber, notifierBox.get('children').size()); |
1490 | + }; |
1491 | + |
1492 | + it('should appear when a new error is notified', function() { |
1493 | + notifications.add({title: 'mytitle', level: 'error'}); |
1494 | + assertNumNotifiers(1); |
1495 | + }); |
1496 | + |
1497 | + it('should only appear when the DOM contains the notifier box', function() { |
1498 | + notifierBox.remove(); |
1499 | + notifications.add({title: 'mytitle', level: 'error'}); |
1500 | + assertNumNotifiers(0); |
1501 | + }); |
1502 | + |
1503 | + it('should not appear when the notification is not an error', function() { |
1504 | + notifications.add({title: 'mytitle', level: 'info'}); |
1505 | + assertNumNotifiers(0); |
1506 | + }); |
1507 | + |
1508 | + it('should not appear when the notification comes form delta', function() { |
1509 | + notifications.add({title: 'mytitle', level: 'error', isDelta: true}); |
1510 | + assertNumNotifiers(0); |
1511 | + }); |
1512 | + |
1513 | +}); |
1514 | |
1515 | === modified file 'test/test_notifier_widget.js' |
1516 | --- test/test_notifier_widget.js 2012-11-23 16:21:32 +0000 |
1517 | +++ test/test_notifier_widget.js 2013-01-09 15:35:25 +0000 |
1518 | @@ -1,101 +1,104 @@ |
1519 | 'use strict'; |
1520 | |
1521 | -YUI(GlobalConfig).use(['notifier', 'node-event-simulate'], function(Y) { |
1522 | - describe('notifier widget', function() { |
1523 | - var Notifier, notifierBox; |
1524 | - |
1525 | - before(function() { |
1526 | - Notifier = Y.namespace('juju.widgets').Notifier; |
1527 | - }); |
1528 | - |
1529 | - // Create the notifier box and attach it as first element of the body. |
1530 | - beforeEach(function() { |
1531 | - notifierBox = Y.Node.create('<div id="notifier-box"></div>'); |
1532 | - notifierBox.setStyle('display', 'none'); |
1533 | - Y.one('body').prepend(notifierBox); |
1534 | - }); |
1535 | - |
1536 | - // Destroy the notifier box created in beforeEach. |
1537 | - afterEach(function() { |
1538 | - notifierBox.remove(); |
1539 | - notifierBox.destroy(true); |
1540 | - }); |
1541 | - |
1542 | - // Factory rendering and returning a notifier instance. |
1543 | - var makeNotifier = function(title, message, timeout) { |
1544 | - var notifier = new Notifier({ |
1545 | - title: title || 'mytitle', |
1546 | - message: message || 'mymessage', |
1547 | - timeout: timeout || 10000 |
1548 | - }); |
1549 | - notifier.render(notifierBox); |
1550 | - return notifier; |
1551 | - }; |
1552 | - |
1553 | - // Assert the notifier box contains the expectedNumber of notifiers. |
1554 | - var assertNumNotifiers = function(expectedNumber) { |
1555 | - assert.equal(expectedNumber, notifierBox.get('children').size()); |
1556 | - }; |
1557 | - |
1558 | - it('should be able to display a notification', function() { |
1559 | + |
1560 | +describe('notifier widget', function() { |
1561 | + var Notifier, notifierBox, Y; |
1562 | + |
1563 | + before(function(done) { |
1564 | + Y = YUI(GlobalConfig).use(['notifier', 'node-event-simulate'], function(Y) { |
1565 | + Notifier = Y.namespace('juju.widgets').Notifier; |
1566 | + done(); |
1567 | + }); |
1568 | + }); |
1569 | + |
1570 | + // Create the notifier box and attach it as first element of the body. |
1571 | + beforeEach(function() { |
1572 | + notifierBox = Y.Node.create('<div id="notifier-box"></div>'); |
1573 | + notifierBox.setStyle('display', 'none'); |
1574 | + Y.one('body').prepend(notifierBox); |
1575 | + }); |
1576 | + |
1577 | + // Destroy the notifier box created in beforeEach. |
1578 | + afterEach(function() { |
1579 | + notifierBox.remove(); |
1580 | + notifierBox.destroy(true); |
1581 | + }); |
1582 | + |
1583 | + // Factory rendering and returning a notifier instance. |
1584 | + var makeNotifier = function(title, message, timeout) { |
1585 | + var notifier = new Notifier({ |
1586 | + title: title || 'mytitle', |
1587 | + message: message || 'mymessage', |
1588 | + timeout: timeout || 10000 |
1589 | + }); |
1590 | + notifier.render(notifierBox); |
1591 | + return notifier; |
1592 | + }; |
1593 | + |
1594 | + // Assert the notifier box contains the expectedNumber of notifiers. |
1595 | + var assertNumNotifiers = function(expectedNumber) { |
1596 | + assert.equal(expectedNumber, notifierBox.get('children').size()); |
1597 | + }; |
1598 | + |
1599 | + it('should be able to display a notification', function() { |
1600 | + makeNotifier(); |
1601 | + assertNumNotifiers(1); |
1602 | + }); |
1603 | + |
1604 | + it('should display the given title and message', function() { |
1605 | + makeNotifier('mytitle', 'mymessage'); |
1606 | + var notifierNode = notifierBox.one('*'); |
1607 | + assert.equal('mytitle', notifierNode.one('h3').getContent()); |
1608 | + assert.equal('mymessage', notifierNode.one('div').getContent()); |
1609 | + }); |
1610 | + |
1611 | + it('should be able to display multiple notifications', function() { |
1612 | + var number = 10; |
1613 | + for (var i = 0; i < number; i += 1) { |
1614 | makeNotifier(); |
1615 | + } |
1616 | + assertNumNotifiers(number); |
1617 | + }); |
1618 | + |
1619 | + it('should display new notifications on top', function() { |
1620 | + makeNotifier('mytitle1', 'mymessage1'); |
1621 | + makeNotifier('mytitle2', 'mymessage2'); |
1622 | + var notifierNode = notifierBox.one('*'); |
1623 | + assert.equal('mytitle2', notifierNode.one('h3').getContent()); |
1624 | + assert.equal('mymessage2', notifierNode.one('div').getContent()); |
1625 | + }); |
1626 | + |
1627 | + it('should destroy notifications after N milliseconds', function(done) { |
1628 | + makeNotifier('mytitle', 'mymessage', 1); |
1629 | + // A timeout of 250 milliseconds is used so that we ensure the destroying |
1630 | + // animation can be completed. |
1631 | + setTimeout(function() { |
1632 | + assertNumNotifiers(0); |
1633 | + done(); |
1634 | + }, 250); |
1635 | + }); |
1636 | + |
1637 | + it('should destroy notifications on click', function(done) { |
1638 | + makeNotifier(); |
1639 | + notifierBox.one('*').simulate('click'); |
1640 | + // A timeout of 250 milliseconds is used so that we ensure the destroying |
1641 | + // animation can be completed. |
1642 | + setTimeout(function() { |
1643 | + assertNumNotifiers(0); |
1644 | + done(); |
1645 | + }, 250); |
1646 | + }); |
1647 | + |
1648 | + it('should prevent notification removal on mouse enter', function(done) { |
1649 | + makeNotifier('mytitle', 'mymessage', 1); |
1650 | + notifierBox.one('*').simulate('mouseover'); |
1651 | + // A timeout of 250 milliseconds is used so that we ensure the node is not |
1652 | + // preserved by the destroying animation. |
1653 | + setTimeout(function() { |
1654 | assertNumNotifiers(1); |
1655 | - }); |
1656 | - |
1657 | - it('should display the given title and message', function() { |
1658 | - makeNotifier('mytitle', 'mymessage'); |
1659 | - var notifierNode = notifierBox.one('*'); |
1660 | - assert.equal('mytitle', notifierNode.one('h3').getContent()); |
1661 | - assert.equal('mymessage', notifierNode.one('div').getContent()); |
1662 | - }); |
1663 | - |
1664 | - it('should be able to display multiple notifications', function() { |
1665 | - var number = 10; |
1666 | - for (var i = 0; i < number; i += 1) { |
1667 | - makeNotifier(); |
1668 | - } |
1669 | - assertNumNotifiers(number); |
1670 | - }); |
1671 | - |
1672 | - it('should display new notifications on top', function() { |
1673 | - makeNotifier('mytitle1', 'mymessage1'); |
1674 | - makeNotifier('mytitle2', 'mymessage2'); |
1675 | - var notifierNode = notifierBox.one('*'); |
1676 | - assert.equal('mytitle2', notifierNode.one('h3').getContent()); |
1677 | - assert.equal('mymessage2', notifierNode.one('div').getContent()); |
1678 | - }); |
1679 | - |
1680 | - it('should destroy notifications after N milliseconds', function(done) { |
1681 | - makeNotifier('mytitle', 'mymessage', 1); |
1682 | - // A timeout of 250 milliseconds is used so that we ensure the destroying |
1683 | - // animation can be completed. |
1684 | - setTimeout(function() { |
1685 | - assertNumNotifiers(0); |
1686 | - done(); |
1687 | - }, 250); |
1688 | - }); |
1689 | - |
1690 | - it('should destroy notifications on click', function(done) { |
1691 | - makeNotifier(); |
1692 | - notifierBox.one('*').simulate('click'); |
1693 | - // A timeout of 250 milliseconds is used so that we ensure the destroying |
1694 | - // animation can be completed. |
1695 | - setTimeout(function() { |
1696 | - assertNumNotifiers(0); |
1697 | - done(); |
1698 | - }, 250); |
1699 | - }); |
1700 | - |
1701 | - it('should prevent notification removal on mouse enter', function(done) { |
1702 | - makeNotifier('mytitle', 'mymessage', 1); |
1703 | - notifierBox.one('*').simulate('mouseover'); |
1704 | - // A timeout of 250 milliseconds is used so that we ensure the node is not |
1705 | - // preserved by the destroying animation. |
1706 | - setTimeout(function() { |
1707 | - assertNumNotifiers(1); |
1708 | - done(); |
1709 | - }, 250); |
1710 | - }); |
1711 | - |
1712 | + done(); |
1713 | + }, 250); |
1714 | }); |
1715 | + |
1716 | }); |
1717 | + |
Reviewers: mp+141197_ code.launchpad. net,
Message:
Please take a look.
Description:
Make tests more reliable.
- Disable async loading for yui to ensure app code is loaded before
tests.
- Yank yui loader closures around tests, this is an anti-pattern.
- Any test file can be run in isolation now.
- Re-order test loading into alphabetical order to detect/correct
collisions.
- Enable using firefox for tests via test loader fix.
- WIP enable using phantomjs for running tests on cli.
- Make topology-mega.js more foregiving on its (there's a event
subscription leak here.)
- Fix event subscription leak in charm-panel code.
https:/ /code.launchpad .net/~hazmat/ juju-gui/ reliable- test/+merge/ 141197
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/7003054/
Affected files: debug.js overview. handlebars charm-panel. js environment. js topology/ mega.js topology/ panzoom. js app_hotkeys. js d3_components. js notifications. js notifier_ widget. js
A [revision details]
M app/app.js
M app/modules-
M app/templates/
M app/views/
M app/views/
M app/views/
M app/views/
M app/views/utils.js
M package.json
M test/index.html
M test/test_app.js
M test/test_
M test/test_
M test/test_
M test/test_