@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@createiq/htmldiff",
3
- "version": "1.2.0-beta.2",
3
+ "version": "1.2.0-beta.3",
4
4
  "description": "TypeScript port of htmldiff.net",
5
5
  "type": "module",
6
6
  "author": "Mathew Mannion <mathew.mannion@linklaters.com>",
@@ -303,7 +303,21 @@ function preprocessByContent(
303
303
  return { modifiedGenesis, modifiedCp, modifiedMe, placeholderToDiff }
304
304
  }
305
305
 
306
- const POSITIONAL_PAIR_SIMILARITY_THRESHOLD = 0.5
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
  })