import {Buffer} from 'node:buffer';
import fs from 'node:fs';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import {gunzipSync} from 'node:zlib';
import {execa} from 'execa';
import {test} from 'tap';
import {temporaryDirectory} from 'tempy';
import {extractCompressedSnapshot} from '../../lib/snapshot-manager.js';
import {execCli} from '../helper/cli.js';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
for (const object of [
{type: 'colocated', rel: '', dir: ''},
{type: '__tests__', rel: '__tests__-dir', dir: '__tests__/__snapshots__'},
{type: 'test', rel: 'test-dir', dir: 'test/snapshots'},
{type: 'tests', rel: 'tests-dir', dir: 'tests/snapshots'},
]) {
test(`snapshots work (${object.type})`, t => {
const snapPath = path.join(__dirname, '..', 'fixture', 'snapshots', object.rel, object.dir, 'test.cjs.snap');
try {
fs.unlinkSync(snapPath);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
const dirname = path.join('fixture/snapshots', object.rel);
execCli(['--update-snapshots'], {dirname, env: {AVA_FORCE_CI: 'not-ci'}}, error => {
t.error(error);
t.ok(fs.existsSync(snapPath));
execCli([], {dirname}, error => {
t.error(error);
t.end();
});
});
});
}
test('appends to existing snapshots', t => {
const cliPath = fileURLToPath(new URL('../../entrypoints/cli.mjs', import.meta.url));
const avaPath = fileURLToPath(new URL('../../entrypoints/main.cjs', import.meta.url));
const cwd = temporaryDirectory();
fs.writeFileSync(path.join(cwd, 'package.json'), '{}');
const initial = `const test = require(${JSON.stringify(avaPath)})
test('one', t => {
t.snapshot({one: true})
})`;
fs.writeFileSync(path.join(cwd, 'test.cjs'), initial);
const run = () => execa(process.execPath, [cliPath, '--no-color'], {cwd, env: {AVA_FORCE_CI: 'not-ci'}, reject: false});
return run().then(result => {
t.match(result.stdout, /1 test passed/);
fs.writeFileSync(path.join(cwd, 'test.cjs'), `${initial}
test('two', t => {
t.snapshot({two: true})
})`);
return run();
}).then(result => {
t.match(result.stdout, /2 tests passed/);
fs.writeFileSync(path.join(cwd, 'test.cjs'), `${initial}
test('two', t => {
t.snapshot({two: false})
})`);
return run();
}).then(result => {
t.match(result.stdout, /1 test failed/);
});
});
test('outdated snapshot version is reported to the console', t => {
const snapPath = path.join(__dirname, '..', 'fixture', 'snapshots', 'test.cjs.snap');
fs.writeFileSync(snapPath, Buffer.from([0x0A, 0x00, 0x00]));
execCli(['test.cjs'], {dirname: 'fixture/snapshots'}, (error, stdout) => {
t.ok(error);
t.match(stdout, /The snapshot file is v0, but only v3 is supported\./);
t.match(stdout, /File path:/);
t.match(stdout, snapPath);
t.match(stdout, /Please run AVA again with the .*--update-snapshots.* flag to upgrade\./);
t.end();
});
});
test('outdated snapshot version can be updated', t => {
const snapPath = path.join(__dirname, '..', 'fixture', 'snapshots', 'test.cjs.snap');
fs.writeFileSync(snapPath, Buffer.from([0x0A, 0x00, 0x00]));
execCli(['test.cjs', '--update-snapshots'], {dirname: 'fixture/snapshots', env: {AVA_FORCE_CI: 'not-ci'}}, (error, stdout) => {
t.error(error);
t.match(stdout, /2 tests passed/);
t.end();
});
});
test('newer snapshot version is reported to the console', t => {
const snapPath = path.join(__dirname, '..', 'fixture', 'snapshots', 'test.cjs.snap');
fs.writeFileSync(snapPath, Buffer.from([0x0A, 0xFF, 0xFF]));
execCli(['test.cjs'], {dirname: 'fixture/snapshots'}, (error, stdout) => {
t.ok(error);
t.match(stdout, /The snapshot file is v65535, but only v3 is supported\./);
t.match(stdout, /File path:/);
t.match(stdout, snapPath);
t.match(stdout, /You should upgrade AVA\./);
t.end();
});
});
test('snapshot corruption is reported to the console', t => {
const snapPath = path.join(__dirname, '..', 'fixture', 'snapshots', 'test.cjs.snap');
fs.writeFileSync(snapPath, Buffer.from([0x0A, 0x03, 0x00]));
execCli(['test.cjs'], {dirname: 'fixture/snapshots'}, (error, stdout) => {
t.ok(error);
t.match(stdout, /The snapshot file is corrupted\./);
t.match(stdout, /File path:/);
t.match(stdout, snapPath);
t.match(stdout, /Please run AVA again with the .*--update-snapshots.* flag to recreate it\./);
t.end();
});
});
test('legacy snapshot files are reported to the console', t => {
const snapPath = path.join(__dirname, '..', 'fixture', 'snapshots', 'test.cjs.snap');
fs.writeFileSync(snapPath, Buffer.from('// Jest Snapshot v1, https://goo.gl/fbAQLP\n'));
execCli(['test.cjs'], {dirname: 'fixture/snapshots'}, (error, stdout) => {
t.ok(error);
t.match(stdout, /The snapshot file was created with AVA 0\.19\. Itβs not supported by this AVA version\./);
t.match(stdout, /File path:/);
t.match(stdout, snapPath);
t.match(stdout, /Please run AVA again with the .*--update-snapshots.* flag to upgrade\./);
t.end();
});
});
test('snapshots infer their location and name from sourcemaps', t => {
t.plan(8);
const relativeFixtureDir = path.join('fixture/snapshots/test-sourcemaps');
const snapDirStructure = [
'src',
'src/test/snapshots',
'src/feature/__tests__/__snapshots__',
];
const snapFixtureFilePaths = snapDirStructure
.flatMap(snapRelativeDir => {
const snapPath = path.join(__dirname, '..', relativeFixtureDir, snapRelativeDir);
return [
path.join(snapPath, 'test.ts.md'),
path.join(snapPath, 'test.ts.snap'),
];
});
const removeExistingSnapFixtureFiles = snapPath => {
try {
fs.unlinkSync(snapPath);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
};
for (const x of snapFixtureFilePaths) {
removeExistingSnapFixtureFiles(x);
}
const verifySnapFixtureFiles = relFilePath => {
t.ok(fs.existsSync(relFilePath));
};
execCli([], {dirname: relativeFixtureDir, env: {AVA_FORCE_CI: 'not-ci'}}, (error, stdout) => {
t.error(error);
for (const x of snapFixtureFilePaths) {
verifySnapFixtureFiles(x);
}
t.match(stdout, /6 tests passed/);
t.end();
});
});
test('snapshots resolved location from "snapshotDir" in AVA config', t => {
t.plan(8);
const relativeFixtureDir = 'fixture/snapshots/test-snapshot-location';
const snapDir = 'snapshot-fixtures';
const snapDirStructure = [
'src',
'src/feature',
'src/feature/nested-feature',
];
const snapFixtureFilePaths = snapDirStructure
.flatMap(snapRelativeDir => {
const snapPath = path.join(__dirname, '..', relativeFixtureDir, snapDir, snapRelativeDir);
return [
path.join(snapPath, 'test.cjs.md'),
path.join(snapPath, 'test.cjs.snap'),
];
});
const removeExistingSnapFixtureFiles = snapPath => {
try {
fs.unlinkSync(snapPath);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
};
for (const x of snapFixtureFilePaths) {
removeExistingSnapFixtureFiles(x);
}
const verifySnapFixtureFiles = relFilePath => {
t.ok(fs.existsSync(relFilePath));
};
execCli([], {dirname: relativeFixtureDir, env: {AVA_FORCE_CI: 'not-ci'}}, (error, stdout) => {
t.error(error);
for (const x of snapFixtureFilePaths) {
verifySnapFixtureFiles(x);
}
t.match(stdout, /6 tests passed/);
t.end();
});
});
test('snapshots are identical on different platforms', t => {
const fixtureDir = path.join(__dirname, '..', 'fixture', 'snapshots', 'test-content');
const reportPath = path.join(fixtureDir, 'tests', 'snapshots', 'test.cjs.md');
const snapPath = path.join(fixtureDir, 'tests', 'snapshots', 'test.cjs.snap');
const expectedReportPath = path.join(fixtureDir, 'test.cjs.md.expected');
const expectedSnapPath = path.join(fixtureDir, 'test.cjs.snap.expected');
const removeFile = filePath => {
try {
fs.unlinkSync(filePath);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
};
for (const fp of [reportPath, snapPath]) {
removeFile(fp);
}
execCli(['--update-snapshots'], {dirname: fixtureDir, env: {AVA_FORCE_CI: 'not-ci'}}, error => {
t.error(error);
t.ok(fs.existsSync(reportPath));
t.ok(fs.existsSync(snapPath));
const reportContents = fs.readFileSync(reportPath);
const snapContents = gunzipSync(extractCompressedSnapshot(fs.readFileSync(snapPath)).compressed);
const expectedReportContents = fs.readFileSync(expectedReportPath);
const expectedSnapContents = gunzipSync(extractCompressedSnapshot(fs.readFileSync(expectedSnapPath)).compressed);
t.ok(reportContents.equals(expectedReportContents), 'report file contents matches snapshot');
t.ok(snapContents.equals(expectedSnapContents), 'snap file contents matches snapshot');
t.end();
});
});
test('in CI, new snapshots are not recorded', t => {
const fixtureDir = path.join(__dirname, '..', 'fixture', 'snapshots', 'test-content');
const reportPath = path.join(fixtureDir, 'tests', 'snapshots', 'test.cjs.md');
const snapPath = path.join(fixtureDir, 'tests', 'snapshots', 'test.cjs.snap');
const removeFile = filePath => {
try {
fs.unlinkSync(filePath);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
};
for (const fp of [reportPath, snapPath]) {
removeFile(fp);
}
execCli([], {dirname: fixtureDir}, (_, stdout) => {
t.match(stdout, 'No snapshot available β new snapshots are not created in CI environments');
t.notOk(fs.existsSync(reportPath));
t.notOk(fs.existsSync(snapPath));
t.end();
});
});