mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 21:19:50 +01:00
c921676512
This commit marks the test runner's snapshot testing API as stable. PR-URL: https://github.com/nodejs/node/pull/55897 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Pietro Marchini <pietro.marchini94@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
411 lines
14 KiB
JavaScript
411 lines
14 KiB
JavaScript
// Flags: --expose-internals
|
|
/* eslint-disable no-template-curly-in-string */
|
|
'use strict';
|
|
const common = require('../common');
|
|
const fixtures = require('../common/fixtures');
|
|
const tmpdir = require('../common/tmpdir');
|
|
const {
|
|
snapshot,
|
|
suite,
|
|
test,
|
|
} = require('node:test');
|
|
const {
|
|
SnapshotManager,
|
|
defaultResolveSnapshotPath,
|
|
defaultSerializers,
|
|
} = require('internal/test_runner/snapshot');
|
|
const fs = require('node:fs');
|
|
|
|
tmpdir.refresh();
|
|
|
|
suite('SnapshotManager', () => {
|
|
test('uses default snapshot naming scheme', (t) => {
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(__filename);
|
|
t.assert.strictEqual(file.snapshotFile, `${__filename}.snapshot`);
|
|
});
|
|
|
|
test('generates snapshot IDs based on provided name', (t) => {
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(__filename);
|
|
|
|
t.assert.strictEqual(file.nextId('foo'), 'foo 1');
|
|
t.assert.strictEqual(file.nextId('foo'), 'foo 2');
|
|
t.assert.strictEqual(file.nextId('bar'), 'bar 1');
|
|
t.assert.strictEqual(file.nextId('baz'), 'baz 1');
|
|
t.assert.strictEqual(file.nextId('foo'), 'foo 3');
|
|
t.assert.strictEqual(file.nextId('foo`'), 'foo` 1');
|
|
t.assert.strictEqual(file.nextId('foo\\'), 'foo\\ 1');
|
|
t.assert.strictEqual(file.nextId('foo`${x}`'), 'foo`${x}` 1');
|
|
});
|
|
|
|
test('throws if snapshot file does not have exports', (t) => {
|
|
const fixture = fixtures.path(
|
|
'test-runner', 'snapshots', 'malformed-exports.js'
|
|
);
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(fixture);
|
|
|
|
t.assert.throws(() => {
|
|
file.readFile();
|
|
}, (err) => {
|
|
t.assert.strictEqual(err.code, 'ERR_INVALID_STATE');
|
|
t.assert.match(err.message, /Cannot read snapshot/);
|
|
t.assert.strictEqual(err.filename, file.snapshotFile);
|
|
t.assert.match(err.cause.message, /Malformed snapshot file/);
|
|
return true;
|
|
});
|
|
});
|
|
|
|
test('provides a tip if snapshot file does not exist', (t) => {
|
|
const fixture = fixtures.path(
|
|
'test-runner', 'snapshots', 'this-file-should-not-exist.js'
|
|
);
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(fixture);
|
|
|
|
t.assert.throws(() => {
|
|
file.readFile();
|
|
}, /Missing snapshots can be generated by rerunning the command/);
|
|
});
|
|
|
|
test('throws if serialization cannot generate a string', (t) => {
|
|
const manager = new SnapshotManager(false);
|
|
const cause = new Error('boom');
|
|
const input = {
|
|
foo: 1,
|
|
toString() {
|
|
throw cause;
|
|
},
|
|
};
|
|
|
|
t.assert.throws(() => {
|
|
manager.serialize(input, [(value) => { return value; }]);
|
|
}, (err) => {
|
|
t.assert.strictEqual(err.code, 'ERR_INVALID_STATE');
|
|
t.assert.match(err.message, /The provided serializers did not generate a string/);
|
|
t.assert.strictEqual(err.input, input);
|
|
t.assert.strictEqual(err.cause, cause);
|
|
return true;
|
|
});
|
|
});
|
|
|
|
test('serializes values using provided functions', (t) => {
|
|
const manager = new SnapshotManager(false);
|
|
const output = manager.serialize({ foo: 1 }, [
|
|
(value) => { return JSON.stringify(value); },
|
|
(value) => { return value + '424242'; },
|
|
]);
|
|
|
|
t.assert.strictEqual(output, '\n{"foo":1}424242\n');
|
|
});
|
|
|
|
test('serialized values get cast to string', (t) => {
|
|
const manager = new SnapshotManager(false);
|
|
const output = manager.serialize(5, []);
|
|
|
|
t.assert.strictEqual(output, '\n5\n');
|
|
});
|
|
|
|
test('serialized values get escaped', (t) => {
|
|
const manager = new SnapshotManager(false);
|
|
const output = manager.serialize('fo\\o`${x}`', []);
|
|
|
|
t.assert.strictEqual(output, '\nfo\\\\o\\`\\${x}\\`\n');
|
|
});
|
|
|
|
test('reads individual snapshots from snapshot file', (t) => {
|
|
const fixture = fixtures.path('test-runner', 'snapshots', 'simple.js');
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(fixture);
|
|
file.readFile();
|
|
const snapshot = file.getSnapshot('foo 1');
|
|
|
|
t.assert.strictEqual(snapshot, '\n{\n "bar": 1,\n "baz": 2\n}\n');
|
|
});
|
|
|
|
test('snapshot file is not read in update mode', (t) => {
|
|
const fixture = fixtures.path('test-runner', 'snapshots', 'simple.js');
|
|
const manager = new SnapshotManager(true);
|
|
const file = manager.resolveSnapshotFile(fixture);
|
|
file.readFile();
|
|
|
|
t.assert.throws(() => {
|
|
file.getSnapshot('foo 1');
|
|
}, /Snapshot 'foo 1' not found/);
|
|
});
|
|
|
|
test('throws if requested snapshot does not exist in file', (t) => {
|
|
const fixture = fixtures.path('test-runner', 'snapshots', 'simple.js');
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(fixture);
|
|
|
|
t.assert.throws(() => {
|
|
file.getSnapshot('does not exist 1');
|
|
}, (err) => {
|
|
t.assert.strictEqual(err.code, 'ERR_INVALID_STATE');
|
|
t.assert.match(err.message, /Snapshot 'does not exist 1' not found/);
|
|
t.assert.strictEqual(err.snapshot, 'does not exist 1');
|
|
t.assert.strictEqual(err.filename, file.snapshotFile);
|
|
return true;
|
|
});
|
|
});
|
|
|
|
test('snapshot IDs are escaped when stored', (t) => {
|
|
const fixture = fixtures.path('test-runner', 'snapshots', 'simple.js');
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(fixture);
|
|
|
|
file.setSnapshot('foo`${x}` 1', 'test');
|
|
t.assert.strictEqual(file.getSnapshot('foo\\`\\${x}\\` 1'), 'test');
|
|
});
|
|
|
|
test('throws if snapshot file cannot be resolved', (t) => {
|
|
const manager = new SnapshotManager(false);
|
|
const assertion = manager.createAssert();
|
|
|
|
t.assert.throws(() => {
|
|
Reflect.apply(assertion, { filePath: null }, ['foo']);
|
|
}, (err) => {
|
|
t.assert.strictEqual(err.code, 'ERR_INVALID_STATE');
|
|
t.assert.match(err.message, /Invalid snapshot filename/);
|
|
t.assert.strictEqual(err.filename, null);
|
|
return true;
|
|
});
|
|
});
|
|
|
|
test('writes the specified snapshot files', (t) => {
|
|
const testFile1 = tmpdir.resolve('test1.js');
|
|
const testFile2 = tmpdir.resolve('test2.js');
|
|
const manager = new SnapshotManager(true);
|
|
const file1 = manager.resolveSnapshotFile(testFile1);
|
|
const file2 = manager.resolveSnapshotFile(testFile2);
|
|
file1.setSnapshot('foo 1', 'foo 1 value');
|
|
file2.setSnapshot('foo 2', 'foo 2 value');
|
|
t.assert.strictEqual(fs.existsSync(file1.snapshotFile), false);
|
|
t.assert.strictEqual(fs.existsSync(file2.snapshotFile), false);
|
|
manager.writeSnapshotFiles();
|
|
t.assert.strictEqual(fs.existsSync(file1.snapshotFile), true);
|
|
t.assert.strictEqual(fs.existsSync(file2.snapshotFile), true);
|
|
});
|
|
|
|
test('creates snapshot directory if it does not exist', (t) => {
|
|
const testFile = tmpdir.resolve('foo/bar/baz/test2.js');
|
|
const manager = new SnapshotManager(true);
|
|
const file = manager.resolveSnapshotFile(testFile);
|
|
file.setSnapshot('foo 1', 'foo value');
|
|
t.assert.strictEqual(fs.existsSync(file.snapshotFile), false);
|
|
manager.writeSnapshotFiles();
|
|
t.assert.strictEqual(fs.existsSync(file.snapshotFile), true);
|
|
});
|
|
|
|
test('does not write snapshot files in read mode', (t) => {
|
|
const testFile = tmpdir.resolve('test3.js');
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(testFile);
|
|
file.setSnapshot('foo 1', 'foo value');
|
|
t.assert.strictEqual(fs.existsSync(file.snapshotFile), false);
|
|
manager.writeSnapshotFiles();
|
|
t.assert.strictEqual(fs.existsSync(file.snapshotFile), false);
|
|
});
|
|
|
|
test('throws if snapshot files cannot be written', (t) => {
|
|
const testFile = tmpdir.resolve('test4.js');
|
|
const error = new Error('boom');
|
|
const manager = new SnapshotManager(true);
|
|
const file = manager.resolveSnapshotFile(testFile);
|
|
file.snapshots['foo 1'] = { toString() { throw error; } };
|
|
t.assert.strictEqual(fs.existsSync(file.snapshotFile), false);
|
|
t.assert.throws(() => {
|
|
manager.writeSnapshotFiles();
|
|
}, (err) => {
|
|
t.assert.strictEqual(err.code, 'ERR_INVALID_STATE');
|
|
t.assert.match(err.message, /Cannot write snapshot file/);
|
|
t.assert.strictEqual(err.filename, file.snapshotFile);
|
|
t.assert.strictEqual(err.cause, error);
|
|
return true;
|
|
});
|
|
|
|
t.assert.strictEqual(fs.existsSync(file.snapshotFile), false);
|
|
});
|
|
});
|
|
|
|
suite('t.assert.snapshot() validation', () => {
|
|
test('options must be an object', (t) => {
|
|
t.assert.throws(() => {
|
|
t.assert.snapshot('', null);
|
|
}, /The "options" argument must be of type object/);
|
|
});
|
|
|
|
test('options.serializers must be an array if present', (t) => {
|
|
t.assert.throws(() => {
|
|
t.assert.snapshot('', { serializers: 5 });
|
|
}, /The "options\.serializers" property must be an instance of Array/);
|
|
});
|
|
|
|
test('options.serializers must only contain functions', (t) => {
|
|
t.assert.throws(() => {
|
|
t.assert.snapshot('', { serializers: [() => {}, ''] });
|
|
}, /The "options\.serializers\[1\]" property must be of type function/);
|
|
});
|
|
});
|
|
|
|
suite('setResolveSnapshotPath()', () => {
|
|
test('throws if input is not a function', (t) => {
|
|
t.assert.throws(() => {
|
|
snapshot.setResolveSnapshotPath('');
|
|
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
|
});
|
|
|
|
test('changes default snapshot output path', (t) => {
|
|
t.after(() => {
|
|
snapshot.setResolveSnapshotPath(defaultResolveSnapshotPath);
|
|
});
|
|
|
|
snapshot.setResolveSnapshotPath(() => { return 'foobarbaz'; });
|
|
const manager = new SnapshotManager(false);
|
|
const file = manager.resolveSnapshotFile(__filename);
|
|
t.assert.strictEqual(file.snapshotFile, 'foobarbaz');
|
|
});
|
|
});
|
|
|
|
suite('setDefaultSnapshotSerializers()', () => {
|
|
test('throws if input is not a function array', (t) => {
|
|
t.assert.throws(() => {
|
|
snapshot.setDefaultSnapshotSerializers('');
|
|
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
|
t.assert.throws(() => {
|
|
snapshot.setDefaultSnapshotSerializers([5]);
|
|
}, { code: 'ERR_INVALID_ARG_TYPE' });
|
|
});
|
|
|
|
test('changes default serializers', (t) => {
|
|
t.after(() => {
|
|
snapshot.setDefaultSnapshotSerializers(defaultSerializers);
|
|
});
|
|
|
|
snapshot.setDefaultSnapshotSerializers([() => { return 'foobarbaz'; }]);
|
|
const manager = new SnapshotManager(false);
|
|
const output = manager.serialize({ foo: 1 });
|
|
t.assert.strictEqual(output, '\nfoobarbaz\n');
|
|
});
|
|
});
|
|
|
|
test('t.assert.snapshot()', async (t) => {
|
|
const fixture = fixtures.path(
|
|
'test-runner', 'snapshots', 'unit.js'
|
|
);
|
|
|
|
await t.test('fails prior to snapshot generation', async (t) => {
|
|
const child = await common.spawnPromisified(
|
|
process.execPath,
|
|
[fixture],
|
|
{ cwd: tmpdir.path },
|
|
);
|
|
|
|
t.assert.strictEqual(child.code, 1);
|
|
t.assert.strictEqual(child.signal, null);
|
|
t.assert.match(child.stdout, /tests 5/);
|
|
t.assert.match(child.stdout, /pass 0/);
|
|
t.assert.match(child.stdout, /fail 5/);
|
|
t.assert.match(child.stdout, /Missing snapshots/);
|
|
});
|
|
|
|
await t.test('passes when regenerating snapshots', async (t) => {
|
|
const child = await common.spawnPromisified(
|
|
process.execPath,
|
|
['--test-update-snapshots', fixture],
|
|
{ cwd: tmpdir.path },
|
|
);
|
|
|
|
t.assert.strictEqual(child.code, 0);
|
|
t.assert.strictEqual(child.signal, null);
|
|
t.assert.match(child.stdout, /tests 5/);
|
|
t.assert.match(child.stdout, /pass 5/);
|
|
t.assert.match(child.stdout, /fail 0/);
|
|
});
|
|
|
|
await t.test('passes when snapshots exist', async (t) => {
|
|
const child = await common.spawnPromisified(
|
|
process.execPath,
|
|
[fixture],
|
|
{ cwd: tmpdir.path },
|
|
);
|
|
|
|
t.assert.strictEqual(child.code, 0);
|
|
t.assert.strictEqual(child.signal, null);
|
|
t.assert.match(child.stdout, /tests 5/);
|
|
t.assert.match(child.stdout, /pass 5/);
|
|
t.assert.match(child.stdout, /fail 0/);
|
|
});
|
|
});
|
|
|
|
test('snapshots from multiple files (isolation=none)', async (t) => {
|
|
tmpdir.refresh();
|
|
|
|
const fixture = fixtures.path('test-runner', 'snapshots', 'unit.js');
|
|
const fixture2 = fixtures.path('test-runner', 'snapshots', 'unit-2.js');
|
|
|
|
await t.test('fails prior to snapshot generation', async (t) => {
|
|
const args = [
|
|
'--test',
|
|
'--experimental-test-isolation=none',
|
|
fixture,
|
|
fixture2,
|
|
];
|
|
const child = await common.spawnPromisified(
|
|
process.execPath,
|
|
args,
|
|
{ cwd: tmpdir.path },
|
|
);
|
|
|
|
t.assert.strictEqual(child.code, 1);
|
|
t.assert.strictEqual(child.signal, null);
|
|
t.assert.match(child.stdout, /tests 6/);
|
|
t.assert.match(child.stdout, /pass 0/);
|
|
t.assert.match(child.stdout, /fail 6/);
|
|
t.assert.match(child.stdout, /Missing snapshots/);
|
|
});
|
|
|
|
await t.test('passes when regenerating snapshots', async (t) => {
|
|
const args = [
|
|
'--test',
|
|
'--experimental-test-isolation=none',
|
|
'--test-update-snapshots',
|
|
fixture,
|
|
fixture2,
|
|
];
|
|
const child = await common.spawnPromisified(
|
|
process.execPath,
|
|
args,
|
|
{ cwd: tmpdir.path },
|
|
);
|
|
|
|
t.assert.strictEqual(child.code, 0);
|
|
t.assert.strictEqual(child.signal, null);
|
|
t.assert.match(child.stdout, /tests 6/);
|
|
t.assert.match(child.stdout, /pass 6/);
|
|
t.assert.match(child.stdout, /fail 0/);
|
|
});
|
|
|
|
await t.test('passes when snapshots exist', async (t) => {
|
|
const args = [
|
|
'--test',
|
|
'--experimental-test-isolation=none',
|
|
fixture,
|
|
fixture2,
|
|
];
|
|
const child = await common.spawnPromisified(
|
|
process.execPath,
|
|
args,
|
|
{ cwd: tmpdir.path },
|
|
);
|
|
|
|
t.assert.strictEqual(child.code, 0);
|
|
t.assert.strictEqual(child.signal, null);
|
|
t.assert.match(child.stdout, /tests 6/);
|
|
t.assert.match(child.stdout, /pass 6/);
|
|
t.assert.match(child.stdout, /fail 0/);
|
|
});
|
|
});
|