3 Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
7 <link rel="import" href="/base/events.html">
8 <link rel="import" href="/base/utils.html">
9 <link rel="import" href="/base/unittest/constants.html">
10 <link rel="import" href="/base/ui.html">
11 <link rel="stylesheet" href="/base/unittest/common.css">
13 x-tv.unittest-test-resultsbase
14 display: -webkit-flex;
15 -webkit-flex-direction: column;
18 x-tv.unittest-test-results > x-html-test-case-result.dark {
19 background-color: #eee;
22 x-html-test-case-result {
25 x-html-test-case-result > #title,
26 x-html-test-case-result > #status,
27 x-html-test-case-result > #details > x-html-test-case-error > #message,
28 x-html-test-case-result > #details > x-html-test-case-error > #stack,
29 x-html-test-case-result > #details > x-html-test-case-error > #return-value {
30 -webkit-user-select: auto;
33 x-html-test-case-result > #details > x-html-test-case-error {
35 border: 1px solid grey;
37 font-family: monospace;
41 x-html-test-case-result > #details > x-html-test-case-error > #message,
42 x-html-test-case-result > #details > x-html-test-case-error > #stack {
46 x-html-test-case-result > #details > x-html-test-case-html-result {
51 <template id="x-html-test-case-result-template">
52 <span id="title"></span> <span id="status"></span> <span id="return-value"></span>
53 <div id="details"></div>
56 <template id="x-html-test-case-error-template">
57 <div id="stack"></div>
62 tv.exportTo('tv.unittest', function() {
63 var THIS_DOC = document.currentScript.ownerDocument;
65 var TestStatus = tv.unittest.TestStatus;
66 var TestTypes = tv.unittest.TestTypes;
71 var HTMLTestCaseResult = tv.ui.define('x-html-test-case-result');
73 HTMLTestCaseResult.prototype = {
74 __proto__: HTMLUnknownElement.prototype,
76 decorate: function() {
77 this.appendChild(tv.instantiateTemplate(
78 '#x-html-test-case-result-template', THIS_DOC));
79 this.testCase_ = undefined;
80 this.testCaseHRef_ = undefined;
81 this.duration_ = undefined;
82 this.testStatus_ = TestStatus.PENDING;
83 this.testReturnValue_ = undefined;
84 this.showHTMLOutput_ = false;
85 this.updateColorAndStatus_();
88 get showHTMLOutput() {
89 return this.showHTMLOutput_;
92 set showHTMLOutput(showHTMLOutput) {
93 this.showHTMLOutput_ = showHTMLOutput;
94 this.updateHTMLOutputDisplayState_();
98 return this.testCase_;
101 set testCase(testCase) {
102 this.testCase_ = testCase;
107 return this.testCaseHRef_;
110 set testCaseHRef(href) {
111 this.testCaseHRef_ = href;
114 updateTitle_: function() {
115 var titleEl = this.querySelector('#title');
116 if (this.testCase_ === undefined) {
117 titleEl.textContent = '';
121 if (this.testCaseHRef_) {
122 titleEl.innerHTML = '<a href="' + this.testCaseHRef_ + '">' +
123 this.testCase_.fullyQualifiedName + '</a>';
125 titleEl.textContent = this.testCase_.fullyQualifiedName;
129 addError: function(normalizedException) {
130 var errorEl = document.createElement('x-html-test-case-error');
131 errorEl.appendChild(tv.instantiateTemplate(
132 '#x-html-test-case-error-template', THIS_DOC));
133 errorEl.querySelector('#stack').textContent = normalizedException.stack;
134 this.querySelector('#details').appendChild(errorEl);
135 this.updateColorAndStatus_();
138 addHTMLOutput: function(element) {
139 var htmlResultEl = document.createElement('x-html-test-case-html-result');
140 htmlResultEl.appendChild(element);
141 this.querySelector('#details').appendChild(htmlResultEl);
144 updateHTMLOutputDisplayState_: function() {
145 var htmlResults = this.querySelectorAll('x-html-test-case-html-result');
147 if (this.showHTMLOutput)
150 display = (this.testStatus_ == TestStatus.RUNNING) ? '' : 'none';
151 for (var i = 0; i < htmlResults.length; i++)
152 htmlResults[i].style.display = display;
156 return !!this.querySelector('x-html-test-case-error');
160 return this.duration_;
163 set duration(duration) {
164 this.duration_ = duration;
165 this.updateColorAndStatus_();
169 return this.testStatus_;
172 set testStatus(testStatus) {
173 this.testStatus_ = testStatus;
174 this.updateColorAndStatus_();
175 this.updateHTMLOutputDisplayState_();
178 updateColorAndStatus_: function() {
181 if (this.hadErrors) {
182 colorCls = 'unittest-failed';
184 } else if (this.testStatus_ == TestStatus.PENDING) {
185 colorCls = 'unittest-pending';
187 } else if (this.testStatus_ == TestStatus.RUNNING) {
188 colorCls = 'unittest-running';
190 } else { // DONE_RUNNING and no errors
191 colorCls = 'unittest-passed';
195 var statusEl = this.querySelector('#status');
197 statusEl.textContent = status + ' (' +
198 this.duration_.toFixed(2) + 'ms)';
200 statusEl.textContent = status;
201 statusEl.className = colorCls;
204 get testReturnValue() {
205 return this.testReturnValue_;
208 set testReturnValue(testReturnValue) {
209 this.testReturnValue_ = testReturnValue;
210 this.querySelector('#return-value').textContent = testReturnValue;
220 var HTMLTestResults = tv.ui.define('x-tv.unittest-test-results');
222 HTMLTestResults.prototype = {
223 __proto__: HTMLUnknownElement.prototype,
225 decorate: function() {
226 this.currentTestCaseStartTime_ = undefined;
227 this.totalRunTime_ = 0;
228 this.numTestsThatPassed_ = 0;
229 this.numTestsThatFailed_ = 0;
230 this.showHTMLOutput_ = false;
231 this.showPendingAndPassedTests_ = false;
232 this.linkifyCallback_ = undefined;
235 getHRefForTestCase: function(testCase) {
236 /* Override this to create custom links */
240 get showHTMLOutput() {
241 return this.showHTMLOutput_;
244 set showHTMLOutput(showHTMLOutput) {
245 this.showHTMLOutput_ = showHTMLOutput;
246 var testCaseResults = this.querySelectorAll('x-html-test-case-result');
247 for (var i = 0; i < testCaseResults.length; i++)
248 testCaseResults[i].showHTMLOutput = showHTMLOutput;
251 get showPendingAndPassedTests() {
252 return this.showPendingAndPassedTests_;
255 set showPendingAndPassedTests(showPendingAndPassedTests) {
256 this.showPendingAndPassedTests_ = showPendingAndPassedTests;
258 var testCaseResults = this.querySelectorAll('x-html-test-case-result');
259 for (var i = 0; i < testCaseResults.length; i++)
260 this.updateDisplayStateForResult_(testCaseResults[i]);
263 updateDisplayStateForResult_: function(res) {
265 if (this.showPendingAndPassedTests_) {
266 if (res.testStatus == TestStatus.RUNNING ||
275 res.style.display = display;
277 // This bit of mess gives res objects a dark class based on whether their
278 // last visible sibling was not dark. It relies on the
279 // updateDisplayStateForResult_ being called on all previous siblings of
280 // an element before being called on the element itself. Yay induction.
282 if (!res.previousSibling) {
286 for (var cur = res.previousSibling;
288 cur = cur.previousSibling) {
289 if (cur.style.display == '') {
295 dark = !lastVisible.classList.contains('dark');
302 res.classList.add('dark');
304 res.classList.remove('dark');
307 willRunTest: function(testCase) {
308 this.currentTestCaseResult_ = new HTMLTestCaseResult();
309 this.currentTestCaseResult_.showHTMLOutput = this.showHTMLOutput_;
310 this.currentTestCaseResult_.testCase = testCase;
311 var href = this.getHRefForTestCase(testCase);
313 this.currentTestCaseResult_.testCaseHRef = href;
314 this.currentTestCaseResult_.testStatus = TestStatus.RUNNING;
315 this.currentTestCaseStartTime_ = window.performance.now();
316 this.appendChild(this.currentTestCaseResult_);
317 this.updateDisplayStateForResult_(this.currentTestCaseResult_);
318 this.log_(testCase.fullyQualifiedName + ': ');
321 addErrorForCurrentTest: function(error) {
324 var normalizedException = tv.normalizeException(error);
325 this.log_('Exception: ' + normalizedException.message + '\n' +
326 normalizedException.stack);
328 this.currentTestCaseResult_.addError(normalizedException);
329 this.updateDisplayStateForResult_(this.currentTestCaseResult_);
332 addHTMLOutputForCurrentTest: function(element) {
333 this.currentTestCaseResult_.addHTMLOutput(element);
334 this.updateDisplayStateForResult_(this.currentTestCaseResult_);
337 setReturnValueFromCurrentTest: function(returnValue) {
338 this.currentTestCaseResult_.testReturnValue = returnValue;
341 didCurrentTestEnd: function() {
342 var testCaseResult = this.currentTestCaseResult_;
343 var testCaseDuration = window.performance.now() -
344 this.currentTestCaseStartTime_;
345 this.currentTestCaseResult_.testStatus = TestStatus.DONE_RUNNING;
346 testCaseResult.duration = testCaseDuration;
347 this.totalRunTime_ += testCaseDuration;
348 if (testCaseResult.hadErrors) {
349 this.log_('[FAILED]\n');
350 this.numTestsThatFailed_ += 1;
351 tv.dispatchSimpleEvent(this, 'testfailed');
353 this.log_('[PASSED]\n');
354 this.numTestsThatPassed_ += 1;
355 tv.dispatchSimpleEvent(this, 'testpassed');
358 this.updateDisplayStateForResult_(this.currentTestCaseResult_);
359 this.currentTestCaseResult_ = undefined;
362 didRunTests: function() {
363 this.log_('[DONE]\n');
366 getStats: function() {
368 numTestsThatPassed: this.numTestsThatPassed_,
369 numTestsThatFailed: this.numTestsThatFailed_,
370 totalRunTime: this.totalRunTime_
374 log_: function(msg) {
375 //this.textContent += msg;
376 tv.dispatchSimpleEvent(this, 'statschange');
381 HTMLTestResults: HTMLTestResults