Chromium Code Reviews| OLD | NEW |
|---|---|
| (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('&','&'); | |
| 85 str = str.replaceAll('<','<'); | |
| 86 str = str.replaceAll('>','>'); | |
| 87 str = str.replaceAll('"','"'); | |
| 88 str = str.replaceAll("'",'''); | |
| 89 str = str.replaceAll('/','/'); | |
| 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 ⇲'; | |
| 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 = '►'; | |
| 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 = '↷'; | |
| 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 } | |
| OLD | NEW |