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 26var ui = ui || {}; 27ui.notifications = ui.notifications || {}; 28 29(function(){ 30 31var kMaxTestsPerGroup = 3; 32 33ui.notifications.Stream = base.extends('ol', { 34 init: function() 35 { 36 this.className = 'notifications'; 37 }, 38 add: function(notification) 39 { 40 this.appendChild(notification); 41 return notification; 42 } 43}); 44 45ui.notifications.Notification = base.extends('li', { 46 init: function() 47 { 48 this._how = this.appendChild(document.createElement('div')); 49 this._how.className = 'how'; 50 this._what = this.appendChild(document.createElement('div')); 51 this._what.className = 'what'; 52 this._index = 0; 53 $(this).hide().fadeIn('fast'); 54 }, 55 dismiss: function() 56 { 57 // FIXME: These fade in/out effects are lame. 58 $(this).fadeOut(function() 59 { 60 this.parentNode && this.parentNode.removeChild(this); 61 }); 62 }, 63}); 64 65ui.notifications.Info = base.extends(ui.notifications.Notification, { 66 init: function(message) 67 { 68 this.update(message); 69 }, 70 update: function(message) 71 { 72 this._what.textContent = message; 73 }, 74 updateWithNode: function(node) 75 { 76 $(this._what).empty(); 77 this._what.appendChild(node); 78 } 79}); 80 81ui.notifications.FailingTestGroup = base.extends('li', { 82 init: function(groupName, testNameList) 83 { 84 this.appendChild(ui.createLinkNode(ui.urlForFlakinessDashboard(testNameList), groupName)); 85 } 86}); 87 88var Cause = base.extends('li', { 89 init: function() 90 { 91 this._description = this.appendChild(document.createElement('div')); 92 this._description.className = 'description'; 93 } 94}); 95 96ui.notifications.SuspiciousCommit = base.extends(Cause, { 97 init: function(commitData) 98 { 99 this._revision = commitData.revision; 100 this._description.appendChild(ui.createLinkNode(trac.changesetURL(commitData.revision), commitData.revision)); 101 this._details = this._description.appendChild(document.createElement('span')); 102 this._addDetail('title', commitData); 103 this._addDetail('author', commitData); 104 this._addDetail('reviewer', commitData); 105 this._addDetail('bugID', commitData, 106 ui.urlForCrbug, 107 function(value) { 108 return value.split(/\s*,\s*/); 109 } 110 ); 111 }, 112 hasRevision: function(revision) 113 { 114 return this._revision == revision; 115 }, 116 _addDetail: function(part, commitData, linkFunction) 117 { 118 var content = commitData[part]; 119 if (!content) 120 return; 121 122 var span = this._details.appendChild(document.createElement('span')); 123 span.className = part; 124 125 if (linkFunction) { 126 var parts = $.isArray(content) ? content : [content]; 127 parts.forEach(function(item, index) { 128 if (index > 0) 129 span.appendChild(document.createTextNode(', ')); 130 var link = ui.createLinkNode(linkFunction(item), item); 131 link.className = part + '-item'; 132 span.appendChild(link); 133 }); 134 } else { 135 span.textContent = content; 136 } 137 } 138}); 139 140ui.notifications.Failure = base.extends(ui.notifications.Notification, { 141 init: function() 142 { 143 this._problem = this._what.appendChild(document.createElement('div')); 144 this._problem.className = 'problem'; 145 this._effects = this._problem.appendChild(document.createElement('ul')); 146 this._effects.className = 'effects'; 147 this._causes = this._what.appendChild(document.createElement('ul')); 148 this._causes.className = 'causes'; 149 }, 150}); 151 152ui.notifications.FailingTests = base.extends(ui.notifications.Failure, { 153 init: function() { 154 // FIXME: Convert actions to a link from test! 155 this._problem.appendChild(new ui.actions.List([ 156 new ui.actions.Examine().makeDefault(), 157 new ui.actions.Rebaseline(), 158 ])); 159 this._testNameList = []; 160 }, 161 testNameList: function() 162 { 163 return this._testNameList; 164 }, 165 containsFailureAnalysis: function(failureAnalysis) 166 { 167 return this._testNameList.indexOf(failureAnalysis.testName) != -1; 168 }, 169 addFailureAnalysis: function(failureAnalysis) 170 { 171 if (this.containsFailureAnalysis(failureAnalysis)) 172 return false; 173 this._testNameList.push(failureAnalysis.testName); 174 $(this._effects).empty(); 175 this._forEachTestGroup(function(groupName, testNameList) { 176 this._effects.appendChild(new ui.notifications.FailingTestGroup(groupName, testNameList)) 177 }.bind(this)); 178 return true; 179 }, 180 _forEachTestGroup: function(callback) 181 { 182 var individualTests = []; 183 base.forEachDirectory(this._testNameList, function(groupLabel, testsInDirectory) { 184 if (testsInDirectory.length <= kMaxTestsPerGroup) { 185 individualTests = individualTests.concat(testsInDirectory); 186 return; 187 } 188 callback(groupLabel, testsInDirectory); 189 }); 190 individualTests.forEach(function(testName) { 191 callback(testName, [testName]); 192 }); 193 } 194}); 195 196ui.notifications.FailingTestsSummary = base.extends(ui.notifications.FailingTests, { 197 init: function() { 198 this._where = this._how.appendChild(new ui.failures.FailureGrid()); 199 this._commitDataPinned = false; 200 }, 201 purge: function() { 202 this._where.purge(); 203 }, 204 updateBuilderResults: function(resultNodesByBuilder) 205 { 206 this._where.update(resultNodesByBuilder); 207 }, 208 addFailureAnalysis: function(failureAnalysis) 209 { 210 this.updateBuilderResults(failureAnalysis.resultNodesByBuilder); 211 if (!ui.notifications.FailingTests.prototype.addFailureAnalysis.call(this, failureAnalysis)) 212 return false; 213 }, 214 pinToCommitData: function(commitData) 215 { 216 if (this._commitDataPinned) 217 return; 218 this._commitDataPinned = true; 219 $(this._causes).children().each(function() { 220 if (this.hasRevision(commitData.revision)) 221 return; 222 $(this).detach(); 223 }); 224 }, 225 addCommitData: function(commitData) 226 { 227 if (this._commitDataPinned) 228 return null; 229 return this._causes.appendChild(new ui.notifications.SuspiciousCommit(commitData)); 230 } 231}); 232 233ui.notifications.BuildersFailing = base.extends(ui.notifications.Failure, { 234 init: function(message) 235 { 236 if (message) 237 this._problem.insertBefore(document.createTextNode(message + ':'), this._problem.firstChild); 238 }, 239 setFailingBuilders: function(failuresList) 240 { 241 $(this._effects).empty().append(Object.keys(failuresList).map(function(builderName) { 242 var effect = document.createElement('li'); 243 effect.className = 'builder'; 244 effect.appendChild(new ui.failures.Builder(builderName, failuresList[builderName])); 245 return effect; 246 })); 247 } 248}); 249 250})(); 251