@createiq/htmldiff 1.2.0-beta.2 → 1.2.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/ThreeWayTable.ts
CHANGED
|
@@ -303,7 +303,21 @@ function preprocessByContent(
|
|
|
303
303
|
return { modifiedGenesis, modifiedCp, modifiedMe, placeholderToDiff }
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
|
|
306
|
+
// Positional pairing is the strict-default for three-way table merge:
|
|
307
|
+
// when all three inputs have the same number of tables in the same
|
|
308
|
+
// order, we pair them by index and let `diffTableThreeWay` handle
|
|
309
|
+
// per-table cell/row level differences. The similarity guard below
|
|
310
|
+
// only kicks in to *reject* positional alignment when a pair is
|
|
311
|
+
// SO dissimilar that it's near-certainly a table reorder/rename
|
|
312
|
+
// where content-LCS pairing would be materially better. The
|
|
313
|
+
// threshold is intentionally low — the 2-way path has no such guard
|
|
314
|
+
// and pairs purely by index (its `diffTable` falls back through
|
|
315
|
+
// same-dimension → equal-row-count → row-LCS → whole-table on its
|
|
316
|
+
// own), so the three-way path was stricter than its sibling and
|
|
317
|
+
// silently dropped to whole-table del+ins for legitimate edits
|
|
318
|
+
// like "rename one column and tweak its values". Aligning the
|
|
319
|
+
// threshold here keeps the two-way and three-way paths in step.
|
|
320
|
+
const POSITIONAL_PAIR_SIMILARITY_THRESHOLD = 0.15
|
|
307
321
|
|
|
308
322
|
function positionallyAligned(
|
|
309
323
|
genesis: string,
|
|
@@ -298,4 +298,30 @@ describe('HtmlDiff.executeThreeWay (tables, genesis-spine)', () => {
|
|
|
298
298
|
expect(HtmlDiff.executeThreeWay('<p>a</p>', '<p>a</p>', '<p>a</p>')).toBe('<p>a</p>')
|
|
299
299
|
})
|
|
300
300
|
})
|
|
301
|
+
|
|
302
|
+
describe('positional pairing under moderate dissimilarity', () => {
|
|
303
|
+
it('column rename + value rewrite still routes through cell-level diff (not whole-table del+ins)', () => {
|
|
304
|
+
// Real-world regression: cp renamed a column ("Form/Document/Certificate"
|
|
305
|
+
// → "Extra column") and replaced the values in that column with short
|
|
306
|
+
// tokens. Word-level Jaccard between the genesis table and cp's edited
|
|
307
|
+
// table drops to ~0.38 — under the 0.5 threshold the three-way path
|
|
308
|
+
// used to take, which kicked the diff into multi-table content-LCS
|
|
309
|
+
// and produced whole-table del+ins (the cp's CP-bubble showed the
|
|
310
|
+
// entire old table struck through and the entire new table inserted).
|
|
311
|
+
// 2-way had no such guard and produced a cell-level diff for the same
|
|
312
|
+
// inputs; lowering the 3-way threshold brings the two paths in step.
|
|
313
|
+
const genesis =
|
|
314
|
+
'<table><tr><td>A</td><td>Form/Document/Certificate</td><td>Date</td></tr><tr><td>Party A</td><td>IRS W-8</td><td>On execution</td></tr></table>'
|
|
315
|
+
const cp =
|
|
316
|
+
'<table><tr><td>A</td><td>Extra column</td><td>Date</td></tr><tr><td>Party A</td><td>Yes</td><td>On execution</td></tr></table>'
|
|
317
|
+
const me = genesis
|
|
318
|
+
const out = HtmlDiff.executeThreeWay(genesis, cp, me)
|
|
319
|
+
// Expect cell-level cp attribution INSIDE the table cells, NOT a
|
|
320
|
+
// whole-table del+ins wrapping the entire <table>.
|
|
321
|
+
expect(out).not.toMatch(/<del[^>]*><table/)
|
|
322
|
+
expect(out).toMatch(/data-author='cp'/)
|
|
323
|
+
expect(out).toContain('Extra column')
|
|
324
|
+
expect(out).toContain('Form/Document/Certificate')
|
|
325
|
+
})
|
|
326
|
+
})
|
|
301
327
|
})
|