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

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

Powered by Google App Engine
This is Rietveld 408576698