Skip to content
This repository was archived by the owner on Sep 21, 2022. It is now read-only.

Commit 6219b74

Browse files
committed
feat(html-reporter): add ability to copy suite name and open suite urls
1 parent 74168cd commit 6219b74

12 files changed

Lines changed: 167 additions & 22 deletions

File tree

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ node_modules
33
/coverage
44
lib/coverage/tinytable.sorter.js
55
lib/browser/client-scripts/gemini.calibrate.min.js
6+
/lib/reporters/html/static/report.min.js
67
/_book

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ build
1818
node_modules
1919
/coverage
2020
/lib/browser/client-scripts/gemini.calibrate.min.js
21+
/lib/reporters/html/static/report.min.js
2122
/_book

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ test/
33
coverage/
44
CONTRIBUTING.md
55
/lib/browser/client-scripts/gemini.calibrate.js
6+
/lib/reporters/html/static/report.js
67
/_book
78
/book.json
89
/.bookignore

lib/reporters/html/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function makeDirFor(destPath) {
4040

4141
function prepareViewData(runner) {
4242
return new Promise((resolve) => {
43-
var model = new ViewModel();
43+
var model = new ViewModel(runner.config);
4444

4545
runner.on(Events.SKIP_STATE, model.addSkipped.bind(model));
4646

lib/reporters/html/static/report.css

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,39 @@
127127
display: none;
128128
}
129129

130+
.section__icon {
131+
display: inline-block;
132+
width: 19px;
133+
height: 19px;
134+
vertical-align: top;
135+
padding: 0 3px;
136+
border: none;
137+
opacity: 0.5;
138+
cursor: pointer;
139+
}
140+
141+
.section__icon:hover {
142+
opacity: 1;
143+
}
144+
145+
.section__icon:before {
146+
display: block;
147+
width: 100%;
148+
height: 100%;
149+
content: '';
150+
background-repeat: no-repeat;
151+
background-size: 100%;
152+
background-position: center;
153+
}
154+
155+
.section__icon_view-local:before {
156+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgLTAuNzA5IC0zMi4wODEgMTQxLjczMiAxNDEuNzMyIiBoZWlnaHQ9IjE0MS43MzJweCIgaWQ9IkxpdmVsbG9fMSIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSItMC43MDkgLTMyLjA4MSAxNDEuNzMyIDE0MS43MzIiIHdpZHRoPSIxNDEuNzMycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxnIGlkPSJMaXZlbGxvXzgwIj48cGF0aCBkPSJNODkuNjY4LDM4Ljc4NmMwLTEwLjc3My04LjczMS0xOS41MTItMTkuNTEtMTkuNTEyUzUwLjY0NiwyOC4wMSw1MC42NDYsMzguNzg2YzAsMTAuNzc0LDguNzMyLDE5LjUxMSwxOS41MTIsMTkuNTExICAgQzgwLjkzNCw1OC4yOTcsODkuNjY4LDQ5LjU2MSw4OS42NjgsMzguNzg2IE0xMjguMzUyLDM4LjcyN2MtMTMuMzE1LDE3LjU5OS0zNC40MjYsMjguOTcyLTU4LjE5MywyOC45NzIgICBjLTIzLjc3LDAtNDQuODc5LTExLjM3My01OC4xOTQtMjguOTcyQzI1LjI3OSwyMS4xMjksNDYuMzg5LDkuNzU2LDcwLjE1OCw5Ljc1NkM5My45MjcsOS43NTYsMTE1LjAzNiwyMS4xMjksMTI4LjM1MiwzOC43MjcgICAgTTE0MC4zMTQsMzguNzZDMTI1LjY2NiwxNS40NzgsOTkuNzI1LDAsNzAuMTU4LDBTMTQuNjQ4LDE1LjQ3OCwwLDM4Ljc2YzE0LjY0OCwyMy4zMTIsNDAuNTkxLDM4LjgxLDcwLjE1OCwzOC44MSAgIFMxMjUuNjY2LDYyLjA3MiwxNDAuMzE0LDM4Ljc2Ii8+PC9nPjxnIGlkPSJMaXZlbGxvXzFfMV8iLz48L3N2Zz4=);
157+
}
158+
159+
.section__icon_copy-to-clipboard:before {
160+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUxMiA1MTIiIGhlaWdodD0iNTEycHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwYXRoIGQ9Ik00NjguNDkzLDEwMS42MzdMMzcxLjk1NSw1LjA5OEgxNTkuNTd2NzcuMjMxSDQzLjcyNHY0MjQuNzY5aDMwOC45MjN2LTc3LjIzMWgxMTUuODQ2VjEwMS42Mzd6ICAgTTM3MS45NTUsMzIuNDAxbDY5LjIzNiw2OS4yMzVoLTY5LjIzNlYzMi40MDF6IE02My4wMzEsNDg3Ljc5VjEwMS42MzdoMTczLjc2OXY5Ni41MzhoOTYuNTM4VjQ4Ny43OUg2My4wMzF6IE0yNTYuMTA4LDEwOS42MzIgIGw2OS4yMzYsNjkuMjM1aC02OS4yMzZWMTA5LjYzMnogTTM1Mi42NDcsNDEwLjU2VjE3OC44NjdsLTk2LjUzOC05Ni41MzhoLTc3LjIzMVYyNC40MDZoMTczLjc2OXY5Ni41MzhoOTYuNTM4VjQxMC41NkgzNTIuNjQ3eiIgZmlsbD0iIzM3NDA0RCIvPjwvc3ZnPg==);
161+
}
162+
130163
.section_collapsed .section__title:before,
131164
.meta-info_collapsed .meta-info__switcher:before {
132165
-webkit-transform: rotate(-90deg);
@@ -246,3 +279,11 @@ a:active {
246279
.meta-info_collapsed .meta-info__content {
247280
display: none;
248281
}
282+
283+
.text-input {
284+
outline: 0;
285+
line-height: 14px;
286+
padding: 2px 5px;
287+
border: 1px solid #ccc;
288+
border-radius: 2px;
289+
}

lib/reporters/html/static/report.js

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,28 @@
33
'use strict';
44

55
var forEach = Array.prototype.forEach,
6-
filter = Array.prototype.filter;
6+
filter = Array.prototype.filter,
7+
sections = document.querySelectorAll('.section'),
8+
url = require('url'),
9+
Clipboard = require('clipboard');
710

811
function expandAll() {
912
loadLazyImages(document, '.section_collapsed img');
10-
forEach.call(document.querySelectorAll('.section'), function(section) {
13+
forEach.call(sections, function(section) {
1114
section.classList.remove('section_collapsed');
1215
});
1316
}
1417

1518
function collapseAll() {
16-
forEach.call(document.querySelectorAll('.section'), function(section) {
19+
forEach.call(sections, function(section) {
1720
section.classList.add('section_collapsed');
1821
});
1922
}
2023

2124
function expandErrors() {
2225
loadLazyImages(document, '.section_status_fail > .section__body > .image-box img');
2326
loadLazyImages(document, '.section_status_warning > .section__body > .image-box img');
24-
forEach.call(document.querySelectorAll('.section'), function(section) {
27+
forEach.call(sections, function(section) {
2528
if (section.classList.contains('section_status_fail') ||
2629
section.classList.contains('section_status_warning')) {
2730
section.classList.remove('section_collapsed');
@@ -33,7 +36,7 @@
3336

3437
function expandRetries() {
3538
loadLazyImages(document, '.has-retries > .section__body > .image-box img');
36-
forEach.call(document.querySelectorAll('.section'), function(section) {
39+
forEach.call(sections, function(section) {
3740
if (section.classList.contains('has-retries')) {
3841
section.classList.remove('section_collapsed');
3942
} else {
@@ -125,6 +128,57 @@
125128
document.getElementById('skippedList').classList.toggle('collapsed');
126129
}
127130

131+
function handleHostChange() {
132+
var textInput = document.getElementById('viewHostInput');
133+
var viewButtons = document.querySelectorAll('.section__icon_view-local');
134+
135+
textInput.addEventListener('change', function() {
136+
setViewLinkHost(textInput.value);
137+
// will save host to local storage
138+
if (window.localStorage) {
139+
window.localStorage.setItem('_gemini-replace-host', textInput.value);
140+
}
141+
});
142+
143+
// read saved host from local storage
144+
if (window.localStorage) {
145+
var host = window.localStorage.getItem('_gemini-replace-host');
146+
if (host) {
147+
setViewLinkHost(host);
148+
textInput.value = host;
149+
}
150+
}
151+
152+
function setViewLinkHost(host) {
153+
viewButtons.forEach(function(item) {
154+
var href = item.dataset.suiteViewLink,
155+
parsedHost;
156+
157+
if (host) {
158+
parsedHost = url.parse(host, false, true);
159+
// extending current url from entered host
160+
href = url.format(Object.assign(
161+
url.parse(href),
162+
{
163+
host: parsedHost.slashes ? parsedHost.host : host,
164+
protocol: parsedHost.slashes ? parsedHost.protocol : null,
165+
hostname: null,
166+
port: null
167+
}
168+
));
169+
}
170+
item.setAttribute('href', href);
171+
});
172+
}
173+
}
174+
175+
function handleClipboard() {
176+
forEach.call(document.querySelectorAll('.section__icon_copy-to-clipboard'), function(clipboard) {
177+
/* eslint-disable no-new */
178+
new Clipboard(clipboard);
179+
});
180+
}
181+
128182
document.addEventListener('DOMContentLoaded', function() {
129183
document.getElementById('expandAll').addEventListener('click', expandAll);
130184
document.getElementById('collapseAll').addEventListener('click', collapseAll);
@@ -140,7 +194,16 @@
140194
section.classList.toggle('section_collapsed');
141195
});
142196
});
197+
198+
// turning off event bubbling when click button
199+
forEach.call(document.querySelectorAll('.button'), function(button) {
200+
button.addEventListener('click', function(e) {
201+
e.stopPropagation();
202+
});
203+
});
143204
});
144205

206+
handleClipboard();
207+
handleHostChange();
145208
expandErrors();
146209
}());

lib/reporters/html/templates/report.hbs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<button id="showSkipped" class="button">Show skipped</button>
2121
<button id="showRetries" class="button">Expand retries</button>
2222
<button id="showOnlyDiff" class="button">Show only diff</button>
23+
<input id="viewHostInput" class="text-input" size="40" placeholder="change original host for view in browser"/>
2324

2425
<div id="skippedList" class="skipped__list collapsed">
2526
{{#if skips}}
@@ -36,6 +37,6 @@
3637
{{> suite}}
3738
{{/each}}
3839

39-
<script src="report.js"></script>
40+
<script src="report.min.js"></script>
4041
</body>
4142
</html>

lib/reporters/html/templates/suite.hbs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<div class="section {{collapse}} {{section-status}} {{has-retries}}">
2-
<div class="section__title">{{name}}</div>
2+
<div class="section__title">
3+
{{name}}
4+
{{#if suitePath}}
5+
<button
6+
class="button section__icon section__icon_copy-to-clipboard"
7+
title="copy to clipboard"
8+
data-clipboard-text="{{suitePath}}">
9+
</button>
10+
{{/if}}
11+
</div>
312
<div class="section__body section__body_guided">
413
{{#each browsers}}
514
<div class="section section_collapsed {{section-status}} {{has-retries}}">
@@ -12,7 +21,15 @@
1221
{{/unless}}
1322
{{/if}}
1423
{{#unless result.skipped}}
15-
<div class="section__title">{{name}}</div>
24+
<div class="section__title">
25+
{{name}}
26+
<a
27+
class="button section__icon section__icon_view-local"
28+
data-suite-view-link="{{result.suiteUrl}}" href="{{result.suiteUrl}}"
29+
title="view in browser"
30+
target="_blank">
31+
</a>
32+
</div>
1633
<div class="section__body">
1734
<div class="image-box {{image-box-status}}">
1835
<div class="cswitcher">

lib/reporters/html/view-model.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ const TestCounter = require('../../test-counter');
77
const NO_STATE = 'NO_STATE';
88

99
module.exports = class ViewModel {
10-
constructor() {
10+
constructor(config) {
1111
this._tree = {name: 'root'};
1212
this._skips = [];
1313
this._counter = new TestCounter();
14+
this._config = config;
1415
}
1516

1617
/**
@@ -109,15 +110,19 @@ module.exports = class ViewModel {
109110
}
110111

111112
_addTestResult(result, props) {
112-
const metaInfo = result.suite ? result.suite.metaInfo : {};
113+
const browserId = result.browserId;
114+
const rootUrl = this._getRootUrlforBrowser(browserId);
115+
const suite = result.suite;
116+
const metaInfo = suite ? suite.metaInfo : {};
113117
metaInfo.sessionId = result.sessionId || 'unknown session id';
114118

115119
const testResult = _.assign({
116-
name: result.browserId,
120+
suiteUrl: rootUrl + suite.url,
121+
name: browserId,
117122
metaInfo: JSON.stringify(metaInfo, null, 4) || 'Meta info is not available'
118123
}, props);
119-
const node = findOrCreate(this._tree, result.suite.path.concat(result.state ? result.state.name : NO_STATE));
120124

125+
const node = findOrCreate(this._tree, suite.path.concat(result.state ? result.state.name : NO_STATE));
121126
node.browsers = Array.isArray(node.browsers) ? node.browsers : [];
122127

123128
const existing = _.findIndex(node.browsers, {name: testResult.name});
@@ -137,6 +142,10 @@ module.exports = class ViewModel {
137142
stateInBrowser.result = testResult;
138143
}
139144

145+
_getRootUrlforBrowser(browserId) {
146+
return this._config.forBrowser(browserId).rootUrl;
147+
}
148+
140149
static hasFails(node) {
141150
return walk(node, ViewModel.hasFails, node.result && (node.result.error || node.result.fail));
142151
}
@@ -170,6 +179,7 @@ function findOrCreate(node, statePath) {
170179
node.children = Array.isArray(node.children) ? node.children : [];
171180

172181
const pathPart = statePath.shift();
182+
node.suitePath = node.suitePath || [];
173183

174184
if (pathPart === NO_STATE) {
175185
return node;
@@ -178,7 +188,10 @@ function findOrCreate(node, statePath) {
178188
let child = _.find(node.children, {name: pathPart});
179189

180190
if (!child) {
181-
child = {name: pathPart};
191+
child = {
192+
name: pathPart,
193+
suitePath: node.suitePath.concat(pathPart)
194+
};
182195
node.children.push(child);
183196
}
184197

lib/reporters/html/view.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,10 @@ module.exports = {
9393
*/
9494
save: function(html) {
9595
return fs.mkdirsAsync(REPORT_DIR)
96-
.then(function() {
97-
return Promise.all([
98-
fs.writeFileAsync(makeOutFilePath('index.html'), html, 'utf8'),
99-
copyToReportDir('report.js'),
100-
copyToReportDir('report.css')
101-
]);
102-
});
96+
.then(() => Promise.all([
97+
fs.writeFileAsync(makeOutFilePath('index.html'), html, 'utf8'),
98+
copyToReportDir('report.min.js'),
99+
copyToReportDir('report.css')
100+
]));
103101
}
104102
};

0 commit comments

Comments
 (0)