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

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');
pdr 2011/12/14 21:02:36 I would strongly recommend using dart:html. It's g
shauvik 2011/12/16 07:19:27 When I started, you couldn't do many things using
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{
pdr 2011/12/14 21:02:36 Under the names section of the style guide (http:/
shauvik 2011/12/16 07:19:27 Kelly and I decided to have the same name across a
14 Map<String, HTMLElement> inAppElements, fullAppElements, appElements;
pdr 2011/12/14 21:02:36 I don't think these are necessary (if they are, ca
shauvik 2011/12/16 07:19:27 I spoke to Kelly and Joel and its difficult and so
15 bool inAppMode;
Kelly Norton 2011/12/15 18:35:31 I suspect you want these to be private _inAppMode?
shauvik 2011/12/16 07:19:27 Done.
16 DOMWindow win, appWindow;
Kelly Norton 2011/12/15 18:35:31 private?
shauvik 2011/12/16 07:19:27 Done.
17
18 DARTest(){
19 win = window;
Kelly Norton 2011/12/15 18:35:31 It seems unnecessary to store a reference to windo
shauvik 2011/12/16 07:19:27 Done.
20 appWindow = window;
21 dartestLoggingFunc = log;
22 inAppMode = true;
23 inAppElements = new HashMap<String, HTMLElement>();
24 appElements = inAppElements;
25
26 DARTestCss.inject(win.document, true);
Kelly Norton 2011/12/15 18:35:31 document should also be available as a top-level g
shauvik 2011/12/16 07:19:27 Done.
27 }
28
29 void run() {
30 renderMain();
31 createResultsTable();
32 }
33
34 void log(String message){
pdr 2011/12/14 21:02:36 Log calls should generally be behind a flag at wor
shauvik 2011/12/16 07:19:27 Whole of dartest is behind a compiler flag. In dis
35 appWindow.console.log(message);
36 }
37
38 void addChildToElem(String key, HTMLElement child) {
Kelly Norton 2011/12/15 18:35:31 This seems like a sizeable public API. Are all of
shauvik 2011/12/16 07:19:27 Made it private. On 2011/12/15 18:35:31, Kelly No
39 if(appElements[key] != null) {
40 appElements[key].appendChild(child);
41 }
42 }
43
44 /** Create the results table after loading tests */
45 void createResultsTable(){
46 log('Creating results table');
47 HTMLTableElement table = win.document.createElement('table');
Kelly Norton 2011/12/15 18:35:31 I'm really confused by all the references to diffe
shauvik 2011/12/16 07:19:27 Done.
48 table.className = 'dt-results';
49 HTMLTableSectionElement head = win.document.createElement('thead');
50 head.innerHTML = '<tr><th>ID <th>Description <th>Result';
51 table.appendChild(head);
52
53 HTMLTableSectionElement body = win.document.createElement('tbody');
54 body.id = 'dt-results-body';
55 body.innerHTML = '';
56 for (TestCase t in tests){
57 body.innerHTML += "<tr id='dt-test-${t.id}'>" + getTestDetails(t);
58 body.innerHTML += "<tr id='dt-detail-${t.id}' class='dt-hide'></span>";
59 }
60 table.appendChild(body);
61 addChildToElem('testBody', table);
62 }
63
64 /** Update the results table for test */
65 void updateResultsTable(TestCase t, DOMWindow domWin){
66 HTMLTableRowElement row = domWin.document.getElementById('dt-test-${t.id}');
67 row.className = 'dt-result-row';
68 row.innerHTML = getTestDetails(t);
69
70 HTMLTableRowElement details = domWin.document.getElementById('dt-detail-${t. id}');
pdr 2011/12/14 21:02:36 Line > 100 chars
shauvik 2011/12/16 07:19:27 Done.
71 details.innerHTML = getTestStats(t);
72
73 row.addEventListener('click', (Event e) {
74 if(details.className == 'dt-hide') {
75 details.className = '';
76 } else {
77 details.className = 'dt-hide';
78 }
79 }, true);
80 }
81
82 /** Escape HTML Special Chars */
83 String escape(String str){
84 str = str.replaceAll('&','&amp;');
85 str = str.replaceAll('<','&lt;');
86 str = str.replaceAll('>','&gt;');
87 str = str.replaceAll('"','&quot;');
88 str = str.replaceAll("'",'&#x27;');
89 str = str.replaceAll('/','&#x2F;');
90 return str;
91 }
92
93 /** Get test results as table cells */
pdr 2011/12/14 21:02:36 This can be private so you can rename it using the
shauvik 2011/12/16 07:19:27 Done.
94 String getTestDetails(TestCase t) {
pdr 2011/12/14 21:02:36 Instead of building up this table by strings, I wo
pdr 2011/12/14 21:02:36 Without escaping and innerHTML-based DOM manipulat
shauvik 2011/12/16 07:19:27 Done.
shauvik 2011/12/16 07:19:27 Actually I need that value in the class too. So, c
95 String desc = escape(t.description);
96 String result = "none";
97 if(t.result != null){
98 result = t.result;
99 }
100 return "<td>${t.id} <td>$desc <td class='dt-$result' " +
101 "title='${escape(t.message)}'> ${result.toUpperCase()}";
102 }
103
104 String getTestStats(TestCase t) {
pdr 2011/12/14 21:02:36 See comments in getTestDetails
shauvik 2011/12/16 07:19:27 Done.
105 String message = '';
106 if(t.message != '') {
107 message = t.message + '<br>';
Kelly Norton 2011/12/15 18:35:31 This looks problematic. where does t.message get h
shauvik 2011/12/16 07:19:27 Done.
108 }
109 if(t.stackTrace != null) {
110 message += '<pre>${t.stackTrace}</pre>';
Kelly Norton 2011/12/15 18:35:31 Similar here, I don't see where t.stackTrace is es
shauvik 2011/12/16 07:19:27 Done.
111 }
112 return "<td colspan='3'> $message took ${printDuration(t.timeDuration)}</td> ";
pdr 2011/12/14 21:02:36 Line > 100 cols
shauvik 2011/12/16 07:19:27 Done.
113 }
114
115 /** Update the UI after running test */
pdr 2011/12/14 21:02:36 Nit: make sure to use a period in your comments :)
shauvik 2011/12/16 07:19:27 Done.
116 void updateDARTestUI(TestCase test){
Kelly Norton 2011/12/15 18:35:31 space before {
shauvik 2011/12/16 07:19:27 Done.
117 // Update table
pdr 2011/12/14 21:02:36 unnecessary comment
shauvik 2011/12/16 07:19:27 Done.
118 updateResultsTable(test, win);
119 if(!inAppMode) {
120 updateResultsTable(test, appWindow);
121 }
122
123 if(test.result != null) {
124 log(' Result: ${test.result.toUpperCase()} ${test.message}');
125 }
126 if(test.timeDuration != null){
127 log(' took ${printDuration(test.timeDuration)}');
128 }
129 updateStatusProgress(appElements);
130 if(!inAppMode) {
131 updateStatusProgress(inAppElements);
132 }
133 }
134
135 void updateStatusProgress(Map<String, HTMLElement> elements){
Kelly Norton 2011/12/15 18:35:31 space before {
shauvik 2011/12/16 07:19:27 Done.
136 // Update progressbar
137 var pPass = ((numTestsRun - numTestsFailed - numTestsErrors) / tests.length) * 100;
pdr 2011/12/14 21:02:36 Line > 100 cols (here and elsewhere).
shauvik 2011/12/16 07:19:27 Done.
138 elements['green'].setAttribute('style', 'width:$pPass%');
Kelly Norton 2011/12/15 18:35:31 Why do you use a map to store references to things
shauvik 2011/12/16 07:19:27 Actually everything is stored in maps. We need to
Kelly Norton 2011/12/16 19:48:13 I'm just saying that a map is definitely not the b
139 var pFailed = pPass + (numTestsFailed / tests.length) * 100;
140 elements['red'].setAttribute('style', 'width:$pFailed%');
141 var pErrors = pFailed + (numTestsErrors / tests.length) * 100;
142 elements['orange'].setAttribute('style', 'width:$pErrors%');
143
144 // Update status
145 elements['testsRunElem'].innerText = '$numTestsRun';
pdr 2011/12/14 21:02:36 There's no need to do this for your strings. Just
Kelly Norton 2011/12/15 18:35:31 You want to use textContent instead of innerText a
shauvik 2011/12/16 07:19:27 Yes, I did that to get rid of int-to-string warnin
shauvik 2011/12/16 07:19:27 Cool, Thanks for the tip. I didn't know innerText
146 elements['testsFailedElem'].innerText = '$numTestsFailed';
147 elements['testsErrorsElem'].innerText = '$numTestsErrors';
148 }
149
150 String printDuration(Duration timeDuration) {
151 StringBuffer out = new StringBuffer();
152 if(timeDuration.inDays > 0){
153 out.add('${timeDuration.inDays} days ');
154 }
155 if(timeDuration.inHours > 0){
156 out.add('${timeDuration.inHours} hrs ');
157 }
158 if(timeDuration.inMinutes > 0){
159 out.add('${timeDuration.inMinutes} mins ');
160 }
161 if(timeDuration.inSeconds > 0){
162 out.add('${timeDuration.inSeconds} s ');
163 }
164 if(timeDuration.inMilliseconds > 0 || out.length == 0){
165 out.add('${timeDuration.inMilliseconds} ms');
166 }
167 return out.toString();
168 }
169
170 /** Populates the floating div with controls and toolbar */
171 HTMLDivElement renderMain(){
172 HTMLDivElement containerDiv = win.document.createElement('div');
173 containerDiv.className = 'dt-container';
174 appElements['containerDiv'] = containerDiv;
175
176 // Add the test controls
177 HTMLDivElement mainElem = win.document.createElement('div');
178 mainElem.className = 'dt-main';
179 appElements['mainElem'] = mainElem;
180
181 showTestControls();
182
183 // Create header to hold window controls
184 if(inAppMode) {
185 HTMLDivElement headDiv = win.document.createElement('div');
186 headDiv.className = 'dt-header';
187 headDiv.innerHTML = '<b>DARTest: In-App View</b>';
188 HTMLImageElement close = win.document.createElement('img');
189 close.className = 'dt-header-close';
190 close.addEventListener('click', (Event){
191 containerDiv.className = 'dt-hide';
192 }, true);
193 HTMLImageElement pop = win.document.createElement('img');
194 pop.className = 'dt-header-pop';
195 pop.addEventListener('click', (Event) => dartestMaximize(), true);
196 HTMLImageElement minMax = win.document.createElement('img');
197 minMax.className = 'dt-header-min';
198 minMax.addEventListener('click', (Event) {
199 if (mainElem.classList.contains('dt-hide')){
200 mainElem.classList.remove('dt-hide');
201 mainElem.classList.add('dt-show');
202 minMax.className = 'dt-header-min';
203 } else {
204 if (mainElem.classList.contains('dt-show')){
205 mainElem.classList.remove('dt-show');
206 }
207 mainElem.classList.add('dt-hide');
208 minMax.className = 'dt-header-max';
209 }
210 }, true);
211 headDiv.appendChild(close);
212 headDiv.appendChild(pop);
213 headDiv.appendChild(minMax);
214
215 containerDiv.appendChild(headDiv);
216 }
217
218 HTMLDivElement tabDiv = win.document.createElement('div');
219 tabDiv.className = 'dt-tab';
220 HTMLUListElement tabList = win.document.createElement('ul');
221 HTMLLIElement testingTab = win.document.createElement('li');
222 HTMLLIElement coverageTab = win.document.createElement('li');
223 testingTab.className = 'dt-tab-selected';
224 testingTab.innerText = 'Testing';
225 testingTab.addEventListener('click', (Event) {
226 showTestControls();
227 changeTabs(testingTab, coverageTab);
228 }, true);
229 tabList.appendChild(testingTab);
230 coverageTab.innerText = 'Coverage';
231 coverageTab.addEventListener('click', (Event) {
232 showCoverageControls();
233 changeTabs(coverageTab, testingTab);
234 }, true);
235 tabList.appendChild(coverageTab);
236 tabDiv.appendChild(tabList);
237 containerDiv.appendChild(tabDiv);
238
239 if(!inAppMode) {
240 HTMLDivElement popIn = win.document.createElement('div');
241 popIn.className = 'dt-minimize';
242 popIn.innerHTML = 'Pop In &#8690;';
243 popIn.addEventListener('click', (Event) => dartestMinimize(), true);
244 containerDiv.appendChild(popIn);
245 }
246
247 containerDiv.appendChild(mainElem);
248 win.document.body.appendChild(containerDiv);
249 }
250
251 void changeTabs(HTMLLIElement clickedTab, HTMLLIElement oldTab){
252 oldTab.className = '';
253 clickedTab.className = 'dt-tab-selected';
254 }
255
256 void showTestControls(){
257 HTMLDivElement testBody = appElements['testBody'];
258 if(testBody == null){
259 testBody = win.document.createElement('div');
260 appElements['testBody'] = testBody;
261
262 // Create a toolbar to hold action buttons
263 HTMLDivElement toolDiv = win.document.createElement('div');
264 toolDiv.className = 'dt-toolbar';
265 HTMLButtonElement runBtn = win.document.createElement('button');
266 runBtn.innerHTML = '&#9658;';
267 runBtn.title = 'Run Tests';
268 runBtn.className = 'dt-button dt-run';
269 runBtn.addEventListener('click', (Event) {
270 log('Running tests');
271 uiUpdateFunc = updateDARTestUI;
272 runDARTests();
273 }, true);
274 toolDiv.appendChild(runBtn);
275 HTMLButtonElement exportBtn = win.document.createElement('button');
276 exportBtn.innerHTML = '&#8631;';
277 exportBtn.title = 'Export Results';
278 exportBtn.className = 'dt-button dt-run';
279 exportBtn.addEventListener('click', (Event e) {
280 log('Exporting results');
281 exportTestResults();
282 }, true);
283 toolDiv.appendChild(exportBtn);
284 testBody.appendChild(toolDiv);
285
286 // Create a datalist element for showing test status"<td class='dt-pass'> PASS";
287 HTMLDListElement statList = win.document.createElement('dl');
288 statList.className = 'dt-status';
289 HTMLElement runsDt = win.document.createElement('dt');
290 runsDt.innerText = 'Runs:';
291 statList.appendChild(runsDt);
292 HTMLElement testsRunElem = win.document.createElement('dd');
293 appElements['testsRunElem'] = testsRunElem;
294 testsRunElem.innerText = '$numTestsRun';
295 statList.appendChild(testsRunElem);
296
297 HTMLElement failDt = win.document.createElement('dt');
298 failDt.innerText = 'Failed:';
299 statList.appendChild(failDt);
300 HTMLElement testsFailedElem = win.document.createElement('dd');
301 appElements['testsFailedElem'] = testsFailedElem;
302 testsFailedElem.innerText = '$numTestsFailed';
303 statList.appendChild(testsFailedElem);
304
305 HTMLElement errDt = win.document.createElement('dt');
306 errDt.innerText = 'Errors:';
307 statList.appendChild(errDt);
308 HTMLElement testsErrorsElem = win.document.createElement('dd');
309 appElements['testsErrorsElem'] = testsErrorsElem;
310 testsErrorsElem.innerText = '$numTestsErrors';
311 statList.appendChild(testsErrorsElem);
312 testBody.appendChild(statList);
313
314 // Create progressbar and add red, green, orange bars
315 HTMLDivElement progressDiv = win.document.createElement('div');
316 progressDiv.className = 'dt-progressbar';
317 progressDiv.innerHTML = "<span style='width:100%'></span>";
318
319 HTMLSpanElement orange = win.document.createElement('span');
320 appElements['orange'] = orange;
321 orange.className = 'orange';
322 progressDiv.appendChild(orange);
323
324 HTMLSpanElement red = win.document.createElement('span');
325 appElements['red'] = red;
326 red.className = 'red';
327 progressDiv.appendChild(red);
328
329 HTMLSpanElement green = win.document.createElement('span');
330 appElements['green'] = green;
331 green.className = 'green';
332
333 progressDiv.appendChild(green);
334 testBody.appendChild(progressDiv);
335
336 HTMLDivElement hiddenElem = win.document.createElement('div');
337 hiddenElem.className = 'dt-hide';
338 hiddenElem.innerHTML = "<a id='dt-export' download='test_results.csv' href ='#' />";
339 testBody.appendChild(hiddenElem);
340
341 addChildToElem('mainElem', testBody);
342 }
343
344 // Show hide divs
345 showHide('testBody', 'coverageBody');
346 }
347
348 void showCoverageControls(){
349 HTMLDivElement coverageBody = appElements['coverageBody'];
350 if(coverageBody == null){
351 coverageBody = win.document.createElement('div');
352 appElements['coverageBody'] = coverageBody;
353
354 HTMLPreElement covPreElem = win.document.createElement('pre');
355 appElements['covPreElem'] = covPreElem;
356 coverageBody.appendChild(covPreElem);
357
358 HTMLTableElement covTable = win.document.createElement('table');
359 covTable.className = 'dt-results';
360 HTMLTableSectionElement head = win.document.createElement('thead');
361 head.innerHTML = '<tr><th>Unit <th>Function <th>Statement <th>Branch';
362 covTable.appendChild(head);
363 HTMLTableSectionElement covTableBody = win.document.createElement('tbody') ;
364 appElements['covTableBody'] = covTableBody;
365 covTableBody.id = 'dt-results-body';
366 covTable.appendChild(covTableBody);
367 coverageBody.appendChild(covTable);
368
369 addChildToElem('mainElem', coverageBody);
370 }
371 showHide('coverageBody', 'testBody');
372
373 appElements['covPreElem'].innerText = getCoverageSummary();
374 appElements['covTableBody'].innerHTML = getCoverageDetails();
375 }
376
377 void showHide(String toShow, String toHide) {
pdr 2011/12/14 21:02:36 I'd split this into show and hide.
shauvik 2011/12/16 07:19:27 Done.
378 HTMLElement show = appElements[toShow];
379 HTMLElement hide = appElements[toHide];
380 if(show != null){
381 if(show.classList.contains('dt-hide')) {
382 show.classList.remove('dt-hide');
383 }
384 show.classList.add('dt-show');
385 }
386 if(hide != null){
387 if(hide.classList.contains('dt-show')){
388 hide.classList.remove('dt-show');
389 }
390 hide.classList.add('dt-hide');
391 }
392 }
393
394 void dartestMaximize(){
395 showHide('', 'containerDiv');
396 win = win.open('', 'dartest-window', 'width=600,height=750');
397 log('Window:'+win.toString());
pdr 2011/12/14 21:02:36 I would remove these logging calls, or put them be
shauvik 2011/12/16 07:19:27 Done.
398 win.document.title = 'Dartest';
399 inAppMode = false;
400 fullAppElements = new HashMap<String, HTMLElement>();
401 appElements = fullAppElements;
402 DARTestCss.inject(win.document, false);
403 run();
404 if(numTestsRun > 0) {
405 populateResults();
406 }
407 }
408
409 void populateResults() {
pdr 2011/12/14 21:02:36 I think this can be: tests.forEach(TestCase t) =>
shauvik 2011/12/16 07:19:27 Thanks. Its slightly different that what you propo
410 for(TestCase t in tests) {
411 updateDARTestUI(t);
412 }
413 }
414
415 void dartestMinimize(){
416 win.close();
417 win = appWindow;
418 inAppMode = true;
419 appElements = inAppElements;
420 showHide('containerDiv', '');
421 }
422
423 void exportTestResults(){
424 String csvData = getTestResultsCSV();
425 log(csvData);
426 HTMLAnchorElement exportLink = win.document.getElementById('dt-export');
427
428 /** Bug: Can't instantiate WebKitBlobBuilder
429 * If this bug is fixed, we can remove the urlencode and lpad function.
430 WebKitBlobBuilder bb = new WebKitBlobBuilder();
431 bb.append(csvData);
432 Blob blob = bb.getBlob('text/plain;charset=${document.characterSet}');
433 exportLink.href = window.webkitURL.createObjectURL(blob);
434 **/
435
436 exportLink.href = 'data:text/csv,'+urlencode(csvData);
437
438 MouseEvent ev = document.createEvent("MouseEvents");
439 ev.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0
440 , false, false, false, false, 0, null);
441 exportLink.dispatchEvent(ev);
442
443 }
444
445 String urlencode(String s) {
Kelly Norton 2011/12/15 18:35:31 How is the different from the native encodeURI?
shauvik 2011/12/16 07:19:27 Actually I think encodeURI isn't supported in dart
446 StringBuffer out = new StringBuffer();
447 for(int i=0;i<s.length;i++) {
448 int cc = s.charCodeAt(i);
449 if((cc>=48 && cc<=57) || (cc>=65 && cc<=90) || (cc>=97 && cc<=122)) {
450 out.add(s[i]);
451 } else {
452 out.add('%${lpad(cc.toRadixString(16),2).toUpperCase()}');
453 }
454 }
455 return out.toString();
456 }
457
458 String lpad(String s, int n) {
459 if(s.length<n) {
Kelly Norton 2011/12/15 18:35:31 s.length < n Lots of other space problems in this
shauvik 2011/12/16 07:19:27 Done.
460 for(int i=0;i<n-s.length;i++) {
461 s = '0'+s;
462 }
463 }
464 return s;
465 }
466 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698