Skip to content

Commit 339e65b

Browse files
committed
Improve error handling by providing screenshots
1 parent a0972ac commit 339e65b

File tree

1 file changed

+42
-14
lines changed

1 file changed

+42
-14
lines changed

graph-visualization/renderVisualizations.js

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ console.log(`renderVisualizations.js: dirname=${__dirname}`);
1313

1414
/**
1515
* Crops the image in the buffer so that there is no empty frame around it.
16-
* @param {Buffer} buffer
16+
* @param {Buffer} buffer
1717
* @returns Buffer
1818
*/
1919
const autoCropImageBuffer = async (buffer) => {
@@ -31,41 +31,71 @@ const autoCropImageBuffer = async (buffer) => {
3131
*/
3232
const camelToKebabCase = (str) => str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
3333

34+
/**
35+
* Take a screenshot after an error happened.
36+
*
37+
* @param {string} htmlFilename
38+
* @param {string} reason
39+
*/
40+
const makeScreenshotOfError = async (page, htmlFilename, reason) => {
41+
const reportName = basename(htmlFilename, ".html");
42+
const directoryName = camelToKebabCase(reportName);
43+
console.log(`Taking an error screenshot of report ${reportName}. Reason: ${reason}`);
44+
await page.screenshot({ path: `./${directoryName}/error-${reportName}-${reason}.png`, omitBackground: false });
45+
};
46+
47+
/**
48+
* Handle a catched error by taking a screenshot.
49+
*
50+
* @param {string} htmlFilename
51+
* @param {string} reason
52+
* @return
53+
*/
54+
const handleErrorWithScreenshot = (page, htmlFilename, reason) => async (error) => {
55+
await makeScreenshotOfError(page, htmlFilename, reason);
56+
throw new Error(error);
57+
};
3458
/**
3559
* Creates a new page and takes a screenshot of all canvas tags that are contained within a div.
3660
* Note: Don't run this in parallel. See https://github.com/puppeteer/puppeteer/issues/1479
3761
* @param {Browser} browser
3862
* @param {string} htmlFilename
3963
*/
4064
const takeCanvasScreenshots = async (browser, htmlFilename) => {
65+
const reportName = basename(htmlFilename, ".html");
66+
const directoryName = camelToKebabCase(reportName);
67+
if (!existsSync(directoryName)) {
68+
mkdirSync(directoryName);
69+
}
70+
4171
const page = await browser.newPage();
4272
await page.setViewport({ width: 1500, height: 1000, isMobile: false, isLandscape: true, hasTouch: false, deviceScaleFactor: 1 });
4373

4474
console.log(`Loading ${htmlFilename}`);
4575
await page.goto(`file://${htmlFilename}`);
4676

77+
if (!process.env.NEO4J_INITIAL_PASSWORD) {
78+
throw new Error("Missing environment variable NEO4J_INITIAL_PASSWORD");
79+
}
4780
// Login with Neo4j server password from the environment variable NEO4J_INITIAL_PASSWORD
4881
const loginButton = await page.waitForSelector("#neo4j-server-login");
4982
await page.type("#neo4j-server-password", process.env.NEO4J_INITIAL_PASSWORD);
5083
await loginButton.click();
5184

5285
// Wait for the graph visualization to be rendered onto a HTML5 canvas
5386
console.log(`Waiting for visualizations to be finished`);
54-
await page.waitForSelector(".visualization-finished", { timeout: 90_000 });
87+
await page
88+
.waitForSelector(".visualization-finished", { timeout: 90_000 })
89+
.catch(handleErrorWithScreenshot(page, htmlFilename, "visualization-did-not-finish"));
5590

5691
// Get all HTML canvas tag elements
5792
const canvasElements = await page.$$("canvas");
5893
if (canvasElements.length <= 0) {
59-
console.error(`No elements with CSS selector 'canvas' found in ${htmlFilename}`);
94+
await makeScreenshotOfError(page, htmlFilename, "no-canvas-found");
6095
}
6196
console.log(`Found ${canvasElements.length} visualizations`);
6297

6398
// Take a png screenshot of every canvas element and save them with increasing indices
64-
const reportName = basename(htmlFilename, ".html");
65-
const directoryName = camelToKebabCase(reportName);
66-
if (!existsSync(directoryName)) {
67-
mkdirSync(directoryName);
68-
}
6999
await Promise.all(
70100
Array.from(canvasElements).map(async (canvasElement, index) => {
71101
console.log(`Exporting image ${reportName}-${index}.png...`);
@@ -74,10 +104,8 @@ const takeCanvasScreenshots = async (browser, htmlFilename) => {
74104
}, canvasElement);
75105
let data = Buffer.from(dataUrl.split(",").pop(), "base64");
76106
console.log(`Cropping image ${reportName}-${index}.png...`);
77-
data = await autoCropImageBuffer(data);
107+
data = await autoCropImageBuffer(data).catch(handleErrorWithScreenshot(page, htmlFilename, `failed-to-crop-image-${index}`));
78108
writeFileSync(`./${directoryName}/${reportName}-${index}.png`, data);
79-
// console.log(`Taking screenshot ${reportName} of canvas ${index} in ${htmlFilename} of element...`);
80-
// await canvasElement.screenshot({ path: `./${directoryName}/${reportName}-${index}.png`, omitBackground: true });
81109
})
82110
);
83111
};
@@ -89,13 +117,13 @@ let browser;
89117
* and takes a screenshot of the canvas elements using {@link takeCanvasScreenshots}.
90118
*/
91119
(async () => {
92-
console.log('renderVisualizations.js: Starting headless browser...');
120+
console.log("renderVisualizations.js: Starting headless browser...");
93121
browser = await puppeteer.launch({ headless: "new" }); // { headless: false } for testing
94122

95123
// Get all *.html files in this (script) directory and its subdirectories
96-
// The separate filter is needed to ignore the "node_modules" directory.
124+
// The separate filter is needed to ignore the "node_modules" directory.
97125
// Glob's build-in filter doesn't seem to work on Windows.
98-
const htmlFiles = globSync(`${__dirname}/**/*.html`, { absolute: true }).filter(file => !file.includes('node_modules'));
126+
const htmlFiles = globSync(`${__dirname}/**/*.html`, { absolute: true }).filter((file) => !file.includes("node_modules"));
99127
for (const htmlFile of htmlFiles) {
100128
await takeCanvasScreenshots(browser, htmlFile);
101129
}

0 commit comments

Comments
 (0)