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 suites.map(suite => {
147 return `<div class="section">
149 <div class="title">${suite}</div>
155 notifiyTimelinesRender() {
156 for (let suite in this.children) {
157 this.children[suite].notifiyRerender();
162 let view = new MainView();
163 const onLayoutChange = new EventStream();
164 onLayoutChange.action(() => {
165 view.notifiyTimelinesRender();
168 DOM.inject(document.getElementById('app'), `${ToolTip}
171 for (let suite in view.children) {
172 view.children[suite].update();
175 LimitSlider(() => {view.reload()}),
176 BranchSelector(() => {
180 ConfigurationSelectors(() => {view.reload()}),
181 ], () => onLayoutChange.add())}
182 <div class="main left under-topbar-with-actions">
183 <div class="content">