diff --git a/.gitignore b/.gitignore
index 6fe4bcd..bb07c6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -105,4 +105,5 @@ dist
Report/
.DS_Store
-Summary/
\ No newline at end of file
+Summary/
+.vscode
\ No newline at end of file
diff --git a/bin/cli.js b/bin/cli.js
index dba521d..1ea5a71 100755
--- a/bin/cli.js
+++ b/bin/cli.js
@@ -1,4 +1,5 @@
#!/usr/bin/env node
+require('dotenv').config()
const { Command } = require('commander');
const { ar } = require('date-fns/locale');
const { Generate,Summary } = require('../src');
@@ -7,7 +8,7 @@ const {endOfDay,startOfDay} = require('date-fns')
program.name('percy-report')
.description('Generate Percy Reports & Download Images Locally')
-.version('0.0.1')
+.version('0.0.2')
program.command('generate')
.description('Genetate Report')
diff --git a/package-lock.json b/package-lock.json
index 31ba501..2d9ada7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,11 +9,12 @@
"version": "0.0.1",
"license": "Apache-2.0",
"dependencies": {
- "@percy/cli": "^1.10.3",
+ "@percy/cli": "^1.16.0",
"axios": "^0.27.2",
"chromedriver": "^105.0.0",
"commander": "^9.4.0",
"date-fns": "^2.29.3",
+ "dotenv": "^16.0.3",
"ejs": "^3.1.8"
},
"bin": {
@@ -704,6 +705,14 @@
"node": ">=8"
}
},
+ "node_modules/dotenv": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
+ "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/ejs": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz",
@@ -2195,6 +2204,11 @@
"path-type": "^4.0.0"
}
},
+ "dotenv": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
+ "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
+ },
"ejs": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz",
diff --git a/package.json b/package.json
index 64211f3..32553bf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "percy-report",
- "version": "0.0.1",
+ "version": "0.0.2",
"description": "Percy Report Generator",
"main": "index.js",
"scripts": {},
@@ -26,6 +26,7 @@
"chromedriver": "^105.0.0",
"commander": "^9.4.0",
"date-fns": "^2.29.3",
+ "dotenv": "^16.0.3",
"ejs": "^3.1.8"
},
"devDependencies": {
diff --git a/src/generate.js b/src/generate.js
index 98f7061..8ff0503 100644
--- a/src/generate.js
+++ b/src/generate.js
@@ -1,3 +1,4 @@
+
const { Parser } = require('./response-parser')
const { Axios } = require('axios')
const fs = require('fs')
@@ -27,6 +28,7 @@ module.exports.Generate = async function (config) {
throw res.data
}
})
+ const isApp = buildDetails['data']['attributes']['type'] == 'app'
while (buildDetails.data && buildDetails.data.attributes.state !== 'finished') {
console.log("Waiting for build to complete on Percy...")
await wait(30000)
@@ -41,6 +43,7 @@ module.exports.Generate = async function (config) {
throw new Error("Build Failed with an Error on Percy Server. Please check your percy dashboard for more information.")
}
}
+
console.log(`Generating report for Build ID ${buildId}`)
let snapshotsData = await axios.get(`/snapshots?build_id=${buildId}`, { responseType: 'json' }).then((res) => {
if (res.status == 200) {
@@ -54,7 +57,6 @@ module.exports.Generate = async function (config) {
buildURL = buildDetails['data']['attributes']['web-url']
projectURL = buildURL.split("/builds/")[0]
projectName = projectURL.split('/').slice(-1)[0]
- console.log(projectURL);
let report = {
totalScreenshots: 0,
@@ -64,7 +66,8 @@ module.exports.Generate = async function (config) {
unreviewedSnapshots: 0,
widths: [],
browsers: [],
- projectURL : projectURL,
+ devices: [],
+ projectURL: projectURL,
buildNumber: buildDetails['data']['attributes']['build-number'],
projectName: projectName
}
@@ -79,13 +82,29 @@ module.exports.Generate = async function (config) {
let base = images['base'] = getComparisonImage(comp, 'base-screenshot')
let head = images['head'] = getComparisonImage(comp, 'head-screenshot')
let diff = images['diff'] = getComparisonImage(comp, 'diff-image')
- let browser = getComparisonBrowser(comp)
+ let compTag;
+ if (isApp) {
+ let device = getComparisonDevice(comp)
+ compTag = device.name
+ if(!report.devices.includes(device.name)){
+ report.devices.push(device.name)
+ }
+ } else {
+ let browser = getComparisonBrowser(comp)
+ compTag = device.nam
+ if (!report.browsers.includes(browser.name)) {
+ report.browsers.push(browser.name)
+ }
+ if (!report.widths.includes(comparison['width'])) {
+ report.widths.push(comparison['width'])
+ }
+ }
if (downloadImages) {
['base', 'head', 'diff'].forEach((val) => {
if (images[val]) {
images[val].file = downloadImage({
name: String(snapshot?.['attributes'].name).replace('/', '-'),
- browser: browser.name,
+ compTag,
width: images[val].width,
type: val,
baseDir,
@@ -102,13 +121,9 @@ module.exports.Generate = async function (config) {
report['unreviewedScreenshots']++
flagChanged = true
}
- Object.assign(comparison, comp.attributes, { images }, { browser: browser.name || '' })
- if (!report.browsers.includes(browser.name)) {
- report.browsers.push(browser.name)
- }
- if (!report.widths.includes(comparison['width'])) {
- report.widths.push(comparison['width'])
- }
+ Object.assign(comparison, comp.attributes, { images }, isApp?{ device:compTag }:{browser:compTag})
+
+
comparison['diff-percentage'] = (comparison['diff-ratio'] * 100).toFixed(2)
comparison['diff-color'] = "yellow"
if (comparison['diff-percentage'] > diffThreshold) {
@@ -122,11 +137,12 @@ module.exports.Generate = async function (config) {
return formattedSnapshot
})
fs.writeFileSync(`${baseDir}/report.json`, JSON.stringify(report, undefined, 2))
- HtmlReportGenerator(config, report)
+ HtmlReportGenerator(config, report, isApp)
console.log("Build Report Generated.")
return report
}
+
function getComparisonImage(comparison, key) {
let screenshot = comparison.relationships[key]
if (!screenshot) return;
@@ -141,20 +157,24 @@ function getComparisonBrowser(comparison) {
return comparison.relationships['browser']?.relationships['browser-family']?.attributes
}
+function getComparisonDevice(comparison) {
+ return comparison.relationships['comparison-tag']?.attributes
+}
+
function downloadImage(options) {
- let { name, browser, width, type, baseDir, url } = options
- if(!fs.existsSync(`${baseDir}/${type}`)){
- fs.mkdirSync(`${baseDir}/${type}`,{recursive:true})
+ let { name, width, type, baseDir, url,compTag } = options
+ if (!fs.existsSync(`${baseDir}/${type}`)) {
+ fs.mkdirSync(`${baseDir}/${type}`, { recursive: true })
}
- let path = `${baseDir}/${type}/${name}-${browser}-${width}.png`
+ let path = `${baseDir}/${type}/${name}-${compTag}-${width}.png`
try {
- new Axios({ responseType: 'arraybuffer',url:url }).get(url).then((file) => {
+ new Axios({ responseType: 'arraybuffer', url: url }).get(url).then((file) => {
console.log("Download Complete:" + path)
fs.writeFileSync(path, file.data)
}).catch((err) => {
console.error("Failed to Download: " + path)
})
- return `./${type}/${name}-${browser}-${width}.png`;
+ return `file:./${type}/${name}-${compTag}-${width}.png`;
} catch {
}
diff --git a/src/html-render.js b/src/html-render.js
index 46e2ff3..8b827b2 100644
--- a/src/html-render.js
+++ b/src/html-render.js
@@ -1,24 +1,64 @@
const fs = require('fs')
const ejs = require('ejs');
const path = require('path')
-function HtmlReportGenerator(config,jsonReport){
- let { buildId, downloadPath } = config
- let template_path = path.resolve(__dirname,'template/report.html')
- const template = fs.readFileSync(template_path,{encoding:'utf-8'}).toString()
- let htmlReport = ejs.render(template,{buildId,...jsonReport})
- fs.writeFileSync(`${downloadPath}/${buildId}/report.html`,htmlReport)
+
+function HtmlReportGenerator(config, jsonReport, isApp) {
+ if (isApp) {
+ let {
+ buildId,
+ downloadPath
+ } = config
+ let template_path = path.resolve(__dirname, 'template/app-report.html')
+ const template = fs.readFileSync(template_path, {
+ encoding: 'utf-8'
+ }).toString()
+ let htmlReport = ejs.render(template, {
+ buildId,
+ ...jsonReport
+ })
+ fs.writeFileSync(`${downloadPath}/${buildId}/app-report.html`, htmlReport)
+ } else {
+ let {
+ buildId,
+ downloadPath
+ } = config
+ let template_path = path.resolve(__dirname, 'template/report.html')
+ const template = fs.readFileSync(template_path, {
+ encoding: 'utf-8'
+ }).toString()
+ let htmlReport = ejs.render(template, {
+ buildId,
+ ...jsonReport
+ })
+ fs.writeFileSync(`${downloadPath}/${buildId}/report.html`, htmlReport)
+ }
}
-function HtmlSummary(summary,filename){
- let template_path = path.resolve(__dirname,'template/summary.html')
- const template = fs.readFileSync(template_path,{encoding:"utf-8"}).toString()
- try{
- let htmlSummary = ejs.render(template,summary)
- fs.writeFileSync(filename,htmlSummary)
- }catch(err){
- console.log(err)
+function HtmlSummary(summary, filename, isApp) {
+ if (isApp) {
+ let template_path = path.resolve(__dirname, 'template/app-summary.html')
+ const template = fs.readFileSync(template_path, {
+ encoding: "utf-8"
+ }).toString()
+ try {
+ let htmlSummary = ejs.render(template, summary)
+ fs.writeFileSync(filename, htmlSummary)
+ } catch (err) {
+ console.log(err)
+ }
+ } else {
+ let template_path = path.resolve(__dirname, 'template/summary.html')
+ const template = fs.readFileSync(template_path, {
+ encoding: "utf-8"
+ }).toString()
+ try {
+ let htmlSummary = ejs.render(template, summary)
+ fs.writeFileSync(filename, htmlSummary)
+ } catch (err) {
+ console.log(err)
+ }
}
-
+
}
module.exports.HtmlReportGenerator = HtmlReportGenerator
diff --git a/src/summary.js b/src/summary.js
index a098e29..edce511 100644
--- a/src/summary.js
+++ b/src/summary.js
@@ -35,6 +35,7 @@ module.exports.Summary = async function (opts) {
throw res.data
}
})
+ const isApp = project['data']['attributes']['type'] == 'app'
let projectId = project.data.id
let projectURL = "https://percy.io/"+project.data.attributes['full-slug']
let projectName = project.data.attributes.name
@@ -130,7 +131,7 @@ module.exports.Summary = async function (opts) {
fs.mkdirSync('Summary',{recursive:true})
}
fs.writeFileSync(`Summary/${summary.projectName}-${Date.now()}.json`,JSON.stringify(summary,undefined,2))
- HtmlSummary(summary,`Summary/${summary.projectName}-${Date.now()}.html`)
+ HtmlSummary(summary,`Summary/${summary.projectName}-${Date.now()}.html`, isApp)
console.log("Summary report generated")
return summary;
diff --git a/src/template/app-report.html b/src/template/app-report.html
new file mode 100644
index 0000000..f4638d4
--- /dev/null
+++ b/src/template/app-report.html
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
<%= projectName %>
+
+
+ Build Number |
+ Device Count |
+ Total Snapshots |
+ Total Screenshots |
+ Snapshots Unreviewed |
+ Screenshots Unreviewed |
+
+
+
+
+ <%= buildNumber %>
+ |
+ <%= devices.length %> |
+
+ <%= totalSnapshots %>
+ |
+
+ <%= totalScreenshots %>
+ |
+
+ <%= unreviewedSnapshots %>
+ |
+
+ <%= unreviewedScreenshots %>
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <% for(let snapshot of details){ %>
+ <% for(let comparison of snapshot.comparisons){ %>
+
+
+
+
+
+ Diff Ratio: |
+ <%= comparison['diff-percentage'] %> |
+
+
+
+
+
+
+
+ <% if(!comparison.images['base'] || comparison.images['diff'] || comparison.images['head']){ %>
+
+
+ <% if (comparison.images['diff']) { %>
+ <% } %>
+
+ <% } %>
+
+ <% }} %>
+
+
+
+
\ No newline at end of file
diff --git a/src/template/app-summary.html b/src/template/app-summary.html
new file mode 100644
index 0000000..881d4b6
--- /dev/null
+++ b/src/template/app-summary.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+ Summary Report |
+ <%=new Date(startDate).toDateString()%> - <%=new Date(endDate).toDateString()%> |
+
+
+
+
+ Project Name |
+ <%=projectName%> |
+
+
+ Total Builds |
+ <%=totalBuilds%> |
+
+
+ Total Builds Approved |
+ <%=totalBuildsApproved%> |
+
+
+ Total Builds Unreviewed |
+
+
+ <%=totalBuildsUnreviewed%>
+
+ |
+
+
+ Total Builds Failed |
+
+
+ <%=totalBuildsFailed%>
+
+ |
+
+
+ Total Builds Requesting Changes |
+ <%=totalBuildsRequestingChanges%> |
+
+
+ Total Snapshots |
+ <%=totalSnapshots%> |
+
+
+ Total Snapshots Requesting Changes |
+ <%=totalSnapshotsRequestingChanges%> |
+
+
+ Total Snapshots Unreviewed |
+ <%=totalSnapshotsUnreviewed%> |
+
+
+ Total Snapshots Reviewed |
+ <%=totalSnapshotsReviewed%> |
+
+
+
+ Total Comparisons |
+ <%=totalComparisons%> |
+
+
+
+
+
+
+
+
+ Unreviewed Builds |
+
+
+ Build ID |
+ Build Created At |
+
+
+
+ <% for(let unreviewedBuild of unreviewedBuilds){ %>
+
+
+ Build <%= unreviewedBuild['buildNo'] %>
+ |
+
+ <%= new Date(unreviewedBuild['timestamp']) %>
+ |
+
+ <% } %>
+
+
+
+
+
+
+
+
+ Failed Builds |
+
+
+ Build ID |
+ Build Created At |
+
+
+
+ <% for(let failedBuild of failedBuilds){ %>
+
+
+ Build <%= failedBuild['buildNo'] %>
+ |
+
+ <%= new Date(failedBuild['timestamp']) %>
+ |
+
+ <% } %>
+
+
+
+
+
+
\ No newline at end of file