protractor-jasmine2-screenshot-reporter
Advanced tools
Comparing version 0.2.0 to 0.3.0
@@ -0,1 +1,13 @@ | ||
# 0.3.0 | ||
## Features | ||
- support test sharding | ||
- added example configurations: [protractor.example.conf](https://github.com/mlison/protractor-jasmine2-screenshot-reporter/blob/master/protractor.example.conf) and [protractor.sharding-example.conf](https://github.com/mlison/protractor-jasmine2-screenshot-reporter/blob/master/protractor.sharding-example.conf) | ||
- added [cleanDestination](https://github.com/mlison/protractor-jasmine2-screenshot-reporter/blob/master/README.md#clean-destination-directory-optional) option | ||
## BRAKING CHANGES | ||
In order to support running multiple browser instances in parallel, there's a little more setup to be done in protractor configuration when adding the reporter. This affects also users who don't use sharding. | ||
Please refer to setup instructions before upgrading: [Usage instructions](https://github.com/mlison/protractor-jasmine2-screenshot-reporter#usage). | ||
# 0.2.0 | ||
@@ -2,0 +14,0 @@ |
273
index.js
@@ -8,2 +8,3 @@ var DEFAULT_DESTINATION = 'target/screenshots'; | ||
path = require('path'), | ||
uuid = require('uuid'), | ||
hat = require('hat'); | ||
@@ -34,8 +35,2 @@ | ||
// store extra css files. | ||
cssLinks = [], | ||
// monitor failed specs for quick links | ||
failedSpecIds = [], | ||
// when use use fit, jasmine never calls suiteStarted / suiteDone, so make a fake one to use | ||
@@ -49,3 +44,7 @@ fakeFocusedSuite = { | ||
var linkTemplate = _.template( | ||
'<li id="<%= id %>" class="<%= cssClass %>">' + | ||
'<li id="<%= id %>" ' + | ||
'class="<%= cssClass %>" ' + | ||
'data-spec="<%= specId %>" ' + | ||
'data-name="<%= name %>" ' + | ||
'data-browser="<%= browserName %>">' + | ||
'<%= mark %>' + | ||
@@ -59,3 +58,8 @@ '<a href="<%= filename %>"><%= name %></a> ' + | ||
var nonLinkTemplate = _.template( | ||
'<li title="No screenshot was created for this test case." id="<%= id %>" class="<%= cssClass %>">' + | ||
'<li title="No screenshot was created for this test case." ' + | ||
'id="<%= id %>" ' + | ||
'class="<%= cssClass %>" ' + | ||
'data-spec="<%= specId %>" ' + | ||
'data-name="<%= name %>" ' + | ||
'data-browser="<%= browserName %>">' + | ||
'<%= mark %>' + | ||
@@ -68,3 +72,3 @@ '<%= name %> ' + | ||
var reportTemplate = _.template( | ||
var openReportTemplate = _.template( | ||
'<html>' + | ||
@@ -81,10 +85,70 @@ '<head>' + | ||
'<%= userCss %>' + | ||
'<script type="text/javascript">' + | ||
'function showhide(id) {' + | ||
'var e = document.getElementById(id);' + | ||
'e.style.display = (e.style.display == "block") ? "none" : "block";' + | ||
'}' + | ||
'function buildQuickLinks() {' + | ||
'var failedSpecs = document.querySelectorAll("li.failed");' + | ||
'var quickLinksContainer = document.getElementById("quickLinks");' + | ||
'for (var i = 0; i < failedSpecs.length; ++i) {' + | ||
'var li = document.createElement("li");' + | ||
'var a = document.createElement("a");' + | ||
'a.href = "#" + failedSpecs[i].id;' + | ||
'a.textContent = failedSpecs[i].dataset.name + " (" + failedSpecs[i].dataset.browser + ")";' + | ||
'li.appendChild(a);' + | ||
'quickLinksContainer.appendChild(li);' + | ||
'}' + | ||
'}' + | ||
'function updatePassCount() {' + | ||
'var totalPassed = document.querySelectorAll("li.passed").length;' + | ||
'var totalFailed = document.querySelectorAll("li.failed").length;' + | ||
'var totalSpecs = totalFailed + totalPassed;' + | ||
'console.log("passed: %s, failed: %s, total: %s", totalPassed, totalFailed, totalSpecs);' + | ||
'document.getElementById("summaryTotalSpecs").textContent = ' + | ||
'document.getElementById("summaryTotalSpecs").textContent + totalSpecs;' + | ||
'document.getElementById("summaryTotalFailed").textContent = ' + | ||
'document.getElementById("summaryTotalFailed").textContent + totalFailed;' + | ||
'if (totalFailed) {' + | ||
'document.getElementById("summary").className = "failed";' + | ||
'}' + | ||
'}' + | ||
'function start() {' + | ||
'updatePassCount();' + | ||
'buildQuickLinks();' + | ||
'}' + | ||
'window.onload = start;' + | ||
'</script>' + | ||
'</head>' + | ||
'<body>' + | ||
'<h1><%= title %></h1>' + | ||
'<%= report %>' + | ||
'</body>' + | ||
'<body>' | ||
); | ||
var addReportTitle = _.template( | ||
'<h1><%= title %></h1>' | ||
); | ||
var addReportSummary = _.template( | ||
'<div id="summary" class="passed">' + | ||
'<h4>Summary</h4>' + | ||
'<ul>' + | ||
'<li id="summaryTotalSpecs">Total specs tested: </li>' + | ||
'<li id="summaryTotalFailed">Total failed: </li>' + | ||
'</ul>' + | ||
'<%= quickLinks %>' + | ||
'</div>' | ||
); | ||
var addQuickLinks = _.template( | ||
'<ul id="quickLinks"></ul>' | ||
); | ||
var closeReportTemplate = _.template( | ||
'</body>' + | ||
'</html>' | ||
); | ||
var reportTemplate = _.template( | ||
'<%= report %>' | ||
); | ||
var reasonsTemplate = _.template( | ||
@@ -98,12 +162,7 @@ '<ul>' + | ||
var summaryTemplate = _.template( | ||
'<div id="summary" class="<%= cssClass %>">' + | ||
'<h4>Summary</h4>' + | ||
'<%= summaryBody %>' + | ||
'<%= quickLinks %>' + | ||
'</div>' | ||
); | ||
var configurationTemplate = _.template( | ||
'<div id="config">' + | ||
'<a href="javascript:showhide(\'<%= configId %>\')">' + | ||
'Toggle Configuration' + | ||
'</a>' + | ||
'<div class="config" id="<%= configId %>" style="display: none">' + | ||
'<h4>Configuration</h4>' + | ||
@@ -120,12 +179,2 @@ '<%= configBody %>' + | ||
var quickLinksTemplate = _.template( | ||
'<ul id="quickLinks"><%= quickLinks %></ul>' | ||
); | ||
var quickLinkListItemTemplate = _.template( | ||
'<li>' + | ||
'<a href="#<%= specId %>"><%= specId %></a>' + | ||
'</li>' | ||
); | ||
// write data into opts.dest as filename | ||
@@ -148,3 +197,2 @@ var writeScreenshot = function (data, filename) { | ||
} | ||
}; | ||
@@ -231,2 +279,24 @@ | ||
var cleanDestination = function(callback) { | ||
// if we aren't removing the old report folder then simply return | ||
if (!opts.cleanDestination) { | ||
callback(); | ||
return; | ||
} | ||
rimraf(opts.dest, function(err) { | ||
if(err) { | ||
throw new Error('Could not remove previous destination directory ' + opts.dest); | ||
} | ||
mkdirp(opts.dest, function(err) { | ||
if(err) { | ||
throw new Error('Could not create directory ' + opts.dest); | ||
} | ||
callback(err); | ||
}); | ||
}); | ||
}; | ||
// TODO: more options | ||
@@ -244,25 +314,63 @@ opts = opts || {}; | ||
opts.totalSpecsDefined = null; | ||
opts.failedSpecs = 0; | ||
opts.showSummary = opts.showSummary || true; | ||
opts.showSummary = opts.hasOwnProperty('showSummary') ? opts.showSummary : true; | ||
opts.showQuickLinks = opts.showQuickLinks || false; | ||
opts.browserCaps = {}; | ||
opts.configurationStrings = opts.configurationStrings || {}; | ||
opts.showConfiguration = opts.showConfiguration || true; | ||
opts.reportTitle = opts.reportTitle || 'Report'; | ||
opts.showConfiguration = opts.hasOwnProperty('showConfiguration') ? opts.showConfiguration : true; | ||
opts.reportTitle = opts.hasOwnProperty('reportTitle') ? opts.reportTitle : 'Report'; | ||
opts.cleanDestination = opts.hasOwnProperty('cleanDestination') ? opts.cleanDestination : true; | ||
this.beforeLaunch = function(callback) { | ||
console.log('Report destination: ', path.join(opts.dest, opts.filename)); | ||
this.jasmineStarted = function(suiteInfo) { | ||
opts.totalSpecsDefined = suiteInfo.totalSpecsDefined; | ||
var cssLinks = getCssLinks(opts.userCss); | ||
var summaryQuickLinks = opts.showQuickLinks ? addQuickLinks(): ''; | ||
var reportSummary = opts.showSummary ? addReportSummary({ quickLinks: summaryQuickLinks }) : ''; | ||
rimraf(opts.dest, function(err) { | ||
// Now you'll need to build the replacement report text for the file. | ||
var reportContent = openReportTemplate({ userCss: cssLinks}); | ||
reportContent += addReportTitle({ title: opts.reportTitle}); | ||
reportContent += reportSummary; | ||
// Now remove the existing stored content and replace it with the new report shell. | ||
cleanDestination(function(err) { | ||
if (err) { | ||
throw err; | ||
} | ||
fs.appendFile( | ||
path.join(opts.dest, opts.filename), | ||
reportContent, | ||
{ encoding: 'utf8' }, | ||
function(err) { | ||
if (err) { | ||
console.error ('Error writing to file: ' + path.join(opts.dest, opts.filename)); | ||
throw err; | ||
} | ||
callback(); | ||
} | ||
); | ||
}); | ||
}; | ||
this.afterLaunch = function(callback) { | ||
console.log('Closing report'); | ||
fs.appendFile( | ||
path.join(opts.dest, opts.filename), | ||
closeReportTemplate(), | ||
{ encoding: 'utf8' }, | ||
function(err) { | ||
if(err) { | ||
throw new Error('Could not remove previous destination directory ' + opts.dest); | ||
console.error('Error writing to file:' + path.join(opts.dest, opts.filename)); | ||
throw err; | ||
} | ||
callback(); | ||
} | ||
); | ||
}; | ||
mkdirp(opts.dest, function(err) { | ||
if(err) { | ||
throw new Error('Could not create directory ' + opts.dest); | ||
} | ||
}); | ||
}); | ||
this.jasmineStarted = function(suiteInfo) { | ||
opts.totalSpecsDefined = suiteInfo.totalSpecsDefined; | ||
@@ -323,7 +431,2 @@ browser.getCapabilities().then(function (capabilities) { | ||
if (spec.status === 'failed') { | ||
opts.failedSpecs += 1; | ||
failedSpecIds.push(spec.id); | ||
} | ||
browser.takeScreenshot().then(function (png) { | ||
@@ -369,6 +472,2 @@ browser.getCapabilities().then(function (capabilities) { | ||
if (opts.showSummary) { | ||
output += printTestSummary(); | ||
} | ||
_.each(suites, function(suite) { | ||
@@ -383,17 +482,22 @@ output += printResults(suite); | ||
// Add configuration information when requested and only if specs have been reported. | ||
if (opts.showConfiguration) { | ||
output += printTestConfiguration(); | ||
var suiteHasSpecs = false; | ||
_.each(specs, function(spec) { | ||
suiteHasSpecs = spec.isPrinted || suiteHasSpecs; | ||
}); | ||
if (suiteHasSpecs) { | ||
output += printTestConfiguration(); | ||
} | ||
} | ||
var cssLinks = getCssLinks(opts.userCss); | ||
fs.appendFileSync( | ||
opts.dest + opts.filename, | ||
reportTemplate({ report: output, | ||
title: opts.reportTitle, | ||
userCss: cssLinks}), | ||
path.join(opts.dest, opts.filename), | ||
reportTemplate({ report: output }), | ||
{ encoding: 'utf8' }, | ||
function(err) { | ||
if(err) { | ||
console.error('Error writing to file:' + opts.dest + opts.filename); | ||
console.error('Error writing to file:' + path.join(opts.dest, opts.filename)); | ||
throw err; | ||
@@ -416,9 +520,11 @@ } | ||
return template({ | ||
browserName: opts.browserCaps.browserName, | ||
cssClass: statusCssClass[spec.status], | ||
duration: getDuration(spec), | ||
filename: encodeURI(spec.filename), | ||
id: uuid.v1(), | ||
mark: marks[spec.status], | ||
cssClass: statusCssClass[spec.status], | ||
id: spec.id, | ||
name: spec.fullName.replace(suiteName, '').trim(), | ||
reason: printReasonsForFailure(spec), | ||
filename: encodeURI(spec.filename), | ||
duration: getDuration(spec), | ||
specId: spec.id, | ||
}); | ||
@@ -464,30 +570,2 @@ } | ||
function printTestSummary() { | ||
var summary = { | ||
"Total specs": opts.totalSpecsDefined, | ||
"Failed specs": opts.failedSpecs | ||
}; | ||
var cssClass = opts.failedSpecs > 0 ? statusCssClass["failed"] : statusCssClass["passed"]; | ||
var keys = Object.keys(summary); | ||
var summaryOutput = ""; | ||
_.each(keys, function(key) { | ||
summaryOutput += objectToItemTemplate({"key": key, "value": summary[key]}); | ||
}); | ||
var quickLinks = opts.showQuickLinks ? printFailedSpecQuickLinks() : ''; | ||
return summaryTemplate({"summaryBody": summaryOutput, "cssClass": cssClass, "quickLinks": quickLinks}); | ||
} | ||
function printFailedSpecQuickLinks() { | ||
var quickLinksOutput = ""; | ||
_.each(failedSpecIds, function(id) { | ||
quickLinksOutput += quickLinkListItemTemplate({specId: id}); | ||
}); | ||
return quickLinksTemplate({quickLinks: quickLinksOutput}); | ||
} | ||
function printTestConfiguration() { | ||
@@ -512,3 +590,4 @@ var testConfiguration = { | ||
return configurationTemplate({"configBody": configOutput}); | ||
var configId = uuid.v1(); | ||
return configurationTemplate({"configBody": configOutput, "configId": configId}); | ||
} | ||
@@ -515,0 +594,0 @@ |
{ | ||
"name": "protractor-jasmine2-screenshot-reporter", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "Use the screenshot reporter to capture screenshots after each executed Protractor test case.", | ||
@@ -15,3 +15,4 @@ "main": "index.js", | ||
"rimraf": "^2.4.3", | ||
"string.prototype.startswith": "^0.2.0" | ||
"string.prototype.startswith": "^0.2.0", | ||
"uuid": "^2.0.0" | ||
}, | ||
@@ -18,0 +19,0 @@ "keywords": [ |
102
README.md
@@ -16,12 +16,27 @@ ## Protractor screenshot reporter for Jasmine2 | ||
var reporter = new HtmlScreenshotReporter({ | ||
dest: 'target/screenshots', | ||
filename: 'my-report.html' | ||
}); | ||
exports.config = { | ||
// ... | ||
// Setup the report before any tests start | ||
beforeLaunch: function() { | ||
return new Promise(function(resolve){ | ||
reporter.beforeLaunch(resolve); | ||
}); | ||
}, | ||
// Assign the test reporter to each running instance | ||
onPrepare: function() { | ||
jasmine.getEnv().addReporter( | ||
new HtmlScreenshotReporter({ | ||
dest: 'target/screenshots', | ||
filename: 'my-report.html' | ||
}) | ||
); | ||
jasmine.getEnv().addReporter(reporter); | ||
}, | ||
// Close the report after all tests finish | ||
afterLaunch: function(exitCode) { | ||
return new Promise(function(resolve){ | ||
reporter.afterLaunch(resolve.bind(this, exitCode)); | ||
}); | ||
} | ||
@@ -37,6 +52,22 @@ }</code></pre> | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
dest: '/project/test/screenshots' | ||
}));</code></pre> | ||
});</code></pre> | ||
### Clean destination directory (optional) | ||
This option is __enabled by default__. Toggle whether or not to remove and rebuild destination when jasmine starts. | ||
This is useful when you are running protractor tests in parallel, and wish all of the processes to report to the same directory. | ||
When cleanDestination is set to true, it is recommended that you disabled showSummary and showConfiguration, and set reportTitle to null. If you do not, the report will be pretty cluttered. | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
cleanDestination: false, | ||
showSummary: false, | ||
showConfiguration: false, | ||
reportTitle: null | ||
});</code></pre> | ||
### Filename (optional) | ||
@@ -46,5 +77,5 @@ | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
filename: 'my-report.html' | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -57,5 +88,5 @@ Default is <code>report.html</code> | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
userCss: 'my-report-styles.css' | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -66,5 +97,5 @@ ### Ignore pending specs (optional) | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
ignoreSkippedSpecs: true | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -77,5 +108,5 @@ Default is <code>false</code> | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
captureOnlyFailedSpecs: true | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -88,6 +119,6 @@ Default is <code>false</code> | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
reportOnlyFailedSpecs: false, | ||
captureOnlyFailedSpecs: true | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -98,5 +129,5 @@ ### Display summary in report (optional) | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
showSummary: true | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -109,6 +140,6 @@ Default is <code>true</code> | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
showSummary: true, | ||
showQuickLinks: true | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -121,5 +152,5 @@ Default is <code>false</code> | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
showConfiguration: true | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -132,5 +163,5 @@ Default is <code>true</code> | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
reportTitle: "Report Title" | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -143,3 +174,3 @@ Default is <code>'Report'</code> | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
configurationStrings: { | ||
@@ -149,3 +180,3 @@ "My 1st Param": firstParam, | ||
} | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -156,3 +187,3 @@ ### Path Builder (optional) | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
pathBuilder: function(currentSpec, suites, browserCapabilities) { | ||
@@ -162,3 +193,3 @@ // will return chrome/your-spec-name.png | ||
} | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -172,7 +203,7 @@ By default, the path builder will generate a random ID for each spec. | ||
<pre><code>jasmine.getEnv().addReporter(new ScreenShotReporter({ | ||
<pre><code>var reporter = new ScreenShotReporter({ | ||
metadataBuilder: function(currentSpec, suites, browserCapabilities) { | ||
return { id: currentSpec.id, os: browserCapabilities.get('browserName') }; | ||
} | ||
}));</code></pre> | ||
});</code></pre> | ||
@@ -185,6 +216,5 @@ By default, the runner builder will not save any metadata except the actual html report. | ||
created separate directory with unique name. Directory unique name will be generated randomly. | ||
<pre><code>jasmine.getEnv().addReporter(new HtmlScreenshotReporter({ | ||
<pre><code>var reporter = new HtmlScreenshotReporter({ | ||
preserveDirectory: true | ||
}));</code></pre> | ||
});</code></pre> |
31522
8
487
204
6
+ Addeduuid@^2.0.0
+ Addeduuid@2.0.3(transitive)