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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: client/testing/dartest/dartest.dart
===================================================================
--- client/testing/dartest/dartest.dart (revision 0)
+++ client/testing/dartest/dartest.dart (revision 0)
@@ -0,0 +1,496 @@
+// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#library('dartest');
+
+#import('dart:dom');
+#import('../unittest/unittest_dartest.dart');
+
+#source('css.dart');
+
+/** DARTest provides a library to run tests in the app. */
+class DARTest{
+ Map<String, HTMLElement> _inAppElements, _fullAppElements, _appElements;
+ DOMWindow _runnerWindow;
+
+ DARTest() {
+ _runnerWindow = window;
+ dartestLogger = _log;
+ _inAppElements = new HashMap<String, HTMLElement>();
+ _appElements = _inAppElements;
+ DARTestCss.inject(document, true);
+ }
+
+ void run() {
+ _renderMain();
+ _createResultsTable();
+ }
+
+ void _log(String message) {
+ _runnerWindow.console.log(message);
+ }
+
+ void _addChildToElem(String key, HTMLElement child) {
+ if(_appElements[key] != null) {
+ _appElements[key].appendChild(child);
+ }
+ }
+
+ /** Create the results table after loading tests. */
+ void _createResultsTable() {
+ _log('Creating results table');
+ HTMLTableElement table = _runnerWindow.document.createElement('table');
+ table.className = 'dt-results';
+ HTMLTableSectionElement head = _runnerWindow.document.createElement('thead');
+ head.innerHTML = '<tr><th>ID <th>Description <th>Result';
+ table.appendChild(head);
+
+ HTMLTableSectionElement body = _runnerWindow.document.createElement('tbody');
+ body.id = 'dt-results-body';
+ tests.forEach((final t) {
+ HTMLTableRowElement testDetailRow = _runnerWindow.document.createElement('tr');
+ testDetailRow.id = 'dt-test-${t.id}';
+ _addTestDetails(t, testDetailRow);
+ body.appendChild(testDetailRow);
+
+ HTMLTableRowElement testMessageRow = _runnerWindow.document.createElement('tr');
+ testMessageRow.id = 'dt-detail-${t.id}';
+ testMessageRow.className = 'dt-hide';
+ body.appendChild(testMessageRow);
+ });
+
+ table.appendChild(body);
+ _addChildToElem('testBody', table);
+ }
+
+ /** Update the results table for test. */
+ void _updateResultsTable(TestCase t, DOMWindow domWin) {
+ HTMLTableRowElement row = domWin.document.getElementById('dt-test-${t.id}');
+ row.className = 'dt-result-row';
+ row.innerHTML = ''; // Remove all children as we will re-populate them
+ _addTestDetails(t, row);
+
+ HTMLTableRowElement details =
+ domWin.document.getElementById('dt-detail-${t.id}');
+ details.appendChild(_getTestStats(t, domWin));
+
+ row.addEventListener('click', (Event e) {
+ if(details.className == 'dt-hide') {
+ details.className = '';
+ } else {
+ details.className = 'dt-hide';
+ }
+ }, true);
+ }
+
+ /** Escape HTML special chars. */
+ String _escape(String str) {
+ str = str.replaceAll('&','&amp;');
+ str = str.replaceAll('<','&lt;');
+ str = str.replaceAll('>','&gt;');
+ str = str.replaceAll('"','&quot;');
+ str = str.replaceAll("'",'&#x27;');
+ str = str.replaceAll('/','&#x2F;');
+ return str;
+ }
+
+ /** Get test results as table cells. */
+ void _addTestDetails(TestCase t, HTMLTableRowElement row) {
+
pdr 2011/12/16 17:00:50 Extra space
shauvik 2011/12/16 17:57:06 Done.
+ HTMLTableCellElement testId = _runnerWindow.document.createElement('td');
+ testId.textContent = t.id;
+ row.appendChild(testId);
+
+ HTMLTableCellElement testDesc = _runnerWindow.document.createElement('td');
+ testDesc.textContent = t.description;
+ row.appendChild(testDesc);
+
+ HTMLTableCellElement testResult = _runnerWindow.document.createElement('td');
+ String result = (t.result == null) ? 'none' : _escape(t.result);
+ testResult.className = 'dt-$result';
+ testResult.title = '${_escape(t.message)}';
+ testResult.textContent = result.toUpperCase();
+ row.appendChild(testResult);
+ }
+
+ HTMLTableCellElement _getTestStats(TestCase t, DOMWindow domWin) {
+ HTMLTableCellElement tableCell = domWin.document.createElement('td');
+ tableCell.colSpan = 3;
+
+ if(t.message != '') {
+ HTMLSpanElement messageSpan = domWin.document.createElement('span');
+ messageSpan.textContent = t.message;
+ tableCell.appendChild(messageSpan);
+ tableCell.appendChild(domWin.document.createElement('br'));
+ }
+ if(t.stackTrace != null) {
+ HTMLPreElement stackTacePre = domWin.document.createElement('pre');
+ stackTacePre.textContent = t.stackTrace;
+ }
+
+ HTMLSpanElement durationSpan = domWin.document.createElement('span');
+ durationSpan.textContent = 'took ${_printDuration(t.runningTime)}';
+ tableCell.appendChild(durationSpan);
+
+ return tableCell;
+ }
+
+ /** Update the UI after running test. */
+ void _updateDARTestUI(TestCase test) {
+ _updateResultsTable(test, window);
+ if(_runnerWindow != window) {
+ _updateResultsTable(test, _runnerWindow);
+ }
+
+ if(test.result != null) {
+ _log(' Result: ${test.result.toUpperCase()} ${test.message}');
+ }
+ if(test.runningTime != null) {
+ _log(' took ${_printDuration(test.runningTime)}');
+ }
+ _updateStatusProgress(_appElements);
+ if(_runnerWindow != window) {
+ _updateStatusProgress(_inAppElements);
+ }
+ }
+
+ void _updateStatusProgress(Map<String, HTMLElement> elements) {
+ // Update progressbar
+ var pPass =
+ ((testsRun - testsFailed - testsErrors) / tests.length) * 100;
+ elements['green'].setAttribute('style', 'width:$pPass%');
+ var pFailed = pPass + (testsFailed / tests.length) * 100;
+ elements['red'].setAttribute('style', 'width:$pFailed%');
+ var pErrors = pFailed + (testsErrors / tests.length) * 100;
+ elements['orange'].setAttribute('style', 'width:$pErrors%');
+
+ // Update status
+ elements['testsRunElem'].textContent = testsRun.toString();
+ elements['testsFailedElem'].textContent = testsFailed.toString();
+ elements['testsErrorsElem'].textContent = testsErrors.toString();
+ }
+
+ String _printDuration(Duration timeDuration) {
+ StringBuffer out = new StringBuffer();
+ if(timeDuration.inDays > 0) {
+ out.add('${timeDuration.inDays} days ');
+ }
+ if(timeDuration.inHours > 0) {
+ out.add('${timeDuration.inHours} hrs ');
+ }
+ if(timeDuration.inMinutes > 0) {
+ out.add('${timeDuration.inMinutes} mins ');
+ }
+ if(timeDuration.inSeconds > 0) {
+ out.add('${timeDuration.inSeconds} s ');
+ }
+ if(timeDuration.inMilliseconds > 0 || out.length == 0) {
+ out.add('${timeDuration.inMilliseconds} ms');
+ }
+ return out.toString();
+ }
+
+ /** Populates the floating div with controls and toolbar. */
+ HTMLDivElement _renderMain() {
+ HTMLDivElement containerDiv = _runnerWindow.document.createElement('div');
+ containerDiv.className = 'dt-container';
+ _appElements['containerDiv'] = containerDiv;
+
+ // Add the test controls
+ HTMLDivElement mainElem = _runnerWindow.document.createElement('div');
+ mainElem.className = 'dt-main';
+ _appElements['mainElem'] = mainElem;
+
+ _showTestControls();
+
+ // Create header to hold window controls
+ if(_runnerWindow == window) {
+ HTMLDivElement headDiv = _runnerWindow.document.createElement('div');
+ headDiv.className = 'dt-header';
+ 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
+ HTMLImageElement close = _runnerWindow.document.createElement('img');
+ close.className = 'dt-header-close';
+ close.addEventListener('click', (Event) {
+ containerDiv.className = 'dt-hide';
+ }, true);
+ HTMLImageElement pop = _runnerWindow.document.createElement('img');
+ pop.className = 'dt-header-pop';
+ pop.addEventListener('click', (Event) => dartestMaximize(), true);
+ HTMLImageElement minMax = _runnerWindow.document.createElement('img');
+ minMax.className = 'dt-header-min';
+ minMax.addEventListener('click', (Event) {
+ if (mainElem.classList.contains('dt-hide')) {
+ mainElem.classList.remove('dt-hide');
+ mainElem.classList.add('dt-show');
+ minMax.className = 'dt-header-min';
+ } else {
+ if (mainElem.classList.contains('dt-show')) {
+ mainElem.classList.remove('dt-show');
+ }
+ mainElem.classList.add('dt-hide');
+ minMax.className = 'dt-header-max';
+ }
+ }, true);
+ headDiv.appendChild(close);
+ headDiv.appendChild(pop);
+ headDiv.appendChild(minMax);
+
+ containerDiv.appendChild(headDiv);
+ }
+
+ HTMLDivElement tabDiv = _runnerWindow.document.createElement('div');
+ tabDiv.className = 'dt-tab';
+ HTMLUListElement tabList = _runnerWindow.document.createElement('ul');
+ HTMLLIElement testingTab = _runnerWindow.document.createElement('li');
+ HTMLLIElement coverageTab = _runnerWindow.document.createElement('li');
+ testingTab.className = 'dt-tab-selected';
+ testingTab.textContent = 'Testing';
+ testingTab.addEventListener('click', (Event) {
+ _showTestControls();
+ _changeTabs(testingTab, coverageTab);
+ }, true);
+ tabList.appendChild(testingTab);
+ coverageTab.textContent = 'Coverage';
+ coverageTab.addEventListener('click', (Event) {
+ _showCoverageControls();
+ _changeTabs(coverageTab, testingTab);
+ }, true);
+ tabList.appendChild(coverageTab);
+ tabDiv.appendChild(tabList);
+ containerDiv.appendChild(tabDiv);
+
+ if(_runnerWindow != window) {
+ HTMLDivElement popIn = _runnerWindow.document.createElement('div');
+ popIn.className = 'dt-minimize';
+ popIn.innerHTML = 'Pop In &#8690;';
+ popIn.addEventListener('click', (Event) => dartestMinimize(), true);
+ containerDiv.appendChild(popIn);
+ }
+
+ containerDiv.appendChild(mainElem);
+ _runnerWindow.document.body.appendChild(containerDiv);
+ }
+
+ void _changeTabs(HTMLLIElement clickedTab, HTMLLIElement oldTab) {
+ oldTab.className = '';
+ clickedTab.className = 'dt-tab-selected';
+ }
+
+ void _showTestControls() {
+ HTMLDivElement testBody = _appElements['testBody'];
+ if(testBody == null) {
+ testBody = _runnerWindow.document.createElement('div');
+ _appElements['testBody'] = testBody;
+
+ // Create a toolbar to hold action buttons
+ HTMLDivElement toolDiv = _runnerWindow.document.createElement('div');
+ toolDiv.className = 'dt-toolbar';
+ HTMLButtonElement runBtn = _runnerWindow.document.createElement('button');
+ runBtn.innerHTML = '&#9658;';
+ runBtn.title = 'Run Tests';
+ runBtn.className = 'dt-button dt-run';
+ runBtn.addEventListener('click', (Event) {
+ _log('Running tests');
+ updateUI = _updateDARTestUI;
+ runDartests();
+ }, true);
+ toolDiv.appendChild(runBtn);
+ HTMLButtonElement exportBtn =
+ _runnerWindow.document.createElement('button');
+ exportBtn.innerHTML = '&#8631;';
+ exportBtn.title = 'Export Results';
+ exportBtn.className = 'dt-button dt-run';
+ exportBtn.addEventListener('click', (Event e) {
+ _log('Exporting results');
+ exportTestResults();
+ }, true);
+ toolDiv.appendChild(exportBtn);
+ testBody.appendChild(toolDiv);
+
+ // Create a datalist element for showing test status
+ HTMLDListElement statList = _runnerWindow.document.createElement('dl');
+ statList.className = 'dt-status';
+ HTMLElement runsDt = _runnerWindow.document.createElement('dt');
+ runsDt.textContent = 'Runs:';
+ statList.appendChild(runsDt);
+ HTMLElement testsRunElem = _runnerWindow.document.createElement('dd');
+ _appElements['testsRunElem'] = testsRunElem;
+ testsRunElem.textContent = testsRun.toString();
+ statList.appendChild(testsRunElem);
+
+ HTMLElement failDt = _runnerWindow.document.createElement('dt');
+ failDt.textContent = 'Failed:';
+ statList.appendChild(failDt);
+ HTMLElement testsFailedElem = _runnerWindow.document.createElement('dd');
+ _appElements['testsFailedElem'] = testsFailedElem;
+ testsFailedElem.textContent = testsFailed.toString();
+ statList.appendChild(testsFailedElem);
+
+ HTMLElement errDt = _runnerWindow.document.createElement('dt');
+ errDt.textContent = 'Errors:';
+ statList.appendChild(errDt);
+ HTMLElement testsErrorsElem = _runnerWindow.document.createElement('dd');
+ _appElements['testsErrorsElem'] = testsErrorsElem;
+ testsErrorsElem.textContent = testsErrors.toString();
+ statList.appendChild(testsErrorsElem);
+ testBody.appendChild(statList);
+
+ // Create progressbar and add red, green, orange bars
+ HTMLDivElement progressDiv = _runnerWindow.document.createElement('div');
+ progressDiv.className = 'dt-progressbar';
+ progressDiv.innerHTML = "<span style='width:100%'></span>";
+
+ HTMLSpanElement orange = _runnerWindow.document.createElement('span');
+ _appElements['orange'] = orange;
+ orange.className = 'orange';
+ progressDiv.appendChild(orange);
+
+ HTMLSpanElement red = _runnerWindow.document.createElement('span');
+ _appElements['red'] = red;
+ red.className = 'red';
+ progressDiv.appendChild(red);
+
+ HTMLSpanElement green = _runnerWindow.document.createElement('span');
+ _appElements['green'] = green;
+ green.className = 'green';
+
+ progressDiv.appendChild(green);
+ testBody.appendChild(progressDiv);
+
+ HTMLDivElement hiddenElem = _runnerWindow.document.createElement('div');
+ hiddenElem.className = 'dt-hide';
+ hiddenElem.innerHTML =
+ "<a id='dt-export' download='test_results.csv' href='#' />";
+ testBody.appendChild(hiddenElem);
+
+ _addChildToElem('mainElem', testBody);
+ }
+
+ // Show hide divs
+ _show('testBody');
+ _hide('coverageBody');
+ }
+
+ void _showCoverageControls() {
+ HTMLDivElement coverageBody = _appElements['coverageBody'];
+ if(coverageBody == null) {
+ coverageBody = _runnerWindow.document.createElement('div');
+ _appElements['coverageBody'] = coverageBody;
+
+ HTMLPreElement covPreElem = _runnerWindow.document.createElement('pre');
+ _appElements['covPreElem'] = covPreElem;
+ coverageBody.appendChild(covPreElem);
+
+ HTMLTableElement covTable = _runnerWindow.document.createElement('table');
+ covTable.className = 'dt-results';
+ HTMLTableSectionElement head =
+ _runnerWindow.document.createElement('thead');
+ head.innerHTML = '<tr><th>Unit <th>Function <th>Statement <th>Branch';
+ covTable.appendChild(head);
+ HTMLTableSectionElement covTableBody =
+ _runnerWindow.document.createElement('tbody');
+ _appElements['covTableBody'] = covTableBody;
+ covTableBody.id = 'dt-results-body';
+ covTable.appendChild(covTableBody);
pdr 2011/12/16 17:00:50 indentation
shauvik 2011/12/16 17:57:06 Done.
+ coverageBody.appendChild(covTable);
+
+ _addChildToElem('mainElem', coverageBody);
+ }
+ _show('coverageBody');
+ _hide('testBody');
+
+ _appElements['covPreElem'].textContent = getCoverageSummary();
+ _appElements['covTableBody'].innerHTML = getCoverageDetails();
+ }
+
+ void _show(String toShow) {
+ HTMLElement show = _appElements[toShow];
+ if(show != null) {
+ if(show.classList.contains('dt-hide')) {
+ show.classList.remove('dt-hide');
+ }
+ show.classList.add('dt-show');
+ }
+ }
+
+ void _hide(String toHide) {
+ HTMLElement hide = _appElements[toHide];
+ if(hide != null) {
+ if(hide.classList.contains('dt-show')) {
+ hide.classList.remove('dt-show');
+ }
+ hide.classList.add('dt-hide');
+ }
+ }
+
+ void dartestMaximize() {
+ _hide('containerDiv');
+ _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.
+ _runnerWindow.document.title = 'Dartest';
+ _fullAppElements = new HashMap<String, HTMLElement>();
+ _appElements = _fullAppElements;
+ DARTestCss.inject(_runnerWindow.document, false);
+ run();
+ if(testsRun > 0) {
+ populateResults();
+ }
+ }
+
+ void populateResults() {
+ tests.forEach((final t) => _updateDARTestUI(t));
+ }
+
+ void dartestMinimize() {
+ _runnerWindow.close();
+ _runnerWindow = window;
+ _appElements = _inAppElements;
+ _show('containerDiv');
+ }
+
+ void exportTestResults() {
+ String csvData = getTestResultsCsv();
+ _log(csvData);
+ HTMLAnchorElement exportLink =
+ _runnerWindow.document.getElementById('dt-export');
+
+ /** Bug: Can't instantiate WebKitBlobBuilder
+ * If this bug is fixed, we can remove the urlencode and lpad function.
+ WebKitBlobBuilder bb = new WebKitBlobBuilder();
+ bb.append(csvData);
+ Blob blob = bb.getBlob('text/plain;charset=${document.characterSet}');
+ exportLink.href = window.webkitURL.createObjectURL(blob);
+ **/
+
+ exportLink.href = 'data:text/csv,'+urlencode(csvData);
+
+ MouseEvent ev = document.createEvent("MouseEvents");
+ ev.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0
+ , false, false, false, false, 0, null);
+ exportLink.dispatchEvent(ev);
+
+ }
+
pdr 2011/12/16 17:00:50 Make these private and static
shauvik 2011/12/16 17:57:06 Done.
+ String urlencode(String s) {
+ StringBuffer out = new StringBuffer();
+ for(int i = 0; i < s.length; i++) {
+ int cc = s.charCodeAt(i);
+ if((cc >= 48 && cc <= 57) || (cc >= 65 && cc <= 90) ||
+ (cc >= 97 && cc <= 122)) {
+ out.add(s[i]);
+ } else {
+ out.add('%${lpad(cc.toRadixString(16),2).toUpperCase()}');
+ }
+ }
+ return out.toString();
+ }
+
+ String lpad(String s, int n) {
+ if(s.length < n) {
+ for(int i = 0; i < n - s.length; i++) {
+ s = '0'+s;
+ }
+ }
+ return s;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698