Approval-based snapshot testing library for Go (mirror)

refactor: change SnapJSON to output raw JSON instead of Go maps

SnapJSON now uses json.MarshalIndent to produce properly formatted raw JSON
strings in snapshots, rather than converting to Go map representations. This
provides cleaner, more readable snapshots that match the actual JSON format
users work with.

Benefits:
- Snapshots now show actual JSON syntax users recognize
- Consistent with standard JSON formatting (2-space indentation)
- Clearer diffs when JSON structure changes
- Better alignment with how JSON is typically represented

ptdewey 8160dbe1 208db5d3

+548 -646
+1
.gitattributes
··· 1 + internal/diff/diff.go linguist-vendored
__snapshots__/test_accept.snap internal/files/__snapshots__/test_accept.snap
+1 -1
__snapshots__/test_complex_j_s_o_n_structure.snap __snapshots__/test_complex_json_structure.snap
··· 1 1 --- 2 2 title: Complex JSON Structure 3 - test_name: TestComplexJSONStructure 3 + test_name: TestComplexJsonStructure 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_deeply_nested_j_s_o_n.snap __snapshots__/test_deeply_nested_json.snap
··· 1 1 --- 2 2 title: Deeply Nested JSON 3 - test_name: TestDeeplyNestedJSON 3 + test_name: TestDeeplyNestedJson 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_go_struct_marshalled_to_j_s_o_n.snap __snapshots__/test_go_struct_marshalled_to_json.snap
··· 1 1 --- 2 2 title: Go Struct Marshalled to JSON 3 - test_name: TestGoStructMarshalledToJSON 3 + test_name: TestGoStructMarshalledToJson 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_j_s_o_n_array_of_objects.snap __snapshots__/test_json_array_of_objects.snap
··· 1 1 --- 2 2 title: JSON Array of Objects 3 - test_name: TestJSONArrayOfObjects 3 + test_name: TestJsonArrayOfObjects 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_j_s_o_n_numbers.snap __snapshots__/test_json_numbers.snap
··· 1 1 --- 2 2 title: JSON Numbers 3 - test_name: TestJSONNumbers 3 + test_name: TestJsonNumbers 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_j_s_o_n_object.snap __snapshots__/test_json_object.snap
··· 1 1 --- 2 2 title: JSON Object 3 - test_name: TestJSONObject 3 + test_name: TestJsonObject 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_j_s_o_n_with_mixed_arrays.snap __snapshots__/test_json_with_mixed_arrays.snap
··· 1 1 --- 2 2 title: JSON with Mixed Arrays 3 - test_name: TestJSONWithMixedArrays 3 + test_name: TestJsonWithMixedArrays 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_j_s_o_n_with_special_characters.snap __snapshots__/test_json_with_special_characters.snap
··· 1 1 --- 2 2 title: JSON with Special Characters 3 - test_name: TestJSONWithSpecialCharacters 3 + test_name: TestJsonWithSpecialCharacters 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_j_s_o_n_with_various_types.snap __snapshots__/test_json_with_various_types.snap
··· 1 1 --- 2 2 title: JSON with Various Types 3 - test_name: TestJSONWithVariousTypes 3 + test_name: TestJsonWithVariousTypes 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
+1 -1
__snapshots__/test_large_j_s_o_n.snap __snapshots__/test_large_json.snap
··· 1 1 --- 2 2 title: Large JSON Structure 3 - test_name: TestLargeJSON 3 + test_name: TestLargeJson 4 4 file_path: 5 5 func_name: 6 6 version: 0.1.0
-30
__snapshots__/test_snap_j_s_o_n_array_of_objects.snap
··· 1 - --- 2 - title: SnapJSON Array of Objects 3 - test_name: TestSnapJSONArrayOfObjects 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - []interface{}{ 9 - map[string]interface{}{ 10 - "id": 1.0, 11 - "likes": 42.0, 12 - "title": "First Post", 13 - "type": "post", 14 - "views": 150.0, 15 - }, 16 - map[string]interface{}{ 17 - "id": 2.0, 18 - "likes": 75.0, 19 - "title": "Second Post", 20 - "type": "post", 21 - "views": 280.0, 22 - }, 23 - map[string]interface{}{ 24 - "id": 3.0, 25 - "likes": 120.0, 26 - "title": "Third Post", 27 - "type": "post", 28 - "views": 450.0, 29 - }, 30 - }
-13
__snapshots__/test_snap_j_s_o_n_basic.snap
··· 1 - --- 2 - title: SnapJSON Basic Object 3 - test_name: TestSnapJSONBasic 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "age": 30.0, 10 - "email": "john@example.com", 11 - "name": "John Doe", 12 - "verified": true, 13 - }
-17
__snapshots__/test_snap_j_s_o_n_compact_format.snap
··· 1 - --- 2 - title: SnapJSON Compact Format 3 - test_name: TestSnapJSONCompactFormat 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "id": 1.0, 10 - "in_stock": true, 11 - "name": "Product", 12 - "price": 99.99, 13 - "tags": []interface{}{ 14 - "electronics", 15 - "gadgets", 16 - }, 17 - }
-43
__snapshots__/test_snap_j_s_o_n_complex_a_p_i.snap
··· 1 - --- 2 - title: SnapJSON Complex API Response 3 - test_name: TestSnapJSONComplexAPI 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "code": 200.0, 10 - "data": map[string]interface{}{ 11 - "pagination": map[string]interface{}{ 12 - "page": 1.0, 13 - "per_page": 10.0, 14 - "total": 3.0, 15 - "total_pages": 1.0, 16 - }, 17 - "users": []interface{}{ 18 - map[string]interface{}{ 19 - "active": true, 20 - "department": "Engineering", 21 - "id": 1.0, 22 - "name": "Alice", 23 - "role": "admin", 24 - }, 25 - map[string]interface{}{ 26 - "active": true, 27 - "department": "Sales", 28 - "id": 2.0, 29 - "name": "Bob", 30 - "role": "user", 31 - }, 32 - map[string]interface{}{ 33 - "active": false, 34 - "department": "Marketing", 35 - "id": 3.0, 36 - "name": "Charlie", 37 - "role": "user", 38 - }, 39 - }, 40 - }, 41 - "status": "success", 42 - "timestamp": "2023-11-18T21:45:30Z", 43 - }
-23
__snapshots__/test_snap_j_s_o_n_empty_structures.snap
··· 1 - --- 2 - title: SnapJSON Empty Structures 3 - test_name: TestSnapJSONEmptyStructures 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "empty_array": []interface{}{ 10 - }, 11 - "empty_object": map[string]interface{}{ 12 - }, 13 - "empty_string": "", 14 - "false_value": false, 15 - "nested": map[string]interface{}{ 16 - "also_empty": map[string]interface{}{ 17 - }, 18 - "empty": []interface{}{ 19 - }, 20 - }, 21 - "null_value": nil, 22 - "zero": 0.0, 23 - }
-105
__snapshots__/test_snap_j_s_o_n_large_nested_structure.snap
··· 1 - --- 2 - title: SnapJSON Large Nested Structure 3 - test_name: TestSnapJSONLargeNestedStructure 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "organization": map[string]interface{}{ 10 - "departments": []interface{}{ 11 - map[string]interface{}{ 12 - "manager": "Alice", 13 - "name": "Engineering", 14 - "teams": []interface{}{ 15 - map[string]interface{}{ 16 - "lead": "John", 17 - "members": []interface{}{ 18 - map[string]interface{}{ 19 - "id": 1.0, 20 - "level": "senior", 21 - "name": "John", 22 - }, 23 - map[string]interface{}{ 24 - "id": 2.0, 25 - "level": "mid", 26 - "name": "Jane", 27 - }, 28 - }, 29 - "name": "Backend", 30 - "projects": []interface{}{ 31 - map[string]interface{}{ 32 - "id": "proj_1", 33 - "name": "API Service", 34 - "status": "active", 35 - }, 36 - map[string]interface{}{ 37 - "id": "proj_2", 38 - "name": "Database Optimization", 39 - "status": "planning", 40 - }, 41 - }, 42 - }, 43 - map[string]interface{}{ 44 - "lead": "Bob", 45 - "members": []interface{}{ 46 - map[string]interface{}{ 47 - "id": 3.0, 48 - "level": "senior", 49 - "name": "Bob", 50 - }, 51 - map[string]interface{}{ 52 - "id": 4.0, 53 - "level": "junior", 54 - "name": "Carol", 55 - }, 56 - }, 57 - "name": "Frontend", 58 - "projects": []interface{}{ 59 - map[string]interface{}{ 60 - "id": "proj_3", 61 - "name": "Web App", 62 - "status": "active", 63 - }, 64 - }, 65 - }, 66 - }, 67 - }, 68 - map[string]interface{}{ 69 - "manager": "Charlie", 70 - "name": "Sales", 71 - "teams": []interface{}{ 72 - map[string]interface{}{ 73 - "lead": "Dave", 74 - "members": []interface{}{ 75 - map[string]interface{}{ 76 - "id": 5.0, 77 - "level": "senior", 78 - "name": "Dave", 79 - }, 80 - map[string]interface{}{ 81 - "id": 6.0, 82 - "level": "mid", 83 - "name": "Eve", 84 - }, 85 - }, 86 - "name": "Enterprise", 87 - "projects": []interface{}{ 88 - }, 89 - }, 90 - }, 91 - }, 92 - }, 93 - "id": "org_123", 94 - "metadata": map[string]interface{}{ 95 - "employees": 150.0, 96 - "founded": "2020", 97 - "locations": []interface{}{ 98 - "USA", 99 - "EU", 100 - "APAC", 101 - }, 102 - }, 103 - "name": "TechCorp", 104 - }, 105 - }
-42
__snapshots__/test_snap_j_s_o_n_mixed_types.snap
··· 1 - --- 2 - title: SnapJSON Mixed Types 3 - test_name: TestSnapJSONMixedTypes 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "complex": []interface{}{ 10 - map[string]interface{}{ 11 - "id": 1.0, 12 - "type": "user", 13 - }, 14 - map[string]interface{}{ 15 - "id": 100.0, 16 - "type": "post", 17 - }, 18 - []interface{}{ 19 - 1.0, 20 - 2.0, 21 - 3.0, 22 - }, 23 - "string", 24 - nil, 25 - }, 26 - "mixed_array": []interface{}{ 27 - "string", 28 - 123.0, 29 - 45.67, 30 - true, 31 - false, 32 - nil, 33 - map[string]interface{}{ 34 - "nested": "object", 35 - }, 36 - []interface{}{ 37 - 1.0, 38 - 2.0, 39 - 3.0, 40 - }, 41 - }, 42 - }
-85
__snapshots__/test_snap_j_s_o_n_real_world_example.snap
··· 1 - --- 2 - title: SnapJSON Real World Example 3 - test_name: TestSnapJSONRealWorldExample 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "data": map[string]interface{}{ 10 - "product": map[string]interface{}{ 11 - "description": "High-quality wireless headphones with noise cancellation", 12 - "id": "prod_12345", 13 - "inventory": map[string]interface{}{ 14 - "available": 425.0, 15 - "damaged": 25.0, 16 - "reserved": 50.0, 17 - "total": 500.0, 18 - }, 19 - "name": "Premium Wireless Headphones", 20 - "price": map[string]interface{}{ 21 - "amount": 199.99, 22 - "currency": "USD", 23 - "discount": 10.0, 24 - "final_price": 179.99, 25 - }, 26 - "ratings": map[string]interface{}{ 27 - "average": 4.5, 28 - "breakdown": map[string]interface{}{ 29 - "1": 20.0, 30 - "2": 30.0, 31 - "3": 100.0, 32 - "4": 350.0, 33 - "5": 750.0, 34 - }, 35 - "count": 1250.0, 36 - }, 37 - "reviews": []interface{}{ 38 - map[string]interface{}{ 39 - "content": "Great sound quality and comfortable to wear.", 40 - "created_at": "2023-11-15T10:30:00Z", 41 - "helpful": 25.0, 42 - "id": "rev_001", 43 - "rating": 5.0, 44 - "title": "Excellent product!", 45 - "user": "john_doe", 46 - }, 47 - map[string]interface{}{ 48 - "content": "Works well, could be cheaper.", 49 - "created_at": "2023-11-10T14:20:00Z", 50 - "helpful": 12.0, 51 - "id": "rev_002", 52 - "rating": 4.0, 53 - "title": "Good but pricey", 54 - "user": "jane_smith", 55 - }, 56 - }, 57 - "sku": "PWH-001", 58 - "specifications": map[string]interface{}{ 59 - "battery_life": "30 hours", 60 - "colors": []interface{}{ 61 - "black", 62 - "white", 63 - "blue", 64 - }, 65 - "warranty_months": 24.0, 66 - "weight": "250g", 67 - }, 68 - }, 69 - "related_products": []interface{}{ 70 - map[string]interface{}{ 71 - "id": "prod_12346", 72 - "name": "Headphone Case", 73 - "price": 29.99, 74 - }, 75 - map[string]interface{}{ 76 - "id": "prod_12347", 77 - "name": "Audio Cable", 78 - "price": 14.99, 79 - }, 80 - }, 81 - }, 82 - "request_id": "req_abc123def456", 83 - "success": true, 84 - "timestamp": "2023-11-18T22:00:00Z", 85 - }
-13
__snapshots__/test_snap_j_s_o_n_simple_array.snap
··· 1 - --- 2 - title: SnapJSON Simple Array 3 - test_name: TestSnapJSONSimpleArray 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - []interface{}{ 9 - "apple", 10 - "banana", 11 - "orange", 12 - "grape", 13 - }
-27
__snapshots__/test_snap_j_s_o_n_with_nested_objects.snap
··· 1 - --- 2 - title: SnapJSON Nested Objects 3 - test_name: TestSnapJSONWithNestedObjects 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "created_at": "2023-06-15T10:30:00Z", 10 - "user": map[string]interface{}{ 11 - "id": 42.0, 12 - "permissions": []interface{}{ 13 - "read", 14 - "write", 15 - "admin", 16 - }, 17 - "profile": map[string]interface{}{ 18 - "avatar": "https://example.com/avatar.jpg", 19 - "settings": map[string]interface{}{ 20 - "language": "en", 21 - "notifications": true, 22 - "theme": "dark", 23 - }, 24 - "username": "jane_smith", 25 - }, 26 - }, 27 - }
-19
__snapshots__/test_snap_j_s_o_n_with_nulls.snap
··· 1 - --- 2 - title: SnapJSON With Nulls 3 - test_name: TestSnapJSONWithNulls 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "category": nil, 10 - "description": nil, 11 - "id": 1.0, 12 - "metadata": map[string]interface{}{ 13 - "created": "2023-01-01", 14 - "deleted": nil, 15 - "updated": nil, 16 - }, 17 - "name": "Item", 18 - "tags": nil, 19 - }
-35
__snapshots__/test_snap_j_s_o_n_with_numbers.snap
··· 1 - --- 2 - title: SnapJSON With Numbers 3 - test_name: TestSnapJSONWithNumbers 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "financial": map[string]interface{}{ 10 - "expenses": 750000.75, 11 - "profit_margin": 0.2499, 12 - "revenue": 1.0000005e+06, 13 - }, 14 - "floats": []interface{}{ 15 - 0.0, 16 - 3.14, 17 - -2.5, 18 - 0.001, 19 - 0.000123, 20 - 5.67e+10.0, 21 - }, 22 - "integers": []interface{}{ 23 - 0.0, 24 - 1.0, 25 - -1.0, 26 - 42.0, 27 - -100.0, 28 - 9.999999e+06.0, 29 - }, 30 - "measurements": map[string]interface{}{ 31 - "distance": 1000.25, 32 - "temperature": -40.5, 33 - "weight": 0.5, 34 - }, 35 - }
-16
__snapshots__/test_snap_j_s_o_n_with_special_characters.snap
··· 1 - --- 2 - title: SnapJSON With Special Characters 3 - test_name: TestSnapJSONWithSpecialCharacters 4 - file_path: 5 - func_name: 6 - version: 0.1.0 7 - --- 8 - map[string]interface{}{ 9 - "escaped": "line1\nline2\ttab\rcarriage", 10 - "html": "<div class=\"container\">Content</div>", 11 - "paths": "C:\\Users\\name\\Documents\\file.txt", 12 - "quotes": "He said \"hello\" and she said 'goodbye'", 13 - "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", 14 - "special": "!@#$%^&*()_+-=[]{}|;:',.<>?/", 15 - "unicode": "Hello 世界 🌍 مرحبا Привет", 16 - }
+30
__snapshots__/test_snap_json_array_of_objects.snap
··· 1 + --- 2 + title: SnapJSON Array of Objects 3 + test_name: TestSnapJsonArrayOfObjects 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + [ 9 + { 10 + "id": 1, 11 + "likes": 42, 12 + "title": "First Post", 13 + "type": "post", 14 + "views": 150 15 + }, 16 + { 17 + "id": 2, 18 + "likes": 75, 19 + "title": "Second Post", 20 + "type": "post", 21 + "views": 280 22 + }, 23 + { 24 + "id": 3, 25 + "likes": 120, 26 + "title": "Third Post", 27 + "type": "post", 28 + "views": 450 29 + } 30 + ]
+13
__snapshots__/test_snap_json_basic.snap
··· 1 + --- 2 + title: SnapJSON Basic Object 3 + test_name: TestSnapJsonBasic 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "age": 30, 10 + "email": "john@example.com", 11 + "name": "John Doe", 12 + "verified": true 13 + }
+17
__snapshots__/test_snap_json_compact_format.snap
··· 1 + --- 2 + title: SnapJSON Compact Format 3 + test_name: TestSnapJsonCompactFormat 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "id": 1, 10 + "in_stock": true, 11 + "name": "Product", 12 + "price": 99.99, 13 + "tags": [ 14 + "electronics", 15 + "gadgets" 16 + ] 17 + }
+43
__snapshots__/test_snap_json_complex_a_p_i.snap
··· 1 + --- 2 + title: SnapJSON Complex API Response 3 + test_name: TestSnapJsonComplexAPI 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "code": 200, 10 + "data": { 11 + "pagination": { 12 + "page": 1, 13 + "per_page": 10, 14 + "total": 3, 15 + "total_pages": 1 16 + }, 17 + "users": [ 18 + { 19 + "active": true, 20 + "department": "Engineering", 21 + "id": 1, 22 + "name": "Alice", 23 + "role": "admin" 24 + }, 25 + { 26 + "active": true, 27 + "department": "Sales", 28 + "id": 2, 29 + "name": "Bob", 30 + "role": "user" 31 + }, 32 + { 33 + "active": false, 34 + "department": "Marketing", 35 + "id": 3, 36 + "name": "Charlie", 37 + "role": "user" 38 + } 39 + ] 40 + }, 41 + "status": "success", 42 + "timestamp": "2023-11-18T21:45:30Z" 43 + }
+19
__snapshots__/test_snap_json_empty_structures.snap
··· 1 + --- 2 + title: SnapJSON Empty Structures 3 + test_name: TestSnapJsonEmptyStructures 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "empty_array": [], 10 + "empty_object": {}, 11 + "empty_string": "", 12 + "false_value": false, 13 + "nested": { 14 + "also_empty": {}, 15 + "empty": [] 16 + }, 17 + "null_value": null, 18 + "zero": 0 19 + }
+104
__snapshots__/test_snap_json_large_nested_structure.snap
··· 1 + --- 2 + title: SnapJSON Large Nested Structure 3 + test_name: TestSnapJsonLargeNestedStructure 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "organization": { 10 + "departments": [ 11 + { 12 + "manager": "Alice", 13 + "name": "Engineering", 14 + "teams": [ 15 + { 16 + "lead": "John", 17 + "members": [ 18 + { 19 + "id": 1, 20 + "level": "senior", 21 + "name": "John" 22 + }, 23 + { 24 + "id": 2, 25 + "level": "mid", 26 + "name": "Jane" 27 + } 28 + ], 29 + "name": "Backend", 30 + "projects": [ 31 + { 32 + "id": "proj_1", 33 + "name": "API Service", 34 + "status": "active" 35 + }, 36 + { 37 + "id": "proj_2", 38 + "name": "Database Optimization", 39 + "status": "planning" 40 + } 41 + ] 42 + }, 43 + { 44 + "lead": "Bob", 45 + "members": [ 46 + { 47 + "id": 3, 48 + "level": "senior", 49 + "name": "Bob" 50 + }, 51 + { 52 + "id": 4, 53 + "level": "junior", 54 + "name": "Carol" 55 + } 56 + ], 57 + "name": "Frontend", 58 + "projects": [ 59 + { 60 + "id": "proj_3", 61 + "name": "Web App", 62 + "status": "active" 63 + } 64 + ] 65 + } 66 + ] 67 + }, 68 + { 69 + "manager": "Charlie", 70 + "name": "Sales", 71 + "teams": [ 72 + { 73 + "lead": "Dave", 74 + "members": [ 75 + { 76 + "id": 5, 77 + "level": "senior", 78 + "name": "Dave" 79 + }, 80 + { 81 + "id": 6, 82 + "level": "mid", 83 + "name": "Eve" 84 + } 85 + ], 86 + "name": "Enterprise", 87 + "projects": [] 88 + } 89 + ] 90 + } 91 + ], 92 + "id": "org_123", 93 + "metadata": { 94 + "employees": 150, 95 + "founded": "2020", 96 + "locations": [ 97 + "USA", 98 + "EU", 99 + "APAC" 100 + ] 101 + }, 102 + "name": "TechCorp" 103 + } 104 + }
+42
__snapshots__/test_snap_json_mixed_types.snap
··· 1 + --- 2 + title: SnapJSON Mixed Types 3 + test_name: TestSnapJsonMixedTypes 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "complex": [ 10 + { 11 + "id": 1, 12 + "type": "user" 13 + }, 14 + { 15 + "id": 100, 16 + "type": "post" 17 + }, 18 + [ 19 + 1, 20 + 2, 21 + 3 22 + ], 23 + "string", 24 + null 25 + ], 26 + "mixed_array": [ 27 + "string", 28 + 123, 29 + 45.67, 30 + true, 31 + false, 32 + null, 33 + { 34 + "nested": "object" 35 + }, 36 + [ 37 + 1, 38 + 2, 39 + 3 40 + ] 41 + ] 42 + }
+85
__snapshots__/test_snap_json_real_world_example.snap
··· 1 + --- 2 + title: SnapJSON Real World Example 3 + test_name: TestSnapJsonRealWorldExample 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "data": { 10 + "product": { 11 + "description": "High-quality wireless headphones with noise cancellation", 12 + "id": "prod_12345", 13 + "inventory": { 14 + "available": 425, 15 + "damaged": 25, 16 + "reserved": 50, 17 + "total": 500 18 + }, 19 + "name": "Premium Wireless Headphones", 20 + "price": { 21 + "amount": 199.99, 22 + "currency": "USD", 23 + "discount": 10, 24 + "final_price": 179.99 25 + }, 26 + "ratings": { 27 + "average": 4.5, 28 + "breakdown": { 29 + "1": 20, 30 + "2": 30, 31 + "3": 100, 32 + "4": 350, 33 + "5": 750 34 + }, 35 + "count": 1250 36 + }, 37 + "reviews": [ 38 + { 39 + "content": "Great sound quality and comfortable to wear.", 40 + "created_at": "2023-11-15T10:30:00Z", 41 + "helpful": 25, 42 + "id": "rev_001", 43 + "rating": 5, 44 + "title": "Excellent product!", 45 + "user": "john_doe" 46 + }, 47 + { 48 + "content": "Works well, could be cheaper.", 49 + "created_at": "2023-11-10T14:20:00Z", 50 + "helpful": 12, 51 + "id": "rev_002", 52 + "rating": 4, 53 + "title": "Good but pricey", 54 + "user": "jane_smith" 55 + } 56 + ], 57 + "sku": "PWH-001", 58 + "specifications": { 59 + "battery_life": "30 hours", 60 + "colors": [ 61 + "black", 62 + "white", 63 + "blue" 64 + ], 65 + "warranty_months": 24, 66 + "weight": "250g" 67 + } 68 + }, 69 + "related_products": [ 70 + { 71 + "id": "prod_12346", 72 + "name": "Headphone Case", 73 + "price": 29.99 74 + }, 75 + { 76 + "id": "prod_12347", 77 + "name": "Audio Cable", 78 + "price": 14.99 79 + } 80 + ] 81 + }, 82 + "request_id": "req_abc123def456", 83 + "success": true, 84 + "timestamp": "2023-11-18T22:00:00Z" 85 + }
+13
__snapshots__/test_snap_json_simple_array.snap
··· 1 + --- 2 + title: SnapJSON Simple Array 3 + test_name: TestSnapJsonSimpleArray 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + [ 9 + "apple", 10 + "banana", 11 + "orange", 12 + "grape" 13 + ]
+27
__snapshots__/test_snap_json_with_nested_objects.snap
··· 1 + --- 2 + title: SnapJSON Nested Objects 3 + test_name: TestSnapJsonWithNestedObjects 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "created_at": "2023-06-15T10:30:00Z", 10 + "user": { 11 + "id": 42, 12 + "permissions": [ 13 + "read", 14 + "write", 15 + "admin" 16 + ], 17 + "profile": { 18 + "avatar": "https://example.com/avatar.jpg", 19 + "settings": { 20 + "language": "en", 21 + "notifications": true, 22 + "theme": "dark" 23 + }, 24 + "username": "jane_smith" 25 + } 26 + } 27 + }
+19
__snapshots__/test_snap_json_with_nulls.snap
··· 1 + --- 2 + title: SnapJSON With Nulls 3 + test_name: TestSnapJsonWithNulls 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "category": null, 10 + "description": null, 11 + "id": 1, 12 + "metadata": { 13 + "created": "2023-01-01", 14 + "deleted": null, 15 + "updated": null 16 + }, 17 + "name": "Item", 18 + "tags": null 19 + }
+35
__snapshots__/test_snap_json_with_numbers.snap
··· 1 + --- 2 + title: SnapJSON With Numbers 3 + test_name: TestSnapJsonWithNumbers 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "financial": { 10 + "expenses": 750000.75, 11 + "profit_margin": 0.2499, 12 + "revenue": 1000000.5 13 + }, 14 + "floats": [ 15 + 0, 16 + 3.14, 17 + -2.5, 18 + 0.001, 19 + 0.000123, 20 + 56700000000 21 + ], 22 + "integers": [ 23 + 0, 24 + 1, 25 + -1, 26 + 42, 27 + -100, 28 + 9999999 29 + ], 30 + "measurements": { 31 + "distance": 1000.25, 32 + "temperature": -40.5, 33 + "weight": 0.5 34 + } 35 + }
+16
__snapshots__/test_snap_json_with_special_characters.snap
··· 1 + --- 2 + title: SnapJSON With Special Characters 3 + test_name: TestSnapJsonWithSpecialCharacters 4 + file_path: 5 + func_name: 6 + version: 0.1.0 7 + --- 8 + { 9 + "escaped": "line1\nline2\ttab\rcarriage", 10 + "html": "\u003cdiv class=\"container\"\u003eContent\u003c/div\u003e", 11 + "paths": "C:\\Users\\name\\Documents\\file.txt", 12 + "quotes": "He said \"hello\" and she said 'goodbye'", 13 + "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", 14 + "special": "!@#$%^\u0026*()_+-=[]{}|;:',.\u003c\u003e?/", 15 + "unicode": "Hello 世界 🌍 مرحبا Привет" 16 + }
+17 -18
api.go
··· 1 1 package freeze 2 2 3 3 import ( 4 - "github.com/ptdewey/freeze/internal/api" 4 + "github.com/ptdewey/freeze/internal/diff" 5 + "github.com/ptdewey/freeze/internal/files" 6 + "github.com/ptdewey/freeze/internal/pretty" 5 7 ) 6 8 7 - type Snapshot = api.Snapshot 9 + type Snapshot = files.Snapshot 8 10 9 - type DiffLine = api.DiffLine 11 + type DiffLine = diff.DiffLine 10 12 11 13 const ( 12 - DiffShared = api.DiffShared 13 - DiffOld = api.DiffOld 14 - DiffNew = api.DiffNew 14 + DiffShared = diff.DiffShared 15 + DiffOld = diff.DiffOld 16 + DiffNew = diff.DiffNew 15 17 ) 16 18 17 19 func Deserialize(raw string) (*Snapshot, error) { 18 - return api.Deserialize(raw) 20 + return files.Deserialize(raw) 19 21 } 20 22 21 23 func SaveSnapshot(snap *Snapshot, state string) error { 22 - return api.SaveSnapshot(snap, state) 24 + return files.SaveSnapshot(snap, state) 23 25 } 24 26 25 27 func ReadSnapshot(testName string, state string) (*Snapshot, error) { 26 - return api.ReadSnapshot(testName, state) 28 + return files.ReadSnapshot(testName, state) 27 29 } 28 30 29 31 func SnapshotFileName(testName string) string { 30 - return api.SnapshotFileName(testName) 32 + return files.SnapshotFileName(testName) 31 33 } 32 34 33 35 func Histogram(old, new string) []DiffLine { 34 - return api.Histogram(old, new) 36 + return diff.Histogram(old, new) 35 37 } 36 38 37 39 func NewSnapshotBox(snap *Snapshot) string { 38 - return api.NewSnapshotBox(snap) 40 + return pretty.NewSnapshotBox(snap) 39 41 } 40 42 41 - func NewSnapshotBoxFunc(snap *Snapshot) string { 42 - return api.NewSnapshotBoxFunc(snap) 43 - } 44 - 45 - func DiffSnapshotBox(old, new *Snapshot) string { 46 - return api.DiffSnapshotBox(old, new) 43 + func DiffSnapshotBox(oldSnap, newSnap *Snapshot) string { 44 + diffLines := convertDiffLines(diff.Histogram(oldSnap.Content, newSnap.Content)) 45 + return pretty.DiffSnapshotBox(oldSnap, newSnap, diffLines) 47 46 }
+1 -5
cmd/tui/main.go
··· 271 271 b.WriteString(pretty.DiffSnapshotBox(m.accepted, m.newSnap, m.diffLines)) 272 272 } else { 273 273 if m.newSnap != nil { 274 - if m.newSnap.FuncName != "" { 275 - b.WriteString(pretty.NewSnapshotBoxFunc(m.newSnap)) 276 - } else { 277 - b.WriteString(pretty.NewSnapshotBox(m.newSnap)) 278 - } 274 + b.WriteString(pretty.NewSnapshotBox(m.newSnap)) 279 275 } 280 276 } 281 277
+11 -6
freeze.go
··· 24 24 snap(t, title, content) 25 25 } 26 26 27 - // SnapJSON takes a JSON string, unmarshals it, and then marshals it back with 28 - // pretty-printing before snapshotting. This ensures consistent formatting and 29 - // validates the JSON structure. 27 + // SnapJSON takes a JSON string, validates it, and pretty-prints it with 28 + // consistent formatting before snapshotting. This preserves the raw JSON 29 + // format while ensuring valid JSON structure. 30 30 func SnapJSON(t testingT, title string, jsonStr string) { 31 31 t.Helper() 32 32 ··· 36 36 return 37 37 } 38 38 39 - // Format the JSON data using the same mechanism as Snap 40 - content := formatValue(data) 41 - snap(t, title, content) 39 + // Pretty-print the JSON with consistent indentation 40 + prettyJSON, err := json.MarshalIndent(data, "", " ") 41 + if err != nil { 42 + t.Error("failed to marshal JSON:", err) 43 + return 44 + } 45 + 46 + snap(t, title, string(prettyJSON)) 42 47 } 43 48 44 49 func Snap(t testingT, title string, values ...any) {
+25 -25
freeze_test.go
··· 9 9 "time" 10 10 11 11 "github.com/ptdewey/freeze" 12 - "github.com/ptdewey/freeze/internal/api" 12 + "github.com/ptdewey/freeze/internal/files" 13 13 ) 14 14 15 15 func TestSnapString(t *testing.T) { ··· 80 80 Content: "file test content", 81 81 } 82 82 83 - if err := api.SaveSnapshot(snap, "test"); err != nil { 83 + if err := files.SaveSnapshot(snap, "test"); err != nil { 84 84 t.Fatalf("failed to save snapshot: %v", err) 85 85 } 86 86 ··· 534 534 // ============================================================================ 535 535 536 536 // TestJSONObject tests snapshot with JSON objects 537 - func TestJSONObject(t *testing.T) { 537 + func TestJsonObject(t *testing.T) { 538 538 jsonStr := `{ 539 539 "user": { 540 540 "id": 1, ··· 562 562 } 563 563 564 564 // TestComplexJSONStructure tests complex nested JSON structures 565 - func TestComplexJSONStructure(t *testing.T) { 565 + func TestComplexJsonStructure(t *testing.T) { 566 566 jsonStr := `{ 567 567 "api": { 568 568 "version": "2.0", ··· 631 631 } 632 632 633 633 // TestJSONArrayOfObjects tests JSON arrays with multiple object types 634 - func TestJSONArrayOfObjects(t *testing.T) { 634 + func TestJsonArrayOfObjects(t *testing.T) { 635 635 jsonStr := `[ 636 636 { 637 637 "type": "user", ··· 670 670 } 671 671 672 672 // TestJSONWithVariousTypes tests JSON with various data types 673 - func TestJSONWithVariousTypes(t *testing.T) { 673 + func TestJsonWithVariousTypes(t *testing.T) { 674 674 jsonStr := `{ 675 675 "string": "hello world", 676 676 "integer": 42, ··· 697 697 } 698 698 699 699 // TestJSONNumbers tests JSON with various number formats 700 - func TestJSONNumbers(t *testing.T) { 700 + func TestJsonNumbers(t *testing.T) { 701 701 jsonStr := `{ 702 702 "integers": { 703 703 "zero": 0, ··· 726 726 } 727 727 728 728 // TestJSONWithSpecialCharacters tests JSON with special characters and unicode 729 - func TestJSONWithSpecialCharacters(t *testing.T) { 729 + func TestJsonWithSpecialCharacters(t *testing.T) { 730 730 jsonStr := `{ 731 731 "english": "Hello, World!", 732 732 "unicode": "こんにちは 世界 🌍", ··· 747 747 } 748 748 749 749 // TestGoStructMarshalledToJSON tests Go struct marshalled to JSON 750 - func TestGoStructMarshalledToJSON(t *testing.T) { 750 + func TestGoStructMarshalledToJson(t *testing.T) { 751 751 type Address struct { 752 752 Street string `json:"street"` 753 753 City string `json:"city"` ··· 792 792 } 793 793 794 794 // TestDeeplyNestedJSON tests deeply nested JSON structure 795 - func TestDeeplyNestedJSON(t *testing.T) { 795 + func TestDeeplyNestedJson(t *testing.T) { 796 796 type Level4 struct { 797 797 Value string 798 798 } ··· 833 833 } 834 834 835 835 // TestLargeJSON tests larger JSON structure with many fields 836 - func TestLargeJSON(t *testing.T) { 836 + func TestLargeJson(t *testing.T) { 837 837 type Product struct { 838 838 ID int `json:"id"` 839 839 Name string `json:"name"` ··· 910 910 } 911 911 912 912 // TestJSONWithMixedArrays tests JSON with arrays containing different types 913 - func TestJSONWithMixedArrays(t *testing.T) { 913 + func TestJsonWithMixedArrays(t *testing.T) { 914 914 jsonStr := `{ 915 915 "heterogeneous_array": [ 916 916 "string", ··· 946 946 // ============================================================================ 947 947 948 948 // TestSnapJSONBasic tests the SnapJSON function with basic JSON 949 - func TestSnapJSONBasic(t *testing.T) { 949 + func TestSnapJsonBasic(t *testing.T) { 950 950 jsonStr := `{ 951 951 "name": "John Doe", 952 952 "email": "john@example.com", ··· 958 958 } 959 959 960 960 // TestSnapJSONSimpleArray tests SnapJSON with simple arrays 961 - func TestSnapJSONSimpleArray(t *testing.T) { 961 + func TestSnapJsonSimpleArray(t *testing.T) { 962 962 jsonStr := `[ 963 963 "apple", 964 964 "banana", ··· 970 970 } 971 971 972 972 // TestSnapJSONCompactFormat tests SnapJSON with compact (minified) JSON 973 - func TestSnapJSONCompactFormat(t *testing.T) { 973 + func TestSnapJsonCompactFormat(t *testing.T) { 974 974 jsonStr := `{"id":1,"name":"Product","price":99.99,"in_stock":true,"tags":["electronics","gadgets"]}` 975 975 976 976 freeze.SnapJSON(t, "SnapJSON Compact Format", jsonStr) 977 977 } 978 978 979 979 // TestSnapJSONWithNestedObjects tests SnapJSON with nested JSON structures 980 - func TestSnapJSONWithNestedObjects(t *testing.T) { 980 + func TestSnapJsonWithNestedObjects(t *testing.T) { 981 981 jsonStr := `{ 982 982 "user": { 983 983 "id": 42, ··· 999 999 } 1000 1000 1001 1001 // TestSnapJSONComplexAPI tests SnapJSON with complex API response 1002 - func TestSnapJSONComplexAPI(t *testing.T) { 1002 + func TestSnapJsonComplexAPI(t *testing.T) { 1003 1003 jsonStr := `{ 1004 1004 "status": "success", 1005 1005 "code": 200, ··· 1041 1041 } 1042 1042 1043 1043 // TestSnapJSONWithNulls tests SnapJSON handling of null values 1044 - func TestSnapJSONWithNulls(t *testing.T) { 1044 + func TestSnapJsonWithNulls(t *testing.T) { 1045 1045 jsonStr := `{ 1046 1046 "id": 1, 1047 1047 "name": "Item", ··· 1059 1059 } 1060 1060 1061 1061 // TestSnapJSONArrayOfObjects tests SnapJSON with arrays of objects 1062 - func TestSnapJSONArrayOfObjects(t *testing.T) { 1062 + func TestSnapJsonArrayOfObjects(t *testing.T) { 1063 1063 jsonStr := `[ 1064 1064 { 1065 1065 "id": 1, ··· 1088 1088 } 1089 1089 1090 1090 // TestSnapJSONLargeNestedStructure tests SnapJSON with deeply nested JSON 1091 - func TestSnapJSONLargeNestedStructure(t *testing.T) { 1091 + func TestSnapJsonLargeNestedStructure(t *testing.T) { 1092 1092 jsonStr := `{ 1093 1093 "organization": { 1094 1094 "name": "TechCorp", ··· 1151 1151 } 1152 1152 1153 1153 // TestSnapJSONWithNumbers tests SnapJSON with various number formats 1154 - func TestSnapJSONWithNumbers(t *testing.T) { 1154 + func TestSnapJsonWithNumbers(t *testing.T) { 1155 1155 jsonStr := `{ 1156 1156 "integers": [0, 1, -1, 42, -100, 9999999], 1157 1157 "floats": [0.0, 3.14, -2.5, 0.001, 1.23e-4, 5.67e10], ··· 1171 1171 } 1172 1172 1173 1173 // TestSnapJSONWithSpecialCharacters tests SnapJSON with special chars 1174 - func TestSnapJSONWithSpecialCharacters(t *testing.T) { 1174 + func TestSnapJsonWithSpecialCharacters(t *testing.T) { 1175 1175 jsonStr := `{ 1176 1176 "special": "!@#$%^&*()_+-=[]{}|;:',.<>?/", 1177 1177 "escaped": "line1\nline2\ttab\rcarriage", ··· 1186 1186 } 1187 1187 1188 1188 // TestSnapJSONEmptyStructures tests SnapJSON with empty collections 1189 - func TestSnapJSONEmptyStructures(t *testing.T) { 1189 + func TestSnapJsonEmptyStructures(t *testing.T) { 1190 1190 jsonStr := `{ 1191 1191 "empty_array": [], 1192 1192 "empty_object": {}, ··· 1204 1204 } 1205 1205 1206 1206 // TestSnapJSONMixedTypes tests SnapJSON with mixed array types 1207 - func TestSnapJSONMixedTypes(t *testing.T) { 1207 + func TestSnapJsonMixedTypes(t *testing.T) { 1208 1208 jsonStr := `{ 1209 1209 "mixed_array": [ 1210 1210 "string", ··· 1229 1229 } 1230 1230 1231 1231 // TestSnapJSONRealWorldExample tests SnapJSON with real-world API data 1232 - func TestSnapJSONRealWorldExample(t *testing.T) { 1232 + func TestSnapJsonRealWorldExample(t *testing.T) { 1233 1233 jsonStr := `{ 1234 1234 "success": true, 1235 1235 "data": {
-99
internal/api/api.go
··· 1 - package api 2 - 3 - import ( 4 - "github.com/ptdewey/freeze/internal/diff" 5 - "github.com/ptdewey/freeze/internal/files" 6 - "github.com/ptdewey/freeze/internal/pretty" 7 - ) 8 - 9 - type Snapshot = files.Snapshot 10 - 11 - type DiffLine = diff.DiffLine 12 - 13 - const ( 14 - DiffShared = diff.DiffShared 15 - DiffOld = diff.DiffOld 16 - DiffNew = diff.DiffNew 17 - ) 18 - 19 - func Deserialize(raw string) (*Snapshot, error) { 20 - return files.Deserialize(raw) 21 - } 22 - 23 - func SaveSnapshot(snap *Snapshot, state string) error { 24 - return files.SaveSnapshot(snap, state) 25 - } 26 - 27 - func ReadSnapshot(testName string, state string) (*Snapshot, error) { 28 - return files.ReadSnapshot(testName, state) 29 - } 30 - 31 - func SnapshotFileName(testName string) string { 32 - return files.SnapshotFileName(testName) 33 - } 34 - 35 - func Histogram(old, new string) []DiffLine { 36 - return diff.Histogram(old, new) 37 - } 38 - 39 - func NewSnapshotBox(snap *Snapshot) string { 40 - return pretty.NewSnapshotBox(snap) 41 - } 42 - 43 - func NewSnapshotBoxFunc(snap *Snapshot) string { 44 - return pretty.NewSnapshotBoxFunc(snap) 45 - } 46 - 47 - func DiffSnapshotBox(old, new *Snapshot) string { 48 - diffLines := convertDiffLines(diff.Histogram(old.Content, new.Content)) 49 - return pretty.DiffSnapshotBox(old, new, diffLines) 50 - } 51 - 52 - func convertDiffLines(diffLines []diff.DiffLine) []pretty.DiffLine { 53 - result := make([]pretty.DiffLine, len(diffLines)) 54 - for i, dl := range diffLines { 55 - result[i] = pretty.DiffLine{ 56 - OldNumber: dl.OldNumber, 57 - NewNumber: dl.NewNumber, 58 - Line: dl.Line, 59 - Kind: pretty.DiffKind(dl.Kind), 60 - } 61 - } 62 - return result 63 - } 64 - 65 - func Red(s string) string { 66 - return pretty.Red(s) 67 - } 68 - 69 - func Green(s string) string { 70 - return pretty.Green(s) 71 - } 72 - 73 - func Yellow(s string) string { 74 - return pretty.Yellow(s) 75 - } 76 - 77 - func Blue(s string) string { 78 - return pretty.Blue(s) 79 - } 80 - 81 - func Gray(s string) string { 82 - return pretty.Gray(s) 83 - } 84 - 85 - func Bold(s string) string { 86 - return pretty.Bold(s) 87 - } 88 - 89 - func TerminalWidth() int { 90 - return pretty.TerminalWidth() 91 - } 92 - 93 - func ClearScreen() { 94 - pretty.ClearScreen() 95 - } 96 - 97 - func ClearLine() { 98 - pretty.ClearLine() 99 - }
+13 -7
internal/files/files.go
··· 18 18 } 19 19 20 20 func (s *Snapshot) Serialize() string { 21 - header := fmt.Sprintf("---\ntitle: %s\ntest_name: %s\nfile_path: %s\nfunc_name: %s\nversion: %s\n---\n", s.Title, s.Name, s.FilePath, s.FuncName, s.Version) 21 + header := fmt.Sprintf( 22 + "---\ntitle: %s\ntest_name: %s\nfile_path: %s\nfunc_name: %s\nversion: %s\n---\n", 23 + s.Title, s.Name, s.FilePath, s.FuncName, s.Version, 24 + ) 22 25 return header + s.Content 23 26 } 24 27 ··· 64 67 return snap, nil 65 68 } 66 69 70 + // TODO: make snapshots in root vs package dirs a configurable option? 67 71 func findProjectRoot() (string, error) { 68 72 cwd, err := os.Getwd() 69 73 if err != nil { ··· 85 89 } 86 90 87 91 func getSnapshotDir() (string, error) { 88 - root, err := findProjectRoot() 89 - if err != nil { 90 - return "", err 91 - } 92 - 93 - snapshotDir := filepath.Join(root, "__snapshots__") 92 + // NOTE: maybe this could be configurable? 93 + // Storing snapshots in root may be desirable in some cases 94 + // root, err := findProjectRoot() 95 + // if err != nil { 96 + // return "", err 97 + // } 98 + // snapshotDir := filepath.Join(root, "__snapshots__") 99 + snapshotDir := "__snapshots__" 94 100 if err := os.MkdirAll(snapshotDir, 0755); err != nil { 95 101 return "", err 96 102 }
+7 -8
internal/pretty/boxes.go
··· 23 23 DiffNew 24 24 ) 25 25 26 - func newSnapshotBoxInternal(snap *files.Snapshot, isFuncSnapshot bool) string { 26 + func newSnapshotBoxInternal(snap *files.Snapshot) string { 27 27 width := TerminalWidth() 28 28 snapshotFileName := files.SnapshotFileName(snap.Name) + ".snap.new" 29 29 ··· 36 36 if snap.Name != "" { 37 37 sb.WriteString(fmt.Sprintf(" test: %s\n", Blue(snap.Name))) 38 38 } 39 + // TODO: maybe put this on the "new snapshot row"? (or only show on the diff view) 39 40 if snapshotFileName != "" { 40 41 sb.WriteString(fmt.Sprintf(" file: %s\n", Gray(snapshotFileName))) 41 42 } ··· 45 46 numLines := len(lines) 46 47 lineNumWidth := len(strconv.Itoa(numLines)) 47 48 48 - topBar := strings.Repeat("─", lineNumWidth+3) + "┬" + strings.Repeat("─", width-lineNumWidth-2) + "\n" 49 + topBar := strings.Repeat("─", lineNumWidth+3) + "┬" + 50 + strings.Repeat("─", width-lineNumWidth-2) + "\n" 49 51 sb.WriteString(topBar) 50 52 51 53 for i, line := range lines { ··· 60 62 sb.WriteString(fmt.Sprintf(" %s\n", display)) 61 63 } 62 64 63 - bottomBar := strings.Repeat("─", lineNumWidth+3) + "┴" + strings.Repeat("─", width-lineNumWidth-2) + "\n" 65 + bottomBar := strings.Repeat("─", lineNumWidth+3) + "┴" + 66 + strings.Repeat("─", width-lineNumWidth-2) + "\n" 64 67 sb.WriteString(bottomBar) 65 68 66 69 return sb.String() 67 70 } 68 71 69 72 func NewSnapshotBox(snap *files.Snapshot) string { 70 - return newSnapshotBoxInternal(snap, false) 71 - } 72 - 73 - func NewSnapshotBoxFunc(snap *files.Snapshot) string { 74 - return newSnapshotBoxInternal(snap, true) 73 + return newSnapshotBoxInternal(snap) 75 74 } 76 75 77 76 func DiffSnapshotBox(old, new *files.Snapshot, diffLines []DiffLine) string {