mirror of
https://github.com/nodejs/node.git
synced 2025-05-05 21:04:16 +00:00

PR-URL: https://github.com/nodejs/node/pull/44095 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
130 lines
3.5 KiB
JavaScript
130 lines
3.5 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ArrayPrototypeJoin,
|
|
ArrayPrototypeMap,
|
|
ArrayPrototypeSlice,
|
|
RegExp,
|
|
SafeMap,
|
|
SafeSet,
|
|
StringPrototypeSplit,
|
|
StringPrototypeReplace,
|
|
Symbol,
|
|
} = primordials;
|
|
|
|
const { codes: { ERR_ASSERT_SNAPSHOT_NOT_SUPPORTED } } = require('internal/errors');
|
|
const AssertionError = require('internal/assert/assertion_error');
|
|
const { inspect } = require('internal/util/inspect');
|
|
const { getOptionValue } = require('internal/options');
|
|
const { validateString } = require('internal/validators');
|
|
const { once } = require('events');
|
|
const { createReadStream, createWriteStream } = require('fs');
|
|
const path = require('path');
|
|
const assert = require('assert');
|
|
|
|
const kUpdateSnapshot = getOptionValue('--update-assert-snapshot');
|
|
const kInitialSnapshot = Symbol('kInitialSnapshot');
|
|
const kDefaultDelimiter = '\n#*#*#*#*#*#*#*#*#*#*#*#\n';
|
|
const kDefaultDelimiterRegex = new RegExp(kDefaultDelimiter.replaceAll('*', '\\*').replaceAll('\n', '\r?\n'), 'g');
|
|
const kKeyDelimiter = /:\r?\n/g;
|
|
|
|
function getSnapshotPath() {
|
|
if (process.mainModule) {
|
|
const { dir, name } = path.parse(process.mainModule.filename);
|
|
return path.join(dir, `${name}.snapshot`);
|
|
}
|
|
if (!process.argv[1]) {
|
|
throw new ERR_ASSERT_SNAPSHOT_NOT_SUPPORTED();
|
|
}
|
|
const { dir, name } = path.parse(process.argv[1]);
|
|
return path.join(dir, `${name}.snapshot`);
|
|
}
|
|
|
|
function getSource() {
|
|
return createReadStream(getSnapshotPath(), { encoding: 'utf8' });
|
|
}
|
|
|
|
let _target;
|
|
function getTarget() {
|
|
_target ??= createWriteStream(getSnapshotPath(), { encoding: 'utf8' });
|
|
return _target;
|
|
}
|
|
|
|
function serializeName(name) {
|
|
validateString(name, 'name');
|
|
return StringPrototypeReplace(`${name}`, kKeyDelimiter, '_');
|
|
}
|
|
|
|
let writtenNames;
|
|
let snapshotValue;
|
|
let counter = 0;
|
|
|
|
async function writeSnapshot({ name, value }) {
|
|
const target = getTarget();
|
|
if (counter > 1) {
|
|
target.write(kDefaultDelimiter);
|
|
}
|
|
writtenNames = writtenNames || new SafeSet();
|
|
if (writtenNames.has(name)) {
|
|
throw new AssertionError({ message: `Snapshot "${name}" already used` });
|
|
}
|
|
writtenNames.add(name);
|
|
const drained = target.write(`${name}:\n${value}`);
|
|
await drained || once(target, 'drain');
|
|
}
|
|
|
|
async function getSnapshot() {
|
|
if (snapshotValue !== undefined) {
|
|
return snapshotValue;
|
|
}
|
|
if (kUpdateSnapshot) {
|
|
snapshotValue = kInitialSnapshot;
|
|
return kInitialSnapshot;
|
|
}
|
|
try {
|
|
const source = getSource();
|
|
let data = '';
|
|
for await (const line of source) {
|
|
data += line;
|
|
}
|
|
snapshotValue = new SafeMap(
|
|
ArrayPrototypeMap(
|
|
StringPrototypeSplit(data, kDefaultDelimiterRegex),
|
|
(item) => {
|
|
const arr = StringPrototypeSplit(item, kKeyDelimiter);
|
|
return [
|
|
arr[0],
|
|
ArrayPrototypeJoin(ArrayPrototypeSlice(arr, 1), ':\n'),
|
|
];
|
|
}
|
|
));
|
|
} catch (e) {
|
|
if (e.code === 'ENOENT') {
|
|
snapshotValue = kInitialSnapshot;
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
return snapshotValue;
|
|
}
|
|
|
|
|
|
async function snapshot(input, name) {
|
|
const snapshot = await getSnapshot();
|
|
counter = counter + 1;
|
|
name = serializeName(name);
|
|
|
|
const value = inspect(input);
|
|
if (snapshot === kInitialSnapshot) {
|
|
await writeSnapshot({ name, value });
|
|
} else if (snapshot.has(name)) {
|
|
const expected = snapshot.get(name);
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
assert.strictEqual(value, expected);
|
|
} else {
|
|
throw new AssertionError({ message: `Snapshot "${name}" does not exist`, actual: inspect(snapshot) });
|
|
}
|
|
}
|
|
|
|
module.exports = snapshot;
|