Adds support for screenshots
This commit is contained in:
parent
c5ae2b607a
commit
68eeda7aa6
|
@ -2,3 +2,4 @@ node_modules
|
||||||
examples
|
examples
|
||||||
run.sh
|
run.sh
|
||||||
screenshot*
|
screenshot*
|
||||||
|
output
|
||||||
|
|
|
@ -287,3 +287,8 @@ Ouput:
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It is also possible to make screenshots of each slide but removing the considered element.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
locator --output output-directory example.html
|
||||||
|
```
|
||||||
|
|
70
index.js
70
index.js
|
@ -4,6 +4,7 @@ const fs = require('fs').promises;
|
||||||
const process = require('process');
|
const process = require('process');
|
||||||
const puppeteer = require('puppeteer');
|
const puppeteer = require('puppeteer');
|
||||||
const quality = require('./quality.js');
|
const quality = require('./quality.js');
|
||||||
|
const uuid = require('uuid').v4;
|
||||||
|
|
||||||
// Size of the rendering of the web page
|
// Size of the rendering of the web page
|
||||||
const size = { width: 1280, height: 720 };
|
const size = { width: 1280, height: 720 };
|
||||||
|
@ -42,21 +43,36 @@ async function eprintln(data) {
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|
||||||
if (process.argv[2] === undefined) {
|
let outputDir = null;
|
||||||
console.error('This program expects an argument.');
|
let filename = process.argv[2];
|
||||||
console.error('USAGE: locator <path-to-HTML-file>');
|
|
||||||
|
if (process.argv[2] === "-o" || process.argv[2] === "--output") {
|
||||||
|
outputDir = process.argv[3];
|
||||||
|
filename = process.argv[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.mkdir(outputDir);
|
||||||
|
} catch (e) {
|
||||||
|
eprintln("Couldn't create directory " + outputDir + ": " + e);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename === undefined) {
|
||||||
|
eprintln('This program expects an argument.');
|
||||||
|
eprintln('USAGE: locator <path-to-HTML-file>');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path to the HTML file to analyse (given as relative path from current directory)
|
// Path to the HTML file to analyse (given as relative path from current directory)
|
||||||
// We need the full path so that puppeteer is able to access it
|
// We need the full path so that puppeteer is able to access it
|
||||||
const path = process.argv[2].startsWith('/') ? process.argv[2] : process.cwd() + '/' + process.argv[2];
|
const path = filename.startsWith('/') ? filename : process.cwd() + '/' + filename;
|
||||||
|
|
||||||
// Check that the file exists
|
// Check that the file exists
|
||||||
try {
|
try {
|
||||||
await fs.access(path, fs.constants.F_OK);
|
await fs.access(path, fs.constants.F_OK);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('No such file: ' + path);
|
eprintln('No such file: ' + path);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,18 +88,25 @@ async function main() {
|
||||||
|
|
||||||
// If there is no slide, try to run on HTML body
|
// If there is no slide, try to run on HTML body
|
||||||
if (root === null) {
|
if (root === null) {
|
||||||
console.error('Not a marp HTML file.');
|
eprintln('Not a marp HTML file.');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide slides controls
|
||||||
|
await page.evaluate(() => {
|
||||||
|
for (let elt of document.getElementsByClassName('bespoke-marp-osc')) {
|
||||||
|
elt.style.visibility = "hidden";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Take a first screenshot
|
// Take a first screenshot
|
||||||
await page.screenshot({path: __dirname + '/' + 'screenshot1.png'});
|
await page.screenshot({path: (outputDir === null ? __dirname : outputDir) + '/' + 'screenshot1.png'});
|
||||||
|
|
||||||
// Edit the page to shrink elements in order to get better bounding boxes
|
// Edit the page to shrink elements in order to get better bounding boxes
|
||||||
let withSpan = await addSpan(root);
|
let withSpan = await addSpan(root);
|
||||||
|
|
||||||
// Take another screenshot and check the modification we made didn't change the layout of the page
|
// Take another screenshot and check the modification we made didn't change the layout of the page
|
||||||
await page.screenshot({path: __dirname + '/' + 'screenshot2.png'});
|
await page.screenshot({path: (outputDir === null ? __dirname : outputDir) + '/' + 'screenshot2.png'});
|
||||||
|
|
||||||
// Compare both screenshots
|
// Compare both screenshots
|
||||||
let file1 = await fs.readFile(__dirname + '/' + 'screenshot1.png');
|
let file1 = await fs.readFile(__dirname + '/' + 'screenshot1.png');
|
||||||
|
@ -96,7 +119,7 @@ async function main() {
|
||||||
|
|
||||||
// Crash if they're different
|
// Crash if they're different
|
||||||
if (psnr > 70) {
|
if (psnr > 70) {
|
||||||
eprintln("\x1b[33mWarning: " + process.argv[2] + " produced slight diff: psnr = " + psnr + '\x1b[0m');
|
eprintln("\x1b[33mWarning: " + filename + " produced slight diff: psnr = " + psnr + '\x1b[0m');
|
||||||
} else {
|
} else {
|
||||||
await eprintln("\x1b[31mError: age edit changed the layout: psnr = " + psnr + '\x1b[0m');
|
await eprintln("\x1b[31mError: age edit changed the layout: psnr = " + psnr + '\x1b[0m');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
@ -104,13 +127,18 @@ async function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analyse the root and output the result
|
// Analyse the root and output the result
|
||||||
let analyse = await analyseElement(root);
|
let analyse = await analyseElement(root, page, outputDir);
|
||||||
|
|
||||||
if (process.argv[3] === '--flatten') {
|
if (process.argv[3] === '--flatten') {
|
||||||
analyse = flatten(analyse);
|
analyse = flatten(analyse);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(JSON.stringify(analyse, undefined, 4));
|
let json = JSON.stringify(analyse, undefined, 4);
|
||||||
|
if (outputDir === null) {
|
||||||
|
console.log();
|
||||||
|
} else {
|
||||||
|
await fs.writeFile(outputDir + '/annotations.json', json);
|
||||||
|
}
|
||||||
|
|
||||||
await browser.close();
|
await browser.close();
|
||||||
|
|
||||||
|
@ -143,7 +171,7 @@ async function addSpan(element) {
|
||||||
|
|
||||||
// Recursive function to analyse an HTML element.
|
// Recursive function to analyse an HTML element.
|
||||||
// The output is written in hierarchy.
|
// The output is written in hierarchy.
|
||||||
async function analyseElement(element) {
|
async function analyseElement(element, page, outputDir = null) {
|
||||||
// Get some information on the element
|
// Get some information on the element
|
||||||
let tagAttr = await element.getProperty('tagName');
|
let tagAttr = await element.getProperty('tagName');
|
||||||
let tagName = await tagAttr.jsonValue();
|
let tagName = await tagAttr.jsonValue();
|
||||||
|
@ -159,6 +187,7 @@ async function analyseElement(element) {
|
||||||
let analyse = {};
|
let analyse = {};
|
||||||
analyse.tag = tagName;
|
analyse.tag = tagName;
|
||||||
analyse.class = className;
|
analyse.class = className;
|
||||||
|
analyse.uuid = uuid();
|
||||||
analyse.box = box;
|
analyse.box = box;
|
||||||
box.x /= size.width;
|
box.x /= size.width;
|
||||||
box.width /= size.width;
|
box.width /= size.width;
|
||||||
|
@ -166,6 +195,21 @@ async function analyseElement(element) {
|
||||||
box.height /= size.height;
|
box.height /= size.height;
|
||||||
analyse.children = [];
|
analyse.children = [];
|
||||||
|
|
||||||
|
if (outputDir !== null) {
|
||||||
|
// Perform a screenshot where the element is hidden
|
||||||
|
let previousVisibility = await element.evaluate(el => {
|
||||||
|
let previousVisibility = el.style.visibility;
|
||||||
|
el.style.visibility = "hidden";
|
||||||
|
return previousVisibility;
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.screenshot({path: outputDir + '/' + analyse.uuid + '.png'});
|
||||||
|
|
||||||
|
await element.evaluate((el, previousVisibility) => {
|
||||||
|
el.style.visibility = previousVisibility;
|
||||||
|
}, [ previousVisibility ]);
|
||||||
|
}
|
||||||
|
|
||||||
// Extract the text content if it is a span (we made those spans by ourselves in the addSpan function)
|
// Extract the text content if it is a span (we made those spans by ourselves in the addSpan function)
|
||||||
if (tagName === 'SPAN' && textContent !== "") {
|
if (tagName === 'SPAN' && textContent !== "") {
|
||||||
analyse.text = textContent;
|
analyse.text = textContent;
|
||||||
|
@ -178,7 +222,7 @@ async function analyseElement(element) {
|
||||||
|
|
||||||
for (let child of children) {
|
for (let child of children) {
|
||||||
// Recursively analyse the children
|
// Recursively analyse the children
|
||||||
analyse.children.push(await analyseElement(child));
|
analyse.children.push(await analyseElement(child, page, outputDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
return analyse;
|
return analyse;
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"puppeteer": "^19.7.1"
|
"puppeteer": "^19.7.1",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"locator": "index.js"
|
"locator": "index.js"
|
||||||
|
@ -742,6 +743,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||||
|
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"puppeteer": "^19.7.1"
|
"puppeteer": "^19.7.1",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue