@@ -1744,3 +1744,149 @@ func TestEmailTemplate_UnmarshalJSON_Minimal_ExistingFile(t *testing.T) {
17441744 t .Fatalf ("unexpected error: %v" , err )
17451745 }
17461746}
1747+
1748+ // TestEmailTemplate_MjRawContent_JSONRoundTrip tests that mj-raw block content is preserved
1749+ // through JSON serialization/deserialization (GitHub issue #229)
1750+ func TestEmailTemplate_MjRawContent_JSONRoundTrip (t * testing.T ) {
1751+ // JSON that represents a template with mj-raw content
1752+ jsonData := []byte (`{
1753+ "sender_id": "test-sender",
1754+ "subject": "Test Subject",
1755+ "compiled_preview": "<html>test</html>",
1756+ "visual_editor_tree": {
1757+ "id": "mjml-1",
1758+ "type": "mjml",
1759+ "children": [
1760+ {
1761+ "id": "body-1",
1762+ "type": "mj-body",
1763+ "children": [
1764+ {
1765+ "id": "section-1",
1766+ "type": "mj-section",
1767+ "children": [
1768+ {
1769+ "id": "column-1",
1770+ "type": "mj-column",
1771+ "children": [
1772+ {
1773+ "id": "raw-1",
1774+ "type": "mj-raw",
1775+ "content": "<table><tr><td>Cell 1</td><td>Cell 2</td></tr></table>",
1776+ "attributes": {}
1777+ }
1778+ ]
1779+ }
1780+ ]
1781+ }
1782+ ]
1783+ }
1784+ ]
1785+ }
1786+ }` )
1787+
1788+ // Unmarshal the JSON
1789+ var emailTemplate EmailTemplate
1790+ err := emailTemplate .UnmarshalJSON (jsonData )
1791+ assert .NoError (t , err , "Failed to unmarshal EmailTemplate" )
1792+
1793+ // Verify the visual_editor_tree was unmarshaled correctly
1794+ assert .NotNil (t , emailTemplate .VisualEditorTree , "VisualEditorTree should not be nil" )
1795+ assert .Equal (t , notifuse_mjml .MJMLComponentMjml , emailTemplate .VisualEditorTree .GetType ())
1796+
1797+ // Find the mj-raw block and verify content
1798+ var rawBlock notifuse_mjml.EmailBlock
1799+ bodyBlock := emailTemplate .VisualEditorTree .GetChildren ()[0 ]
1800+ sectionBlock := bodyBlock .GetChildren ()[0 ]
1801+ columnBlock := sectionBlock .GetChildren ()[0 ]
1802+ rawBlock = columnBlock .GetChildren ()[0 ]
1803+
1804+ assert .Equal (t , notifuse_mjml .MJMLComponentMjRaw , rawBlock .GetType (), "Expected mj-raw block" )
1805+ content := rawBlock .GetContent ()
1806+ assert .NotNil (t , content , "mj-raw content should not be nil" )
1807+ assert .Equal (t , "<table><tr><td>Cell 1</td><td>Cell 2</td></tr></table>" , * content )
1808+
1809+ // Marshal back to JSON
1810+ marshaledJSON , err := emailTemplate .MarshalJSON ()
1811+ assert .NoError (t , err , "Failed to marshal EmailTemplate" )
1812+
1813+ // Verify the content is preserved in the marshaled JSON
1814+ assert .Contains (t , string (marshaledJSON ), "Cell 1" , "Marshaled JSON should contain mj-raw content" )
1815+ assert .Contains (t , string (marshaledJSON ), "Cell 2" , "Marshaled JSON should contain mj-raw content" )
1816+
1817+ // Unmarshal again to verify round-trip
1818+ var emailTemplate2 EmailTemplate
1819+ err = emailTemplate2 .UnmarshalJSON (marshaledJSON )
1820+ assert .NoError (t , err , "Failed to unmarshal EmailTemplate after round-trip" )
1821+
1822+ // Find the mj-raw block again and verify content
1823+ var rawBlock2 notifuse_mjml.EmailBlock
1824+ bodyBlock2 := emailTemplate2 .VisualEditorTree .GetChildren ()[0 ]
1825+ sectionBlock2 := bodyBlock2 .GetChildren ()[0 ]
1826+ columnBlock2 := sectionBlock2 .GetChildren ()[0 ]
1827+ rawBlock2 = columnBlock2 .GetChildren ()[0 ]
1828+
1829+ content2 := rawBlock2 .GetContent ()
1830+ assert .NotNil (t , content2 , "mj-raw content should not be nil after round-trip" )
1831+ assert .Equal (t , "<table><tr><td>Cell 1</td><td>Cell 2</td></tr></table>" , * content2 )
1832+ }
1833+
1834+ // TestEmailTemplate_MjRawContent_Value_Scan tests that mj-raw content is preserved
1835+ // when using database Value() and Scan() methods (simulating database save/load)
1836+ func TestEmailTemplate_MjRawContent_Value_Scan (t * testing.T ) {
1837+ // Create an EmailTemplate with mj-raw content
1838+ rawContent := "<table><tr><td>Cell 1</td><td>Cell 2</td></tr></table>"
1839+
1840+ rawBase := notifuse_mjml .NewBaseBlock ("raw-1" , notifuse_mjml .MJMLComponentMjRaw )
1841+ rawBase .Content = & rawContent
1842+ rawBlock := & notifuse_mjml.MJRawBlock {BaseBlock : rawBase }
1843+
1844+ columnBlock := & notifuse_mjml.MJColumnBlock {BaseBlock : notifuse_mjml .NewBaseBlock ("column-1" , notifuse_mjml .MJMLComponentMjColumn )}
1845+ columnBlock .Children = []notifuse_mjml.EmailBlock {rawBlock }
1846+
1847+ sectionBlock := & notifuse_mjml.MJSectionBlock {BaseBlock : notifuse_mjml .NewBaseBlock ("section-1" , notifuse_mjml .MJMLComponentMjSection )}
1848+ sectionBlock .Children = []notifuse_mjml.EmailBlock {columnBlock }
1849+
1850+ bodyBlock := & notifuse_mjml.MJBodyBlock {BaseBlock : notifuse_mjml .NewBaseBlock ("body-1" , notifuse_mjml .MJMLComponentMjBody )}
1851+ bodyBlock .Children = []notifuse_mjml.EmailBlock {sectionBlock }
1852+
1853+ mjmlBlock := & notifuse_mjml.MJMLBlock {BaseBlock : notifuse_mjml .NewBaseBlock ("mjml-1" , notifuse_mjml .MJMLComponentMjml )}
1854+ mjmlBlock .Children = []notifuse_mjml.EmailBlock {bodyBlock }
1855+
1856+ emailTemplate := & EmailTemplate {
1857+ SenderID : "test-sender" ,
1858+ Subject : "Test Subject" ,
1859+ CompiledPreview : "<html>test</html>" ,
1860+ VisualEditorTree : mjmlBlock ,
1861+ }
1862+
1863+ // Test Value() - simulates database save
1864+ value , err := emailTemplate .Value ()
1865+ assert .NoError (t , err , "Value() should not return error" )
1866+ assert .NotNil (t , value , "Value() should return data" )
1867+
1868+ // Verify the value contains the content
1869+ valueBytes , ok := value .([]byte )
1870+ assert .True (t , ok , "Value() should return []byte" )
1871+ assert .Contains (t , string (valueBytes ), "Cell 1" , "Value() should contain mj-raw content" )
1872+
1873+ // Test Scan() - simulates database load
1874+ var emailTemplate2 EmailTemplate
1875+ err = emailTemplate2 .Scan (valueBytes )
1876+ assert .NoError (t , err , "Scan() should not return error" )
1877+
1878+ // Verify the visual_editor_tree was scanned correctly
1879+ assert .NotNil (t , emailTemplate2 .VisualEditorTree , "VisualEditorTree should not be nil after Scan" )
1880+
1881+ // Find the mj-raw block and verify content
1882+ var rawBlock2 notifuse_mjml.EmailBlock
1883+ bodyBlock2 := emailTemplate2 .VisualEditorTree .GetChildren ()[0 ]
1884+ sectionBlock2 := bodyBlock2 .GetChildren ()[0 ]
1885+ columnBlock2 := sectionBlock2 .GetChildren ()[0 ]
1886+ rawBlock2 = columnBlock2 .GetChildren ()[0 ]
1887+
1888+ assert .Equal (t , notifuse_mjml .MJMLComponentMjRaw , rawBlock2 .GetType (), "Expected mj-raw block after Scan" )
1889+ content2 := rawBlock2 .GetContent ()
1890+ assert .NotNil (t , content2 , "mj-raw content should not be nil after Scan" )
1891+ assert .Equal (t , rawContent , * content2 , "mj-raw content should be preserved after Value/Scan round-trip" )
1892+ }
0 commit comments