gitlab_markdown_helper_spec.rb 11.2 KB
Newer Older
R
Riyad Preukschas 已提交
1 2
require "spec_helper"

R
randx 已提交
3
describe GitlabMarkdownHelper do
R
Robert Speicher 已提交
4 5 6 7 8 9 10 11 12
  let!(:project) { create(:project) }

  let(:user)          { create(:user, name: 'gfm') }
  let(:commit)        { CommitDecorator.decorate(project.commit) }
  let(:issue)         { create(:issue, project: project) }
  let(:merge_request) { create(:merge_request, project: project) }
  let(:snippet)       { create(:snippet, project: project) }
  let(:member)        { project.users_projects.where(user_id: user).first }

R
Riyad Preukschas 已提交
13
  before do
R
Robert Speicher 已提交
14 15
    # Helper expects a @project instance variable
    @project = project
R
Riyad Preukschas 已提交
16 17 18
  end

  describe "#gfm" do
R
Robert Speicher 已提交
19 20 21 22 23
    it "should return unaltered text if project is nil" do
      actual = "Testing references: ##{issue.id}"

      gfm(actual).should_not == actual

24
      @project = nil
R
Robert Speicher 已提交
25 26
      gfm(actual).should == actual
    end
27

R
Robert Speicher 已提交
28 29 30 31 32 33
    it "should not alter non-references" do
      actual = expected = "_Please_ *stop* 'helping' and all the other b*$#%' you do."
      gfm(actual).should == expected
    end

    it "should not touch HTML entities" do
34
      @project.issues.stub(:where).with(id: '39').and_return([issue])
R
Robert Speicher 已提交
35 36 37 38 39 40
      actual = expected = "We'll accept good pull requests."
      gfm(actual).should == expected
    end

    it "should forward HTML options to links" do
      gfm("Fixed in #{commit.id}", class: "foo").should have_selector("a.gfm.foo")
41 42
    end

R
Riyad Preukschas 已提交
43
    describe "referencing a commit" do
R
Robert Speicher 已提交
44 45
      let(:expected) { project_commit_path(project, commit) }

R
Riyad Preukschas 已提交
46
      it "should link using a full id" do
R
Robert Speicher 已提交
47 48
        actual = "Reverts #{commit.id}"
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
49 50 51
      end

      it "should link using a short id" do
R
Robert Speicher 已提交
52 53 54 55 56 57 58
        actual = "Backported from #{commit.short_id(6)}"
        gfm(actual).should match(expected)
      end

      it "should link with adjacent text" do
        actual = "Reverted (see #{commit.id})"
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
59 60
      end

R
Robert Speicher 已提交
61 62 63 64
      it "should keep whitespace intact" do
        actual   = "Changes #{commit.id} dramatically"
        expected = /Changes <a.+>#{commit.id}<\/a> dramatically/
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
65 66 67
      end

      it "should not link with an invalid id" do
R
Robert Speicher 已提交
68 69 70 71 72 73 74 75 76 77 78 79
        actual = expected = "What happened in #{commit.id.reverse}"
        gfm(actual).should == expected
      end

      it "should include a title attribute" do
        actual = "Reverts #{commit.id}"
        gfm(actual).should match(/title="#{commit.link_title}"/)
      end

      it "should include standard gfm classes" do
        actual = "Reverts #{commit.id}"
        gfm(actual).should match(/class="\s?gfm gfm-commit\s?"/)
R
Riyad Preukschas 已提交
80 81 82 83
      end
    end

    describe "referencing a team member" do
R
Robert Speicher 已提交
84 85
      let(:actual)   { "@#{user.name} you are right." }
      let(:expected) { project_team_member_path(project, member) }
R
Riyad Preukschas 已提交
86

R
Robert Speicher 已提交
87 88
      before do
        project.users << user
R
Riyad Preukschas 已提交
89 90
      end

R
Robert Speicher 已提交
91 92 93
      it "should link using a simple name" do
        gfm(actual).should match(expected)
      end
R
Riyad Preukschas 已提交
94

R
Robert Speicher 已提交
95 96 97
      it "should link using a name with dots" do
        user.update_attributes(name: "alphA.Beta")
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
98 99 100
      end

      it "should link using name with underscores" do
R
Robert Speicher 已提交
101 102
        user.update_attributes(name: "ping_pong_king")
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
103 104
      end

R
Robert Speicher 已提交
105 106 107 108
      it "should link with adjacent text" do
        actual = "Mail the admin (@gfm)"
        gfm(actual).should match(expected)
      end
R
Riyad Preukschas 已提交
109

R
Robert Speicher 已提交
110 111 112 113
      it "should keep whitespace intact" do
        actual   = "Yes, @#{user.name} is right."
        expected = /Yes, <a.+>@#{user.name}<\/a> is right/
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
114 115
      end

R
Robert Speicher 已提交
116 117 118
      it "should not link with an invalid id" do
        actual = expected = "@#{user.name.reverse} you are right."
        gfm(actual).should == expected
R
Riyad Preukschas 已提交
119 120
      end

R
Robert Speicher 已提交
121 122
      it "should include standard gfm classes" do
        gfm(actual).should match(/class="\s?gfm gfm-team_member\s?"/)
R
Riyad Preukschas 已提交
123 124 125
      end
    end

R
Robert Speicher 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138 139
    # Shared examples for referencing an object
    #
    # Expects the following attributes to be available in the example group:
    #
    # - object    - The object itself
    # - reference - The object reference string (e.g., #1234, $1234, !1234)
    #
    # Currently limited to Snippets, Issues and MergeRequests
    shared_examples 'referenced object' do
      let(:actual)   { "Reference to #{reference}" }
      let(:expected) { polymorphic_path([project, object]) }

      it "should link using a valid id" do
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
140 141
      end

R
Robert Speicher 已提交
142 143 144
      it "should link with adjacent text" do
        # Wrap the reference in parenthesis
        gfm(actual.gsub(reference, "(#{reference})")).should match(expected)
R
Riyad Preukschas 已提交
145

R
Robert Speicher 已提交
146 147
        # Append some text to the end of the reference
        gfm(actual.gsub(reference, "#{reference}, right?")).should match(expected)
R
Riyad Preukschas 已提交
148 149
      end

R
Robert Speicher 已提交
150 151 152 153
      it "should keep whitespace intact" do
        actual   = "Referenced #{reference} already."
        expected = /Referenced <a.+>[^\s]+<\/a> already/
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
154 155
      end

R
Robert Speicher 已提交
156 157 158 159
      it "should not link with an invalid id" do
        # Modify the reference string so it's still parsed, but is invalid
        reference.gsub!(/^(.)(\d+)$/, '\1' + ('\2' * 2))
        gfm(actual).should == actual
R
Riyad Preukschas 已提交
160 161
      end

R
Robert Speicher 已提交
162 163 164
      it "should include a title attribute" do
        title = "#{object.class.to_s.titlecase}: #{object.title}"
        gfm(actual).should match(/title="#{title}"/)
R
Riyad Preukschas 已提交
165 166
      end

R
Robert Speicher 已提交
167 168 169
      it "should include standard gfm classes" do
        css = object.class.to_s.underscore
        gfm(actual).should match(/class="\s?gfm gfm-#{css}\s?"/)
R
Riyad Preukschas 已提交
170
      end
R
Robert Speicher 已提交
171
    end
R
Riyad Preukschas 已提交
172

R
Robert Speicher 已提交
173 174 175
    describe "referencing an issue" do
      let(:object)    { issue }
      let(:reference) { "##{issue.id}" }
R
Riyad Preukschas 已提交
176

R
Robert Speicher 已提交
177 178
      include_examples 'referenced object'
    end
R
Riyad Preukschas 已提交
179

R
Robert Speicher 已提交
180 181 182 183 184
    describe "referencing a merge request" do
      let(:object)    { merge_request }
      let(:reference) { "!#{merge_request.id}" }

      include_examples 'referenced object'
R
Riyad Preukschas 已提交
185 186 187
    end

    describe "referencing a snippet" do
R
Robert Speicher 已提交
188 189
      let(:object)    { snippet }
      let(:reference) { "$#{snippet.id}" }
R
Riyad Preukschas 已提交
190

R
Robert Speicher 已提交
191 192
      include_examples 'referenced object'
    end
R
Riyad Preukschas 已提交
193

R
Robert Speicher 已提交
194 195 196 197 198 199
    describe "referencing multiple objects" do
      let(:actual) { "!#{merge_request.id} -> #{commit.id} -> ##{issue.id}" }

      it "should link to the merge request" do
        expected = project_merge_request_path(project, merge_request)
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
200 201
      end

R
Robert Speicher 已提交
202 203 204
      it "should link to the commit" do
        expected = project_commit_path(project, commit)
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
205 206
      end

R
Robert Speicher 已提交
207 208 209
      it "should link to the issue" do
        expected = project_issue_path(project, issue)
        gfm(actual).should match(expected)
R
Riyad Preukschas 已提交
210 211
      end
    end
R
Robert Speicher 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

    describe "emoji" do
      it "matches at the start of a string" do
        gfm(":+1:").should match(/<img/)
      end

      it "matches at the end of a string" do
        gfm("This gets a :-1:").should match(/<img/)
      end

      it "matches with adjacent text" do
        gfm("+1 (:+1:)").should match(/<img/)
      end

      it "has a title attribute" do
        gfm(":-1:").should match(/title=":-1:"/)
      end

      it "has an alt attribute" do
        gfm(":-1:").should match(/alt=":-1:"/)
      end

      it "has an emoji class" do
        gfm(":+1:").should match('class="emoji"')
      end

238 239 240 241 242 243
      it "sets height and width" do
        actual = gfm(":+1:")
        actual.should match(/width="20"/)
        actual.should match(/height="20"/)
      end

R
Robert Speicher 已提交
244 245 246 247 248 249 250
      it "keeps whitespace intact" do
        gfm("This deserves a :+1: big time.").should match(/deserves a <img.+\/> big time/)
      end

      it "ignores invalid emoji" do
        gfm(":invalid-emoji:").should_not match(/<img/)
      end
251 252 253 254 255

      it "should work independet of reference links (i.e. without @project being set)" do
        @project = nil
        gfm(":+1:").should match(/<img/)
      end
R
Robert Speicher 已提交
256
    end
R
Robert Speicher 已提交
257
  end
R
Riyad Preukschas 已提交
258

R
Robert Speicher 已提交
259 260 261
  describe "#link_to_gfm" do
    let(:commit_path) { project_commit_path(project, commit) }
    let(:issues)      { create_list(:issue, 2, project: project) }
R
Riyad Preukschas 已提交
262

R
Robert Speicher 已提交
263 264
    it "should handle references nested in links with all the text" do
      actual = link_to_gfm("This should finally fix ##{issues[0].id} and ##{issues[1].id} for real", commit_path)
R
Riyad Preukschas 已提交
265

R
Robert Speicher 已提交
266 267 268
      # Break the result into groups of links with their content, without
      # closing tags
      groups = actual.split("</a>")
R
Riyad Preukschas 已提交
269

R
Robert Speicher 已提交
270 271 272
      # Leading commit link
      groups[0].should match(/href="#{commit_path}"/)
      groups[0].should match(/This should finally fix $/)
R
Riyad Preukschas 已提交
273

R
Robert Speicher 已提交
274 275 276
      # First issue link
      groups[1].should match(/href="#{project_issue_path(project, issues[0])}"/)
      groups[1].should match(/##{issues[0].id}$/)
277

R
Robert Speicher 已提交
278 279 280
      # Internal commit link
      groups[2].should match(/href="#{commit_path}"/)
      groups[2].should match(/ and /)
281

R
Robert Speicher 已提交
282 283 284 285 286 287 288
      # Second issue link
      groups[3].should match(/href="#{project_issue_path(project, issues[1])}"/)
      groups[3].should match(/##{issues[1].id}$/)

      # Trailing commit link
      groups[4].should match(/href="#{commit_path}"/)
      groups[4].should match(/ for real$/)
289 290 291
    end

    it "should forward HTML options" do
R
Robert Speicher 已提交
292 293
      actual = link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo')
      actual.should have_selector 'a.gfm.gfm-commit.foo'
294
    end
295 296 297 298 299

    it "escapes HTML passed in as the body" do
      actual = "This is a <h1>test</h1> - see ##{issues[0].id}"
      link_to_gfm(actual, commit_path).should match('&lt;h1&gt;test&lt;/h1&gt;')
    end
300
  end
301 302 303

  describe "#markdown" do
    it "should handle references in paragraphs" do
304 305 306
      actual = "\n\nLorem ipsum dolor sit amet. #{commit.id} Nam pulvinar sapien eget.\n"
      expected = project_commit_path(project, commit)
      markdown(actual).should match(expected)
307 308 309
    end

    it "should handle references in headers" do
R
Robert Speicher 已提交
310 311 312 313
      actual = "\n# Working around ##{issue.id}\n## Apply !#{merge_request.id}"

      markdown(actual).should match(%r{<h1[^<]*>Working around <a.+>##{issue.id}</a></h1>})
      markdown(actual).should match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.id}</a></h2>})
314 315 316
    end

    it "should handle references in lists" do
R
Robert Speicher 已提交
317 318 319 320 321 322
      project.users << user

      actual = "\n* dark: ##{issue.id}\n* light by @#{member.user_name}"

      markdown(actual).should match(%r{<li>dark: <a.+>##{issue.id}</a></li>})
      markdown(actual).should match(%r{<li>light by <a.+>@#{member.user_name}</a></li>})
323 324 325
    end

    it "should handle references in <em>" do
R
Robert Speicher 已提交
326 327 328
      actual = "Apply _!#{merge_request.id}_ ASAP"

      markdown(actual).should match(%r{Apply <em><a.+>!#{merge_request.id}</a></em>})
329 330 331
    end

    it "should leave code blocks untouched" do
R
Robert Speicher 已提交
332
      markdown("\n    some code from $#{snippet.id}\n    here too\n").should == "<div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre>\n</div>\n"
333

R
Robert Speicher 已提交
334
      markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should == "<div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre>\n</div>\n"
335 336 337
    end

    it "should leave inline code untouched" do
R
Robert Speicher 已提交
338
      markdown("\nDon't use `$#{snippet.id}` here.\n").should == "<p>Don&#39;t use <code>$#{snippet.id}</code> here.</p>\n"
339 340
    end
  end
R
Riyad Preukschas 已提交
341
end