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="[&quot;beta&quot;, &quot;experimental&quot;]"></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