/*
* MIT License
*
* Copyright (c) 2024 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package render_test
import (
"strings"
"github.com/go-hermes/hermes/v2"
"github.com/nabbar/golib/mail/render"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Email Rendering", func() {
var mailer render.Mailer
BeforeEach(func() {
mailer = render.New()
mailer.SetName("Test Company")
mailer.SetLink("https://example.com")
mailer.SetLogo("https://example.com/logo.png")
mailer.SetCopyright("© 2024 Test Company")
mailer.SetTroubleText("Having trouble? Contact us")
})
Describe("HTML Generation", func() {
Context("with basic content", func() {
BeforeEach(func() {
body := &hermes.Body{
Name: "John Doe",
Intros: []string{"Welcome to our service!"},
Outros: []string{"Thank you for using our service."},
}
mailer.SetBody(body)
})
It("should generate valid HTML", func() {
buf, err := mailer.GenerateHTML()
Expect(err).To(BeNil())
Expect(buf).ToNot(BeNil())
Expect(buf.Len()).To(BeNumerically(">", 0))
html := buf.String()
Expect(html).To(ContainSubstring("", 0))
})
It("should generate HTML with flat theme", func() {
mailer.SetTheme(render.ThemeFlat)
buf, err := mailer.GenerateHTML()
Expect(err).To(BeNil())
Expect(buf.Len()).To(BeNumerically(">", 0))
})
})
Context("with CSS inline options", func() {
BeforeEach(func() {
body := &hermes.Body{
Name: "User",
Intros: []string{"Test"},
}
mailer.SetBody(body)
})
It("should generate HTML with CSS inline enabled", func() {
mailer.SetCSSInline(false)
buf, err := mailer.GenerateHTML()
Expect(err).To(BeNil())
html := buf.String()
// With inline CSS, styles should be in style attributes
Expect(html).To(ContainSubstring("style="))
})
It("should generate HTML with CSS inline disabled", func() {
mailer.SetCSSInline(true)
buf, err := mailer.GenerateHTML()
Expect(err).To(BeNil())
Expect(buf).ToNot(BeNil())
})
})
})
Describe("Plain Text Generation", func() {
Context("with basic content", func() {
BeforeEach(func() {
body := &hermes.Body{
Name: "John Doe",
Intros: []string{"Welcome to our service!"},
Outros: []string{"Thank you."},
}
mailer.SetBody(body)
})
It("should generate valid plain text", func() {
buf, err := mailer.GeneratePlainText()
Expect(err).To(BeNil())
Expect(buf).ToNot(BeNil())
Expect(buf.Len()).To(BeNumerically(">", 0))
text := buf.String()
Expect(text).To(ContainSubstring("John Doe"))
Expect(text).To(ContainSubstring("Welcome to our service!"))
Expect(text).ToNot(ContainSubstring(""))
})
})
Context("with complex content", func() {
BeforeEach(func() {
body := &hermes.Body{
Name: "User",
Intros: []string{"Report:"},
Dictionary: []hermes.Entry{
{Key: "ID", Value: "123"},
},
Tables: []hermes.Table{{
Data: [][]hermes.Entry{
{{Key: "Item", Value: "A"}},
},
}},
Actions: []hermes.Action{
{
Instructions: "Click here:",
Button: hermes.Button{
Text: "Action",
Link: "https://example.com",
},
},
},
}
mailer.SetBody(body)
})
It("should include all content in plain text", func() {
buf, err := mailer.GeneratePlainText()
Expect(err).To(BeNil())
text := buf.String()
Expect(text).To(ContainSubstring("User"))
Expect(text).To(ContainSubstring("Report:"))
Expect(text).To(ContainSubstring("https://example.com"))
})
})
})
Describe("ParseData", func() {
Context("with template variables", func() {
BeforeEach(func() {
body := &hermes.Body{
Name: "{{username}}",
Intros: []string{"Hello {{username}}, your code is {{code}}"},
Greeting: "Hi {{username}}",
Signature: "{{company}}",
Title: "Welcome {{username}}",
Dictionary: []hermes.Entry{
{Key: "{{key}}", Value: "{{value}}"},
},
}
mailer.SetBody(body)
mailer.SetName("{{company}}")
mailer.SetCopyright("© {{year}} {{company}}")
})
It("should replace all template variables", func() {
data := map[string]string{
"{{username}}": "John Doe",
"{{code}}": "123456",
"{{company}}": "Test Inc",
"{{year}}": "2024",
"{{key}}": "Key1",
"{{value}}": "Value1",
}
mailer.ParseData(data)
body := mailer.GetBody()
Expect(body.Name).To(Equal("John Doe"))
Expect(body.Intros[0]).To(Equal("Hello John Doe, your code is 123456"))
Expect(body.Greeting).To(Equal("Hi John Doe"))
Expect(body.Signature).To(Equal("Test Inc"))
Expect(body.Title).To(Equal("Welcome John Doe"))
Expect(body.Dictionary[0].Key).To(Equal("Key1"))
Expect(body.Dictionary[0].Value).To(Equal("Value1"))
Expect(mailer.GetName()).To(Equal("Test Inc"))
Expect(mailer.GetCopyright()).To(Equal("© 2024 Test Inc"))
})
It("should handle multiple replacements in same string", func() {
body := &hermes.Body{
Intros: []string{"{{var1}} and {{var2}} and {{var1}}"},
}
mailer.SetBody(body)
data := map[string]string{
"{{var1}}": "First",
"{{var2}}": "Second",
}
mailer.ParseData(data)
body = mailer.GetBody()
Expect(body.Intros[0]).To(Equal("First and Second and First"))
})
})
Context("with table and action replacements", func() {
BeforeEach(func() {
body := &hermes.Body{
Tables: []hermes.Table{{
Data: [][]hermes.Entry{
{
{Key: "{{header1}}", Value: "{{value1}}"},
{Key: "{{header2}}", Value: "{{value2}}"},
},
},
}},
Actions: []hermes.Action{
{
Instructions: "{{instruction}}",
InviteCode: "{{code}}",
Button: hermes.Button{
Text: "{{buttonText}}",
Link: "{{buttonLink}}",
Color: "{{color}}",
TextColor: "{{textColor}}",
},
},
},
}
mailer.SetBody(body)
})
It("should replace variables in table data", func() {
data := map[string]string{
"{{header1}}": "Name",
"{{header2}}": "Email",
"{{value1}}": "John",
"{{value2}}": "john@example.com",
}
mailer.ParseData(data)
body := mailer.GetBody()
Expect(body.Tables[0].Data[0][0].Key).To(Equal("Name"))
Expect(body.Tables[0].Data[0][0].Value).To(Equal("John"))
Expect(body.Tables[0].Data[0][1].Key).To(Equal("Email"))
Expect(body.Tables[0].Data[0][1].Value).To(Equal("john@example.com"))
})
It("should replace variables in actions", func() {
data := map[string]string{
"{{instruction}}": "Click below",
"{{code}}": "INVITE123",
"{{buttonText}}": "Accept",
"{{buttonLink}}": "https://example.com/accept",
"{{color}}": "#007bff",
"{{textColor}}": "#ffffff",
}
mailer.ParseData(data)
body := mailer.GetBody()
action := body.Actions[0]
Expect(action.Instructions).To(Equal("Click below"))
Expect(action.InviteCode).To(Equal("INVITE123"))
Expect(action.Button.Text).To(Equal("Accept"))
Expect(action.Button.Link).To(Equal("https://example.com/accept"))
Expect(action.Button.Color).To(Equal("#007bff"))
Expect(action.Button.TextColor).To(Equal("#ffffff"))
})
})
Context("with markdown content", func() {
It("should replace variables in free markdown", func() {
body := &hermes.Body{
FreeMarkdown: hermes.Markdown("# {{title}}\n\nHello {{name}}!"),
}
mailer.SetBody(body)
data := map[string]string{
"{{title}}": "Welcome",
"{{name}}": "User",
}
mailer.ParseData(data)
body = mailer.GetBody()
markdown := string(body.FreeMarkdown)
Expect(markdown).To(ContainSubstring("# Welcome"))
Expect(markdown).To(ContainSubstring("Hello User!"))
})
})
Context("with empty or nil data", func() {
It("should handle empty data map", func() {
body := &hermes.Body{
Name: "Original",
}
mailer.SetBody(body)
mailer.ParseData(map[string]string{})
// Should not modify anything
Expect(mailer.GetBody().Name).To(Equal("Original"))
})
It("should handle nil slices gracefully", func() {
body := &hermes.Body{
Name: "Test",
}
mailer.SetBody(body)
// Should not panic
mailer.ParseData(map[string]string{"{{test}}": "value"})
Expect(mailer.GetBody().Name).To(Equal("Test"))
})
})
})
Describe("Integration", func() {
Context("when using complete workflow", func() {
It("should create, configure, parse and generate email", func() {
// Configure
mailer.SetName("{{company}}")
mailer.SetTheme(render.ThemeFlat)
// Set body
body := &hermes.Body{
Name: "{{user}}",
Intros: []string{"Welcome {{user}}!"},
Actions: []hermes.Action{
{
Instructions: "Verify your email:",
Button: hermes.Button{
Text: "Verify",
Link: "{{link}}",
},
},
},
}
mailer.SetBody(body)
// Parse data
mailer.ParseData(map[string]string{
"{{company}}": "Test Inc",
"{{user}}": "John",
"{{link}}": "https://example.com/verify",
})
// Generate both formats
htmlBuf, htmlErr := mailer.GenerateHTML()
textBuf, textErr := mailer.GeneratePlainText()
Expect(htmlErr).To(BeNil())
Expect(textErr).To(BeNil())
Expect(htmlBuf.Len()).To(BeNumerically(">", 0))
Expect(textBuf.Len()).To(BeNumerically(">", 0))
html := htmlBuf.String()
text := textBuf.String()
Expect(html).To(ContainSubstring("John"))
Expect(html).To(ContainSubstring("Welcome John!"))
Expect(html).To(ContainSubstring("https://example.com/verify"))
Expect(text).To(ContainSubstring("John"))
Expect(text).To(ContainSubstring("Welcome John!"))
})
})
})
Describe("Error Handling", func() {
Context("with invalid configurations", func() {
It("should handle generation with minimal setup", func() {
minimalMailer := render.New()
minimalMailer.SetBody(&hermes.Body{
Name: "Test",
})
htmlBuf, htmlErr := minimalMailer.GenerateHTML()
textBuf, textErr := minimalMailer.GeneratePlainText()
// Should not error with minimal setup
Expect(htmlErr).To(BeNil())
Expect(textErr).To(BeNil())
Expect(htmlBuf).ToNot(BeNil())
Expect(textBuf).ToNot(BeNil())
})
})
})
Describe("Special Characters", func() {
Context("when content contains special characters", func() {
It("should handle HTML special characters", func() {
body := &hermes.Body{
Name: "User <>&\"'",
Intros: []string{"Test "},
}
mailer.SetBody(body)
buf, err := mailer.GenerateHTML()
Expect(err).To(BeNil())
html := buf.String()
// Hermes should escape HTML entities
Expect(html).ToNot(ContainSubstring("