start test suite for dubdiff engine

This commit is contained in:
Adam Brown 2016-12-09 18:01:59 -05:00
parent 55a30797ec
commit 755143c0c3
5 changed files with 156 additions and 17 deletions

3
.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"presets": ["node6", "react"],
}

View File

@ -9,7 +9,8 @@
"start": "npm run copy-css && webpack --progress --colors --watch",
"serve": "node src/server/babel.index.js",
"webpack-stats": "webpack --profile --json > stats.json",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "mocha --watch --compilers js:babel-register"
},
"author": "",
"license": "BSD-2-Clause",
@ -39,7 +40,10 @@
"babel-preset-es2015-native-modules": "^6.9.4",
"babel-preset-node6": "^11.0.0",
"babel-preset-react": "^6.3.13",
"babel-register": "^6.18.0",
"chai": "^3.5.0",
"copyfiles": "^0.2.2",
"mocha": "^3.2.0",
"piping": "^1.0.0-rc.4",
"webpack": "^2.1.0-beta.27"
}

View File

@ -6,6 +6,8 @@ import * as JsDiff from 'diff'
// the diff would use a custom compare function that would disregard the spaces
// alternately, the text could be split with the spaces included in the array and then compared with a
// custom diff function that would treat the space elements as null/ignored
//the current mechanism for adding and removing spaces is fragile and broken
export function plaintextDiff(original, final) {
let arrOriginal = plaintextSplit(original)
let arrFinal = plaintextSplit(final)
@ -27,22 +29,29 @@ export function markdownDiff(original, final) {
return diff
}
export function diffToLogString(diff) {
// returns a string version of the diff, with "{+ ... +}" and "[- ... -]"
// representing ins and del blocks
export function diffToString(diff) {
return diff.map(({added, removed, value}) => {
let sym = added ? "+" : removed ? '-' : '/'
return sym+value+sym
})
let start = added ? '{+' : removed ? '[-' : ''
let end = added ? '+}' : removed ? '-]' : ''
let string = value
if (Array.isArray(value))
string = value.join('')
return start+string+end
}).join(' ')
}
let plaintextSplit = text =>text.split(/[ ]|(\n)/)
function plaintextRestoreSpaces (diff) {
return diff.map(({added, removed, value}) => ({
added,
removed,
value:value.map((str, idx, arr) => (
(str!='\n' && (idx<arr.length-1)) ? str+" " : str)
).join('')
)
}))
}
@ -79,7 +88,7 @@ function rewriteMarkdownDiff(diff) {
function applyTransformationRule1(diff) {
let transformedDiff = []
const B_ADD='added', B_REM='removed', B_SAME='same'
const B_ADDED='added', B_REMOVED='removed', B_SAME='same'
let previousBlockType = null
let currentBlockType = null
let previousBlockWasMultiline = false
@ -87,16 +96,18 @@ function applyTransformationRule1(diff) {
//iterate the input tokens to create the intermediate representation
diff.forEach((currentBlock) => {
previousBlockType = currentBlockType
previousBlockWasMultiline = currentBlockIsMultiline
currentBlockType = (currentBlock.added ? B_ADD : (currentBlock.removed ? B_REMOVED : B_SAME))
currentBlockType = (currentBlock.added ? B_ADDED : (currentBlock.removed ? B_REMOVED : B_SAME))
currentBlockIsMultiline = isMultilineDiffBlock(currentBlock)
//transform rule 1 applys when:
// the previous block was a del and had multiple lines
// the current block is an ins
if (previousBlockType == B_REM && currentBlockType == B_INS && previousBlockWasMultiline) {
if (previousBlockType == B_REMOVED && currentBlockType == B_ADDED && previousBlockWasMultiline) {
console.log('trigger rule 1')
//split the first line from the current block
let currentBlockSplit = splitMultilineDiffBlock(currentBlock)
@ -104,13 +115,14 @@ function applyTransformationRule1(diff) {
let previousBlock = transformedDiff.pop()
//split the first line from the previous block
let previousBlockSplit = splitMultilineDiffBlock(currentBlock)
let previousBlockSplit = splitMultilineDiffBlock(previousBlock)
console.log({currentBlock, currentBlockSplit, previousBlock, previousBlockSplit})
//now add the blocks back, interleaving del and ins blocks
for (let i=0; i<Math.max(previousBlockSplit.length, currentBlockSplit.length); i++) {
if (i<previousBlockSplit.length)
transformedDiff.push(previousBlockSplit[i])
if (i<currentBlockSplit.length)
transformedDiff.push(currentBlockSplit[i])
}
@ -142,6 +154,7 @@ function applyTransformationRule2(diff) {
/// ...
/*
transform.forEach(function(item) {
//newlines are undecorated
if (item.string == '\n') {
@ -193,7 +206,7 @@ function applyTransformationRule2(diff) {
}
});
*/
}
@ -213,23 +226,31 @@ function isMultilineDiffBlock({value}) {
//if the diff block begins with a newline, the returned array will begin with an empty diff
function splitMultilineDiffBlock({added, removed, value}) {
//find the indices of the diff block that coorespond to newlines
const splits = findIndicesOf(value, '\n')
const splits = findIndicesOf(value, str => str=='\n')
splits.push(value.length)
//create a range from each index
const ranges = splits.reduce(
//the accumulator is a structure with the last index and the list of ranges
//the ranges are a {start, end} structure
({last, ranges}, i) => {i, ranges.concat([{start:last, end:i}])},
({last, ranges}, i) => {
ranges = ranges.concat([{start:last, end:i}])
return {last:i, ranges}
},
//start with the zero index and an empty array
{last: 0, ranges:[]}
).ranges
//map the ranges into blocks
const blocks = ranges.map(
//each block is the same as the given original block, but with the values split at newlines
({start, end}) => {added, removed, value.slice(start, end)}
({start, end}) => ({added, removed, value:value.slice(start, end)})
)
console.log({value, splits, ranges, blocks})
return blocks
}

48
test/dubdiffMarkdown.js Normal file
View File

@ -0,0 +1,48 @@
/*eslint-env node, mocha */
/*global expect */
/*eslint no-console: 0*/
'use strict';
import chai from 'chai'
import {markdownDiff, diffToString} from '../src/common/util/dubdiff'
let diff = (a,b) => diffToString(markdownDiff(a,b))
const expect = chai.expect
describe('dubdiff', () => {
let db;
beforeEach(() => {
});
it('plaintext diffs consecutive words', ()=>{
expect(diff(
'This is a smlb sentnce with no errors.',
'This is a simple sentence with no errors.'
)).to.equal('This is a [-smlb sentnce-] {+simple sentence+} with no errors.')
})
it('plaintext diffs with word deletion', ()=>{
expect(diff(
'Gonna delete a word.',
'Gonna delete word.'
)).to.equal('Gonna delete [-a-] word.')
})
it('plaintext diffs with word insertion', ()=>{
expect(diff(
'Gonna delete word.',
'Gonna delete a word.'
)).to.equal('Gonna delete {+a+} word.')
})
it('reorganizes insertions after multiline deletions', ()=>{
expect(diff(
`# Title
other`,
`# Subtitle`
)).to.equal('# [-Title-] {+Subtitle+}[-\nother-]')
})
})

63
test/dubdiffPlaintext.js Normal file
View File

@ -0,0 +1,63 @@
/*eslint-env node, mocha */
/*global expect */
/*eslint no-console: 0*/
'use strict';
import chai from 'chai'
import {plaintextDiff, diffToString} from '../src/common/util/dubdiff'
let diff = (a,b) => diffToString(plaintextDiff(a,b))
const expect = chai.expect
describe('dubdiff', () => {
beforeEach(() => {
});
it('diffs single words', ()=>{
expect(diff(
'This is a smlb sentence.',
'This is a simple sentence.'
)).to.equal('This is a [-smlb-] {+simple+} sentence.')
})
it('diffs consecutive words', ()=>{
expect(diff(
'This is a smlb sentnce with no errors.',
'This is a simple sentence with no errors.'
)).to.equal('This is a [-smlb sentnce-] {+simple sentence+} with no errors.')
})
it('diffs with word deletion', ()=>{
expect(diff(
'Gonna delete a word.',
'Gonna delete word.'
)).to.equal('Gonna delete [-a-] word.')
})
it('diffs with word insertion', ()=>{
expect(diff(
'Gonna delete word.',
'Gonna delete a word.'
)).to.equal('Gonna delete {+a+} word.')
})
it('diffs accross newline without weird spaces', () => {
expect(diff(
'This is a flawed\ncomment',
'This is a corrected\nitem'
)).to.equal('This is a [-flawed-] {+corrected+}\n[-comment-] {+item+}')
})
it('doesn\'t add spaces after newline', () => {
expect(diff(
'\nhere',
'\nhere'
)).to.equal('\nhere')
})
it('doesn\'t add spaces before newline', () => {
expect(diff(
'there\n',
'there\n'
)).to.equal('there\n')
})
})