Jest snapshot testing utilities that enable capturing component output, API responses, or any serializable values as snapshots for regression testing and change detection
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Utility functions for maintaining snapshot files and removing orphaned snapshots that no longer have corresponding test files. Essential for keeping snapshot directories clean and avoiding stale snapshot accumulation.
Removes orphaned snapshot files that no longer have corresponding test files, with support for ignore patterns and different update modes.
/**
* Removes orphaned snapshot files that no longer have corresponding test files
* @param fileSystem - File system interface for file operations
* @param update - Snapshot update mode controlling cleanup behavior
* @param snapshotResolver - Resolver for converting between test and snapshot paths
* @param testPathIgnorePatterns - Optional patterns for ignoring test paths
* @returns Summary of cleanup results
*/
function cleanup(
fileSystem: FileSystem,
update: Config.SnapshotUpdateState,
snapshotResolver: SnapshotResolver,
testPathIgnorePatterns?: string[]
): {
filesRemoved: number;
filesRemovedList: Array<string>;
};
type Config.SnapshotUpdateState = 'all' | 'new' | 'none';Usage Examples:
import { cleanup, buildSnapshotResolver } from "jest-snapshot";
// File system implementation (typically jest-haste-map's HasteFS)
const fileSystem = {
exists: (path) => fs.existsSync(path),
matchFiles: (pattern) => glob.sync(pattern, { cwd: projectRoot })
};
// Build snapshot resolver
const snapshotResolver = await buildSnapshotResolver({
rootDir: '/project/root',
snapshotResolver: undefined
});
// Clean up orphaned snapshots
const result = cleanup(
fileSystem,
'all', // Update mode: 'all' enables cleanup
snapshotResolver,
['**/node_modules/**'] // Ignore patterns
);
console.log(`Removed ${result.filesRemoved} orphaned snapshot files`);
console.log('Files removed:', result.filesRemovedList);
// Example output:
// Removed 3 orphaned snapshot files
// Files removed: [
// '/project/src/__snapshots__/DeletedComponent.test.js.snap',
// '/project/src/__snapshots__/RenamedFile.spec.js.snap',
// '/project/tests/__snapshots__/old-test.js.snap'
// ]The cleanup function follows a systematic process:
.snap files)// Internal cleanup logic (simplified)
const pattern = `\\.${EXTENSION}$`; // '\.snap$'
const snapshotFiles = fileSystem.matchFiles(pattern);
const orphanedFiles = snapshotFiles.filter(snapshotFile => {
// Get corresponding test path
const testPath = snapshotResolver.resolveTestPath(snapshotFile);
// Check ignore patterns
if (testIgnorePatternsRegex && testIgnorePatternsRegex.test(testPath)) {
return false; // Skip ignored test paths
}
// Check if test file exists
return !fileSystem.exists(testPath);
});
// Remove files if in 'all' update mode
if (update === 'all') {
orphanedFiles.forEach(file => fs.unlinkSync(file));
}The cleanup behavior depends on the snapshot update mode:
'all' Mode: Actively removes orphaned snapshot files
const result = cleanup(fileSystem, 'all', resolver);
// Physically deletes orphaned .snap files
// result.filesRemoved > 0 if files were removed'new' or 'none' Mode: Only reports orphaned files without removing them
const result = cleanup(fileSystem, 'none', resolver);
// Reports orphaned files but doesn't delete them
// result.filesRemoved = 0, but filesRemovedList shows what would be removedUse ignore patterns to exclude certain test paths from cleanup consideration:
// Common ignore patterns
const ignorePatterns = [
'**/node_modules/**', // Ignore dependencies
'**/dist/**', // Ignore build output
'**/*.d.ts', // Ignore TypeScript declarations
'**/fixtures/**', // Ignore test fixtures
'**/coverage/**' // Ignore coverage reports
];
const result = cleanup(
fileSystem,
'all',
resolver,
ignorePatterns
);
// Snapshots for tests matching these patterns won't be removed
// even if the test files appear to be missingThe cleanup function expects a file system interface that provides file existence checking and pattern matching:
interface FileSystem {
/**
* Checks if a file exists at the given path
* @param path - File path to check
* @returns True if file exists, false otherwise
*/
exists(path: string): boolean;
/**
* Finds files matching a pattern
* @param pattern - RegExp or string pattern to match files
* @returns Array of matching file paths
*/
matchFiles(pattern: RegExp | string): Array<string>;
}Common FileSystem Implementations:
// Using Node.js fs and glob
const nodeFileSystem = {
exists: (path) => require('fs').existsSync(path),
matchFiles: (pattern) => require('glob').sync(pattern)
};
// Using jest-haste-map (typical in Jest environments)
const hasteFileSystem = {
exists: (path) => hasteFs.exists(path),
matchFiles: (pattern) => hasteFs.matchFiles(pattern)
};
// Custom implementation with caching
const cachedFileSystem = {
exists: (path) => {
if (!existsCache.has(path)) {
existsCache.set(path, fs.existsSync(path));
}
return existsCache.get(path);
},
matchFiles: (pattern) => glob.sync(pattern, { cache: globCache })
};Cleanup is typically integrated into test runner workflows:
// Jest integration example
module.exports = {
// Jest configuration
setupFilesAfterEnv: ['<rootDir>/cleanup-snapshots.js']
};
// cleanup-snapshots.js
afterAll(async () => {
if (process.env.UPDATE_SNAPSHOTS === 'true') {
const result = cleanup(fileSystem, 'all', resolver);
console.log(`Cleaned up ${result.filesRemoved} orphaned snapshots`);
}
});
// CI/CD integration
if (process.env.CI && process.env.UPDATE_SNAPSHOTS) {
const result = cleanup(fileSystem, 'all', resolver);
if (result.filesRemoved > 0) {
console.log('Orphaned snapshots removed:', result.filesRemovedList);
process.exit(1); // Fail CI to highlight the cleanup
}
}Cleanup operations can encounter various error conditions:
// File system errors
try {
const result = cleanup(fileSystem, 'all', resolver);
} catch (error) {
if (error.code === 'ENOENT') {
console.log('Snapshot directory not found');
} else if (error.code === 'EPERM') {
console.log('Permission denied removing snapshot file');
}
}
// Invalid resolver
const result = cleanup(fileSystem, 'all', null);
// Error: Invalid snapshot resolver
// Pattern matching errors
const faultyFileSystem = {
exists: () => true,
matchFiles: () => { throw new Error('Pattern error'); }
};
// Error: Failed to match snapshot filesDetailed reporting helps understand what was cleaned up:
const result = cleanup(fileSystem, 'all', resolver, ignorePatterns);
console.log(`Cleanup Summary:
Files Removed: ${result.filesRemoved}
Ignore Patterns: ${ignorePatterns?.length || 0}
Removed Files:`);
result.filesRemovedList.forEach(file => {
const testPath = resolver.resolveTestPath(file);
console.log(` ${file} (test: ${testPath})`);
});interface CleanupResult {
filesRemoved: number; // Count of files actually removed
filesRemovedList: Array<string>; // Paths of removed files
}
type SnapshotUpdateState = 'all' | 'new' | 'none';
interface FileSystem {
exists(path: string): boolean;
matchFiles(pattern: RegExp | string): Array<string>;
}
interface SnapshotResolver {
resolveTestPath(snapshotPath: string, snapshotExtension?: string): string;
resolveSnapshotPath(testPath: string, snapshotExtension?: string): string;
testPathForConsistencyCheck: string;
}