Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding test run data to html report #176

Merged
merged 3 commits into from Aug 26, 2014
Merged
Changes from 2 commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -16,12 +16,23 @@ exports = module.exports = internals.Reporter = function (options) {

var filename = Path.join(__dirname, 'html', 'report.html');
var template = Fs.readFileSync(filename, 'utf8');
this.view = Handlebars.compile(template);

Handlebars.registerHelper('hits', function (hits) {

return (hits === undefined ? '' : hits);
});

Handlebars.registerHelper('join', function (array, separator) {

return array.join(separator);
});

Handlebars.registerHelper('replace', function (str, from, to, flags) {

return str.replace(new RegExp(from, flags), to);
});

this.view = Handlebars.compile(template);
};


@@ -37,13 +48,43 @@ internals.Reporter.prototype.test = function (test) {

internals.Reporter.prototype.end = function (notebook) {

notebook.coverage = notebook.coverage || { percent: 0, files: [] };
var percent = notebook.coverage.percent;
var context = {
cov: notebook.coverage,
percentClass: percent > 75 ? 'high' : (percent > 50 ? 'medium' : (percent > 25 ? 'low' : 'terrible')),
percent: (percent % 1 === 0) ? percent.toFixed() : percent.toFixed(2)
percent: (percent % 1 === 0) ? percent.toFixed() : percent.toFixed(2),
tests: notebook.tests || [],
duration: notebook.ms
};

context.failures = context.tests.filter(function (test) {

return !!test.err;
});

context.skipped = context.tests.filter(function (test) {

return test.skipped;
});

// Populate path to be used for filtering
context.paths = [];
context.tests.forEach(function (test) {

var paths = [];
test.path.forEach(function (path) {

path = path.replace(/\ /gi, '_');
paths.push(path);
if (context.paths.indexOf(path) === -1) {
context.paths.push(path);
}
});

test.path = paths;
});

notebook.coverage.files.forEach(function (file) {

file.segments = file.filename.split('/');
@@ -1,15 +1,16 @@
<!doctype html>
<html>
<head>
<title>Coverage</title>
<title>Tests &amp; Coverage</title>
<script>
headings = [];

onload = function(){
onload = function () {
headings = document.querySelectorAll('h2');
reset();
};

onscroll = function(e){
onscroll = function (e) {
var heading = find(window.scrollY);
if (!heading) return;
var links = document.querySelectorAll('#menu a')
@@ -21,7 +22,7 @@
}
};

function find(y) {
function find (y) {
var i = headings.length
, heading;

@@ -32,6 +33,56 @@
}
}
}

function show (className) {

This comment has been minimized.

Copy link
@arb

arb Aug 26, 2014

Contributor

Could you combine show and hide into a single function and call it toggle or something? They look all but identical.


var elements = document.getElementsByClassName(className);

for (var i = 0, il = elements.length; i < il; ++i) {
var element = elements[i];
element.classList.remove('hide');
element.classList.add('show');
};
}

function hide (className) {

var elements = document.getElementsByClassName(className);

for (var i = 0, il = elements.length; i < il; ++i) {
var element = elements[i];
element.classList.remove('show');
element.classList.add('hide');
};
}

function reset () {

var shownElements = document.getElementsByClassName('show');
var filterElements = document.querySelectorAll('input[type=checkbox]');

for (var i = 0, il = filterElements.length; i < il; ++i) {
filterElements[i].checked = false;
}

This comment has been minimized.

Copy link
@chrisdickinson

chrisdickinson Aug 26, 2014

nit: document.querySelectorAll('input[type=checkbox]') could pre-filter for you here.


// Check any filters with visible elements
for (i = 0, il = shownElements.length; i < il; ++i) {
var shownElement = shownElements[i];
var classNames = shownElement.className.split(' ');
for (var ci = 0, cl = classNames.length; ci < cl; ++ci){
var element = document.getElementById('show-' + classNames[ci]);
if (element) {
element.checked = true;
}
}
}
};

function filter (element) {

element.checked ? show(element.value) : hide(element.value);
reset();
}
</script>
<style>
body {
@@ -135,7 +186,7 @@
padding: 2px 3px;
}

.stats:nth-child(2n) {
#files .stats:nth-child(2n) {
display: inline-block;
margin-top: 15px;
border: 1px solid #eee;
@@ -179,6 +230,31 @@
color: #b6b6b6;
}

.stats .failures::after {
content: ' Failures';
color: #b6b6b6;

This comment has been minimized.

Copy link
@arb

arb Aug 26, 2014

Contributor

You could apply this color to the parent element so it wouldn't need to be repeated on each of these classes.

This comment has been minimized.

Copy link
@geek

geek Aug 26, 2014

Author Member

I only want the text for Failures to be this color, not the number

}

.stats .skipped::after {
content: ' Skipped';
color: #b6b6b6;
}

.stats .test-count::after {
content: ' Tests';
color: #b6b6b6;
}

.stats .duration::before {
content: '(';
color: #b6b6b6;
}

.stats .duration::after {
content: ' ms)';
color: #b6b6b6;
}

.high {
color: #00d4b4;
}
@@ -193,7 +269,7 @@
font-weight: bold;
}

table {
#files table {
width: 80%;
margin-top: 10px;
border-collapse: collapse;
@@ -202,7 +278,7 @@
border-radius: 3px;
}

table thead {
#files thead {
display: none;
}

@@ -254,13 +330,124 @@
td.source div.never {
background: #f8d5d8;
}

#tests {
padding: 60px;
}

#tests table {
width: 80%;
margin-top: 10px;
border-collapse: collapse;
border: 1px solid #cbcbcb;
color: #363636;
border-radius: 3px;
}

#tests thead {
background: #F5F5F5;
}

#tests tr {
border: 1px solid #ccc;
}

#tests td {
padding-left: 8px;
}

#tests .success:nth-child(2n) {
background: #F5F5F5;
}

#tests .failure {
background: #FF9E9E;
}

#tests .skipped {
background: #AA82FF;
}

#tests .success {
color: #949494;
}

#tests .failure .test-title {
font-weight: bold;
margin-top: 5px;
}

#tests .stack {
margin-top: 4px;
padding-left: 15px;
margin-bottom: 12px;
font: 12px monaco, monospace;
white-space: pre;
line-height: 15px;
}

.hide {
position: absolute;
left: -9999em;
}

.show {
position: relative;
}

#filters {
width: 75%;
margin-top: 25px;
}

#filters label {
margin-right: 10px;
}
</style>
</head>
<body>
<div id="tests">
<h1>Test Report</h1>
<div class="stats {{#if failures.length}}terrible{{else}}{{#if skipped.length}}low{{else}}high{{/if}}{{/if}}">
<div class="failures">{{failures.length}}</div>
<div class="skipped">{{skipped.length}}</div>
<div class="test-count">{{tests.length}}</div>
<div class="duration">{{duration}}</div>
</div>
<div id="filters">
<input type="checkbox" checked="" onchange="filter(this)" value="success" id="show-success"><label for="show-success">Show Success</label></input>

This comment has been minimized.

Copy link
@arb

arb Aug 26, 2014

Contributor

I didn't think you were supposed to set attributes equal to empty strings. Maybe I'm wrong though.

This comment has been minimized.

Copy link
@geek

geek Aug 26, 2014

Author Member

Only way to get checked to be settable

<input type="checkbox" checked="" onchange="filter(this)" value="failure" id="show-failure"><label for="show-failure">Show Failure</label></input>
{{#each paths}}
<input type="checkbox" checked="" onchange="filter(this)" value="{{this}}" id="show-{{this}}"><label for="show-{{this}}">{{replace this "\_" " " "gi"}}</label></input>
{{/each}}
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Duration (ms)</th>
</tr>
</thead>
<tbody>
{{#each tests}}
<tr class="show {{join this.path " "}} {{#if this.err}}failure{{else}}success{{/if}}">
<td class="test-id">{{this.id}}</td>
<td class="test-title">{{this.title}}
{{#if this.err}}<div class="stack">{{this.err.stack}}</div>{{/if}}
</td>
<td class="test-duration">{{this.duration}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>

<div id="coverage">
<h1 id="overview">Code Coverage Report</h1>
<h1>Code Coverage Report</h1>
<div id="menu">
<li><a href="#overview">overview</a></li>
<li><a href="#tests">Test Report</a></li>
<li><a href="#coverage">Coverage Report</a></li>
{{#each cov.files}}
<li>
<span class="cov {{this.percentClass}}">{{this.percent}}</span>
@@ -708,6 +708,38 @@ describe('Reporter', function () {
done();
});
});

it('includes test run data', function (done) {

var Test = require('./coverage/html');

var script = Lab.script({ schedule: false });
script.experiment('test', function () {

script.describe('lab', function () {

script.test('something', function (finished) {

Test.method(1, 2, 3);
finished();
});

script.test('something else', function (finished) {

Test.method(1, 2, 3);
finished();
});
});
});

Lab.report(script, { reporter: 'html', coveragePath: Path.join(__dirname, './coverage/html') }, function (err, code, output) {

expect(output).to.contain('Test Report');
expect(output).to.contain('test-title');
delete global.__$$testCovHtml;
done();
});
});
});

describe('tap', function () {
@@ -423,7 +423,7 @@ describe('Runner', function () {
});


describe('Changing global timeout functions', function () {
describe('global timeout functions', function () {

// We can't poison global.Date because the normal implementation of
// global.setTimeout uses it [1] so if the runnable.js keeps a copy of
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.