Web Inspector: Audit: add plural result strings
[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._percentageTextElement = this.headerView.element.appendChild(document.createElement("div"));
63         this._percentageTextElement.classList.add("percentage-pass");
64         this.headerView.element.appendChild(this._percentageTextElement);
65     }
66
67     layout()
68     {
69         if (this.layoutReason !== WI.View.LayoutReason.Dirty)
70             return;
71
72         super.layout();
73
74         let result = this.representedObject.result;
75         if (!result) {
76             if (this._levelScopeBar) {
77                 this._levelNavigationBar.removeNavigationItem(this._levelScopeBar);
78                 this._levelScopeBar = null;
79             }
80
81             this._percentageTextElement.textContent = "";
82
83             if (this.representedObject.runningState === WI.AuditManager.RunningState.Inactive)
84                 this.showNoResultPlaceholder();
85             else if (this.representedObject.runningState === WI.AuditManager.RunningState.Active)
86                 this.showRunningPlaceholder();
87             else if (this.representedObject.runningState === WI.AuditManager.RunningState.Stopping)
88                 this.showStoppingPlaceholder();
89
90             return;
91         }
92
93         let levelCounts = result.levelCounts;
94         let totalCount = Object.values(levelCounts).reduce((accumulator, current) => accumulator + current);
95         this._percentageTextElement.textContent = Math.floor(100 * levelCounts[WI.AuditTestCaseResult.Level.Pass] / totalCount);
96
97         if (!this._levelScopeBar) {
98             let scopeBarItems = [];
99
100             let addScopeBarItem = (level, labelSingular, labelPlural) => {
101                 let count = levelCounts[level];
102                 if (isNaN(count) || count <= 0)
103                     return;
104
105                 let label = (labelPlural && count !== 1) ? labelPlural : labelSingular;
106                 let scopeBarItem = new WI.ScopeBarItem(level, label.format(count), {
107                     className: level,
108                     exclusive: false,
109                     independent: true,
110                 });
111                 scopeBarItem.selected = true;
112                 scopeBarItems.push(scopeBarItem);
113             };
114
115             addScopeBarItem(WI.AuditTestCaseResult.Level.Pass, WI.UIString("%d Passed"));
116             addScopeBarItem(WI.AuditTestCaseResult.Level.Warn, WI.UIString("%d Warning"), WI.UIString("%d Warnings"));
117             addScopeBarItem(WI.AuditTestCaseResult.Level.Fail, WI.UIString("%d Failed"));
118             addScopeBarItem(WI.AuditTestCaseResult.Level.Error, WI.UIString("%d Error"), WI.UIString("%d Errors"));
119             addScopeBarItem(WI.AuditTestCaseResult.Level.Unsupported, WI.UIString("%d Unsupported"));
120
121             this._levelScopeBar = new WI.ScopeBar(null, scopeBarItems);
122             this._levelScopeBar.addEventListener(WI.ScopeBar.Event.SelectionChanged, this._handleLevelScopeBarSelectionChanged, this);
123             this._levelNavigationBar.addNavigationItem(this._levelScopeBar);
124         }
125
126         if (this.applyFilter())
127             this.hidePlaceholder();
128         else
129             this.showFilteredPlaceholder();
130     }
131
132     shown()
133     {
134         super.shown();
135
136         if (this.representedObject instanceof WI.AuditTestGroup) {
137             this.representedObject.addEventListener(WI.AuditTestBase.Event.Progress, this._handleTestGroupProgress, this);
138             this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestGroupScheduled, this);
139         }
140
141         for (let subobject of this._subobjects()) {
142             let view = WI.ContentView.contentViewForRepresentedObject(subobject);
143             this.contentView.addSubview(view);
144             view.shown();
145         }
146     }
147
148     hidden()
149     {
150         for (let view of this.contentView.subviews)
151             view.hidden();
152
153         this.contentView.removeAllSubviews();
154
155         super.hidden();
156     }
157
158     applyFilter(levels)
159     {
160         if (this._levelScopeBar && !levels)
161             levels = this._levelScopeBar.selectedItems.map((item) => item.id);
162
163         this._updateLevelScopeBar(levels);
164
165         return super.applyFilter(levels);
166     }
167
168     resetFilter()
169     {
170         this._updateLevelScopeBar(Object.values(WI.AuditTestCaseResult.Level));
171
172         super.resetFilter();
173     }
174
175     showRunningPlaceholder()
176     {
177         if (!this.placeholderElement || !this.placeholderElement.__placeholderRunning) {
178             this.placeholderElement = WI.createMessageTextView(WI.UIString("Running the \u201C%s\u201D audit").format(this.representedObject.name));
179             this.placeholderElement.__placeholderRunning = true;
180
181             this.placeholderElement.__progress = document.createElement("progress");
182             this.placeholderElement.__progress.value = 0;
183             this.placeholderElement.appendChild(this.placeholderElement.__progress);
184         }
185
186         super.showRunningPlaceholder();
187     }
188
189     // Private
190
191     _subobjects()
192     {
193         if (this.representedObject instanceof WI.AuditTestGroup)
194             return this.representedObject.tests;
195
196         if (this.representedObject instanceof WI.AuditTestGroupResult)
197             return this.representedObject.results;
198
199         console.error("Unknown representedObject", this.representedObject);
200         return [];
201     }
202
203     _updateLevelScopeBar(levels)
204     {
205         if (!this._levelScopeBar)
206             return;
207
208         for (let item of this._levelScopeBar.items)
209             item.selected = levels.includes(item.id);
210
211         for (let view of this.contentView.subviews) {
212             if (view instanceof WI.AuditTestGroupContentView)
213                 view._updateLevelScopeBar(levels);
214         }
215     }
216
217     _handleTestGroupProgress(event)
218     {
219         let {index, count} = event.data;
220         if (this.placeholderElement && this.placeholderElement.__progress)
221             this.placeholderElement.__progress.value = (index + 1) / count;
222     }
223
224     _handleTestGroupScheduled(event)
225     {
226         if (this.placeholderElement && this.placeholderElement.__progress)
227             this.placeholderElement.__progress.value = 0;
228     }
229
230     _handleLevelScopeBarSelectionChanged(event)
231     {
232         this.needsLayout();
233     }
234 };