FIX: Handle poll titles when headings are present (#10832)

FIX: Handle poll titles when headings are present (#10832)

Poll markdown processing failed when there were any heading elements preceding a poll.

(Issue originally reported in https://github.com/discourse/discourse/commit/babbebfb3571b09bfe6600bbe0921ded3c29fb3e#commitcomment-42983768)

diff --git a/plugins/poll/assets/javascripts/lib/discourse-markdown/poll.js.es6 b/plugins/poll/assets/javascripts/lib/discourse-markdown/poll.js.es6
index b098a0b..da28392 100644
--- a/plugins/poll/assets/javascripts/lib/discourse-markdown/poll.js.es6
+++ b/plugins/poll/assets/javascripts/lib/discourse-markdown/poll.js.es6
@@ -81,18 +81,25 @@ function invalidPoll(state, tag) {
   token.content = "[/" + tag + "]";
 }
 
-function getTitle(tokens) {
-  const open = tokens.findIndex((token) => token.type === "heading_open");
-  const close = tokens.findIndex((token) => token.type === "heading_close");
+function getTitle(tokens, startToken) {
+  const startIndex = tokens.indexOf(startToken);
+
+  if (startIndex === -1) {
+    return;
+  }
+
+  const pollTokens = tokens.slice(startIndex);
+  const open = pollTokens.findIndex((token) => token.type === "heading_open");
+  const close = pollTokens.findIndex((token) => token.type === "heading_close");
 
   if (open === -1 || close === -1) {
     return;
   }
 
-  const titleTokens = tokens.slice(open + 1, close);
+  const titleTokens = pollTokens.slice(open + 1, close);
 
   // Remove the heading element
-  tokens.splice(open, close - open + 1);
+  tokens.splice(startIndex + open, close - open + 1);
 
   return titleTokens;
 }
@@ -108,7 +115,7 @@ const rule = {
   },
 
   after: function (state, openToken, raw) {
-    const titleTokens = getTitle(state.tokens);
+    const titleTokens = getTitle(state.tokens, openToken);
     let items = getListItems(state.tokens, openToken);
 
     if (!items) {
diff --git a/plugins/poll/spec/lib/pretty_text_spec.rb b/plugins/poll/spec/lib/pretty_text_spec.rb
index b76d9c3..d887b10 100644
--- a/plugins/poll/spec/lib/pretty_text_spec.rb
+++ b/plugins/poll/spec/lib/pretty_text_spec.rb
@@ -169,4 +169,49 @@ describe PrettyText do
       </div>
     HTML
   end
+
+  it "does not break when there are headings before/after a poll with a title" do
+    cooked = PrettyText.cook <<~MD
+      # Pre-heading
+
+      [poll]
+      # What's your favorite *berry*? :wink: https://google.com/
+      * Strawberry
+      * Raspberry
+      * Blueberry
+      [/poll]
+
+      # Post-heading
+    MD
+
+    expect(cooked).to include(<<~HTML)
+      <div class="poll-title">What’s your favorite <em>berry</em>? <img src="/images/emoji/twitter/wink.png?v=9" title=":wink:" class="emoji" alt=":wink:"> <a href="https://google.com/" rel="noopener nofollow ugc">https://google.com/</a>
+      </div>
+    HTML
+
+    expect(cooked).to include("<h1>Pre-heading</h1>")
+    expect(cooked).to include("<h1>Post-heading</h1>")
+  end
+
+  it "does not break when there are headings before/after a poll without a title" do
+    cooked = PrettyText.cook <<~MD
+      # Pre-heading
+
+      [poll]
+      * Strawberry
+      * Raspberry
+      * Blueberry
+      [/poll]
+
+      # Post-heading
+    MD
+
+    expect(cooked).to_not include('<div class="poll-title">')
+    expect(cooked).to include(<<~HTML)
+      <div class="poll" data-poll-status="open" data-poll-name="poll">
+    HTML
+
+    expect(cooked).to include("<h1>Pre-heading</h1>")
+    expect(cooked).to include("<h1>Post-heading</h1>")
+  end
 end

GitHub sha: 5c3f1202

This commit appears in #10832 which was approved by ZogStriP. It was merged by CvX.

This commit has been mentioned on Discourse Meta. There might be relevant details there:

https://meta.discourse.org/t/polls-stop-working-correctly/166324/9