Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(220)

Side by Side Diff: lib/html_enhanced_config.dart

Issue 934413002: Replace the existing unittest APIs with the new runner infrastructure. (Closed) Base URL: git@github.com:dart-lang/unittest@master
Patch Set: Rebase Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// A simple unit test library for running tests in a browser.
6 ///
7 /// Provides enhanced HTML output with collapsible group headers
8 /// and other at-a-glance information about the test results.
9 library unittest.html_enhanced_config; 5 library unittest.html_enhanced_config;
kevmoo 2015/02/19 23:13:18 Mark the library as deprecated – its lights up nic
10 6
11 import 'dart:collection' show LinkedHashMap; 7 import 'src/simple_configuration.dart';
12 import 'dart:convert';
13 import 'dart:html';
14 import 'unittest.dart';
15 8
9 /// This is a stub class used to preserve compatibility with unittest 0.11.*.
10 ///
11 /// It will be removed before the next version is released.
12 @deprecated
16 class HtmlEnhancedConfiguration extends SimpleConfiguration { 13 class HtmlEnhancedConfiguration extends SimpleConfiguration {
17 /// Whether this is run within dartium layout tests. 14 HtmlEnhancedConfiguration(bool isLayoutTest);
18 final bool _isLayoutTest;
19 HtmlEnhancedConfiguration(this._isLayoutTest);
20 15
21 var _onErrorSubscription = null; 16 void processMessage(e) {}
22 var _onMessageSubscription = null;
23
24 void _installOnErrorHandler() {
25 if (_onErrorSubscription == null) {
26 // Listen for uncaught errors.
27 _onErrorSubscription = window.onError
28 .listen((e) => handleExternalError(e, '(DOM callback has errors)'));
29 }
30 }
31
32 void _installOnMessageHandler() {
33 if (_onMessageSubscription == null) {
34 // Listen for errors from JS.
35 _onMessageSubscription =
36 window.onMessage.listen((e) => processMessage(e));
37 }
38 }
39
40 void _installHandlers() {
41 _installOnErrorHandler();
42 _installOnMessageHandler();
43 }
44
45 void _uninstallHandlers() {
46 if (_onErrorSubscription != null) {
47 _onErrorSubscription.cancel();
48 _onErrorSubscription = null;
49 }
50 if (_onMessageSubscription != null) {
51 _onMessageSubscription.cancel();
52 _onMessageSubscription = null;
53 }
54 }
55
56 void processMessage(e) {
57 if ('unittest-suite-external-error' == e.data) {
58 handleExternalError('<unknown>', '(external error detected)');
59 }
60 }
61
62 void onInit() {
63 _installHandlers();
64 //initialize and load CSS
65 final String _CSSID = '_unittestcss_';
66
67 var cssElement = document.head.querySelector('#${_CSSID}');
68 if (cssElement == null) {
69 cssElement = new StyleElement();
70 cssElement.id = _CSSID;
71 document.head.append(cssElement);
72 }
73
74 cssElement.text = _htmlTestCSS;
75 window.postMessage('unittest-suite-wait-for-done', '*');
76 }
77
78 void onStart() {
79 // Listen for uncaught errors.
80 _installOnErrorHandler();
81 }
82
83 void onSummary(int passed, int failed, int errors, List<TestCase> results,
84 String uncaughtError) {
85 _showInteractiveResultsInPage(
86 passed, failed, errors, results, _isLayoutTest, uncaughtError);
87 }
88
89 void onDone(bool success) {
90 _uninstallHandlers();
91 window.postMessage('unittest-suite-done', '*');
92 }
93
94 void _showInteractiveResultsInPage(int passed, int failed, int errors,
95 List<TestCase> results, bool isLayoutTest, String uncaughtError) {
96 if (isLayoutTest && passed == results.length) {
97 document.body.innerHtml = "PASS";
98 } else {
99 // changed the StringBuffer to an Element fragment
100 Element te = new Element.html('<div class="unittest-table"></div>');
101
102 te.children.add(new Element.html(passed == results.length
103 ? "<div class='unittest-overall unittest-pass'>PASS</div>"
104 : "<div class='unittest-overall unittest-fail'>FAIL</div>"));
105
106 // moved summary to the top since web browsers
107 // don't auto-scroll to the bottom like consoles typically do.
108 if (passed == results.length && uncaughtError == null) {
109 te.children.add(new Element.html("""
110 <div class='unittest-pass'>All ${passed} tests passed</div>"""));
111 } else {
112 if (uncaughtError != null) {
113 te.children.add(new Element.html("""
114 <div class='unittest-summary'>
115 <span class='unittest-error'>Uncaught error: $uncaughtError</span>
116 </div>"""));
117 }
118
119 te.children.add(new Element.html("""
120 <div class='unittest-summary'>
121 <span class='unittest-pass'>Total ${passed} passed</span>,
122 <span class='unittest-fail'>${failed} failed</span>,
123 <span class='unittest-error'>
124 ${errors + (uncaughtError == null ? 0 : 1)} errors</span>
125 </div>"""));
126 }
127
128 te.children.add(new Element.html("""
129 <div><button id='btnCollapseAll'>Collapse All</button></div>
130 """));
131
132 // handle the click event for the collapse all button
133 te.querySelector('#btnCollapseAll').onClick.listen((_) {
134 document
135 .querySelectorAll('.unittest-row')
136 .forEach((el) => el.attributes['class'] = el.attributes['class']
137 .replaceAll('unittest-row ', 'unittest-row-hidden '));
138 });
139
140 var previousGroup = '';
141 var groupPassFail = true;
142
143 // order by group and sort numerically within each group
144 var groupedBy = new LinkedHashMap<String, List<TestCase>>();
145
146 for (final t in results) {
147 if (!groupedBy.containsKey(t.currentGroup)) {
148 groupedBy[t.currentGroup] = new List<TestCase>();
149 }
150
151 groupedBy[t.currentGroup].add(t);
152 }
153
154 // flatten the list again with tests ordered
155 List<TestCase> flattened = new List<TestCase>();
156
157 groupedBy.values.forEach((tList) {
158 tList.sort((tcA, tcB) => tcA.id - tcB.id);
159 flattened.addAll(tList);
160 });
161
162 var nonAlphanumeric = new RegExp('[^a-z0-9A-Z]');
163
164 // output group headers and test rows
165 for (final test_ in flattened) {
166
167 // replace everything but numbers and letters from the group name with
168 // '_' so we can use in id and class properties.
169 var safeGroup = test_.currentGroup.replaceAll(nonAlphanumeric, '_');
170
171 if (test_.currentGroup != previousGroup) {
172 previousGroup = test_.currentGroup;
173
174 var testsInGroup = results
175 .where((TestCase t) => t.currentGroup == previousGroup)
176 .toList();
177 var groupTotalTestCount = testsInGroup.length;
178 var groupTestPassedCount =
179 testsInGroup.where((TestCase t) => t.result == 'pass').length;
180 groupPassFail = groupTotalTestCount == groupTestPassedCount;
181 var passFailClass = "unittest-group-status unittest-group-"
182 "status-${groupPassFail ? 'pass' : 'fail'}";
183
184 te.children.add(new Element.html("""
185 <div>
186 <div id='${safeGroup}'
187 class='unittest-group ${safeGroup} test${safeGroup}'>
188 <div ${_isIE ? "style='display:inline-block' ": ""}
189 class='unittest-row-status'>
190 <div class='$passFailClass'></div>
191 </div>
192 <div ${_isIE ? "style='display:inline-block' ": ""}>
193 ${test_.currentGroup}</div>
194 &nbsp;
195 <div ${_isIE ? "style='display:inline-block' ": ""}>
196 (${groupTestPassedCount}/${groupTotalTestCount})</div>
197 </div>
198 </div>"""));
199
200 // 'safeGroup' could be empty
201 var grp =
202 (safeGroup == '') ? null : te.querySelector('#${safeGroup}');
203 if (grp != null) {
204 grp.onClick.listen((_) {
205 var row = document.querySelector('.unittest-row-${safeGroup}');
206 if (row.attributes['class'].contains('unittest-row ')) {
207 document.querySelectorAll('.unittest-row-${safeGroup}').forEach(
208 (e) => e.attributes['class'] = e.attributes['class']
209 .replaceAll('unittest-row ', 'unittest-row-hidden '));
210 } else {
211 document.querySelectorAll('.unittest-row-${safeGroup}').forEach(
212 (e) => e.attributes['class'] = e.attributes['class']
213 .replaceAll('unittest-row-hidden', 'unittest-row'));
214 }
215 });
216 }
217 }
218
219 _buildRow(test_, te, safeGroup, !groupPassFail);
220 }
221
222 document.body.children.clear();
223 document.body.children.add(te);
224 }
225 }
226
227 void _buildRow(TestCase test_, Element te, String groupID, bool isVisible) {
228 var background = 'unittest-row-${test_.id % 2 == 0 ? "even" : "odd"}';
229 var display = '${isVisible ? "unittest-row" : "unittest-row-hidden"}';
230
231 addRowElement(id, status, description) {
232 te.children.add(new Element.html(''' <div>
233 <div class='$display unittest-row-${groupID} $background'>
234 <div ${_isIE ? "style='display:inline-block' ": ""}
235 class='unittest-row-id'>$id</div>
236 <div ${_isIE ? "style='display:inline-block' ": ""}
237 class="unittest-row-status unittest-${test_.result}">
238 $status</div>
239 <div ${_isIE ? "style='display:inline-block' ": ""}
240 class='unittest-row-description'>$description</div>
241 </div>
242 </div>'''));
243 }
244
245 if (!test_.isComplete) {
246 addRowElement('${test_.id}', 'NO STATUS', 'Test did not complete.');
247 return;
248 }
249
250 addRowElement('${test_.id}', '${test_.result.toUpperCase()}',
251 '${test_.description}. ${HTML_ESCAPE.convert(test_.message)}');
252
253 if (test_.stackTrace != null) {
254 addRowElement('', '',
255 '<pre>${HTML_ESCAPE.convert(test_.stackTrace.toString())}</pre>');
256 }
257 }
258
259 static bool get _isIE => window.navigator.userAgent.contains('MSIE');
260
261 String get _htmlTestCSS => '''
262 body{
263 font-size: 14px;
264 font-family: 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande','''
265 ''' sans-serif;
266 background: WhiteSmoke;
267 }
268
269 .unittest-group
270 {
271 background: rgb(75,75,75);
272 width:98%;
273 color: WhiteSmoke;
274 font-weight: bold;
275 padding: 6px;
276 cursor: pointer;
277
278 /* Provide some visual separation between groups for IE */
279 ${_isIE ? "border-bottom:solid black 1px;": ""}
280 ${_isIE ? "border-top:solid #777777 1px;": ""}
281
282 background-image: -webkit-linear-gradient(bottom, rgb(50,50,50) 0%, '''
283 '''rgb(100,100,100) 100%);
284 background-image: -moz-linear-gradient(bottom, rgb(50,50,50) 0%, '''
285 '''rgb(100,100,100) 100%);
286 background-image: -ms-linear-gradient(bottom, rgb(50,50,50) 0%, '''
287 '''rgb(100,100,100) 100%);
288 background-image: linear-gradient(bottom, rgb(50,50,50) 0%, '''
289 '''rgb(100,100,100) 100%);
290
291 display: -webkit-box;
292 display: -moz-box;
293 display: -ms-box;
294 display: box;
295
296 -webkit-box-orient: horizontal;
297 -moz-box-orient: horizontal;
298 -ms-box-orient: horizontal;
299 box-orient: horizontal;
300
301 -webkit-box-align: center;
302 -moz-box-align: center;
303 -ms-box-align: center;
304 box-align: center;
305 }
306
307 .unittest-group-status
308 {
309 width: 20px;
310 height: 20px;
311 border-radius: 20px;
312 margin-left: 10px;
313 }
314
315 .unittest-group-status-pass{
316 background: Green;
317 background: '''
318 '''-webkit-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
319 background: '''
320 '''-moz-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
321 background: '''
322 '''-ms-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
323 background: '''
324 '''radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
325 }
326
327 .unittest-group-status-fail{
328 background: Red;
329 background: '''
330 '''-webkit-radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
331 background: '''
332 '''-moz-radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
333 background: '''
334 '''-ms-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
335 background: radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
336 }
337
338 .unittest-overall{
339 font-size: 20px;
340 }
341
342 .unittest-summary{
343 font-size: 18px;
344 }
345
346 .unittest-pass{
347 color: Green;
348 }
349
350 .unittest-fail, .unittest-error
351 {
352 color: Red;
353 }
354
355 .unittest-row
356 {
357 display: -webkit-box;
358 display: -moz-box;
359 display: -ms-box;
360 display: box;
361 -webkit-box-orient: horizontal;
362 -moz-box-orient: horizontal;
363 -ms-box-orient: horizontal;
364 box-orient: horizontal;
365 width: 100%;
366 }
367
368 .unittest-row-hidden
369 {
370 display: none;
371 }
372
373 .unittest-row-odd
374 {
375 background: WhiteSmoke;
376 }
377
378 .unittest-row-even
379 {
380 background: #E5E5E5;
381 }
382
383 .unittest-row-id
384 {
385 width: 3em;
386 }
387
388 .unittest-row-status
389 {
390 width: 4em;
391 }
392
393 .unittest-row-description
394 {
395 }
396
397 ''';
398 } 17 }
399 18
400 void useHtmlEnhancedConfiguration([bool isLayoutTest = false]) { 19 @deprecated
401 unittestConfiguration = isLayoutTest ? _singletonLayout : _singletonNotLayout; 20 void useHtmlEnhancedConfiguration([bool isLayoutTest]) {}
402 }
403
404 final _singletonLayout = new HtmlEnhancedConfiguration(true);
405 final _singletonNotLayout = new HtmlEnhancedConfiguration(false);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698