| OLD | NEW |
| (Empty) |
| 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.results = ui.results || {}; | |
| 28 | |
| 29 (function(){ | |
| 30 | |
| 31 var kResultsPrefetchDelayMS = 500; | |
| 32 | |
| 33 ui.results.ResultsGrid = base.extends('div', { | |
| 34 init: function() | |
| 35 { | |
| 36 this.className = 'results-grid'; | |
| 37 }, | |
| 38 addResults: function(resultsURLs) | |
| 39 { | |
| 40 var resultsURLsByTypeAndKind = {}; | |
| 41 resultsURLs.forEach(function(url) { | |
| 42 var resultType = results.resultType(url); | |
| 43 if (!resultsURLsByTypeAndKind[resultType]) | |
| 44 resultsURLsByTypeAndKind[resultType] = {}; | |
| 45 resultsURLsByTypeAndKind[resultType][results.resultKind(url)] = url; | |
| 46 }); | |
| 47 | |
| 48 Object.keys(resultsURLsByTypeAndKind, function(resultType, resultsURLsBy
Kind) { | |
| 49 if (results.kUnknownKind in resultsURLsByKind) { | |
| 50 // This is something like "crash" that isn't a comparison. | |
| 51 var result = document.createElement('ct-test-output'); | |
| 52 result.url = resultsURLsByKind[results.kUnknownKind]; | |
| 53 result.type = results.kTextType; | |
| 54 this.appendChild(result); | |
| 55 return; | |
| 56 } | |
| 57 | |
| 58 var comparison = document.createElement('ct-results-comparison'); | |
| 59 comparison.type = resultType; | |
| 60 | |
| 61 if (results.kActualKind in resultsURLsByKind) | |
| 62 comparison.actualUrl = resultsURLsByKind[results.kActualKind]; | |
| 63 if (results.kExpectedKind in resultsURLsByKind) | |
| 64 comparison.expectedUrl = resultsURLsByKind[results.kExpectedKind
]; | |
| 65 if (results.kDiffKind in resultsURLsByKind) | |
| 66 comparison.diffUrl = resultsURLsByKind[results.kDiffKind]; | |
| 67 | |
| 68 this.appendChild(comparison); | |
| 69 }.bind(this)); | |
| 70 | |
| 71 if (!this.children.length) | |
| 72 this.textContent = 'No results to display.' | |
| 73 } | |
| 74 }); | |
| 75 | |
| 76 ui.results.ResultsDetails = base.extends('div', { | |
| 77 init: function(delegate, failureInfo) | |
| 78 { | |
| 79 this.className = 'results-detail'; | |
| 80 this._delegate = delegate; | |
| 81 this._failureInfo = failureInfo; | |
| 82 this._haveShownOnce = false; | |
| 83 }, | |
| 84 show: function() { | |
| 85 if (this._haveShownOnce) | |
| 86 return; | |
| 87 this._haveShownOnce = true; | |
| 88 this._delegate.fetchResultsURLs(this._failureInfo).then(function(results
URLs) { | |
| 89 var resultsGrid = new ui.results.ResultsGrid(); | |
| 90 resultsGrid.addResults(resultsURLs); | |
| 91 | |
| 92 this.innerHTML = '' | |
| 93 this.appendChild( | |
| 94 new ui.actions.List([ | |
| 95 new ui.actions.Previous(), | |
| 96 new ui.actions.Next() | |
| 97 ])) | |
| 98 this.appendChild(resultsGrid); | |
| 99 }.bind(this)); | |
| 100 }, | |
| 101 }); | |
| 102 | |
| 103 ui.results.FlakinessData = base.extends('iframe', { | |
| 104 init: function() | |
| 105 { | |
| 106 this.className = 'flakiness-iframe'; | |
| 107 this.src = ui.urlForEmbeddedFlakinessDashboard(); | |
| 108 this.addEventListener('load', function() { | |
| 109 window.addEventListener('message', this._handleMessage.bind(this)); | |
| 110 }); | |
| 111 }, | |
| 112 _handleMessage: function(event) { | |
| 113 if (!this.contentWindow) | |
| 114 return; | |
| 115 | |
| 116 if (event.data.command != 'heightChanged') { | |
| 117 return; | |
| 118 } | |
| 119 | |
| 120 this.style.height = event.data.height + 'px'; | |
| 121 } | |
| 122 }); | |
| 123 | |
| 124 ui.results.TestSelector = base.extends('div', { | |
| 125 init: function(delegate, resultsByTest) | |
| 126 { | |
| 127 this.className = 'test-selector'; | |
| 128 this._delegate = delegate; | |
| 129 | |
| 130 var topPanel = document.createElement('div'); | |
| 131 topPanel.className = 'top-panel'; | |
| 132 this.appendChild(topPanel); | |
| 133 | |
| 134 this._appendResizeHandle(); | |
| 135 | |
| 136 var bottomPanel = document.createElement('div'); | |
| 137 bottomPanel.className = 'bottom-panel'; | |
| 138 this.appendChild(bottomPanel); | |
| 139 | |
| 140 this._flakinessData = new ui.results.FlakinessData(); | |
| 141 this.appendChild(this._flakinessData); | |
| 142 | |
| 143 var testNames = Object.keys(resultsByTest); | |
| 144 testNames.sort().forEach(function(testName) { | |
| 145 var nonLinkTitle = document.createElement('a'); | |
| 146 nonLinkTitle.classList.add('non-link-title'); | |
| 147 nonLinkTitle.textContent = testName; | |
| 148 | |
| 149 var linkTitle = document.createElement('a'); | |
| 150 linkTitle.classList.add('link-title'); | |
| 151 linkTitle.setAttribute('href', ui.urlForFlakinessDashboard([testName
])); | |
| 152 ui.setTargetForLink(linkTitle); | |
| 153 linkTitle.textContent = testName; | |
| 154 | |
| 155 var header = document.createElement('h3'); | |
| 156 header.appendChild(nonLinkTitle); | |
| 157 header.appendChild(linkTitle); | |
| 158 header.addEventListener('click', this._showResults.bind(this, header
, false)); | |
| 159 topPanel.appendChild(header); | |
| 160 }, this); | |
| 161 | |
| 162 // If we have a small amount of content, don't show the resize handler. | |
| 163 // Otherwise, set the minHeight so that the percentage height of the | |
| 164 // topPanel is not too small. | |
| 165 if (testNames.length <= 4) | |
| 166 this.removeChild(this.querySelector('.resize-handle')); | |
| 167 else | |
| 168 topPanel.style.minHeight = '100px'; | |
| 169 }, | |
| 170 _appendResizeHandle: function() | |
| 171 { | |
| 172 var resizeHandle = document.createElement('div'); | |
| 173 resizeHandle.className = 'resize-handle'; | |
| 174 this.appendChild(resizeHandle); | |
| 175 | |
| 176 resizeHandle.addEventListener('mousedown', function(event) { | |
| 177 this._is_resizing = true; | |
| 178 event.preventDefault(); | |
| 179 }.bind(this)); | |
| 180 | |
| 181 var cancelResize = function(event) { this._is_resizing = false; }.bind(t
his); | |
| 182 this.addEventListener('mouseup', cancelResize); | |
| 183 document.body.addEventListener('mouseleave', cancelResize); | |
| 184 | |
| 185 this.addEventListener('mousemove', function(event) { | |
| 186 if (!this._is_resizing) | |
| 187 return; | |
| 188 var mouseY = event.clientY + document.body.scrollTop - this.offsetTo
p; | |
| 189 var percentage = 100 * mouseY / this.offsetHeight; | |
| 190 document.querySelector('.top-panel').style.maxHeight = percentage +
'%'; | |
| 191 }.bind(this)) | |
| 192 }, | |
| 193 _showResults: function(header, scrollInfoView) | |
| 194 { | |
| 195 if (!header) | |
| 196 return false; | |
| 197 | |
| 198 var activeHeader = this.querySelector('.active') | |
| 199 if (activeHeader) | |
| 200 activeHeader.classList.remove('active'); | |
| 201 header.classList.add('active'); | |
| 202 | |
| 203 var testName = this.currentTestName(); | |
| 204 this._flakinessData.src = ui.urlForEmbeddedFlakinessDashboard([testName]
); | |
| 205 | |
| 206 var bottomPanel = this.querySelector('.bottom-panel') | |
| 207 bottomPanel.innerHTML = ''; | |
| 208 bottomPanel.appendChild(this._delegate.contentForTest(testName)); | |
| 209 | |
| 210 var topPanel = this.querySelector('.top-panel'); | |
| 211 if (scrollInfoView) { | |
| 212 topPanel.scrollTop = header.offsetTop; | |
| 213 if (header.offsetTop - topPanel.scrollTop < header.offsetHeight) | |
| 214 topPanel.scrollTop = topPanel.scrollTop - header.offsetHeight; | |
| 215 } | |
| 216 | |
| 217 var resultsDetails = this.querySelectorAll('.results-detail'); | |
| 218 if (resultsDetails.length) | |
| 219 resultsDetails[0].show(); | |
| 220 setTimeout(function() { | |
| 221 Array.prototype.forEach.call(resultsDetails, function(resultsDetail)
{ | |
| 222 resultsDetail.show(); | |
| 223 }); | |
| 224 }, kResultsPrefetchDelayMS); | |
| 225 | |
| 226 return true; | |
| 227 }, | |
| 228 nextResult: function() | |
| 229 { | |
| 230 if (this.querySelector('.builder-selector').nextResult()) | |
| 231 return true; | |
| 232 return this.nextTest(); | |
| 233 }, | |
| 234 previousResult: function() | |
| 235 { | |
| 236 if (this.querySelector('.builder-selector').previousResult()) | |
| 237 return true; | |
| 238 return this.previousTest(); | |
| 239 }, | |
| 240 nextTest: function() | |
| 241 { | |
| 242 return this._showResults(this.querySelector('.active').nextSibling, true
); | |
| 243 }, | |
| 244 previousTest: function() | |
| 245 { | |
| 246 var succeeded = this._showResults(this.querySelector('.active').previous
Sibling, true); | |
| 247 if (succeeded) | |
| 248 this.querySelector('.builder-selector').lastResult(); | |
| 249 return succeeded; | |
| 250 }, | |
| 251 firstResult: function() | |
| 252 { | |
| 253 this._showResults(this.querySelector('h3'), true); | |
| 254 }, | |
| 255 currentTestName: function() | |
| 256 { | |
| 257 return this.querySelector('.active .non-link-title').textContent; | |
| 258 } | |
| 259 }); | |
| 260 | |
| 261 ui.results.BuilderSelector = base.extends('div', { | |
| 262 init: function(delegate, testName, resultsByBuilder) | |
| 263 { | |
| 264 this.className = 'builder-selector'; | |
| 265 this._delegate = delegate; | |
| 266 | |
| 267 var tabStrip = this.appendChild(document.createElement('ul')); | |
| 268 | |
| 269 Object.keys(resultsByBuilder).sort().forEach(function(builderName) { | |
| 270 var builderHash = base.underscoredBuilderName(builderName); | |
| 271 | |
| 272 var link = document.createElement('a'); | |
| 273 link.href = "#" + builderHash; | |
| 274 link.textContent = ui.displayNameForBuilder(builderName); | |
| 275 tabStrip.appendChild(document.createElement('li')).appendChild(link)
; | |
| 276 | |
| 277 var content = this._delegate.contentForTestAndBuilder(testName, buil
derName); | |
| 278 content.id = builderHash; | |
| 279 this.appendChild(content); | |
| 280 }, this); | |
| 281 | |
| 282 $(this).tabs(); | |
| 283 }, | |
| 284 nextResult: function() | |
| 285 { | |
| 286 var nextIndex = $(this).tabs('option', 'selected') + 1; | |
| 287 if (nextIndex >= $(this).tabs('length')) | |
| 288 return false | |
| 289 $(this).tabs('option', 'selected', nextIndex); | |
| 290 return true; | |
| 291 }, | |
| 292 previousResult: function() | |
| 293 { | |
| 294 var previousIndex = $(this).tabs('option', 'selected') - 1; | |
| 295 if (previousIndex < 0) | |
| 296 return false; | |
| 297 $(this).tabs('option', 'selected', previousIndex); | |
| 298 return true; | |
| 299 }, | |
| 300 firstResult: function() | |
| 301 { | |
| 302 $(this).tabs('option', 'selected', 0); | |
| 303 }, | |
| 304 lastResult: function() | |
| 305 { | |
| 306 $(this).tabs('option', 'selected', $(this).tabs('length') - 1); | |
| 307 } | |
| 308 }); | |
| 309 | |
| 310 ui.results.View = base.extends('div', { | |
| 311 init: function(delegate) | |
| 312 { | |
| 313 this.className = 'results-view'; | |
| 314 this._delegate = delegate; | |
| 315 }, | |
| 316 contentForTest: function(testName) | |
| 317 { | |
| 318 var builderSelector = new ui.results.BuilderSelector(this, testName, thi
s._resultsByTest[testName]); | |
| 319 $(builderSelector).bind('tabsselect', function(event, ui) { | |
| 320 // We will probably have pre-fetched the tab already, but we need to
make sure. | |
| 321 ui.panel.show(); | |
| 322 }); | |
| 323 return builderSelector; | |
| 324 }, | |
| 325 contentForTestAndBuilder: function(testName, builderName) | |
| 326 { | |
| 327 var failureInfo = results.failureInfoForTestAndBuilder(this._resultsByTe
st, testName, builderName); | |
| 328 return new ui.results.ResultsDetails(this, failureInfo); | |
| 329 }, | |
| 330 setResultsByTest: function(resultsByTest) | |
| 331 { | |
| 332 this.innerHTML = ''; | |
| 333 this._resultsByTest = resultsByTest; | |
| 334 this._testSelector = new ui.results.TestSelector(this, resultsByTest); | |
| 335 this.appendChild(this._testSelector); | |
| 336 }, | |
| 337 fetchResultsURLs: function(failureInfo) | |
| 338 { | |
| 339 return this._delegate.fetchResultsURLs(failureInfo); | |
| 340 }, | |
| 341 nextResult: function() | |
| 342 { | |
| 343 return this._testSelector.nextResult(); | |
| 344 }, | |
| 345 previousResult: function() | |
| 346 { | |
| 347 return this._testSelector.previousResult(); | |
| 348 }, | |
| 349 nextTest: function() | |
| 350 { | |
| 351 return this._testSelector.nextTest(); | |
| 352 }, | |
| 353 previousTest: function() | |
| 354 { | |
| 355 return this._testSelector.previousTest(); | |
| 356 }, | |
| 357 firstResult: function() | |
| 358 { | |
| 359 this._testSelector.firstResult() | |
| 360 }, | |
| 361 currentTestName: function() | |
| 362 { | |
| 363 return this._testSelector.currentTestName() | |
| 364 } | |
| 365 }); | |
| 366 | |
| 367 })(); | |
| OLD | NEW |