Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */
import type { DiffOptionsColor } from './types'import { Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT } from './cleanupSemantic'
// Given change op and array of diffs, return concatenated string:// * include common strings// * include change strings which have argument op with changeColor// * exclude change strings which have opposite opfunction concatenateRelevantDiffs( op: number, diffs: Array<Diff>, changeColor: DiffOptionsColor,): string { return diffs.reduce( (reduced: string, diff: Diff): string => reduced + (diff[0] === DIFF_EQUAL ? diff[1] : diff[0] === op && diff[1].length !== 0 // empty if change is newline ? changeColor(diff[1]) : ''), '', )}
// Encapsulate change lines until either a common newline or the end.class ChangeBuffer { private readonly op: number private line: Array<Diff> // incomplete line private lines: Array<Diff> // complete lines private readonly changeColor: DiffOptionsColor
constructor(op: number, changeColor: DiffOptionsColor) { this.op = op this.line = [] this.lines = [] this.changeColor = changeColor }
private pushSubstring(substring: string): void { this.pushDiff(new Diff(this.op, substring)) }
private pushLine(): void { // Assume call only if line has at least one diff, // therefore an empty line must have a diff which has an empty string.
// If line has multiple diffs, then assume it has a common diff, // therefore change diffs have change color; // otherwise then it has line color only. this.lines.push( this.line.length !== 1 ? new Diff( this.op, concatenateRelevantDiffs(this.op, this.line, this.changeColor), ) : this.line[0][0] === this.op ? this.line[0] // can use instance : new Diff(this.op, this.line[0][1]), // was common diff ) this.line.length = 0 }
isLineEmpty() { return this.line.length === 0 }
// Minor input to buffer. pushDiff(diff: Diff): void { this.line.push(diff) }
// Main input to buffer. align(diff: Diff): void { const string = diff[1]
if (string.includes('\n')) { const substrings = string.split('\n') const iLast = substrings.length - 1 substrings.forEach((substring, i) => { if (i < iLast) { // The first substring completes the current change line. // A middle substring is a change line. this.pushSubstring(substring) this.pushLine() } else if (substring.length !== 0) { // The last substring starts a change line, if it is not empty. // Important: This non-empty condition also automatically omits // the newline appended to the end of expected and received strings. this.pushSubstring(substring) } }) } else { // Append non-multiline string to current change line. this.pushDiff(diff) } }
// Output from buffer. moveLinesTo(lines: Array<Diff>): void { if (!this.isLineEmpty()) { this.pushLine() }
lines.push(...this.lines) this.lines.length = 0 }}
// Encapsulate common and change lines.class CommonBuffer { private readonly deleteBuffer: ChangeBuffer private readonly insertBuffer: ChangeBuffer private readonly lines: Array<Diff>
constructor(deleteBuffer: ChangeBuffer, insertBuffer: ChangeBuffer) { this.deleteBuffer = deleteBuffer this.insertBuffer = insertBuffer this.lines = [] }
private pushDiffCommonLine(diff: Diff): void { this.lines.push(diff) }
private pushDiffChangeLines(diff: Diff): void { const isDiffEmpty = diff[1].length === 0
// An empty diff string is redundant, unless a change line is empty. if (!isDiffEmpty || this.deleteBuffer.isLineEmpty()) { this.deleteBuffer.pushDiff(diff) }
if (!isDiffEmpty || this.insertBuffer.isLineEmpty()) { this.insertBuffer.pushDiff(diff) } }
private flushChangeLines(): void { this.deleteBuffer.moveLinesTo(this.lines) this.insertBuffer.moveLinesTo(this.lines) }
// Input to buffer. align(diff: Diff): void { const op = diff[0] const string = diff[1]
if (string.includes('\n')) { const substrings = string.split('\n') const iLast = substrings.length - 1 substrings.forEach((substring, i) => { if (i === 0) { const subdiff = new Diff(op, substring) if ( this.deleteBuffer.isLineEmpty() && this.insertBuffer.isLineEmpty() ) { // If both current change lines are empty, // then the first substring is a common line. this.flushChangeLines() this.pushDiffCommonLine(subdiff) } else { // If either current change line is non-empty, // then the first substring completes the change lines. this.pushDiffChangeLines(subdiff) this.flushChangeLines() } } else if (i < iLast) { // A middle substring is a common line. this.pushDiffCommonLine(new Diff(op, substring)) } else if (substring.length !== 0) { // The last substring starts a change line, if it is not empty. // Important: This non-empty condition also automatically omits // the newline appended to the end of expected and received strings. this.pushDiffChangeLines(new Diff(op, substring)) } }) } else { // Append non-multiline string to current change lines. // Important: It cannot be at the end following empty change lines, // because newline appended to the end of expected and received strings. this.pushDiffChangeLines(diff) } }
// Output from buffer. getLines(): Array<Diff> { this.flushChangeLines() return this.lines }}
// Given diffs from expected and received strings,// return new array of diffs split or joined into lines.//// To correctly align a change line at the end, the algorithm:// * assumes that a newline was appended to the strings// * omits the last newline from the output array//// Assume the function is not called:// * if either expected or received is empty string// * if neither expected nor received is multiline stringfunction getAlignedDiffs( diffs: Array<Diff>, changeColor: DiffOptionsColor,): Array<Diff> { const deleteBuffer = new ChangeBuffer(DIFF_DELETE, changeColor) const insertBuffer = new ChangeBuffer(DIFF_INSERT, changeColor) const commonBuffer = new CommonBuffer(deleteBuffer, insertBuffer)
diffs.forEach((diff) => { switch (diff[0]) { case DIFF_DELETE: deleteBuffer.align(diff) break
case DIFF_INSERT: insertBuffer.align(diff) break
default: commonBuffer.align(diff) } })
return commonBuffer.getLines()}
export default getAlignedDiffs