screenshot-test-server
Version:
A node server for screenshot testing that takes base64 string for captured screenshot and compares it with exisiting screenshot and generates diff image and a test report as an html file.
279 lines (278 loc) • 13.9 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const url = __importStar(require("url"));
const fs = __importStar(require("fs"));
const utils_1 = require("./utils");
const puppeteer_1 = __importDefault(require("puppeteer"));
var http = require('http');
const isHeadLess = process.argv[2] ? JSON.parse(process.argv[2]) : true;
const uiUrl = process.argv[3] || 'http://localhost:8081';
const serverPort = process.argv[4] || 8080;
var page;
http
.createServer(function (req, res) {
return __awaiter(this, void 0, void 0, function* () {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'POST' && req.url === '/data') {
let body = '';
// Listen for the data event to collect the request body
req.on('data', (chunk) => {
body += chunk.toString(); // Convert buffer to string
});
// End event when all data is received
req.on('end', () => __awaiter(this, void 0, void 0, function* () {
try {
let { data, id, path, showDiffInGrayScale } = JSON.parse(body); // Parse the received JSON
if (!data && isHeadLess) {
console.log('id', id);
const card = yield page.$(`#${id}`);
console.log('uiComponent', card);
data = yield (card === null || card === void 0 ? void 0 : card.screenshot({ encoding: 'base64' }));
}
if (!fs.existsSync(path)) {
// If the folder itself not present -> 1st ever run of the testing library
fs.mkdirSync(path);
}
const oldPath = path + '/old';
const newPath = path + '/new';
if (!fs.existsSync(oldPath)) {
// If the folder itself not present -> 1st ever run of the testing library
fs.mkdirSync(oldPath);
}
const oldFilePath = `${oldPath}/${id}.png`;
// if file not present -> New test
if (!fs.existsSync(oldFilePath)) {
try {
yield fs.writeFileSync(oldFilePath, Buffer.from(data, 'base64'));
console.log(`Old File saved as ${oldFilePath}`);
}
catch (err) {
console.error(`Error writing the file ${oldFilePath}:`, err);
}
}
// existing test
else {
// file already present in /old, check if the contents match, do nothing, else create another file in /new
const bitmap = (0, utils_1.base64_encode)(oldFilePath);
const isMatched = bitmap === data;
if (isMatched) {
// do nothing
}
else {
if (!fs.existsSync(newPath)) {
// If the folder itself not present, create it
fs.mkdirSync(newPath);
console.log(`created the folder ${newPath}`);
}
const newFilePath = `${newPath}/${id}.png`;
try {
yield fs.writeFileSync(newFilePath, Buffer.from(data, 'base64'));
console.log(`New File saved as ${newFilePath}`);
}
catch (err) {
console.error(`Error writing the file ${newFilePath}:`, err);
}
const diffPath = path + '/diff';
if (!fs.existsSync(diffPath)) {
// If the folder itself not present, create it
fs.mkdirSync(diffPath);
}
(0, utils_1.compareImages)(newFilePath, oldFilePath, diffPath, id, showDiffInGrayScale);
}
}
// Send response back
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
message: 'Screenshots generated successfully',
data
}));
}
catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Invalid JSON format' }));
}
}));
}
else if (req.method === 'POST' && req.url === '/generate') {
let body = '';
// Listen for the data event to collect the request body
req.on('data', (chunk) => {
body += chunk.toString(); // Convert buffer to string
});
// End event when all data is received
req.on('end', () => __awaiter(this, void 0, void 0, function* () {
try {
const { path, maxWidth, backgroundColor, metaData } = JSON.parse(body); // Parse the received JSON
yield (0, utils_1.generateHtml)(path, maxWidth, Number(serverPort), backgroundColor, metaData);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
message: 'test.html generated successfully',
path
}));
}
catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Invalid JSON format' }));
}
}));
}
else if (req.method === 'GET' && req.url.startsWith('/update')) {
const parsedUrl = url.parse(req.url, true);
let queryData = parsedUrl.query;
const { path, id, maxWidth } = queryData;
// path = path?.toString()
// maxWidth = Number(maxWidth?.toString() ?? 0)
// End event when all data is received
try {
const oldPath = path + '/old';
const newPath = path + '/new';
const diffPath = path + '/diff';
const oldFilePath = `${oldPath}/${id}.png`;
const newFilePath = `${newPath}/${id}.png`;
const diffFilePath = `${diffPath}/${id}.png`;
try {
yield fs.copyFileSync(newFilePath, oldFilePath);
console.log(`Succesfully copied ${oldFilePath} to ${newFilePath}`);
}
catch (err) {
console.error(`Error copying ${oldFilePath} to ${newFilePath}:`, err);
}
try {
yield fs.unlinkSync(diffFilePath);
console.log(`Succesfully deleted ${diffFilePath}`);
}
catch (err) {
console.error(`Error deleting ${diffFilePath}:`, err);
}
try {
yield fs.unlinkSync(newFilePath);
console.log(`Succesfully deleted ${newFilePath}`);
}
catch (err) {
console.error(`Error deleting ${newFilePath}:`, err);
}
yield (0, utils_1.generateHtml)(path, maxWidth, Number(serverPort));
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
message: `Succesfully updated the test ${id}`,
path
}));
}
catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Invalid JSON format' }));
}
}
else if (req.method === 'GET' && req.url.startsWith('/reset')) {
const parsedUrl = url.parse(req.url, true);
const { path, maxWidth } = parsedUrl.query;
// End event when all data is received
try {
const oldPath = path + '/old';
const newPath = path + '/new';
const diffPath = path + '/diff';
if (fs.existsSync(newPath)) {
const files = yield fs.readdirSync(newPath);
const promises = files.map((file) => __awaiter(this, void 0, void 0, function* () {
const newFilePath = newPath + '/' + file;
const oldFilePath = oldPath + '/' + file;
try {
yield fs.copyFileSync(newFilePath, oldFilePath);
console.log(`Succesfully copied ${oldFilePath} to ${newFilePath}`);
}
catch (err) {
console.log(`Error copying ${oldFilePath} to ${newFilePath}:`, err);
}
}));
Promise.all(promises).then(() => __awaiter(this, void 0, void 0, function* () {
try {
yield fs.rmSync(diffPath, { recursive: true, force: true });
console.log(`Succesfully deleted ${diffPath}`);
}
catch (err) {
console.error(`Error deleting ${diffPath}:`, err);
}
try {
yield fs.rmSync(newPath, { recursive: true, force: true });
console.log(`Succesfully deleted ${newPath}`);
}
catch (err) {
console.error(`Error deleting ${newPath}:`, err);
}
yield (0, utils_1.generateHtml)(path, maxWidth, Number(serverPort));
}));
}
else {
try {
yield fs.rmSync(diffPath, { recursive: true, force: true });
console.log(`Succesfully deleted ${diffPath}`);
}
catch (err) {
console.error(`Error deleting ${diffPath}:`, err);
}
yield (0, utils_1.generateHtml)(path, maxWidth, Number(serverPort));
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'All tests updated successfully', path }));
}
catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Invalid JSON format' }));
}
}
else {
// Handle unsupported routes or methods
res.writeHead(200);
res.end(utils_1.msg);
}
});
})
.listen(serverPort, () => __awaiter(void 0, void 0, void 0, function* () {
console.log(`Screenshot-test-server running on port ${serverPort}`);
if (isHeadLess) {
const browser = yield puppeteer_1.default.launch({ headless: true });
console.log('browser', browser);
page = yield browser.newPage();
yield page.goto(uiUrl);
}
else {
console.log('Render the tests on your emulator / device and hit the "Capture and Compare" button.');
}
}));