2 Copyright (C) 2019 Apple Inc. All rights reserved.
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions
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.
13 THIS SOFTWARE IS PROVIDED BY APPLE INC. "AS IS" AND ANY
14 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 {% extends "base.html" %}
28 <link rel="stylesheet" type="text/css" href="assets/css/search.css">
29 <link rel="stylesheet" type="text/css" href="assets/css/timeline.css">
30 <link rel="stylesheet" type="text/css" href="assets/css/tooltip.css">
32 <script type="module">
33 import {CommitBank} from '/assets/js/commit.js';
34 import {deepCompare, ErrorDisplay, queryToParams, paramsToQuery} from '/assets/js/common.js';
35 import {Configuration} from '/assets/js/configuration.js';
36 import {Drawer, BranchSelector, ConfigurationSelectors, LimitSlider} from '/assets/js/drawer.js';
37 import {Legend, TimelineFromEndpoint} from '/assets/js/timeline.js';
38 import {ToolTip} from '/assets/js/tooltip.js';
39 import {DOM, REF, EventStream} from '/library/js/Ref.js';
41 const DEFAULT_LIMIT = 100;
42 const SUITES = JSON.parse('{{ suites|safe }}');
46 this.currentParams = queryToParams(document.URL.split('?')[1]);
47 this.currentLimit = this.currentParams.limit ? parseInt(this.currentParams.limit[this.currentParams.limit.length - 1]) : DEFAULT_LIMIT;
48 delete this.currentParams.limit;
50 this.ref = REF.createRef({
52 onStateUpdate: (element, state) => {
54 element.innerHTML = this.placeholder();
56 element.innerHTML = ErrorDisplay(state);
57 else if (state.length === 0)
58 element.innerHTML = ErrorDisplay({
60 description: 'No suites matching the specified criteria',
63 DOM.inject(element, this.render(state));
66 this.latestDispatch = Date.now();
69 SUITES.forEach((suite) => {
70 this.children[suite] = new TimelineFromEndpoint('api/results/' + suite, suite);
73 unpackSuites(suitesJson) {
77 let suites = new Set();
78 suitesJson.forEach((pair) => {
79 pair[1].forEach((suite) => {
84 let sortedSuites = [...suites].sort();
85 Object.keys(this.children).forEach(key => {this.children[key].teardown();});
87 sortedSuites.forEach((suite) => {
88 this.children[suite] = new TimelineFromEndpoint('api/results/' + suite);
93 let params = queryToParams(document.URL.split('?')[1]);
94 let limit = params.limit ? parseInt(params.limit[params.limit.length - 1]) : DEFAULT_LIMIT;
97 if (deepCompare(params, this.currentParams)) {
98 if (limit === this.currentLimit)
101 for (let suite in this.children) {
102 if (limit > this.currentLimit)
103 this.children[suite].reload();
105 this.children[suite].rerender();
107 this.currentLimit = limit;
111 let myDispatch = Date.now();
112 this.currentParams = params;
113 this.currentLimit = limit;
114 this.latestDispatch = Math.max(this.latestDispatch, myDispatch);
115 this.ref.setState(null);
118 let suiteParams = {};
119 Configuration.members().forEach(member => {
121 suiteParams[member] = params[member];
123 let query = paramsToQuery(suiteParams);
124 fetch(query ? 'api/suites?' + query: 'api/suites').then(response => {
125 response.json().then(json => {
126 if (myDispatch === this.latestDispatch)
127 self.ref.setState(this.unpackSuites(json));
130 if (myDispatch === self.latestDispatch)
131 self.ref.setState({error: "Connection Error", description: error});
135 return `<div class="placeholder dimmer">
137 <div class="spinner"></div>
142 return `<div ref="${this.ref}"></div>`;
145 let children = this.children;
146 return Legend(() => {
147 for (let suite in children) {
148 children[suite].update();
150 }, true) + suites.map(suite => {
151 return `<div class="section">
153 <div class="title">${suite}</div>
159 notifiyTimelinesRender() {
160 for (let suite in this.children) {
161 this.children[suite].notifiyRerender();
166 let view = new MainView();
167 const onLayoutChange = new EventStream();
168 onLayoutChange.action(() => {
169 view.notifiyTimelinesRender();
172 DOM.inject(document.getElementById('app'), `${ToolTip}
174 LimitSlider(() => {view.reload()}),
175 BranchSelector(() => {
179 ConfigurationSelectors(() => {view.reload()}),
180 ], () => onLayoutChange.add())}
181 <div class="main left under-topbar-with-actions">
182 <div class="content">