chore: Code clean up moves to service classes
This commit is contained in:
@@ -1,314 +0,0 @@
|
||||
module Messages::ForwardedMessageFormatter
|
||||
def self.parse_from_field(from_field)
|
||||
return '' if from_field.blank?
|
||||
|
||||
from_field =~ /(.*)<(.*)>/ ? Regexp.last_match(2).strip : from_field
|
||||
end
|
||||
|
||||
def self.format_plain_text_to_html(text)
|
||||
ERB::Util.html_escape(text.to_s).gsub("\n", '<br>')
|
||||
end
|
||||
|
||||
def self.extract_email(from_field)
|
||||
return '' if from_field.blank?
|
||||
|
||||
from_field =~ /<(.*)>/ ? Regexp.last_match(1) : from_field
|
||||
end
|
||||
|
||||
def self.format_date_string(date_str)
|
||||
return '' if date_str.blank?
|
||||
|
||||
begin
|
||||
parsed_date = DateTime.now
|
||||
parsed_date.strftime('%a, %b %-d, %Y at %-l:%M %p')
|
||||
rescue StandardError
|
||||
date_str
|
||||
end
|
||||
end
|
||||
|
||||
def self.strip_markdown(text)
|
||||
return '' if text.blank?
|
||||
|
||||
# Convert markdown to HTML using CommonMarker
|
||||
html = CommonMarker.render_html(text, :DEFAULT)
|
||||
|
||||
# Strip HTML tags to get plain text
|
||||
ActionView::Base.full_sanitizer.sanitize(html)
|
||||
end
|
||||
|
||||
def self.convert_markdown_to_html(text)
|
||||
return '' if text.blank?
|
||||
|
||||
# Use CommonMarker with GitHub Flavored Markdown options
|
||||
options = [:GITHUB_PRE_LANG, :UNSAFE]
|
||||
extensions = [:table, :strikethrough, :autolink]
|
||||
|
||||
CommonMarker.render_html(text, options, extensions)
|
||||
end
|
||||
|
||||
# Convert text to plain text, stripping any markdown formatting
|
||||
def self.markdown_to_plain_text(text)
|
||||
return '' if text.blank?
|
||||
|
||||
# Simply strip any markdown by converting to HTML and sanitizing
|
||||
strip_markdown(text)
|
||||
end
|
||||
end
|
||||
|
||||
# Only modify the formatted_html_content method in ForwardedMessageContentBuilder
|
||||
module Messages::ForwardedMessageContentBuilder
|
||||
def forwarded_header_text
|
||||
return '' unless formatted_info.values.any?
|
||||
|
||||
build_header_lines.compact.join("\n")
|
||||
end
|
||||
|
||||
def build_header_lines
|
||||
[
|
||||
"\n\n---------- Forwarded message ---------",
|
||||
("From: #{formatted_info[:from]}" if formatted_info[:from]),
|
||||
("Date: #{formatted_info[:date]}" if formatted_info[:date]),
|
||||
("Subject: #{formatted_info[:subject]}" if formatted_info[:subject]),
|
||||
("To: <#{formatted_info[:to]}>" if formatted_info[:to]),
|
||||
"\n"
|
||||
]
|
||||
end
|
||||
|
||||
def forwarded_body_text
|
||||
return forwarded_message.content.to_s if email_data.blank?
|
||||
|
||||
text = email_data.dig('text_content', 'full')
|
||||
html = email_data.dig('html_content', 'full')
|
||||
if text.present?
|
||||
text
|
||||
elsif html.present?
|
||||
ActionView::Base.full_sanitizer.sanitize(html)
|
||||
else
|
||||
forwarded_message.content.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def formatted_content(original_content = '')
|
||||
return original_content.to_s if forwarded_message.blank?
|
||||
|
||||
original_content.to_s + forwarded_header_text + forwarded_body_text
|
||||
end
|
||||
|
||||
# Update this method to always use markdown conversion
|
||||
def formatted_html_content(original_content = '')
|
||||
return original_content if forwarded_message.blank?
|
||||
|
||||
# Always use markdown conversion since it handles plain text correctly
|
||||
converted_content = Messages::ForwardedMessageFormatter.convert_markdown_to_html(original_content)
|
||||
html_wrapper(converted_content)
|
||||
end
|
||||
end
|
||||
|
||||
module Messages::ForwardedMessageHtmlBuilder
|
||||
def html_wrapper(content)
|
||||
base = '<div dir="ltr">'
|
||||
if content.blank?
|
||||
"#{base}#{forwarded_header_html}#{forwarded_body_html}</div>"
|
||||
else
|
||||
"#{base}#{content}<br><br>#{forwarded_header_html}#{forwarded_body_html}</div>"
|
||||
end
|
||||
end
|
||||
|
||||
def forwarded_header_html
|
||||
from_value = formatted_info[:from].to_s
|
||||
email = Messages::ForwardedMessageFormatter.extract_email(from_value)
|
||||
|
||||
[
|
||||
'<div class="gmail_quote gmail_quote_container">',
|
||||
'<div dir="ltr" class="gmail_attr">---------- Forwarded message ---------<br>',
|
||||
build_email_html(email),
|
||||
build_field_html('Date', formatted_info[:date]),
|
||||
build_field_html('Subject', formatted_info[:subject]),
|
||||
build_to_html(formatted_info[:to]),
|
||||
'</div><br><br>'
|
||||
].compact.join
|
||||
end
|
||||
|
||||
def build_email_html(email)
|
||||
return nil if email.blank?
|
||||
|
||||
"From: <<a href=\"mailto:#{email}\">#{email}</a>><br>"
|
||||
end
|
||||
|
||||
def build_field_html(label, value)
|
||||
value.present? ? "#{label}: #{value}<br>" : nil
|
||||
end
|
||||
|
||||
def build_to_html(to_email)
|
||||
return nil if to_email.blank?
|
||||
|
||||
"To: <<a href=\"mailto:#{to_email}\">#{to_email}</a>><br>"
|
||||
end
|
||||
|
||||
# Update this method to always use markdown conversion
|
||||
def forwarded_body_html
|
||||
# Return HTML content directly if available
|
||||
return email_data.dig('html_content', 'full') if email_data&.dig('html_content', 'full').present?
|
||||
|
||||
# Otherwise format content to HTML
|
||||
content = if email_data&.dig('text_content', 'full').present?
|
||||
email_data.dig('text_content', 'full')
|
||||
else
|
||||
forwarded_message.content.to_s
|
||||
end
|
||||
|
||||
# Always use markdown conversion since it handles plain text correctly
|
||||
Messages::ForwardedMessageFormatter.convert_markdown_to_html(content)
|
||||
end
|
||||
end
|
||||
|
||||
# The rest of the modules and class remain unchanged
|
||||
module Messages::ForwardedMessageDataHandler
|
||||
def prepare_email_data
|
||||
initialize_email_data
|
||||
end
|
||||
|
||||
def initialize_email_data
|
||||
data = base_email_data
|
||||
add_content_fields(data)
|
||||
add_header_fields(data)
|
||||
data
|
||||
end
|
||||
|
||||
def base_email_data
|
||||
email_data.present? ? email_data.dup || {} : {}
|
||||
end
|
||||
|
||||
def add_content_fields(data)
|
||||
data['html_content'] ||= {}
|
||||
data['text_content'] ||= {}
|
||||
end
|
||||
|
||||
def add_header_fields(data)
|
||||
data['from'] = [email_from_inbox] # Always overwrite with inbox email
|
||||
data['to'] = Array(@params[:to_emails]) if @params && @params[:to_emails].present?
|
||||
data['subject'] ||= subject
|
||||
data['date'] ||= Time.zone.now.to_s
|
||||
end
|
||||
|
||||
def formatted_info
|
||||
{
|
||||
from: inbox_email.to_s,
|
||||
date: Messages::ForwardedMessageFormatter.format_date_string(email_date),
|
||||
subject: subject.to_s,
|
||||
to: recipient_email.to_s
|
||||
}
|
||||
end
|
||||
|
||||
def inbox_email
|
||||
email_from_data || email_from_inbox
|
||||
end
|
||||
|
||||
def email_from_data
|
||||
return nil unless email_data_has_from?
|
||||
|
||||
from_field = email_data['from'].first.to_s
|
||||
Messages::ForwardedMessageFormatter.parse_from_field(from_field)
|
||||
end
|
||||
|
||||
def email_data_has_from?
|
||||
email_data.present? &&
|
||||
email_data['from'].present? &&
|
||||
email_data['from'].first.present?
|
||||
end
|
||||
|
||||
def email_from_inbox
|
||||
inbox = forwarded_message&.conversation&.inbox
|
||||
return nil if inbox.blank? || inbox.channel_type != 'Channel::Email'
|
||||
|
||||
email = inbox.channel&.email
|
||||
return nil if email.blank?
|
||||
|
||||
email
|
||||
end
|
||||
|
||||
def recipient_email
|
||||
return email_data&.dig('to', 0) if email_data&.dig('to', 0).present?
|
||||
|
||||
forwarded_message&.content_attributes&.dig('to_emails', 0).presence
|
||||
end
|
||||
|
||||
def subject
|
||||
email_data&.dig('subject').presence ||
|
||||
forwarded_message&.conversation&.additional_attributes&.dig('subject').presence ||
|
||||
'No Subject'
|
||||
end
|
||||
|
||||
def email_date
|
||||
email_data&.dig('date').presence || Time.zone.now.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class Messages::ForwardedMessageBuilder
|
||||
include Messages::ForwardedMessageFormatter
|
||||
include Messages::ForwardedMessageHtmlBuilder
|
||||
include Messages::ForwardedMessageContentBuilder
|
||||
include Messages::ForwardedMessageDataHandler
|
||||
|
||||
def initialize(message_id, params = {})
|
||||
@message_id = message_id
|
||||
@params = params || {}
|
||||
end
|
||||
|
||||
def perform
|
||||
return {} unless @message_id
|
||||
return basic_attributes if forwarded_message.blank?
|
||||
|
||||
build_forwarded_attributes
|
||||
end
|
||||
|
||||
def forwarded_email_data(original_content = '')
|
||||
return {} if forwarded_message.blank?
|
||||
|
||||
data = prepare_email_data
|
||||
full_content = formatted_content(original_content)
|
||||
|
||||
# Convert markdown in original_content to plain text
|
||||
original_plain = Messages::ForwardedMessageFormatter.markdown_to_plain_text(original_content.to_s)
|
||||
|
||||
# Convert full_content to plain text if it contains markdown
|
||||
full_plain = Messages::ForwardedMessageFormatter.markdown_to_plain_text(full_content)
|
||||
|
||||
stripped = Messages::ForwardedMessageFormatter.strip_markdown(original_content.to_s)
|
||||
html_full = formatted_html_content(original_content)
|
||||
|
||||
data['html_content'].merge!('quoted' => stripped, 'reply' => full_content, 'full' => html_full)
|
||||
|
||||
data['text_content'].merge!(
|
||||
'quoted' => original_plain,
|
||||
'reply' => full_plain,
|
||||
'full' => full_plain
|
||||
)
|
||||
|
||||
data['attachments'] = forwarded_message.attachments.map(&:serializable_hash) if forwarded_message.attachments.present?
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def forwarded_attachments
|
||||
forwarded_message.attachments if forwarded_message&.attachments&.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_forwarded_attributes
|
||||
{ content_attributes: { forwarded_message_id: @message_id, email: prepare_email_data } }
|
||||
end
|
||||
|
||||
def forwarded_message
|
||||
@forwarded_message ||= Message.find_by(id: @message_id)
|
||||
end
|
||||
|
||||
def email_data
|
||||
@email_data ||= forwarded_message&.content_attributes&.dig('email')
|
||||
end
|
||||
|
||||
def basic_attributes
|
||||
{ content_attributes: { forwarded_message_id: @message_id } }
|
||||
end
|
||||
end
|
||||
@@ -25,7 +25,7 @@ class Messages::MessageBuilder
|
||||
private
|
||||
|
||||
def process_forwarded_message
|
||||
builder = Messages::ForwardedMessageBuilder.new(@forwarded_message_id, { to_emails: @params[:to_emails] })
|
||||
builder = Messages::ForwardedMessageBuilderService.new(@forwarded_message_id, { to_emails: @params[:to_emails] })
|
||||
@forwarded_attributes = builder.perform
|
||||
@forwarded_message_attachments = builder.forwarded_attachments
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Messages::ForwardedMessageBuilderService
|
||||
attr_reader :message_id, :params
|
||||
|
||||
def initialize(message_id, params = {})
|
||||
@message_id = message_id
|
||||
@params = params || {}
|
||||
end
|
||||
|
||||
def perform
|
||||
return {} unless message_id
|
||||
return basic_attributes if forwarded_message.blank?
|
||||
|
||||
build_forwarded_attributes
|
||||
end
|
||||
|
||||
def formatted_content(original_content = '')
|
||||
return original_content.to_s if forwarded_message.blank?
|
||||
|
||||
data_handler = Messages::ForwardedMessageDataHandlerService.new(forwarded_message, email_data, params)
|
||||
formatted_info = data_handler.formatted_info
|
||||
|
||||
content_builder = Messages::ForwardedMessageContentBuilderService.new(
|
||||
formatted_info,
|
||||
forwarded_message,
|
||||
email_data
|
||||
)
|
||||
|
||||
content_builder.formatted_content(original_content)
|
||||
end
|
||||
|
||||
def forwarded_email_data(original_content = '')
|
||||
return {} if forwarded_message.blank?
|
||||
|
||||
data_handler = Messages::ForwardedMessageDataHandlerService.new(forwarded_message, email_data, params)
|
||||
data = data_handler.prepare_email_data
|
||||
formatted_info = data_handler.formatted_info
|
||||
|
||||
content_builder = Messages::ForwardedMessageContentBuilderService.new(
|
||||
formatted_info,
|
||||
forwarded_message,
|
||||
email_data
|
||||
)
|
||||
|
||||
process_email_content(data, content_builder, original_content)
|
||||
end
|
||||
|
||||
def forwarded_attachments
|
||||
forwarded_message.attachments if forwarded_message&.attachments&.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_email_content(data, content_builder, original_content)
|
||||
full_content = content_builder.formatted_content(original_content)
|
||||
process_text_content(data, original_content, full_content)
|
||||
process_html_content(data, content_builder, original_content, full_content)
|
||||
add_attachments(data)
|
||||
data
|
||||
end
|
||||
|
||||
def process_text_content(data, original_content, full_content)
|
||||
# Convert markdown in original_content to plain text
|
||||
original_plain = Messages::ForwardedMessageFormatterService.markdown_to_plain_text(original_content.to_s)
|
||||
|
||||
# Convert full_content to plain text if it contains markdown
|
||||
full_plain = Messages::ForwardedMessageFormatterService.markdown_to_plain_text(full_content)
|
||||
|
||||
data['text_content'].merge!(
|
||||
'quoted' => original_plain,
|
||||
'reply' => full_plain,
|
||||
'full' => full_plain
|
||||
)
|
||||
end
|
||||
|
||||
def process_html_content(data, content_builder, original_content, full_content)
|
||||
stripped = Messages::ForwardedMessageFormatterService.strip_markdown(original_content.to_s)
|
||||
html_full = content_builder.formatted_html_content(original_content)
|
||||
|
||||
data['html_content'].merge!(
|
||||
'quoted' => stripped,
|
||||
'reply' => full_content,
|
||||
'full' => html_full
|
||||
)
|
||||
end
|
||||
|
||||
def add_attachments(data)
|
||||
return if forwarded_message.attachments.blank?
|
||||
|
||||
data['attachments'] = forwarded_message.attachments.map(&:serializable_hash)
|
||||
end
|
||||
|
||||
def build_forwarded_attributes
|
||||
{ content_attributes: { forwarded_message_id: message_id, email: prepare_email_data } }
|
||||
end
|
||||
|
||||
def prepare_email_data
|
||||
data_handler = Messages::ForwardedMessageDataHandlerService.new(forwarded_message, email_data, params)
|
||||
data_handler.prepare_email_data
|
||||
end
|
||||
|
||||
def forwarded_message
|
||||
@forwarded_message ||= Message.find_by(id: message_id)
|
||||
end
|
||||
|
||||
def email_data
|
||||
@email_data ||= forwarded_message&.content_attributes&.dig('email')
|
||||
end
|
||||
|
||||
def basic_attributes
|
||||
{ content_attributes: { forwarded_message_id: message_id } }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Messages::ForwardedMessageContentBuilderService
|
||||
attr_reader :formatted_info, :forwarded_message, :email_data
|
||||
|
||||
def initialize(formatted_info, forwarded_message, email_data)
|
||||
@formatted_info = formatted_info
|
||||
@forwarded_message = forwarded_message
|
||||
@email_data = email_data
|
||||
end
|
||||
|
||||
def forwarded_header_text
|
||||
return '' unless formatted_info.values.any?
|
||||
|
||||
build_header_lines.compact.join("\n")
|
||||
end
|
||||
|
||||
def forwarded_body_text
|
||||
return forwarded_message.content.to_s if email_data.blank?
|
||||
|
||||
text = email_data.dig('text_content', 'full')
|
||||
html = email_data.dig('html_content', 'full')
|
||||
if text.present?
|
||||
text
|
||||
elsif html.present?
|
||||
ActionView::Base.full_sanitizer.sanitize(html)
|
||||
else
|
||||
forwarded_message.content.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def formatted_content(original_content = '')
|
||||
return original_content.to_s if forwarded_message.blank?
|
||||
|
||||
original_content.to_s + forwarded_header_text + forwarded_body_text
|
||||
end
|
||||
|
||||
def formatted_html_content(original_content = '')
|
||||
return original_content if forwarded_message.blank?
|
||||
|
||||
# Always use markdown conversion since it handles plain text correctly
|
||||
converted_content = Messages::ForwardedMessageFormatterService.convert_markdown_to_html(original_content)
|
||||
|
||||
html_builder = Messages::ForwardedMessageHtmlBuilderService.new(formatted_info, forwarded_message, email_data)
|
||||
html_builder.html_wrapper(converted_content)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_header_lines
|
||||
[
|
||||
"\n\n---------- Forwarded message ---------",
|
||||
("From: #{formatted_info[:from]}" if formatted_info[:from].present?),
|
||||
("Date: #{formatted_info[:date]}" if formatted_info[:date].present?),
|
||||
("Subject: #{formatted_info[:subject]}" if formatted_info[:subject].present?),
|
||||
("To: <#{formatted_info[:to]}>" if formatted_info[:to].present?),
|
||||
"\n"
|
||||
]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,92 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Messages::ForwardedMessageDataHandlerService
|
||||
attr_reader :forwarded_message, :email_data, :params
|
||||
|
||||
def initialize(forwarded_message, email_data, params = {})
|
||||
@forwarded_message = forwarded_message
|
||||
@email_data = email_data
|
||||
@params = params || {}
|
||||
end
|
||||
|
||||
def prepare_email_data
|
||||
initialize_email_data
|
||||
end
|
||||
|
||||
def formatted_info
|
||||
{
|
||||
from: inbox_email.to_s,
|
||||
date: Messages::ForwardedMessageFormatterService.format_date_string(email_date),
|
||||
subject: subject.to_s,
|
||||
to: recipient_email.to_s
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_email_data
|
||||
data = base_email_data
|
||||
add_content_fields(data)
|
||||
add_header_fields(data)
|
||||
data
|
||||
end
|
||||
|
||||
def base_email_data
|
||||
email_data.present? ? email_data.dup || {} : {}
|
||||
end
|
||||
|
||||
def add_content_fields(data)
|
||||
data['html_content'] ||= {}
|
||||
data['text_content'] ||= {}
|
||||
end
|
||||
|
||||
def add_header_fields(data)
|
||||
data['from'] = [email_from_inbox] # Always overwrite with inbox email
|
||||
data['to'] = Array(params[:to_emails]) if params && params[:to_emails].present?
|
||||
data['subject'] ||= subject
|
||||
data['date'] ||= Time.zone.now.to_s
|
||||
end
|
||||
|
||||
def inbox_email
|
||||
email_from_data || email_from_inbox
|
||||
end
|
||||
|
||||
def email_from_data
|
||||
return nil unless email_data_has_from?
|
||||
|
||||
from_field = email_data['from'].first.to_s
|
||||
Messages::ForwardedMessageFormatterService.parse_from_field(from_field)
|
||||
end
|
||||
|
||||
def email_data_has_from?
|
||||
email_data.present? &&
|
||||
email_data['from'].present? &&
|
||||
email_data['from'].first.present?
|
||||
end
|
||||
|
||||
def email_from_inbox
|
||||
inbox = forwarded_message&.conversation&.inbox
|
||||
return nil if inbox.blank? || inbox.channel_type != 'Channel::Email'
|
||||
|
||||
email = inbox.channel&.email
|
||||
return nil if email.blank?
|
||||
|
||||
email
|
||||
end
|
||||
|
||||
def recipient_email
|
||||
return email_data&.dig('to', 0) if email_data&.dig('to', 0).present?
|
||||
|
||||
forwarded_message&.content_attributes&.dig('to_emails', 0).presence
|
||||
end
|
||||
|
||||
def subject
|
||||
email_data&.dig('subject').presence ||
|
||||
forwarded_message&.conversation&.additional_attributes&.dig('subject').presence ||
|
||||
'No Subject'
|
||||
end
|
||||
|
||||
def email_date
|
||||
email_data&.dig('date').presence || Time.zone.now.to_s
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Messages::ForwardedMessageFormatterService
|
||||
def self.parse_from_field(from_field)
|
||||
return '' if from_field.blank?
|
||||
|
||||
from_field =~ /(.*)<(.*)>/ ? Regexp.last_match(2).strip : from_field
|
||||
end
|
||||
|
||||
def self.format_plain_text_to_html(text)
|
||||
ERB::Util.html_escape(text.to_s).gsub("\n", '<br>')
|
||||
end
|
||||
|
||||
def self.extract_email(from_field)
|
||||
return '' if from_field.blank?
|
||||
|
||||
from_field =~ /<(.*)>/ ? Regexp.last_match(1) : from_field
|
||||
end
|
||||
|
||||
def self.format_date_string(date_str)
|
||||
return '' if date_str.blank?
|
||||
|
||||
begin
|
||||
parsed_date = DateTime.now
|
||||
parsed_date.strftime('%a, %b %-d, %Y at %-l:%M %p')
|
||||
rescue StandardError
|
||||
date_str
|
||||
end
|
||||
end
|
||||
|
||||
def self.strip_markdown(text)
|
||||
return '' if text.blank?
|
||||
|
||||
# Convert markdown to HTML using CommonMarker
|
||||
html = CommonMarker.render_html(text, :DEFAULT)
|
||||
|
||||
# Strip HTML tags to get plain text
|
||||
ActionView::Base.full_sanitizer.sanitize(html)
|
||||
end
|
||||
|
||||
def self.convert_markdown_to_html(text)
|
||||
return '' if text.blank?
|
||||
|
||||
# Use CommonMarker with GitHub Flavored Markdown options
|
||||
options = [:GITHUB_PRE_LANG, :UNSAFE]
|
||||
extensions = [:table, :strikethrough, :autolink]
|
||||
|
||||
CommonMarker.render_html(text, options, extensions)
|
||||
end
|
||||
|
||||
# Convert text to plain text, stripping any markdown formatting
|
||||
def self.markdown_to_plain_text(text)
|
||||
return '' if text.blank?
|
||||
|
||||
# Simply strip any markdown by converting to HTML and sanitizing
|
||||
strip_markdown(text)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Messages::ForwardedMessageHtmlBuilderService
|
||||
attr_reader :formatted_info, :forwarded_message, :email_data
|
||||
|
||||
def initialize(formatted_info, forwarded_message, email_data)
|
||||
@formatted_info = formatted_info
|
||||
@forwarded_message = forwarded_message
|
||||
@email_data = email_data
|
||||
end
|
||||
|
||||
def html_wrapper(content)
|
||||
base = '<div dir="ltr">'
|
||||
if content.blank?
|
||||
"#{base}#{forwarded_header_html}#{forwarded_body_html}</div>"
|
||||
else
|
||||
"#{base}#{content}<br><br>#{forwarded_header_html}#{forwarded_body_html}</div>"
|
||||
end
|
||||
end
|
||||
|
||||
def forwarded_header_html
|
||||
from_value = formatted_info[:from].to_s
|
||||
email = Messages::ForwardedMessageFormatterService.extract_email(from_value)
|
||||
|
||||
[
|
||||
'<div class="gmail_quote gmail_quote_container">',
|
||||
'<div dir="ltr" class="gmail_attr">---------- Forwarded message ---------<br>',
|
||||
build_email_html(email),
|
||||
build_field_html('Date', formatted_info[:date]),
|
||||
build_field_html('Subject', formatted_info[:subject]),
|
||||
build_to_html(formatted_info[:to]),
|
||||
'</div><br><br>'
|
||||
].compact.join
|
||||
end
|
||||
|
||||
def forwarded_body_html
|
||||
# Return HTML content directly if available
|
||||
return email_data.dig('html_content', 'full') if email_data&.dig('html_content', 'full').present?
|
||||
|
||||
# Otherwise format content to HTML
|
||||
content = if email_data&.dig('text_content', 'full').present?
|
||||
email_data.dig('text_content', 'full')
|
||||
else
|
||||
forwarded_message.content.to_s
|
||||
end
|
||||
|
||||
# Always use markdown conversion since it handles plain text correctly
|
||||
Messages::ForwardedMessageFormatterService.convert_markdown_to_html(content)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_email_html(email)
|
||||
return nil if email.blank?
|
||||
|
||||
"From: <<a href=\"mailto:#{email}\">#{email}</a>><br>"
|
||||
end
|
||||
|
||||
def build_field_html(label, value)
|
||||
value.present? ? "#{label}: #{value}<br>" : nil
|
||||
end
|
||||
|
||||
def build_to_html(to_email)
|
||||
return nil if to_email.blank?
|
||||
|
||||
"To: <<a href=\"mailto:#{to_email}\">#{to_email}</a>><br>"
|
||||
end
|
||||
end
|
||||
+1
-165
@@ -1,6 +1,6 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Messages::ForwardedMessageBuilder do
|
||||
RSpec.describe Messages::ForwardedMessageBuilderService do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:conversation) { create(:conversation, inbox: inbox, account: account) }
|
||||
@@ -140,28 +140,6 @@ RSpec.describe Messages::ForwardedMessageBuilder do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#formatted_html_content' do
|
||||
context 'when forwarded message is blank' do
|
||||
it 'returns original content' do
|
||||
builder = described_class.new(999) # Non-existent message ID
|
||||
expect(builder.formatted_html_content('Original')).to eq('Original')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when forwarded message has email data' do
|
||||
it 'returns formatted HTML content with header and body' do
|
||||
builder = described_class.new(forwarded_message.id)
|
||||
result = builder.formatted_html_content('**Original**')
|
||||
|
||||
# Updated expectation to match CommonMarker output format
|
||||
expect(result).to include('<p><strong>Original</strong></p>')
|
||||
expect(result).to include('---------- Forwarded message ---------')
|
||||
expect(result).to include('Subject: Test Subject')
|
||||
expect(result).to include('<div>HTML content</div>') # Should include the HTML content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#forwarded_email_data' do
|
||||
context 'when forwarded message is blank' do
|
||||
it 'returns empty hash' do
|
||||
@@ -237,146 +215,4 @@ RSpec.describe Messages::ForwardedMessageBuilder do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'formatter methods' do
|
||||
context 'when using convert_markdown_to_html' do
|
||||
it 'converts bold markdown to HTML' do
|
||||
formatted = Messages::ForwardedMessageFormatter.convert_markdown_to_html('**Bold Text**')
|
||||
# Updated expectation to match CommonMarker output format
|
||||
expect(formatted).to include('<strong>Bold Text</strong>')
|
||||
end
|
||||
|
||||
it 'converts italic markdown to HTML' do
|
||||
formatted = Messages::ForwardedMessageFormatter.convert_markdown_to_html('*Italic Text*')
|
||||
expect(formatted).to include('<em>Italic Text</em>')
|
||||
end
|
||||
|
||||
it 'converts underscore italic markdown to HTML' do
|
||||
formatted = Messages::ForwardedMessageFormatter.convert_markdown_to_html('_Italic Text_')
|
||||
expect(formatted).to include('<em>Italic Text</em>')
|
||||
end
|
||||
|
||||
it 'handles multiple markdown elements' do
|
||||
formatted = Messages::ForwardedMessageFormatter.convert_markdown_to_html('**Bold** and _italic_')
|
||||
expect(formatted).to include('<strong>Bold</strong>')
|
||||
expect(formatted).to include('<em>italic</em>')
|
||||
end
|
||||
|
||||
it 'handles empty text' do
|
||||
expect(Messages::ForwardedMessageFormatter.convert_markdown_to_html('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil text' do
|
||||
expect(Messages::ForwardedMessageFormatter.convert_markdown_to_html(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using strip_markdown' do
|
||||
it 'strips bold markdown' do
|
||||
stripped = Messages::ForwardedMessageFormatter.strip_markdown('**Bold Text**')
|
||||
expect(stripped.strip).to eq('Bold Text')
|
||||
end
|
||||
|
||||
it 'strips italic markdown' do
|
||||
stripped = Messages::ForwardedMessageFormatter.strip_markdown('*Italic Text*')
|
||||
expect(stripped.strip).to eq('Italic Text')
|
||||
end
|
||||
|
||||
it 'strips underscore italic markdown' do
|
||||
stripped = Messages::ForwardedMessageFormatter.strip_markdown('_Italic Text_')
|
||||
expect(stripped.strip).to eq('Italic Text')
|
||||
end
|
||||
|
||||
it 'handles multiple markdown elements' do
|
||||
stripped = Messages::ForwardedMessageFormatter.strip_markdown('**Bold** and _italic_')
|
||||
# Updated expectation to handle trailing newline
|
||||
expect(stripped.strip).to eq('Bold and italic')
|
||||
end
|
||||
|
||||
it 'handles empty text' do
|
||||
expect(Messages::ForwardedMessageFormatter.strip_markdown('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil text' do
|
||||
expect(Messages::ForwardedMessageFormatter.strip_markdown(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using extract_email' do
|
||||
it 'extracts email from format "Name <email@example.com>"' do
|
||||
extracted = Messages::ForwardedMessageFormatter.extract_email('John Doe <john@example.com>')
|
||||
expect(extracted).to eq('john@example.com')
|
||||
end
|
||||
|
||||
it 'returns plain email as-is' do
|
||||
extracted = Messages::ForwardedMessageFormatter.extract_email('john@example.com')
|
||||
expect(extracted).to eq('john@example.com')
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(Messages::ForwardedMessageFormatter.extract_email('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(Messages::ForwardedMessageFormatter.extract_email(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using parse_from_field' do
|
||||
it 'extracts email from format "Name <email@example.com>"' do
|
||||
parsed = Messages::ForwardedMessageFormatter.parse_from_field('John Doe <john@example.com>')
|
||||
expect(parsed).to eq('john@example.com')
|
||||
end
|
||||
|
||||
it 'returns plain email as-is' do
|
||||
parsed = Messages::ForwardedMessageFormatter.parse_from_field('john@example.com')
|
||||
expect(parsed).to eq('john@example.com')
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(Messages::ForwardedMessageFormatter.parse_from_field('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(Messages::ForwardedMessageFormatter.parse_from_field(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using format_date_string' do
|
||||
it 'formats the date in a readable format' do
|
||||
# NOTE: We don't test the exact formatted output since it uses DateTime.now
|
||||
# which would be different for each test run
|
||||
result = Messages::ForwardedMessageFormatter.format_date_string('2025-04-29T14:29:07+05:30')
|
||||
expect(result).to match(/\w{3}, \w{3} \d+, \d{4} at \d+:\d+ [AP]M/)
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(Messages::ForwardedMessageFormatter.format_date_string('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(Messages::ForwardedMessageFormatter.format_date_string(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using format_plain_text_to_html' do
|
||||
it 'converts newlines to <br>' do
|
||||
result = Messages::ForwardedMessageFormatter.format_plain_text_to_html("Line 1\nLine 2")
|
||||
expect(result).to eq('Line 1<br>Line 2')
|
||||
end
|
||||
|
||||
it 'escapes HTML special characters' do
|
||||
result = Messages::ForwardedMessageFormatter.format_plain_text_to_html('<script>alert("XSS")</script>')
|
||||
expect(result).to eq('<script>alert("XSS")</script>')
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(Messages::ForwardedMessageFormatter.format_plain_text_to_html('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(Messages::ForwardedMessageFormatter.format_plain_text_to_html(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,144 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Messages::ForwardedMessageContentBuilderService do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:conversation) { create(:conversation, inbox: inbox, account: account) }
|
||||
|
||||
let(:formatted_info) do
|
||||
{
|
||||
from: 'sender@example.com',
|
||||
date: 'Wed, Apr 29, 2025 at 2:29 PM',
|
||||
subject: 'Test Subject',
|
||||
to: 'recipient@example.com'
|
||||
}
|
||||
end
|
||||
|
||||
let(:base_email_content) do
|
||||
{
|
||||
'html_content' => {
|
||||
'full' => '<div>HTML content</div>'
|
||||
},
|
||||
'text_content' => {
|
||||
'full' => 'Text content'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:forwarded_message) do
|
||||
create(:message, conversation: conversation, account: account,
|
||||
content_attributes: { email: base_email_content })
|
||||
end
|
||||
|
||||
let(:html_only_message) do
|
||||
create(:message, conversation: conversation, account: account,
|
||||
content_attributes: {
|
||||
email: {
|
||||
'html_content' => { 'full' => '<div>HTML only content</div>' },
|
||||
'text_content' => {}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
let(:content_builder) { described_class.new(formatted_info, forwarded_message, base_email_content) }
|
||||
let(:html_only_builder) { described_class.new(formatted_info, html_only_message, html_only_message.content_attributes[:email]) }
|
||||
let(:empty_builder) { described_class.new(formatted_info, forwarded_message, {}) }
|
||||
|
||||
describe '#forwarded_header_text' do
|
||||
it 'returns a formatted header with all information' do
|
||||
header = content_builder.forwarded_header_text
|
||||
|
||||
expect(header).to include('---------- Forwarded message ---------')
|
||||
expect(header).to include('From: sender@example.com')
|
||||
expect(header).to include('Date: Wed, Apr 29, 2025 at 2:29 PM')
|
||||
expect(header).to include('Subject: Test Subject')
|
||||
expect(header).to include('To: <recipient@example.com>')
|
||||
end
|
||||
|
||||
it 'returns a formatted header without missing fields' do
|
||||
empty_info = { from: '', date: '', subject: '', to: '' }
|
||||
builder = described_class.new(empty_info, forwarded_message, base_email_content)
|
||||
|
||||
expect(builder.forwarded_header_text).to include('---------- Forwarded message ---------')
|
||||
expect(builder.forwarded_header_text).not_to include('From: ')
|
||||
expect(builder.forwarded_header_text).not_to include('Date: ')
|
||||
expect(builder.forwarded_header_text).not_to include('Subject: ')
|
||||
expect(builder.forwarded_header_text).not_to include('To: ')
|
||||
end
|
||||
|
||||
it 'handles partial information' do
|
||||
partial_info = { from: 'sender@example.com', subject: 'Test Subject' }
|
||||
builder = described_class.new(partial_info, forwarded_message, base_email_content)
|
||||
header = builder.forwarded_header_text
|
||||
|
||||
expect(header).to include('From: sender@example.com')
|
||||
expect(header).to include('Subject: Test Subject')
|
||||
expect(header).not_to include('Date:')
|
||||
expect(header).not_to include('To:')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#forwarded_body_text' do
|
||||
it 'returns text content when available' do
|
||||
expect(content_builder.forwarded_body_text).to eq('Text content')
|
||||
end
|
||||
|
||||
it 'returns sanitized HTML content when only HTML is available' do
|
||||
expect(html_only_builder.forwarded_body_text).to include('HTML only content')
|
||||
end
|
||||
|
||||
it 'returns message content when no email data is available' do
|
||||
expect(empty_builder.forwarded_body_text).to eq(forwarded_message.content.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#formatted_content' do
|
||||
it 'returns original content concatenated with header and body' do
|
||||
result = content_builder.formatted_content('Original message')
|
||||
|
||||
expect(result).to start_with('Original message')
|
||||
expect(result).to include('---------- Forwarded message ---------')
|
||||
expect(result).to include('Text content')
|
||||
end
|
||||
|
||||
it 'returns just the original content when forwarded message is blank' do
|
||||
builder = described_class.new(formatted_info, nil, base_email_content)
|
||||
expect(builder.formatted_content('Original message')).to eq('Original message')
|
||||
end
|
||||
|
||||
it 'handles empty original content' do
|
||||
result = content_builder.formatted_content('')
|
||||
|
||||
expect(result).to include('---------- Forwarded message ---------')
|
||||
expect(result).to include('Text content')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#formatted_html_content' do
|
||||
it 'converts markdown in original content to HTML' do
|
||||
html_builder = instance_double(Messages::ForwardedMessageHtmlBuilderService)
|
||||
allow(Messages::ForwardedMessageHtmlBuilderService).to receive(:new)
|
||||
.with(formatted_info, forwarded_message, base_email_content)
|
||||
.and_return(html_builder)
|
||||
|
||||
allow(Messages::ForwardedMessageFormatterService).to receive(:convert_markdown_to_html)
|
||||
.with('**Original** message')
|
||||
.and_return('<p>Converted HTML</p>')
|
||||
|
||||
allow(html_builder).to receive(:html_wrapper)
|
||||
.with('<p>Converted HTML</p>')
|
||||
.and_return('<div>Wrapped content</div>')
|
||||
|
||||
result = content_builder.formatted_html_content('**Original** message')
|
||||
|
||||
expect(Messages::ForwardedMessageFormatterService).to have_received(:convert_markdown_to_html)
|
||||
.with('**Original** message')
|
||||
expect(result).to eq('<div>Wrapped content</div>')
|
||||
end
|
||||
|
||||
it 'returns original content when forwarded message is blank' do
|
||||
builder = described_class.new(formatted_info, nil, base_email_content)
|
||||
expect(builder.formatted_html_content('Original message')).to eq('Original message')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,180 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Messages::ForwardedMessageDataHandlerService do
|
||||
let(:account) { create(:account) }
|
||||
let(:email_channel) { create(:channel_email, account: account) }
|
||||
let(:email_inbox) { create(:inbox, channel: email_channel, account: account) }
|
||||
let(:conversation) { create(:conversation, inbox: email_inbox, account: account) }
|
||||
let(:regular_inbox) { create(:inbox, account: account) }
|
||||
let(:regular_conversation) { create(:conversation, inbox: regular_inbox, account: account) }
|
||||
|
||||
let(:base_email_content) do
|
||||
{
|
||||
'from' => ['John Doe <sender@example.com>'],
|
||||
'to' => ['recipient@example.com'],
|
||||
'subject' => 'Test Subject',
|
||||
'date' => '2025-04-29T14:29:07+05:30'
|
||||
}
|
||||
end
|
||||
|
||||
# Message with email data and from an email inbox
|
||||
let(:email_message) do
|
||||
create(:message, conversation: conversation, account: account,
|
||||
content_attributes: { email: base_email_content })
|
||||
end
|
||||
|
||||
# Message with email data but from a regular inbox
|
||||
let(:regular_message) do
|
||||
create(:message, conversation: regular_conversation, account: account,
|
||||
content_attributes: { email: base_email_content })
|
||||
end
|
||||
|
||||
# Message with no email data
|
||||
let(:message_no_email) { create(:message, conversation: conversation, account: account) }
|
||||
|
||||
describe '#prepare_email_data' do
|
||||
it 'returns empty hash structure when email_data is nil' do
|
||||
handler = described_class.new(message_no_email, nil)
|
||||
result = handler.prepare_email_data
|
||||
|
||||
expect(result).to be_a(Hash)
|
||||
expect(result['html_content']).to eq({})
|
||||
expect(result['text_content']).to eq({})
|
||||
expect(result).to have_key('date')
|
||||
expect(result).to have_key('subject')
|
||||
end
|
||||
|
||||
it 'preserves existing email data and adds required fields' do
|
||||
handler = described_class.new(email_message, base_email_content)
|
||||
result = handler.prepare_email_data
|
||||
|
||||
expect(result).to be_a(Hash)
|
||||
expect(result['html_content']).to eq({})
|
||||
expect(result['text_content']).to eq({})
|
||||
expect(result['from']).to be_present
|
||||
expect(result['to']).to eq(['recipient@example.com'])
|
||||
expect(result['subject']).to eq('Test Subject')
|
||||
expect(result['date']).to eq('2025-04-29T14:29:07+05:30')
|
||||
end
|
||||
|
||||
it 'adds to_emails from params if provided' do
|
||||
handler = described_class.new(email_message, base_email_content, { to_emails: 'new@example.com' })
|
||||
result = handler.prepare_email_data
|
||||
|
||||
expect(result['to']).to eq(['new@example.com'])
|
||||
end
|
||||
|
||||
it 'overwrites from field with inbox email' do
|
||||
handler = described_class.new(email_message, base_email_content)
|
||||
allow(handler).to receive(:email_from_inbox).and_return('inbox@example.com')
|
||||
|
||||
result = handler.prepare_email_data
|
||||
|
||||
expect(result['from']).to eq(['inbox@example.com'])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#formatted_info' do
|
||||
it 'returns hash with properly formatted fields' do
|
||||
allow(Messages::ForwardedMessageFormatterService).to receive(:format_date_string).and_return('Formatted Date')
|
||||
|
||||
handler = described_class.new(email_message, base_email_content)
|
||||
result = handler.formatted_info
|
||||
|
||||
expect(result[:from]).to be_present
|
||||
expect(result[:date]).to eq('Formatted Date')
|
||||
expect(result[:subject]).to eq('Test Subject')
|
||||
expect(result[:to]).to eq('recipient@example.com')
|
||||
|
||||
expect(Messages::ForwardedMessageFormatterService).to have_received(:format_date_string).with('2025-04-29T14:29:07+05:30')
|
||||
end
|
||||
|
||||
it 'works with missing email data' do
|
||||
# Create instance first, then stub its methods
|
||||
message_without_email = build(:message)
|
||||
handler = described_class.new(message_without_email, nil)
|
||||
|
||||
# Stub the methods on the specific instance
|
||||
allow(handler).to receive(:email_from_data).and_return(nil)
|
||||
allow(handler).to receive(:email_from_inbox).and_return(nil)
|
||||
|
||||
# For email_date, we should expect it to use Time.zone.now.to_s when email_data is nil
|
||||
# So we should mock the format_date_string to return a predictable value
|
||||
allow(Messages::ForwardedMessageFormatterService).to receive(:format_date_string).with(kind_of(String)).and_return('Fri, May 2, 2025 at 12:33 PM') # rubocop:disable Layout/LineLength
|
||||
|
||||
result = handler.formatted_info
|
||||
|
||||
expect(result[:from]).to eq('')
|
||||
expect(result[:date]).to eq('Fri, May 2, 2025 at 12:33 PM')
|
||||
expect(result[:subject]).to eq('No Subject')
|
||||
expect(result[:to]).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when private methods are called' do
|
||||
describe '#inbox_email' do
|
||||
it 'returns email from data when available' do
|
||||
handler = described_class.new(email_message, base_email_content)
|
||||
allow(handler).to receive(:email_from_data).and_return('from_data@example.com')
|
||||
allow(handler).to receive(:email_from_inbox).and_return('from_inbox@example.com')
|
||||
|
||||
result = handler.send(:inbox_email)
|
||||
|
||||
expect(result).to eq('from_data@example.com')
|
||||
end
|
||||
|
||||
it 'returns email from inbox when email data is not available' do
|
||||
handler = described_class.new(email_message, {})
|
||||
allow(handler).to receive(:email_from_data).and_return(nil)
|
||||
allow(handler).to receive(:email_from_inbox).and_return('from_inbox@example.com')
|
||||
|
||||
result = handler.send(:inbox_email)
|
||||
|
||||
expect(result).to eq('from_inbox@example.com')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#email_from_data' do
|
||||
it 'parses email address from from field' do
|
||||
handler = described_class.new(email_message, base_email_content)
|
||||
allow(Messages::ForwardedMessageFormatterService).to receive(:parse_from_field).and_return('parsed@example.com')
|
||||
|
||||
result = handler.send(:email_from_data)
|
||||
|
||||
expect(Messages::ForwardedMessageFormatterService).to have_received(:parse_from_field).with('John Doe <sender@example.com>')
|
||||
expect(result).to eq('parsed@example.com')
|
||||
end
|
||||
|
||||
it 'returns nil when from field is not available' do
|
||||
handler = described_class.new(email_message, {})
|
||||
result = handler.send(:email_from_data)
|
||||
|
||||
expect(result).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#subject' do
|
||||
it 'returns subject from email data when available' do
|
||||
handler = described_class.new(email_message, base_email_content)
|
||||
result = handler.send(:subject)
|
||||
|
||||
expect(result).to eq('Test Subject')
|
||||
end
|
||||
|
||||
it 'returns subject from conversation when available' do
|
||||
allow(conversation).to receive(:additional_attributes).and_return({ 'subject' => 'Conversation Subject' })
|
||||
handler = described_class.new(email_message, {})
|
||||
result = handler.send(:subject)
|
||||
|
||||
expect(result).to eq('Conversation Subject')
|
||||
end
|
||||
|
||||
it 'returns "No Subject" when no subject is available' do
|
||||
handler = described_class.new(message_no_email, nil)
|
||||
result = handler.send(:subject)
|
||||
|
||||
expect(result).to eq('No Subject')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,158 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Messages::ForwardedMessageFormatterService do
|
||||
describe '.convert_markdown_to_html' do
|
||||
it 'converts bold markdown to HTML' do
|
||||
formatted = described_class.convert_markdown_to_html('**Bold Text**')
|
||||
# Updated expectation to match CommonMarker output format
|
||||
expect(formatted).to include('<strong>Bold Text</strong>')
|
||||
end
|
||||
|
||||
it 'converts italic markdown to HTML' do
|
||||
formatted = described_class.convert_markdown_to_html('*Italic Text*')
|
||||
expect(formatted).to include('<em>Italic Text</em>')
|
||||
end
|
||||
|
||||
it 'converts underscore italic markdown to HTML' do
|
||||
formatted = described_class.convert_markdown_to_html('_Italic Text_')
|
||||
expect(formatted).to include('<em>Italic Text</em>')
|
||||
end
|
||||
|
||||
it 'handles multiple markdown elements' do
|
||||
formatted = described_class.convert_markdown_to_html('**Bold** and _italic_')
|
||||
expect(formatted).to include('<strong>Bold</strong>')
|
||||
expect(formatted).to include('<em>italic</em>')
|
||||
end
|
||||
|
||||
it 'handles empty text' do
|
||||
expect(described_class.convert_markdown_to_html('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil text' do
|
||||
expect(described_class.convert_markdown_to_html(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.strip_markdown' do
|
||||
it 'strips bold markdown' do
|
||||
stripped = described_class.strip_markdown('**Bold Text**')
|
||||
expect(stripped.strip).to eq('Bold Text')
|
||||
end
|
||||
|
||||
it 'strips italic markdown' do
|
||||
stripped = described_class.strip_markdown('*Italic Text*')
|
||||
expect(stripped.strip).to eq('Italic Text')
|
||||
end
|
||||
|
||||
it 'strips underscore italic markdown' do
|
||||
stripped = described_class.strip_markdown('_Italic Text_')
|
||||
expect(stripped.strip).to eq('Italic Text')
|
||||
end
|
||||
|
||||
it 'handles multiple markdown elements' do
|
||||
stripped = described_class.strip_markdown('**Bold** and _italic_')
|
||||
# Updated expectation to handle trailing newline
|
||||
expect(stripped.strip).to eq('Bold and italic')
|
||||
end
|
||||
|
||||
it 'handles empty text' do
|
||||
expect(described_class.strip_markdown('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil text' do
|
||||
expect(described_class.strip_markdown(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.extract_email' do
|
||||
it 'extracts email from format "Name <email@example.com>"' do
|
||||
extracted = described_class.extract_email('John Doe <john@example.com>')
|
||||
expect(extracted).to eq('john@example.com')
|
||||
end
|
||||
|
||||
it 'returns plain email as-is' do
|
||||
extracted = described_class.extract_email('john@example.com')
|
||||
expect(extracted).to eq('john@example.com')
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(described_class.extract_email('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(described_class.extract_email(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.parse_from_field' do
|
||||
it 'extracts email from format "Name <email@example.com>"' do
|
||||
parsed = described_class.parse_from_field('John Doe <john@example.com>')
|
||||
expect(parsed).to eq('john@example.com')
|
||||
end
|
||||
|
||||
it 'returns plain email as-is' do
|
||||
parsed = described_class.parse_from_field('john@example.com')
|
||||
expect(parsed).to eq('john@example.com')
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(described_class.parse_from_field('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(described_class.parse_from_field(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.format_date_string' do
|
||||
it 'formats the date in a readable format' do
|
||||
# NOTE: We don't test the exact formatted output since it uses DateTime.now
|
||||
# which would be different for each test run
|
||||
result = described_class.format_date_string('2025-04-29T14:29:07+05:30')
|
||||
expect(result).to match(/\w{3}, \w{3} \d+, \d{4} at \d+:\d+ [AP]M/)
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(described_class.format_date_string('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(described_class.format_date_string(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.format_plain_text_to_html' do
|
||||
it 'converts newlines to <br>' do
|
||||
result = described_class.format_plain_text_to_html("Line 1\nLine 2")
|
||||
expect(result).to eq('Line 1<br>Line 2')
|
||||
end
|
||||
|
||||
it 'escapes HTML special characters' do
|
||||
result = described_class.format_plain_text_to_html('<script>alert("XSS")</script>')
|
||||
expect(result).to eq('<script>alert("XSS")</script>')
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(described_class.format_plain_text_to_html('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(described_class.format_plain_text_to_html(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.markdown_to_plain_text' do
|
||||
it 'converts markdown text to plain text' do
|
||||
plain_text = described_class.markdown_to_plain_text('**Bold** and _italic_ text')
|
||||
expect(plain_text.strip).to eq('Bold and italic text')
|
||||
end
|
||||
|
||||
it 'handles empty string' do
|
||||
expect(described_class.markdown_to_plain_text('')).to eq('')
|
||||
end
|
||||
|
||||
it 'handles nil' do
|
||||
expect(described_class.markdown_to_plain_text(nil)).to eq('')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,156 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Messages::ForwardedMessageHtmlBuilderService do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:conversation) { create(:conversation, inbox: inbox, account: account) }
|
||||
|
||||
let(:formatted_info) do
|
||||
{
|
||||
from: 'sender@example.com',
|
||||
date: 'Wed, Apr 29, 2025 at 2:29 PM',
|
||||
subject: 'Test Subject',
|
||||
to: 'recipient@example.com'
|
||||
}
|
||||
end
|
||||
|
||||
let(:base_email_content) do
|
||||
{
|
||||
'html_content' => {
|
||||
'full' => '<div>HTML content</div>'
|
||||
},
|
||||
'text_content' => {
|
||||
'full' => 'Text content'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:forwarded_message) do
|
||||
create(:message, conversation: conversation, account: account,
|
||||
content_attributes: { email: base_email_content })
|
||||
end
|
||||
|
||||
let(:html_builder) { described_class.new(formatted_info, forwarded_message, base_email_content) }
|
||||
let(:empty_builder) { described_class.new({}, forwarded_message, {}) }
|
||||
|
||||
describe '#html_wrapper' do
|
||||
it 'wraps content with proper HTML structure' do
|
||||
result = html_builder.html_wrapper('<p>Test content</p>')
|
||||
|
||||
expect(result).to start_with('<div dir="ltr">')
|
||||
expect(result).to include('<p>Test content</p>')
|
||||
expect(result).to include('<br><br>')
|
||||
expect(result).to include('---------- Forwarded message ---------')
|
||||
expect(result).to end_with('</div>')
|
||||
end
|
||||
|
||||
it 'includes header and body even when content is blank' do
|
||||
result = html_builder.html_wrapper('')
|
||||
|
||||
expect(result).to start_with('<div dir="ltr">')
|
||||
expect(result).to include('---------- Forwarded message ---------')
|
||||
expect(result).to include('<br><br>')
|
||||
expect(result).to end_with('</div>')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#forwarded_header_html' do
|
||||
it 'formats header with all fields' do
|
||||
allow(Messages::ForwardedMessageFormatterService).to receive(:extract_email).and_return('sender@example.com')
|
||||
|
||||
result = html_builder.forwarded_header_html
|
||||
|
||||
expect(result).to include('gmail_quote_container')
|
||||
expect(result).to include('---------- Forwarded message ---------')
|
||||
expect(result).to include('From: <<a href="mailto:sender@example.com">sender@example.com</a>>')
|
||||
expect(result).to include('Date: Wed, Apr 29, 2025 at 2:29 PM')
|
||||
expect(result).to include('Subject: Test Subject')
|
||||
expect(result).to include('To: <<a href="mailto:recipient@example.com">recipient@example.com</a>>')
|
||||
|
||||
expect(Messages::ForwardedMessageFormatterService).to have_received(:extract_email).with('sender@example.com')
|
||||
end
|
||||
|
||||
it 'omits empty fields' do
|
||||
empty_info = { from: '', subject: 'Test Subject' }
|
||||
builder = described_class.new(empty_info, forwarded_message, base_email_content)
|
||||
|
||||
result = builder.forwarded_header_html
|
||||
|
||||
expect(result).to include('---------- Forwarded message ---------')
|
||||
expect(result).to include('Subject: Test Subject')
|
||||
expect(result).not_to include('From:')
|
||||
expect(result).not_to include('Date:')
|
||||
expect(result).not_to include('To:')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#forwarded_body_html' do
|
||||
it 'returns HTML content when available' do
|
||||
result = html_builder.forwarded_body_html
|
||||
|
||||
expect(result).to eq('<div>HTML content</div>')
|
||||
end
|
||||
|
||||
it 'converts text to HTML when HTML is not available' do
|
||||
text_only_content = { 'text_content' => { 'full' => 'Text only content' } }
|
||||
builder = described_class.new(formatted_info, forwarded_message, text_only_content)
|
||||
|
||||
allow(Messages::ForwardedMessageFormatterService).to receive(:convert_markdown_to_html).and_return('<p>Converted HTML</p>')
|
||||
|
||||
result = builder.forwarded_body_html
|
||||
|
||||
expect(Messages::ForwardedMessageFormatterService).to have_received(:convert_markdown_to_html).with('Text only content')
|
||||
expect(result).to eq('<p>Converted HTML</p>')
|
||||
end
|
||||
|
||||
it 'falls back to message content when no email data is available' do
|
||||
allow(forwarded_message).to receive(:content).and_return('Message content')
|
||||
|
||||
result = empty_builder.forwarded_body_html
|
||||
|
||||
expect(Messages::ForwardedMessageFormatterService).to receive(:convert_markdown_to_html).with('Message content')
|
||||
empty_builder.forwarded_body_html
|
||||
end
|
||||
end
|
||||
|
||||
describe 'private methods' do
|
||||
describe '#build_email_html' do
|
||||
it 'formats email with HTML markup' do
|
||||
result = html_builder.send(:build_email_html, 'test@example.com')
|
||||
|
||||
expect(result).to eq('From: <<a href="mailto:test@example.com">test@example.com</a>><br>')
|
||||
end
|
||||
|
||||
it 'returns nil for blank email' do
|
||||
expect(html_builder.send(:build_email_html, '')).to be_nil
|
||||
expect(html_builder.send(:build_email_html, nil)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#build_field_html' do
|
||||
it 'formats field with label and value' do
|
||||
result = html_builder.send(:build_field_html, 'Test', 'Value')
|
||||
|
||||
expect(result).to eq('Test: Value<br>')
|
||||
end
|
||||
|
||||
it 'returns nil for blank value' do
|
||||
expect(html_builder.send(:build_field_html, 'Test', '')).to be_nil
|
||||
expect(html_builder.send(:build_field_html, 'Test', nil)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#build_to_html' do
|
||||
it 'formats to field with HTML markup' do
|
||||
result = html_builder.send(:build_to_html, 'recipient@example.com')
|
||||
|
||||
expect(result).to eq('To: <<a href="mailto:recipient@example.com">recipient@example.com</a>><br>')
|
||||
end
|
||||
|
||||
it 'returns nil for blank email' do
|
||||
expect(html_builder.send(:build_to_html, '')).to be_nil
|
||||
expect(html_builder.send(:build_to_html, nil)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user