UNPKG

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
"use strict"; 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.'); } }));