Commit ef24576fc2239935d2d7b553e7d55674abf4eb4c

Authored by Robert Speicher
1 parent 40d61910

Redesign gfm helper specs

Should now be much clearer about what each spec is actually testing.
For example, instead of testing stuff like link classes and titles in
every single call, we only test those things once, in their own specs.
Showing 1 changed file with 186 additions and 130 deletions   Show diff stats
spec/helpers/gitlab_markdown_helper_spec.rb
1 1 require "spec_helper"
2 2  
3 3 describe GitlabMarkdownHelper do
  4 + let!(:project) { create(:project) }
  5 +
  6 + let(:user) { create(:user, name: 'gfm') }
  7 + let(:commit) { CommitDecorator.decorate(project.commit) }
  8 + let(:issue) { create(:issue, project: project) }
  9 + let(:merge_request) { create(:merge_request, project: project) }
  10 + let(:snippet) { create(:snippet, project: project) }
  11 + let(:member) { project.users_projects.where(user_id: user).first }
  12 +
4 13 before do
5   - @project = Factory(:project)
6   - @commit = @project.repo.commits.first.parents.first
7   - @commit = CommitDecorator.decorate(Commit.new(@commit))
8   - @other_project = Factory :project, path: "OtherPath", code: "OtherCode"
9   - @fake_user = Factory :user, name: "fred"
  14 + # Helper expects a @project instance variable
  15 + @project = project
10 16 end
11 17  
12 18 describe "#gfm" do
13   - it "should return text if @project is not set" do
  19 + it "should return unaltered text if project is nil" do
  20 + actual = "Testing references: ##{issue.id}"
  21 +
  22 + gfm(actual).should_not == actual
  23 +
14 24 @project = nil
  25 + gfm(actual).should == actual
  26 + end
15 27  
16   - gfm("foo").should == "foo"
  28 + it "should not alter non-references" do
  29 + actual = expected = "_Please_ *stop* 'helping' and all the other b*$#%' you do."
  30 + gfm(actual).should == expected
  31 + end
  32 +
  33 + it "should not touch HTML entities" do
  34 + actual = expected = "We'll accept good pull requests."
  35 + gfm(actual).should == expected
  36 + end
  37 +
  38 + it "should forward HTML options to links" do
  39 + gfm("Fixed in #{commit.id}", class: "foo").should have_selector("a.gfm.foo")
17 40 end
18 41  
19 42 describe "referencing a commit" do
  43 + let(:expected) { project_commit_path(project, commit) }
  44 +
20 45 it "should link using a full id" do
21   - gfm("Reverts changes from #{@commit.id}").should == "Reverts changes from #{link_to @commit.id, project_commit_path(@project, id: @commit.id), title: "Commit: #{@commit.author_name} - #{@commit.title}", class: "gfm gfm-commit "}"
  46 + actual = "Reverts #{commit.id}"
  47 + gfm(actual).should match(expected)
22 48 end
23 49  
24 50 it "should link using a short id" do
25   - gfm("Backported from #{@commit.id[0, 6]}").should == "Backported from #{link_to @commit.id[0, 6], project_commit_path(@project, id: @commit.id), title: "Commit: #{@commit.author_name} - #{@commit.title}", class: "gfm gfm-commit "}"
  51 + actual = "Backported from #{commit.short_id(6)}"
  52 + gfm(actual).should match(expected)
  53 + end
  54 +
  55 + it "should link with adjacent text" do
  56 + actual = "Reverted (see #{commit.id})"
  57 + gfm(actual).should match(expected)
26 58 end
27 59  
28   - it "should link with adjecent text" do
29   - gfm("Reverted (see #{@commit.id})").should == "Reverted (see #{link_to @commit.id, project_commit_path(@project, id: @commit.id), title: "Commit: #{@commit.author_name} - #{@commit.title}", class: "gfm gfm-commit "})"
  60 + it "should keep whitespace intact" do
  61 + actual = "Changes #{commit.id} dramatically"
  62 + expected = /Changes <a.+>#{commit.id}<\/a> dramatically/
  63 + gfm(actual).should match(expected)
30 64 end
31 65  
32 66 it "should not link with an invalid id" do
33   - gfm("What happened in 12345678?").should == "What happened in 12345678?"
  67 + actual = expected = "What happened in #{commit.id.reverse}"
  68 + gfm(actual).should == expected
  69 + end
  70 +
  71 + it "should include a title attribute" do
  72 + actual = "Reverts #{commit.id}"
  73 + gfm(actual).should match(/title="#{commit.link_title}"/)
  74 + end
  75 +
  76 + it "should include standard gfm classes" do
  77 + actual = "Reverts #{commit.id}"
  78 + gfm(actual).should match(/class="\s?gfm gfm-commit\s?"/)
34 79 end
35 80 end
36 81  
37 82 describe "referencing a team member" do
38   - it "should link using a simple name" do
39   - user = Factory :user, name: "barry"
40   - @project.users << user
41   - member = @project.users_projects.where(user_id: user).first
  83 + let(:actual) { "@#{user.name} you are right." }
  84 + let(:expected) { project_team_member_path(project, member) }
42 85  
43   - gfm("@#{user.name} you are right").should == "#{link_to "@#{user.name}", project_team_member_path(@project, member), class: "gfm gfm-team_member "} you are right"
  86 + before do
  87 + project.users << user
44 88 end
45 89  
46   - it "should link using a name with dots" do
47   - user = Factory :user, name: "alphA.Beta"
48   - @project.users << user
49   - member = @project.users_projects.where(user_id: user).first
  90 + it "should link using a simple name" do
  91 + gfm(actual).should match(expected)
  92 + end
50 93  
51   - gfm("@#{user.name} you are right").should == "#{link_to "@#{user.name}", project_team_member_path(@project, member), class: "gfm gfm-team_member "} you are right"
  94 + it "should link using a name with dots" do
  95 + user.update_attributes(name: "alphA.Beta")
  96 + gfm(actual).should match(expected)
52 97 end
53 98  
54 99 it "should link using name with underscores" do
55   - user = Factory :user, name: "ping_pong_king"
56   - @project.users << user
57   - member = @project.users_projects.where(user_id: user).first
58   -
59   - gfm("@#{user.name} you are right").should == "#{link_to "@#{user.name}", project_team_member_path(@project, member), class: "gfm gfm-team_member "} you are right"
  100 + user.update_attributes(name: "ping_pong_king")
  101 + gfm(actual).should match(expected)
60 102 end
61 103  
62   - it "should link with adjecent text" do
63   - user = Factory.create(:user, name: "ace")
64   - @project.users << user
65   - member = @project.users_projects.where(user_id: user).first
  104 + it "should link with adjacent text" do
  105 + actual = "Mail the admin (@gfm)"
  106 + gfm(actual).should match(expected)
  107 + end
66 108  
67   - gfm("Mail the Admin (@#{user.name})").should == "Mail the Admin (#{link_to "@#{user.name}", project_team_member_path(@project, member), class: "gfm gfm-team_member "})"
  109 + it "should keep whitespace intact" do
  110 + actual = "Yes, @#{user.name} is right."
  111 + expected = /Yes, <a.+>@#{user.name}<\/a> is right/
  112 + gfm(actual).should match(expected)
68 113 end
69 114  
70   - it "should add styles" do
71   - user = Factory :user, name: "barry"
72   - @project.users << user
73   - gfm("@#{user.name} you are right").should have_selector(".gfm.gfm-team_member")
  115 + it "should not link with an invalid id" do
  116 + actual = expected = "@#{user.name.reverse} you are right."
  117 + gfm(actual).should == expected
74 118 end
75 119  
76   - it "should not link using a bogus name" do
77   - gfm("What hapened to @foo?").should == "What hapened to @foo?"
  120 + it "should include standard gfm classes" do
  121 + gfm(actual).should match(/class="\s?gfm gfm-team_member\s?"/)
78 122 end
79 123 end
80 124  
81   - describe "referencing an issue" do
82   - before do
83   - @issue = Factory :issue, assignee: @fake_user, author: @fake_user, project: @project
84   - @invalid_issue = Factory :issue, assignee: @fake_user, author: @fake_user, project: @other_project
  125 + # Shared examples for referencing an object
  126 + #
  127 + # Expects the following attributes to be available in the example group:
  128 + #
  129 + # - object - The object itself
  130 + # - reference - The object reference string (e.g., #1234, $1234, !1234)
  131 + #
  132 + # Currently limited to Snippets, Issues and MergeRequests
  133 + shared_examples 'referenced object' do
  134 + let(:actual) { "Reference to #{reference}" }
  135 + let(:expected) { polymorphic_path([project, object]) }
  136 +
  137 + it "should link using a valid id" do
  138 + gfm(actual).should match(expected)
85 139 end
86 140  
87   - it "should link using a correct id" do
88   - gfm("Fixes ##{@issue.id}").should == "Fixes #{link_to "##{@issue.id}", project_issue_path(@project, @issue), title: "Issue: #{@issue.title}", class: "gfm gfm-issue "}"
89   - end
  141 + it "should link with adjacent text" do
  142 + # Wrap the reference in parenthesis
  143 + gfm(actual.gsub(reference, "(#{reference})")).should match(expected)
90 144  
91   - it "should link with adjecent text" do
92   - gfm("This has already been discussed (see ##{@issue.id})").should == "This has already been discussed (see #{link_to "##{@issue.id}", project_issue_path(@project, @issue), title: "Issue: #{@issue.title}", class: "gfm gfm-issue "})"
  145 + # Append some text to the end of the reference
  146 + gfm(actual.gsub(reference, "#{reference}, right?")).should match(expected)
93 147 end
94 148  
95   - it "should add styles" do
96   - gfm("Fixes ##{@issue.id}").should have_selector(".gfm.gfm-issue")
  149 + it "should keep whitespace intact" do
  150 + actual = "Referenced #{reference} already."
  151 + expected = /Referenced <a.+>[^\s]+<\/a> already/
  152 + gfm(actual).should match(expected)
97 153 end
98 154  
99   - it "should not link using an invalid id" do
100   - gfm("##{@invalid_issue.id} has been marked duplicate of this").should == "##{@invalid_issue.id} has been marked duplicate of this"
  155 + it "should not link with an invalid id" do
  156 + # Modify the reference string so it's still parsed, but is invalid
  157 + reference.gsub!(/^(.)(\d+)$/, '\1' + ('\2' * 2))
  158 + gfm(actual).should == actual
101 159 end
102   - end
103 160  
104   - describe "referencing a merge request" do
105   - before do
106   - @merge_request = Factory :merge_request, assignee: @fake_user, author: @fake_user, project: @project
107   - @invalid_merge_request = Factory :merge_request, assignee: @fake_user, author: @fake_user, project: @other_project
  161 + it "should include a title attribute" do
  162 + title = "#{object.class.to_s.titlecase}: #{object.title}"
  163 + gfm(actual).should match(/title="#{title}"/)
108 164 end
109 165  
110   - it "should link using a correct id" do
111   - gfm("Fixed in !#{@merge_request.id}").should == "Fixed in #{link_to "!#{@merge_request.id}", project_merge_request_path(@project, @merge_request), title: "Merge Request: #{@merge_request.title}", class: "gfm gfm-merge_request "}"
  166 + it "should include standard gfm classes" do
  167 + css = object.class.to_s.underscore
  168 + gfm(actual).should match(/class="\s?gfm gfm-#{css}\s?"/)
112 169 end
  170 + end
113 171  
114   - it "should link with adjecent text" do
115   - gfm("This has been fixed already (see !#{@merge_request.id})").should == "This has been fixed already (see #{link_to "!#{@merge_request.id}", project_merge_request_path(@project, @merge_request), title: "Merge Request: #{@merge_request.title}", class: "gfm gfm-merge_request "})"
116   - end
  172 + describe "referencing an issue" do
  173 + let(:object) { issue }
  174 + let(:reference) { "##{issue.id}" }
117 175  
118   - it "should add styles" do
119   - gfm("Fixed in !#{@merge_request.id}").should have_selector(".gfm.gfm-merge_request")
120   - end
  176 + include_examples 'referenced object'
  177 + end
121 178  
122   - it "should not link using an invalid id" do
123   - gfm("!#{@invalid_merge_request.id} violates our coding guidelines")
124   - end
  179 + describe "referencing a merge request" do
  180 + let(:object) { merge_request }
  181 + let(:reference) { "!#{merge_request.id}" }
  182 +
  183 + include_examples 'referenced object'
125 184 end
126 185  
127 186 describe "referencing a snippet" do
128   - before do
129   - @snippet = Factory.create(:snippet,
130   - title: "Render asset to string",
131   - author: @fake_user,
132   - project: @project)
133   - end
  187 + let(:object) { snippet }
  188 + let(:reference) { "$#{snippet.id}" }
134 189  
135   - it "should link using a correct id" do
136   - gfm("Check out $#{@snippet.id}").should == "Check out #{link_to "$#{@snippet.id}", project_snippet_path(@project, @snippet), title: "Snippet: #{@snippet.title}", class: "gfm gfm-snippet "}"
137   - end
  190 + include_examples 'referenced object'
  191 + end
138 192  
139   - it "should link with adjecent text" do
140   - gfm("I have created a snippet for that ($#{@snippet.id})").should == "I have created a snippet for that (#{link_to "$#{@snippet.id}", project_snippet_path(@project, @snippet), title: "Snippet: #{@snippet.title}", class: "gfm gfm-snippet "})"
  193 + describe "referencing multiple objects" do
  194 + let(:actual) { "!#{merge_request.id} -> #{commit.id} -> ##{issue.id}" }
  195 +
  196 + it "should link to the merge request" do
  197 + expected = project_merge_request_path(project, merge_request)
  198 + gfm(actual).should match(expected)
141 199 end
142 200  
143   - it "should add styles" do
144   - gfm("Check out $#{@snippet.id}").should have_selector(".gfm.gfm-snippet")
  201 + it "should link to the commit" do
  202 + expected = project_commit_path(project, commit)
  203 + gfm(actual).should match(expected)
145 204 end
146 205  
147   - it "should not link using an invalid id" do
148   - gfm("Don't use $1234").should == "Don't use $1234"
  206 + it "should link to the issue" do
  207 + expected = project_issue_path(project, issue)
  208 + gfm(actual).should match(expected)
149 209 end
150 210 end
  211 + end
151 212  
152   - it "should link to multiple things" do
153   - user = Factory :user, name: "barry"
154   - @project.users << user
155   - member = @project.users_projects.where(user_id: user).first
  213 + describe "#link_to_gfm" do
  214 + let(:commit_path) { project_commit_path(project, commit) }
  215 + let(:issues) { create_list(:issue, 2, project: project) }
156 216  
157   - gfm("Let @#{user.name} fix the *mess* in #{@commit.id}").should == "Let #{link_to "@#{user.name}", project_team_member_path(@project, member), class: "gfm gfm-team_member "} fix the *mess* in #{link_to @commit.id, project_commit_path(@project, id: @commit.id), title: "Commit: #{@commit.author_name} - #{@commit.title}", class: "gfm gfm-commit "}"
158   - end
  217 + it "should handle references nested in links with all the text" do
  218 + actual = link_to_gfm("This should finally fix ##{issues[0].id} and ##{issues[1].id} for real", commit_path)
159 219  
160   - it "should not trip over other stuff" do
161   - gfm("_Please_ *stop* 'helping' and all the other b*$#%' you do.").should == "_Please_ *stop* 'helping' and all the other b*$#%' you do."
162   - end
  220 + # Break the result into groups of links with their content, without
  221 + # closing tags
  222 + groups = actual.split("</a>")
163 223  
164   - it "should not touch HTML entities" do
165   - gfm("We&#39;ll accept good pull requests.").should == "We&#39;ll accept good pull requests."
166   - end
  224 + # Leading commit link
  225 + groups[0].should match(/href="#{commit_path}"/)
  226 + groups[0].should match(/This should finally fix $/)
167 227  
168   - it "should forward HTML options to links" do
169   - gfm("fixed in #{@commit.id}", class: "foo").should have_selector("a.foo")
170   - end
171   - end
  228 + # First issue link
  229 + groups[1].should match(/href="#{project_issue_path(project, issues[0])}"/)
  230 + groups[1].should match(/##{issues[0].id}$/)
172 231  
173   - describe "#link_to_gfm" do
174   - let(:issue1) { Factory :issue, assignee: @fake_user, author: @fake_user, project: @project }
175   - let(:issue2) { Factory :issue, assignee: @fake_user, author: @fake_user, project: @project }
  232 + # Internal commit link
  233 + groups[2].should match(/href="#{commit_path}"/)
  234 + groups[2].should match(/ and /)
176 235  
177   - it "should handle references nested in links with all the text" do
178   - link_to_gfm("This should finally fix ##{issue1.id} and ##{issue2.id} for real", project_commit_path(@project, id: @commit.id)).should == "#{link_to "This should finally fix ", project_commit_path(@project, id: @commit.id)}#{link_to "##{issue1.id}", project_issue_path(@project, issue1), title: "Issue: #{issue1.title}", class: "gfm gfm-issue "}#{link_to " and ", project_commit_path(@project, id: @commit.id)}#{link_to "##{issue2.id}", project_issue_path(@project, issue2), title: "Issue: #{issue2.title}", class: "gfm gfm-issue "}#{link_to " for real", project_commit_path(@project, id: @commit.id)}"
  236 + # Second issue link
  237 + groups[3].should match(/href="#{project_issue_path(project, issues[1])}"/)
  238 + groups[3].should match(/##{issues[1].id}$/)
  239 +
  240 + # Trailing commit link
  241 + groups[4].should match(/href="#{commit_path}"/)
  242 + groups[4].should match(/ for real$/)
179 243 end
180 244  
181 245 it "should forward HTML options" do
182   - link_to_gfm("This should finally fix ##{issue1.id} for real", project_commit_path(@project, id: @commit.id), class: "foo").should have_selector(".foo")
  246 + actual = link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo')
  247 + actual.should have_selector 'a.gfm.gfm-commit.foo'
183 248 end
184 249 end
185 250  
186 251 describe "#markdown" do
187   - before do
188   - @issue = Factory :issue, assignee: @fake_user, author: @fake_user, project: @project
189   - @merge_request = Factory :merge_request, assignee: @fake_user, author: @fake_user, project: @project
190   - @note = Factory.create(:note,
191   - note: "Screenshot of the new feature",
192   - project: @project,
193   - noteable_id: @commit.id,
194   - noteable_type: "Commit",
195   - attachment: "screenshot123.jpg")
196   - @snippet = Factory.create(:snippet,
197   - title: "Render asset to string",
198   - author: @fake_user,
199   - project: @project)
200   -
201   - @other_user = Factory :user, name: "bill"
202   - @project.users << @other_user
203   - @member = @project.users_projects.where(user_id: @other_user).first
204   - end
205   -
206 252 it "should handle references in paragraphs" do
207   - markdown("\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. #{@commit.id} Nam pulvinar sapien eget odio adipiscing at faucibus orci vestibulum.\n").should == "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. #{link_to @commit.id, project_commit_path(@project, id: @commit.id), title: "Commit: #{@commit.author_name} - #{@commit.title}", class: "gfm gfm-commit "} Nam pulvinar sapien eget odio adipiscing at faucibus orci vestibulum.</p>\n"
  253 + markdown("\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. #{commit.id} Nam pulvinar sapien eget odio adipiscing at faucibus orci vestibulum.\n").should == "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. #{link_to commit.id, project_commit_path(project, commit), title: commit.link_title, class: "gfm gfm-commit "} Nam pulvinar sapien eget odio adipiscing at faucibus orci vestibulum.</p>\n"
208 254 end
209 255  
210 256 it "should handle references in headers" do
211   - markdown("\n# Working around ##{@issue.id} for now\n## Apply !#{@merge_request.id}").should == "<h1 id=\"toc_0\">Working around #{link_to "##{@issue.id}", project_issue_path(@project, @issue), title: "Issue: #{@issue.title}", class: "gfm gfm-issue "} for now</h1>\n\n<h2 id=\"toc_1\">Apply #{link_to "!#{@merge_request.id}", project_merge_request_path(@project, @merge_request), title: "Merge Request: #{@merge_request.title}", class: "gfm gfm-merge_request "}</h2>\n"
  257 + actual = "\n# Working around ##{issue.id}\n## Apply !#{merge_request.id}"
  258 +
  259 + markdown(actual).should match(%r{<h1[^<]*>Working around <a.+>##{issue.id}</a></h1>})
  260 + markdown(actual).should match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.id}</a></h2>})
212 261 end
213 262  
214 263 it "should handle references in lists" do
215   - markdown("\n* dark: ##{@issue.id}\n* light by @#{@other_user.name}\n").should == "<ul>\n<li>dark: #{link_to "##{@issue.id}", project_issue_path(@project, @issue), title: "Issue: #{@issue.title}", class: "gfm gfm-issue "}</li>\n<li>light by #{link_to "@#{@other_user.name}", project_team_member_path(@project, @member), class: "gfm gfm-team_member "}</li>\n</ul>\n"
  264 + project.users << user
  265 +
  266 + actual = "\n* dark: ##{issue.id}\n* light by @#{member.user_name}"
  267 +
  268 + markdown(actual).should match(%r{<li>dark: <a.+>##{issue.id}</a></li>})
  269 + markdown(actual).should match(%r{<li>light by <a.+>@#{member.user_name}</a></li>})
216 270 end
217 271  
218 272 it "should handle references in <em>" do
219   - markdown("Apply _!#{@merge_request.id}_ ASAP").should == "<p>Apply <em>#{link_to "!#{@merge_request.id}", project_merge_request_path(@project, @merge_request), title: "Merge Request: #{@merge_request.title}", class: "gfm gfm-merge_request "}</em> ASAP</p>\n"
  273 + actual = "Apply _!#{merge_request.id}_ ASAP"
  274 +
  275 + markdown(actual).should match(%r{Apply <em><a.+>!#{merge_request.id}</a></em>})
220 276 end
221 277  
222 278 it "should leave code blocks untouched" do
223   - 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"
  279 + 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"
224 280  
225   - 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"
  281 + 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"
226 282 end
227 283  
228 284 it "should leave inline code untouched" do
229   - markdown("\nDon't use `$#{@snippet.id}` here.\n").should == "<p>Don&#39;t use <code>$#{@snippet.id}</code> here.</p>\n"
  285 + markdown("\nDon't use `$#{snippet.id}` here.\n").should == "<p>Don&#39;t use <code>$#{snippet.id}</code> here.</p>\n"
230 286 end
231 287 end
232 288 end
... ...