ENOENT error encountered while using fileNameFormatter feature
DESCRIPTION:
The following error is encountered when running into an image with valid difference from baseline:

STEPS TO REPLICATE:
Set the following attribute in loki.config.js:
fileNameFormatter: ({ configurationName, kind, story }) = ${configurationName}/${kind}_${story}.toLowerCase()
EXPECTED RESULT: An image is different error is encountered Difference is shown on the difference folder
ACTUAL RESULT: ENOENT error is encountered No image gets saved on the difference folder
NOTE: Haven't tested on a flat file structure since our storybook instance have folders set up in them
Haven't tested on a flat file structure
It works for flat structure, so there's definitely a bug related to missing directories when creating diff. +1 cause this currently makes using fileNameFormatter impossible for most usages.
After a short research I think I found it:
Loki uses looksSame.createDiff passing the diff option to looks-same in the form of the path to the file. looks-same in turn, after some processing saves the file using fs.createWriteStream(), passing that path as a parameter. Now, fs.createWriteStream() fails when the directory the path is pointing to doesn't exist - and that's the problem.
The reason why saving reference and current images works in Loki is because Loki saves them itself using fsExtra.outputFile() which creates any missing directories.
That's a tough one then - seems like the best option would be to ask looks-same to use the same saving method as Loki (it makes sense for them from the usability point of view). If it doesn't work out, I guess the only option is for Loki to create that dir manually before executing looksSame.createDiff(). Which option should we take @oblador ?
+1
opened an issue to 'looks-same', feel free to elaborate there, +1 or comment
@alonseg A link wouldn't hurt 😉
@jalooc sure, I just thought you can see here my mention :)
https://github.com/gemini-testing/looks-same/issues/68
Ran into this problem recently while using fileNameFormatter to place references near the component:
fileNameFormatter({configurationName, kind, story}) {
return `components/${kind}/__references__/${configurationName}/${story.toLowerCase()}`;
}
looks-same definitely has a flaw, but when switching diffing engine to gm the same error occurs: gm compare: Unable to open file (...) [No such file or directory].
Maybe Loki should create path to the difference image before calling differ and then just remove it if there is no any difference? BTW, gm differ already removes (https://github.com/oblador/loki/blob/master/packages/diff-graphics-magick/src/create-graphics-magick-differ.js#L21).
@kirilldronkin in the looks-same case I don't think we even need the removal part, we can create the path only if it is not the same. see here: looksSame.createDiff
I can create the PR but I'd like to hear @oblador opinion first to know if he'll approve it
I think a better approach would be to write the diff to a temp dir and then move it if there is a diff. Otherwise we'd end up with a bunch of empty directories.
For anyone stumbling on this, here's a workound using patch-package
- Add these two patches to
<root>/patches
@loki+diff-graphics-magick+0.25.0.patch
index 8a32d31..f5cc93f 100644
--- a/node_modules/@loki/diff-graphics-magick/src/create-graphics-magick-differ.js
+++ b/node_modules/@loki/diff-graphics-magick/src/create-graphics-magick-differ.js
@@ -4,6 +4,7 @@ const gm = require('gm');
function createGraphicsMagickDiffer(config) {
return function getImageDiff(path1, path2, diffPath, tolerance) {
const instanceConfig = { tolerance: tolerance / 100, ...config };
+ fs.ensureFileSync(diffPath);
return new Promise((resolve, reject) => {
gm.compare(
path1,
@loki+diff-looks-same+0.25.1.patch
index 033a2e6..3d29fde 100644
--- a/node_modules/@loki/diff-looks-same/src/create-looks-same-differ.js
+++ b/node_modules/@loki/diff-looks-same/src/create-looks-same-differ.js
@@ -4,6 +4,7 @@ const looksSame = require('looks-same');
function createLooksSameDiffer(config) {
return function getImageDiff(path1, path2, diffPath, tolerance) {
const instanceConfig = { tolerance, ...config };
+ fs.ensureFileSync(diffPath);
return new Promise(async (resolve, reject) => {
const [reference, current] = (await Promise.all([
fs.readFile(path1),
- Make sure you have
"postinstall": "patch-package"in the scripts of yourpackage.json
This will automatically patch the scripts for diffing with the two engines to add a fs.ensureFileSync, therefore creating the necessary directories/files before the actual diff. This might result in a bunch of empty directories, but that isn't much of a concern for me (especially since this is simply a workaround to make it work)