Introduce screenshot tests
This patch introduces a goldentest.js. The tests inside the file should rely on "golden" results rather then asserts. For now, goldentest.js allows only image expectations. If the actual result doesn't match the expected result, the two files are created under `test/output` folder: - The '-actual.png' contains the actual test result - The '-diff.png' contains the diff between images
This commit is contained in:
parent
28a3343d2c
commit
242a6a6e73
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
/node_modules/
|
||||
/test/output
|
||||
/.local-chromium/
|
||||
/.dev_profile*
|
||||
.DS_Store
|
||||
|
10
package.json
10
package.json
@ -5,8 +5,9 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test-puppeteer": "jasmine test/test.js",
|
||||
"test-golden": "jasmine test/goldentest.js",
|
||||
"test-phantom": "python third_party/phantomjs/test/run-tests.py",
|
||||
"test": "npm run test-puppeteer && npm run test-phantom",
|
||||
"test": "npm run test-puppeteer && npm run test-golden && npm run test-phantom",
|
||||
"install": "node install.js"
|
||||
},
|
||||
"author": "The Chromium Authors",
|
||||
@ -22,9 +23,10 @@
|
||||
"chromium_revision": "478524"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ncp": "^2.0.0",
|
||||
"minimist": "^1.2.0",
|
||||
"deasync": "^0.1.9",
|
||||
"jasmine": "^2.6.0"
|
||||
"jasmine": "^2.6.0",
|
||||
"minimist": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"pixelmatch": "^4.0.2"
|
||||
}
|
||||
}
|
||||
|
44
test/assets/grid.html
Normal file
44
test/assets/grid.html
Normal file
@ -0,0 +1,44 @@
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
function generatePalette(amount) {
|
||||
var result = [];
|
||||
var hueStep = 360 / amount;
|
||||
for (var i = 0; i < amount; ++i)
|
||||
result.push('hsl(' + (hueStep * i) + ', 100%, 90%)');
|
||||
return result;
|
||||
}
|
||||
|
||||
var palette = generatePalette(100);
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var box = document.createElement('div');
|
||||
box.classList.add('box');
|
||||
box.textContent = i;
|
||||
box.style.setProperty('background-color', palette[i % palette.length]);
|
||||
document.body.appendChild(box);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
/* Hide scrollbar so that it is not captured on screenshots */
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.box {
|
||||
font-family: arial;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid darkgray;
|
||||
}
|
||||
</style>
|
BIN
test/golden/screenshot-clip-rect.png
Normal file
BIN
test/golden/screenshot-clip-rect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
test/golden/screenshot-sanity.png
Normal file
BIN
test/golden/screenshot-sanity.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
136
test/goldentest.js
Normal file
136
test/goldentest.js
Normal file
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var rm = require('rimraf').sync;
|
||||
var Browser = require('../lib/Browser');
|
||||
var StaticServer = require('./StaticServer');
|
||||
var PNG = require('pngjs').PNG;
|
||||
var pixelmatch = require('pixelmatch');
|
||||
|
||||
var PORT = 8907;
|
||||
var STATIC_PREFIX = 'http://localhost:' + PORT;
|
||||
var GOLDEN_DIR = path.join(__dirname, 'golden');
|
||||
var OUTPUT_DIR = path.join(__dirname, 'output');
|
||||
|
||||
describe('GoldenTests', function() {
|
||||
var browser;
|
||||
var staticServer;
|
||||
var page;
|
||||
|
||||
beforeAll(function() {
|
||||
browser = new Browser();
|
||||
staticServer = new StaticServer(path.join(__dirname, 'assets'), PORT);
|
||||
if (fs.existsSync(OUTPUT_DIR))
|
||||
rm(OUTPUT_DIR);
|
||||
});
|
||||
|
||||
afterAll(function() {
|
||||
browser.close();
|
||||
staticServer.stop();
|
||||
});
|
||||
|
||||
beforeEach(SX(async function() {
|
||||
page = await browser.newPage();
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
page.close();
|
||||
});
|
||||
|
||||
imageTest('screenshot-sanity.png', async function() {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.navigate(STATIC_PREFIX + '/grid.html');
|
||||
return page.screenshot('png');
|
||||
});
|
||||
|
||||
imageTest('screenshot-clip-rect.png', async function() {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.navigate(STATIC_PREFIX + '/grid.html');
|
||||
return page.screenshot('png', {
|
||||
x: 50,
|
||||
y: 100,
|
||||
width: 150,
|
||||
height: 100
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} fileName
|
||||
* @param {function():!Promise} runner
|
||||
*/
|
||||
function imageTest(fileName, runner) {
|
||||
var expectedPath = path.join(GOLDEN_DIR, fileName);
|
||||
var actualPath = path.join(OUTPUT_DIR, fileName);
|
||||
var expected = null;
|
||||
if (fs.existsSync(expectedPath)) {
|
||||
var buffer = fs.readFileSync(expectedPath);
|
||||
expected = PNG.sync.read(buffer);
|
||||
}
|
||||
it(fileName, SX(async function() {
|
||||
var imageBuffer = await runner();
|
||||
if (!imageBuffer || !(imageBuffer instanceof Buffer)) {
|
||||
fail(fileName + ' test did not return Buffer with image.');
|
||||
return;
|
||||
}
|
||||
var actual = PNG.sync.read(imageBuffer);
|
||||
if (!expected) {
|
||||
ensureOutputDir();
|
||||
fs.writeFileSync(addSuffix(actualPath, '-actual'), imageBuffer);
|
||||
fail(fileName + ' is missing in golden results.');
|
||||
return;
|
||||
}
|
||||
if (expected.width !== actual.width || expected.height !== actual.height) {
|
||||
ensureOutputDir();
|
||||
fs.writeFileSync(addSuffix(actualPath, '-actual'), imageBuffer);
|
||||
fail(`Sizes differ: expected image ${expected.width}px X ${expected.height}px, but got ${actual.width}px X ${actual.height}px`);
|
||||
return;
|
||||
}
|
||||
var diff = new PNG({width: expected.width, height: expected.height});
|
||||
var count = pixelmatch(expected.data, actual.data, diff.data, expected.width, expected.height, {threshold: 0.1});
|
||||
if (count > 0) {
|
||||
ensureOutputDir();
|
||||
fs.writeFileSync(addSuffix(actualPath, '-actual'), imageBuffer);
|
||||
fs.writeFileSync(addSuffix(actualPath, '-diff'), PNG.sync.write(diff));
|
||||
fail(fileName + ' mismatch!');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function ensureOutputDir() {
|
||||
if (!fs.existsSync(OUTPUT_DIR))
|
||||
fs.mkdirSync(OUTPUT_DIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @param {string} suffix
|
||||
* @return {string}
|
||||
*/
|
||||
function addSuffix(filePath, suffix) {
|
||||
var dirname = path.dirname(filePath);
|
||||
var ext = path.extname(filePath);
|
||||
var name = path.basename(filePath, ext);
|
||||
return path.join(dirname, name + suffix + ext);
|
||||
}
|
||||
|
||||
// Since Jasmine doesn't like async functions, they should be wrapped
|
||||
// in a SX function.
|
||||
function SX(fun) {
|
||||
return done => Promise.resolve(fun()).then(done).catch(done.fail);
|
||||
}
|
Loading…
Reference in New Issue
Block a user