Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions test/layout_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_tree_graph/flutter_tree_graph.dart';

class TestNode extends TreeNodeData {
final String _id;
final List<String> _parentIds;

TestNode(this._id, [this._parentIds = const []]);

@override
String get id => _id;

@override
List<String> get parentIds => _parentIds;
}

void main() {
group('SimpleLayout', () {
test('positions single root node at origin', () {
final root = TreeNode(TestNode('root'));
const layout = SimpleLayout();

layout.calculateLayout([root]);

expect(root.x, 0.0);
expect(root.y, 0.0);
});

test('positions child below parent', () {
final root = TreeNode(TestNode('root'));
final child = TreeNode(TestNode('child'));

root.children.add(child);
child.parents.add(root);

const layout = SimpleLayout();
layout.calculateLayout(
[root],
nodeWidth: 100,
nodeHeight: 50,
verticalSpacing: 50,
);

// Child should be at same X as parent (since it's the only child)
// and below parent by verticalSpacing
expect(child.x, 0.0);
expect(
child.y,
50.0,
); // 0 + verticalSpacing (assuming y starts at 0 for each level?)
// Actually SimpleLayout accumulates y + vSpacing
// root.y=0, child.y = 0 + 50 = 50.
});

test('positions siblings with spacing', () {
final root = TreeNode(TestNode('root'));
final child1 = TreeNode(TestNode('child1'));
final child2 = TreeNode(TestNode('child2'));

root.children.addAll([child1, child2]);

const layout = SimpleLayout();
layout.calculateLayout([root], nodeWidth: 100, horizontalSpacing: 20);

// Child 1 starts at parent's X (initially 0)
expect(child1.x, 0.0);

// Child 2 starts at Child 1's X + nodeWidth + spacing
// 0 + 100 + 20 = 120
expect(child2.x, 120.0);

// Parent should be centered over children
// Center of children block: (0 + (120 + 100)) / 2 = 110
// Parent center: 110 - (100 / 2) = 60
expect(root.x, 60.0);
});
});
}
70 changes: 70 additions & 0 deletions test/models_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_tree_graph/flutter_tree_graph.dart';

class TestNode extends TreeNodeData {
final String _id;
final List<String> _parentIds;

TestNode(this._id, [this._parentIds = const []]);

@override
String get id => _id;

@override
List<String> get parentIds => _parentIds;
}

void main() {
group('TreeNodeData', () {
test('equality works correctly', () {
final node1 = TestNode('1');
final node2 = TestNode('1');
final node3 = TestNode('3');

expect(node1, equals(node2));
expect(node1, isNot(equals(node3)));
});

test('hashCode is consistent', () {
final node1 = TestNode('1');
final node2 = TestNode('1');

expect(node1.hashCode, equals(node2.hashCode));
});
});

group('TreeNode', () {
test('initialization sets default values', () {
final data = TestNode('1');
final node = TreeNode(data);

expect(node.data, equals(data));
expect(node.parents, isEmpty);
expect(node.children, isEmpty);
expect(node.x, 0.0);
expect(node.y, 0.0);
});

test('structure properties work correctly', () {
final rootData = TestNode('root');
final childData = TestNode('child', ['root']);

final root = TreeNode(rootData);
final child = TreeNode(childData);

// Manually link for unit testing structure properties
// Note: In real usage, TreeBuilder handles this
root.children.add(child);
child.parents.add(root);

expect(root.isRoot, isTrue);
expect(root.isLeaf, isFalse);
expect(root.leftmostChild, equals(child));
expect(root.rightmostChild, equals(child));

expect(child.isRoot, isFalse);
expect(child.isLeaf, isTrue);
expect(child.leftmostChild, isNull);
});
});
}
70 changes: 70 additions & 0 deletions test/partner_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_tree_graph/flutter_tree_graph.dart';
import 'package:flutter_tree_graph/src/utils/tree_builder.dart';

class TestNode extends TreeNodeData {
final String _id;
final List<String> _parentIds;
final String? _partnerId;

TestNode(this._id, {List<String> parentIds = const [], String? partnerId})
: _parentIds = parentIds,
_partnerId = partnerId;

@override
String get id => _id;

@override
List<String> get parentIds => _parentIds;

@override
String? get partnerId => _partnerId;
}

void main() {
group('TreeBuilder Partner Logic', () {
test('Root couple: Only one becomes root', () {
final data = [
TestNode('A', partnerId: 'B'),
TestNode('B', partnerId: 'A'),
];

final builder = TreeBuilder<TestNode>();
final roots = builder.buildTree(data);

expect(roots.length, 1);
final root = roots.first;
expect(root.data.id, anyOf('A', 'B'));
expect(root.partner, isNotNull);
expect(root.partner!.data.id, anyOf('A', 'B'));
expect(root.partner!.data.id, isNot(root.data.id));
});

test('Descendant couple: Partner is not added to roots', () {
final data = [
TestNode('Root'),
TestNode('Child', parentIds: ['Root'], partnerId: 'Spouse'),
TestNode(
'Spouse',
partnerId: 'Child',
), // No parents, but partner has parents
];

final builder = TreeBuilder<TestNode>();
final roots = builder.buildTree(data);

expect(roots.length, 1);
final root = roots.first;
expect(root.data.id, 'Root');

expect(root.children, hasLength(1));
final child = root.children.first;
expect(child.data.id, 'Child');
expect(child.partner, isNotNull);
expect(child.partner!.data.id, 'Spouse');

// Verify 'Spouse' is NOT a root
// (If Spouse were a root, roots.length would be 2)
});
});
}
92 changes: 92 additions & 0 deletions test/walkers_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_tree_graph/flutter_tree_graph.dart';
import 'package:flutter_tree_graph/src/layout/walkers_layout.dart';

class SimpleNode extends TreeNodeData {
final String _id;
SimpleNode(this._id);

@override
String get id => _id;

@override
List<String> get parentIds => [];

@override
// ignore: annotate_overrides
String? get partnerId => null;
}

TreeNode<SimpleNode> node(String id) {
return TreeNode<SimpleNode>(SimpleNode(id));
}

void connect(TreeNode<SimpleNode> parent, List<TreeNode<SimpleNode>> children) {
for (var child in children) {
if (!parent.children.contains(child)) parent.children.add(child);
if (!child.parents.contains(parent)) child.parents.add(parent);
}
}

void wirePartner(TreeNode<SimpleNode> p1, TreeNode<SimpleNode> p2) {
p1.partner = p2;
p2.partner = p1;
}

void main() {
group('WalkersTreeLayout', () {
const layout = WalkersTreeLayout();
const double w = 100;
const double h = 80;
const double hSpace = 50;
const double vSpace = 100;

test('Single root node positioning', () {
final root = node('Root');
layout.calculateLayout([root], nodeWidth: w, horizontalSpacing: hSpace);

expect(root.x, 0);
expect(root.y, 0);
});

test('Parent with one child (centered)', () {
final child = node('Child');
final root = node('Root');
connect(root, [child]);

layout.calculateLayout([root], nodeWidth: w, horizontalSpacing: hSpace);

expect(child.x, 0);
expect(root.x, 0);
expect(child.y, vSpace);
});

test('Parent with two children (spaced)', () {
final c1 = node('C1');
final c2 = node('C2');
final root = node('Root');
connect(root, [c1, c2]);

layout.calculateLayout([root], nodeWidth: w, horizontalSpacing: hSpace);

// c1 starts at 0
expect(c1.x, 0);
// c2 starts at 100 + 50 = 150
expect(c2.x, 150);
// Parent centered at (0 + 150) / 2 = 75
expect(root.x, 75);
});

test('Partner handling (Couple width)', () {
final p1 = node('P1');
final p2 = node('P2');
wirePartner(p1, p2);

final root = p1;
layout.calculateLayout([root], nodeWidth: w, horizontalSpacing: hSpace);

expect(p1.x, 0);
expect(p2.x, 150); // straight to right
});
});
}
66 changes: 66 additions & 0 deletions test/widget_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_tree_graph/flutter_tree_graph.dart';
import 'package:flutter_tree_graph/src/widgets/tree_painter.dart';

class TestNode extends TreeNodeData {
final String _id;
final List<String> _parentIds;
final String label;

TestNode(this._id, this.label, [this._parentIds = const []]);

@override
String get id => _id;

@override
List<String> get parentIds => _parentIds;
}

void main() {
testWidgets('TreeView renders nodes correctly', (WidgetTester tester) async {
final data = [
TestNode('1', 'Root'),
TestNode('2', 'Child', ['1']),
];

await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: TreeView<TestNode>(
data: data,
nodeBuilder: (context, node) {
return Text(node.label);
},
),
),
),
);

// Verify nodes are rendered
expect(find.text('Root'), findsOneWidget);
expect(find.text('Child'), findsOneWidget);

// Verify CustomPaint is present (for lines)
final customPaintFinder = find.byWidgetPredicate(
(widget) => widget is CustomPaint && widget.painter is TreePainter,
);
expect(customPaintFinder, findsOneWidget);
});

testWidgets('TreeView handles empty data', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
body: TreeView<TestNode>(data: [], nodeBuilder: _buildNode),
),
),
);

expect(find.text('No data'), findsOneWidget);
});
}

Widget _buildNode(BuildContext context, TestNode node) {
return Text(node.label);
}