github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/webapp/components/wpt-insights.js (about) 1 /** 2 * Copyright 2018 The WPT Dashboard Project. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 */ 6 7 import '../node_modules/@polymer/paper-card/paper-card.js'; 8 import '../node_modules/@polymer/polymer/lib/elements/dom-repeat.js'; 9 import { html, PolymerElement } from '../node_modules/@polymer/polymer/polymer-element.js'; 10 import '../node_modules/@polymer/paper-radio-button/paper-radio-button.js'; 11 import '../node_modules/@polymer/paper-radio-group/paper-radio-group.js'; 12 import './browser-picker.js'; 13 import './channel-picker.js'; 14 import './info-banner.js'; 15 import { AllProducts, DefaultProductSpecs, DefaultBrowserNames, ProductInfo } from './product-info.js'; 16 import { TestStatuses } from './test-info.js'; 17 18 class Insights extends ProductInfo(PolymerElement) { 19 static get template() { 20 return html` 21 <style> 22 info-banner { 23 margin: 0; 24 } 25 wpt-anomalies, wpt-flakes { 26 display: block; 27 } 28 </style> 29 30 <wpt-anomalies></wpt-anomalies> 31 <wpt-flakes></wpt-flakes> 32 <wpt-release-regressions></wpt-release-regressions> 33 `; 34 } 35 36 static get is() { 37 return 'wpt-insights'; 38 } 39 } 40 window.customElements.define(Insights.is, Insights); 41 42 const cardStyle = html` 43 paper-card { 44 display: block; 45 margin-top: 1em; 46 width: 100%; 47 } 48 .query { 49 word-break: break-all; 50 } 51 `; 52 53 class Flakes extends ProductInfo(PolymerElement) { 54 static get is() { 55 return 'wpt-flakes'; 56 } 57 58 static get template() { 59 return html` 60 <style> 61 ${cardStyle} 62 </style> 63 <paper-card> 64 <div class="card-content"> 65 <h3>Flakes</h3> 66 <browser-picker browser="{{browser}}" products="[[allProducts]]"></browser-picker> 67 <info-banner> 68 <a class="query" href="[[url]]">[[query]]</a> 69 </info-banner> 70 <p> 71 Tests that have both passing and non-passing results in the last 10 [[browserDisplayName]] runs 72 </p> 73 </div> 74 </paper-card> 75 `; 76 } 77 78 static get properties() { 79 return { 80 browser: String, 81 browserDisplayName: { 82 type: String, 83 computed: 'displayName(browser)', 84 }, 85 query: { 86 type: String, 87 computed: 'computeQuery()', 88 }, 89 url: { 90 type: URL, 91 computed: 'computeURL(browser, query)', 92 } 93 }; 94 } 95 96 computeQuery() { 97 const passStatuses =Object.values(TestStatuses).filter(s => s.isPass); 98 const passing = passStatuses.map(s => `status:${s}`).join('|'); 99 // Ignore UNKNOWN - that's just a missing test. 100 const notPassing = passStatuses.concat(['unknown']).map(s => `status:!${s}`).join('&'); 101 return `seq((${passing}) (${notPassing})) seq((${notPassing}) (${passing}))`; 102 } 103 104 computeURL(browser, query) { 105 const url = new URL('/results/', window.location); 106 url.searchParams.set('q', query); 107 url.searchParams.set('product', browser); 108 url.searchParams.set('max-count', 10); 109 url.searchParams.set('labels', 'master,experimental'); 110 return url; 111 } 112 } 113 window.customElements.define(Flakes.is, Flakes); 114 115 class Anomalies extends ProductInfo(PolymerElement) { 116 static get is() { 117 return 'wpt-anomalies'; 118 } 119 120 static get template() { 121 return html` 122 <style> 123 ${cardStyle} 124 </style> 125 <paper-card> 126 <div class="card-content"> 127 <h3>Anomalies</h3> 128 <div> 129 <browser-picker browser="{{browser}}" products="[[allProducts]]"></browser-picker> 130 vs 131 <browser-multi-picker products="[[allProductsExcept(browser)]]" selected="{{others}}"></browser-multi-picker> 132 </div> 133 where [[browserDisplayName]] is the only one 134 <paper-radio-group selected="{{anomalyType}}"> 135 <paper-radio-button name="failing">Failing</paper-radio-button> 136 <paper-radio-button name="passing">Passing</paper-radio-button> 137 </paper-radio-group> 138 <info-banner> 139 <a class="query" href="[[url]]">[[query]]</a> 140 </info-banner> 141 <p> 142 Tests that are failing in [[browserDisplayName]], but passing in the other browsers ([[othersDisplayNames]]) 143 </p> 144 </div> 145 </paper-card> 146 `; 147 } 148 149 static get properties() { 150 return { 151 browser: String, 152 browserDisplayName: { 153 type: String, 154 computed: 'displayName(browser)', 155 }, 156 others: { 157 type: Array, 158 value: DefaultBrowserNames.filter(b => b !== 'chrome'), 159 }, 160 othersDisplayNames: { 161 type: String, 162 computed: 'computeOthersDisplayNames(others)', 163 }, 164 anomalyType: { 165 type: String, 166 value: 'failing', 167 }, 168 query: { 169 type: String, 170 computed: 'computeQuery(anomalyType, browser, others)', 171 }, 172 url: { 173 type: URL, 174 computed: 'computeURL(query, browser, others)', 175 } 176 }; 177 } 178 179 allProductsExcept(browser) { 180 return AllProducts.filter(b => b.browser_name !== browser); 181 } 182 183 computeOthersDisplayNames(others) { 184 return others 185 .map(p => this.displayName(p)) 186 .join(', '); 187 } 188 189 computeQuery(anomalyType, browser, others) { 190 const not = anomalyType === 'passing' ? '!' : ''; 191 const notnot = anomalyType === 'passing' ? '' : '!'; 192 const otherFilters = others 193 .map(o => `(${o}:${not}pass|${o}:${not}ok)`) 194 .join(' '); 195 return `(${browser}:${notnot}pass&${browser}:${notnot}ok) ${otherFilters}`; 196 } 197 198 computeURL(query, browser, others) { 199 const url = new URL('/results/', window.location); 200 url.searchParams.set('labels', 'master'); 201 url.searchParams.set('q', query); 202 const products = [browser, ...others]; 203 if (DefaultProductSpecs.join(',') !== products.join(',')) { 204 url.searchParams.set('products', products.join(',')); 205 } 206 return url; 207 } 208 } 209 window.customElements.define(Anomalies.is, Anomalies); 210 211 class ReleaseRegressions extends ProductInfo(PolymerElement) { 212 static get is() { 213 return 'wpt-release-regressions'; 214 } 215 216 static get template() { 217 return html` 218 <style> 219 ${cardStyle} 220 .wrapper { 221 display: flex; 222 align-items: center; 223 } 224 display-logo { 225 margin-left: 16px; 226 margin-right: 16px; 227 } 228 display-logo:first-child { 229 margin-left: 32px; 230 } 231 </style> 232 <paper-card> 233 <div class="card-content"> 234 <h3>Release Regressions</h3> 235 <div class="wrapper"> 236 <browser-picker browser="{{browser}}" products="[[allProducts]]"></browser-picker> 237 <channel-picker browser="[[browser]]" channel="{{channel}}" channels="["beta", "experimental"]"></channel-picker> 238 <display-logo product="[[channelBrowser]]"></display-logo> 239 vs 240 <display-logo product="[[stableBrowser]]"></display-logo> 241 </div> 242 <info-banner> 243 <a class="query" href="[[url]]">[[query]]</a> 244 </info-banner> 245 <p> 246 Tests that are passing in the latest stable [[browserDisplayName]] release, 247 but not passing in the latest [[channel]] run. 248 </p> 249 </div> 250 </paper-card> 251 `; 252 } 253 254 static get properties() { 255 return { 256 browser: String, 257 browserDisplayName: { 258 type: String, 259 computed: 'displayName(browser)', 260 }, 261 channel: { 262 type: String, 263 value: 'beta', 264 }, 265 channelBrowser: { 266 type: Object, 267 computed: 'computeBrowser(browser, channel)' 268 }, 269 stableBrowser: { 270 type: Object, 271 computed: 'computeBrowser(browser, "stable")' 272 }, 273 query: { 274 type: String, 275 computed: 'computeQuery()', 276 }, 277 url: { 278 type: URL, 279 computed: 'computeURL(browser, channel, query)', 280 } 281 }; 282 } 283 284 computeQuery() { 285 const passStatuses = Object.values(TestStatuses).filter(s => s.isPass); 286 const passing = passStatuses.map(s => `status:${s}`).join('|'); 287 // Ignore UNKNOWN - that's just a missing test. 288 const notPassing = passStatuses.concat(['unknown']).map(s => `status:!${s}`).join('&'); 289 return `seq((${passing}) (${notPassing}))`; 290 } 291 292 computeURL(browser, channel, query) { 293 const url = new URL('/results/', window.location); 294 url.searchParams.set('q', query); 295 url.searchParams.set('products', `${browser}[stable],${browser}[${channel}]`); 296 url.searchParams.set('labels', 'master'); 297 url.searchParams.set('diff', 'true'); 298 return url; 299 } 300 301 computeBrowser(browser, channel) { 302 return { 303 browser_name: browser, 304 labels: [channel], 305 }; 306 } 307 } 308 window.customElements.define(ReleaseRegressions.is, ReleaseRegressions); 309 310 export { Insights, Anomalies, Flakes, ReleaseRegressions }; 311