import 'package:clide/src/svg/svg_document.dart'; import 'package:clide/src/svg/svg_node.dart'; import 'package:clide/src/svg/svg_path.dart'; import 'package:clide/src/svg/svg_transform.dart'; import 'package:test/test.dart'; void main() { List kids(String s) => buildSvgDocument(s).root.children; group('buildSvgDocument', () { test('a non-svg root yields the empty document', () { expect(buildSvgDocument('
').root.children, isEmpty); expect(buildSvgDocument('garbage').root.children, isEmpty); }); test('width, height and viewBox', () { final d = buildSvgDocument(''); expect(d.width, 480); expect(d.height, 360); expect(d.viewBox!.width, 48); expect(d.viewBox!.height, 36); }); test('rect geometry with rx inheriting to ry', () { final r = kids('').single as SvgRect; expect([r.x, r.y, r.width, r.height], [1, 2, 3, 4]); expect(r.rx, 5); expect(r.ry, 5); }); test('circle becomes an ellipse with rx == ry', () { final e = kids('').single as SvgEllipse; expect([e.cx, e.cy, e.rx, e.ry], [5, 6, 7, 7]); }); test('polygon is closed, polyline is not', () { final poly = kids('').single as SvgPolyline; expect(poly.points, [0, 0, 10, 0, 10, 10]); expect(poly.closed, isTrue); final line = kids('').single as SvgPolyline; expect(line.closed, isFalse); }); test('path data is parsed into segments', () { final p = kids('').single as SvgPath; expect(p.segments, [ const SvgPathSeg(SvgPathOp.moveTo, [0, 0]), const SvgPathSeg(SvgPathOp.lineTo, [10, 10]), ]); }); test('fill and stroke resolve to packed ARGB', () { final r = kids('').single as SvgRect; expect(r.style.fill, 0xFF0D32B2); expect(r.style.stroke, 0xFFFF0000); }); test('inheritable style flows from the parent group', () { final g = kids('').single as SvgGroup; expect((g.children.single as SvgRect).style.fill, 0xFFFF0000); }); test('a child overrides an inherited value', () { final g = kids('').single as SvgGroup; expect((g.children.single as SvgRect).style.fill, 0xFF00FF00); }); test('opacity is not inherited', () { final g = kids('').single as SvgGroup; expect(g.style.opacity, 0.5); expect((g.children.single as SvgRect).style.opacity, 1.0); }); test('transform parses; identity collapses to null', () { final r = kids('').single as SvgRect; expect(r.transform, const Affine(1, 0, 0, 1, 5, 10)); expect((kids('').single as SvgRect).transform, isNull); }); test('text content is collected and whitespace-collapsed', () { final t = kids(' build ').single as SvgText; expect(t.text, 'build'); expect([t.x, t.y], [1, 2]); }); test('image reads the xlink:href fallback', () { final i = kids('').single as SvgImage; expect(i.href, 'a.png'); }); test('lengths tolerate units', () { final t = kids('x').single as SvgText; expect(t.style.fontSize, 12); }); test('end-to-end: a d2-style class resolves through to an ARGB fill', () { final p = kids( '' '', ).single as SvgPath; expect(p.style.fill, 0xFF0D32B2); expect(p.segments.first.op, SvgPathOp.moveTo); }); test('marker defs are collected (not drawn inline) with the path marker-end ref', () { final d = buildSvgDocument( '' '' '', ); // defs/marker are not drawable children — only the path is. expect(d.root.children.map((n) => n.runtimeType.toString()), ['SvgPath']); // …but the marker is collected, with its content style resolved. final m = d.markers['arrow']!; expect(m.refX, 7); expect(m.orientAuto, isTrue); expect(m.strokeScaled, isFalse); // markerUnits=userSpaceOnUse expect((m.children.single as SvgPolyline).style.fill, 0xFF0D32B2); expect((d.root.children.single as SvgPath).markerEnd, 'arrow'); }); test('a rect with data-label/data-description yields one annotation at its bbox', () { final d = buildSvgDocument(''); final a = d.annotations.single; expect([a.x, a.y, a.width, a.height], [10, 20, 30, 40]); expect(a.label, 'Box'); expect(a.description, 'a box'); expect(a.lightbox, isFalse); }); test('data-lightbox on an image yields a lightbox annotation carrying the href', () { final d = buildSvgDocument(''); final a = d.annotations.single; expect(a.lightbox, isTrue); expect(a.href, 'pic.png'); }); test('the annotation bbox respects the element transform', () { final d = buildSvgDocument(''); expect([d.annotations.single.x, d.annotations.single.y], [5, 5]); }); test('the annotation bbox respects an inherited group transform', () { final d = buildSvgDocument(''); final a = d.annotations.single; expect([a.x, a.y, a.width, a.height], [100, 0, 10, 10]); }); test('elements without data-* produce no annotations', () { expect(buildSvgDocument('').annotations, isEmpty); }); test('a group with data-label is skipped — annotations anchor leaf shapes', () { expect(buildSvgDocument('').annotations, isEmpty); }); test('computes a bounding box for every leaf shape type (T-318)', () { final d = buildSvgDocument( '' '' '' '' '' 'hi' '', ); expect(d.annotations.map((a) => a.label), containsAll(['e', 'l', 'p', 'pa', 't'])); // ellipse bbox = [cx-rx, cy-ry, 2rx, 2ry]. final e = d.annotations.firstWhere((a) => a.label == 'e'); expect([e.x, e.y, e.width, e.height], [30, 30, 40, 20]); }); }); group('buildSvgDocument — style attribute vocabulary', () { test('parses every stroke/text style branch', () { // Exercises _cap/_join/_anchor/_baseline/_weight/_dash. final kids = buildSvgDocument( '' '' '' 'a' 'b' '', ).root.children; expect(kids, hasLength(4)); // all parsed; every style branch hit }); test('stroke-dasharray="none" yields no dashes', () { expect(buildSvgDocument('').root.children, hasLength(1)); }); }); }