diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-03-27 21:01:33 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-03-27 21:01:33 +0100 |
commit | cc97a4dd2a967e1c2273bd5f4c5f49a5bf2e2585 (patch) | |
tree | 92c5d88706a6ffc654d1b133618d357890e7096b /node_modules/ava/lib/snapshot-manager.js | |
parent | 3771b4d6b67b34c130f3a9a1a15f42deefdb2eda (diff) |
remove node_modules
Diffstat (limited to 'node_modules/ava/lib/snapshot-manager.js')
-rw-r--r-- | node_modules/ava/lib/snapshot-manager.js | 418 |
1 files changed, 0 insertions, 418 deletions
diff --git a/node_modules/ava/lib/snapshot-manager.js b/node_modules/ava/lib/snapshot-manager.js deleted file mode 100644 index fcc24922e..000000000 --- a/node_modules/ava/lib/snapshot-manager.js +++ /dev/null @@ -1,418 +0,0 @@ -'use strict'; - -const crypto = require('crypto'); -const fs = require('fs'); -const path = require('path'); -const zlib = require('zlib'); - -const writeFileAtomic = require('@ava/write-file-atomic'); -const concordance = require('concordance'); -const indentString = require('indent-string'); -const makeDir = require('make-dir'); -const md5Hex = require('md5-hex'); -const Buffer = require('safe-buffer').Buffer; -const convertSourceMap = require('convert-source-map'); - -const concordanceOptions = require('./concordance-options').snapshotManager; - -// Increment if encoding layout or Concordance serialization versions change. Previous AVA versions will not be able to -// decode buffers generated by a newer version, so changing this value will require a major version bump of AVA itself. -// The version is encoded as an unsigned 16 bit integer. -const VERSION = 1; - -const VERSION_HEADER = Buffer.alloc(2); -VERSION_HEADER.writeUInt16LE(VERSION); - -// The decoder matches on the trailing newline byte (0x0A). -const READABLE_PREFIX = Buffer.from(`AVA Snapshot v${VERSION}\n`, 'ascii'); -const REPORT_SEPARATOR = Buffer.from('\n\n', 'ascii'); -const REPORT_TRAILING_NEWLINE = Buffer.from('\n', 'ascii'); - -const MD5_HASH_LENGTH = 16; - -class SnapshotError extends Error { - constructor(message, snapPath) { - super(message); - this.name = 'SnapshotError'; - this.snapPath = snapPath; - } -} -exports.SnapshotError = SnapshotError; - -class ChecksumError extends SnapshotError { - constructor(snapPath) { - super('Checksum mismatch', snapPath); - this.name = 'ChecksumError'; - } -} -exports.ChecksumError = ChecksumError; - -class VersionMismatchError extends SnapshotError { - constructor(snapPath, version) { - super('Unexpected snapshot version', snapPath); - this.name = 'VersionMismatchError'; - this.snapVersion = version; - this.expectedVersion = VERSION; - } -} -exports.VersionMismatchError = VersionMismatchError; - -const LEGACY_SNAPSHOT_HEADER = Buffer.from('// Jest Snapshot v1'); -function isLegacySnapshot(buffer) { - return LEGACY_SNAPSHOT_HEADER.equals(buffer.slice(0, LEGACY_SNAPSHOT_HEADER.byteLength)); -} - -class LegacyError extends SnapshotError { - constructor(snapPath) { - super('Legacy snapshot file', snapPath); - this.name = 'LegacyError'; - } -} -exports.LegacyError = LegacyError; - -function tryRead(file) { - try { - return fs.readFileSync(file); - } catch (err) { - if (err.code === 'ENOENT') { - return null; - } - - throw err; - } -} - -function withoutLineEndings(buffer) { - let newLength = buffer.byteLength - 1; - while (buffer[newLength] === 0x0A || buffer[newLength] === 0x0D) { - newLength--; - } - return buffer.slice(0, newLength); -} - -function formatEntry(label, descriptor) { - if (label) { - label = `> ${label}\n\n`; - } - const codeBlock = indentString(concordance.formatDescriptor(descriptor, concordanceOptions), 4); - return Buffer.from(label + codeBlock, 'utf8'); -} - -function combineEntries(entries) { - const buffers = []; - let byteLength = 0; - - const sortedKeys = Array.from(entries.keys()).sort(); - for (const key of sortedKeys) { - const keyBuffer = Buffer.from(`\n\n## ${key}\n\n`, 'utf8'); - buffers.push(keyBuffer); - byteLength += keyBuffer.byteLength; - - const formattedEntries = entries.get(key); - const last = formattedEntries[formattedEntries.length - 1]; - for (const entry of formattedEntries) { - buffers.push(entry); - byteLength += entry.byteLength; - - if (entry !== last) { - buffers.push(REPORT_SEPARATOR); - byteLength += REPORT_SEPARATOR.byteLength; - } - } - } - - return {buffers, byteLength}; -} - -function generateReport(relFile, snapFile, entries) { - const combined = combineEntries(entries); - const buffers = combined.buffers; - let byteLength = combined.byteLength; - - const header = Buffer.from(`# Snapshot report for \`${relFile}\` - -The actual snapshot is saved in \`${snapFile}\`. - -Generated by [AVA](https://ava.li).`, 'utf8'); - buffers.unshift(header); - byteLength += header.byteLength; - - buffers.push(REPORT_TRAILING_NEWLINE); - byteLength += REPORT_TRAILING_NEWLINE.byteLength; - return Buffer.concat(buffers, byteLength); -} - -function appendReportEntries(existingReport, entries) { - const combined = combineEntries(entries); - const buffers = combined.buffers; - let byteLength = combined.byteLength; - - const prepend = withoutLineEndings(existingReport); - buffers.unshift(prepend); - byteLength += prepend.byteLength; - - return Buffer.concat(buffers, byteLength); -} - -function encodeSnapshots(buffersByHash) { - const buffers = []; - let byteOffset = 0; - - // Entry start and end pointers are relative to the header length. This means - // it's possible to append new entries to an existing snapshot file, without - // having to rewrite pointers for existing entries. - const headerLength = Buffer.alloc(4); - buffers.push(headerLength); - byteOffset += 4; - - // Allows 65535 hashes (tests or identified snapshots) per file. - const numHashes = Buffer.alloc(2); - numHashes.writeUInt16LE(buffersByHash.size); - buffers.push(numHashes); - byteOffset += 2; - - const entries = []; - for (const pair of buffersByHash) { - const hash = pair[0]; - const snapshotBuffers = pair[1]; - - buffers.push(Buffer.from(hash, 'hex')); - byteOffset += MD5_HASH_LENGTH; - - // Allows 65535 snapshots per hash. - const numSnapshots = Buffer.alloc(2); - numSnapshots.writeUInt16LE(snapshotBuffers.length, 0); - buffers.push(numSnapshots); - byteOffset += 2; - - for (const value of snapshotBuffers) { - // Each pointer is 32 bits, restricting the total, uncompressed buffer to - // 4 GiB. - const start = Buffer.alloc(4); - const end = Buffer.alloc(4); - entries.push({start, end, value}); - - buffers.push(start, end); - byteOffset += 8; - } - } - - headerLength.writeUInt32LE(byteOffset, 0); - - let bodyOffset = 0; - for (const entry of entries) { - const start = bodyOffset; - const end = bodyOffset + entry.value.byteLength; - entry.start.writeUInt32LE(start, 0); - entry.end.writeUInt32LE(end, 0); - buffers.push(entry.value); - bodyOffset = end; - } - byteOffset += bodyOffset; - - const compressed = zlib.gzipSync(Buffer.concat(buffers, byteOffset)); - const md5sum = crypto.createHash('md5').update(compressed).digest(); - return Buffer.concat([ - READABLE_PREFIX, - VERSION_HEADER, - md5sum, - compressed - ], READABLE_PREFIX.byteLength + VERSION_HEADER.byteLength + MD5_HASH_LENGTH + compressed.byteLength); -} - -function decodeSnapshots(buffer, snapPath) { - if (isLegacySnapshot(buffer)) { - throw new LegacyError(snapPath); - } - - // The version starts after the readable prefix, which is ended by a newline - // byte (0x0A). - const versionOffset = buffer.indexOf(0x0A) + 1; - const version = buffer.readUInt16LE(versionOffset); - if (version !== VERSION) { - throw new VersionMismatchError(snapPath, version); - } - - const md5sumOffset = versionOffset + 2; - const compressedOffset = md5sumOffset + MD5_HASH_LENGTH; - const compressed = buffer.slice(compressedOffset); - - const md5sum = crypto.createHash('md5').update(compressed).digest(); - const expectedSum = buffer.slice(md5sumOffset, compressedOffset); - if (!md5sum.equals(expectedSum)) { - throw new ChecksumError(snapPath); - } - - const decompressed = zlib.gunzipSync(compressed); - let byteOffset = 0; - - const headerLength = decompressed.readUInt32LE(byteOffset); - byteOffset += 4; - - const snapshotsByHash = new Map(); - const numHashes = decompressed.readUInt16LE(byteOffset); - byteOffset += 2; - - for (let count = 0; count < numHashes; count++) { - const hash = decompressed.toString('hex', byteOffset, byteOffset + MD5_HASH_LENGTH); - byteOffset += MD5_HASH_LENGTH; - - const numSnapshots = decompressed.readUInt16LE(byteOffset); - byteOffset += 2; - - const snapshotsBuffers = new Array(numSnapshots); - for (let index = 0; index < numSnapshots; index++) { - const start = decompressed.readUInt32LE(byteOffset) + headerLength; - byteOffset += 4; - const end = decompressed.readUInt32LE(byteOffset) + headerLength; - byteOffset += 4; - snapshotsBuffers[index] = decompressed.slice(start, end); - } - - // Allow for new entries to be appended to an existing header, which could - // lead to the same hash being present multiple times. - if (snapshotsByHash.has(hash)) { - snapshotsByHash.set(hash, snapshotsByHash.get(hash).concat(snapshotsBuffers)); - } else { - snapshotsByHash.set(hash, snapshotsBuffers); - } - } - - return snapshotsByHash; -} - -class Manager { - constructor(options) { - this.appendOnly = options.appendOnly; - this.dir = options.dir; - this.relFile = options.relFile; - this.reportFile = options.reportFile; - this.snapFile = options.snapFile; - this.snapPath = options.snapPath; - this.snapshotsByHash = options.snapshotsByHash; - - this.hasChanges = false; - this.reportEntries = new Map(); - } - - compare(options) { - const hash = md5Hex(options.belongsTo); - const entries = this.snapshotsByHash.get(hash) || []; - if (options.index > entries.length) { - throw new RangeError(`Cannot record snapshot ${options.index} for ${JSON.stringify(options.belongsTo)}, exceeds expected index of ${entries.length}`); - } - if (options.index === entries.length) { - this.record(hash, options); - return {pass: true}; - } - - const snapshotBuffer = entries[options.index]; - const actual = concordance.deserialize(snapshotBuffer, concordanceOptions); - - const expected = concordance.describe(options.expected, concordanceOptions); - const pass = concordance.compareDescriptors(actual, expected); - - return {actual, expected, pass}; - } - - record(hash, options) { - const descriptor = concordance.describe(options.expected, concordanceOptions); - - this.hasChanges = true; - const snapshot = concordance.serialize(descriptor); - if (this.snapshotsByHash.has(hash)) { - this.snapshotsByHash.get(hash).push(snapshot); - } else { - this.snapshotsByHash.set(hash, [snapshot]); - } - - const entry = formatEntry(options.label, descriptor); - if (this.reportEntries.has(options.belongsTo)) { - this.reportEntries.get(options.belongsTo).push(entry); - } else { - this.reportEntries.set(options.belongsTo, [entry]); - } - } - - save() { - if (!this.hasChanges) { - return null; - } - - const snapPath = this.snapPath; - const buffer = encodeSnapshots(this.snapshotsByHash); - - const reportPath = path.join(this.dir, this.reportFile); - const existingReport = this.appendOnly ? tryRead(reportPath) : null; - const reportBuffer = existingReport ? - appendReportEntries(existingReport, this.reportEntries) : - generateReport(this.relFile, this.snapFile, this.reportEntries); - - makeDir.sync(this.dir); - const tmpSnapPath = writeFileAtomic.sync(snapPath, buffer); - const tmpReportPath = writeFileAtomic.sync(reportPath, reportBuffer); - - return [tmpSnapPath, tmpReportPath, snapPath, reportPath]; - } -} - -function determineSnapshotDir(options) { - const testDir = determineSourceMappedDir(options); - if (options.fixedLocation) { - const relativeTestLocation = path.relative(options.projectDir, testDir); - return path.join(options.fixedLocation, relativeTestLocation); - } - - const parts = new Set(path.relative(options.projectDir, testDir).split(path.sep)); - if (parts.has('__tests__')) { - return path.join(testDir, '__snapshots__'); - } - if (parts.has('test') || parts.has('tests')) { // Accept tests, even though it's not in the default test patterns - return path.join(testDir, 'snapshots'); - } - - return testDir; -} - -function determineSourceMappedDir(options) { - const source = tryRead(options.file).toString(); - const converter = convertSourceMap.fromSource(source) || convertSourceMap.fromMapFileSource(source, options.testDir); - if (converter) { - const map = converter.toObject(); - const firstSource = `${map.sourceRoot || ''}${map.sources[0]}`; - const sourceFile = path.resolve(options.testDir, firstSource); - return path.dirname(sourceFile); - } - - return options.testDir; -} - -function load(options) { - const dir = determineSnapshotDir(options); - const reportFile = `${options.name}.md`; - const snapFile = `${options.name}.snap`; - const snapPath = path.join(dir, snapFile); - - let appendOnly = !options.updating; - let snapshotsByHash; - - if (!options.updating) { - const buffer = tryRead(snapPath); - if (buffer) { - snapshotsByHash = decodeSnapshots(buffer, snapPath); - } else { - appendOnly = false; - } - } - - return new Manager({ - appendOnly, - dir, - relFile: options.relFile, - reportFile, - snapFile, - snapPath, - snapshotsByHash: snapshotsByHash || new Map() - }); -} -exports.load = load; |