cd28ade014ea01b54fccd203fde33d2f9b97098a
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / AuditTreeElement.js
1 /*
2  * Copyright (C) 2018 Apple 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 WI.AuditTreeElement = class AuditTreeElement extends WI.GeneralTreeElement
27 {
28     constructor(representedObject)
29     {
30         let isTestCase = representedObject instanceof WI.AuditTestCase;
31         let isTestGroup = representedObject instanceof WI.AuditTestGroup;
32         let isTestCaseResult = representedObject instanceof WI.AuditTestCaseResult;
33         let isTestGroupResult = representedObject instanceof WI.AuditTestGroupResult;
34         console.assert(isTestCase || isTestGroup || isTestCaseResult || isTestGroupResult);
35
36         let classNames = ["audit"];
37         if (isTestCase)
38             classNames.push("test-case");
39         else if (isTestGroup)
40             classNames.push("test-group");
41         else if (isTestCaseResult)
42             classNames.push("test-case-result");
43         else if (isTestGroupResult)
44             classNames.push("test-group-result");
45
46         let options = {
47             hasChildren: isTestGroup || isTestGroupResult,
48         };
49
50         const subtitle = null;
51         super(classNames, representedObject.name, subtitle, representedObject, options);
52
53         if (isTestGroup)
54             this._expandedSetting = new WI.Setting(`audit-tree-element-${this.representedObject.name}-expanded`, false);
55     }
56
57     // Protected
58
59     onattach()
60     {
61         super.onattach();
62
63         if (this.representedObject instanceof WI.AuditTestBase) {
64             this.representedObject.addEventListener(WI.AuditTestBase.Event.ResultCleared, this._handleTestResultCleared, this);
65
66             if (this.representedObject instanceof WI.AuditTestCase)
67                 this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestCaseScheduled, this);
68             else if (this.representedObject instanceof WI.AuditTestGroup)
69                 this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestGroupScheduled, this);
70
71             WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
72             WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
73         }
74
75         if (this._expandedSetting && this._expandedSetting.value)
76             this.expand();
77
78         this._updateLevel();
79     }
80
81     ondetach()
82     {
83         WI.auditManager.removeEventListener(null, null, this);
84         this.representedObject.removeEventListener(null, null, this);
85
86         super.ondetach();
87     }
88
89     onpopulate()
90     {
91         super.onpopulate();
92
93         if (this.children.length && !this.shouldRefreshChildren)
94             return;
95
96         this.shouldRefreshChildren = false;
97
98         this.removeChildren();
99
100         if (this.representedObject instanceof WI.AuditTestGroup) {
101             for (let test of this.representedObject.tests)
102                 this.appendChild(new WI.AuditTreeElement(test));
103         } else if (this.representedObject instanceof WI.AuditTestGroupResult) {
104             for (let result of this.representedObject.results)
105                 this.appendChild(new WI.AuditTreeElement(result));
106         }
107     }
108
109     onexpand()
110     {
111         console.assert(this.expanded);
112
113         if (this._expandedSetting)
114             this._expandedSetting.value = this.expanded;
115     }
116
117     oncollapse()
118     {
119         console.assert(!this.expanded);
120
121         if (this._expandedSetting)
122             this._expandedSetting.value = this.expanded;
123     }
124
125     ondelete()
126     {
127         if (!(this.representedObject instanceof WI.AuditTestBase))
128             return false;
129
130         if (!(this.parent instanceof WI.TreeOutline))
131             return false;
132
133         WI.auditManager.removeTest(this.representedObject);
134
135         return true;
136     }
137
138     populateContextMenu(contextMenu, event)
139     {
140         if (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive) {
141             contextMenu.appendItem(WI.UIString("Start"), (event) => {
142                 this._start();
143             });
144         }
145
146         contextMenu.appendSeparator();
147
148         if (this.representedObject instanceof WI.AuditTestCase || this.representedObject instanceof WI.AuditTestGroup) {
149             contextMenu.appendItem(WI.UIString("Export Test"), (event) => {
150                 WI.auditManager.export(this.representedObject);
151             });
152         }
153
154         if (this.representedObject.result) {
155             contextMenu.appendItem(WI.UIString("Export Result"), (event) => {
156                 WI.auditManager.export(this.representedObject.result);
157             });
158         }
159
160         contextMenu.appendSeparator();
161
162         super.populateContextMenu(contextMenu, event);
163     }
164
165     // Private
166
167     _start()
168     {
169         if (WI.auditManager.runningState !== WI.AuditManager.RunningState.Inactive)
170             return;
171
172         WI.auditManager.start([this.representedObject]);
173     }
174
175     _updateLevel()
176     {
177         let className = "";
178
179         let result = this.representedObject.result;
180         if (result) {
181             if (result.didError)
182                 className = WI.AuditTestCaseResult.Level.Error;
183             else if (result.didFail)
184                 className = WI.AuditTestCaseResult.Level.Fail;
185             else if (result.didWarn)
186                 className = WI.AuditTestCaseResult.Level.Warn;
187             else if (result.didPass)
188                 className = WI.AuditTestCaseResult.Level.Pass;
189             else if (result.unsupported)
190                 className = WI.AuditTestCaseResult.Level.Unsupported;
191         }
192
193         this.status = document.createElement("img");
194
195         if (this.representedObject instanceof WI.AuditTestCase || this.representedObject instanceof WI.AuditTestGroup) {
196             this.status.title = WI.UIString("Start");
197             this.status.addEventListener("click", this._handleStatusClick.bind(this));
198
199             if (!className)
200                 className = "show-on-hover";
201         }
202
203         this.status.classList.add(className);
204     }
205
206     _showRunningSpinner()
207     {
208         if (this.representedObject.runningState === WI.AuditManager.RunningState.Inactive) {
209             this._updateLevel();
210             return;
211         }
212
213         if (!this.status || !this.status.__spinner) {
214             let spinner = new WI.IndeterminateProgressSpinner;
215             this.status = spinner.element;
216             this.status.__spinner = true;
217         }
218     }
219
220     _showRunningProgress(progress)
221     {
222         if (!this.representedObject.runningState === WI.AuditManager.RunningState.Inactive) {
223             this._updateLevel();
224             return;
225         }
226
227         if (!this.status || !this.status.__progress) {
228             this.status = document.createElement("progress");
229             this.status.__progress = true;
230         }
231
232         this.status.value = progress || 0;
233     }
234
235     _handleTestCaseCompleted(event)
236     {
237         this.representedObject.removeEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCaseCompleted, this);
238
239         this._updateLevel();
240     }
241
242     _handleTestResultCleared(event)
243     {
244         this._updateLevel();
245     }
246
247     _handleTestCaseScheduled(event)
248     {
249         this.representedObject.addEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCaseCompleted, this);
250
251         this._showRunningSpinner();
252     }
253
254     _handleTestGroupCompleted(event)
255     {
256         this.representedObject.removeEventListener(WI.AuditTestBase.Event.Completed, this._handleTestGroupCompleted, this);
257         this.representedObject.removeEventListener(WI.AuditTestBase.Event.Progress, this._handleTestGroupProgress, this);
258
259         this._updateLevel();
260     }
261
262     _handleTestGroupProgress(event)
263     {
264         let {index, count} = event.data;
265         this._showRunningProgress((index + 1) / count);
266     }
267
268     _handleTestGroupScheduled(event)
269     {
270         this.representedObject.addEventListener(WI.AuditTestBase.Event.Completed, this._handleTestGroupCompleted, this);
271         this.representedObject.addEventListener(WI.AuditTestBase.Event.Progress, this._handleTestGroupProgress, this);
272
273         this._showRunningProgress();
274     }
275
276     _handleAuditManagerTestScheduled(event)
277     {
278         this.addClassName("manager-active");
279     }
280
281     _handleAuditManagerTestCompleted(event)
282     {
283         this.removeClassName("manager-active");
284     }
285
286     _handleStatusClick(event)
287     {
288         this._start();
289     }
290 };