Web Inspector: Provide UIString descriptions to improve localizations
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / AuditTestGroupContentView.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.AuditTestGroupContentView = class AuditTestGroupContentView extends WI.AuditTestContentView
27 {
28     constructor(representedObject)
29     {
30         console.assert(representedObject instanceof WI.AuditTestGroup || representedObject instanceof WI.AuditTestGroupResult);
31
32         super(representedObject);
33
34         this.element.classList.add("audit-test-group");
35         this.element.classList.toggle("contains-test-case", this._subobjects().some((test) => test instanceof WI.AuditTestCase || test instanceof WI.AuditTestCaseResult));
36         this.element.classList.toggle("contains-test-group", this._subobjects().some((test) => test instanceof WI.AuditTestGroup || test instanceof WI.AuditTestGroupResult));
37
38         this._levelScopeBar = null;
39     }
40
41     // Protected
42
43     initialLayout()
44     {
45         super.initialLayout();
46
47         let informationContainer = this.headerView.element.appendChild(document.createElement("div"));
48         informationContainer.classList.add("information");
49
50         let nameElement = informationContainer.appendChild(document.createElement("h1"));
51         nameElement.textContent = this.representedObject.name;
52
53         if (this.representedObject.description) {
54             let descriptionElement = informationContainer.appendChild(document.createElement("p"));
55             descriptionElement.textContent = this.representedObject.description;
56         }
57
58         this._levelNavigationBar = new WI.NavigationBar(document.createElement("nav"));
59         this._levelNavigationBar.element.dataset.prefix = WI.UIString("Showing:");
60         this.headerView.addSubview(this._levelNavigationBar);
61
62         this._percentageContainer = this.headerView.element.appendChild(document.createElement("div"));
63         this._percentageContainer.classList.add("percentage-pass");
64         this._percentageContainer.hidden = true;
65
66         this._percentageTextElement = document.createElement("span");
67
68         const format = WI.UIString("%s%%", "Percentage (of audits)", "The number of tests that passed expressed as a percentage, followed by a literal %.");
69         String.format(format, [this._percentageTextElement], String.standardFormatters, this._percentageContainer, (a, b) => {
70             a.append(b);
71             return a;
72         });
73     }
74
75     layout()
76     {
77         if (this.layoutReason !== WI.View.LayoutReason.Dirty)
78             return;
79
80         super.layout();
81
82         let result = this.representedObject.result;
83         if (!result) {
84             if (this._levelScopeBar) {
85                 this._levelNavigationBar.removeNavigationItem(this._levelScopeBar);
86                 this._levelScopeBar = null;
87             }
88
89             this._percentageContainer.hidden = true;
90             this._percentageTextElement.textContent = "";
91
92             if (this.representedObject.runningState === WI.AuditManager.RunningState.Inactive)
93                 this.showNoResultPlaceholder();
94             else if (this.representedObject.runningState === WI.AuditManager.RunningState.Active)
95                 this.showRunningPlaceholder();
96             else if (this.representedObject.runningState === WI.AuditManager.RunningState.Stopping)
97                 this.showStoppingPlaceholder();
98
99             return;
100         }
101
102         let levelCounts = result.levelCounts;
103         let totalCount = Object.values(levelCounts).reduce((accumulator, current) => accumulator + current);
104         this._percentageTextElement.textContent = Math.floor(100 * levelCounts[WI.AuditTestCaseResult.Level.Pass] / totalCount);
105         this._percentageContainer.hidden = false;
106
107         if (!this._levelScopeBar) {
108             let scopeBarItems = [];
109
110             let addScopeBarItem = (level, labelSingular, labelPlural) => {
111                 let count = levelCounts[level];
112                 if (isNaN(count) || count <= 0)
113                     return;
114
115                 let label = (labelPlural && count !== 1) ? labelPlural : labelSingular;
116                 let scopeBarItem = new WI.ScopeBarItem(level, label.format(count), {
117                     className: level,
118                     exclusive: false,
119                     independent: true,
120                 });
121                 scopeBarItem.selected = true;
122
123                 scopeBarItem.element.insertBefore(document.createElement("img"), scopeBarItem.element.firstChild);
124
125                 scopeBarItems.push(scopeBarItem);
126             };
127
128             addScopeBarItem(WI.AuditTestCaseResult.Level.Pass, WI.UIString("%d Passed", "%d Passed (singular)", ""), WI.UIString("%d Passed", "%d Passed (plural)", ""));
129             addScopeBarItem(WI.AuditTestCaseResult.Level.Warn, WI.UIString("%d Warning"), WI.UIString("%d Warnings"));
130             addScopeBarItem(WI.AuditTestCaseResult.Level.Fail, WI.UIString("%d Failed", "%d Failed (singular)", ""), WI.UIString("%d Failed", "%d Failed (plural)", ""));
131             addScopeBarItem(WI.AuditTestCaseResult.Level.Error, WI.UIString("%d Error"), WI.UIString("%d Errors"));
132             addScopeBarItem(WI.AuditTestCaseResult.Level.Unsupported, WI.UIString("%d Unsupported", "%d Unsupported (singular)", ""), WI.UIString("%d Unsupported", "%d Unsupported (plural)", ""));
133
134             this._levelScopeBar = new WI.ScopeBar(null, scopeBarItems);
135             this._levelScopeBar.addEventListener(WI.ScopeBar.Event.SelectionChanged, this._handleLevelScopeBarSelectionChanged, this);
136             this._levelNavigationBar.addNavigationItem(this._levelScopeBar);
137         }
138
139         if (this.applyFilter())
140             this.hidePlaceholder();
141         else
142             this.showFilteredPlaceholder();
143     }
144
145     shown()
146     {
147         super.shown();
148
149         if (this.representedObject instanceof WI.AuditTestGroup) {
150             this.representedObject.addEventListener(WI.AuditTestBase.Event.Progress, this._handleTestGroupProgress, this);
151             this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestGroupScheduled, this);
152         }
153
154         for (let subobject of this._subobjects()) {
155             if (subobject instanceof WI.AuditTestBase && subobject.disabled)
156                 continue;
157
158             let view = WI.ContentView.contentViewForRepresentedObject(subobject);
159             this.contentView.addSubview(view);
160             view.shown();
161         }
162     }
163
164     hidden()
165     {
166         for (let view of this.contentView.subviews)
167             view.hidden();
168
169         this.contentView.removeAllSubviews();
170
171         super.hidden();
172     }
173
174     applyFilter(levels)
175     {
176         if (this._levelScopeBar && !levels)
177             levels = this._levelScopeBar.selectedItems.map((item) => item.id);
178
179         this._updateLevelScopeBar(levels);
180
181         return super.applyFilter(levels);
182     }
183
184     resetFilter()
185     {
186         this._updateLevelScopeBar(Object.values(WI.AuditTestCaseResult.Level));
187
188         super.resetFilter();
189     }
190
191     showRunningPlaceholder()
192     {
193         if (!this.placeholderElement || !this.placeholderElement.__placeholderRunning) {
194             this.placeholderElement = WI.createMessageTextView(WI.UIString("Running the \u201C%s\u201D audit").format(this.representedObject.name));
195             this.placeholderElement.__placeholderRunning = true;
196
197             this.placeholderElement.__progress = document.createElement("progress");
198             this.placeholderElement.__progress.value = 0;
199             this.placeholderElement.appendChild(this.placeholderElement.__progress);
200         }
201
202         super.showRunningPlaceholder();
203     }
204
205     // Private
206
207     _subobjects()
208     {
209         if (this.representedObject instanceof WI.AuditTestGroup)
210             return this.representedObject.tests;
211
212         if (this.representedObject instanceof WI.AuditTestGroupResult)
213             return this.representedObject.results;
214
215         console.error("Unknown representedObject", this.representedObject);
216         return [];
217     }
218
219     _updateLevelScopeBar(levels)
220     {
221         if (!this._levelScopeBar)
222             return;
223
224         for (let item of this._levelScopeBar.items)
225             item.selected = levels.includes(item.id);
226
227         for (let view of this.contentView.subviews) {
228             if (view instanceof WI.AuditTestGroupContentView)
229                 view._updateLevelScopeBar(levels);
230         }
231     }
232
233     _handleTestGroupProgress(event)
234     {
235         let {index, count} = event.data;
236         if (this.placeholderElement && this.placeholderElement.__progress)
237             this.placeholderElement.__progress.value = (index + 1) / count;
238     }
239
240     _handleTestGroupScheduled(event)
241     {
242         if (this.placeholderElement && this.placeholderElement.__progress)
243             this.placeholderElement.__progress.value = 0;
244     }
245
246     _handleLevelScopeBarSelectionChanged(event)
247     {
248         this.needsLayout();
249     }
250 };