From 66f315e331aeefbdd257a8b6addf2001b25da0ee Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Fri, 12 Jul 2024 18:05:47 +0300 Subject: [PATCH] feat: read snapshot configuration from jest config in package.json --- package.json | 1 + src/jest.config.js | 41 +++++++++++++++++++++++++++++++++++++++++ src/jest.mock.js | 5 ++++- src/jest.snapshot.js | 16 ++++++++++++++-- 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 src/jest.config.js diff --git a/package.json b/package.json index 033d830..5c61a3e 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "bin/jest.js", "src/dark.cjs", "src/jest.js", + "src/jest.config.js", "src/jest.fn.js", "src/jest.mock.js", "src/jest.snapshot.js", diff --git a/src/jest.config.js b/src/jest.config.js new file mode 100644 index 0000000..afd6db3 --- /dev/null +++ b/src/jest.config.js @@ -0,0 +1,41 @@ +import assert from 'node:assert/strict' +import { readFile } from 'node:fs/promises' +import path from 'node:path' + +const files = process.argv.slice(1) +const baseDir = files.length === 1 ? path.dirname(path.resolve(files[0])) : undefined + +async function getJestConfig(dir) { + if (!dir) return + + try { + const pkg = JSON.parse(await readFile(path.resolve(dir, 'package.json'), 'utf8')) + if (pkg.jest) return pkg.jest + } catch {} + + const parent = path.dirname(dir) + return parent === dir ? undefined : getJestConfig(parent) +} + +const normalizeJestConfig = (config) => ({ + testEnvironment: 'node', + snapshotSerializers: [], + ...config, + snapshotFormat: { + // jest-snapshot defaults + indent: 2, + escapeRegex: true, + printFunctionName: false, + // defaults from https://jestjs.io/docs/configuration#snapshotformat-object + escapeString: false, + printBasicPrototype: false, + // user config + ...config?.snapshotFormat, + // not overridable per doc + compareKeys: undefined, + }, +}) + +export const config = normalizeJestConfig(await getJestConfig(baseDir)) + +assert.equal(config.testEnvironment, 'node', 'Only "node" testEnvironment is supported') diff --git a/src/jest.mock.js b/src/jest.mock.js index 0e5f8db..2e18662 100644 --- a/src/jest.mock.js +++ b/src/jest.mock.js @@ -7,10 +7,13 @@ import { jestfn } from './jest.fn.js' const files = process.argv.slice(1) const baseUrl = files.length === 1 && existsSync(files[0]) ? normalize(files[0]) : undefined -const require = createRequire(baseUrl || import.meta.url) const mapMocks = new Map() const mapActual = new Map() +const require = createRequire(baseUrl || import.meta.url) + +export const relativeRequire = require + export function resolveModule(name) { assert(baseUrl || /^[@a-zA-Z]/u.test(name), 'Mocking relative paths is not possible') return require.resolve(name) diff --git a/src/jest.snapshot.js b/src/jest.snapshot.js index 9bc4f3c..ba478bf 100644 --- a/src/jest.snapshot.js +++ b/src/jest.snapshot.js @@ -4,16 +4,27 @@ import { expect } from 'expect' import { format } from 'pretty-format' import assert from 'node:assert/strict' import { basename, dirname, join } from 'node:path' +import { config } from './jest.config.js' +import { relativeRequire } from './jest.mock.js' +const { snapshotFormat, snapshotSerializers } = config const plugins = [] -const opts = { indent: 2, escapeRegex: true, printFunctionName: false, printBasicPrototype: false } -const serialize = (val) => format(val, { ...opts, plugins }).replaceAll(/\r\n|\r/gu, '\n') +const serialize = (val) => format(val, { ...snapshotFormat, plugins }).replaceAll(/\r\n|\r/gu, '\n') +let serializersAreSetup = false let snapshotsAreJest = false +function maybeSetupSerializers() { + if (serializersAreSetup) return + // empty require and serializers should not let this fail, non-empty serializers and empty require should + if (snapshotSerializers.length > 0) plugins.push(...snapshotSerializers.map(relativeRequire)) + serializersAreSetup = true +} + // We want to setup snapshots to behave like jest only when first used from jest API function maybeSetupJestSnapshots() { if (snapshotsAreJest) return + maybeSetupSerializers() const require = createRequire(import.meta.url) const { snapshot } = require('node:test') // attempt to load them, and we need to do that synchronously assert(snapshot, 'snapshots require Node.js >=22.3.0') @@ -68,6 +79,7 @@ const throws = (fn, check) => const snapInline = (obj, inline) => { assert(inline !== undefined, 'Inline Snapshots generation is not supported') assert(typeof inline === 'string') + maybeSetupSerializers() getAssert().strictEqual(serialize(obj).trim(), inline.trim()) }