visual testing

This commit is contained in:
2026-04-06 03:17:30 +03:00
parent 53ba44e2f9
commit f04c39cb69
29 changed files with 2575 additions and 76 deletions

View File

@@ -0,0 +1,33 @@
{
"satis": [
{ "ay": "Ocak", "gelir": 15000, "kanal": "Online" },
{ "ay": "Ocak", "gelir": 8000, "kanal": "Magaza" },
{ "ay": "Ocak", "gelir": 3000, "kanal": "Toptan" },
{ "ay": "Subat", "gelir": 18000, "kanal": "Online" },
{ "ay": "Subat", "gelir": 9500, "kanal": "Magaza" },
{ "ay": "Subat", "gelir": 4200, "kanal": "Toptan" },
{ "ay": "Mart", "gelir": 22000, "kanal": "Online" },
{ "ay": "Mart", "gelir": 11000, "kanal": "Magaza" },
{ "ay": "Mart", "gelir": 5100, "kanal": "Toptan" },
{ "ay": "Nisan", "gelir": 19500, "kanal": "Online" },
{ "ay": "Nisan", "gelir": 10200, "kanal": "Magaza" },
{ "ay": "Nisan", "gelir": 4800, "kanal": "Toptan" }
],
"trend": [
{ "hafta": "H1", "ziyaretci": 1200, "kaynak": "Organik" },
{ "hafta": "H1", "ziyaretci": 800, "kaynak": "Reklam" },
{ "hafta": "H2", "ziyaretci": 1500, "kaynak": "Organik" },
{ "hafta": "H2", "ziyaretci": 950, "kaynak": "Reklam" },
{ "hafta": "H3", "ziyaretci": 1350, "kaynak": "Organik" },
{ "hafta": "H3", "ziyaretci": 1100, "kaynak": "Reklam" },
{ "hafta": "H4", "ziyaretci": 1800, "kaynak": "Organik" },
{ "hafta": "H4", "ziyaretci": 1250, "kaynak": "Reklam" }
],
"dagilim": [
{ "kategori": "Elektronik", "oran": 35 },
{ "kategori": "Giyim", "oran": 25 },
{ "kategori": "Gida", "oran": 20 },
{ "kategori": "Kozmetik", "oran": 12 },
{ "kategori": "Diger", "oran": 8 }
]
}

View File

@@ -0,0 +1,131 @@
{
"id": "chart_test",
"name": "Chart Visual Test",
"page": { "width": 210, "height": 297 },
"fonts": ["Noto Sans"],
"root": {
"id": "root",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"direction": "column",
"gap": 8,
"padding": { "top": 15, "right": 15, "bottom": 15, "left": 15 },
"align": "stretch",
"justify": "start",
"style": {},
"children": [
{
"id": "bar_chart",
"type": "chart",
"position": { "type": "flow" },
"size": {
"width": { "type": "fr", "value": 1 },
"height": { "type": "fixed", "value": 80 }
},
"chartType": "bar",
"dataSource": { "path": "satis" },
"categoryField": "ay",
"valueField": "gelir",
"groupField": "kanal",
"groupMode": "grouped",
"title": {
"text": "Aylik Satis Geliri",
"fontSize": 4.0,
"color": "#1a1a1a"
},
"legend": {
"show": true,
"position": "bottom",
"fontSize": 2.8
},
"labels": {
"show": true,
"fontSize": 2.2,
"color": "#333333"
},
"axis": {
"xLabel": "Aylar",
"yLabel": "Gelir (TL)",
"showGrid": true,
"gridColor": "#E5E7EB"
},
"style": {
"colors": ["#4F46E5", "#10B981", "#F59E0B"],
"backgroundColor": "#FFFFFF",
"barGap": 0.2
}
},
{
"id": "line_chart",
"type": "chart",
"position": { "type": "flow" },
"size": {
"width": { "type": "fr", "value": 1 },
"height": { "type": "fixed", "value": 80 }
},
"chartType": "line",
"dataSource": { "path": "trend" },
"categoryField": "hafta",
"valueField": "ziyaretci",
"groupField": "kaynak",
"title": {
"text": "Haftalik Ziyaretci Trendi",
"fontSize": 4.0,
"color": "#1a1a1a"
},
"legend": {
"show": true,
"position": "bottom",
"fontSize": 2.8
},
"labels": {
"show": false
},
"axis": {
"showGrid": true,
"gridColor": "#E5E7EB"
},
"style": {
"colors": ["#EF4444", "#8B5CF6"],
"backgroundColor": "#FFFFFF",
"lineWidth": 0.5,
"showPoints": true
}
},
{
"id": "pie_chart",
"type": "chart",
"position": { "type": "flow" },
"size": {
"width": { "type": "fr", "value": 1 },
"height": { "type": "fixed", "value": 80 }
},
"chartType": "pie",
"dataSource": { "path": "dagilim" },
"categoryField": "kategori",
"valueField": "oran",
"title": {
"text": "Kategori Dagilimi",
"fontSize": 4.0,
"color": "#1a1a1a"
},
"legend": {
"show": true,
"position": "right",
"fontSize": 2.8
},
"labels": {
"show": true,
"fontSize": 2.5,
"color": "#FFFFFF"
},
"style": {
"colors": ["#4F46E5", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6"],
"backgroundColor": "#FFFFFF",
"innerRadius": 0.0
}
}
]
}
}

View File

@@ -0,0 +1,42 @@
{
"company": {
"name": "Teknova Yazilim A.S.",
"city": "Istanbul",
"revenue": 148200
},
"order": {
"code": "ORD-2026-0042"
},
"meta": {
"version": "1.0.0"
},
"products": [
{ "no": 1, "name": "Web Application Development", "qty": 1, "price": 45000, "total": 45000 },
{ "no": 2, "name": "Mobile App Development", "qty": 1, "price": 35000, "total": 35000 },
{ "no": 3, "name": "UI/UX Design Service", "qty": 40, "price": 750, "total": 30000 },
{ "no": 4, "name": "Server Maintenance (Annual)", "qty": 1, "price": 12000, "total": 12000 },
{ "no": 5, "name": "SSL Certificate", "qty": 3, "price": 500, "total": 1500 },
{ "no": 6, "name": "Cloud Hosting Setup", "qty": 1, "price": 8500, "total": 8500 },
{ "no": 7, "name": "Database Optimization", "qty": 2, "price": 6000, "total": 12000 }
],
"distribution": [
{ "category": "Development", "value": 80000 },
{ "category": "Design", "value": 30000 },
{ "category": "Infrastructure", "value": 22000 },
{ "category": "Support", "value": 12000 }
],
"trend": [
{ "month": "Jan", "series": "Revenue", "value": 18000 },
{ "month": "Feb", "series": "Revenue", "value": 22000 },
{ "month": "Mar", "series": "Revenue", "value": 19500 },
{ "month": "Apr", "series": "Revenue", "value": 28000 },
{ "month": "May", "series": "Revenue", "value": 32000 },
{ "month": "Jun", "series": "Revenue", "value": 35000 },
{ "month": "Jan", "series": "Costs", "value": 12000 },
{ "month": "Feb", "series": "Costs", "value": 14000 },
{ "month": "Mar", "series": "Costs", "value": 11000 },
{ "month": "Apr", "series": "Costs", "value": 16000 },
{ "month": "May", "series": "Costs", "value": 18000 },
{ "month": "Jun", "series": "Costs", "value": 20000 }
]
}

View File

@@ -0,0 +1,466 @@
{
"id": "comprehensive_test",
"name": "Comprehensive Element Test",
"page": { "width": 210, "height": 297 },
"fonts": ["Noto Sans", "Noto Sans Mono"],
"root": {
"id": "root",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"direction": "column",
"gap": 4,
"padding": { "top": 12, "right": 12, "bottom": 12, "left": 12 },
"align": "stretch",
"justify": "start",
"style": {},
"children": [
{
"id": "title",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 16, "fontWeight": "bold", "color": "#1a1a1a", "align": "center" },
"content": "COMPREHENSIVE ELEMENT TEST"
},
{
"id": "subtitle",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 9, "color": "#888888", "align": "center" },
"content": "All element types in a single document"
},
{
"id": "line_top",
"type": "line",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"style": { "strokeColor": "#1e293b", "strokeWidth": 1 }
},
{
"id": "section_text",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "column",
"gap": 2,
"padding": { "top": 2, "right": 4, "bottom": 2, "left": 4 },
"align": "stretch",
"justify": "start",
"style": { "backgroundColor": "#f0f4ff", "borderColor": "#c7d2fe", "borderWidth": 0.5, "borderRadius": 2 },
"children": [
{
"id": "sec1_label",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 8, "fontWeight": "bold", "color": "#4338ca" },
"content": "TEXT ELEMENTS"
},
{
"id": "row_texts",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "row",
"gap": 4,
"padding": { "top": 0, "right": 0, "bottom": 0, "left": 0 },
"align": "start",
"justify": "start",
"style": {},
"children": [
{
"id": "bound_text",
"type": "text",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"style": { "fontSize": 10, "color": "#333333" },
"content": "Company: ",
"binding": { "path": "company.name" }
},
{
"id": "bound_text2",
"type": "text",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"style": { "fontSize": 10, "color": "#333333" },
"content": "City: ",
"binding": { "path": "company.city" }
}
]
},
{
"id": "rich_text_el",
"type": "rich_text",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"style": { "fontSize": 9, "color": "#333333" },
"content": [
{ "text": "Rich text: ", "style": { "fontSize": 9, "fontWeight": "bold", "color": "#1e293b" } },
{ "text": "normal ", "style": { "fontSize": 9, "color": "#555555" } },
{ "text": "bold ", "style": { "fontSize": 9, "fontWeight": "bold", "color": "#dc2626" } },
{ "text": "large ", "style": { "fontSize": 12, "color": "#059669" } },
{ "text": "mono", "style": { "fontSize": 9, "fontFamily": "Noto Sans Mono", "color": "#7c3aed" } }
]
},
{
"id": "calc_text",
"type": "calculated_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 9, "color": "#333333" },
"expression": "company.revenue * 0.20",
"format": "currency"
},
{
"id": "row_date_page",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "row",
"gap": 8,
"padding": { "top": 0, "right": 0, "bottom": 0, "left": 0 },
"align": "center",
"justify": "start",
"style": {},
"children": [
{
"id": "date_el",
"type": "current_date",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 9, "color": "#666666" },
"format": "DD.MM.YYYY"
},
{
"id": "page_num",
"type": "page_number",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 9, "color": "#666666" },
"format": "Page {current}/{total}"
}
]
}
]
},
{
"id": "line_thin",
"type": "line",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"style": { "strokeColor": "#e2e8f0", "strokeWidth": 0.3 }
},
{
"id": "section_shapes",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "column",
"gap": 2,
"padding": { "top": 2, "right": 4, "bottom": 2, "left": 4 },
"align": "stretch",
"justify": "start",
"style": { "backgroundColor": "#fef3c7", "borderColor": "#fbbf24", "borderWidth": 0.5, "borderRadius": 2 },
"children": [
{
"id": "sec2_label",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 8, "fontWeight": "bold", "color": "#92400e" },
"content": "SHAPES, CHECKBOXES & BARCODES"
},
{
"id": "row_shapes",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "row",
"gap": 4,
"padding": { "top": 0, "right": 0, "bottom": 0, "left": 0 },
"align": "center",
"justify": "start",
"style": {},
"children": [
{
"id": "shape_rect",
"type": "shape",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 15 }, "height": { "type": "fixed", "value": 10 } },
"shapeType": "rectangle",
"style": { "backgroundColor": "#3b82f6", "borderColor": "#1d4ed8", "borderWidth": 0.5 }
},
{
"id": "shape_ellipse",
"type": "shape",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 15 }, "height": { "type": "fixed", "value": 10 } },
"shapeType": "ellipse",
"style": { "backgroundColor": "#ef4444", "borderColor": "#b91c1c", "borderWidth": 0.5 }
},
{
"id": "shape_rounded",
"type": "shape",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 15 }, "height": { "type": "fixed", "value": 10 } },
"shapeType": "rounded_rectangle",
"style": { "backgroundColor": "#10b981", "borderColor": "#047857", "borderWidth": 0.5, "borderRadius": 3 }
},
{
"id": "cb_checked",
"type": "checkbox",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 5 }, "height": { "type": "fixed", "value": 5 } },
"checked": true,
"style": { "size": 5, "checkColor": "#059669", "borderColor": "#333333", "borderWidth": 0.3 }
},
{
"id": "cb_label1",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 8, "color": "#333333" },
"content": "Checked"
},
{
"id": "cb_unchecked",
"type": "checkbox",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 5 }, "height": { "type": "fixed", "value": 5 } },
"checked": false,
"style": { "size": 5, "checkColor": "#000000", "borderColor": "#333333", "borderWidth": 0.3 }
},
{
"id": "cb_label2",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 8, "color": "#333333" },
"content": "Unchecked"
}
]
},
{
"id": "row_barcodes",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "row",
"gap": 6,
"padding": { "top": 2, "right": 0, "bottom": 0, "left": 0 },
"align": "start",
"justify": "start",
"style": {},
"children": [
{
"id": "barcode_qr",
"type": "barcode",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 20 }, "height": { "type": "fixed", "value": 20 } },
"format": "qr",
"value": "https://dreport.dev",
"style": {}
},
{
"id": "barcode_128",
"type": "barcode",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 40 }, "height": { "type": "fixed", "value": 15 } },
"format": "code128",
"binding": { "path": "order.code" },
"style": { "includeText": true }
},
{
"id": "barcode_ean",
"type": "barcode",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 35 }, "height": { "type": "fixed", "value": 15 } },
"format": "ean13",
"value": "5901234123457",
"style": { "includeText": true }
}
]
}
]
},
{
"id": "section_table",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "column",
"gap": 2,
"padding": { "top": 2, "right": 0, "bottom": 0, "left": 0 },
"align": "stretch",
"justify": "start",
"style": {},
"children": [
{
"id": "sec3_label",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 8, "fontWeight": "bold", "color": "#0f766e" },
"content": "REPEATING TABLE"
},
{
"id": "products_table",
"type": "repeating_table",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"dataSource": { "path": "products" },
"columns": [
{ "id": "col_no", "field": "no", "title": "#", "width": { "type": "fixed", "value": 8 }, "align": "center" },
{ "id": "col_name", "field": "name", "title": "Product", "width": { "type": "fr", "value": 1 }, "align": "left" },
{ "id": "col_qty", "field": "qty", "title": "Qty", "width": { "type": "fixed", "value": 15 }, "align": "right" },
{ "id": "col_price", "field": "price", "title": "Price", "width": { "type": "fixed", "value": 25 }, "align": "right", "format": "currency" },
{ "id": "col_total", "field": "total", "title": "Total", "width": { "type": "fixed", "value": 25 }, "align": "right", "format": "currency" }
],
"style": {
"fontSize": 8,
"headerFontSize": 8,
"headerBg": "#0f766e",
"headerColor": "#ffffff",
"zebraOdd": "#ffffff",
"zebraEven": "#f0fdfa",
"borderColor": "#99f6e4",
"borderWidth": 0.3
}
}
]
},
{
"id": "section_charts",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "column",
"gap": 2,
"padding": { "top": 2, "right": 0, "bottom": 0, "left": 0 },
"align": "stretch",
"justify": "start",
"style": {},
"children": [
{
"id": "sec4_label",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 8, "fontWeight": "bold", "color": "#9333ea" },
"content": "CHARTS"
},
{
"id": "charts_row",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "row",
"gap": 4,
"padding": { "top": 0, "right": 0, "bottom": 0, "left": 0 },
"align": "start",
"justify": "start",
"style": {},
"children": [
{
"id": "chart_bar",
"type": "chart",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "fixed", "value": 45 } },
"chartType": "bar",
"dataSource": { "path": "products" },
"categoryField": "name",
"valueField": "total",
"title": { "text": "Revenue by Product", "fontSize": 3, "color": "#1e293b" },
"legend": { "show": false },
"labels": { "show": true, "fontSize": 2, "color": "#333" },
"axis": { "showGrid": true },
"style": { "colors": ["#6366f1", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6"] }
},
{
"id": "chart_pie",
"type": "chart",
"position": { "type": "flow" },
"size": { "width": { "type": "fixed", "value": 55 }, "height": { "type": "fixed", "value": 45 } },
"chartType": "pie",
"dataSource": { "path": "distribution" },
"categoryField": "category",
"valueField": "value",
"title": { "text": "Distribution", "fontSize": 3, "color": "#1e293b" },
"legend": { "show": true, "position": "bottom", "fontSize": 2 },
"labels": { "show": true, "fontSize": 2, "color": "#333" },
"style": { "colors": ["#3b82f6", "#ef4444", "#10b981", "#f59e0b"], "innerRadius": 0.4 }
}
]
},
{
"id": "chart_line",
"type": "chart",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "fixed", "value": 40 } },
"chartType": "line",
"dataSource": { "path": "trend" },
"categoryField": "month",
"valueField": "value",
"groupField": "series",
"title": { "text": "Monthly Trend", "fontSize": 3, "color": "#1e293b" },
"legend": { "show": true, "position": "top", "fontSize": 2 },
"labels": { "show": false },
"axis": { "showGrid": true },
"style": { "colors": ["#6366f1", "#ef4444"], "lineWidth": 1.5, "showPoints": true }
}
]
},
{
"id": "line_bottom",
"type": "line",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"style": { "strokeColor": "#1e293b", "strokeWidth": 0.5 }
},
{
"id": "footer_row",
"type": "container",
"position": { "type": "flow" },
"size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" } },
"direction": "row",
"gap": 0,
"padding": { "top": 0, "right": 0, "bottom": 0, "left": 0 },
"align": "center",
"justify": "space-between",
"style": {},
"children": [
{
"id": "footer_left",
"type": "static_text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 7, "color": "#94a3b8" },
"content": "Generated by dreport visual test suite"
},
{
"id": "footer_right",
"type": "text",
"position": { "type": "flow" },
"size": { "width": { "type": "auto" }, "height": { "type": "auto" } },
"style": { "fontSize": 7, "color": "#94a3b8", "align": "right" },
"content": "Version: ",
"binding": { "path": "meta.version" }
}
]
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

File diff suppressed because one or more lines are too long

View File

@@ -13,7 +13,7 @@ mod visual {
use std::process::Command;
use dreport_core::models::Template;
use dreport_layout::{compute_layout, FontData};
use dreport_layout::{compute_layout, FontData, ResolvedContent};
use dreport_layout::pdf_render::render_pdf;
fn fixtures_dir() -> std::path::PathBuf {
@@ -156,17 +156,15 @@ mod visual {
}
}
#[test]
fn test_visual_snapshot_basic() {
let pdf_bytes =
generate_test_pdf("visual_test_template.json", "visual_test_data.json");
fn run_visual_test(template_file: &str, data_file: &str, test_name: &str) {
let pdf_bytes = generate_test_pdf(template_file, data_file);
assert!(!pdf_bytes.is_empty(), "PDF should not be empty");
let snap_dir = snapshots_dir();
fs::create_dir_all(&snap_dir).unwrap();
let actual_png = snap_dir.join("visual_test_actual.png");
let reference_png = snap_dir.join("visual_test_reference.png");
let actual_png = snap_dir.join(format!("{}_actual.png", test_name));
let reference_png = snap_dir.join(format!("{}_reference.png", test_name));
if !pdf_to_png(&pdf_bytes, &actual_png) {
eprintln!("Skipping visual comparison - pdftoppm not available");
@@ -188,7 +186,8 @@ mod visual {
match compare_images(&actual_png, &reference_png, 0.01) {
Ok(diff) => {
println!(
"Visual test passed: {:.4}% pixels differ",
"Visual test [{}] passed: {:.4}% pixels differ",
test_name,
diff * 100.0
);
let _ = fs::remove_file(&actual_png);
@@ -196,10 +195,99 @@ mod visual {
Err(err) => {
// Keep actual for debugging
panic!(
"Visual regression detected: {}. Actual saved at {:?}",
err, actual_png
"Visual regression [{}]: {}. Actual saved at {:?}",
test_name, err, actual_png
);
}
}
}
/// SVG'yi standalone HTML'e sar — chart'ın HTML render'ını görmek icin
fn generate_chart_svg_html(template_file: &str, data_file: &str, output_path: &Path) {
let template_json = fs::read_to_string(fixtures_dir().join(template_file)).unwrap();
let data_json = fs::read_to_string(fixtures_dir().join(data_file)).unwrap();
let template: Template = serde_json::from_str(&template_json).unwrap();
let data: serde_json::Value = serde_json::from_str(&data_json).unwrap();
let fonts = load_test_fonts();
let layout = compute_layout(&template, &data, &fonts);
let mut html = String::from("<!DOCTYPE html><html><head><style>body{margin:20px;font-family:sans-serif;background:#f5f5f5}.chart-box{margin:10px 0;background:white;box-shadow:0 1px 3px rgba(0,0,0,.1)}</style></head><body><h2>Chart SVG Preview (HTML render)</h2>");
for page in &layout.pages {
for el in &page.elements {
if let Some(ResolvedContent::Chart { svg, .. }) = &el.content {
html.push_str(&format!(
"<div class='chart-box' style='width:{}mm;height:{}mm'>{}</div>",
el.width_mm, el.height_mm, svg
));
}
}
}
html.push_str("</body></html>");
fs::write(output_path, html).unwrap();
}
/// Cross-renderer reference PNG output directory
fn cross_renderer_dir() -> std::path::PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("frontend/tests/visual/cross-renderer-refs")
}
/// Generates PDF→PNG references for cross-renderer comparison with HTML render.
/// Run explicitly: cargo test -p dreport-layout --test visual_test -- generate_cross_renderer --ignored
#[test]
#[ignore]
fn generate_cross_renderer_refs() {
let fixtures = [
("visual_test_template.json", "visual_test_data.json", "visual_test"),
("chart_test_template.json", "chart_test_data.json", "chart_test"),
("comprehensive_test_template.json", "comprehensive_test_data.json", "comprehensive_test"),
];
let out_dir = cross_renderer_dir();
fs::create_dir_all(&out_dir).unwrap();
for (template_file, data_file, name) in &fixtures {
let pdf_bytes = generate_test_pdf(template_file, data_file);
assert!(!pdf_bytes.is_empty(), "PDF should not be empty for {}", name);
let png_path = out_dir.join(format!("{}.png", name));
if !pdf_to_png(&pdf_bytes, &png_path) {
panic!("pdftoppm failed for {} — install poppler-utils", name);
}
println!("Cross-renderer reference: {:?}", png_path);
}
}
#[test]
fn test_visual_snapshot_basic() {
run_visual_test("visual_test_template.json", "visual_test_data.json", "visual_test");
}
#[test]
fn test_visual_snapshot_charts() {
let pdf_bytes = generate_test_pdf("chart_test_template.json", "chart_test_data.json");
assert!(!pdf_bytes.is_empty(), "Chart PDF should not be empty");
let snap_dir = snapshots_dir();
fs::create_dir_all(&snap_dir).unwrap();
// PDF ciktisini kaydet (inceleme icin)
let pdf_path = snap_dir.join("chart_test.pdf");
fs::write(&pdf_path, &pdf_bytes).unwrap();
println!("Chart PDF saved to {:?}", pdf_path);
// SVG HTML ciktisini kaydet (karsilastirma icin)
let html_path = snap_dir.join("chart_test_svg.html");
generate_chart_svg_html("chart_test_template.json", "chart_test_data.json", &html_path);
println!("Chart SVG HTML saved to {:?}", html_path);
// Visual regression test
run_visual_test("chart_test_template.json", "chart_test_data.json", "chart_test");
}
}