Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.20'
go-version: '1.23'
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
with:
Expand All @@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.20', '1.21', '1.22', '1.23']
go-version: ['1.23', '1.24']
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -41,7 +41,7 @@ jobs:
go mod download
- name: Test
run: |
go test -cover -race ./... -coverprofile=coverage.text
go test -v -cover -race ./... -coverprofile=coverage.text
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
COVERAGE_FILE := coverage.out
COVERAGE_HTML := coverage.html
MIN_COVERAGE := 80
MIN_COVERAGE := 90

# Format code
.PHONY: fmt
Expand All @@ -25,7 +25,7 @@ install:
.PHONY: test
test:
@echo "Running tests..."
go test -cover -race ./... -coverprofile=$(COVERAGE_FILE) -coverpkg=./...
go test -v -cover -race ./... -coverprofile=$(COVERAGE_FILE) -coverpkg=./...

.PHONY: coverage
coverage:
Expand Down
84 changes: 12 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Go-Mailer is a Go library that wraps the [wneessen/go-mail](https://github.com/w
- Attachment Support: Easily attach files from local disk, embedded filesystems, IOFS filesystems, or `io.Reader`/`io.ReadSeeker`.
- Customizable Email Content: Allows setting greetings, salutations, and product information for a personalized experience.
- Responsive HTML Template: Automatically formats emails with a clean, responsive design compatible with most email clients.
- CSS-inlined HTML: Automatically inlines CSS styles for better compatibility with email clients that strip out `<style>` tags.

## Installation
To install Go-Mailer, run the following command:
Expand Down Expand Up @@ -50,7 +51,11 @@ func main() {

// Initialize the Go-Mailer instance with the existing client
m := mailer.New(client,
mailer.WithFrom("noreply@example.com")
mailer.WithFrom("noreply@example.com"),
mailer.WithProduct(mailer.Product{
Name: "My Application",
URL: "https://example.com",
})
)

// Build an HTML email using the fluent message builder
Expand All @@ -72,81 +77,16 @@ func main() {
## Fluent HTML Message Builder
The fluent HTML message builder provides a chainable API to construct HTML emails intuitively. Key methods include:

- `Subject(string)`: Sets the email subject.
- `To(string)`: Adds a recipient email address.
- `Cc(string)`: Adds a CC recipient email address.
- `Bcc(string)`: Adds a BCC recipient email address.
- `Line(string)`: Adds a plain text line to the email body.
- `Linef(string, ...interface{})`: Adds a formatted line to the email body.
- `Action(string, string)`: Adds a clickable button with a label and URL.
- `Greeting(string)`: Sets a greeting line at the top of the email.
- `Salutation(string)`: Sets a salutation line at the bottom of the email.
- `Line(string)`: Adds a plain text line to the email body.
- `Linef(string, ...interface{})`: Adds a formatted line to the email body.
- `Product(mailer.Product)`: Sets product information for the email, which can be used in the footer.
- `AttachFile(string, ...mailer.FileOption)`: Attaches a file from the local disk.
- `AttachFromEmbedFS(string, *embed.FS, ...mailer.FileOption)`: Attaches a file from an embedded filesystem.
- `AttachFromIOFS(string, fs.FS, ...mailer.FileOption)`: Attaches a file from an IOFS filesystem.
- `AttachReader(string, io.Reader, ...mailer.FileOption)`: Attaches a file from an `io.Reader`.
- `AttachReadSeeker(string, io.ReadSeeker, ...mailer.FileOption)`: Attaches a file from an `io.ReadSeeker`.
- `Salutation(string)`: Sets a salutation line at the bottom of the email.
- `Theme(string)`: Sets the theme for the email. Supported themes are "default" and "plain".

The builder automatically formats the email with a clean, responsive HTML template, ensuring compatibility with most email clients.

Example of a more complex email:

```go
message := mailer.NewMessage().
Subject("Welcome to Our Platform").
To("user@example.com").
Line("Thank you for signing up!").
Action("Activate Account", "https://example.com/activate").
Line("We're excited to have you on board.")
```

## Configuration
Go-Mailer uses an existing `mail.Client` from `wneessen/go-mail`, which must be configured separately. The `mailer.New` function accepts optional configuration options for additional customization (e.g., default sender, reply-to address, etc.). Example configuration for the underlying `go-mail` client:

```go
client, err := mail.NewClient("smtp.sendgrid.net",
mail.WithPort(587),
mail.WithSMTPAuth(mail.SMTPAuthPlain),
mail.WithUsername("apikey"),
mail.WithPassword("your-sendgrid-api-key"),
)
if err != nil {
panic(err)
}

m := mailer.New(client,
mailer.WithFrom("noreply@example.com"),
mailer.WithReplyTo("support@example.com"),
mailer.WithProduct(mailer.Product{
Name: "Go Mailer",
URL: "https://github.com/ahmadfaizk/go-mailer",
})
)
```

## Dependencies
Go-Mailer relies on the following key dependency:
- [wneessen/go-mail](https://github.com/wneessen/go-mail): Provides the core email sending functionality with support for SMTP, STARTTLS, and advanced features like DKIM and S/MIME.

## Contributing
Contributions are welcome! To contribute to Go-Mailer:

1. Fork the repository
2. Create a new branch (`git checkout -b feature/your-feature`)
3. Make your changes
4. Commit your changes (`git commit -m 'Add your feature'`)
5. Push to the branch (`git push origin feature/your-feature`)
6. Open a pull request

Please ensure your code adheres to the project's coding standards and includes tests.
The HTML template is based on [Postmark Transactional Email Templates](https://github.com/ActiveCampaign/postmark-templates).

## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

## Acknowledgements
- [wneessen/go-mail](https://github.com/wneessen/go-mail) for providing the robust foundation for this library.
- All contributors to the `go-mail` project for their excellent work.

## Contact
For questions or feedback, reach out to the maintainer at [ahmadfaizk's GitHub profile](https://github.com/ahmadfaizk).
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
10 changes: 10 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mailer

type config struct {
theme string
from Address
replyTo string
product Product
Expand Down Expand Up @@ -33,12 +34,21 @@ func WithReplyTo(replyTo string) Option {
}
}

// WithTheme sets the theme for the email messages sent by the Mailer.
// Supported themes are "default" and "plain".
func WithTheme(theme string) Option {
return func(c *config) {
c.theme = theme
}
}

func newConfig(opts ...Option) *config {
cfg := &config{
product: Product{
Name: "GoMailer",
URL: "https://github.com/ahmadfaizk/go-mailer",
},
theme: "default",
}
for _, opt := range opts {
opt(cfg)
Expand Down
12 changes: 9 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
module github.com/ahmadfaizk/go-mailer

go 1.20
go 1.23.0

require (
github.com/mocktools/go-smtp-mock/v2 v2.5.1
github.com/stretchr/testify v1.10.0
github.com/vanng822/go-premailer v1.25.0
github.com/wneessen/go-mail v0.6.2
)

require (
github.com/PuerkitoBio/goquery v1.10.3 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/text v0.22.0 // indirect
github.com/vanng822/css v1.0.1 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/text v0.26.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
24 changes: 22 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/mocktools/go-smtp-mock/v2 v2.5.1 h1:QcMJMChSgG1olVj4o6xxQFdrWzRjYNrcq660HAjd0wA=
github.com/mocktools/go-smtp-mock/v2 v2.5.1/go.mod h1:Rr8M2njlxx//l5INl2+uESnsL2lDsL24teEykCrGfmE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -16,6 +22,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vanng822/css v1.0.1 h1:10yiXc4e8NI8ldU6mSrWmSWMuyWgPr9DZ63RSlsgDw8=
github.com/vanng822/css v1.0.1/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w=
github.com/vanng822/go-premailer v1.25.0 h1:hGHKfroCXrCDTyGVR8o4HCON5/HWvc7C1uocS+VnaZs=
github.com/vanng822/go-premailer v1.25.0/go.mod h1:8WJKIPZtegxqSOA8+eDFx7QNesKmMYfGEIodLTJqrtM=
github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
Expand All @@ -24,8 +34,10 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
Expand All @@ -39,12 +51,16 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -56,6 +72,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand All @@ -65,6 +82,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand All @@ -74,8 +92,10 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
Expand Down
27 changes: 21 additions & 6 deletions mailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,25 @@ func (m *Mailer) SendContext(ctx context.Context, message *Message) error {
return m.client.DialAndSendWithContext(ctx, msg)
}

func (m *Mailer) toMailMsg(message *Message) (*mail.Msg, error) {
func (m *Mailer) validateAndModifyMessage(message *Message) error {
if message == nil {
return nil, errors.New("message cannot be nil")
return errors.New("message cannot be nil")
}
if len(message.GetTo()) == 0 {
return nil, errors.New("message must have at least one recipient")
return errors.New("message must have at least one recipient")
}
if message.GetSubject() == "" {
return nil, errors.New("message must have a subject")
return errors.New("message must have a subject")
}

message.Product(m.cfg.product)
html, err := message.GenerateHTML()
if err != nil {
message.Theme(m.cfg.theme)

return nil
}

func (m *Mailer) toMailMsg(message *Message) (*mail.Msg, error) {
if err := m.validateAndModifyMessage(message); err != nil {
return nil, err
}

Expand Down Expand Up @@ -83,6 +88,16 @@ func (m *Mailer) toMailMsg(message *Message) (*mail.Msg, error) {
}
}
msg.Subject(message.GetSubject())

plaintext, err := message.GeneratePlaintext()
if err != nil {
return nil, err
}
msg.SetBodyString(mail.TypeTextPlain, plaintext)
html, err := message.GenerateHTML()
if err != nil {
return nil, err
}
msg.SetBodyString(mail.TypeTextHTML, html)

if err := m.setAttachments(msg, message); err != nil {
Expand Down
20 changes: 16 additions & 4 deletions mailer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ func TestNew(t *testing.T) {
opts: []mailer.Option{
mailer.WithFrom("Test Sender", "test@example.com"),
mailer.WithProduct(mailer.Product{
Name: "Test Product",
LogoURL: "https://example.com/logo.png",
URL: "https://example.com",
Name: "Test Product",
URL: "https://example.com",
}),
mailer.WithReplyTo("reply@example.com"),
},
wantNil: false,
},
Expand Down Expand Up @@ -86,7 +86,7 @@ func TestMailer_Send(t *testing.T) {
)
require.NoError(t, err)

mailMailer := mailer.New(client, mailer.WithFrom("noreply@example.com"))
mailMailer := mailer.New(client, mailer.WithFrom("noreply@example.com", "No Reply"))

tests := []struct {
name string
Expand Down Expand Up @@ -305,6 +305,18 @@ func TestMailer_Send(t *testing.T) {
wantErr: true,
errContains: "failed to parse mail address",
},
{
name: "return errors when reply-to email is invalid",
message: func() *mailer.Message {
msg := mailer.NewMessage().
To("recipient@example.com").
ReplyTo("invalid-email").
Subject("Test Subject")
return msg
}(),
wantErr: true,
errContains: "failed to parse reply-to address",
},
{
name: "return errors when attachment embedded file is not found",
message: func() *mailer.Message {
Expand Down
Loading