Skip to content

Commit

Permalink
Merge pull request #38 from rmachado-studocu/add-screen-name-generato…
Browse files Browse the repository at this point in the history
…r-in-options

feat: adds option `screenshotNameGenerator` to customize screenshots' name
  • Loading branch information
philipp-winterle committed Sep 7, 2022
2 parents 44d094c + 1227a81 commit bd784f8
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 33 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -4,3 +4,5 @@ tmp/*
yarn-error.log
test.js
.vscode
test/screenshots
test/*.css
35 changes: 18 additions & 17 deletions README.md
Expand Up @@ -177,23 +177,24 @@ The CLI usage is not implemented yet :scream:. At the moment there is no need of

## Options

| Property | Values | Description |
| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **urls** | Array | An array containing the urls to check the css against. Has to be at least 1 url. |
| css | String | Optional. Can be a plain css string or path to a css file or empty. If it is a path it has to end with `.css`! Otherwise it is not recognized as a path. If not set, the whole website css will be taken. |
| timeout | Number | Optional. Integer number of milliseconds to wait for a page to navigate to. After timeout is reached the page navigation is aborted. **ATTENTION**: The critical css of the url timed out is not included. Default: 30000 |
| pageLoadTimeout | Number | Optional. After the page load event is fired the pageLoadTimeout is started. After the amount of milliseconds the ongoing loading of assets or xhr requests is stopped and the extraction continues. Default: 2000 |
| outputRemainingCss | Boolean | Optional. If set to false the result obj will not contain any rest css. Only an empty string will be given. Default: true |
| browser | Object | Optional. Configuration object of browser. E.g. userAgent, ... See documentation for [browser object](#browser-options). |
| device | Object | Optional. Configuration object of device. E.g. width, height, ... See documentation for [device object](#device-options). |
| puppeteer | Object | Optional. Configuration object of puppeteer options like an already existing browser instance or a path to a Chrome instead of the used Chromium. See documentation for [puppeteer object](#puppeteer-options). |
| printBrowserConsole | Boolean | Optional. If set to true prints console output of urls to the stdoutput. Defaults: false |
| dropKeyframes | Boolean | Optional. If set to false keeps keyframes as critical css content. Otherwise they are removed. Default: false |
| takeScreenshots | Boolean | Optional. If set a screenshot is taken for every url processed. Default: false |
| screenshotPath | String | Optional. The path the screenshots will be saved to. Default: "." (execution path) |
| keepSelectors | Array | Optional. Every CSS Selector in this array will be kept as part of the critical css even if they are not part of it. You can use wildcards (%) to capture more rules with one entry. [Read more](#wildcards). Default: [] |
| removeSelectors: | Array | Optional. Every CSS Selector in this array will be removed of the critical css even if they are part of it. You can use wildcards (%) to capture more rules with one entry. [Read more](#wildcards). Default: [] |
| blockRequests | Array | Optional. Some of the requests made by pages are an |
| Property | Values | Description |
| ----------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **urls** | Array | An array containing the urls to check the css against. Has to be at least 1 url. |
| css | String | Optional. Can be a plain css string or path to a css file or empty. If it is a path it has to end with `.css`! Otherwise it is not recognized as a path. If not set, the whole website css will be taken. |
| timeout | Number | Optional. Integer number of milliseconds to wait for a page to navigate to. After timeout is reached the page navigation is aborted. **ATTENTION**: The critical css of the url timed out is not included. Default: 30000 |
| pageLoadTimeout | Number | Optional. After the page load event is fired the pageLoadTimeout is started. After the amount of milliseconds the ongoing loading of assets or xhr requests is stopped and the extraction continues. Default: 2000 |
| outputRemainingCss | Boolean | Optional. If set to false the result obj will not contain any rest css. Only an empty string will be given. Default: true |
| browser | Object | Optional. Configuration object of browser. E.g. userAgent, ... See documentation for [browser object](#browser-options). |
| device | Object | Optional. Configuration object of device. E.g. width, height, ... See documentation for [device object](#device-options). |
| puppeteer | Object | Optional. Configuration object of puppeteer options like an already existing browser instance or a path to a Chrome instead of the used Chromium. See documentation for [puppeteer object](#puppeteer-options). |
| printBrowserConsole | Boolean | Optional. If set to true prints console output of urls to the stdoutput. Defaults: false |
| dropKeyframes | Boolean | Optional. If set to false keeps keyframes as critical css content. Otherwise they are removed. Default: false |
| takeScreenshots | Boolean | Optional. If set a screenshot is taken for every url processed. Default: false |
| screenshotPath | String | Optional. The path the screenshots will be saved to. Default: "." (execution path) |
| screenshotNameGenerator | (url: string) => Promise<string> | Optional. Function that receives the processed URL as an argument and returns a `Promise` that resolves with the name to be used for the screenshot. When not provided, the URL's name will be sanitized and used as a filename. |
| keepSelectors | Array | Optional. Every CSS Selector in this array will be kept as part of the critical css even if they are not part of it. You can use wildcards (%) to capture more rules with one entry. [Read more](#wildcards). Default: [] |
| removeSelectors: | Array | Optional. Every CSS Selector in this array will be removed of the critical css even if they are part of it. You can use wildcards (%) to capture more rules with one entry. [Read more](#wildcards). Default: [] |
| blockRequests | Array | Optional. Some of the requests made by pages are an |

### Browser options

Expand Down
2 changes: 2 additions & 0 deletions lib/Constants.js
Expand Up @@ -28,4 +28,6 @@ module.exports = {

// CODE BASED
RULE_SEPARATOR: '-#-',

SCREENSHOT_NAME_GENERATOR: null,
};
10 changes: 9 additions & 1 deletion lib/classes/Crittr.class.js
Expand Up @@ -80,6 +80,7 @@ class Crittr {
dropKeyframes: DEFAULTS.DROP_KEYFRAMES,
takeScreenshots: DEFAULTS.PAGE_SCREENSHOT,
screenshotPath: path.join('.'),
screenshotNameGenerator: DEFAULTS.SCREENSHOT_NAME_GENERATOR,
keepSelectors: [],
removeSelectors: [],
blockRequests: [
Expand Down Expand Up @@ -723,7 +724,14 @@ class Crittr {
debug('evaluateUrl - Extracting critical selectors');
await page.waitForTimeout(250); // Needed because puppeteer sometimes isn't able to handle quick tab openings
if (this.options.takeScreenshots === true) {
const screenName = url.replace(/[^\w\s]/gi, '_') + '.png';
let screenName = url.replace(/[^\w\s]/gi, '_') + '.png';
if (typeof this.options.screenshotNameGenerator === 'function') {
const screenGeneratedName = await this.options.screenshotNameGenerator(url);
screenName = `${screenGeneratedName}.png`;
}

await fs.mkdirp(this.options.screenshotPath);

await page.screenshot({
path: path.join(this.options.screenshotPath, screenName),
type: 'png',
Expand Down
45 changes: 45 additions & 0 deletions test/setup.js
Expand Up @@ -2,6 +2,7 @@ const Critter = require('../index');
const fs = require('fs-extra');
const path = require('path');
const merge = require('deepmerge');
const crypto = require('crypto');

const rootDir = path.join(__dirname, '..');
const staticServer = require('../lib/helper/localFileServer')(rootDir);
Expand Down Expand Up @@ -39,6 +40,36 @@ const noCssOptions = merge(defaultOptions, {
css: null,
});

const screenshotOptions = merge(defaultOptions, {
urls: [
'http://localhost:8000/test/data/test.html?1',
'http://localhost:8000/test/data/test.html?2',
'http://localhost:8000/test/data/test.html?3',
'http://localhost:8000/test/data/test.html?4',
],
css: testData.css,
takeScreenshots: true,
screenshotPath: path.join(__dirname, 'screenshots', 'normal'),
});

const screenNameGenerator = async (url) => {
const sha1HashGen = crypto.createHash('sha1');
sha1HashGen.update(url);
return sha1HashGen.digest('hex');
}
const screenshotWithFunctionOptions = merge(defaultOptions, {
urls: [
'http://localhost:8000/test/data/test.html?1',
'http://localhost:8000/test/data/test.html?2',
'http://localhost:8000/test/data/test.html?3',
'http://localhost:8000/test/data/test.html?4',
],
css: testData.css,
takeScreenshots: true,
screenshotPath: path.join(__dirname, 'screenshots', 'withFunction'),
screenshotNameGenerator: screenNameGenerator,
});

module.exports = () => {
return new Promise(async (resolve, reject) => {
const server = staticServer
Expand All @@ -57,6 +88,20 @@ module.exports = () => {

fs.writeFileSync(path.join(rootDir, './test/test_result_noCss_remaining.css'), noCssRest, 'utf-8');

// Third Run for URL
let { critical: screenshotCssCritical, rest: screenshotCssRest } = await Critter(screenshotOptions);

fs.writeFileSync(path.join(rootDir, './test/test_result_screenshotCss.css'), screenshotCssCritical, 'utf-8');

fs.writeFileSync(path.join(rootDir, './test/test_result_screenshotCss_remaining.css'), screenshotCssRest, 'utf-8');

// Fourth Run for URL
let { critical: screenshotWithFunctionCssCritical, rest: screenshotWithFunctionCssRest } = await Critter(screenshotWithFunctionOptions);

fs.writeFileSync(path.join(rootDir, './test/test_result_screenshotWithFunctionCss.css'), screenshotWithFunctionCssCritical, 'utf-8');

fs.writeFileSync(path.join(rootDir, './test/test_result_screenshotWithFunctionCss_remaining.css'), screenshotWithFunctionCssRest, 'utf-8');

resolve();
} catch (err) {
reject(err);
Expand Down
22 changes: 7 additions & 15 deletions test/teardown.js
Expand Up @@ -3,19 +3,11 @@ const path = require('path');
const rootDir = path.join(__dirname, '..');

module.exports = async function () {
fs.unlink(path.join(rootDir, './test/test_result.css'), err => {
if (err) throw err;
});

fs.unlink(path.join(rootDir, './test/test_result_remaining.css'), err => {
if (err) throw err;
});

fs.unlink(path.join(rootDir, './test/test_result_noCss.css'), err => {
if (err) throw err;
});

fs.unlink(path.join(rootDir, './test/test_result_noCss_remaining.css'), err => {
if (err) throw err;
});
// Cleans up artifacts (`.css` files and `screenshots` folder from `test`)
const files = await fs.readdir(__dirname);
await Promise.all(
files
.filter(fileOrFolder => fileOrFolder.endsWith('.css') || fileOrFolder === 'screenshots')
.map(fileOrFolder => fs.remove(path.join('./test/', fileOrFolder))),
);
};
40 changes: 40 additions & 0 deletions test/tests/screenshot.test.js
@@ -0,0 +1,40 @@
const fs = require('fs-extra');
const path = require('path');
const crypto = require('crypto');

const rootDir = path.join(__dirname, '..', '..');
const helpers = require('./../helpers.js');
const Rule = require(path.join(rootDir, 'lib/classes/Rule.class'));

const urls = [
'http://localhost:8000/test/data/test.html?1',
'http://localhost:8000/test/data/test.html?2',
'http://localhost:8000/test/data/test.html?3',
'http://localhost:8000/test/data/test.html?4',
];

describe('Screenshots', () => {
describe('Check screenshot names', () => {
test('Check that the normal screenshots were generated with the name based on the URL', async () => {
const files = await fs.readdir(path.join(__dirname, '..', 'screenshots', 'normal'));
expect(
urls.every(url => {
const expectedScreenName = url.replace(/[^\w\s]/gi, '_') + '.png';
return files.includes(expectedScreenName);
})
).toBeTruthy();
});

test('Check that the screenshots with a name generator function were generated with the name as the URL SHA1 hashed', async () => {
const files = await fs.readdir(path.join(__dirname, '..', 'screenshots', 'withFunction'));
expect(
urls.every(url => {
const sha1 = crypto.createHash('sha1');
sha1.update(url);
const expectedScreenName = `${sha1.digest('hex')}.png`;
return files.includes(expectedScreenName);
})
).toBeTruthy();
});
});
});

0 comments on commit bd784f8

Please sign in to comment.