Compare commits
1 Commits
develop
...
fix/CW-806
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ac95ce607 |
@@ -42,7 +42,7 @@ class Messages::MarkdownRendererService
|
||||
|
||||
def render_html
|
||||
markdown_renderer = BaseMarkdownRenderer.new
|
||||
doc = CommonMarker.render_doc(@content, :DEFAULT, [:strikethrough])
|
||||
doc = CommonMarker.render_doc(MarkdownSetextEscape.call(@content), :DEFAULT, [:strikethrough])
|
||||
markdown_renderer.render(doc)
|
||||
end
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ class ChatwootMarkdownRenderer
|
||||
|
||||
def render_message(hardbreaks: false)
|
||||
markdown_renderer = BaseMarkdownRenderer.new(options: hardbreaks ? [:HARDBREAKS] : :DEFAULT)
|
||||
doc = CommonMarker.render_doc(@content, :DEFAULT, [:strikethrough, :autolink])
|
||||
doc = CommonMarker.render_doc(MarkdownSetextEscape.call(@content), :DEFAULT, [:strikethrough, :autolink])
|
||||
html = markdown_renderer.render(doc)
|
||||
render_as_html_safe(html)
|
||||
end
|
||||
|
||||
15
lib/markdown_setext_escape.rb
Normal file
15
lib/markdown_setext_escape.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# cmark parses a dashes/equals line sitting directly under a hard-break
|
||||
# backslash ("\" + newline) as a setext heading underline: the dashes vanish
|
||||
# into markup and the stray "\" renders as heading text. The reply editor
|
||||
# serializes an empty line above the "--" signature delimiter exactly that
|
||||
# way. Escaping the underline keeps it literal text (matching the dashboard,
|
||||
# which disables setext parsing), while intentional setext headings — no
|
||||
# trailing backslash — stay untouched. Stored content can be CRLF, so both
|
||||
# line endings are handled.
|
||||
module MarkdownSetextEscape
|
||||
UNDERLINE_AFTER_HARD_BREAK = /\\\r?\n(?=(?:-+|=+)[ \t]*\r?(?:\n|\z))/
|
||||
|
||||
def self.call(content)
|
||||
content.gsub(UNDERLINE_AFTER_HARD_BREAK) { |hard_break| "#{hard_break}\\" }
|
||||
end
|
||||
end
|
||||
@@ -84,6 +84,14 @@ RSpec.describe ChatwootMarkdownRenderer do
|
||||
expect(renderer.render_message.to_s).to eq("<p>Visit <a href=\"https://example.com\">https://example.com</a> for details</p>\n")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an empty line above the signature delimiter' do
|
||||
let(:markdown_content) { "Answer\n\n\\\n--\n\nRegards" }
|
||||
|
||||
it 'keeps the delimiter and the empty line instead of rendering a setext heading' do
|
||||
expect(rendered_message.to_s).to eq("<p>Answer</p>\n<p><br />\n--</p>\n<p>Regards</p>\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#render_markdown_to_plain_text' do
|
||||
|
||||
48
spec/lib/markdown_setext_escape_spec.rb
Normal file
48
spec/lib/markdown_setext_escape_spec.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe MarkdownSetextEscape do
|
||||
describe '.call' do
|
||||
it 'escapes the delimiter when an empty line sits directly above it' do
|
||||
content = "Answer\n\n\\\n--\n\nRegards"
|
||||
expect(described_class.call(content)).to eq("Answer\n\n\\\n\\--\n\nRegards")
|
||||
end
|
||||
|
||||
it 'handles CRLF line endings' do
|
||||
content = "Answer\r\n\r\n\\\r\n--\r\n\r\nRegards"
|
||||
expect(described_class.call(content)).to eq("Answer\r\n\r\n\\\r\n\\--\r\n\r\nRegards")
|
||||
end
|
||||
|
||||
it 'escapes equals underlines the same way' do
|
||||
expect(described_class.call("Answer\n\n\\\n==\n\nRegards")).to eq("Answer\n\n\\\n\\==\n\nRegards")
|
||||
end
|
||||
|
||||
it 'escapes only the underline when several empty lines precede the delimiter' do
|
||||
content = "Answer\n\n\\\n\\\n--\n\nRegards"
|
||||
expect(described_class.call(content)).to eq("Answer\n\n\\\n\\\n\\--\n\nRegards")
|
||||
end
|
||||
|
||||
it 'escapes a delimiter at the end of the content' do
|
||||
expect(described_class.call("Answer\n\n\\\n--")).to eq("Answer\n\n\\\n\\--")
|
||||
end
|
||||
|
||||
it 'leaves a plain delimiter without a preceding empty line untouched' do
|
||||
content = "Answer\n\n--\n\nRegards"
|
||||
expect(described_class.call(content)).to eq(content)
|
||||
end
|
||||
|
||||
it 'leaves hard breaks followed by regular text untouched' do
|
||||
content = "Thanks \\\nSivin | Chatwoot"
|
||||
expect(described_class.call(content)).to eq(content)
|
||||
end
|
||||
|
||||
it 'leaves intentional setext headings untouched' do
|
||||
content = "Title\n---\n\nBody"
|
||||
expect(described_class.call(content)).to eq(content)
|
||||
end
|
||||
|
||||
it 'leaves thematic breaks untouched' do
|
||||
content = "para\n\n---\n\npara2"
|
||||
expect(described_class.call(content)).to eq(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -362,6 +362,13 @@ RSpec.describe Messages::MarkdownRendererService, type: :service do
|
||||
result = described_class.new(content, channel_type).render
|
||||
expect(result).to include('<del>strikethrough text</del>')
|
||||
end
|
||||
|
||||
it 'keeps the signature delimiter when an empty line sits above it' do
|
||||
content = "Answer\n\n\\\n--\n\nRegards"
|
||||
result = described_class.new(content, channel_type).render
|
||||
expect(result).to include("<p><br />\n--</p>")
|
||||
expect(result).not_to include('<h2>')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when channel is Channel::WebWidget' do
|
||||
|
||||
Reference in New Issue
Block a user