kibana/utilities/visual_regression.js
Spencer f0cc432faf
Apache 2.0 license headers (#19383)
In order to make the license that applies to each file as clear as possible, and to be consistent with elasticsearch, we are adding Apache 2.0 license headers to the top of each file.

Existence of this header is enforced by eslint and tslint and missing headers were automatically added in the last commit by running:

```
node scripts/eslint --fix && node scripts/tslint --fix
```
2018-05-28 20:06:30 -07:00

155 lines
4.6 KiB
JavaScript

/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import bluebird, {
fromNode,
promisify,
} from 'bluebird';
import Handlebars from 'handlebars';
import fs from 'fs';
import path from 'path';
import imageDiff from 'image-diff';
import mkdirp from 'mkdirp';
import moment from 'moment';
import SimpleGit from 'simple-git';
const readDirAsync = promisify(fs.readdir);
const readFileAsync = promisify(fs.readFile);
const writeFileAsync = promisify(fs.writeFile);
Handlebars.registerHelper('lte', function lessThanEquals(value, threshold, options) {
if (value <= threshold) {
return options.fn(this);
}
return options.inverse(this);
});
Handlebars.registerHelper('gte', function greaterThanEquals(value, threshold, options) {
if (value >= threshold) {
return options.fn(this);
}
return options.inverse(this);
});
async function buildGallery(comparisons) {
const simpleGit = new SimpleGit();
const asyncBranch = promisify(simpleGit.branch, simpleGit);
const branch = await asyncBranch();
const template = Handlebars.compile(await readFileAsync(
path.resolve('./utilities/templates/visual_regression_gallery.handlebars')
, 'utf8'));
const html = template({
date: moment().format('MMMM Do YYYY, h:mm:ss a'),
branch: branch.current,
hiddenThreshold: 0,
warningThreshold: 0.03,
comparisons,
});
return writeFileAsync(
path.resolve('./test/functional/screenshots/visual_regression_gallery.html'),
html
);
}
async function compareScreenshots() {
const SCREENSHOTS_DIR = 'test/functional/screenshots';
const BASELINE_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'baseline');
const DIFF_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'diff');
const SESSION_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'session');
// We don't need to create the baseline dir because it's committed.
mkdirp.sync(DIFF_SCREENSHOTS_DIR);
mkdirp.sync(SESSION_SCREENSHOTS_DIR);
const files = await readDirAsync(SESSION_SCREENSHOTS_DIR);
const screenshots = files.filter(file => file.indexOf('.png') !== -1);
// We'll use this data to build a screenshot gallery in HTML.
return await bluebird.map(screenshots, async screenshot => {
// We're going to load image data and cache it in this object.
const comparison = {
name: screenshot,
change: undefined,
percentage: undefined,
imageData: {
session: undefined,
baseline: undefined,
diff: undefined,
}
};
const sessionImagePath = path.resolve(
SESSION_SCREENSHOTS_DIR,
screenshot
);
const baselineImagePath = path.resolve(
BASELINE_SCREENSHOTS_DIR,
screenshot
);
const diffImagePath = path.resolve(
DIFF_SCREENSHOTS_DIR,
screenshot
);
// Diff the images asynchronously.
const diffResult = await fromNode((cb) => {
imageDiff.getFullResult({
actualImage: sessionImagePath,
expectedImage: baselineImagePath,
diffImage: diffImagePath,
shadow: true,
}, cb);
});
const change = diffResult.percentage;
const changePercentage = (change * 100).toFixed(2);
console.log(`(${changePercentage}%) ${screenshot}`);
comparison.percentage = changePercentage;
comparison.change = change;
// Once the images have been diffed, we can load and store the image data.
comparison.imageData.session =
await readFileAsync(sessionImagePath, 'base64');
comparison.imageData.baseline =
await readFileAsync(baselineImagePath, 'base64');
comparison.imageData.diff =
await readFileAsync(diffImagePath, 'base64');
return comparison;
});
}
export function run(done) {
compareScreenshots().then(screenshotComparisons => {
// Once all of the data has been loaded, we can build the gallery.
buildGallery(screenshotComparisons).then(() => {
done();
});
}, error => {
console.error(error);
done(false);
});
}