1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 var ui = ui || {}; 27 ui.notifications = ui.notifications || {}; 28 29 (function(){ 30 31 var kMaxTestsPerGroup = 3; 32 33 ui.notifications.Stream = base.extends('ol', { 34 init: function() 35 { 36 this.className = 'notifications'; 37 }, 38 add: function(notification) 39 { 40 var insertBefore = null; 41 Array.prototype.some.call(this.children, function(existingNotification) { 42 if (existingNotification.index() < notification.index()) { 43 insertBefore = existingNotification; 44 return true; 45 } 46 }); 47 this.insertBefore(notification, insertBefore); 48 return notification; 49 } 50 }); 51 52 ui.notifications.Notification = base.extends('li', { 53 init: function() 54 { 55 this._how = this.appendChild(document.createElement('div')); 56 this._how.className = 'how'; 57 this._what = this.appendChild(document.createElement('div')); 58 this._what.className = 'what'; 59 this._index = 0; 60 $(this).hide().fadeIn('fast'); 61 }, 62 index: function() 63 { 64 return this._index; 65 }, 66 setIndex: function(index) 67 { 68 this._index = index; 69 }, 70 dismiss: function() 71 { 72 // FIXME: These fade in/out effects are lame. 73 $(this).fadeOut(function() 74 { 75 this.parentNode && this.parentNode.removeChild(this); 76 }); 77 }, 78 }); 79 80 ui.notifications.Info = base.extends(ui.notifications.Notification, { 81 init: function(message) 82 { 83 this.update(message); 84 }, 85 update: function(message) 86 { 87 this._what.textContent = message; 88 }, 89 updateWithNode: function(node) 90 { 91 $(this._what).empty(); 92 this._what.appendChild(node); 93 } 94 }); 95 96 ui.notifications.FailingTestGroup = base.extends('li', { 97 init: function(groupName, testNameList) 98 { 99 this.appendChild(base.createLinkNode(ui.urlForFlakinessDashboard(testNameList), groupName, '_blank')); 100 } 101 }); 102 103 var Cause = base.extends('li', { 104 init: function() 105 { 106 this._description = this.appendChild(document.createElement('div')); 107 this._description.className = 'description'; 108 } 109 }); 110 111 ui.notifications.SuspiciousCommit = base.extends(Cause, { 112 init: function(commitData) 113 { 114 this._revision = commitData.revision; 115 this._description.appendChild(base.createLinkNode(trac.changesetURL(commitData.revision), commitData.revision, '_blank')); 116 this._details = this._description.appendChild(document.createElement('span')); 117 this._addDetail('summary', commitData); 118 this._addDetail('author', commitData); 119 this._addDetail('reviewer', commitData); 120 // FIXME: Add bugID detail. 121 // this._addDetail('bugID', commitData, bugzilla.bugURL); 122 }, 123 hasRevision: function(revision) 124 { 125 return this._revision == revision; 126 }, 127 _addDetail: function(part, commitData, linkFunction) 128 { 129 var content = commitData[part]; 130 if (!content) 131 return; 132 133 var span = this._details.appendChild(document.createElement('span')); 134 span.className = part; 135 136 if (linkFunction) { 137 var link = base.createLinkNode(linkFunction(content), content, '_blank'); 138 span.appendChild(link); 139 } else 140 span.textContent = content; 141 } 142 }); 143 144 ui.notifications.Failure = base.extends(ui.notifications.Notification, { 145 init: function() 146 { 147 this._time = this._how.appendChild(new ui.RelativeTime()); 148 this._problem = this._what.appendChild(document.createElement('div')); 149 this._problem.className = 'problem'; 150 this._effects = this._problem.appendChild(document.createElement('ul')); 151 this._effects.className = 'effects'; 152 this._causes = this._what.appendChild(document.createElement('ul')); 153 this._causes.className = 'causes'; 154 }, 155 date: function() 156 { 157 return this._time.date; 158 } 159 }); 160 161 ui.notifications.FailingTests = base.extends(ui.notifications.Failure, { 162 init: function() { 163 // FIXME: Convert actions to a link from test! 164 this._problem.appendChild(new ui.actions.List([ 165 new ui.actions.Examine().makeDefault(), 166 new ui.actions.Rebaseline(), 167 ])); 168 this._testNameList = []; 169 }, 170 testNameList: function() 171 { 172 return this._testNameList; 173 }, 174 containsFailureAnalysis: function(failureAnalysis) 175 { 176 return this._testNameList.indexOf(failureAnalysis.testName) != -1; 177 }, 178 addFailureAnalysis: function(failureAnalysis) 179 { 180 if (this.containsFailureAnalysis(failureAnalysis)) 181 return false; 182 this._testNameList.push(failureAnalysis.testName); 183 $(this._effects).empty(); 184 this._forEachTestGroup(function(groupName, testNameList) { 185 this._effects.appendChild(new ui.notifications.FailingTestGroup(groupName, testNameList)) 186 }.bind(this)); 187 return true; 188 }, 189 _forEachTestGroup: function(callback) 190 { 191 var individualTests = []; 192 base.forEachDirectory(this._testNameList, function(groupLabel, testsInDirectory) { 193 if (testsInDirectory.length <= kMaxTestsPerGroup) { 194 individualTests = individualTests.concat(testsInDirectory); 195 return; 196 } 197 callback(groupLabel, testsInDirectory); 198 }); 199 individualTests.forEach(function(testName) { 200 callback(testName, [testName]); 201 }); 202 } 203 }); 204 205 ui.notifications.FailingTestsSummary = base.extends(ui.notifications.FailingTests, { 206 init: function() { 207 this._where = this._how.appendChild(new ui.failures.FailureGrid()); 208 this._commitDataPinned = false; 209 }, 210 purge: function() { 211 this._where.purge(); 212 }, 213 updateBuilderResults: function(resultNodesByBuilder) 214 { 215 this._where.update(resultNodesByBuilder); 216 }, 217 addFailureAnalysis: function(failureAnalysis) 218 { 219 this.updateBuilderResults(failureAnalysis.resultNodesByBuilder); 220 if (!ui.notifications.FailingTests.prototype.addFailureAnalysis.call(this, failureAnalysis)) 221 return false; 222 }, 223 pinToCommitData: function(commitData) 224 { 225 if (this._commitDataPinned) 226 return; 227 this._commitDataPinned = true; 228 $(this._causes).children().each(function() { 229 if (this.hasRevision(commitData.revision)) 230 return; 231 $(this).detach(); 232 }); 233 }, 234 addCommitData: function(commitData) 235 { 236 if (this._commitDataPinned) 237 return null; 238 var commitDataDate = new Date(commitData.time); 239 if (this._time.date > commitDataDate); { 240 this.setIndex(commitDataDate.getTime()); 241 this._time.setDate(commitDataDate); 242 } 243 return this._causes.appendChild(new ui.notifications.SuspiciousCommit(commitData)); 244 } 245 }); 246 247 ui.notifications.BuildersFailing = base.extends(ui.notifications.Failure, { 248 init: function(message) 249 { 250 this._problem.insertBefore(document.createTextNode(message + ':'), this._problem.firstChild); 251 }, 252 setFailingBuilders: function(failuresList) 253 { 254 $(this._effects).empty().append(Object.keys(failuresList).map(function(builderName) { 255 var effect = document.createElement('li'); 256 effect.className = 'builder'; 257 effect.appendChild(new ui.failures.Builder(builderName, failuresList[builderName])); 258 return effect; 259 })); 260 } 261 }); 262 263 })(); 264