dom-to-image-more
Version:
Generates an image from a DOM node using HTML5 canvas and SVG
147 lines (136 loc) • 6.33 kB
JavaScript
const fs = require('fs');
const path = require('path');
// Dev-only middleware: when running with UPDATE_CONTROLS=1, the spec POSTs each
// freshly-rendered image here and we overwrite the matching control-image file.
// The browser can't write files itself, so it round-trips through the karma server.
// IMPORTANT: control images are environment-specific (OS font rasterization, DPR),
// so only regenerate them in the same environment that runs the suite in CI.
function controlUpdaterMiddleware() {
return function (request, response, next) {
if (request.method !== 'POST' || request.url !== '/__update_control__') {
return next();
}
let body = '';
request.setEncoding('utf8');
request.on('data', (chunk) => (body += chunk));
request.on('end', () => {
try {
const payload = JSON.parse(body);
const rel = String(payload.path || '').replace(/\\/g, '/');
if (!rel || rel.indexOf('..') !== -1 || rel.startsWith('/')) {
throw new Error('refusing unsafe control path: ' + rel);
}
const target = path.join(
path.dirname(__filename),
'spec',
'resources',
rel
);
fs.writeFileSync(target, payload.data, 'utf8');
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.end('updated ' + rel);
} catch (e) {
response.writeHead(500, { 'Content-Type': 'text/plain' });
response.end('error: ' + e.message);
}
});
};
}
module.exports = function (config) {
const updateControls = !!process.env.UPDATE_CONTROLS;
// LOGIC_ONLY skips the OS-font-dependent `compareToControlImage` image tests (tagged
// with the spec's `itImage` helper) so the OS-robust logic subset can run anywhere —
// notably in CI. The full suite (default) stays the local/WSL gate.
const logicOnly = !!process.env.LOGIC_ONLY;
// Run Chrome headless on display-less hosts (CI). GitHub Actions sets CI=true.
const headless = !!process.env.HEADLESS || !!process.env.CI;
// Browser to run: `chrome` (default) or `firefox`. See customLaunchers below.
// Namespaced (not `BROWSER`, which WSL/Linux desktops set to the default web
// browser, e.g. `wslview`, and would otherwise be picked up here).
const browser = process.env.KARMA_BROWSER || 'chrome';
// Device-pixel-ratio for the browser. Pinned to 1 by default so renders match the
// static reference images regardless of the host's display scaling. Override for
// ad-hoc high-DPI / fractional-DPR verification, e.g. `DPR=1.25 npm test` (the
// image-comparison tests will then differ from the 1x controls — regenerate with
// UPDATE_CONTROLS=1 at that DPR if you intend to re-baseline).
const deviceScaleFactor = process.env.DPR || '1';
// Run only the tests whose full Mocha title (describe + it) matches this
// substring/regex, e.g. `GREP=border npm run test:chrome` for one group, or a
// single test by its exact name. Empty (default) runs everything.
const grep = process.env.GREP || '';
config.set({
basePath: '',
frameworks: ['mocha', 'chai'],
concurrency: 1,
files: [
{
pattern: 'spec/resources/**/*',
included: false,
served: true,
},
{
pattern: 'tests/fontawesome/webfonts/*.*',
included: false,
served: true,
},
{
pattern: 'tests/fontawesome/css/*.*',
included: false,
served: true,
},
'tests/tesseract-4.0.2.min.js',
'src/dom-to-image-more.js',
'spec/dom-to-image-more.spec.js',
],
exclude: [],
preprocessors: {},
reporters: ['mocha'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
client: {
captureConsole: true,
// Tells the spec to POST renders to the updater middleware instead
// of asserting against the existing control images.
updateControls: updateControls,
// Tells the spec's `itImage` helper to skip image-comparison tests.
logicOnly: logicOnly,
// Mocha options; `grep` (from GREP env) runs only matching tests.
mocha: grep ? { grep: grep } : {},
},
// Register the updater plugin, but only insert it into the request
// pipeline when UPDATE_CONTROLS=1 (beforeMiddleware runs ahead of karma's
// file server so our POST route isn't treated as a missing file).
plugins: [
'karma-*',
{ 'middleware:control-updater': ['factory', controlUpdaterMiddleware] },
],
beforeMiddleware: updateControls ? ['control-updater'] : [],
autoWatch: true,
// Which browser to run, `chrome` (default) or `firefox`. Chrome bakes the
// control images, so Firefox should run only the OS-robust subset (pair with
// LOGIC_ONLY=1 — image tests would otherwise diff against Chrome's renders).
// Firefox also exercises the library's `cssText` fast-path (Chrome uses the
// sandbox-diff path), so it's a genuine second-engine check.
browsers: [browser],
customLaunchers: {
chrome: {
base: 'Chrome',
// Pin the viewport and device-pixel-ratio so renders match the
// static reference images regardless of the host's display scaling.
flags: [
'--no-sandbox',
'--window-size=1024,768',
`--force-device-scale-factor=${deviceScaleFactor}`,
'--high-dpi-support=1',
].concat(headless ? ['--headless=new', '--disable-gpu'] : []),
debug: true,
},
firefox: {
base: headless ? 'FirefoxHeadless' : 'Firefox',
},
},
singleRun: false,
browserNoActivityTimeout: 60000,
});
};