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

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

Powered by Google App Engine
This is Rietveld 408576698