Skip to content

Commit 3f364bd

Browse files
committed
Added Feature to Generate HTML reports.
1 parent 6f5975a commit 3f364bd

File tree

9 files changed

+418
-39
lines changed

9 files changed

+418
-39
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules
2-
.idea
2+
.idea
3+
gallery

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ Utilities for Protractor with jasmine2 [Screenshot, Browser Console log and more
1010
1. This plugin can take a screenshot for each Jasmine2 expect failure
1111
2. It can take screenshot for each spec failure as well
1212
3. It can fail your spec/test if browser console has errors
13-
4. TODO: It can output browser console logs on failure(Done) or always(TODO) :)
13+
4. It can generate beautiful readable HTML reports
14+
5. TODO: It can output browser console logs on failure(Done) or always(TODO) :)
1415

1516
# Usage
1617

@@ -29,6 +30,8 @@ exports.config = {
2930
screenshotOnExpectFailure: {Boolean} (Default - false),
3031
screenshotOnSpecFailure: {Boolean} (Default - false),
3132
screenshotPath: {String} (Default - 'reports/screenshots')
33+
clearFoldersBeforeTest: {Boolean} (Default - false),
34+
htmlReportDir: {String} (Default - './reports/htmlReports')
3235
failTestOnErrorLog: {
3336
failTestOnErrorLogLevel: {Number}, (Default - 900)
3437
excludeKeywords: {A JSON Array}
@@ -97,6 +100,19 @@ If not present , please add the following to the config file:
97100

98101
Default: 'reports/screenshots'
99102

103+
## clearFoldersBeforeTest
104+
105+
If this flag set to true, screenshot and html report directories will be emptied before generating new reports and screenshots
106+
107+
Default: false
108+
109+
## htmlReportDir
110+
111+
Path where HTML report will be saved. If path does not exist , will be created.
112+
e.g './reports/something/savehere/' , please take care of './' and '/' at the beginning and end.
113+
114+
Default: 'reports/htmlReports'
115+
100116
## failTestOnErrorLog (Chrome only)
101117

102118
Contains a set of configuration for console log. When browser console has errors of a certain log level (default:>900), the spec/test is marked failed along with log in the error report/stacktrace.

index.js

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
var q = require('q');
22
var fs = require('fs');
3+
var fse = require('fs-extra');
34
var mkdirp = require('mkdirp');
5+
var jasmine2Reporter = require('./reporter/jasmine2_reporter.js');
46

57
/**
68
* This plugin does few things:
79
* 1. Takes a screenshot for each jasmine expect/matcher failure
810
* 2. Takes a screenshot for each test/spec failure
9-
* 3. Marks the test as failure if browser console log has error - Chrome only //TODO
11+
* 3. Genrates a HTML report
12+
* 4. Marks the test as failure if browser console log has error - Chrome only //TODO
1013
*
1114
* exports.config = {
1215
* plugins: [{
13-
* path: 'node_modules/jasmine2-protractor-utils/index.js',
14-
* screenshotOnExpectFailure: {Boolean} (Default - false),
15-
* screenshotOnSpecFailure: {Boolean} (Default - false),
16-
* screenshotPath: {String} (Default - 'reports/screenshots')
17-
* }]
16+
* package: 'jasmine2-protractor-utils',
17+
* screenshotOnExpectFailure: {Boolean} (Default - false),
18+
* screenshotOnSpecFailure: {Boolean} (Default - false),
19+
* screenshotPath: {String} (Default - 'reports/screenshots')
20+
* clearFoldersBeforeTest: {Boolean} (Default - false),
21+
* htmlReportDir: {String} (Default - './reports/htmlReports')
22+
* failTestOnErrorLog: {
23+
* failTestOnErrorLogLevel: {Number}, (Default - 900)
24+
* excludeKeywords: {A JSON Array}
25+
* }
26+
* }]
1827
* };
1928
* @author Abhishek Swain
2029
* @blog www.qaautomationsimplified.com
@@ -88,6 +97,24 @@ protractorUtil.takeScreenshotOnSpecFail = function (context) {
8897
}
8998
};
9099

100+
/**
101+
* Generates HTML report for tests
102+
*
103+
* @param {Object} context The plugin context object
104+
* @return {!webdriver.promise.Promise.<R>} A promise
105+
*/
106+
protractorUtil.generateHTMLReport = function (context) {
107+
108+
return global.browser.getProcessedConfig().then(function (config) {
109+
110+
if (context.config.htmlReportDir) {
111+
return global.browser.getProcessedConfig().then(function (config) {
112+
jasmine.getEnv().addReporter(new jasmine2Reporter(context.config.htmlReportDir));
113+
});
114+
}
115+
});
116+
};
117+
91118
/**
92119
* Fails the test/spec if browser has console logs
93120
*
@@ -179,17 +206,53 @@ protractorUtil.prototype.setup = function () {
179206
}
180207
}
181208
else {
182-
209+
if (this.config.clearFoldersBeforeTest) {
210+
try {
211+
fse.removeSync(this.config.screenshotPath);
212+
} catch (err) {
213+
console.error(err);
214+
}
215+
}
183216

184217
mkdirp.sync(this.config.screenshotPath, function (err) {
185218
if (err) console.error(err);
186219
else console.log(self.config.screenshotPath + ' folder created!');
187220
});
188221
}
189222

223+
224+
if (!this.config.htmlReportDir) {
225+
//creates reports folder if does not exist
226+
var reportsDir = './reports';
227+
if (!fs.existsSync(reportsDir)) {
228+
fs.mkdirSync(reportsDir);
229+
}
230+
231+
//creates htmlReports folder if does not exist
232+
var htmlReportsDir = './reports/htmlReports';
233+
if (!fs.existsSync(htmlReportsDir)) {
234+
fs.mkdirSync(htmlReportsDir);
235+
}
236+
}
237+
else {
238+
if (this.config.clearFoldersBeforeTest) {
239+
try {
240+
fse.removeSync(this.config.htmlReportDir);
241+
} catch (err) {
242+
console.error(err);
243+
}
244+
}
245+
246+
mkdirp.sync(this.config.htmlReportDir, function (err) {
247+
if (err) console.error(err);
248+
else console.log(self.config.htmlReportDir + ' folder created!');
249+
});
250+
}
251+
190252
protractorUtil.takeScreenshotOnExpectFail(this);
191253
protractorUtil.takeScreenshotOnSpecFail(this);
192254
protractorUtil.failTestOnErrorLog(this);
255+
protractorUtil.generateHTMLReport(this);
193256

194257
};
195258

package.json

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,33 @@
11
{
2-
"name": "jasmine2-protractor-utils",
3-
"version": "1.1.2",
4-
"description": "Utilities for Protractor with jasmine2 [Screenshot, Browser Console log and more]",
5-
"main": "index.js",
6-
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
8-
},
9-
"repository": {
10-
"type": "git",
11-
"url": "git+https://github.com/abhishekswain/jasmine2-protractor-utils.git"
12-
},
13-
"keywords": [
14-
"jasmine2",
15-
"Protractor",
16-
"protractor-screenshot",
17-
"screenshot-on-failure",
18-
"protractor-utilities",
19-
"browser-console-log",
20-
"protractor-spec-fail-on-console-error"
21-
],
22-
"author": "Abhishek Swain",
23-
"license": "MIT",
24-
"bugs": {
25-
"url": "https://github.com/abhishekswain/jasmine2-protractor-utils/issues"
26-
},
27-
"homepage": "https://github.com/abhishekswain/jasmine2-protractor-utils#readme",
28-
"dependencies": {
29-
"mkdirp": "^0.5.1",
30-
"q": "^1.4.1"
31-
}
2+
"name": "jasmine2-protractor-utils",
3+
"version": "1.2.0",
4+
"description": "Utilities for Protractor with jasmine2 [HTML Reports, Screenshot, Browser Console log and more]",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/abhishekswain/jasmine2-protractor-utils.git"
12+
},
13+
"keywords": [
14+
"jasmine2",
15+
"Protractor",
16+
"protractor-screenshot",
17+
"screenshot-on-failure",
18+
"protractor-utilities",
19+
"browser-console-log",
20+
"protractor-spec-fail-on-console-error"
21+
],
22+
"author": "Abhishek Swain",
23+
"license": "MIT",
24+
"bugs": {
25+
"url": "https://github.com/abhishekswain/jasmine2-protractor-utils/issues"
26+
},
27+
"homepage": "https://github.com/abhishekswain/jasmine2-protractor-utils#readme",
28+
"dependencies": {
29+
"mkdirp": "^0.5.1",
30+
"q": "^1.4.1",
31+
"fs-extra": "^0.26.5"
32+
}
3233
}

report/css/style.css

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
table {
2+
width: 100%;
3+
border-collapse: separate;
4+
margin:50px auto;
5+
}
6+
7+
th {
8+
background: #3498db;
9+
color: white;
10+
font-weight: bold;
11+
}
12+
13+
td, th {
14+
padding: 10px;
15+
border: 1px solid #ccc;
16+
text-align: left;
17+
font-size: 18px;
18+
vertical-align: text-top;
19+
20+
}
21+
22+
.labels tr td {
23+
background-color: #2cc16a;
24+
font-weight: bold;
25+
color: #fff;
26+
}
27+
28+
.labelsFail tr td {
29+
background-color: red;
30+
font-weight: bold;
31+
color: #fff;
32+
}
33+
34+
35+
.label tr td label {
36+
display: block;
37+
}
38+
39+
40+
[data-toggle="toggle"] {
41+
display: none;
42+
}
43+
44+
div{
45+
word-break: break-all;
46+
max-width: 1500px;
47+
}

report/index.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Protractor Reports</title>
6+
<link rel="stylesheet" href="css/style.css">
7+
<script type="text/javascript" src="data.json"></script>
8+
<script type="text/javascript" src="loadResult.js"></script>
9+
<script src="js/index.js"></script>
10+
11+
</head>
12+
13+
<body onload="loadContent();">
14+
15+
<table id="1">
16+
</table>
17+
</body>
18+
</html>

report/js/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
activateToggle=function(element) {
2+
3+
var tidNo = element.getAttribute('tid');
4+
var eles = document.querySelectorAll('[class="hide"][tid="'+tidNo+'"]');
5+
6+
for (var i = 0; i < eles.length; ++i) {
7+
eles[i].style.display = eles[i].style.display === 'none' ? '' : 'none';
8+
}
9+
};

report/loadResult.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
var loadContent = function () {
2+
3+
try {
4+
5+
var resultString = JSON.stringify(data);
6+
resultJSON = JSON.parse(resultString);
7+
8+
var getTimeDiff = function (start, end) {
9+
10+
var date1 = new Date(start);
11+
var date2 = new Date(end);
12+
13+
var diff = date2.getTime() - date1.getTime();
14+
15+
var msec = diff;
16+
var hh = Math.floor(msec / 1000 / 60 / 60);
17+
msec -= hh * 1000 * 60 * 60;
18+
var mm = Math.floor(msec / 1000 / 60);
19+
msec -= mm * 1000 * 60;
20+
var ss = Math.floor(msec / 1000);
21+
msec -= ss * 1000;
22+
23+
if (hh !== 0)
24+
return(hh + " hours " + mm + " mins " + ss + " secs");
25+
else if (hh === 0 && mm !== 0)
26+
return(mm + " mins " + ss + " secs");
27+
else
28+
return(ss + " secs");
29+
};
30+
31+
32+
var headerTemplate = '<thead>\n <tr>\n <th>Description</th>\n <th>Status</th>\n <th>Time</th>\n <th>Total Specs</th>\n <th>Failed</th>\n <th>Passed</th>\n </tr>\n </thead>';
33+
var suiteTemplate = '<tbody onclick="activateToggle(this);" tid="suiteID" class="labels">\n<tr>\n <td colspan="1">\n <label tid="suiteID" >suiteName</label> \n </td>\n <td colspan="1">\n<label>suiteStatus</label>\n</td>\n <td colspan="1">\n<label>suiteDuration</label>\n</td>\n <td colspan="1">\n<label>suiteTotal</label>\n</td>\n <td colspan="1">\n <label>suiteFailedSpecs</label>\n </td>\n <td colspan="1">\n <label>suitePassedSpecs</label>\n</td>\n </tr>\n<tr class="hide" style="display:none;" tid="suiteID" >\n <td colspan="1">\n<label>Spec</label>\n</td>\n<td colspan="1">\n<label>Status</label>\n</td>\n<td colspan="1">\n<label>Passed Assertions</label>\n</td>\n<td colspan="1">\n<label>Failed Assertions</label>\n</td>\n<td colspan="1">\n<label>Duration</label>\n</td>\n<td colspan="1">\n<label>Screenshots</label>\n</td>\n</tr>\n</tbody>'
34+
35+
var specTemplate0 = '<tbody class="hide" style="display:none;" tid="suiteID" tid="suiteID">\n <tr>\n <td>specName</td>\n <td>\n <div>specStatus</div>\n </td>';
36+
var passedSpecsTemplate = '<td>\n<div>passedExpectationNumber</div>\n</td>';
37+
var failedSpecsTemplate = '<td>\n <div>failedSpecsList</div>\n </td>';
38+
var specTemplateLast = '<td>specTime</td>\n <td>Please check screenshots folder!</td>\n </tr>\n </tbody>';
39+
40+
var all = '';
41+
var specAll = '';
42+
43+
resultJSON.suites.forEach(function (suite, index) {
44+
45+
var tempsuiteTemplate = suiteTemplate.replace('suiteName', suite.fullName).replace('suiteStatus', suite.testStatus).replace('suiteDuration', getTimeDiff(suite.startTime, suite.endTime)).replace('suiteTotal', suite.specs.length).replace('suiteFailedSpecs', JSON.stringify(suite.specs).split('"status":"failed"').length - 1).replace('suitePassedSpecs', JSON.stringify(suite.specs).split('"status":"passed"').length - 1);
46+
47+
if (suite.testStatus === 'Fail') {
48+
tempsuiteTemplate = tempsuiteTemplate.replace('labels', 'labelsFail');
49+
}
50+
var tempspecAll = '';
51+
suite.specs.forEach(function (spec, specindex) {
52+
specTemplate0 = '<tbody class="hide" style="display:none;" tid="suiteID" tid="suiteID">\n <tr>\n <td>specName</td>\n <td>\n <div>specStatus</div>\n </td>';
53+
specTemplate0 = specTemplate0.replace('specName', spec.description).replace('specStatus', spec.status);
54+
passedSpecsTemplate = '<td>\n<div>passedExpectationNumber</div>\n</td>';
55+
passedSpecsTemplate = passedSpecsTemplate.replace('passedExpectationNumber', spec.passedExpectations.length);
56+
var failedExpectAll = '';
57+
58+
59+
spec.failedExpectations.forEach(function (expect, expectindex) {
60+
var tempFailed = '<div><b><i style="color: red">Serial: </i></b>' + (expectindex + 1) + '</div>' + '<div><b><i style="color: red">Message:</i></b> ' + expect.message + '</div>' + '<div><b><i style="color: red">StackTrace:</i></b> ' + expect.stack + '</div><br>';
61+
failedExpectAll = failedExpectAll + tempFailed;
62+
});
63+
64+
failedSpecsTemplate = '<td>\n <div>failedSpecsList</div>\n </td>';
65+
66+
if (spec.failedExpectations.length === 0) {
67+
failedSpecsTemplate = failedSpecsTemplate.replace('failedSpecsList', 0);
68+
} else {
69+
failedSpecsTemplate = failedSpecsTemplate.replace('failedSpecsList', failedExpectAll);
70+
}
71+
specTemplateLast = '<td>specTime</td>\n <td>Please check screenshots folder!</td>\n </tr>\n </tbody>';
72+
specTemplateLast = specTemplateLast.replace('specTime', getTimeDiff(spec.startTime, spec.endTime));
73+
74+
tempspecAll = tempspecAll + specTemplate0 + passedSpecsTemplate + failedSpecsTemplate + specTemplateLast;
75+
76+
});
77+
all = all + tempsuiteTemplate + tempspecAll;
78+
all = all.split('"suiteID"').join(index);
79+
});
80+
81+
82+
document.getElementById('1').innerHTML = headerTemplate + all;
83+
} catch (err) {
84+
console.log('Error While Loading Contents!!'+err);
85+
alert('Error While Loading Contents!! : '+err);
86+
}
87+
};
88+
89+

0 commit comments

Comments
 (0)