| OLD | NEW |
| 1 'use strict'; | 1 'use strict'; |
| 2 | 2 |
| 3 describe('notifications', function() { | 3 YUI(GlobalConfig).use(['juju-gui', 'node-event-simulate', 'juju-tests-utils'], |
| 4 var Y, juju, models, views; | |
| 5 | |
| 6 var default_env = { | |
| 7 'result': [ | |
| 8 ['service', 'add', { | |
| 9 'charm': 'cs:precise/wordpress-6', | |
| 10 'id': 'wordpress', | |
| 11 'exposed': false | |
| 12 }], | |
| 13 ['service', 'add', { | |
| 14 'charm': 'cs:precise/mediawiki-3', | |
| 15 'id': 'mediawiki', | |
| 16 'exposed': false | |
| 17 }], | |
| 18 ['service', 'add', { | |
| 19 'charm': 'cs:precise/mysql-6', | |
| 20 'id': 'mysql' | |
| 21 }], | |
| 22 ['relation', 'add', { | |
| 23 'interface': 'reversenginx', | |
| 24 'scope': 'global', | |
| 25 'endpoints': | |
| 26 [['wordpress', {'role': 'peer', 'name': 'loadbalancer'}]], | |
| 27 'id': 'relation-0000000000' | |
| 28 }], | |
| 29 ['relation', 'add', { | |
| 30 'interface': 'mysql', | |
| 31 'scope': 'global', | |
| 32 'endpoints': | |
| 33 [['mysql', {'role': 'server', 'name': 'db'}], | |
| 34 ['wordpress', {'role': 'client', 'name': 'db'}]], | |
| 35 'id': 'relation-0000000001' | |
| 36 }], | |
| 37 ['machine', 'add', { | |
| 38 'agent-state': 'running', | |
| 39 'instance-state': 'running', | |
| 40 'id': 0, | |
| 41 'instance-id': 'local', | |
| 42 'dns-name': 'localhost' | |
| 43 }], | |
| 44 ['unit', 'add', { | |
| 45 'machine': 0, | |
| 46 'agent-state': 'started', | |
| 47 'public-address': '192.168.122.113', | |
| 48 'id': 'wordpress/0' | |
| 49 }], | |
| 50 ['unit', 'add', { | |
| 51 'machine': 0, | |
| 52 'agent-state': 'error', | |
| 53 'public-address': '192.168.122.222', | |
| 54 'id': 'mysql/0' | |
| 55 }] | |
| 56 ], | |
| 57 'op': 'delta' | |
| 58 }; | |
| 59 | |
| 60 | |
| 61 before(function(done) { | |
| 62 Y = YUI(GlobalConfig).use([ | |
| 63 'juju-models', | |
| 64 'juju-views', | |
| 65 'juju-gui', | |
| 66 'juju-env', | |
| 67 'node-event-simulate', | |
| 68 'juju-tests-utils'], | |
| 69 | |
| 70 function(Y) { | 4 function(Y) { |
| 71 juju = Y.namespace('juju'); | 5 describe('notifications', function() { |
| 72 models = Y.namespace('juju.models'); | 6 var juju, models, views; |
| 73 views = Y.namespace('juju.views'); | 7 |
| 74 done(); | 8 var default_env = { |
| 75 }); | 9 'result': [ |
| 76 }); | 10 ['service', 'add', { |
| 77 | 11 'charm': 'cs:precise/wordpress-6', |
| 78 it('must be able to make notification and lists of notifications', | 12 'id': 'wordpress', |
| 79 function() { | 13 'exposed': false |
| 80 var note1 = new models.Notification({ | 14 }], |
| 81 title: 'test1', | 15 ['service', 'add', { |
| 82 message: 'Hello' | 16 'charm': 'cs:precise/mediawiki-3', |
| 83 }), | 17 'id': 'mediawiki', |
| 84 note2 = new models.Notification({ | 18 'exposed': false |
| 85 title: 'test2', | 19 }], |
| 86 message: 'I said goodnight!' | 20 ['service', 'add', { |
| 87 }), | 21 'charm': 'cs:precise/mysql-6', |
| 88 notifications = new models.NotificationList(); | 22 'id': 'mysql' |
| 89 | 23 }], |
| 90 notifications.add([note1, note2]); | 24 ['relation', 'add', { |
| 91 notifications.size().should.equal(2); | 25 'interface': 'reversenginx', |
| 92 | 26 'scope': 'global', |
| 93 // timestamp should be generated once | 27 'endpoints': |
| 94 var ts = note1.get('timestamp'); | 28 [['wordpress', {'role': 'peer', 'name': 'loadbalancer'}]], |
| 95 note1.get('timestamp').should.equal(ts); | 29 'id': 'relation-0000000000' |
| 96 // force an update so we can test ordering | 30 }], |
| 97 // fast execution can result in same timestamp | 31 ['relation', 'add', { |
| 98 note2.set('timestamp', ts + 1); | 32 'interface': 'mysql', |
| 99 note2.get('timestamp').should.be.above(ts); | 33 'scope': 'global', |
| 100 | 34 'endpoints': |
| 101 // defaults as expected | 35 [['mysql', {'role': 'server', 'name': 'db'}], |
| 102 note1.get('level').should.equal('info'); | 36 ['wordpress', {'role': 'client', 'name': 'db'}]], |
| 103 note2.get('level').should.equal('info'); | 37 'id': 'relation-0000000001' |
| 104 // the sort order on the list should be by | 38 }], |
| 105 // timestamp | 39 ['machine', 'add', { |
| 106 notifications.get('title').should.eql(['test2', 'test1']); | 40 'agent-state': 'running', |
| 107 }); | 41 'instance-state': 'running', |
| 108 | 42 'id': 0, |
| 109 it('must be able to render its view with sample data', | 43 'instance-id': 'local', |
| 110 function() { | 44 'dns-name': 'localhost' |
| 111 var note1 = new models.Notification({ | 45 }], |
| 112 title: 'test1', message: 'Hello'}), | 46 ['unit', 'add', { |
| 113 note2 = new models.Notification({ | 47 'machine': 0, |
| 114 title: 'test2', message: 'I said goodnight!'}), | 48 'agent-state': 'started', |
| 115 notifications = new models.NotificationList(), | 49 'public-address': '192.168.122.113', |
| 116 container = Y.Node.create('<div id="test">'), | 50 'id': 'wordpress/0' |
| 117 env = new juju.Environment(), | 51 }], |
| 118 view = new views.NotificationsView({ | 52 ['unit', 'add', { |
| 119 container: container, | 53 'machine': 0, |
| 120 notifications: notifications, | 54 'agent-state': 'error', |
| 121 env: env}); | 55 'public-address': '192.168.122.222', |
| 122 view.render(); | 56 'id': 'mysql/0' |
| 123 // Verify the expected elements appear in the view | 57 }] |
| 124 container.one('#notify-list').should.not.equal(undefined); | 58 ], |
| 125 container.destroy(); | 59 'op': 'delta' |
| 126 }); | 60 }; |
| 127 | 61 |
| 128 it('must be able to limit the size of notification events', | 62 |
| 129 function() { | 63 before(function() { |
| 130 var note1 = new models.Notification({ | |
| 131 title: 'test1', | |
| 132 message: 'Hello' | |
| 133 }), | |
| 134 note2 = new models.Notification({ | |
| 135 title: 'test2', | |
| 136 message: 'I said goodnight!' | |
| 137 }), | |
| 138 note3 = new models.Notification({ | |
| 139 title: 'test3', | |
| 140 message: 'Never remember' | |
| 141 }), | |
| 142 notifications = new models.NotificationList({ | |
| 143 max_size: 2 | |
| 144 }); | |
| 145 | |
| 146 notifications.add([note1, note2]); | |
| 147 notifications.size().should.equal(2); | |
| 148 | |
| 149 // Adding a new notification should pop the oldest from the list (we | |
| 150 // exceed max_size) | |
| 151 notifications.add(note3); | |
| 152 notifications.size().should.equal(2); | |
| 153 notifications.get('title').should.eql(['test3', 'test2']); | |
| 154 }); | |
| 155 | |
| 156 it('must be able to get notifications for a given model', | |
| 157 function() { | |
| 158 var m = new models.Service({id: 'mediawiki'}), | |
| 159 note1 = new models.Notification({ | |
| 160 title: 'test1', | |
| 161 message: 'Hello', | |
| 162 modelId: m | |
| 163 }), | |
| 164 note2 = new models.Notification({ | |
| 165 title: 'test2', | |
| 166 message: 'I said goodnight!' | |
| 167 }), | |
| 168 notifications = new models.NotificationList(); | |
| 169 | |
| 170 notifications.add([note1, note2]); | |
| 171 notifications.size().should.equal(2); | |
| 172 notifications.getNotificationsForModel(m).should.eql( | |
| 173 [note1]); | |
| 174 | |
| 175 }); | |
| 176 | |
| 177 it('must be able to include and show object links', function() { | |
| 178 var container = Y.Node.create('<div id="test">'), | |
| 179 conn = new(Y.namespace('juju-tests.utils')).SocketStub(), | |
| 180 env = new juju.Environment({conn: conn}), | |
| 181 app = new Y.juju.App({env: env, container: container}), | |
| 182 db = app.db, | |
| 183 mw = db.services.create({id: 'mediawiki', | |
| 184 name: 'mediawiki'}), | |
| 185 notifications = db.notifications, | |
| 186 view = new views.NotificationsOverview({ | |
| 187 container: container, | |
| 188 notifications: notifications, | |
| 189 app: app, | |
| 190 env: env}).render(); | |
| 191 // we use overview here for testing as it defaults | |
| 192 // to showing all notices | |
| 193 | |
| 194 // we can use app's routing table to derive a link | |
| 195 notifications.create({title: 'Service Down', | |
| 196 message: 'Your service has an error', | |
| 197 link: app.getModelURL(mw) | |
| 198 }); | |
| 199 view.render(); | |
| 200 var link = container.one('.notice').one('a'); | |
| 201 link.getAttribute('href').should.equal( | |
| 202 '/service/mediawiki/'); | |
| 203 link.getHTML().should.contain('View Details'); | |
| 204 | |
| 205 | |
| 206 // create a new notice passing the link_title | |
| 207 notifications.create({title: 'Service Down', | |
| 208 message: 'Your service has an error', | |
| 209 link: app.getModelURL(mw), | |
| 210 link_title: 'Resolve this' | |
| 211 }); | |
| 212 view.render(); | |
| 213 link = container.one('.notice').one('a'); | |
| 214 link.getAttribute('href').should.equal( | |
| 215 '/service/mediawiki/'); | |
| 216 link.getHTML().should.contain('Resolve this'); | |
| 217 }); | |
| 218 | |
| 219 it('must be able to evict irrelevant notices', function() { | |
| 220 var container = Y.Node.create( | |
| 221 '<div id="test" class="container"></div>'), | |
| 222 conn = new(Y.namespace('juju-tests.utils')).SocketStub(), | |
| 223 env = new juju.Environment({conn: conn}), | |
| 224 app = new Y.juju.App({ | |
| 225 env: env, | |
| 226 container: container, | |
| 227 viewContainer: container | |
| 228 }); | |
| 229 var environment_delta = default_env; | |
| 230 | |
| 231 var notifications = app.db.notifications, | |
| 232 view = new views.NotificationsView({ | |
| 233 container: container, | |
| 234 notifications: notifications, | |
| 235 env: app.env}).render(); | |
| 236 | |
| 237 | |
| 238 app.env.dispatch_result(environment_delta); | |
| 239 | |
| 240 | |
| 241 notifications.size().should.equal(7); | |
| 242 // we have one unit in error | |
| 243 view.getShowable().length.should.equal(1); | |
| 244 | |
| 245 // now fire another delta event marking that node as | |
| 246 // started | |
| 247 app.env.dispatch_result({result: [['unit', 'change', { | |
| 248 'machine': 0, | |
| 249 'agent-state': 'started', | |
| 250 'public-address': '192.168.122.222', | |
| 251 'id': 'mysql/0' | |
| 252 }]], op: 'delta'}); | |
| 253 notifications.size().should.equal(8); | |
| 254 // This should have evicted the prior notice from seen | |
| 255 view.getShowable().length.should.equal(0); | |
| 256 }); | |
| 257 | |
| 258 it('must properly construct title and message based on level from ' + | |
| 259 'event data', | |
| 260 function() { | |
| 261 var container = Y.Node.create( | |
| 262 '<div id="test" class="container"></div>'), | |
| 263 app = new Y.juju.App({ | |
| 264 container: container, | |
| 265 viewContainer: container | |
| 266 }); | |
| 267 var environment_delta = { | |
| 268 'result': [ | |
| 269 ['service', 'add', { | |
| 270 'charm': 'cs:precise/wordpress-6', | |
| 271 'id': 'wordpress' | |
| 272 }], | |
| 273 ['service', 'add', { | |
| 274 'charm': 'cs:precise/mediawiki-3', | |
| 275 'id': 'mediawiki' | |
| 276 }], | |
| 277 ['service', 'add', { | |
| 278 'charm': 'cs:precise/mysql-6', | |
| 279 'id': 'mysql' | |
| 280 }], | |
| 281 ['unit', 'add', { | |
| 282 'agent-state': 'install-error', | |
| 283 'id': 'wordpress/0' | |
| 284 }], | |
| 285 ['unit', 'add', { | |
| 286 'agent-state': 'error', | |
| 287 'public-address': '192.168.122.222', | |
| 288 'id': 'mysql/0' | |
| 289 }], | |
| 290 ['unit', 'add', { | |
| 291 'public-address': '192.168.122.222', | |
| 292 'id': 'mysql/2' | |
| 293 }] | |
| 294 ], | |
| 295 'op': 'delta' | |
| 296 }; | |
| 297 | |
| 298 var notifications = app.db.notifications, | |
| 299 view = new views.NotificationsView({ | |
| 300 container: container, | |
| 301 notifications: notifications, | |
| 302 app: app, | |
| 303 env: app.env}).render(); | |
| 304 | |
| 305 app.env.dispatch_result(environment_delta); | |
| 306 | |
| 307 notifications.size().should.equal(6); | |
| 308 // we have one unit in error | |
| 309 var showable = view.getShowable(); | |
| 310 showable.length.should.equal(2); | |
| 311 // The first showable notification should indicate an error. | |
| 312 showable[0].level.should.equal('error'); | |
| 313 showable[0].title.should.equal('Error with mysql/0'); | |
| 314 showable[0].message.should.equal('Agent-state = error.'); | |
| 315 // The second showable notification should also indicate an error. | |
| 316 showable[1].level.should.equal('error'); | |
| 317 showable[1].title.should.equal('Error with wordpress/0'); | |
| 318 showable[1].message.should.equal('Agent-state = install-error.'); | |
| 319 // The first non-error notice should have an 'info' level and less | |
| 320 // severe messaging. | |
| 321 var notice = notifications.item(0); | |
| 322 notice.get('level').should.equal('info'); | |
| 323 notice.get('title').should.equal('Problem with mysql/2'); | |
| 324 notice.get('message').should.equal(''); | |
| 325 }); | |
| 326 | |
| 327 | |
| 328 it('should open on click and close on clickoutside', function(done) { | |
| 329 var container = Y.Node.create( | |
| 330 '<div id="test-container" style="display: none" class="container"/>'), | |
| 331 notifications = new models.NotificationList(), | |
| 332 env = new juju.Environment(), | |
| 333 view = new views.NotificationsView({ | |
| 334 container: container, | |
| 335 notifications: notifications, | |
| 336 env: env}).render(), | |
| 337 indicator; | |
| 338 | |
| 339 Y.one('body').append(container); | |
| 340 notifications.add({title: 'testing', 'level': 'error'}); | |
| 341 indicator = container.one('#notify-indicator'); | |
| 342 | |
| 343 indicator.simulate('click'); | |
| 344 indicator.ancestor().hasClass('open').should.equal(true); | |
| 345 | |
| 346 Y.one('body').simulate('click'); | |
| 347 indicator.ancestor().hasClass('open').should.equal(false); | |
| 348 | |
| 349 container.remove(); | |
| 350 done(); | |
| 351 }); | |
| 352 | |
| 353 }); | |
| 354 | |
| 355 | |
| 356 describe('changing notifications to words', function() { | |
| 357 var Y, juju; | |
| 358 | |
| 359 before(function(done) { | |
| 360 Y = YUI(GlobalConfig).use( | |
| 361 ['juju-notification-controller'], | |
| 362 function(Y) { | |
| 363 juju = Y.namespace('juju'); | 64 juju = Y.namespace('juju'); |
| 65 models = Y.namespace('juju.models'); |
| 66 views = Y.namespace('juju.views'); |
| 67 }); |
| 68 |
| 69 it('must be able to make notification and lists of notifications', |
| 70 function() { |
| 71 var note1 = new models.Notification({ |
| 72 title: 'test1', |
| 73 message: 'Hello' |
| 74 }), |
| 75 note2 = new models.Notification({ |
| 76 title: 'test2', |
| 77 message: 'I said goodnight!' |
| 78 }), |
| 79 notifications = new models.NotificationList(); |
| 80 |
| 81 notifications.add([note1, note2]); |
| 82 notifications.size().should.equal(2); |
| 83 |
| 84 // timestamp should be generated once |
| 85 var ts = note1.get('timestamp'); |
| 86 note1.get('timestamp').should.equal(ts); |
| 87 // force an update so we can test ordering |
| 88 // fast execution can result in same timestamp |
| 89 note2.set('timestamp', ts + 1); |
| 90 note2.get('timestamp').should.be.above(ts); |
| 91 |
| 92 // defaults as expected |
| 93 note1.get('level').should.equal('info'); |
| 94 note2.get('level').should.equal('info'); |
| 95 // the sort order on the list should be by |
| 96 // timestamp |
| 97 notifications.get('title').should.eql(['test2', 'test1']); |
| 98 }); |
| 99 |
| 100 it('must be able to render its view with sample data', |
| 101 function() { |
| 102 var note1 = new models.Notification({ |
| 103 title: 'test1', message: 'Hello'}), |
| 104 note2 = new models.Notification({ |
| 105 title: 'test2', message: 'I said goodnight!'}), |
| 106 notifications = new models.NotificationList(), |
| 107 container = Y.Node.create('<div id="test">'), |
| 108 env = new juju.Environment(), |
| 109 view = new views.NotificationsView({ |
| 110 container: container, |
| 111 notifications: notifications, |
| 112 env: env}); |
| 113 view.render(); |
| 114 // Verify the expected elements appear in the view |
| 115 container.one('#notify-list').should.not.equal(undefined); |
| 116 container.destroy(); |
| 117 }); |
| 118 |
| 119 it('must be able to limit the size of notification events', |
| 120 function() { |
| 121 var note1 = new models.Notification({ |
| 122 title: 'test1', |
| 123 message: 'Hello' |
| 124 }), |
| 125 note2 = new models.Notification({ |
| 126 title: 'test2', |
| 127 message: 'I said goodnight!' |
| 128 }), |
| 129 note3 = new models.Notification({ |
| 130 title: 'test3', |
| 131 message: 'Never remember' |
| 132 }), |
| 133 notifications = new models.NotificationList({ |
| 134 max_size: 2 |
| 135 }); |
| 136 |
| 137 notifications.add([note1, note2]); |
| 138 notifications.size().should.equal(2); |
| 139 |
| 140 // Adding a new notification should pop the oldest from the list |
| 141 // (we exceed max_size) |
| 142 notifications.add(note3); |
| 143 notifications.size().should.equal(2); |
| 144 notifications.get('title').should.eql(['test3', 'test2']); |
| 145 }); |
| 146 |
| 147 it('must be able to get notifications for a given model', |
| 148 function() { |
| 149 var m = new models.Service({id: 'mediawiki'}), |
| 150 note1 = new models.Notification({ |
| 151 title: 'test1', |
| 152 message: 'Hello', |
| 153 modelId: m |
| 154 }), |
| 155 note2 = new models.Notification({ |
| 156 title: 'test2', |
| 157 message: 'I said goodnight!' |
| 158 }), |
| 159 notifications = new models.NotificationList(); |
| 160 |
| 161 notifications.add([note1, note2]); |
| 162 notifications.size().should.equal(2); |
| 163 notifications.getNotificationsForModel(m).should.eql( |
| 164 [note1]); |
| 165 |
| 166 }); |
| 167 |
| 168 it('must be able to include and show object links', function() { |
| 169 var container = Y.Node.create('<div id="test">'), |
| 170 conn = new(Y.namespace('juju-tests.utils')).SocketStub(), |
| 171 env = new juju.Environment({conn: conn}), |
| 172 app = new Y.juju.App({env: env, container: container}), |
| 173 db = app.db, |
| 174 mw = db.services.create({id: 'mediawiki', |
| 175 name: 'mediawiki'}), |
| 176 notifications = db.notifications, |
| 177 view = new views.NotificationsOverview({ |
| 178 container: container, |
| 179 notifications: notifications, |
| 180 app: app, |
| 181 env: env}).render(); |
| 182 // we use overview here for testing as it defaults |
| 183 // to showing all notices |
| 184 |
| 185 // we can use app's routing table to derive a link |
| 186 notifications.create({title: 'Service Down', |
| 187 message: 'Your service has an error', |
| 188 link: app.getModelURL(mw) |
| 189 }); |
| 190 view.render(); |
| 191 var link = container.one('.notice').one('a'); |
| 192 link.getAttribute('href').should.equal( |
| 193 '/service/mediawiki/'); |
| 194 link.getHTML().should.contain('View Details'); |
| 195 |
| 196 |
| 197 // create a new notice passing the link_title |
| 198 notifications.create({title: 'Service Down', |
| 199 message: 'Your service has an error', |
| 200 link: app.getModelURL(mw), |
| 201 link_title: 'Resolve this' |
| 202 }); |
| 203 view.render(); |
| 204 link = container.one('.notice').one('a'); |
| 205 link.getAttribute('href').should.equal( |
| 206 '/service/mediawiki/'); |
| 207 link.getHTML().should.contain('Resolve this'); |
| 208 }); |
| 209 |
| 210 it('must be able to evict irrelevant notices', function() { |
| 211 var container = Y.Node.create( |
| 212 '<div id="test" class="container"></div>'), |
| 213 conn = new(Y.namespace('juju-tests.utils')).SocketStub(), |
| 214 env = new juju.Environment({conn: conn}), |
| 215 app = new Y.juju.App({ |
| 216 env: env, |
| 217 container: container, |
| 218 viewContainer: container |
| 219 }); |
| 220 var environment_delta = default_env; |
| 221 |
| 222 var notifications = app.db.notifications, |
| 223 view = new views.NotificationsView({ |
| 224 container: container, |
| 225 notifications: notifications, |
| 226 env: app.env}).render(); |
| 227 |
| 228 |
| 229 app.env.dispatch_result(environment_delta); |
| 230 |
| 231 |
| 232 notifications.size().should.equal(7); |
| 233 // we have one unit in error |
| 234 view.getShowable().length.should.equal(1); |
| 235 |
| 236 // now fire another delta event marking that node as |
| 237 // started |
| 238 app.env.dispatch_result({result: [['unit', 'change', { |
| 239 'machine': 0, |
| 240 'agent-state': 'started', |
| 241 'public-address': '192.168.122.222', |
| 242 'id': 'mysql/0' |
| 243 }]], op: 'delta'}); |
| 244 notifications.size().should.equal(8); |
| 245 // This should have evicted the prior notice from seen |
| 246 view.getShowable().length.should.equal(0); |
| 247 }); |
| 248 |
| 249 it('must properly construct title and message based on level from ' + |
| 250 'event data', |
| 251 function() { |
| 252 var container = Y.Node.create( |
| 253 '<div id="test" class="container"></div>'), |
| 254 app = new Y.juju.App({ |
| 255 container: container, |
| 256 viewContainer: container |
| 257 }); |
| 258 var environment_delta = { |
| 259 'result': [ |
| 260 ['service', 'add', { |
| 261 'charm': 'cs:precise/wordpress-6', |
| 262 'id': 'wordpress' |
| 263 }], |
| 264 ['service', 'add', { |
| 265 'charm': 'cs:precise/mediawiki-3', |
| 266 'id': 'mediawiki' |
| 267 }], |
| 268 ['service', 'add', { |
| 269 'charm': 'cs:precise/mysql-6', |
| 270 'id': 'mysql' |
| 271 }], |
| 272 ['unit', 'add', { |
| 273 'agent-state': 'install-error', |
| 274 'id': 'wordpress/0' |
| 275 }], |
| 276 ['unit', 'add', { |
| 277 'agent-state': 'error', |
| 278 'public-address': '192.168.122.222', |
| 279 'id': 'mysql/0' |
| 280 }], |
| 281 ['unit', 'add', { |
| 282 'public-address': '192.168.122.222', |
| 283 'id': 'mysql/2' |
| 284 }] |
| 285 ], |
| 286 'op': 'delta' |
| 287 }; |
| 288 |
| 289 var notifications = app.db.notifications, |
| 290 view = new views.NotificationsView({ |
| 291 container: container, |
| 292 notifications: notifications, |
| 293 app: app, |
| 294 env: app.env}).render(); |
| 295 |
| 296 app.env.dispatch_result(environment_delta); |
| 297 |
| 298 notifications.size().should.equal(6); |
| 299 // we have one unit in error |
| 300 var showable = view.getShowable(); |
| 301 showable.length.should.equal(2); |
| 302 // The first showable notification should indicate an error. |
| 303 showable[0].level.should.equal('error'); |
| 304 showable[0].title.should.equal('Error with mysql/0'); |
| 305 showable[0].message.should.equal('Agent-state = error.'); |
| 306 // The second showable notification should also indicate an error. |
| 307 showable[1].level.should.equal('error'); |
| 308 showable[1].title.should.equal('Error with wordpress/0'); |
| 309 showable[1].message.should.equal('Agent-state = install-error.'); |
| 310 // The first non-error notice should have an 'info' level and less |
| 311 // severe messaging. |
| 312 var notice = notifications.item(0); |
| 313 notice.get('level').should.equal('info'); |
| 314 notice.get('title').should.equal('Problem with mysql/2'); |
| 315 notice.get('message').should.equal(''); |
| 316 }); |
| 317 |
| 318 |
| 319 it('should open on click and close on clickoutside', function(done) { |
| 320 var container = Y.Node.create('<div id="test-container" ' + |
| 321 'style="display: none" class="container"/>'), |
| 322 notifications = new models.NotificationList(), |
| 323 env = new juju.Environment(), |
| 324 view = new views.NotificationsView({ |
| 325 container: container, |
| 326 notifications: notifications, |
| 327 env: env}).render(), |
| 328 indicator; |
| 329 |
| 330 Y.one('body').append(container); |
| 331 notifications.add({title: 'testing', 'level': 'error'}); |
| 332 indicator = container.one('#notify-indicator'); |
| 333 |
| 334 indicator.simulate('click'); |
| 335 indicator.ancestor().hasClass('open').should.equal(true); |
| 336 |
| 337 Y.one('body').simulate('click'); |
| 338 indicator.ancestor().hasClass('open').should.equal(false); |
| 339 |
| 340 container.remove(); |
| 364 done(); | 341 done(); |
| 365 }); | 342 }); |
| 366 }); | 343 }); |
| 367 | 344 |
| 368 it('should correctly translate notification operations into English', | 345 describe('changing notifications to words', function() { |
| 369 function() { | 346 var juju; |
| 370 assert.equal(juju._changeNotificationOpToWords('add'), 'created'); | 347 |
| 371 assert.equal(juju._changeNotificationOpToWords('remove'), 'removed'); | 348 before(function() { |
| 372 assert.equal(juju._changeNotificationOpToWords('not-an-op'), 'changed'); | |
| 373 }); | |
| 374 }); | |
| 375 | |
| 376 describe('relation notifications', function() { | |
| 377 var Y, juju; | |
| 378 | |
| 379 before(function(done) { | |
| 380 Y = YUI(GlobalConfig).use( | |
| 381 ['juju-notification-controller'], | |
| 382 function(Y) { | |
| 383 juju = Y.namespace('juju'); | 349 juju = Y.namespace('juju'); |
| 384 done(); | 350 }); |
| 385 }); | 351 |
| 386 }); | 352 it('should correctly translate notification operations into English', |
| 387 | 353 function() { |
| 388 it('should produce reasonable titles', function() { | 354 assert.equal(juju._changeNotificationOpToWords('add'), 'created'); |
| 389 assert.equal( | 355 assert.equal(juju._changeNotificationOpToWords('remove'), |
| 390 juju._relationNotifications.title(undefined, 'add'), | 356 'removed'); |
| 391 'Relation created'); | 357 assert.equal(juju._changeNotificationOpToWords('not-an-op'), |
| 392 assert.equal( | 358 'changed'); |
| 393 juju._relationNotifications.title(undefined, 'remove'), | 359 }); |
| 394 'Relation removed'); | 360 }); |
| 395 }); | 361 |
| 396 | 362 describe('relation notifications', function() { |
| 397 it('should generate messages about two-party relations', function() { | 363 var juju; |
| 398 var changeData = | 364 |
| 399 { endpoints: | 365 before(function() { |
| 400 [['endpoint0', {name: 'relation0'}], | 366 juju = Y.namespace('juju'); |
| 401 ['endpoint1', {name: 'relation1'}]]}; | 367 }); |
| 402 assert.equal( | 368 |
| 403 juju._relationNotifications.message(undefined, 'add', changeData), | 369 it('should produce reasonable titles', function() { |
| 404 'Relation between endpoint0 (relation type "relation0") and ' + | 370 assert.equal( |
| 405 'endpoint1 (relation type "relation1") was created'); | 371 juju._relationNotifications.title(undefined, 'add'), |
| 406 }); | 372 'Relation created'); |
| 407 | 373 assert.equal( |
| 408 it('should generate messages about one-party relations', function() { | 374 juju._relationNotifications.title(undefined, 'remove'), |
| 409 var changeData = | 375 'Relation removed'); |
| 410 { endpoints: | 376 }); |
| 411 [['endpoint1', {name: 'relation1'}]]}; | 377 |
| 412 assert.equal( | 378 it('should generate messages about two-party relations', function() { |
| 413 juju._relationNotifications.message(undefined, 'add', changeData), | 379 var changeData = |
| 414 'Relation with endpoint1 (relation type "relation1") was created'); | 380 { endpoints: |
| 415 }); | 381 [['endpoint0', {name: 'relation0'}], |
| 416 }); | 382 ['endpoint1', {name: 'relation1'}]]}; |
| 417 | 383 assert.equal( |
| 418 describe('notification visual feedback', function() { | 384 juju._relationNotifications.message(undefined, 'add', |
| 419 var env, models, notifications, notificationsView, notifierBox, views, Y; | 385 changeData), 'Relation between endpoint0 (relation type ' + |
| 420 | 386 '"relation0") and endpoint1 (relation type "relation1") ' + |
| 421 before(function(done) { | 387 'was created'); |
| 422 Y = YUI(GlobalConfig).use('juju-env', 'juju-models', 'juju-views', | 388 }); |
| 423 function(Y) { | 389 |
| 390 it('should generate messages about one-party relations', function() { |
| 391 var changeData = |
| 392 { endpoints: |
| 393 [['endpoint1', {name: 'relation1'}]]}; |
| 394 assert.equal( |
| 395 juju._relationNotifications.message(undefined, 'add', |
| 396 changeData), 'Relation with endpoint1 (relation type ' + |
| 397 '"relation1") was created'); |
| 398 }); |
| 399 }); |
| 400 |
| 401 describe('notification visual feedback', function() { |
| 402 var env, models, notifications, notificationsView, notifierBox, views; |
| 403 |
| 404 before(function() { |
| 424 var juju = Y.namespace('juju'); | 405 var juju = Y.namespace('juju'); |
| 425 env = new juju.Environment(); | 406 env = new juju.Environment(); |
| 426 models = Y.namespace('juju.models'); | 407 models = Y.namespace('juju.models'); |
| 427 views = Y.namespace('juju.views'); | 408 views = Y.namespace('juju.views'); |
| 428 done(); | 409 }); |
| 429 }); | 410 |
| 430 }); | 411 // Instantiate the notifications model list and view. |
| 431 | 412 // Also create the notifier box and attach it as first element of the |
| 432 // Instantiate the notifications model list and view. | 413 // body. |
| 433 // Also create the notifier box and attach it as first element of the body. | 414 beforeEach(function() { |
| 434 beforeEach(function() { | 415 notifications = new models.NotificationList(); |
| 435 notifications = new models.NotificationList(); | 416 notificationsView = new views.NotificationsView({ |
| 436 notificationsView = new views.NotificationsView({ | 417 env: env, |
| 437 env: env, | 418 notifications: notifications |
| 438 notifications: notifications | 419 }); |
| 420 notifierBox = Y.Node.create('<div id="notifier-box"></div>'); |
| 421 notifierBox.setStyle('display', 'none'); |
| 422 Y.one('body').prepend(notifierBox); |
| 423 }); |
| 424 |
| 425 // Destroy the notifier box created in beforeEach. |
| 426 afterEach(function() { |
| 427 notifierBox.remove(); |
| 428 notifierBox.destroy(true); |
| 429 }); |
| 430 |
| 431 // Assert the notifier box contains the expectedNumber of notifiers. |
| 432 var assertNumNotifiers = function(expectedNumber) { |
| 433 assert.equal(expectedNumber, notifierBox.get('children').size()); |
| 434 }; |
| 435 |
| 436 it('should appear when a new error is notified', function() { |
| 437 notifications.add({title: 'mytitle', level: 'error'}); |
| 438 assertNumNotifiers(1); |
| 439 }); |
| 440 |
| 441 it('should only appear when the DOM contains the notifier box', |
| 442 function() { |
| 443 notifierBox.remove(); |
| 444 notifications.add({title: 'mytitle', level: 'error'}); |
| 445 assertNumNotifiers(0); |
| 446 }); |
| 447 |
| 448 it('should not appear when the notification is not an error', |
| 449 function() { |
| 450 notifications.add({title: 'mytitle', level: 'info'}); |
| 451 assertNumNotifiers(0); |
| 452 }); |
| 453 |
| 454 it('should not appear when the notification comes form delta', |
| 455 function() { |
| 456 notifications.add({title: 'mytitle', level: 'error', isDelta: |
| 457 true}); |
| 458 assertNumNotifiers(0); |
| 459 }); |
| 460 |
| 461 }); |
| 439 }); | 462 }); |
| 440 notifierBox = Y.Node.create('<div id="notifier-box"></div>'); | |
| 441 notifierBox.setStyle('display', 'none'); | |
| 442 Y.one('body').prepend(notifierBox); | |
| 443 }); | |
| 444 | |
| 445 // Destroy the notifier box created in beforeEach. | |
| 446 afterEach(function() { | |
| 447 notifierBox.remove(); | |
| 448 notifierBox.destroy(true); | |
| 449 }); | |
| 450 | |
| 451 // Assert the notifier box contains the expectedNumber of notifiers. | |
| 452 var assertNumNotifiers = function(expectedNumber) { | |
| 453 assert.equal(expectedNumber, notifierBox.get('children').size()); | |
| 454 }; | |
| 455 | |
| 456 it('should appear when a new error is notified', function() { | |
| 457 notifications.add({title: 'mytitle', level: 'error'}); | |
| 458 assertNumNotifiers(1); | |
| 459 }); | |
| 460 | |
| 461 it('should only appear when the DOM contains the notifier box', function() { | |
| 462 notifierBox.remove(); | |
| 463 notifications.add({title: 'mytitle', level: 'error'}); | |
| 464 assertNumNotifiers(0); | |
| 465 }); | |
| 466 | |
| 467 it('should not appear when the notification is not an error', function() { | |
| 468 notifications.add({title: 'mytitle', level: 'info'}); | |
| 469 assertNumNotifiers(0); | |
| 470 }); | |
| 471 | |
| 472 it('should not appear when the notification comes form delta', function() { | |
| 473 notifications.add({title: 'mytitle', level: 'error', isDelta: true}); | |
| 474 assertNumNotifiers(0); | |
| 475 }); | |
| 476 | |
| 477 }); | |
| OLD | NEW |