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

Side by Side Diff: pkg/unittest/html_enhanced_config.dart

Issue 11301046: Restructure pkg/unittest and pkg/webdriver to follow the pub conventions. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
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.
4
5 /**
6 * A simple unit test library for running tests in a browser.
7 *
8 * Provides enhanced HTML output with collapsible group headers
9 * and other at-a-glance information about the test results.
10 */
11 #library('unittest_html_enhanced_config');
12
13 #import('dart:html');
14 #import('unittest.dart');
15
16
17 class HtmlEnhancedConfiguration extends Configuration {
18 /** Whether this is run within dartium layout tests. */
19 final bool _isLayoutTest;
20 HtmlEnhancedConfiguration(this._isLayoutTest);
21
22 // TODO(rnystrom): Get rid of this if we get canonical closures for methods.
23 EventListener _onErrorClosure;
24 EventListener _onMessageClosure;
25
26 void _installHandlers() {
27 if (_onErrorClosure == null) {
28 _onErrorClosure =
29 (e) => handleExternalError(e, '(DOM callback has errors)');
30 // Listen for uncaught errors.
31 window.on.error.add(_onErrorClosure);
32 }
33 if (_onMessageClosure == null) {
34 _onMessageClosure = (e) => processMessage(e);
35 // Listen for errors from JS.
36 window.on.message.add(_onMessageClosure);
37 }
38 }
39
40 void _uninstallHandlers() {
41 if (_onErrorClosure != null) {
42 window.on.error.remove(_onErrorClosure);
43 _onErrorClosure = null;
44 }
45 if (_onMessageClosure != null) {
46 window.on.message.remove(_onMessageClosure);
47 _onMessageClosure = null;
48 }
49 }
50
51 void processMessage(e) {
52 if ('unittest-suite-external-error' == e.data) {
53 handleExternalError('<unknown>', '(external error detected)');
54 }
55 }
56
57 void onInit() {
58 _installHandlers();
59 //initialize and load CSS
60 final String _CSSID = '_unittestcss_';
61
62 var cssElement = document.head.query('#${_CSSID}');
63 if (cssElement == null){
64 document.head.elements.add(new Element.html(
65 '<style id="${_CSSID}"></style>'));
66 cssElement = document.head.query('#${_CSSID}');
67 }
68
69 cssElement.innerHTML = _htmlTestCSS;
70 }
71
72 void onStart() {
73 window.postMessage('unittest-suite-wait-for-done', '*');
74 // Listen for uncaught errors.
75 window.on.error.add(_onErrorClosure);
76 }
77
78 void onTestResult(TestCase testCase) {}
79
80 void onDone(int passed, int failed, int errors, List<TestCase> results,
81 String uncaughtError) {
82 _uninstallHandlers();
83
84 _showInteractiveResultsInPage(passed, failed, errors, results,
85 _isLayoutTest, uncaughtError);
86
87 window.postMessage('unittest-suite-done', '*');
88 }
89
90 void _showInteractiveResultsInPage(int passed, int failed, int errors,
91 List<TestCase> results, bool isLayoutTest, String uncaughtError) {
92 if (isLayoutTest && passed == results.length) {
93 document.body.innerHTML = "PASS";
94 } else {
95 // changed the StringBuffer to an Element fragment
96 Element te = new Element.html('<div class="unittest-table"></div>');
97
98 te.elements.add(new Element.html(passed == results.length
99 ? "<div class='unittest-overall unittest-pass'>PASS</div>"
100 : "<div class='unittest-overall unittest-fail'>FAIL</div>"));
101
102 // moved summary to the top since web browsers
103 // don't auto-scroll to the bottom like consoles typically do.
104 if (passed == results.length && uncaughtError == null) {
105 te.elements.add(new Element.html("""
106 <div class='unittest-pass'>All ${passed} tests passed</div>"""));
107 } else {
108
109 if (uncaughtError != null) {
110 te.elements.add(new Element.html("""
111 <div class='unittest-summary'>
112 <span class='unittest-error'>Uncaught error: $uncaughtError</span>
113 </div>"""));
114 }
115
116 te.elements.add(new Element.html("""
117 <div class='unittest-summary'>
118 <span class='unittest-pass'>Total ${passed} passed</span>,
119 <span class='unittest-fail'>${failed} failed</span>,
120 <span class='unittest-error'>
121 ${errors + (uncaughtError == null ? 0 : 1)} errors</span>
122 </div>"""));
123 }
124
125 te.elements.add(new Element.html("""
126 <div><button id='btnCollapseAll'>Collapse All</button></div>
127 """));
128
129 // handle the click event for the collapse all button
130 te.query('#btnCollapseAll').on.click.add((_){
131 document
132 .queryAll('.unittest-row')
133 .forEach((el) => el.attributes['class'] = el.attributes['class']
134 .replaceAll('unittest-row ', 'unittest-row-hidden '));
135 });
136
137 var previousGroup = '';
138 var groupPassFail = true;
139 final indentAmount = 50;
140
141 // order by group and sort numerically within each group
142 var groupedBy = new LinkedHashMap<String, List<TestCase>>();
143
144 for (final t in results){
145 if (!groupedBy.containsKey(t.currentGroup)){
146 groupedBy[t.currentGroup] = new List<TestCase>();
147 }
148
149 groupedBy[t.currentGroup].add(t);
150 }
151
152 // flatten the list again with tests ordered
153 List<TestCase> flattened = new List<TestCase>();
154
155 groupedBy
156 .values
157 .forEach((tList){
158 tList.sort((tcA, tcB) => tcA.id - tcB.id);
159 flattened.addAll(tList);
160 }
161 );
162
163 var nonAlphanumeric = new RegExp('[^a-z0-9A-Z]');
164
165 // output group headers and test rows
166 for (final test_ in flattened) {
167
168 // replace everything but numbers and letters from the group name with
169 // '_' so we can use in id and class properties.
170 var safeGroup = test_.currentGroup.replaceAll(nonAlphanumeric,'_');
171
172 if (test_.currentGroup != previousGroup){
173
174 previousGroup = test_.currentGroup;
175
176 var testsInGroup = results.filter(
177 (TestCase t) => t.currentGroup == previousGroup);
178 var groupTotalTestCount = testsInGroup.length;
179 var groupTestPassedCount = testsInGroup.filter(
180 (TestCase t) => t.result == 'pass').length;
181 groupPassFail = groupTotalTestCount == groupTestPassedCount;
182 var passFailClass = "unittest-group-status unittest-group-"
183 "status-${groupPassFail ? 'pass' : 'fail'}";
184
185 te.elements.add(new Element.html("""
186 <div>
187 <div id='${safeGroup}'
188 class='unittest-group ${safeGroup} test${safeGroup}'>
189 <div ${_isIE ? "style='display:inline-block' ": ""}
190 class='unittest-row-status'>
191 <div class='$passFailClass'></div>
192 </div>
193 <div ${_isIE ? "style='display:inline-block' ": ""}>
194 ${test_.currentGroup}</div>
195 &nbsp;
196 <div ${_isIE ? "style='display:inline-block' ": ""}>
197 (${groupTestPassedCount}/${groupTotalTestCount})</div>
198 </div>
199 </div>"""));
200
201 // 'safeGroup' could be empty
202 var grp = (safeGroup == '') ? null : te.query('#${safeGroup}');
203 if (grp != null){
204 grp.on.click.add((_){
205 var row = document.query('.unittest-row-${safeGroup}');
206 if (row.attributes['class'].contains('unittest-row ')){
207 document.queryAll('.unittest-row-${safeGroup}').forEach(
208 (e) => e.attributes['class'] = e.attributes['class']
209 .replaceAll('unittest-row ', 'unittest-row-hidden '));
210 }else{
211 document.queryAll('.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.elements.clear();
223 document.body.elements.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 // TODO (prujohn@gmail.com) I had to borrow this from html_print.dart
232 // Probably should put it in some more common location.
233 String _htmlEscape(String string) {
234 return string.replaceAll('&', '&amp;')
235 .replaceAll('<','&lt;')
236 .replaceAll('>','&gt;');
237 }
238
239 addRowElement(id, status, description){
240 te.elements.add(
241 new Element.html(
242 ''' <div>
243 <div class='$display unittest-row-${groupID} $background'>
244 <div ${_isIE ? "style='display:inline-block' ": ""}
245 class='unittest-row-id'>$id</div>
246 <div ${_isIE ? "style='display:inline-block' ": ""}
247 class="unittest-row-status unittest-${test_.result}">
248 $status</div>
249 <div ${_isIE ? "style='display:inline-block' ": ""}
250 class='unittest-row-description'>$description</div>
251 </div>
252 </div>'''
253 )
254 );
255 }
256
257 if (!test_.isComplete) {
258 addRowElement('${test_.id}', 'NO STATUS', 'Test did not complete.');
259 return;
260 }
261
262 addRowElement('${test_.id}', '${test_.result.toUpperCase()}',
263 '${test_.description}. ${_htmlEscape(test_.message)}');
264
265 if (test_.stackTrace != null) {
266 addRowElement('', '', '<pre>${_htmlEscape(test_.stackTrace)}</pre>');
267 }
268 }
269
270
271 static bool get _isIE => document.window.navigator.userAgent.contains('MSIE');
272
273 String get _htmlTestCSS =>
274 '''
275 body{
276 font-size: 14px;
277 font-family: 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande','''
278 ''' sans-serif;
279 background: WhiteSmoke;
280 }
281
282 .unittest-group
283 {
284 background: rgb(75,75,75);
285 width:98%;
286 color: WhiteSmoke;
287 font-weight: bold;
288 padding: 6px;
289 cursor: pointer;
290
291 /* Provide some visual separation between groups for IE */
292 ${_isIE ? "border-bottom:solid black 1px;": ""}
293 ${_isIE ? "border-top:solid #777777 1px;": ""}
294
295 background-image: -webkit-linear-gradient(bottom, rgb(50,50,50) 0%, '''
296 '''rgb(100,100,100) 100%);
297 background-image: -moz-linear-gradient(bottom, rgb(50,50,50) 0%, '''
298 '''rgb(100,100,100) 100%);
299 background-image: -ms-linear-gradient(bottom, rgb(50,50,50) 0%, '''
300 '''rgb(100,100,100) 100%);
301 background-image: linear-gradient(bottom, rgb(50,50,50) 0%, '''
302 '''rgb(100,100,100) 100%);
303
304 display: -webkit-box;
305 display: -moz-box;
306 display: -ms-box;
307 display: box;
308
309 -webkit-box-orient: horizontal;
310 -moz-box-orient: horizontal;
311 -ms-box-orient: horizontal;
312 box-orient: horizontal;
313
314 -webkit-box-align: center;
315 -moz-box-align: center;
316 -ms-box-align: center;
317 box-align: center;
318 }
319
320 .unittest-group-status
321 {
322 width: 20px;
323 height: 20px;
324 border-radius: 20px;
325 margin-left: 10px;
326 }
327
328 .unittest-group-status-pass{
329 background: Green;
330 background: '''
331 '''-webkit-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
332 background: '''
333 '''-moz-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
334 background: '''
335 '''-ms-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
336 background: '''
337 '''radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
338 }
339
340 .unittest-group-status-fail{
341 background: Red;
342 background: '''
343 '''-webkit-radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
344 background: '''
345 '''-moz-radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
346 background: '''
347 '''-ms-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
348 background: radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
349 }
350
351 .unittest-overall{
352 font-size: 20px;
353 }
354
355 .unittest-summary{
356 font-size: 18px;
357 }
358
359 .unittest-pass{
360 color: Green;
361 }
362
363 .unittest-fail, .unittest-error
364 {
365 color: Red;
366 }
367
368 .unittest-row
369 {
370 display: -webkit-box;
371 display: -moz-box;
372 display: -ms-box;
373 display: box;
374 -webkit-box-orient: horizontal;
375 -moz-box-orient: horizontal;
376 -ms-box-orient: horizontal;
377 box-orient: horizontal;
378 width: 100%;
379 }
380
381 .unittest-row-hidden
382 {
383 display: none;
384 }
385
386 .unittest-row-odd
387 {
388 background: WhiteSmoke;
389 }
390
391 .unittest-row-even
392 {
393 background: #E5E5E5;
394 }
395
396 .unittest-row-id
397 {
398 width: 3em;
399 }
400
401 .unittest-row-status
402 {
403 width: 4em;
404 }
405
406 .unittest-row-description
407 {
408 }
409
410 ''';
411 }
412
413 void useHtmlEnhancedConfiguration([bool isLayoutTest = false]) {
414 configure(new HtmlEnhancedConfiguration(isLayoutTest));
415 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698