start test suite for dubdiff engine
This commit is contained in:
parent
55a30797ec
commit
755143c0c3
@ -9,7 +9,8 @@
|
|||||||
"start": "npm run copy-css && webpack --progress --colors --watch",
|
"start": "npm run copy-css && webpack --progress --colors --watch",
|
||||||
"serve": "node src/server/babel.index.js",
|
"serve": "node src/server/babel.index.js",
|
||||||
"webpack-stats": "webpack --profile --json > stats.json",
|
"webpack-stats": "webpack --profile --json > stats.json",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "mocha --watch --compilers js:babel-register"
|
||||||
|
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
@ -39,7 +40,10 @@
|
|||||||
"babel-preset-es2015-native-modules": "^6.9.4",
|
"babel-preset-es2015-native-modules": "^6.9.4",
|
||||||
"babel-preset-node6": "^11.0.0",
|
"babel-preset-node6": "^11.0.0",
|
||||||
"babel-preset-react": "^6.3.13",
|
"babel-preset-react": "^6.3.13",
|
||||||
|
"babel-register": "^6.18.0",
|
||||||
|
"chai": "^3.5.0",
|
||||||
"copyfiles": "^0.2.2",
|
"copyfiles": "^0.2.2",
|
||||||
|
"mocha": "^3.2.0",
|
||||||
"piping": "^1.0.0-rc.4",
|
"piping": "^1.0.0-rc.4",
|
||||||
"webpack": "^2.1.0-beta.27"
|
"webpack": "^2.1.0-beta.27"
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import * as JsDiff from 'diff'
|
|||||||
// the diff would use a custom compare function that would disregard the spaces
|
// 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
|
// 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
|
// 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) {
|
export function plaintextDiff(original, final) {
|
||||||
let arrOriginal = plaintextSplit(original)
|
let arrOriginal = plaintextSplit(original)
|
||||||
let arrFinal = plaintextSplit(final)
|
let arrFinal = plaintextSplit(final)
|
||||||
@ -27,22 +29,29 @@ export function markdownDiff(original, final) {
|
|||||||
return diff
|
return diff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns a string version of the diff, with "{+ ... +}" and "[- ... -]"
|
||||||
export function diffToLogString(diff) {
|
// representing ins and del blocks
|
||||||
|
export function diffToString(diff) {
|
||||||
return diff.map(({added, removed, value}) => {
|
return diff.map(({added, removed, value}) => {
|
||||||
let sym = added ? "+" : removed ? '-' : '/'
|
let start = added ? '{+' : removed ? '[-' : ''
|
||||||
return sym+value+sym
|
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)/)
|
let plaintextSplit = text =>text.split(/[ ]|(\n)/)
|
||||||
|
|
||||||
function plaintextRestoreSpaces (diff) {
|
function plaintextRestoreSpaces (diff) {
|
||||||
return diff.map(({added, removed, value}) => ({
|
return diff.map(({added, removed, value}) => ({
|
||||||
added,
|
added,
|
||||||
removed,
|
removed,
|
||||||
value:value.map((str, idx, arr) => (
|
value:value.map((str, idx, arr) => (
|
||||||
(str!='\n' && (idx<arr.length-1)) ? str+" " : str)
|
(str!='\n' && (idx<arr.length-1)) ? str+" " : str)
|
||||||
).join('')
|
)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +88,7 @@ function rewriteMarkdownDiff(diff) {
|
|||||||
function applyTransformationRule1(diff) {
|
function applyTransformationRule1(diff) {
|
||||||
let transformedDiff = []
|
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 previousBlockType = null
|
||||||
let currentBlockType = null
|
let currentBlockType = null
|
||||||
let previousBlockWasMultiline = false
|
let previousBlockWasMultiline = false
|
||||||
@ -87,16 +96,18 @@ function applyTransformationRule1(diff) {
|
|||||||
|
|
||||||
//iterate the input tokens to create the intermediate representation
|
//iterate the input tokens to create the intermediate representation
|
||||||
diff.forEach((currentBlock) => {
|
diff.forEach((currentBlock) => {
|
||||||
|
|
||||||
previousBlockType = currentBlockType
|
previousBlockType = currentBlockType
|
||||||
previousBlockWasMultiline = currentBlockIsMultiline
|
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)
|
currentBlockIsMultiline = isMultilineDiffBlock(currentBlock)
|
||||||
|
|
||||||
//transform rule 1 applys when:
|
//transform rule 1 applys when:
|
||||||
// the previous block was a del and had multiple lines
|
// the previous block was a del and had multiple lines
|
||||||
// the current block is an ins
|
// 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
|
//split the first line from the current block
|
||||||
let currentBlockSplit = splitMultilineDiffBlock(currentBlock)
|
let currentBlockSplit = splitMultilineDiffBlock(currentBlock)
|
||||||
|
|
||||||
@ -104,13 +115,14 @@ function applyTransformationRule1(diff) {
|
|||||||
let previousBlock = transformedDiff.pop()
|
let previousBlock = transformedDiff.pop()
|
||||||
|
|
||||||
//split the first line from the previous block
|
//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
|
//now add the blocks back, interleaving del and ins blocks
|
||||||
for (let i=0; i<Math.max(previousBlockSplit.length, currentBlockSplit.length); i++) {
|
for (let i=0; i<Math.max(previousBlockSplit.length, currentBlockSplit.length); i++) {
|
||||||
if (i<previousBlockSplit.length)
|
if (i<previousBlockSplit.length)
|
||||||
transformedDiff.push(previousBlockSplit[i])
|
transformedDiff.push(previousBlockSplit[i])
|
||||||
|
|
||||||
if (i<currentBlockSplit.length)
|
if (i<currentBlockSplit.length)
|
||||||
transformedDiff.push(currentBlockSplit[i])
|
transformedDiff.push(currentBlockSplit[i])
|
||||||
}
|
}
|
||||||
@ -142,6 +154,7 @@ function applyTransformationRule2(diff) {
|
|||||||
|
|
||||||
|
|
||||||
/// ...
|
/// ...
|
||||||
|
/*
|
||||||
transform.forEach(function(item) {
|
transform.forEach(function(item) {
|
||||||
//newlines are undecorated
|
//newlines are undecorated
|
||||||
if (item.string == '\n') {
|
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
|
//if the diff block begins with a newline, the returned array will begin with an empty diff
|
||||||
function splitMultilineDiffBlock({added, removed, value}) {
|
function splitMultilineDiffBlock({added, removed, value}) {
|
||||||
//find the indices of the diff block that coorespond to newlines
|
//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
|
//create a range from each index
|
||||||
const ranges = splits.reduce(
|
const ranges = splits.reduce(
|
||||||
//the accumulator is a structure with the last index and the list of ranges
|
//the accumulator is a structure with the last index and the list of ranges
|
||||||
//the ranges are a {start, end} structure
|
//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
|
//start with the zero index and an empty array
|
||||||
{last: 0, ranges:[]}
|
{last: 0, ranges:[]}
|
||||||
).ranges
|
).ranges
|
||||||
|
|
||||||
|
|
||||||
//map the ranges into blocks
|
//map the ranges into blocks
|
||||||
const blocks = ranges.map(
|
const blocks = ranges.map(
|
||||||
//each block is the same as the given original block, but with the values split at newlines
|
//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
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
48
test/dubdiffMarkdown.js
Normal file
48
test/dubdiffMarkdown.js
Normal 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
63
test/dubdiffPlaintext.js
Normal 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')
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user