@percy/report 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +70 -0
- package/bin/cli.js +34 -0
- package/package.json +54 -0
- package/src/generate.js +191 -0
- package/src/html-render.js +65 -0
- package/src/index.js +2 -0
- package/src/response-parser.js +44 -0
- package/src/summary.js +138 -0
- package/src/template/app-report.html +180 -0
- package/src/template/app-summary.html +165 -0
- package/src/template/report.html +221 -0
- package/src/template/summary.html +176 -0
package/src/summary.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const { Axios } = require('axios')
|
|
2
|
+
const { startOfDay, startOfWeek, endOfDay, endOfWeek, isAfter, isBefore,intervalToDuration } = require('date-fns')
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const { HtmlSummary } = require('./html-render')
|
|
5
|
+
const defaultOptions = {
|
|
6
|
+
percyToken: process.env.PERCY_TOKEN,
|
|
7
|
+
apiUrl: 'https://percy.io/api/v1',
|
|
8
|
+
}
|
|
9
|
+
module.exports.Summary = async function (opts) {
|
|
10
|
+
let { percyToken, startDate, endDate, projectSlug, apiUrl } = Object.assign(defaultOptions, opts)
|
|
11
|
+
startDate = new Date(startDate)
|
|
12
|
+
endDate = new Date(endDate)
|
|
13
|
+
|
|
14
|
+
if(!startDate){
|
|
15
|
+
throw new Error("Start Date is required")
|
|
16
|
+
}
|
|
17
|
+
if(!endDate){
|
|
18
|
+
throw new Error("End Date is required")
|
|
19
|
+
}
|
|
20
|
+
if(intervalToDuration({start:startDate,end:endDate}).days > 30){
|
|
21
|
+
throw new Error("Interval between start & end date should be less than 30days")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let axios = new Axios({
|
|
25
|
+
baseURL: apiUrl,
|
|
26
|
+
headers: {
|
|
27
|
+
"Authorization": `Token ${percyToken}`
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
let project = await axios.get(`/projects?project_slug=${projectSlug}`, { responseType: 'json' }).then((res) => {
|
|
32
|
+
if (res.status == 200) {
|
|
33
|
+
return JSON.parse(res.data)
|
|
34
|
+
} else {
|
|
35
|
+
throw res.data
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
const isApp = project['data']['attributes']['type'] == 'app'
|
|
39
|
+
let projectId = project.data.id
|
|
40
|
+
let projectURL = "https://percy.io/"+project.data.attributes['full-slug']
|
|
41
|
+
let projectName = project.data.attributes.name
|
|
42
|
+
let browsers = project["included"].filter((i) => i['type'] == 'browser-families')?.map((v) => v['attributes'].name)
|
|
43
|
+
const buildSummary = async (cursor, _summary) => {
|
|
44
|
+
let done = false
|
|
45
|
+
let urlParams = {
|
|
46
|
+
project_id: projectId,
|
|
47
|
+
'page[limit]': 100,
|
|
48
|
+
}
|
|
49
|
+
let summary = Object.assign({
|
|
50
|
+
totalBuilds: 0,
|
|
51
|
+
totalBuildsApproved: 0,
|
|
52
|
+
totalBuildsUnreviewed:0,
|
|
53
|
+
totalBuildsFailed:0,
|
|
54
|
+
totalBuildsRequestingChanges:0,
|
|
55
|
+
totalSnapshots: 0,
|
|
56
|
+
totalSnapshotsRequestingChanges: 0,
|
|
57
|
+
totalSnapshotsUnreviewed: 0,
|
|
58
|
+
totalSnapshotsReviewed: 0,
|
|
59
|
+
totalComparisons: 0,
|
|
60
|
+
projectURL: projectURL,
|
|
61
|
+
unreviewedBuilds:[],
|
|
62
|
+
failedBuilds:[]
|
|
63
|
+
},_summary)
|
|
64
|
+
if (cursor) {
|
|
65
|
+
urlParams['page[cursor]'] = cursor
|
|
66
|
+
}
|
|
67
|
+
let _builds = await axios.get('/builds', { params: urlParams, responseType: 'json' }).then((res) => {
|
|
68
|
+
if (res.status == 200) {
|
|
69
|
+
return JSON.parse(res.data)
|
|
70
|
+
} else {
|
|
71
|
+
throw res.data
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
for (let build of _builds.data) {
|
|
75
|
+
let createdAt = new Date(build.attributes["created-at"])
|
|
76
|
+
if (isBefore(createdAt, startDate)) {
|
|
77
|
+
done = true;
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
if (isBefore(createdAt, endDate) && isAfter(createdAt, startDate)) {
|
|
81
|
+
summary['totalBuilds']++;
|
|
82
|
+
if (build['attributes']['review-state'] == 'approved') {
|
|
83
|
+
summary['totalBuildsApproved']++
|
|
84
|
+
}
|
|
85
|
+
if(build['attributes']['review-state'] == 'unreviewed'){
|
|
86
|
+
summary['totalBuildsUnreviewed'] ++
|
|
87
|
+
summary['unreviewedBuilds'].push({
|
|
88
|
+
timestamp:new Date(build.attributes["created-at"]),
|
|
89
|
+
buildUrl:build['attributes']['web-url'],
|
|
90
|
+
buildNo:build['attributes']['build-number']
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
if(build['attributes']['review-state'] == null){
|
|
94
|
+
summary['totalBuildsFailed'] ++;
|
|
95
|
+
summary['failedBuilds'].push({
|
|
96
|
+
timestamp:new Date(build.attributes["created-at"]),
|
|
97
|
+
buildUrl:build['attributes']['web-url'],
|
|
98
|
+
buildNo:build['attributes']['build-number']
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
if(build['attributes']['review-state'] == 'changes_requested'){
|
|
102
|
+
summary['totalBuildsRequestingChanges'] ++;
|
|
103
|
+
}
|
|
104
|
+
summary['totalSnapshots'] += build['attributes']['total-snapshots']
|
|
105
|
+
summary['totalSnapshotsRequestingChanges'] += build['attributes']['total-snapshots-requesting-changes']
|
|
106
|
+
summary['totalSnapshotsUnreviewed'] += build['attributes']['total-snapshots-unreviewed']
|
|
107
|
+
summary['totalSnapshotsReviewed'] += (build['attributes']['total-snapshots'] - build['attributes']['total-snapshots-unreviewed'])
|
|
108
|
+
summary['totalComparisons'] += build['attributes']['total-comparisons']
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (done) {
|
|
112
|
+
return summary
|
|
113
|
+
} else {
|
|
114
|
+
if (_builds.data.length > 0) {
|
|
115
|
+
return buildSummary(_builds.data[_builds.data.length - 1].id,summary)
|
|
116
|
+
} else {
|
|
117
|
+
return summary;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let summary = await buildSummary()
|
|
123
|
+
summary = {
|
|
124
|
+
...summary,
|
|
125
|
+
browsers,
|
|
126
|
+
projectName,
|
|
127
|
+
startDate,
|
|
128
|
+
endDate
|
|
129
|
+
}
|
|
130
|
+
if(!fs.existsSync('Summary') ){
|
|
131
|
+
fs.mkdirSync('Summary',{recursive:true})
|
|
132
|
+
}
|
|
133
|
+
fs.writeFileSync(`Summary/${summary.projectName}-${Date.now()}.json`,JSON.stringify(summary,undefined,2))
|
|
134
|
+
HtmlSummary(summary,`Summary/${summary.projectName}-${Date.now()}.html`, isApp)
|
|
135
|
+
console.log("Summary report generated")
|
|
136
|
+
return summary;
|
|
137
|
+
|
|
138
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
+
<title>Document</title>
|
|
9
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
|
10
|
+
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
|
11
|
+
<style>
|
|
12
|
+
img {
|
|
13
|
+
width: 100%;
|
|
14
|
+
object-fit: contain;
|
|
15
|
+
border: 1px solid gray;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.comparison-container {
|
|
19
|
+
position: relative;
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.overlay {
|
|
24
|
+
position: absolute;
|
|
25
|
+
top: 0;
|
|
26
|
+
left: 0;
|
|
27
|
+
right: 0;
|
|
28
|
+
bottom: 0;
|
|
29
|
+
z-index: 50;
|
|
30
|
+
width: 100%;
|
|
31
|
+
height: 100%;
|
|
32
|
+
background-color: rgba(0, 0, 0, 0.7);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.color-red{
|
|
36
|
+
background-color: red !important;
|
|
37
|
+
color: white !important;
|
|
38
|
+
}
|
|
39
|
+
.color-green{
|
|
40
|
+
background-color: green !important;
|
|
41
|
+
color: white !important;
|
|
42
|
+
}
|
|
43
|
+
select{
|
|
44
|
+
max-width: 100%;
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
47
|
+
<script type="text/javascript">
|
|
48
|
+
function onClickOverlay(e, element) {
|
|
49
|
+
let hidden = e.currentTarget.querySelector(".overlay").classList.contains('d-none')
|
|
50
|
+
if(hidden){
|
|
51
|
+
e.currentTarget.querySelector(".overlay").classList.remove('d-none')
|
|
52
|
+
}else{
|
|
53
|
+
e.currentTarget.querySelector(".overlay").classList.add('d-none')
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const snapshots = JSON.parse('<%- JSON.stringify(details.map(snp=>snp.name)) %>')
|
|
57
|
+
const widths = JSON.parse('<%- JSON.stringify(widths) %>')
|
|
58
|
+
const devices = JSON.parse('<%- JSON.stringify(devices) %>')
|
|
59
|
+
var snapshotSelect, widthSelect,deviceSelect,images
|
|
60
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
61
|
+
snapshotSelect = document.getElementById('select-snapshot')
|
|
62
|
+
deviceSelect = document.getElementById('select-device')
|
|
63
|
+
images = document.getElementsByTagName('img')
|
|
64
|
+
tables = document.getElementsByTagName('table')
|
|
65
|
+
snapshotSelect.addEventListener('change', ApplyFilter)
|
|
66
|
+
deviceSelect.addEventListener('change', ApplyFilter)
|
|
67
|
+
ApplyFilter()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
function ApplyFilter() {
|
|
71
|
+
const shouldEnable = (dataset) => {
|
|
72
|
+
return (snapshotSelect.value == "All" || snapshotSelect.value == dataset.name) && deviceSelect.value == dataset.device
|
|
73
|
+
}
|
|
74
|
+
for (let i = 0; i < images.length; i++) {
|
|
75
|
+
let image = images.item(i)
|
|
76
|
+
image.hidden = !shouldEnable(image.dataset)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for(let i = 0; i < tables.length; i++){
|
|
80
|
+
let table = tables.item(i)
|
|
81
|
+
if(table.id !== "build-details"){
|
|
82
|
+
table.hidden = !shouldEnable(table.dataset)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
</head>
|
|
88
|
+
|
|
89
|
+
<body class="p-5">
|
|
90
|
+
<div class="container">
|
|
91
|
+
<h1 class="my-3 text-center"> <u> <%= projectName %> </u></h1>
|
|
92
|
+
<h4 class="my-3 text-center"> <u> <a href="<%= buildURL %>">Go to Percy Dashboard Build</a> </u></h3>
|
|
93
|
+
<table class="table table-bordered table-striped" id="build-details">
|
|
94
|
+
<thead>
|
|
95
|
+
<th class="text-center">Build Number</th>
|
|
96
|
+
<th class="text-center">Device Count</th>
|
|
97
|
+
<th class="text-center">Total Snapshots</th>
|
|
98
|
+
<th class="text-center">Total Screenshots</th>
|
|
99
|
+
<th class="text-center">Snapshots Unreviewed</th>
|
|
100
|
+
<th class="text-center">Screenshots Unreviewed</th>
|
|
101
|
+
</thead>
|
|
102
|
+
<tbody>
|
|
103
|
+
<tr>
|
|
104
|
+
<td class="text-center">
|
|
105
|
+
<%= buildNumber %>
|
|
106
|
+
</td>
|
|
107
|
+
<td class="text-center"><%= devices.length %></td>
|
|
108
|
+
<td class="text-center">
|
|
109
|
+
<%= totalSnapshots %>
|
|
110
|
+
</td>
|
|
111
|
+
<td class="text-center">
|
|
112
|
+
<%= totalScreenshots %>
|
|
113
|
+
</td>
|
|
114
|
+
<td class="text-center">
|
|
115
|
+
<%= unreviewedSnapshots %>
|
|
116
|
+
</td>
|
|
117
|
+
<td class="text-center">
|
|
118
|
+
<%= unreviewedScreenshots %>
|
|
119
|
+
</td>
|
|
120
|
+
</tr>
|
|
121
|
+
</tbody>
|
|
122
|
+
</table>
|
|
123
|
+
<div class="row g-0 my-3">
|
|
124
|
+
<div class="col-md-6 text-center border p-2">
|
|
125
|
+
<label>Snapshot Name</label>
|
|
126
|
+
<select id="select-snapshot">
|
|
127
|
+
<option selected>All</option>
|
|
128
|
+
<% for(let snapshot of details){ %>
|
|
129
|
+
<option value="<%= snapshot['name'] %>">
|
|
130
|
+
<%= snapshot['name'] %>
|
|
131
|
+
</option>
|
|
132
|
+
<% } %>
|
|
133
|
+
</select>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="col-md-6 text-center border p-2">
|
|
136
|
+
<label>Devices</label>
|
|
137
|
+
<select id="select-device">
|
|
138
|
+
<% for(let device of devices){ %>
|
|
139
|
+
<option value="<%= device %>">
|
|
140
|
+
<%= device %>
|
|
141
|
+
</option>
|
|
142
|
+
<% } %>
|
|
143
|
+
</select>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<hr>
|
|
147
|
+
<% for(let snapshot of details){ %>
|
|
148
|
+
<% for(let comparison of snapshot.comparisons){ %>
|
|
149
|
+
<div class="row gx-2">
|
|
150
|
+
<div class="col-md-12">
|
|
151
|
+
<table hidden data-type="diff-ratio" data-name="<%= snapshot.name %>" data-device="<%= comparison.device %>" class="m-3">
|
|
152
|
+
<tbody>
|
|
153
|
+
<tr>
|
|
154
|
+
<td>Diff Ratio:</td>
|
|
155
|
+
<td class="color-<%= comparison['diff-color'] %>" ><%= comparison['diff-percentage'] %></td>
|
|
156
|
+
</tr>
|
|
157
|
+
</tbody>
|
|
158
|
+
</table>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="col-md-6 image-container">
|
|
161
|
+
<img hidden data-type="base" data-name="<%= snapshot.name %>" data-device="<%= comparison.device %>"
|
|
162
|
+
src="<%= comparison.images['base']?.file || comparison.images['base']?.url %>" alt="No Base Image Found">
|
|
163
|
+
</div>
|
|
164
|
+
<% if(!comparison.images['base'] || comparison.images['diff'] || comparison.images['head']){ %>
|
|
165
|
+
<div onclick="onClickOverlay(event,this)" class="col-md-6 image-container comparison-container">
|
|
166
|
+
<img hidden data-type="head" data-name="<%= snapshot.name %>" data-device="<%= comparison.device %>"
|
|
167
|
+
src="<%= comparison.images['head'].file || comparison.images['head']?.url %>" alt="No Head Image Found">
|
|
168
|
+
<% if (comparison.images['diff']) { %> <img hidden data-type="diff"
|
|
169
|
+
data-name="<%= snapshot.name %>"
|
|
170
|
+
data-device="<%= comparison.device %>" class="overlay"
|
|
171
|
+
src="<%= comparison.images['diff']?.file || comparison.images['diff']?.url %>" alt="">
|
|
172
|
+
<% } %>
|
|
173
|
+
</div>
|
|
174
|
+
<% } %>
|
|
175
|
+
</div>
|
|
176
|
+
<% }} %>
|
|
177
|
+
</div>
|
|
178
|
+
</body>
|
|
179
|
+
|
|
180
|
+
</html>
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
|
9
|
+
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
|
10
|
+
<title>Document</title>
|
|
11
|
+
</head>
|
|
12
|
+
<style>
|
|
13
|
+
.table {
|
|
14
|
+
max-width: 768px !important;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.logo {
|
|
18
|
+
display: block;
|
|
19
|
+
width: 150px;
|
|
20
|
+
height: auto;
|
|
21
|
+
margin: auto;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
24
|
+
|
|
25
|
+
<body class="p-5">
|
|
26
|
+
<div class="container">
|
|
27
|
+
<svg class="logo my-5" width="116" height="32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
28
|
+
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
29
|
+
d="M109.723 26.385c-.066.211-.175.36-.324.443l-.043.021c-.162.066-.335.057-.539.007-.657-.162-3.706-.505-2.487-4.067l.723-2.112-.164-.41-.792-1.885-4.966-11.538a.614.614 0 0 1 .028-.606c.101-.155.279-.233.531-.238h2.825c.194 0 .359.052.485.15.127.098.219.248.298.432l3.645 8.528 2.615-8.504a.8.8 0 0 1 .289-.446c.131-.1.298-.16.493-.16h3.018c.256 0 .438.074.545.21.107.14.121.338.042.6l-4.058 12.734-1.565 4.942-.599 1.9zM97.368 14.95h3.085c.477 0 .757.303.589.773-.87 2.873-3.618 4.861-6.9 4.861-4.263 0-7.432-3.176-7.432-7.291C86.71 9.175 89.879 6 94.142 6c3.282 0 6.03 1.988 6.9 4.86.168.47-.112.774-.589.774h-3.085c-.365 0-.617-.165-.842-.469-.504-.69-1.374-1.077-2.384-1.077-1.823 0-3.225 1.298-3.225 3.204 0 1.905 1.402 3.203 3.225 3.203 1.01 0 1.88-.386 2.384-1.049.225-.332.449-.497.842-.497zm-15.11-8.202v.72c.35-.61 1.146-1.474 3.223-1.447.15.002.272.059.36.15.132.134.195.315.195.549v3.375c0 .23-.049.4-.15.515-.098.114-.243.172-.431.172a4.08 4.08 0 0 0-1.187.129c-.378.1-.721.271-1.022.505-.3.239-.54.559-.716.949-.178.396-.268.877-.271 1.45l-.02 3.6c-.02 3.76-2.932 3.19-3.612 3.151-.231-.013-.412-.062-.537-.185-.126-.124-.19-.301-.19-.53V6.747c0-.229.064-.406.19-.53s.305-.185.537-.185h2.905c.233 0 .412.061.538.185.126.124.189.3.189.53zm-12.586-.74c1.025.004 1.966.188 2.824.55a6.743 6.743 0 0 1 2.232 1.524 6.957 6.957 0 0 1 1.463 2.303c.345.881.522 1.845.527 2.891 0 .18-.005.354-.019.519-.01.17-.023.335-.037.5-.028.22-.112.38-.242.482-.13.101-.303.151-.513.151h-9.338c.177.542.424.982.75 1.322.326.335.708.578 1.136.734.434.156.895.23 1.385.23a3.658 3.658 0 0 0 1.141-.188c.36-.124.671-.29.928-.5.135-.11.27-.198.4-.262.136-.064.29-.097.466-.097l2.685-.027c.256.005.442.078.554.22.107.142.112.326.005.55a5.947 5.947 0 0 1-1.468 2.006 6.162 6.162 0 0 1-2.125 1.22 8.256 8.256 0 0 1-2.642.41c-1.165-.006-2.214-.194-3.146-.56-.936-.368-1.733-.877-2.395-1.538a6.74 6.74 0 0 1-1.528-2.303c-.354-.882-.532-1.836-.536-2.869.005-1.032.186-1.987.55-2.867a6.913 6.913 0 0 1 1.542-2.304 7 7 0 0 1 2.367-1.538c.914-.367 1.925-.555 3.034-.56zm-19.617 1.5a6.578 6.578 0 0 1 1.88-1.09c.7-.262 1.44-.398 2.19-.398 1.856 0 3.67.817 4.887 2.138 1.215 1.319 1.967 3.14 1.967 5.152 0 2.01-.752 3.832-1.967 5.151a6.746 6.746 0 0 1-4.888 2.138c-1.36 0-2.786-.42-3.78-1.36l.003 4.473c.003 3.76-2.932 3.19-3.612 3.151-.23-.013-.411-.062-.537-.186-.126-.123-.189-.3-.189-.53L46 6.767c0-.233.065-.417.194-.544.13-.127.315-.19.552-.19H49.3c.238 0 .423.063.552.19.13.127.191.31.194.544l.01.741zm7.255 5.763c0-1.774-1.45-3.211-3.24-3.211-1.789 0-3.24 1.438-3.24 3.211 0 1.774 1.451 3.212 3.24 3.212 1.79 0 3.24-1.438 3.24-3.212zm10.363-3.055c-.527.357-.9.885-1.118 1.574h5.987c-.149-.519-.372-.937-.67-1.248a2.618 2.618 0 0 0-1.026-.67 3.446 3.446 0 0 0-1.202-.202c-.788.004-1.45.183-1.97.546z"
|
|
30
|
+
fill="#333"></path>
|
|
31
|
+
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
32
|
+
d="M39.482 7.275c-.23-.758-.84-1.503-1.565-1.2A5.688 5.688 0 0 1 36 6.5c-1.429-1.341-5.631-4-8.398-5.505 0 0 .338.987.804 2.831 0 0-2.978-2.326-6.348-3.826 0 0 .948 1.946 1.026 2.576 0 0-3.092-1.076-7.43-1.484 0 0 2.084 1.453 2.892 2.564 0 0-3.066-.156-7.645 0 0 0 2.603 1.055 3.752 2.025 0 0-5.434.552-8.504 1.435 0 0 3.46 1.22 4.42 2.127 0 0-4.412 1.257-8.86 3.825 0 0 2.471.432 4.405 1.277 0 0-2.463 1.722-6.114 6.42 0 0 2.698-.515 4.549-.37 0 0-2.345 2.74-3.569 6.93 0 0 1.357-.864 2.67-1.177 0 0 .142 4.998 2.875 5.798l.002-.003c.134.042.261.057.372.057.134 0 .273-.02.415-.061.867-.248 1.523-1.104 2.283-2.095.249-.324.505-.657.776-.981a6.595 6.595 0 0 1 1.176-1.27c.966-.797 2.303-1.4 3.931-1.159 1.834.155 2.966 1.982 3.877 3.45.473.764 1.1 2.116 2.149 2.116 1.16 0 1.567-1.39 2.084-3.104a16.946 16.946 0 0 1 2.149-4.553c1.81-2.693 4.124-4.157 6.9-5.6 2.659-1.383 5.172-2.69 6.432-4.672.63-.991.943-2.207.928-3.615-.012-1.262-.288-2.427-.517-3.18zM26.352 6.5l-4.294-2 6.348 1.575-2.055.425zm-9.758.616l5.305 1.357 2.314-.768-7.62-.59zm10.46 2.66l1.132-.858L23.084 10l3.97-.224zm-8.508 0l-1.815 1.216-5.03-.479 6.845-.737zm5.667 3.039l1.841-1.03-6.844.737 5.003.293zm-5.07 1.143l-1.283 2.109-6.422 1.57 7.705-3.679zm-8.023 1.298L6.15 17.445l6.102-4.377-1.13 2.188zM9.401 17.88l-.196 2.19L6 23l3.401-5.12z"
|
|
33
|
+
fill="#333"></path>
|
|
34
|
+
<path
|
|
35
|
+
d="M26.332 31.457c-.558-.009-1.15-.486-1.8-1.454.383-2.192 1.897-4.991 3.718-6.869-.465 1.744-.527 3.495-.576 4.901v.002c-.032.91-.06 1.695-.19 2.267-.174.765-.555 1.154-1.133 1.154h-.02z"
|
|
36
|
+
fill="#333"></path>
|
|
37
|
+
<path
|
|
38
|
+
d="M10.901 29.875c.473 1.068.978 1.588 1.542 1.588a.893.893 0 0 0 .137-.01c.766-.121 1.476-1.606 2.07-3.043.154-.373.298-.745.427-1.09-1.696.02-3.345 1.384-4.176 2.555z"
|
|
39
|
+
fill="#333"></path>
|
|
40
|
+
</svg>
|
|
41
|
+
<table class="table table-bordered m-auto">
|
|
42
|
+
<thead>
|
|
43
|
+
<tr>
|
|
44
|
+
<th>Summary Report</th>
|
|
45
|
+
<th> <%=new Date(startDate).toDateString()%> - <%=new Date(endDate).toDateString()%> </th>
|
|
46
|
+
</tr>
|
|
47
|
+
</thead>
|
|
48
|
+
<tbody>
|
|
49
|
+
<tr>
|
|
50
|
+
<td>Project Name</td>
|
|
51
|
+
<td> <a href="<%=projectURL%>"> <%=projectName%> </a></td>
|
|
52
|
+
</tr>
|
|
53
|
+
<tr>
|
|
54
|
+
<td> Total Builds </td>
|
|
55
|
+
<td> <%=totalBuilds%> </td>
|
|
56
|
+
</tr>
|
|
57
|
+
<tr>
|
|
58
|
+
<td> Total Builds Approved </td>
|
|
59
|
+
<td> <%=totalBuildsApproved%> </td>
|
|
60
|
+
</tr>
|
|
61
|
+
<tr>
|
|
62
|
+
<td> Total Builds Unreviewed </td>
|
|
63
|
+
<td>
|
|
64
|
+
<% if(totalBuildsUnreviewed) { %>
|
|
65
|
+
<a href="#unreviewedBuilds">
|
|
66
|
+
<% } %>
|
|
67
|
+
<%=totalBuildsUnreviewed%>
|
|
68
|
+
</a>
|
|
69
|
+
</td>
|
|
70
|
+
</tr>
|
|
71
|
+
<tr>
|
|
72
|
+
<td> Total Builds Failed </td>
|
|
73
|
+
<td>
|
|
74
|
+
<% if(totalBuildsFailed) { %>
|
|
75
|
+
<a href="#failedBuilds">
|
|
76
|
+
<% } %>
|
|
77
|
+
<%=totalBuildsFailed%>
|
|
78
|
+
</a>
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
81
|
+
<tr>
|
|
82
|
+
<td> Total Builds Requesting Changes</td>
|
|
83
|
+
<td> <%=totalBuildsRequestingChanges%> </td>
|
|
84
|
+
</tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td> Total Snapshots</td>
|
|
87
|
+
<td> <%=totalSnapshots%> </td>
|
|
88
|
+
</tr>
|
|
89
|
+
<tr>
|
|
90
|
+
<td> Total Snapshots Requesting Changes</td>
|
|
91
|
+
<td> <%=totalSnapshotsRequestingChanges%> </td>
|
|
92
|
+
</tr>
|
|
93
|
+
<tr>
|
|
94
|
+
<td> Total Snapshots Unreviewed</td>
|
|
95
|
+
<td> <%=totalSnapshotsUnreviewed%> </td>
|
|
96
|
+
</tr>
|
|
97
|
+
<tr>
|
|
98
|
+
<td> Total Snapshots Reviewed</td>
|
|
99
|
+
<td> <%=totalSnapshotsReviewed%> </td>
|
|
100
|
+
</tr>
|
|
101
|
+
|
|
102
|
+
<tr>
|
|
103
|
+
<td> Total Comparisons</td>
|
|
104
|
+
<td> <%=totalComparisons%> </td>
|
|
105
|
+
</tr>
|
|
106
|
+
</tbody>
|
|
107
|
+
</table>
|
|
108
|
+
<% if(totalBuildsUnreviewed) { %>
|
|
109
|
+
<section id="unreviewedBuilds">
|
|
110
|
+
<table class="table table-bordered mx-auto mt-5">
|
|
111
|
+
<thead>
|
|
112
|
+
<tr>
|
|
113
|
+
<th colspan="2">Unreviewed Builds</th>
|
|
114
|
+
</tr>
|
|
115
|
+
<tr>
|
|
116
|
+
<th>Build ID</th>
|
|
117
|
+
<th>Build Created At</th>
|
|
118
|
+
</tr>
|
|
119
|
+
</thead>
|
|
120
|
+
<tbody>
|
|
121
|
+
<% for(let unreviewedBuild of unreviewedBuilds){ %>
|
|
122
|
+
<tr>
|
|
123
|
+
<td>
|
|
124
|
+
<a href="<%= unreviewedBuild['buildUrl'] %>">Build <%= unreviewedBuild['buildNo'] %></a>
|
|
125
|
+
</td>
|
|
126
|
+
<td>
|
|
127
|
+
<%= new Date(unreviewedBuild['timestamp']) %>
|
|
128
|
+
</td>
|
|
129
|
+
</tr>
|
|
130
|
+
<% } %>
|
|
131
|
+
</tbody>
|
|
132
|
+
</table>
|
|
133
|
+
</section>
|
|
134
|
+
<% } %>
|
|
135
|
+
<% if(totalBuildsFailed) { %>
|
|
136
|
+
<section id="failedBuilds">
|
|
137
|
+
<table class="table table-bordered mx-auto mt-5">
|
|
138
|
+
<thead>
|
|
139
|
+
<tr>
|
|
140
|
+
<th colspan="2">Failed Builds</th>
|
|
141
|
+
</tr>
|
|
142
|
+
<tr>
|
|
143
|
+
<th>Build ID</th>
|
|
144
|
+
<th>Build Created At</th>
|
|
145
|
+
</tr>
|
|
146
|
+
</thead>
|
|
147
|
+
<tbody>
|
|
148
|
+
<% for(let failedBuild of failedBuilds){ %>
|
|
149
|
+
<tr>
|
|
150
|
+
<td>
|
|
151
|
+
<a href="<%= failedBuild['buildUrl'] %>">Build <%= failedBuild['buildNo'] %></a>
|
|
152
|
+
</td>
|
|
153
|
+
<td>
|
|
154
|
+
<%= new Date(failedBuild['timestamp']) %>
|
|
155
|
+
</td>
|
|
156
|
+
</tr>
|
|
157
|
+
<% } %>
|
|
158
|
+
</tbody>
|
|
159
|
+
</table>
|
|
160
|
+
</section>
|
|
161
|
+
<% } %>
|
|
162
|
+
</div>
|
|
163
|
+
</body>
|
|
164
|
+
|
|
165
|
+
</html>
|