Skip to content

Commit

Permalink
MWPW-147289: [Nala] Refine screenshot diff tool, clean take.js and up…
Browse files Browse the repository at this point in the history
…date compare.js and add timestamp
  • Loading branch information
JackySun9 committed May 16, 2024
1 parent 457d801 commit 86c1e29
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 115 deletions.
70 changes: 43 additions & 27 deletions libs/screenshot/compare.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ async function downloadImage(url, localPath) {
}

async function getSavedImages(s3Url, curEntries) {
const response = await axios.get(`${s3Url}/results.json`);
let response;
try {
response = await axios.get(`${s3Url}/results.json`);
} catch (error) {
console.error(`Failed to get previous results.json from ${s3Url}`);
process.exit(1);
}
const preEntries = response.data;

if (Object.keys(curEntries).length !== Object.keys(preEntries).length) {
Expand All @@ -36,7 +42,8 @@ async function getSavedImages(s3Url, curEntries) {
}

const basename = path.basename(preEntries[key][0].a);
entry.a = `${preEntries[key][0].a}`.replace('.png', '-a.png');
entry.a = `${preEntries[key][0].a.includes('-a.png')
? preEntries[key][0].a : preEntries[key][0].a.replace('.png', '-a.png')}`;
console.log(`Downloading ${s3Url}/${basename}`);
// eslint-disable-next-line no-await-in-loop
await downloadImage(`${s3Url}/${basename}`, entry.a);
Expand All @@ -45,44 +52,53 @@ async function getSavedImages(s3Url, curEntries) {

async function main() {
const localPath = process.argv[2];
const s3Url = `${S3URL}/${localPath}`;

if (!localPath || !s3Url) {
console.log('Usage: node compare.js <localPath>');
if (!localPath) {
console.log('Usage: node compare.mjs <localPath>');
process.exit(1);
}

const curEntries = JSON.parse(fs.readFileSync(`${localPath}/results.json`));

if (s3Url) {
await getSavedImages(s3Url, curEntries);
const firstEntry = Object.values(curEntries)[0][0];

if (firstEntry.a && !firstEntry.b) {
const s3Url = `${S3URL}/${localPath}`;

if (s3Url) {
await getSavedImages(s3Url, curEntries);
}
}

const results = {};

// eslint-disable-next-line no-restricted-syntax
for (const [key, value] of Object.entries(curEntries)) {
const result = {};
const entry = value[0];
console.log(entry);

const baseImage = fs.readFileSync(entry.a);
const currImage = fs.readFileSync(entry.b);
result.order = 1;
result.a = entry.a;
result.b = entry.b;
result.urls = entry.urls;

const comparator = getComparator('image/png');
const diffImage = comparator(baseImage, currImage);

if (diffImage) {
const diffName = `${entry.b}`.replace('.png', '-diff.png');
fs.writeFileSync(diffName, diffImage.diff);
result.diff = diffName;
console.info('Differences found');
const resultsArray = [];
// eslint-disable-next-line no-restricted-syntax
for (const entry of value) {
const result = {};
console.log(entry);

const baseImage = fs.readFileSync(entry.a);
const currImage = fs.readFileSync(entry.b);
result.order = 1;
result.a = entry.a;
result.b = entry.b;
result.urls = entry.urls;

const comparator = getComparator('image/png');
const diffImage = comparator(baseImage, currImage);

if (diffImage) {
const diffName = `${entry.b}`.replace('.png', '-diff.png');
fs.writeFileSync(diffName, diffImage.diff);
result.diff = diffName;
console.info('Differences found');
}
resultsArray.push(result);
}
results[key] = [result];
results[key] = resultsArray;
}

fs.writeFileSync(`${localPath}/results.json`, JSON.stringify(results, null, 2));
Expand Down
105 changes: 22 additions & 83 deletions libs/screenshot/take.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable import/no-import-module-exports */
// eslint-disable-next-line import/extensions
import { getComparator } from 'playwright-core/lib/utils';

const fs = require('fs');

/**
* Take a screenshot of a page
* @param {Page} page - The page object
* @param {string} folderPath - The folder path to save the screenshot, e.g., screenshots/milo
* @param {string} fileName - The file name of the screenshot
* @param {object} options - The screenshot options, see https://playwright.dev/docs/api/class-page#page-screenshot
* @returns {object} The screenshot result
*/
async function take(page, folderPath, fileName, options = {}) {
const urls = [];
Expand All @@ -28,6 +22,16 @@ async function take(page, folderPath, fileName, options = {}) {
return result;
}

/**
* Take a screenshot of a page
* @param {Page} page - The page object
* @param {string} url - The URL of the page to take a screenshot
* @param {function} callback - The callback function to run before taking the screenshot
* @param {string} folderPath - The folder path to save the screenshot, e.g., screenshots/milo
* @param {string} fileName - The file name of the screenshot
* @param {object} options - The screenshot options, see https://playwright.dev/docs/api/class-page#page-screenshot
* @returns {object} The screenshot result
*/
async function takeOne(page, url, callback, folderPath, fileName, options = {}) {
const urls = [];
const result = {};
Expand All @@ -50,6 +54,17 @@ async function takeOne(page, url, callback, folderPath, fileName, options = {})
return result;
}

/**
* Take screenshots of two pages
* @param {Page} page - The page object
* @param {string} urlStable - The URL of the stable page
* @param {function} callbackStable - The callback function to run before taking the screenshot of the stable page
* @param {string} urlBeta - The URL of the beta page
* @param {function} callbackBeta - The callback function to run before taking the screenshot of the beta page
* @param {string} folderPath - The folder path to save the screenshots, e.g., screenshots/milo
* @param {string} fileName - The file name of the screenshots
* @returns {object} The screenshot results
*/
async function takeTwo(page, urlStable, callbackStable, urlBeta, callbackBeta, folderPath, fileName) {
const urls = [];
const result = {};
Expand All @@ -75,84 +90,8 @@ async function takeTwo(page, urlStable, callbackStable, urlBeta, callbackBeta, f
return result;
}

async function takeTwoAndCompare(page, urlStable, callbackStable, urlBeta, callbackBeta, folderPath, fileName) {
const urls = [];
const result = {};

console.info(`[Test Page]: ${urlStable}`);
await page.goto(urlStable);
urls.push(urlStable);
if (typeof callbackStable === 'function') { await callbackStable(); }
const nameStable = `${folderPath}/${fileName}-a.png`;
await page.screenshot({ path: nameStable, fullPage: true });
const baseImage = fs.readFileSync(nameStable);
result.order = 1;
result.a = nameStable;

console.info(`[Test Page]: ${urlBeta}`);
await page.goto(urlBeta);
urls.push(urlBeta);
if (typeof callbackBeta === 'function') { await callbackBeta(); }
const nameBeta = `${folderPath}/${fileName}-b.png`;
await page.screenshot({ path: nameBeta, fullPage: true });
const currImage = fs.readFileSync(nameBeta);
result.b = nameBeta;

const comparator = getComparator('image/png');
const diffImage = comparator(baseImage, currImage);

if (diffImage) {
const diffName = `${folderPath}/${fileName}-diff.png`;
fs.writeFileSync(diffName, diffImage.diff);
result.diff = diffName;
console.info('Differences found');
}

result.urls = urls.join(' | ');
return result;
}

function compareScreenshots(stableArray, betaArray) {
const results = [];
const comparator = getComparator('image/png');
for (let i = 0; i < stableArray.length; i += 1) {
if (betaArray[i].a.slice(-10) === stableArray[i].a.slice(-10)) {
const result = {};
const urls = [];
result.order = i + 1;
result.a = `${stableArray[i].a}`;
result.b = `${betaArray[i].a}`;
urls.push(stableArray[i].urls);
urls.push(betaArray[i].urls);
const stableImage = fs.readFileSync(`${stableArray[i].a}`);
const betaImage = fs.readFileSync(`${betaArray[i].a}`);
const diffImage = comparator(stableImage, betaImage);

if (diffImage) {
result.diff = `${stableArray[i].a}-diff.png`;
fs.writeFileSync(`${stableArray[i].a}-diff.png`, diffImage.diff);
console.info('Differences found');
}
result.urls = urls.join(' | ');
results.push(result);
} else {
console.info('Screenshots are not matched');
console.info(`${stableArray[i].a} vs ${betaArray[i].a}`);
}
}
return results;
}

function writeResultsToFile(folderPath, testInfo, results) {
const resultFilePath = `${folderPath}/results-${testInfo.workerIndex}.json`;
fs.writeFileSync(resultFilePath, JSON.stringify(results, null, 2));
}

module.exports = {
takeTwoAndCompare,
compareScreenshots,
takeOne,
takeTwo,
take,
writeResultsToFile,
};
10 changes: 10 additions & 0 deletions libs/screenshot/uploads3.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ async function main() {

console.log('Upload results.json');
await uploadFile(resultsPath, bucket, s3Path, creds, resultsPath, 'application/json');

const timestampPath = path.join(dir, 'timestamp.json');

fs.writeFileSync(
timestampPath,
JSON.stringify([(new Date()).toLocaleString()], null, 2),
);

console.log('Upload timestamp.json');
await uploadFile(timestampPath, bucket, s3Path, creds, timestampPath, 'application/json');
}

main();
42 changes: 42 additions & 0 deletions libs/screenshot/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// eslint-disable-next-line import/no-extraneous-dependencies, import/extensions, import/no-import-module-exports
import { getComparator } from 'playwright-core/lib/utils';

const fs = require('fs');

function compareScreenshots(stableArray, betaArray) {
const results = [];
const comparator = getComparator('image/png');
for (let i = 0; i < stableArray.length; i += 1) {
if (betaArray[i].a.slice(-10) === stableArray[i].a.slice(-10)) {
const result = {};
const urls = [];
result.order = i + 1;
result.a = `${stableArray[i].a}`;
result.b = `${betaArray[i].a}`;
urls.push(stableArray[i].urls);
urls.push(betaArray[i].urls);
const stableImage = fs.readFileSync(`${stableArray[i].a}`);
const betaImage = fs.readFileSync(`${betaArray[i].a}`);
const diffImage = comparator(stableImage, betaImage);

if (diffImage) {
result.diff = `${stableArray[i].a}-diff.png`;
fs.writeFileSync(`${stableArray[i].a}-diff.png`, diffImage.diff);
console.info('Differences found');
}
result.urls = urls.join(' | ');
results.push(result);
} else {
console.info('Screenshots are not matched');
console.info(`${stableArray[i].a} vs ${betaArray[i].a}`);
}
}
return results;
}

function writeResultsToFile(folderPath, testInfo, results) {
const resultFilePath = `${folderPath}/results-${testInfo.workerIndex}.json`;
fs.writeFileSync(resultFilePath, JSON.stringify(results, null, 2));
}

module.exports = { compareScreenshots, writeResultsToFile };
5 changes: 3 additions & 2 deletions tests/visual/caas/cards.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable import/named */
import { test } from '@playwright/test';
import { features } from '../../../features/visual/caas/cards.spec.js';
import { takeTwoAndCompare, writeResultsToFile } from '../../../libs/screenshot/take.js';
import { takeTwo } from '../../../libs/screenshot/take.js';
import { writeResultsToFile } from '../../../libs/screenshot/utils.js';

const folderPath = 'screenshots/caas';
const results = {};
Expand All @@ -12,7 +13,7 @@ test.describe('Milo Caas block visual comparison test suite', () => {
// eslint-disable-next-line no-loop-func
test(`${feature.name},${feature.tags}`, async ({ page, baseURL }, testInfo) => {
const name = `${feature.name}-${testInfo.project.name}`;
const result = await takeTwoAndCompare(
const result = await takeTwo(
page,
baseURL + feature.stable,
async () => { await page.waitForTimeout(3000); },
Expand Down
3 changes: 2 additions & 1 deletion tests/visual/feds/feds.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable import/named */
import { expect, test } from '@playwright/test';
import { features } from '../../../features/visual/feds/feds.spec.js';
import { takeOne, writeResultsToFile } from '../../../libs/screenshot/take.js';
import { takeOne } from '../../../libs/screenshot/take.js';
import { writeResultsToFile } from '../../../libs/screenshot/utils.js';
import FedsHeader from '../../../selectors/feds/feds.header.page.js';

const folderPath = 'screenshots/feds';
Expand Down
3 changes: 2 additions & 1 deletion tests/visual/milo/milo.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable import/named */
import { test } from '@playwright/test';
import { features } from '../../../features/visual/milo/milo.spec.js';
import { takeOne, writeResultsToFile } from '../../../libs/screenshot/take.js';
import { takeOne } from '../../../libs/screenshot/take.js';
import { writeResultsToFile } from '../../../libs/screenshot/utils.js';

const folderPath = 'screenshots/milo';
const results = {};
Expand Down
2 changes: 1 addition & 1 deletion tests/visual/uar/quiz.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Quiz from '../../../selectors/uar/quiz.page.js';

const { features } = require('../../../features/visual/uar/quiz.spec.js');
const { WebUtil } = require('../../../libs/webutil.js');
const { compareScreenshots, writeResultsToFile } = require('../../../libs/screenshot/take.js');
const { compareScreenshots, writeResultsToFile } = require('../../../libs/screenshot/utils.js');
const envs = require('../../../envs/envs.js');

const folderPath = 'screenshots/uar';
Expand Down

0 comments on commit 86c1e29

Please sign in to comment.