jest-html
Version:
Preview Jest snapshots right in your browser
555 lines (469 loc) • 18.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.saveAsBaseline = exports.getSnapshotSuite = exports.getFolder = exports.start = exports.configure = undefined;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _globby = require('globby');
var _globby2 = _interopRequireDefault(_globby);
var _chokidar = require('chokidar');
var _chokidar2 = _interopRequireDefault(_chokidar);
var _lodash = require('lodash.debounce');
var _lodash2 = _interopRequireDefault(_lodash);
var _timm = require('timm');
var _storyboard = require('storyboard');
var _serializer = require('../serializer');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
var FOLDER_PATH_ATTR = '__folderPath';
var DIRTY_ATTR = '__dirty';
var DELETED_ATTR = '__deleted';
var _config = void 0;
var _folderDict = {};
var _watchers = null;
var configure = function configure(newConfig) {
if (!_config) {
_config = (0, _timm.clone)(newConfig);
return;
}
_config = (0, _timm.merge)(_config, newConfig);
};
var start = function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref2$story = _ref2.story,
story = _ref2$story === undefined ? _storyboard.mainStory : _ref2$story;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return loadCommonCss(story);
case 2:
_context.next = 4;
return loadAllSnapshots(story);
case 4:
if (_config.watch) watchStart(story);
broadcastSignal();
case 6:
case 'end':
return _context.stop();
}
}
}, _callee, undefined);
}));
return function start() {
return _ref.apply(this, arguments);
};
}();
// ---------------------------------
// Snapshots
// ---------------------------------
var _snapshotSuiteDict = {};
var loadAllSnapshots = function () {
var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
var story = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _storyboard.mainStory;
var childStory, filePaths, commonCss, i;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
childStory = story.child({
src: 'extractor',
title: 'Refresh snapshots'
});
_context2.prev = 1;
_context2.next = 4;
return (0, _globby2.default)(_config.snapshotPatterns);
case 4:
filePaths = _context2.sent;
childStory.info('extractor', 'Reading snapshot files...');
commonCss = getCommonCss();
_snapshotSuiteDict = {};
i = 0;
case 9:
if (!(i < filePaths.length)) {
_context2.next = 15;
break;
}
_context2.next = 12;
return loadSuite(filePaths[i], commonCss, childStory);
case 12:
i++;
_context2.next = 9;
break;
case 15:
buildFolderDict(childStory);
childStory.debug('extractor', 'Snapshot tree:', { attach: _folderDict });
case 17:
_context2.prev = 17;
childStory.close();
return _context2.finish(17);
case 20:
case 'end':
return _context2.stop();
}
}
}, _callee2, undefined, [[1,, 17, 20]]);
}));
return function loadAllSnapshots() {
return _ref3.apply(this, arguments);
};
}();
var updateSnapshotCss = function updateSnapshotCss() {
var story = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _storyboard.mainStory;
var commonCss = getCommonCss();
Object.keys(_snapshotSuiteDict).forEach(function (filePath) {
var suite = _snapshotSuiteDict[filePath];
var suiteCss = getSuiteCss(filePath, story);
var css = suiteCss != null ? (0, _timm.addLast)(commonCss, suiteCss) : commonCss;
forEachSnapshot(suite, function (snapshot) {
snapshot.css = css; // eslint-disable-line no-param-reassign
});
});
};
var loadSuite = function () {
var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(filePath, commonCss, story) {
var absPath, rawSnapshots, suiteCss, finalFilePath, prevSuite, suite, suiteDirty, nextIds, i, id, rawSnapshot, _rawSnapshot$split, _rawSnapshot$split2, snap, html, css, _snapshot, prevSnapshot, prevBaseline;
return regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
story.info('extractor', 'Processing ' + _storyboard.chalk.cyan.bold(filePath) + '...');
absPath = _path2.default.resolve(process.cwd(), filePath);
rawSnapshots = loadSnapshot(absPath);
suiteCss = getSuiteCss(filePath, story);
finalFilePath = '-/' + filePath.normalize();
prevSuite = _snapshotSuiteDict[finalFilePath];
// $FlowFixMe
suite = {};
suiteDirty = false;
nextIds = Object.keys(rawSnapshots);
for (i = 0; i < nextIds.length; i++) {
id = nextIds[i];
rawSnapshot = rawSnapshots[id];
_rawSnapshot$split = rawSnapshot.split(_serializer.HTML_PREVIEW_SEPARATOR), _rawSnapshot$split2 = _slicedToArray(_rawSnapshot$split, 2), snap = _rawSnapshot$split2[0], html = _rawSnapshot$split2[1];
css = suiteCss != null ? (0, _timm.addLast)(commonCss, suiteCss) : commonCss;
_snapshot = {
id: id,
snap: snap,
html: html,
css: css,
dirty: false,
deleted: false
};
if (prevSuite && prevSuite[id]) {
prevSnapshot = prevSuite[id];
prevBaseline = prevSnapshot.baseline;
// Copy the previous baseline, if any
if (prevBaseline != null) {
_snapshot.baseline = prevBaseline;
_snapshot.dirty = html !== prevBaseline.html || snap !== prevBaseline.snap;
// Create a new baseline if the snapshot's SNAP or HTML have changed
} else if (html !== prevSnapshot.html || snap !== prevSnapshot.snap) {
_snapshot.baseline = {
html: prevSnapshot.html,
snap: prevSnapshot.snap
};
_snapshot.dirty = true;
}
}
suite[id] = _snapshot;
suiteDirty = suiteDirty || _snapshot.dirty;
}
if (prevSuite != null) {
forEachSnapshot(prevSuite, function (snapshot) {
var id = snapshot.id;
if (nextIds.indexOf(id) < 0) {
snapshot.deleted = true; // eslint-disable-line no-param-reassign
suite[id] = snapshot;
suiteDirty = true;
}
});
}
story.debug('extractor', 'Found ' + Object.keys(rawSnapshots).length + ' snapshots');
suite[FOLDER_PATH_ATTR] = _path2.default.dirname(finalFilePath);
suite[DIRTY_ATTR] = suiteDirty;
suite[DELETED_ATTR] = false;
_snapshotSuiteDict[finalFilePath] = sortSnapshots(suite);
case 16:
case 'end':
return _context3.stop();
}
}
}, _callee3, undefined);
}));
return function loadSuite(_x4, _x5, _x6) {
return _ref4.apply(this, arguments);
};
}();
var saveAsBaseline = function saveAsBaseline(filePath, id) {
var suite = _snapshotSuiteDict[filePath.normalize()];
if (suite == null) return;
var snapshot = suite[id];
if (snapshot == null || !snapshot.baseline) return;
delete snapshot.baseline;
snapshot.dirty = false;
suite.__dirty = isSuiteDirty(suite);
buildFolderDict(_storyboard.mainStory);
broadcastSignal();
};
var loadSnapshot = function loadSnapshot(absPath) {
/* eslint-disable global-require, import/no-dynamic-require */
delete require.cache[require.resolve(absPath)];
return require(absPath);
/* eslint-enable global-require */
};
var sortSnapshots = function sortSnapshots(suite) {
var out = {};
Object.keys(suite).sort().forEach(function (id) {
out[id] = suite[id];
});
return out;
};
// ---------------------------------
// CSS
// ---------------------------------
var _commonCss = [];
var getCommonCss = function getCommonCss() {
return _commonCss;
};
var loadCommonCss = function () {
var _ref5 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {
var story = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _storyboard.mainStory;
var cssPaths;
return regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
story.info('extractor', 'Extracting common CSS...');
_context4.next = 3;
return (0, _globby2.default)(_config.cssPatterns);
case 3:
cssPaths = _context4.sent;
_commonCss = cssPaths.map(function (cssPath) {
story.info('extractor', 'Processing ' + _storyboard.chalk.cyan.bold(cssPath) + '...');
return _fs2.default.readFileSync(cssPath, 'utf8');
});
case 5:
case 'end':
return _context4.stop();
}
}
}, _callee4, undefined);
}));
return function loadCommonCss() {
return _ref5.apply(this, arguments);
};
}();
var getSuiteCss = function getSuiteCss(filePath, story) {
var suiteCss = void 0;
try {
var cssPath = getCssPathForSuite(filePath);
story.debug('extractor', 'Trying to read ' + _storyboard.chalk.cyan.bold(cssPath) + '...');
suiteCss = _fs2.default.readFileSync(cssPath, 'utf8');
story.info('extractor', 'Found custom CSS in ' + _storyboard.chalk.cyan.bold(cssPath));
} catch (err) {
/* no file exists */
}
return suiteCss;
};
var getCssPathForSuite = function getCssPathForSuite(filePath) {
var _path$parse = _path2.default.parse(filePath),
dir = _path$parse.dir,
name = _path$parse.name;
return _path2.default.join(dir, name + '.css');
};
// ---------------------------------
// Folder dictionary
// ---------------------------------
var buildFolderDict = function buildFolderDict(story) {
story.info('extractor', 'Building folder tree...');
var nextDict = {};
// Create root node
var curFolderPath = '-';
var curFolder = {
parentFolderPath: null,
folderPath: '-',
dirty: false,
filePaths: [],
suiteDirtyFlags: [],
childrenFolderPaths: [],
childrenFolderDirtyFlags: []
};
nextDict['-'] = curFolder;
story.debug('extractor', 'Snapshot tree:', { attach: nextDict });
// Sort file paths to simplify tree generation
var filePaths = Object.keys(_snapshotSuiteDict).sort();
// Process all file paths
filePaths.forEach(function (filePath) {
story.debug('extractor', 'File: ' + filePath);
var folderPath = _path2.default.dirname(filePath);
if (folderPath === curFolderPath) {
curFolder.filePaths.push(filePath);
} else {
var parentFolderPath = folderPath;
var fFound = false;
while (!fFound) {
if (parentFolderPath === '-') break;
parentFolderPath = _path2.default.dirname(parentFolderPath);
if (nextDict[parentFolderPath]) {
fFound = true;
break;
}
}
if (!fFound) throw new Error('Error building path tree');
nextDict[parentFolderPath].childrenFolderPaths.push(folderPath);
nextDict[folderPath] = {
parentFolderPath: parentFolderPath,
folderPath: folderPath,
dirty: false,
filePaths: [filePath],
suiteDirtyFlags: [],
childrenFolderPaths: [],
childrenFolderDirtyFlags: []
};
curFolder = nextDict[folderPath];
curFolderPath = folderPath;
}
});
// Update children dirty flags for each folder
updateChildrenDirtyFlags(nextDict, _snapshotSuiteDict, '-');
_folderDict = nextDict;
};
// Update dirty flags, recursively, top-down
var updateChildrenDirtyFlags = function updateChildrenDirtyFlags(folderDict, suiteDict, folderPath) {
var curFolder = folderDict[folderPath];
curFolder.suiteDirtyFlags = curFolder.filePaths.map(function (filePath) {
return _snapshotSuiteDict[filePath][DIRTY_ATTR];
});
curFolder.childrenFolderDirtyFlags = curFolder.childrenFolderPaths.map(function (subFolderPath) {
return updateChildrenDirtyFlags(folderDict, suiteDict, subFolderPath);
});
curFolder.dirty = curFolder.suiteDirtyFlags.some(Boolean) || curFolder.childrenFolderDirtyFlags.some(Boolean);
return curFolder.dirty;
};
// ---------------------------------
// Watching and real-time
// ---------------------------------
var watchStart = function watchStart(story) {
if (_watchers) return;
_watchers = {};
// const glob = _config.snapshotPatterns.concat(_config.cssPatterns);
var options = {
ignored: /[/\\]\./,
ignoreInitial: true
};
_watchers.css = _chokidar2.default.watch(_config.cssPatterns, options);
_watchers.snap = _chokidar2.default.watch(_config.snapshotPatterns, options);
['change', 'add', 'unlink'].forEach(function (type) {
// $FlowFixMe
_watchers.css.on(type, function (filePath) {
cssWatchEvent(type, filePath);
});
// $FlowFixMe
_watchers.snap.on(type, function (filePath) {
snapWatchEvent(type, filePath);
});
});
story.info('extractor', 'Started watching over snapshot and CSS files');
};
// const watchStop = (story: StoryT) => {
// if (_watchers == null) return;
// _watchers.close();
// _watchers = null;
// story.info('extractor', 'Stopped file watcher');
// };
var cssWatchEvent = function cssWatchEvent(type, filePath0) {
var filePath = slash(filePath0);
_storyboard.mainStory.debug('extractor', 'CSS watch fired: ' + (_storyboard.chalk.bold(type.toUpperCase()) + ' ' + _storyboard.chalk.cyan.bold(filePath)));
debouncedCssRefresh();
};
var debouncedCssRefresh = (0, _lodash2.default)(function () {
return Promise.resolve().then(function () {
return loadCommonCss();
}).then(function () {
return updateSnapshotCss();
}).then(function () {
return broadcastSignal();
});
}, 300);
var snapWatchEvent = function snapWatchEvent(type, filePath0) {
var filePath = slash(filePath0);
_storyboard.mainStory.debug('extractor', 'Snapshot watch fired: ' + (_storyboard.chalk.bold(type.toUpperCase()) + ' ' + _storyboard.chalk.cyan.bold(filePath)));
Promise.resolve().then(function () {
var commonCss = getCommonCss();
switch (type) {
case 'change':
case 'add':
return Promise.resolve().then(function () {
return loadSuite(filePath, commonCss, _storyboard.mainStory);
}).then(function () {
return buildFolderDict(_storyboard.mainStory);
});
case 'unlink':
return Promise.resolve().then(function () {
var suite = _snapshotSuiteDict['-/' + filePath.normalize()];
if (suite) {
suite[DIRTY_ATTR] = true;
suite[DELETED_ATTR] = true;
return buildFolderDict(_storyboard.mainStory);
}
return null;
});
default:
break;
}
return null;
}).then(function () {
return broadcastSignal();
});
};
var broadcastSignal = function broadcastSignal() {
if (!_config.socketioServer) return;
_config.socketioServer.emit('REFRESH');
};
// ---------------------------------
// Others
// ---------------------------------
var getFolder = function getFolder(folderPath) {
return _folderDict[folderPath.normalize()];
};
var getSnapshotSuite = function getSnapshotSuite(filePath) {
return _snapshotSuiteDict[filePath.normalize()];
};
var forEachSnapshot = function forEachSnapshot(suite, cb) {
Object.keys(suite).forEach(function (id) {
if (id === FOLDER_PATH_ATTR || id === DIRTY_ATTR || id === DELETED_ATTR) return;
var snapshot = suite[id];
if ((typeof snapshot === 'undefined' ? 'undefined' : _typeof(snapshot)) !== 'object') return;
cb(snapshot);
});
};
var isSuiteDirty = function isSuiteDirty(suite) {
var keys = Object.keys(suite);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key === FOLDER_PATH_ATTR || key === DIRTY_ATTR || key === DELETED_ATTR) {
continue;
}
if (suite[key].dirty) return true;
}
return false;
};
var slash = function slash(str) {
return str.replace(/\\/g, '/');
};
// =============================================
// Public API
// =============================================
exports.configure = configure;
exports.start = start;
exports.getFolder = getFolder;
exports.getSnapshotSuite = getSnapshotSuite;
exports.saveAsBaseline = saveAsBaseline;
;