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 require "spec_helper" 1 require "spec_helper"
2 2
3 describe GitlabMarkdownHelper do 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 before do 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 end 16 end
11 17
12 describe "#gfm" do 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 @project = nil 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 end 40 end
18 41
19 describe "referencing a commit" do 42 describe "referencing a commit" do
  43 + let(:expected) { project_commit_path(project, commit) }
  44 +
20 it "should link using a full id" do 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 end 48 end
23 49
24 it "should link using a short id" do 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 end 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 end 64 end
31 65
32 it "should not link with an invalid id" do 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 end 79 end
35 end 80 end
36 81
37 describe "referencing a team member" do 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 end 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 end 97 end
53 98
54 it "should link using name with underscores" do 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 end 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 end 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 end 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 end 122 end
79 end 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 end 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 end 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 end 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 end 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 end 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 end 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 end 184 end
126 185
127 describe "referencing a snippet" do 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 end 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 end 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 end 209 end
150 end 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 end 243 end
180 244
181 it "should forward HTML options" do 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 end 248 end
184 end 249 end
185 250
186 describe "#markdown" do 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 it "should handle references in paragraphs" do 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 end 254 end
209 255
210 it "should handle references in headers" do 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 end 261 end
213 262
214 it "should handle references in lists" do 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 end 270 end
217 271
218 it "should handle references in <em>" do 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 end 276 end
221 277
222 it "should leave code blocks untouched" do 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 end 282 end
227 283
228 it "should leave inline code untouched" do 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 end 286 end
231 end 287 end
232 end 288 end