From a50d279cba5647e0e5db1f58539c97f2d8a5c618 Mon Sep 17 00:00:00 2001 From: kubuntugamer <52578831+kubuntugamer@users.noreply.github.com> Date: Sat, 10 Oct 2026 02:35:34 -0500 Subject: [PATCH] test(datamerge): pin facing-page record placement (#198 no longer reproduces) (#258) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(datamerge): pin facing-page record placement (#198) #198 reported that with `facingPages: true` the merged records after the first landed at identical coordinates on one spread, leaving a later page empty. That no longer reproduces: `data.merge` now builds a new document rather than duplicating pages inside the template, and the three records land on absolute pages 0, 1 and 2 with no overlap. Nothing in the engine changed here. These two tests pin the behaviour the issue asked for, both at the one-record-per-page plan and at the short block that tiles, so a future refactor of `tile_records` can't quietly bring the stacking back. Refs #198 🤖 Generated with Codebuff Co-Authored-By: Codebuff * Merge origin/main; the facing-pages merge tests find each frame's page with page_of_item Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Codebuff Co-authored-by: Brandon Thomas Co-authored-by: Claude Opus 5.5 --- crates/engine/src/cmd/datamerge/tests.rs | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/crates/engine/src/cmd/datamerge/tests.rs b/crates/engine/src/cmd/datamerge/tests.rs index 64f2f5d..e4636d1 100644 --- a/crates/engine/src/cmd/datamerge/tests.rs +++ b/crates/engine/src/cmd/datamerge/tests.rs @@ -809,3 +809,66 @@ fn oversized_data_file_is_refused_before_reading() { assert!(msg.contains("larger than"), "{msg}"); assert_eq!(read_path_capped(&path, 4096).unwrap().len(), 2048); } + +/// #198: with facing pages on, one record per page must not stack the copies at identical +/// coordinates on one spread (leaving a later page empty). +#[test] +fn facing_pages_merge_puts_each_record_on_its_own_page() { + let mut s = Session::new(); + exec(&mut s, "file.new", json!({"facingPages": true, "pages": 1, "width": 612, "height": 792})); + select_csv(&mut s, "Title,Clue\nCase 1,Cellar\nCase 2,Attic\nCase 3,Garden\n"); + // A block taller than the page's usable height, so only one record fits per page. + text_frame(&mut s, [72.0, 72.0, 540.0, 700.0], "<> - <<Clue>>", None); + let r = exec(&mut s, "data.merge", json!({})); + assert_eq!(r["records"], 3); + + let doc = &s.documents()[s.active_index().unwrap()].doc; + assert_eq!(doc.page_count(), 3, "one page per record"); + + // Which absolute page each merged frame sits on. + let mut pages_seen = std::collections::BTreeSet::new(); + let mut origins = Vec::new(); + for sp in &doc.spreads { + for it in &sp.items { + if !matches!(it.content, Content::Text(_)) { + continue; + } + let abs = doc.page_of_item(it.id); + let b = it.bounds(); + origins.push((abs, b.x0.round() as i32, b.y0.round() as i32)); + if let Some(a) = abs { + pages_seen.insert(a); + } + } + } + assert_eq!(origins.len(), 3, "one text frame per record"); + assert_eq!(pages_seen, [0usize, 1, 2].into_iter().collect(), "each record on its own page, none stacked and none left empty: {origins:?}"); +} + +/// #198's actual repro shape: a short block that tiles several records down one page. With +/// facing pages on they must still land at distinct positions, not on top of each other. +#[test] +fn facing_pages_merge_tiles_short_blocks_without_stacking() { + let mut s = Session::new(); + exec(&mut s, "file.new", json!({"facingPages": true, "pages": 1, "width": 612, "height": 792})); + select_csv(&mut s, "Title,Clue\nCase 1,Cellar\nCase 2,Attic\nCase 3,Garden\n"); + text_frame(&mut s, [72.0, 72.0, 540.0, 200.0], "<<Title>> - <<Clue>>", None); + let r = exec(&mut s, "data.merge", json!({})); + assert_eq!(r["records"], 3); + + let doc = &s.documents()[s.active_index().unwrap()].doc; + let mut origins = Vec::new(); + for sp in &doc.spreads { + for it in &sp.items { + if matches!(it.content, Content::Text(_)) { + let abs = doc.page_of_item(it.id); + let b = it.bounds(); + origins.push((abs, b.x0.round() as i32, b.y0.round() as i32)); + } + } + } + origins.sort(); + assert_eq!(origins.len(), 3, "one text frame per record"); + let unique: std::collections::BTreeSet<_> = origins.iter().collect(); + assert_eq!(unique.len(), 3, "no two records may share a page and coordinates: {origins:?}"); +}