test: add tests for conversational email system

Add tests for conversational header generation, HTML rendering
with p-tag wrapping, plain-text conversion, footer handling,
and conditional action links. Update mention test fixtures
with Project field.
This commit is contained in:
kolaente
2026-03-08 15:53:04 +01:00
parent def73e2f8e
commit aacf650ec2
2 changed files with 231 additions and 4 deletions

View File

@@ -117,6 +117,8 @@ func TestSendingMentionNotification(t *testing.T) {
task, err := GetTaskByIDSimple(s, 32)
require.NoError(t, err)
project, err := GetProjectSimpleByID(s, task.ProjectID)
require.NoError(t, err)
tc := &TaskComment{
Comment: `<p>Lorem Ipsum <mention-user data-id="user1">@user1</mention-user> <mention-user data-id="user2">@user2</mention-user> <mention-user data-id="user3">@user3</mention-user> <mention-user data-id="user4">@user4</mention-user> <mention-user data-id="user5">@user5</mention-user> <mention-user data-id="user6">@user6</mention-user></p>`,
TaskID: 32, // user2 has access to the project that task belongs to
@@ -127,6 +129,7 @@ func TestSendingMentionNotification(t *testing.T) {
Doer: u,
Task: &task,
Comment: tc,
Project: project,
}
_, err = notifyMentionedUsers(s, &task, tc.Comment, n)
@@ -171,6 +174,8 @@ func TestSendingMentionNotification(t *testing.T) {
task, err := GetTaskByIDSimple(s, 32)
require.NoError(t, err)
project, err := GetProjectSimpleByID(s, task.ProjectID)
require.NoError(t, err)
tc := &TaskComment{
Comment: `<p>Lorem Ipsum <mention-user data-id="user2">@user2</mention-user></p>`,
TaskID: 32, // user2 has access to the project that task belongs to
@@ -181,6 +186,7 @@ func TestSendingMentionNotification(t *testing.T) {
Doer: u,
Task: &task,
Comment: tc,
Project: project,
}
_, err = notifyMentionedUsers(s, &task, tc.Comment, n)

View File

@@ -134,7 +134,7 @@ This is a line
assertHTMLContainsDarkModeSupport(t, mailopts.HTMLMessage)
// Check for expected content
assert.Contains(t, mailopts.HTMLMessage, `<p>This is a line</p>`)
assert.Contains(t, mailopts.HTMLMessage, `<p style="margin-top: 10px; margin-bottom: 10px;">This is a line</p>`)
assert.Contains(t, mailopts.HTMLMessage, `Hi there,`)
// Verify no action button is present
@@ -224,8 +224,8 @@ This is a footer line
assertHTMLContainsDarkModeSupport(t, mailopts.HTMLMessage)
// Check for content
assert.Contains(t, mailopts.HTMLMessage, `<p>This is a line</p>`)
assert.Contains(t, mailopts.HTMLMessage, `<p>This is a footer line</p>`)
assert.Contains(t, mailopts.HTMLMessage, `<p style="margin-top: 10px; margin-bottom: 10px;">This is a line</p>`)
assert.Contains(t, mailopts.HTMLMessage, `This is a footer line`)
// Verify no action button
assert.NotContains(t, mailopts.HTMLMessage, `class="email-button"`)
@@ -277,7 +277,7 @@ This is a footer line
assert.Contains(t, mailopts.HTMLMessage, `href="https://example.com"`)
// Check for footer
assert.Contains(t, mailopts.HTMLMessage, `<p>This is a footer line</p>`)
assert.Contains(t, mailopts.HTMLMessage, `This is a footer line`)
})
t.Run("with thread ID", func(t *testing.T) {
mail := NewMail().
@@ -488,3 +488,224 @@ This is a footer line
assert.Contains(t, mailopts.HTMLMessage, `<strong>attention</strong>`)
})
}
func TestConversationalMail(t *testing.T) {
t.Run("Conversational flag", func(t *testing.T) {
mail := NewMail().
From("test@example.com").
To("test@otherdomain.com").
Subject("Testmail").
Conversational().
Line("This is a conversational message")
assert.True(t, mail.IsConversational())
})
t.Run("Default is not conversational", func(t *testing.T) {
mail := NewMail().
From("test@example.com").
To("test@otherdomain.com").
Subject("Testmail").
Line("This is a formal message")
assert.False(t, mail.IsConversational())
})
t.Run("Conversational template selection", func(t *testing.T) {
mail := NewMail().
From("test@example.com").
To("test@otherdomain.com").
Subject("Testmail").
Conversational().
Line("This is a conversational message").
Action("View Task", "https://example.com/task/123")
mailopts, err := RenderMail(mail, "en")
require.NoError(t, err)
// Should not contain greeting section
assert.NotContains(t, mailopts.HTMLMessage, "<p>\n\t\t\n\t</p>")
// Should use conversational styling
assert.Contains(t, mailopts.HTMLMessage, "background: #f6f8fa")
assert.Contains(t, mailopts.HTMLMessage, "font-family: -apple-system")
// Should NOT have logo (completely removed)
assert.NotContains(t, mailopts.HTMLMessage, "logo.png")
assert.NotContains(t, mailopts.HTMLMessage, "Vikunja")
// Should have inline action link with arrow
assert.Contains(t, mailopts.HTMLMessage, "View Task →")
assert.Contains(t, mailopts.HTMLMessage, "color: #0969da")
// Should not have the formal button styling
assert.NotContains(t, mailopts.HTMLMessage, "background-color: #1973ff")
assert.NotContains(t, mailopts.HTMLMessage, "width:280px")
// Plain text should not have greeting
assert.NotContains(t, mailopts.Message, "Hi there,")
assert.Contains(t, mailopts.Message, "This is a conversational message")
})
t.Run("Formal template still works", func(t *testing.T) {
mail := NewMail().
From("test@example.com").
To("test@otherdomain.com").
Subject("Testmail").
Greeting("Hi there,").
Line("This is a formal message").
Action("View Task", "https://example.com/task/123")
mailopts, err := RenderMail(mail, "en")
require.NoError(t, err)
// Should contain greeting
assert.Contains(t, mailopts.HTMLMessage, "Hi there,")
// Should use formal styling
assert.Contains(t, mailopts.HTMLMessage, "background: #f3f4f6")
assert.Contains(t, mailopts.HTMLMessage, "font-family: 'Open Sans'")
assert.Contains(t, mailopts.HTMLMessage, "width: 600px")
assert.Contains(t, mailopts.HTMLMessage, "height: 75px")
// Should HAVE logo in formal emails
assert.Contains(t, mailopts.HTMLMessage, "logo.png")
assert.Contains(t, mailopts.HTMLMessage, "Vikunja")
// Should have formal button styling
assert.Contains(t, mailopts.HTMLMessage, "background-color: #1973ff")
assert.Contains(t, mailopts.HTMLMessage, "width:280px")
// Should not have conversational arrow
assert.NotContains(t, mailopts.HTMLMessage, "View Task →")
// Plain text should have greeting
assert.Contains(t, mailopts.Message, "Hi there,")
})
t.Run("Conversational without action", func(t *testing.T) {
mail := NewMail().
From("test@example.com").
To("test@otherdomain.com").
Subject("Testmail").
Conversational().
Line("This is a conversational message without action")
mailopts, err := RenderMail(mail, "en")
require.NoError(t, err)
// Should use conversational styling
assert.Contains(t, mailopts.HTMLMessage, "background: #f6f8fa")
// Should not have action section
assert.NotContains(t, mailopts.HTMLMessage, "border-top: 1px solid #e5e7eb")
})
t.Run("Conversational with footer", func(t *testing.T) {
mail := NewMail().
From("test@example.com").
To("test@otherdomain.com").
Subject("Testmail").
Conversational().
Line("This is a conversational message").
FooterLine("This is a footer line")
mailopts, err := RenderMail(mail, "en")
require.NoError(t, err)
// Should have footer with conversational styling
assert.Contains(t, mailopts.HTMLMessage, "color: #656d76")
assert.Contains(t, mailopts.HTMLMessage, "font-size: 12px")
assert.Contains(t, mailopts.HTMLMessage, "This is a footer line")
})
t.Run("Conversational header line format", func(t *testing.T) {
action := "testuser left a comment"
taskURL := "https://example.com/task/123"
projectTitle := "Test Project"
taskTitle := "Test Task"
taskIdentifier := "#1"
headerLine := CreateConversationalHeader("", action, taskURL, projectTitle, taskIdentifier, taskTitle)
// Should contain action with username
assert.Contains(t, headerLine, "testuser left a comment")
// Should contain task link with identifier and GitHub blue color
assert.Contains(t, headerLine, `<a href="https://example.com/task/123"`)
assert.Contains(t, headerLine, `color: #0969da`)
assert.Contains(t, headerLine, `(Test Project &gt; Test Task) #1`)
})
t.Run("Conversational action link font size", func(t *testing.T) {
mail := NewMail().
Conversational().
Subject("Test").
Line("Content").
Action("View Task", "https://example.com/task/123")
mailOpts, err := RenderMail(mail, "en")
require.NoError(t, err)
// Action link should have smaller font size than main content
assert.Contains(t, mailOpts.HTMLMessage, `font-size: 14px; line-height: 1.5`) // Main content
assert.Contains(t, mailOpts.HTMLMessage, `font-weight: 500; font-size: 12px`) // Action link
})
t.Run("Conversational with header line", func(t *testing.T) {
header := CreateConversationalHeader("", "testuser left a comment", "https://example.com/task/123", "Project", "#1", "Task")
mail := NewMail().
Conversational().
Subject("Test").
HeaderLine(header).
HTML("<p>This is the comment body</p>").
Action("View Task", "https://example.com/task/123")
mailOpts, err := RenderMail(mail, "en")
require.NoError(t, err)
// Header should be in its own section
assert.Contains(t, mailOpts.HTMLMessage, `color: #57606a`)
assert.Contains(t, mailOpts.HTMLMessage, `testuser left a comment`)
// Body content should be separate from the header
assert.Contains(t, mailOpts.HTMLMessage, `This is the comment body`)
// Plain text should contain the header text
assert.Contains(t, mailOpts.Message, "testuser left a comment")
})
t.Run("Conversational header line only (no body)", func(t *testing.T) {
header := CreateConversationalHeader("", "testuser assigned you", "https://example.com/task/123", "Project", "#1", "Task")
mail := NewMail().
Conversational().
Subject("Test").
HeaderLine(header).
Action("View Task", "https://example.com/task/123")
mailOpts, err := RenderMail(mail, "en")
require.NoError(t, err)
// Header should render
assert.Contains(t, mailOpts.HTMLMessage, `testuser assigned you`)
// Should not have the content section when there are no intro/outro lines
assert.NotContains(t, mailOpts.HTMLMessage, `padding: 20px; padding-bottom: 0; color: #24292f`)
})
t.Run("Translation system integration", func(t *testing.T) {
// Test that translation keys are properly structured
// This verifies the translation keys exist and are accessible
// Test action translations
headerLine1 := CreateConversationalHeader("", "John left a comment", "https://example.com", "Project", "#1", "Task")
assert.Contains(t, headerLine1, "John left a comment")
headerLine2 := CreateConversationalHeader("", "Jane assigned you", "https://example.com", "Project", "#2", "Task")
assert.Contains(t, headerLine2, "Jane assigned you")
// Verify header structure is maintained
assert.Contains(t, headerLine1, `color: #0969da`)
assert.Contains(t, headerLine1, "(Project &gt; Task) #1")
})
}