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

Side by Side Diff: client/testing/dartest/dartest.dart

Issue 8905021: Dartest CL - Please review (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 9 years 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 #library('dartest');
6
7 #import('dart:dom');
8 #import('../unittest/unittest_dartest.dart');
9
10 #source('css.dart');
11
12 /** DARTest provides a library to run tests in the app. */
13 class DARTest{
14 Map<String, HTMLElement> _inAppElements, _fullAppElements, _appElements;
15 DOMWindow _runnerWindow;
16
17 DARTest() {
18 _runnerWindow = window;
19 dartestLogger = _log;
20 _inAppElements = new HashMap<String, HTMLElement>();
21 _appElements = _inAppElements;
22 DARTestCss.inject(document, true);
23 }
24
25 void run() {
26 _renderMain();
27 _createResultsTable();
28 }
29
30 void _log(String message) {
31 _runnerWindow.console.log(message);
32 }
33
34 void _addChildToElem(String key, HTMLElement child) {
35 if(_appElements[key] != null) {
36 _appElements[key].appendChild(child);
37 }
38 }
39
40 /** Create the results table after loading tests. */
41 void _createResultsTable() {
42 _log('Creating results table');
43 HTMLTableElement table = _runnerWindow.document.createElement('table');
44 table.className = 'dt-results';
45 HTMLTableSectionElement head = _runnerWindow.document.createElement('thead') ;
46 head.innerHTML = '<tr><th>ID <th>Description <th>Result';
47 table.appendChild(head);
48
49 HTMLTableSectionElement body = _runnerWindow.document.createElement('tbody') ;
50 body.id = 'dt-results-body';
51 tests.forEach((final t) {
52 HTMLTableRowElement testDetailRow =
53 _runnerWindow.document.createElement('tr');
54 testDetailRow.id = 'dt-test-${t.id}';
55 _addTestDetails(t, testDetailRow);
56 body.appendChild(testDetailRow);
57
58 HTMLTableRowElement testMessageRow =
59 _runnerWindow.document.createElement('tr');
60 testMessageRow.id = 'dt-detail-${t.id}';
61 testMessageRow.className = 'dt-hide';
62 body.appendChild(testMessageRow);
63 });
64
65 table.appendChild(body);
66 _addChildToElem('testBody', table);
67 }
68
69 /** Update the results table for test. */
70 void _updateResultsTable(TestCase t, DOMWindow domWin) {
71 HTMLTableRowElement row = domWin.document.getElementById('dt-test-${t.id}');
72 row.className = 'dt-result-row';
73 row.innerHTML = ''; // Remove all children as we will re-populate them
74 _addTestDetails(t, row);
75
76 HTMLTableRowElement details =
77 domWin.document.getElementById('dt-detail-${t.id}');
78 details.appendChild(_getTestStats(t, domWin));
79
80 row.addEventListener('click', (Event e) {
81 if(details.className == 'dt-hide') {
82 details.className = '';
83 } else {
84 details.className = 'dt-hide';
85 }
86 }, true);
87 }
88
89 /** Escape HTML special chars. */
90 String _escape(String str) {
91 str = str.replaceAll('&','&amp;');
92 str = str.replaceAll('<','&lt;');
93 str = str.replaceAll('>','&gt;');
94 str = str.replaceAll('"','&quot;');
95 str = str.replaceAll("'",'&#x27;');
96 str = str.replaceAll('/','&#x2F;');
97 return str;
98 }
99
100 /** Get test results as table cells. */
101 void _addTestDetails(TestCase t, HTMLTableRowElement row) {
102 HTMLTableCellElement testId = _runnerWindow.document.createElement('td');
103 testId.textContent = t.id;
104 row.appendChild(testId);
105
106 HTMLTableCellElement testDesc = _runnerWindow.document.createElement('td');
107 testDesc.textContent = t.description;
108 row.appendChild(testDesc);
109
110 HTMLTableCellElement testResult = _runnerWindow.document.createElement('td') ;
111 String result = (t.result == null) ? 'none' : _escape(t.result);
112 testResult.className = 'dt-$result';
113 testResult.title = '${_escape(t.message)}';
114 testResult.textContent = result.toUpperCase();
115 row.appendChild(testResult);
116 }
117
118 HTMLTableCellElement _getTestStats(TestCase t, DOMWindow domWin) {
119 HTMLTableCellElement tableCell = domWin.document.createElement('td');
120 tableCell.colSpan = 3;
121
122 if(t.message != '') {
123 HTMLSpanElement messageSpan = domWin.document.createElement('span');
124 messageSpan.textContent = t.message;
125 tableCell.appendChild(messageSpan);
126 tableCell.appendChild(domWin.document.createElement('br'));
127 }
128 if(t.stackTrace != null) {
129 HTMLPreElement stackTacePre = domWin.document.createElement('pre');
130 stackTacePre.textContent = t.stackTrace;
131 }
132
133 HTMLSpanElement durationSpan = domWin.document.createElement('span');
134 durationSpan.textContent = 'took ${_printDuration(t.runningTime)}';
135 tableCell.appendChild(durationSpan);
136
137 return tableCell;
138 }
139
140 /** Update the UI after running test. */
141 void _updateDARTestUI(TestCase test) {
142 _updateResultsTable(test, window);
143 if(_runnerWindow != window) {
144 _updateResultsTable(test, _runnerWindow);
145 }
146
147 if(test.result != null) {
148 _log(' Result: ${test.result.toUpperCase()} ${test.message}');
149 }
150 if(test.runningTime != null) {
151 _log(' took ${_printDuration(test.runningTime)}');
152 }
153 _updateStatusProgress(_appElements);
154 if(_runnerWindow != window) {
155 _updateStatusProgress(_inAppElements);
156 }
157 }
158
159 void _updateStatusProgress(Map<String, HTMLElement> elements) {
160 // Update progressbar
161 var pPass =
162 ((testsRun - testsFailed - testsErrors) / tests.length) * 100;
163 elements['green'].setAttribute('style', 'width:$pPass%');
164 var pFailed = pPass + (testsFailed / tests.length) * 100;
165 elements['red'].setAttribute('style', 'width:$pFailed%');
166 var pErrors = pFailed + (testsErrors / tests.length) * 100;
167 elements['orange'].setAttribute('style', 'width:$pErrors%');
168
169 // Update status
170 elements['testsRunElem'].textContent = testsRun.toString();
171 elements['testsFailedElem'].textContent = testsFailed.toString();
172 elements['testsErrorsElem'].textContent = testsErrors.toString();
173 }
174
175 String _printDuration(Duration timeDuration) {
176 StringBuffer out = new StringBuffer();
177 if(timeDuration.inDays > 0) {
178 out.add('${timeDuration.inDays} days ');
179 }
180 if(timeDuration.inHours > 0) {
181 out.add('${timeDuration.inHours} hrs ');
182 }
183 if(timeDuration.inMinutes > 0) {
184 out.add('${timeDuration.inMinutes} mins ');
185 }
186 if(timeDuration.inSeconds > 0) {
187 out.add('${timeDuration.inSeconds} s ');
188 }
189 if(timeDuration.inMilliseconds > 0 || out.length == 0) {
190 out.add('${timeDuration.inMilliseconds} ms');
191 }
192 return out.toString();
193 }
194
195 /** Populates the floating div with controls and toolbar. */
196 HTMLDivElement _renderMain() {
197 HTMLDivElement containerDiv = _runnerWindow.document.createElement('div');
198 containerDiv.className = 'dt-container';
199 _appElements['containerDiv'] = containerDiv;
200
201 // Add the test controls
202 HTMLDivElement mainElem = _runnerWindow.document.createElement('div');
203 mainElem.className = 'dt-main';
204 _appElements['mainElem'] = mainElem;
205
206 _showTestControls();
207
208 // Create header to hold window controls
209 if(_runnerWindow == window) {
210 HTMLDivElement headDiv = _runnerWindow.document.createElement('div');
211 headDiv.className = 'dt-header';
212 headDiv.innerHTML = 'DARTest: In-App View';
213 HTMLImageElement close = _runnerWindow.document.createElement('img');
214 close.className = 'dt-header-close';
215 close.addEventListener('click', (Event) {
216 containerDiv.className = 'dt-hide';
217 }, true);
218 HTMLImageElement pop = _runnerWindow.document.createElement('img');
219 pop.className = 'dt-header-pop';
220 pop.addEventListener('click', (Event) => _dartestMaximize(), true);
221 HTMLImageElement minMax = _runnerWindow.document.createElement('img');
222 minMax.className = 'dt-header-min';
223 minMax.addEventListener('click', (Event) {
224 if (mainElem.classList.contains('dt-hide')) {
225 mainElem.classList.remove('dt-hide');
226 mainElem.classList.add('dt-show');
227 minMax.className = 'dt-header-min';
228 } else {
229 if (mainElem.classList.contains('dt-show')) {
230 mainElem.classList.remove('dt-show');
231 }
232 mainElem.classList.add('dt-hide');
233 minMax.className = 'dt-header-max';
234 }
235 }, true);
236 headDiv.appendChild(close);
237 headDiv.appendChild(pop);
238 headDiv.appendChild(minMax);
239
240 containerDiv.appendChild(headDiv);
241 }
242
243 HTMLDivElement tabDiv = _runnerWindow.document.createElement('div');
244 tabDiv.className = 'dt-tab';
245 HTMLUListElement tabList = _runnerWindow.document.createElement('ul');
246 HTMLLIElement testingTab = _runnerWindow.document.createElement('li');
247 HTMLLIElement coverageTab = _runnerWindow.document.createElement('li');
248 testingTab.className = 'dt-tab-selected';
249 testingTab.textContent = 'Testing';
250 testingTab.addEventListener('click', (Event) {
251 _showTestControls();
252 _changeTabs(testingTab, coverageTab);
253 }, true);
254 tabList.appendChild(testingTab);
255 coverageTab.textContent = 'Coverage';
256 coverageTab.addEventListener('click', (Event) {
257 _showCoverageControls();
258 _changeTabs(coverageTab, testingTab);
259 }, true);
260 tabList.appendChild(coverageTab);
261 tabDiv.appendChild(tabList);
262 containerDiv.appendChild(tabDiv);
263
264 if(_runnerWindow != window) {
265 HTMLDivElement popIn = _runnerWindow.document.createElement('div');
266 popIn.className = 'dt-minimize';
267 popIn.innerHTML = 'Pop In &#8690;';
268 popIn.addEventListener('click', (Event) => _dartestMinimize(), true);
269 containerDiv.appendChild(popIn);
270 }
271
272 containerDiv.appendChild(mainElem);
273 _runnerWindow.document.body.appendChild(containerDiv);
274 }
275
276 void _changeTabs(HTMLLIElement clickedTab, HTMLLIElement oldTab) {
277 oldTab.className = '';
278 clickedTab.className = 'dt-tab-selected';
279 }
280
281 void _showTestControls() {
282 HTMLDivElement testBody = _appElements['testBody'];
283 if(testBody == null) {
284 testBody = _runnerWindow.document.createElement('div');
285 _appElements['testBody'] = testBody;
286
287 // Create a toolbar to hold action buttons
288 HTMLDivElement toolDiv = _runnerWindow.document.createElement('div');
289 toolDiv.className = 'dt-toolbar';
290 HTMLButtonElement runBtn = _runnerWindow.document.createElement('button');
291 runBtn.innerHTML = '&#9658;';
292 runBtn.title = 'Run Tests';
293 runBtn.className = 'dt-button dt-run';
294 runBtn.addEventListener('click', (Event) {
295 _log('Running tests');
296 updateUI = _updateDARTestUI;
297 runDartests();
298 }, true);
299 toolDiv.appendChild(runBtn);
300 HTMLButtonElement exportBtn =
301 _runnerWindow.document.createElement('button');
302 exportBtn.innerHTML = '&#8631;';
303 exportBtn.title = 'Export Results';
304 exportBtn.className = 'dt-button dt-run';
305 exportBtn.addEventListener('click', (Event e) {
306 _log('Exporting results');
307 _exportTestResults();
308 }, true);
309 toolDiv.appendChild(exportBtn);
310 testBody.appendChild(toolDiv);
311
312 // Create a datalist element for showing test status
313 HTMLDListElement statList = _runnerWindow.document.createElement('dl');
314 statList.className = 'dt-status';
315 HTMLElement runsDt = _runnerWindow.document.createElement('dt');
316 runsDt.textContent = 'Runs:';
317 statList.appendChild(runsDt);
318 HTMLElement testsRunElem = _runnerWindow.document.createElement('dd');
319 _appElements['testsRunElem'] = testsRunElem;
320 testsRunElem.textContent = testsRun.toString();
321 statList.appendChild(testsRunElem);
322
323 HTMLElement failDt = _runnerWindow.document.createElement('dt');
324 failDt.textContent = 'Failed:';
325 statList.appendChild(failDt);
326 HTMLElement testsFailedElem = _runnerWindow.document.createElement('dd');
327 _appElements['testsFailedElem'] = testsFailedElem;
328 testsFailedElem.textContent = testsFailed.toString();
329 statList.appendChild(testsFailedElem);
330
331 HTMLElement errDt = _runnerWindow.document.createElement('dt');
332 errDt.textContent = 'Errors:';
333 statList.appendChild(errDt);
334 HTMLElement testsErrorsElem = _runnerWindow.document.createElement('dd');
335 _appElements['testsErrorsElem'] = testsErrorsElem;
336 testsErrorsElem.textContent = testsErrors.toString();
337 statList.appendChild(testsErrorsElem);
338 testBody.appendChild(statList);
339
340 // Create progressbar and add red, green, orange bars
341 HTMLDivElement progressDiv = _runnerWindow.document.createElement('div');
342 progressDiv.className = 'dt-progressbar';
343 progressDiv.innerHTML = "<span style='width:100%'></span>";
344
345 HTMLSpanElement orange = _runnerWindow.document.createElement('span');
346 _appElements['orange'] = orange;
347 orange.className = 'orange';
348 progressDiv.appendChild(orange);
349
350 HTMLSpanElement red = _runnerWindow.document.createElement('span');
351 _appElements['red'] = red;
352 red.className = 'red';
353 progressDiv.appendChild(red);
354
355 HTMLSpanElement green = _runnerWindow.document.createElement('span');
356 _appElements['green'] = green;
357 green.className = 'green';
358
359 progressDiv.appendChild(green);
360 testBody.appendChild(progressDiv);
361
362 HTMLDivElement hiddenElem = _runnerWindow.document.createElement('div');
363 hiddenElem.className = 'dt-hide';
364 hiddenElem.innerHTML =
365 "<a id='dt-export' download='test_results.csv' href='#' />";
366 testBody.appendChild(hiddenElem);
367
368 _addChildToElem('mainElem', testBody);
369 }
370
371 // Show hide divs
372 _show('testBody');
373 _hide('coverageBody');
374 }
375
376 void _showCoverageControls() {
377 HTMLDivElement coverageBody = _appElements['coverageBody'];
378 if(coverageBody == null) {
379 coverageBody = _runnerWindow.document.createElement('div');
380 _appElements['coverageBody'] = coverageBody;
381
382 HTMLPreElement covPreElem = _runnerWindow.document.createElement('pre');
383 _appElements['covPreElem'] = covPreElem;
384 coverageBody.appendChild(covPreElem);
385
386 HTMLTableElement covTable = _runnerWindow.document.createElement('table');
387 covTable.className = 'dt-results';
388 HTMLTableSectionElement head =
389 _runnerWindow.document.createElement('thead');
390 head.innerHTML = '<tr><th>Unit <th>Function <th>Statement <th>Branch';
391 covTable.appendChild(head);
392 HTMLTableSectionElement covTableBody =
393 _runnerWindow.document.createElement('tbody');
394 _appElements['covTableBody'] = covTableBody;
395 covTableBody.id = 'dt-results-body';
396 covTable.appendChild(covTableBody);
397 coverageBody.appendChild(covTable);
398
399 _addChildToElem('mainElem', coverageBody);
400 }
401 _show('coverageBody');
402 _hide('testBody');
403
404 _appElements['covPreElem'].textContent = getCoverageSummary();
405 _appElements['covTableBody'].innerHTML = getCoverageDetails();
406 }
407
408 void _show(String toShow) {
409 HTMLElement show = _appElements[toShow];
410 if(show != null) {
411 if(show.classList.contains('dt-hide')) {
412 show.classList.remove('dt-hide');
413 }
414 show.classList.add('dt-show');
415 }
416 }
417
418 void _hide(String toHide) {
419 HTMLElement hide = _appElements[toHide];
420 if(hide != null) {
421 if(hide.classList.contains('dt-show')) {
422 hide.classList.remove('dt-show');
423 }
424 hide.classList.add('dt-hide');
425 }
426 }
427
428 void _dartestMaximize() {
429 _hide('containerDiv');
430 _runnerWindow = window.open('', 'dartest-window',
431 DARTestCss._fullAppWindowFeatures);
432 _runnerWindow.document.title = 'Dartest';
433 _fullAppElements = new HashMap<String, HTMLElement>();
434 _appElements = _fullAppElements;
435 DARTestCss.inject(_runnerWindow.document, false);
436 run();
437 if(testsRun > 0) {
438 tests.forEach((final t) => _updateDARTestUI(t));
439 }
440 }
441
442 void _dartestMinimize() {
443 _runnerWindow.close();
444 _runnerWindow = window;
445 _appElements = _inAppElements;
446 _show('containerDiv');
447 }
448
449 void _exportTestResults() {
450 String csvData = getTestResultsCsv();
451 _log(csvData);
452 HTMLAnchorElement exportLink =
453 _runnerWindow.document.getElementById('dt-export');
454
455 /** Bug: Can't instantiate WebKitBlobBuilder
456 * If this bug is fixed, we can remove the urlencode and lpad function.
457 *
458 * WebKitBlobBuilder bb = new WebKitBlobBuilder();
459 * bb.append(csvData);
460 * Blob blob = bb.getBlob('text/plain;charset=${document.characterSet}');
461 * exportLink.href = window.webkitURL.createObjectURL(blob);
462 **/
463
464 exportLink.href = 'data:text/csv,' + _urlencode(csvData);
465
466 MouseEvent ev = document.createEvent("MouseEvents");
467 ev.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0
468 , false, false, false, false, 0, null);
469 exportLink.dispatchEvent(ev);
470
471 }
472
473 static String _urlencode(String s) {
474 StringBuffer out = new StringBuffer();
475 for(int i = 0; i < s.length; i++) {
476 int cc = s.charCodeAt(i);
477 if((cc >= 48 && cc <= 57) || (cc >= 65 && cc <= 90) ||
478 (cc >= 97 && cc <= 122)) {
479 out.add(s[i]);
480 } else {
481 out.add('%${_lpad(cc.toRadixString(16),2).toUpperCase()}');
482 }
483 }
484 return out.toString();
485 }
486
487 static String _lpad(String s, int n) {
488 if(s.length < n) {
489 for(int i = 0; i < n - s.length; i++) {
490 s = '0'+s;
491 }
492 }
493 return s;
494 }
495 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698