Skip to content

Instantly share code, notes, and snippets.

@zeropaper
Last active September 24, 2019 08:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeropaper/7e92442c5a282fda8616ab797f524cdc to your computer and use it in GitHub Desktop.
Save zeropaper/7e92442c5a282fda8616ab797f524cdc to your computer and use it in GitHub Desktop.
puppeteer tips and tricks
const renderScreenshot = ({
title = '',
slug = '',
pageIndex = 0,
screenshotPath = '',
description = '',
}) => `<figure id="${slug}-${pageIndex}">
<img src="${screenshotPath}" title="${title}" />
<figcaption>${description}</figcaption>
</figure>`;
const renderSection = ({
title = '',
slug = '',
screenshots = [],
}) => `<section id="${slug}">
<header>
<h1>${title}</h1>
</header>
<div class="content">
<div class="screenshots">
${screenshots.map(renderScreenshot).join('\n')}
</div>
<div class="description">
</div>
</div>
</div>
</section>`;
module.exports = async ({
title = 'Documentation',
info = {},
} = {}) => `<html>
<head>
<title>${title}</title>
<style>
section .content {
display: flex;
}
section .screenshots,
section .description {
flex-grow: 1;
width: 50%;
}
section figure,
section img {
max-width: 100%;
}
</style>
</head>
<body>
<header>
<h1>${title}</h1>
</header>
${info.map(renderSection).join('\n')}
</body>
</html>`;
/* eslint-disable no-console */
const NodeEnvironment = require('jest-environment-node');
const puppeteer = require('puppeteer');
const path = require('path');
const rimraf = require('rimraf');
const mkdirp = require('mkdirp');
const httpServer = require('http-server');
const { promisify } = require('util');
const { writeFile: fsWriteFile } = require('fs');
const documentationTemplate = require('./e2e.documentation-template');
const writeFile = promisify(fsWriteFile);
const slugify = str => str
.trim()
.toLowerCase()
.split(/[^a-z0-9]+/g)
.join('-');
const {
TEST_KEEP_BROWSER,
NODE_WATCH,
HEADLESS,
JEST_SERVE,
SLOWMO,
} = process.env;
const watching = (!!TEST_KEEP_BROWSER || !!NODE_WATCH) && !HEADLESS;
const docsOutputDir = path.resolve(__dirname, '../test-results/docs');
const processDocsScreenshot = ({
title,
slug,
testPages,
number,
}) => async ({
pageIndex = 0,
description = '',
}) => {
const screenshotPath = path
.join(docsOutputDir, 'screenshots', `${number}-${pageIndex}-${slug}.png`);
await testPages[pageIndex].screenshot({
path: screenshotPath,
});
return {
title,
slug,
pageIndex,
screenshotPath: path.relative(docsOutputDir, screenshotPath),
description,
};
};
class PuppeteerEnvironment extends NodeEnvironment {
async setup() {
await super.setup();
this.counter = 0;
this.docsInfo = [];
if (!watching || JEST_SERVE) {
this.server = httpServer.createServer({ root: path.resolve(__dirname, '../build') });
await new Promise((res, rej) => this.server.listen(5000, err => (err ? rej(err) : res())));
}
rimraf.sync(docsOutputDir);
mkdirp.sync(path.join(docsOutputDir, 'screenshots'));
const commonOptions = {
headless: !watching,
slowMo: (!watching || SLOWMO) ? parseInt(SLOWMO || 20, 10) : 0,
};
this.global.testSlowMo = commonOptions.slowMo;
this.global.testWatching = commonOptions.watching;
const browser = await puppeteer.launch({
...commonOptions,
});
this.global.testPages = [
await browser.newPage(),
];
this.global.writeDocs = async ({
info = {},
screenshots = [],
} = {}) => {
const {
title = 'Missing title',
description = '',
} = info;
const generated = {
title,
description,
screenshots: await Promise
.all(screenshots
.map(processDocsScreenshot({
title,
slug: slugify(title),
testPages: this.global.testPages,
number: this.docsInfo.length,
}))),
};
this.docsInfo.push(generated);
};
}
async teardown() {
await new Promise(res => setTimeout(res, 200 * (this.global.slowMo + 1)));
if (!TEST_KEEP_BROWSER) {
await Promise.all((this.global.testPages || [])
.map(async (page) => {
try {
const br = await page.browser();
await closePage(br, page);
await br.close();
} catch (err) {
console.warn(err.message);
}
}));
}
await writeFile(path.join(docsOutputDir, 'index.html'), await documentationTemplate({
title: 'Mesh Documenation',
info: this.docsInfo,
}), 'utf8');
if (this.server) this.server.close();
await super.teardown();
}
runScript(script) {
return super.runScript(script);
}
}
module.exports = PuppeteerEnvironment;
module.exports = {
testMatch: ['**/test/**/*e2e.test.js'],
testRegex: null,
verbose: true,
testEnvironment: absPath('./e2e.environment.js'),
setupFiles: [
absPath('./e2e.init'),
],
};
describe('Some interaction', () => {
// … do something
await writeDocs({
info: {
title: 'Title of the "step"',
},
screenshots: [
{
pageIndex: 0,
description: 'The connection is established and the chat opens on Alice\'s app.',
},
{
pageIndex: 1,
description: 'As well as Bernard\'s.',
},
],
});
// … continue and do something else
await writeDocs({
info: {
title: 'An other "step"',
},
screenshots: [
{
pageIndex: 0,
description: 'Alice sends a message.',
},
],
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment