@smpx/snap-shot-it
Version:
Smarter snapshot utility for Mocha and BDD test runners
207 lines (179 loc) • 5.58 kB
JavaScript
const debug = require('debug')('snap-shot-it')
const { core, restore, prune } = require('@smpx/snap-shot-core')
const compare = require('snap-shot-compare')
const { isDataDriven, dataDriven } = require('@bahmutov/data-driven')
const { isNamedSnapshotArguments } = require('./named-snapshots')
const { isChunkedSnapshotArguments } = require('./chunked-snapshots')
const R = require('ramda')
const { hasOnly, hasFailed } = require('has-only')
const pluralize = require('pluralize')
const path = require('path')
// eslint-disable-next-line immutable/no-let
let packageFile
try {
packageFile = require(path.join(process.cwd(), 'package.json'))
} catch (err) {
packageFile = {}
}
const config = packageFile['snap-shot-it'] || {}
debug('loading snap-shot-it')
const EXTENSION = config.extension || '.js'
// all tests we have seen so we can prune later
const seenSpecs = []
function _pruneSnapshots () {
debug('pruning snapshots')
debug('seen %s', pluralize('spec', seenSpecs.length, true))
debug(seenSpecs)
const opts = {
useRelativePath: Boolean(config.useRelativePath),
sortSnapshots: !process.env.SNAPSHOT_SKIP_SORTING
}
prune({ tests: seenSpecs, ext: EXTENSION }, opts)
// eslint-disable-next-line immutable/no-mutation
seenSpecs.length = 0
}
// eslint-disable-next-line immutable/no-let
let pruneSnapshots
function addToPrune (info) {
// do not add if previous info is the same
if (R.equals(R.last(seenSpecs), info)) {
return
}
seenSpecs.push(info)
}
// eslint-disable-next-line immutable/no-let
let currentTest
function setTest (t) {
if (!t) {
throw new Error('Expected test object')
}
currentTest = t
}
const getTestTitle = test => test.fullTitle().trim()
const getTestInfo = test => {
return {
file: test.file,
specName: getTestTitle(test)
}
}
function clearCurrentTest () {
if (currentTest) {
const fullTitle = getTestTitle(currentTest)
debug('clearing current test "%s"', fullTitle)
restore(getTestInfo(currentTest))
currentTest = null
}
}
global.beforeEach(function () {
/* eslint-disable immutable/no-this */
if (hasOnly(this)) {
debug('skip pruning snapshots because found .only')
pruneSnapshots = function noop () {}
} else {
debug('will prune snapshots because no .only')
pruneSnapshots = _pruneSnapshots
}
/* eslint-enable immutable/no-this */
})
global.beforeEach(function () {
/* eslint-disable immutable/no-this */
if (this.currentTest) {
setTest(this.currentTest)
}
/* eslint-enable immutable/no-this */
})
global.afterEach(clearCurrentTest)
function snapshot (value) {
if (!currentTest) {
throw new Error('Missing current test, cannot make snapshot')
}
const fullTitle = getTestTitle(currentTest)
debug('snapshot in test "%s"', fullTitle)
debug('from file "%s"', currentTest.file)
// eslint-disable-next-line immutable/no-let
let file = currentTest.file
// eslint-disable-next-line immutable/no-let
let savedTestTitle = fullTitle
if (isDataDriven(arguments)) {
// value is a function
debug('data-driven test for %s', value.name)
value = dataDriven(value, Array.from(arguments).slice(1))
savedTestTitle += ' ' + value.name
debug('extended save name to include function name')
debug('snapshot name "%s"', savedTestTitle)
addToPrune(getTestInfo(currentTest))
} else if (isNamedSnapshotArguments(arguments)) {
savedTestTitle = arguments[0]
value = arguments[1]
debug('named snapshots "%s"', savedTestTitle)
addToPrune({
file,
specName: savedTestTitle
})
} else if (isChunkedSnapshotArguments(arguments)) {
if (arguments[0].title) savedTestTitle = arguments[0].title
const chunk = arguments[0].chunk
if (chunk) {
const parsedPath = path.parse(currentTest.file)
file = `${path.join(
parsedPath.dir,
parsedPath.name + '.' + chunk + parsedPath.ext
)}`
savedTestTitle += ` [${chunk}]`
}
value = arguments[1]
addToPrune({
file,
specName: savedTestTitle
})
} else {
debug('snapshot value %j', value)
addToPrune(getTestInfo(currentTest))
}
// grab options from environment variables
const opts = {
show: Boolean(process.env.SNAPSHOT_SHOW),
dryRun: Boolean(process.env.SNAPSHOT_DRY),
update: Boolean(process.env.SNAPSHOT_UPDATE),
ci: Boolean(process.env.CI),
resultsDir: String(config.resultsDir),
useRelativePath: Boolean(config.useRelativePath),
sortSnapshots: !process.env.SNAPSHOT_SKIP_SORTING
}
const snap = {
what: value,
file,
ext: EXTENSION,
compare,
opts
}
if (isNamedSnapshotArguments(arguments)) {
// eslint-disable-next-line immutable/no-mutation
snap.exactSpecName = savedTestTitle
} else {
// eslint-disable-next-line immutable/no-mutation
snap.specName = savedTestTitle
}
return core(snap)
}
/* eslint-disable immutable/no-mutation */
module.exports = snapshot
/* eslint-enable immutable/no-mutation */
global.after(function () {
/* eslint-disable immutable/no-this */
if (!hasFailed(this)) {
debug('the test run was a success')
pruneSnapshots.call(this)
} else {
debug('not attempting to prune snapshots because the test run has failed')
}
/* eslint-enable immutable/no-this */
})
function deleteFromCache () {
// to work with transpiled code, need to force
// re-evaluating this module again on watch
debug('deleting snap-shot-it from cache')
delete require.cache[__filename]
}
global.after(deleteFromCache)