From 1e1dbd8f9fc4a505e8ee783021faa8fadf4dcb8f Mon Sep 17 00:00:00 2001 From: Rushabh Shroff Date: Thu, 6 Apr 2023 13:51:45 +0530 Subject: [PATCH 1/4] add: App Percy Support --- bin/cli.js | 1 + package-lock.json | 16 +++++++++++++- package.json | 1 + src/generate.js | 53 +++++++++++++++++++++++++++++++++-------------- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index dba521d..0554f63 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'); 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..991c25d 100644 --- a/package.json +++ b/package.json @@ -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..595daa2 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,8 @@ module.exports.Generate = async function (config) { throw res.data } }) + const isApp = buildDetails['data']['attributes']['type'] == 'app' + console.log(isApp) while (buildDetails.data && buildDetails.data.attributes.state !== 'finished') { console.log("Waiting for build to complete on Percy...") await wait(30000) @@ -64,7 +67,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 +83,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 +122,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) { @@ -127,6 +143,7 @@ module.exports.Generate = async function (config) { return report } + function getComparisonImage(comparison, key) { let screenshot = comparison.relationships[key] if (!screenshot) return; @@ -141,20 +158,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 { } From 2847ec7041a5470bc5b7b0e41bf3fa63e7f19a5c Mon Sep 17 00:00:00 2001 From: Ansel Dsouza Date: Tue, 11 Apr 2023 01:55:56 +0530 Subject: [PATCH 2/4] add: App Percy Support --- .gitignore | 3 +- src/generate.js | 4 +- src/html-render.js | 70 ++++++++++--- src/summary.js | 3 +- src/template/app-report.html | 180 ++++++++++++++++++++++++++++++++++ src/template/app-summary.html | 142 +++++++++++++++++++++++++++ 6 files changed, 383 insertions(+), 19 deletions(-) create mode 100644 src/template/app-report.html create mode 100644 src/template/app-summary.html 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/src/generate.js b/src/generate.js index 595daa2..faad71f 100644 --- a/src/generate.js +++ b/src/generate.js @@ -29,7 +29,6 @@ module.exports.Generate = async function (config) { } }) const isApp = buildDetails['data']['attributes']['type'] == 'app' - console.log(isApp) while (buildDetails.data && buildDetails.data.attributes.state !== 'finished') { console.log("Waiting for build to complete on Percy...") await wait(30000) @@ -44,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) { @@ -138,7 +138,7 @@ 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 } 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 %>

+

Go to Percy Dashboard Build

+ + + + + + + + + + + + + + + + + + + +
Build NumberDevice CountTotal SnapshotsTotal ScreenshotsSnapshots UnreviewedScreenshots Unreviewed
+ <%= buildNumber %> + <%= devices.length %> + <%= totalSnapshots %> + + <%= totalScreenshots %> + + <%= unreviewedSnapshots %> + + <%= unreviewedScreenshots %> +
+
+
+ + +
+
+ + +
+
+
+ <% for(let snapshot of details){ %> + <% for(let comparison of snapshot.comparisons){ %> +
+
+ + + + + + + + +
+
+ +
+ <% 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%>
+ +
+ + + + + + + + + + + + <% for(let unreviewedBuild of unreviewedBuilds){ %> + + + + + <% } %> + +
Unreviewed Builds
Build IDBuild Created At
+ Build <%= unreviewedBuild['buildNo'] %> + + <%= new Date(unreviewedBuild['timestamp']) %> +
+
+ +
+ + + + + + + + + + + + <% for(let failedBuild of failedBuilds){ %> + + + + + <% } %> + +
Failed Builds
Build IDBuild Created At
+ Build <%= failedBuild['buildNo'] %> + + <%= new Date(failedBuild['timestamp']) %> +
+
+
+ + \ No newline at end of file From 14c02a1290329865f0a5b6e676b88485ebab835f Mon Sep 17 00:00:00 2001 From: Ansel Dsouza Date: Wed, 12 Apr 2023 17:37:29 +0530 Subject: [PATCH 3/4] Updated Package Version --- bin/cli.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 0554f63..1ea5a71 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -8,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.json b/package.json index 991c25d..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": {}, From 90942a464097eb8469b3b8ef6324240221d71353 Mon Sep 17 00:00:00 2001 From: Ansel Dsouza Date: Wed, 12 Apr 2023 20:46:38 +0530 Subject: [PATCH 4/4] Update generate.js --- src/generate.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/generate.js b/src/generate.js index faad71f..8ff0503 100644 --- a/src/generate.js +++ b/src/generate.js @@ -57,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,