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

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

Issue 12210093: Fix HTML enhanced config. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 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 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 /** 5 /**
6 * A simple unit test library for running tests in a browser. 6 * A simple unit test library for running tests in a browser.
7 * 7 *
8 * Provides enhanced HTML output with collapsible group headers 8 * Provides enhanced HTML output with collapsible group headers
9 * and other at-a-glance information about the test results. 9 * and other at-a-glance information about the test results.
10 */ 10 */
11 library unittest_html_enhanced_config; 11 library unittest_html_enhanced_config;
12 12
13 import 'dart:async';
13 import 'dart:collection' show LinkedHashMap; 14 import 'dart:collection' show LinkedHashMap;
14 import 'dart:html'; 15 import 'dart:html';
15 import 'unittest.dart'; 16 import 'unittest.dart';
16 17
17 class HtmlEnhancedConfiguration extends Configuration { 18 class HtmlEnhancedConfiguration extends Configuration {
18 /** Whether this is run within dartium layout tests. */ 19 /** Whether this is run within dartium layout tests. */
19 final bool _isLayoutTest; 20 final bool _isLayoutTest;
20 HtmlEnhancedConfiguration(this._isLayoutTest); 21 HtmlEnhancedConfiguration(this._isLayoutTest);
21 22
22 // TODO(rnystrom): Get rid of this if we get canonical closures for methods. 23 var _onErrorSubscription = null;
23 EventListener _onErrorClosure; 24 var _onMessageSubscription = null;
24 EventListener _onMessageClosure;
25 25
26 void _installHandlers() { 26 void _installOnErrorHandler() {
27 if (_onErrorClosure == null) { 27 if (_onErrorSubscription == null) {
28 _onErrorClosure =
29 (e) => handleExternalError(e, '(DOM callback has errors)');
30 // Listen for uncaught errors. 28 // Listen for uncaught errors.
31 window.on.error.add(_onErrorClosure); 29 _onErrorSubscription = window.onError.listen(
32 } 30 (e) => handleExternalError(e, '(DOM callback has errors)'));
33 if (_onMessageClosure == null) {
34 _onMessageClosure = (e) => processMessage(e);
35 // Listen for errors from JS.
36 window.on.message.add(_onMessageClosure);
37 } 31 }
38 } 32 }
39 33
40 void _uninstallHandlers() { 34 void _installOnMessageHandler() {
41 if (_onErrorClosure != null) { 35 if (_onMessageSubscription == null) {
42 window.on.error.remove(_onErrorClosure); 36 // Listen for errors from JS.
43 _onErrorClosure = null; 37 _onMessageSubscription = window.onMessage.listen(
44 } 38 (e) => processMessage(e));
45 if (_onMessageClosure != null) {
46 window.on.message.remove(_onMessageClosure);
47 _onMessageClosure = null;
48 } 39 }
49 } 40 }
50 41
42 void _installHandlers() {
43 _installOnErrorHandler();
44 _installOnMessageHandler();
45 }
46
47 void _uninstallHandlers() {
48 if (_onErrorSubscription != null) {
49 _onErrorSubscription.cancel();
50 _onErrorSubscription = null;
51 }
52 if (_onMessageSubscription != null) {
53 _onMessageSubscription.cancel();
54 _onMessageSubscription = null;
55 }
56 }
57
51 void processMessage(e) { 58 void processMessage(e) {
52 if ('unittest-suite-external-error' == e.data) { 59 if ('unittest-suite-external-error' == e.data) {
53 handleExternalError('<unknown>', '(external error detected)'); 60 handleExternalError('<unknown>', '(external error detected)');
54 } 61 }
55 } 62 }
56 63
57 void onInit() { 64 void onInit() {
58 _installHandlers(); 65 _installHandlers();
59 //initialize and load CSS 66 //initialize and load CSS
60 final String _CSSID = '_unittestcss_'; 67 final String _CSSID = '_unittestcss_';
61 68
62 var cssElement = document.head.query('#${_CSSID}'); 69 var cssElement = document.head.query('#${_CSSID}');
63 if (cssElement == null){ 70 if (cssElement == null){
64 document.head.elements.add(new Element.html( 71 document.head.children.add(new Element.html(
65 '<style id="${_CSSID}"></style>')); 72 '<style id="${_CSSID}"></style>'));
66 cssElement = document.head.query('#${_CSSID}'); 73 cssElement = document.head.query('#${_CSSID}');
67 } 74 }
68 75
69 cssElement.innerHtml = _htmlTestCSS; 76 cssElement.innerHtml = _htmlTestCSS;
70 window.postMessage('unittest-suite-wait-for-done', '*'); 77 window.postMessage('unittest-suite-wait-for-done', '*');
71 } 78 }
72 79
73 void onStart() { 80 void onStart() {
74 // Listen for uncaught errors. 81 // Listen for uncaught errors.
75 window.on.error.add(_onErrorClosure); 82 _installOnErrorHandler();
76 } 83 }
77 84
78 void onTestResult(TestCase testCase) {} 85 void onTestResult(TestCase testCase) {}
79 86
80 void onSummary(int passed, int failed, int errors, List<TestCase> results, 87 void onSummary(int passed, int failed, int errors, List<TestCase> results,
81 String uncaughtError) { 88 String uncaughtError) {
82 _showInteractiveResultsInPage(passed, failed, errors, results, 89 _showInteractiveResultsInPage(passed, failed, errors, results,
83 _isLayoutTest, uncaughtError); 90 _isLayoutTest, uncaughtError);
84 } 91 }
85 92
86 void onDone(bool success) { 93 void onDone(bool success) {
87 _uninstallHandlers(); 94 _uninstallHandlers();
88 window.postMessage('unittest-suite-done', '*'); 95 window.postMessage('unittest-suite-done', '*');
89 } 96 }
90 97
91 void _showInteractiveResultsInPage(int passed, int failed, int errors, 98 void _showInteractiveResultsInPage(int passed, int failed, int errors,
92 List<TestCase> results, bool isLayoutTest, String uncaughtError) { 99 List<TestCase> results, bool isLayoutTest, String uncaughtError) {
93 if (isLayoutTest && passed == results.length) { 100 if (isLayoutTest && passed == results.length) {
94 document.body.innerHtml = "PASS"; 101 document.body.innerHtml = "PASS";
95 } else { 102 } else {
96 // changed the StringBuffer to an Element fragment 103 // changed the StringBuffer to an Element fragment
97 Element te = new Element.html('<div class="unittest-table"></div>'); 104 Element te = new Element.html('<div class="unittest-table"></div>');
98 105
99 te.elements.add(new Element.html(passed == results.length 106 te.children.add(new Element.html(passed == results.length
100 ? "<div class='unittest-overall unittest-pass'>PASS</div>" 107 ? "<div class='unittest-overall unittest-pass'>PASS</div>"
101 : "<div class='unittest-overall unittest-fail'>FAIL</div>")); 108 : "<div class='unittest-overall unittest-fail'>FAIL</div>"));
102 109
103 // moved summary to the top since web browsers 110 // moved summary to the top since web browsers
104 // don't auto-scroll to the bottom like consoles typically do. 111 // don't auto-scroll to the bottom like consoles typically do.
105 if (passed == results.length && uncaughtError == null) { 112 if (passed == results.length && uncaughtError == null) {
106 te.elements.add(new Element.html(""" 113 te.children.add(new Element.html("""
107 <div class='unittest-pass'>All ${passed} tests passed</div>""")); 114 <div class='unittest-pass'>All ${passed} tests passed</div>"""));
108 } else { 115 } else {
109 116
110 if (uncaughtError != null) { 117 if (uncaughtError != null) {
111 te.elements.add(new Element.html(""" 118 te.children.add(new Element.html("""
112 <div class='unittest-summary'> 119 <div class='unittest-summary'>
113 <span class='unittest-error'>Uncaught error: $uncaughtError</span> 120 <span class='unittest-error'>Uncaught error: $uncaughtError</span>
114 </div>""")); 121 </div>"""));
115 } 122 }
116 123
117 te.elements.add(new Element.html(""" 124 te.children.add(new Element.html("""
118 <div class='unittest-summary'> 125 <div class='unittest-summary'>
119 <span class='unittest-pass'>Total ${passed} passed</span>, 126 <span class='unittest-pass'>Total ${passed} passed</span>,
120 <span class='unittest-fail'>${failed} failed</span>, 127 <span class='unittest-fail'>${failed} failed</span>,
121 <span class='unittest-error'> 128 <span class='unittest-error'>
122 ${errors + (uncaughtError == null ? 0 : 1)} errors</span> 129 ${errors + (uncaughtError == null ? 0 : 1)} errors</span>
123 </div>""")); 130 </div>"""));
124 } 131 }
125 132
126 te.elements.add(new Element.html(""" 133 te.children.add(new Element.html("""
127 <div><button id='btnCollapseAll'>Collapse All</button></div> 134 <div><button id='btnCollapseAll'>Collapse All</button></div>
128 """)); 135 """));
129 136
130 // handle the click event for the collapse all button 137 // handle the click event for the collapse all button
131 te.query('#btnCollapseAll').on.click.add((_){ 138 te.query('#btnCollapseAll').onClick.listen((_){
132 document 139 document
133 .queryAll('.unittest-row') 140 .queryAll('.unittest-row')
134 .forEach((el) => el.attributes['class'] = el.attributes['class'] 141 .forEach((el) => el.attributes['class'] = el.attributes['class']
135 .replaceAll('unittest-row ', 'unittest-row-hidden ')); 142 .replaceAll('unittest-row ', 'unittest-row-hidden '));
136 }); 143 });
137 144
138 var previousGroup = ''; 145 var previousGroup = '';
139 var groupPassFail = true; 146 var groupPassFail = true;
140 final indentAmount = 50; 147 final indentAmount = 50;
141 148
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 var testsInGroup = results 184 var testsInGroup = results
178 .where((TestCase t) => t.currentGroup == previousGroup) 185 .where((TestCase t) => t.currentGroup == previousGroup)
179 .toList(); 186 .toList();
180 var groupTotalTestCount = testsInGroup.length; 187 var groupTotalTestCount = testsInGroup.length;
181 var groupTestPassedCount = testsInGroup.where( 188 var groupTestPassedCount = testsInGroup.where(
182 (TestCase t) => t.result == 'pass').length; 189 (TestCase t) => t.result == 'pass').length;
183 groupPassFail = groupTotalTestCount == groupTestPassedCount; 190 groupPassFail = groupTotalTestCount == groupTestPassedCount;
184 var passFailClass = "unittest-group-status unittest-group-" 191 var passFailClass = "unittest-group-status unittest-group-"
185 "status-${groupPassFail ? 'pass' : 'fail'}"; 192 "status-${groupPassFail ? 'pass' : 'fail'}";
186 193
187 te.elements.add(new Element.html(""" 194 te.children.add(new Element.html("""
188 <div> 195 <div>
189 <div id='${safeGroup}' 196 <div id='${safeGroup}'
190 class='unittest-group ${safeGroup} test${safeGroup}'> 197 class='unittest-group ${safeGroup} test${safeGroup}'>
191 <div ${_isIE ? "style='display:inline-block' ": ""} 198 <div ${_isIE ? "style='display:inline-block' ": ""}
192 class='unittest-row-status'> 199 class='unittest-row-status'>
193 <div class='$passFailClass'></div> 200 <div class='$passFailClass'></div>
194 </div> 201 </div>
195 <div ${_isIE ? "style='display:inline-block' ": ""}> 202 <div ${_isIE ? "style='display:inline-block' ": ""}>
196 ${test_.currentGroup}</div> 203 ${test_.currentGroup}</div>
197 &nbsp; 204 &nbsp;
198 <div ${_isIE ? "style='display:inline-block' ": ""}> 205 <div ${_isIE ? "style='display:inline-block' ": ""}>
199 (${groupTestPassedCount}/${groupTotalTestCount})</div> 206 (${groupTestPassedCount}/${groupTotalTestCount})</div>
200 </div> 207 </div>
201 </div>""")); 208 </div>"""));
202 209
203 // 'safeGroup' could be empty 210 // 'safeGroup' could be empty
204 var grp = (safeGroup == '') ? null : te.query('#${safeGroup}'); 211 var grp = (safeGroup == '') ? null : te.query('#${safeGroup}');
205 if (grp != null){ 212 if (grp != null){
206 grp.on.click.add((_){ 213 grp.onClick.listen((_){
207 var row = document.query('.unittest-row-${safeGroup}'); 214 var row = document.query('.unittest-row-${safeGroup}');
208 if (row.attributes['class'].contains('unittest-row ')){ 215 if (row.attributes['class'].contains('unittest-row ')){
209 document.queryAll('.unittest-row-${safeGroup}').forEach( 216 document.queryAll('.unittest-row-${safeGroup}').forEach(
210 (e) => e.attributes['class'] = e.attributes['class'] 217 (e) => e.attributes['class'] = e.attributes['class']
211 .replaceAll('unittest-row ', 'unittest-row-hidden ')); 218 .replaceAll('unittest-row ', 'unittest-row-hidden '));
212 }else{ 219 }else{
213 document.queryAll('.unittest-row-${safeGroup}').forEach( 220 document.queryAll('.unittest-row-${safeGroup}').forEach(
214 (e) => e.attributes['class'] = e.attributes['class'] 221 (e) => e.attributes['class'] = e.attributes['class']
215 .replaceAll('unittest-row-hidden', 'unittest-row')); 222 .replaceAll('unittest-row-hidden', 'unittest-row'));
216 } 223 }
217 }); 224 });
218 } 225 }
219 } 226 }
220 227
221 _buildRow(test_, te, safeGroup, !groupPassFail); 228 _buildRow(test_, te, safeGroup, !groupPassFail);
222 } 229 }
223 230
224 document.body.elements.clear(); 231 document.body.children.clear();
225 document.body.elements.add(te); 232 document.body.children.add(te);
226 } 233 }
227 } 234 }
228 235
229 void _buildRow(TestCase test_, Element te, String groupID, bool isVisible) { 236 void _buildRow(TestCase test_, Element te, String groupID, bool isVisible) {
230 var background = 'unittest-row-${test_.id % 2 == 0 ? "even" : "odd"}'; 237 var background = 'unittest-row-${test_.id % 2 == 0 ? "even" : "odd"}';
231 var display = '${isVisible ? "unittest-row" : "unittest-row-hidden"}'; 238 var display = '${isVisible ? "unittest-row" : "unittest-row-hidden"}';
232 239
233 // TODO (prujohn@gmail.com) I had to borrow this from html_print.dart 240 // TODO (prujohn@gmail.com) I had to borrow this from html_print.dart
234 // Probably should put it in some more common location. 241 // Probably should put it in some more common location.
235 String _htmlEscape(String string) { 242 String _htmlEscape(String string) {
236 return string.replaceAll('&', '&amp;') 243 return string.replaceAll('&', '&amp;')
237 .replaceAll('<','&lt;') 244 .replaceAll('<','&lt;')
238 .replaceAll('>','&gt;'); 245 .replaceAll('>','&gt;');
239 } 246 }
240 247
241 addRowElement(id, status, description){ 248 addRowElement(id, status, description){
242 te.elements.add( 249 te.children.add(
243 new Element.html( 250 new Element.html(
244 ''' <div> 251 ''' <div>
245 <div class='$display unittest-row-${groupID} $background'> 252 <div class='$display unittest-row-${groupID} $background'>
246 <div ${_isIE ? "style='display:inline-block' ": ""} 253 <div ${_isIE ? "style='display:inline-block' ": ""}
247 class='unittest-row-id'>$id</div> 254 class='unittest-row-id'>$id</div>
248 <div ${_isIE ? "style='display:inline-block' ": ""} 255 <div ${_isIE ? "style='display:inline-block' ": ""}
249 class="unittest-row-status unittest-${test_.result}"> 256 class="unittest-row-status unittest-${test_.result}">
250 $status</div> 257 $status</div>
251 <div ${_isIE ? "style='display:inline-block' ": ""} 258 <div ${_isIE ? "style='display:inline-block' ": ""}
252 class='unittest-row-description'>$description</div> 259 class='unittest-row-description'>$description</div>
(...skipping 10 matching lines...) Expand all
263 270
264 addRowElement('${test_.id}', '${test_.result.toUpperCase()}', 271 addRowElement('${test_.id}', '${test_.result.toUpperCase()}',
265 '${test_.description}. ${_htmlEscape(test_.message)}'); 272 '${test_.description}. ${_htmlEscape(test_.message)}');
266 273
267 if (test_.stackTrace != null) { 274 if (test_.stackTrace != null) {
268 addRowElement('', '', '<pre>${_htmlEscape(test_.stackTrace)}</pre>'); 275 addRowElement('', '', '<pre>${_htmlEscape(test_.stackTrace)}</pre>');
269 } 276 }
270 } 277 }
271 278
272 279
273 static bool get _isIE => document.window.navigator.userAgent.contains('MSIE'); 280 static bool get _isIE => window.navigator.userAgent.contains('MSIE');
274 281
275 String get _htmlTestCSS => 282 String get _htmlTestCSS =>
276 ''' 283 '''
277 body{ 284 body{
278 font-size: 14px; 285 font-size: 14px;
279 font-family: 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',''' 286 font-family: 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande','''
280 ''' sans-serif; 287 ''' sans-serif;
281 background: WhiteSmoke; 288 background: WhiteSmoke;
282 } 289 }
283 290
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 { 416 {
410 } 417 }
411 418
412 '''; 419 ''';
413 } 420 }
414 421
415 void useHtmlEnhancedConfiguration([bool isLayoutTest = false]) { 422 void useHtmlEnhancedConfiguration([bool isLayoutTest = false]) {
416 if (config != null) return; 423 if (config != null) return;
417 configure(new HtmlEnhancedConfiguration(isLayoutTest)); 424 configure(new HtmlEnhancedConfiguration(isLayoutTest));
418 } 425 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698