test+feat(stammbaum): merge sibling blocks across same-rank spouse edge (#361)
AC2 — intra-family marriage. When two parented persons at the same imported generation are spouses but live in separate sibling blocks (each under their own parent), the block-packer used to leave them split, drawing a long spouse line that crossed through any intervening siblings. The new step 3.5 detects that case, moves the focal members to the join boundary (A's spouse rightmost in A's block, B's spouse leftmost in B's), and concatenates B's members onto A's; the combined block centres on the average of the two parents' midpoints. Latent against today's data (no intra-family marriage in the canonical fixture); covered by a synthetic two-family scenario in buildLayout.test.ts. Packer growth stays comfortably under Markus's 80-LoC extraction threshold, so packBlocks.ts is not yet warranted. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildLayout, NODE_H, ROW_GAP } from './buildLayout';
|
||||
import { buildLayout, NODE_W, NODE_H, COL_GAP, ROW_GAP } from './buildLayout';
|
||||
import canonicalFixture from '../__fixtures__/stammbaum.json';
|
||||
import type { components } from '$lib/generated/api';
|
||||
|
||||
@@ -234,6 +234,40 @@ describe('buildLayout — multi-spouse ordering (#361)', () => {
|
||||
expect(x1925).toBeLessThan(xNull);
|
||||
});
|
||||
|
||||
it('intra_family_marriage_places_both_spouses_adjacent_across_sibling_blocks', () => {
|
||||
// AC2 (#361). Two parented persons at the same imported generation,
|
||||
// each in a separate sibling block under their own parent, marry each
|
||||
// other. Before the fix the block-packer left them split, drawing a
|
||||
// long spouse line across an intervening sibling. After the fix the
|
||||
// two blocks merge with the spouses sitting on the join boundary.
|
||||
const A1 = '00000000-0000-0000-0000-0000000000d1';
|
||||
const B1 = '00000000-0000-0000-0000-0000000000d2';
|
||||
const A2 = '00000000-0000-0000-0000-0000000000d3';
|
||||
const A3 = '00000000-0000-0000-0000-0000000000d4';
|
||||
const B2 = '00000000-0000-0000-0000-0000000000d5';
|
||||
|
||||
const layout = buildLayout(
|
||||
[
|
||||
node(A1, 'A1', 0),
|
||||
node(B1, 'B1', 0),
|
||||
node(A2, 'A2', 1),
|
||||
node(A3, 'A3', 1),
|
||||
node(B2, 'B2', 1)
|
||||
],
|
||||
[
|
||||
parentEdge(A1, A2, 'p1'),
|
||||
parentEdge(A1, A3, 'p2'),
|
||||
parentEdge(B1, B2, 'p3'),
|
||||
spouseEdge(A2, B2, 'sp')
|
||||
]
|
||||
);
|
||||
|
||||
const posA2 = layout.positions.get(A2)!;
|
||||
const posB2 = layout.positions.get(B2)!;
|
||||
expect(posA2.y).toBe(posB2.y);
|
||||
expect(Math.abs(posA2.x - posB2.x)).toBe(NODE_W + COL_GAP);
|
||||
});
|
||||
|
||||
it('canonical_fixture_multi_spouse_falls_through_to_displayName_when_no_fromYear', () => {
|
||||
// Real-data assertion: 0 of 28 SPOUSE_OF rows in the canonical fixture
|
||||
// have fromYear populated, so the sort collapses to alphabetical by
|
||||
|
||||
Reference in New Issue
Block a user