blob: 19128576aa3fc1f3383a89b910f0992d3b750e70 [file] [log] [blame]
Chris Craik93216d02015-03-05 13:58:42 -08001<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
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">
12<style>
13 x-tv.b.unittest-test-resultsbase
14 display: -webkit-flex;
15 -webkit-flex-direction: column;
16 }
17
18 x-tv.b.unittest-test-results > x-html-test-case-result.dark {
19 background-color: #eee;
20 }
21
22 x-html-test-case-result {
23 display: block;
24 }
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;
31 }
32
33 x-html-test-case-result > #details > x-html-test-case-error {
34 display: block;
35 border: 1px solid grey;
36 border-radius: 5px;
37 font-family: monospace;
38 margin-bottom: 14px;
39 }
40
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 {
43 white-space: pre;
44 }
45
46 x-html-test-case-result > #details > x-html-test-case-html-result {
47 display: block;
48 }
49
50</style>
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>
54</template>
55
56<template id="x-html-test-case-error-template">
57 <div id="stack"></div>
58</template>
59
60<script>
61'use strict';
62tv.exportTo('tv.b.unittest', function() {
63 var THIS_DOC = document.currentScript.ownerDocument;
64
65 var TestStatus = tv.b.unittest.TestStatus;
66 var TestTypes = tv.b.unittest.TestTypes;
67
68 /**
69 * @constructor
70 */
71 var HTMLTestCaseResult = tv.b.ui.define('x-html-test-case-result');
72
73 HTMLTestCaseResult.prototype = {
74 __proto__: HTMLUnknownElement.prototype,
75
76 decorate: function() {
77 this.appendChild(tv.b.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_();
86 },
87
88 get showHTMLOutput() {
89 return this.showHTMLOutput_;
90 },
91
92 set showHTMLOutput(showHTMLOutput) {
93 this.showHTMLOutput_ = showHTMLOutput;
94 this.updateHTMLOutputDisplayState_();
95 },
96
97 get testCase() {
98 return this.testCase_;
99 },
100
101 set testCase(testCase) {
102 this.testCase_ = testCase;
103 this.updateTitle_();
104 },
105
106 get testCaseHRef() {
107 return this.testCaseHRef_;
108 },
109
110 set testCaseHRef(href) {
111 this.testCaseHRef_ = href;
112 this.updateTitle_();
113 },
114 updateTitle_: function() {
115 var titleEl = this.querySelector('#title');
116 if (this.testCase_ === undefined) {
117 titleEl.textContent = '';
118 return;
119 }
120
121 if (this.testCaseHRef_) {
122 titleEl.innerHTML = '<a href="' + this.testCaseHRef_ + '">' +
123 this.testCase_.fullyQualifiedName + '</a>';
124 } else {
125 titleEl.textContent = this.testCase_.fullyQualifiedName;
126 }
127 },
128
129 addError: function(normalizedException) {
130 var errorEl = document.createElement('x-html-test-case-error');
131 errorEl.appendChild(tv.b.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_();
136 },
137
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);
142 },
143
144 updateHTMLOutputDisplayState_: function() {
145 var htmlResults = this.querySelectorAll('x-html-test-case-html-result');
146 var display;
147 if (this.showHTMLOutput)
148 display = '';
149 else
150 display = (this.testStatus_ == TestStatus.RUNNING) ? '' : 'none';
151 for (var i = 0; i < htmlResults.length; i++)
152 htmlResults[i].style.display = display;
153 },
154
155 get hadErrors() {
156 return !!this.querySelector('x-html-test-case-error');
157 },
158
159 get duration() {
160 return this.duration_;
161 },
162
163 set duration(duration) {
164 this.duration_ = duration;
165 this.updateColorAndStatus_();
166 },
167
168 get testStatus() {
169 return this.testStatus_;
170 },
171
172 set testStatus(testStatus) {
173 this.testStatus_ = testStatus;
174 this.updateColorAndStatus_();
175 this.updateHTMLOutputDisplayState_();
176 },
177
178 updateColorAndStatus_: function() {
179 var colorCls;
180 var status;
181 if (this.hadErrors) {
182 colorCls = 'unittest-failed';
183 status = 'failed';
184 } else if (this.testStatus_ == TestStatus.PENDING) {
185 colorCls = 'unittest-pending';
186 status = 'pending';
187 } else if (this.testStatus_ == TestStatus.RUNNING) {
188 colorCls = 'unittest-running';
189 status = 'running';
190 } else { // DONE_RUNNING and no errors
191 colorCls = 'unittest-passed';
192 status = 'passed';
193 }
194
195 var statusEl = this.querySelector('#status');
196 if (this.duration_)
197 statusEl.textContent = status + ' (' +
198 this.duration_.toFixed(2) + 'ms)';
199 else
200 statusEl.textContent = status;
201 statusEl.className = colorCls;
202 },
203
204 get testReturnValue() {
205 return this.testReturnValue_;
206 },
207
208 set testReturnValue(testReturnValue) {
209 this.testReturnValue_ = testReturnValue;
210 this.querySelector('#return-value').textContent = testReturnValue;
211 }
212 };
213
214
215
216
217 /**
218 * @constructor
219 */
220 var HTMLTestResults = tv.b.ui.define('x-tv.b.unittest-test-results');
221
222 HTMLTestResults.prototype = {
223 __proto__: HTMLUnknownElement.prototype,
224
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;
233 },
234
235 getHRefForTestCase: function(testCase) {
236 /* Override this to create custom links */
237 return undefined;
238 },
239
240 get showHTMLOutput() {
241 return this.showHTMLOutput_;
242 },
243
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;
249 },
250
251 get showPendingAndPassedTests() {
252 return this.showPendingAndPassedTests_;
253 },
254
255 set showPendingAndPassedTests(showPendingAndPassedTests) {
256 this.showPendingAndPassedTests_ = showPendingAndPassedTests;
257
258 var testCaseResults = this.querySelectorAll('x-html-test-case-result');
259 for (var i = 0; i < testCaseResults.length; i++)
260 this.updateDisplayStateForResult_(testCaseResults[i]);
261 },
262
263 updateDisplayStateForResult_: function(res) {
264 var display;
265 if (this.showPendingAndPassedTests_) {
266 if (res.testStatus == TestStatus.RUNNING ||
267 res.hadErrors) {
268 display = '';
269 } else {
270 display = 'none';
271 }
272 } else {
273 display = '';
274 }
275 res.style.display = display;
276
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.
281 var dark;
282 if (!res.previousSibling) {
283 dark = true;
284 } else {
285 var lastVisible;
286 for (var cur = res.previousSibling;
287 cur;
288 cur = cur.previousSibling) {
289 if (cur.style.display == '') {
290 lastVisible = cur;
291 break;
292 }
293 }
294 if (lastVisible) {
295 dark = !lastVisible.classList.contains('dark');
296 } else {
297 dark = true;
298 }
299 }
300
301 if (dark)
302 res.classList.add('dark');
303 else
304 res.classList.remove('dark');
305 },
306
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);
312 if (href)
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 + ': ');
319 },
320
321 addErrorForCurrentTest: function(error) {
322 this.log_('\n');
323
324 var normalizedException = tv.b.normalizeException(error);
325 this.log_('Exception: ' + normalizedException.message + '\n' +
326 normalizedException.stack);
327
328 this.currentTestCaseResult_.addError(normalizedException);
329 this.updateDisplayStateForResult_(this.currentTestCaseResult_);
330 },
331
332 addHTMLOutputForCurrentTest: function(element) {
333 this.currentTestCaseResult_.addHTMLOutput(element);
334 this.updateDisplayStateForResult_(this.currentTestCaseResult_);
335 },
336
337 setReturnValueFromCurrentTest: function(returnValue) {
338 this.currentTestCaseResult_.testReturnValue = returnValue;
339 },
340
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.b.dispatchSimpleEvent(this, 'testfailed');
352 } else {
353 this.log_('[PASSED]\n');
354 this.numTestsThatPassed_ += 1;
355 tv.b.dispatchSimpleEvent(this, 'testpassed');
356 }
357
358 this.updateDisplayStateForResult_(this.currentTestCaseResult_);
359 this.currentTestCaseResult_ = undefined;
360 },
361
362 didRunTests: function() {
363 this.log_('[DONE]\n');
364 },
365
366 getStats: function() {
367 return {
368 numTestsThatPassed: this.numTestsThatPassed_,
369 numTestsThatFailed: this.numTestsThatFailed_,
370 totalRunTime: this.totalRunTime_
371 };
372 },
373
374 log_: function(msg) {
375 //this.textContent += msg;
376 tv.b.dispatchSimpleEvent(this, 'statschange');
377 }
378 };
379
380 return {
381 HTMLTestResults: HTMLTestResults
382 };
383});
384</script>