diff --git a/.gitignore b/.gitignore index ae12dcab..8a845110 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules/ .DS_Store npm-debug.log -temp-test-* +temp-* diff --git a/README.md b/README.md index e6efc8b9..a7e7ed67 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,27 @@ Run with environment variable `DEBUG=snap-shot-it ...` to see log messages. Because under the hood it uses [snap-shot-core][snap-shot-core] you might want to show messages from both libraries with `DEBUG=snap-shot* ...` +## Data callbacks +You can pass your own NPM modules as `pre-compare`, `compare` and `store` functions using `package.json`. For example, to use both local and 3rd party NPM modules + +```json +{ + "config": { + "snap-shot-it": { + "pre-compare": "./pre-compare", + "compare": "snap-shot-compare", + "store": "./store" + } + } +} +``` + +Each NPM module in this case should export a definition of a function that matches the expected core function + +- `pre-compare` is simply an identity or transformation function +- `compare` should match [snap-shot-core#compare-function](/~https://github.com/bahmutov/snap-shot-core#compare-function), for example see [snap-shot-compare](/~https://github.com/bahmutov/snap-shot-compare) +- `store` is another identity or transformation function, see [snap-shot-core#store-function](/~https://github.com/bahmutov/snap-shot-core#store-function) ## Nested snapshots diff --git a/package-lock.json b/package-lock.json index f4e2d5ac..a138c712 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3579,9 +3579,10 @@ "dev": true }, "folktale": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/folktale/-/folktale-2.0.1.tgz", - "integrity": "sha512-3kDSWVkSlErHIt/dC73vu+5zRqbW1mlnL46s2QfYN7Ps0JcS9MVtuLCrDQOBa7sanA+d9Fd8F+bn0VcyNe68Jw==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/folktale/-/folktale-2.3.2.tgz", + "integrity": "sha512-+8GbtQBwEqutP0v3uajDDoN64K2ehmHd0cjlghhxh0WpcfPzAIjPA03e1VvHlxL02FVGR0A6lwXsNQKn3H1RNQ==", + "dev": true }, "for-in": { "version": "1.0.2", @@ -11452,6 +11453,13 @@ "lazy-ass": "1.6.0", "strip-ansi": "4.0.0", "variable-diff": "1.1.0" + }, + "dependencies": { + "folktale": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/folktale/-/folktale-2.0.1.tgz", + "integrity": "sha512-3kDSWVkSlErHIt/dC73vu+5zRqbW1mlnL46s2QfYN7Ps0JcS9MVtuLCrDQOBa7sanA+d9Fd8F+bn0VcyNe68Jw==" + } } }, "snap-shot-core": { diff --git a/package.json b/package.json index aff14ffc..06e05115 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "dont-crack": "1.2.1", "eslint": "5.16.0", "eslint-plugin-immutable": "1.0.0", + "folktale": "2.3.2", "git-issues": "1.3.1", "lazy-ass": "1.6.0", "license-checker": "25.0.1", diff --git a/src/subfolders-spec.js b/src/e2e-spec.js similarity index 68% rename from src/subfolders-spec.js rename to src/e2e-spec.js index f7a1b701..95ba83ab 100644 --- a/src/subfolders-spec.js +++ b/src/e2e-spec.js @@ -86,6 +86,7 @@ const copyFolder = (sourceFolder, tempFolder) => { shell.rm('-rf', tempFolder) shell.mkdir(tempFolder) shell.cp(join(sourceFolder, 'package.json'), tempFolder) + shell.cp(join(sourceFolder, '*.js'), tempFolder) shell.cp('-R', join(sourceFolder, 'specs'), tempFolder) } @@ -172,3 +173,80 @@ describe('snapshots in same folder', () => { }) }) }) + +describe('custom compare function', () => { + // folder with specs to run + const sourceFolder = join(__dirname, '..', 'test-custom-compare-fn') + // temp folder to copy to before running tests + const tempFolder = join(__dirname, '..', 'temp-custom-compare-fn') + + beforeEach(() => { + copyFolder(sourceFolder, tempFolder) + }) + + it('transforms value ', function () { + this.timeout(5000) + + execa.shellSync('npm test', { + cwd: tempFolder, + stdio: 'inherit', + // only use the limited environment keys + // without "CI=1" value + env: limitedEnv, + extendEnv: false + }) + + checkSnapshots(tempFolder, { + 'spec.js': { + 'random string as 10 As 1': '\naaaaaaaaaa\n' + } + }) + + // run the tests again to check if values are not clashing + // but with CI=1 to avoid writing new files accidentally + execa.shellSync('npm test', { + cwd: tempFolder, + stdio: 'inherit', + env: { CI: '1' } + }) + }) +}) + +describe('custom pre-compare function', () => { + // folder with specs to run + const sourceFolder = join(__dirname, '..', 'test-custom-pre-fn') + // temp folder to copy to before running tests + const tempFolder = join(__dirname, '..', 'temp-custom-pre-fn') + + beforeEach(() => { + copyFolder(sourceFolder, tempFolder) + }) + + it('transforms value before comparison', function () { + this.timeout(5000) + + execa.shellSync('npm test', { + cwd: tempFolder, + stdio: 'inherit', + // only use the limited environment keys + // without "CI=1" value + env: limitedEnv, + extendEnv: false + }) + + checkSnapshots(tempFolder, { + 'spec.js': { + 'stores string as number 1': 5, + 'stores string as number 2': 3 + } + }) + + // run the tests again to check if values are not clashing + // but with CI=1 to avoid writing new files accidentally + execa.shellSync('npm test', { + cwd: tempFolder, + stdio: 'inherit', + env: { CI: '1' } + }) + }) +}) diff --git a/src/index.js b/src/index.js index e4829455..3d385940 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,6 @@ const debug = require('debug')('snap-shot-it') const { core, restore, prune } = require('snap-shot-core') -const compare = require('snap-shot-compare') const { isDataDriven, dataDriven } = require('@bahmutov/data-driven') const { isNamedSnapshotArguments } = require('./named-snapshots') const utils = require('./utils') @@ -140,11 +139,22 @@ function snapshot (value) { debug('package config options %o', packageConfigOptions) debug('merged options %o', opts) + const compare = opts.compare + ? utils.load(cwd, opts.compare) + : require('snap-shot-compare') + const store = opts.store ? utils.load(cwd, opts.store) : null + + const preCompare = opts['pre-compare'] + ? utils.load(cwd, opts['pre-compare']) + : R.identity + const what = preCompare(value) + const snap = { - what: value, + what, file: currentTest.file, ext: EXTENSION, compare, + store, opts } diff --git a/src/utils.js b/src/utils.js index 20cd7c8d..f2123ab8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,9 +1,13 @@ const fs = require('fs') const path = require('path') const R = require('ramda') +const debug = require('debug')('snap-shot-it') const defaults = { - useRelativePath: false + useRelativePath: false, + 'pre-compare': null, + compare: null, + store: null } /** * Pick only the keys we know about from whatever the user @@ -30,4 +34,16 @@ const getPackageConfigOptions = cwd => { return pickKnownKeys(options) } -module.exports = { getPackageConfigOptions, defaults } +const load = (cwd, modulePath) => { + debug('loading module %s from cwd %s', modulePath, cwd) + if (!path.isAbsolute(modulePath)) { + const resolved = path.resolve(cwd, modulePath) + debug('resolved path: %s', resolved) + return require(resolved) + } else { + debug('trying to load NPM module %s', modulePath) + return require(modulePath) + } +} + +module.exports = { getPackageConfigOptions, defaults, load } diff --git a/test-custom-compare-fn/README.md b/test-custom-compare-fn/README.md new file mode 100644 index 00000000..d5a65c3e --- /dev/null +++ b/test-custom-compare-fn/README.md @@ -0,0 +1,5 @@ +# test-custom-compare-fn + +This repo shows how `snap-shot-it` can load a custom compare function via config in `package.json` file. + +See /~https://github.com/bahmutov/snap-shot-it/issues/350 diff --git a/test-custom-compare-fn/compaaaaaare.js b/test-custom-compare-fn/compaaaaaare.js new file mode 100644 index 00000000..673cdc06 --- /dev/null +++ b/test-custom-compare-fn/compaaaaaare.js @@ -0,0 +1,24 @@ +const Result = require('folktale/result') +const debug = require('debug')('snap-shot-it') + +const compare = ({ expected, value }) => { + debug('in compaaaaare.js') + + // convert "expected" to string of same length, but with "a" characters + const aaas = value.replace(/./g, 'a') + debug('original value: %s', value) + debug('aaas: %s', aaas) + debug('expected value: %s', expected) + + if (aaas === expected) { + return Result.Ok() + } else { + return Result.Error({ + message: 'Hmm, not the same "aaaa..."', + expected, + value: aaas + }) + } +} + +module.exports = compare diff --git a/test-custom-compare-fn/package.json b/test-custom-compare-fn/package.json new file mode 100644 index 00000000..9753bf5f --- /dev/null +++ b/test-custom-compare-fn/package.json @@ -0,0 +1,18 @@ +{ + "name": "test-custom-compare-fn", + "version": "1.0.0", + "description": "shows custom compare function", + "main": "index.js", + "scripts": { + "test": "../node_modules/.bin/mocha 'specs/**/*.js'" + }, + "keywords": [], + "author": "", + "license": "ISC", + "config": { + "snap-shot-it": { + "compare": "./compaaaaaare", + "store": "./store" + } + } +} diff --git a/test-custom-compare-fn/specs/spec.js b/test-custom-compare-fn/specs/spec.js new file mode 100644 index 00000000..e2a0055c --- /dev/null +++ b/test-custom-compare-fn/specs/spec.js @@ -0,0 +1,11 @@ +const snapshot = require('../..') + +/* eslint-env mocha */ +it('random string as 10 As', () => { + // our custom compare function replaces strings with + // same length of "aaaa..." :) + const s = Math.random() + .toString() + .substr(0, 10) + snapshot(s) +}) diff --git a/test-custom-compare-fn/store.js b/test-custom-compare-fn/store.js new file mode 100644 index 00000000..f2d11e53 --- /dev/null +++ b/test-custom-compare-fn/store.js @@ -0,0 +1,11 @@ +const debug = require('debug')('snap-shot-it') + +const store = value => { + // converts value before storing it on disk + const transformed = value.replace(/./g, 'a') + debug('original value: %s', value) + debug('transformed: %s', transformed) + return transformed +} + +module.exports = store diff --git a/test-custom-pre-fn/README.md b/test-custom-pre-fn/README.md new file mode 100644 index 00000000..579495e5 --- /dev/null +++ b/test-custom-pre-fn/README.md @@ -0,0 +1,5 @@ +# test-custom-pre-fn + +This repo shows how `snap-shot-it` can load a custom pre-compare function via config in `package.json` file and use it to clean up data before comparing. + +See /~https://github.com/bahmutov/snap-shot-it/issues/350 diff --git a/test-custom-pre-fn/package.json b/test-custom-pre-fn/package.json new file mode 100644 index 00000000..c126c394 --- /dev/null +++ b/test-custom-pre-fn/package.json @@ -0,0 +1,17 @@ +{ + "name": "test-custom-pre-fn", + "version": "1.0.0", + "description": "shows custom pre-compare function", + "main": "index.js", + "scripts": { + "test": "../node_modules/.bin/mocha 'specs/**/*.js'" + }, + "keywords": [], + "author": "", + "license": "ISC", + "config": { + "snap-shot-it": { + "pre-compare": "./pre-compare" + } + } +} diff --git a/test-custom-pre-fn/pre-compare.js b/test-custom-pre-fn/pre-compare.js new file mode 100644 index 00000000..f4d88a3f --- /dev/null +++ b/test-custom-pre-fn/pre-compare.js @@ -0,0 +1,6 @@ +const preCompare = value => { + // assuming the value is a string + return value.length +} + +module.exports = preCompare diff --git a/test-custom-pre-fn/specs/spec.js b/test-custom-pre-fn/specs/spec.js new file mode 100644 index 00000000..8dbc57a4 --- /dev/null +++ b/test-custom-pre-fn/specs/spec.js @@ -0,0 +1,8 @@ +const snapshot = require('../..') + +/* eslint-env mocha */ +it('stores string as number', () => { + // should be saved as the length of the string + snapshot('aaaaa') + snapshot('aaa') +})