Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2015-07-31 15:34:53 +0300
committerDouwe Maan <douwe@gitlab.com>2015-07-31 15:34:53 +0300
commit51e5f379dd69837d3405c78e5ef6ce3727939f3c (patch)
treeec0404f8af61a712c25fb43a4bf21e2ff0349098
parent1c7a8b8c27398250983bf4329007f6971df65f34 (diff)
parenta51a3fb8ed92a58b375125b19f75c3d4c545571a (diff)
Merge branch 'master' into api-useremails
-rw-r--r--CHANGELOG5
-rw-r--r--app/controllers/projects/network_controller.rb4
-rw-r--r--app/models/group.rb6
-rw-r--r--app/models/project.rb11
-rw-r--r--app/views/devise/sessions/_new_base.html.haml2
-rw-r--r--app/views/profiles/keys/_form.html.haml7
-rw-r--r--app/views/projects/network/show.html.haml6
-rw-r--r--doc/integration/twitter.md12
-rw-r--r--doc/workflow/gitlab_flow.md8
-rw-r--r--features/project/network_graph.feature5
-rw-r--r--features/steps/project/network_graph.rb14
-rw-r--r--lib/api/entities.rb10
-rw-r--r--lib/backup/database.rb7
-rw-r--r--lib/backup/manager.rb2
-rw-r--r--lib/backup/repository.rb5
-rw-r--r--lib/backup/uploads.rb6
-rw-r--r--lib/gitlab/markdown/relative_link_filter.rb14
-rw-r--r--lib/tasks/gitlab/check.rake8
-rw-r--r--lib/tasks/gitlab/import.rake4
-rw-r--r--spec/features/markdown_spec.rb427
-rw-r--r--spec/fixtures/markdown.md.erb13
-rw-r--r--spec/lib/gitlab/markdown/relative_link_filter_spec.rb16
-rw-r--r--spec/models/project_spec.rb18
-rw-r--r--spec/services/projects/fork_service_spec.rb2
-rw-r--r--spec/support/markdown_feature.rb106
-rw-r--r--spec/support/matchers/markdown_matchers.rb156
26 files changed, 512 insertions, 362 deletions
diff --git a/CHANGELOG b/CHANGELOG
index ab46ef3b169..81c8956fc93 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,12 @@
Please view this file on the master branch, on stable branches it's out of date.
v 7.14.0 (unreleased)
+ - Fix network graph when branch name has single quotes (Stan Hu)
- Upgrade gitlab_git to version 7.2.6 to fix Error 500 when creating network graphs (Stan Hu)
+ - Add support for Unicode filenames in relative links (Hiroyuki Sato)
- Fix URL used for refreshing notes if relative_url is present (Bartłomiej Święcki)
- Fix commit data retrieval when branch name has single quotes (Stan Hu)
+ - Check that project was actually created rather than just validated in import:repos task (Stan Hu)
- Fix full screen mode for snippet comments (Daniel Gerhardt)
- Fix 404 error in files view after deleting the last file in a repository (Stan Hu)
- Fix the "Reload with full diff" URL button (Stan Hu)
@@ -15,9 +18,11 @@ v 7.14.0 (unreleased)
- Expire Rails cache entries after two weeks to prevent endless Redis growth
- Add support for destroying project milestones (Stan Hu)
- Add fetch command to the MR page
+ - Add project star and fork count, group avatar URL and user/group web URL attributes to API
- Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
- Add fetch command to the MR page.
- Add ability to manage user email addresses via the API.
+ - Disabled autocapitalize and autocorrect on login field (Daryl Chan)
v 7.13.2
- Fix randomly failed spec
diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb
index 06aef91cadd..b181c47baec 100644
--- a/app/controllers/projects/network_controller.rb
+++ b/app/controllers/projects/network_controller.rb
@@ -7,6 +7,10 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :authorize_download_code!
def show
+
+ @url = namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))
+ @commit_url = namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")
+
respond_to do |format|
format.html
diff --git a/app/models/group.rb b/app/models/group.rb
index 051c672cb33..cfb8faa1491 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -56,6 +56,12 @@ class Group < Namespace
name
end
+ def avatar_url(size = nil)
+ if avatar.present?
+ [gitlab_config.url, avatar.url].join
+ end
+ end
+
def owners
@owners ||= group_members.owners.map(&:user)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index ff372ea9aa5..0921fdfe9b4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -36,7 +36,6 @@ class Project < ActiveRecord::Base
include Gitlab::ConfigHelper
include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel
- include Rails.application.routes.url_helpers
include Referable
include Sortable
@@ -316,7 +315,7 @@ class Project < ActiveRecord::Base
end
def web_url
- [gitlab_config.url, path_with_namespace].join('/')
+ Rails.application.routes.url_helpers.namespace_project_url(self.namespace, self)
end
def web_url_without_protocol
@@ -433,7 +432,7 @@ class Project < ActiveRecord::Base
if avatar.present?
[gitlab_config.url, avatar.url].join
elsif avatar_in_git
- [gitlab_config.url, namespace_project_avatar_path(namespace, self)].join
+ Rails.application.routes.url_helpers.namespace_project_avatar_url(namespace, self)
end
end
@@ -571,7 +570,7 @@ class Project < ActiveRecord::Base
end
def http_url_to_repo
- [gitlab_config.url, '/', path_with_namespace, '.git'].join('')
+ "#{web_url}.git"
end
# Check if current branch name is marked as protected in the system
@@ -705,14 +704,14 @@ class Project < ActiveRecord::Base
ensure_satellite_exists
true
else
- errors.add(:base, 'Failed to fork repository')
+ errors.add(:base, 'Failed to fork repository via gitlab-shell')
false
end
else
if gitlab_shell.add_repository(path_with_namespace)
true
else
- errors.add(:base, 'Failed to create repository')
+ errors.add(:base, 'Failed to create repository via gitlab-shell')
false
end
end
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 54a39726771..9f5520603cd 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -1,5 +1,5 @@
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
- = f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus"
+ = f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off"
= f.password_field :password, class: "form-control bottom", placeholder: "Password"
- if devise_mapping.rememberable?
.remember-me.checkbox
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index f905417f0e2..b76a5b636ac 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -7,13 +7,12 @@
%li= msg
.form-group
- = f.label :title, class: 'control-label'
- .col-sm-10= f.text_field :title, class: "form-control"
- .form-group
= f.label :key, class: 'control-label'
.col-sm-10
= f.text_area :key, class: "form-control", rows: 8
-
+ .form-group
+ = f.label :title, class: 'control-label'
+ .col-sm-10= f.text_field :title, class: "form-control"
.form-actions
= f.submit 'Add key', class: "btn btn-create"
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index a88cf167511..52b5b8b877e 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -17,9 +17,9 @@
:javascript
network_graph = new Network({
- url: '#{namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))}',
- commit_url: '#{namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")}',
- ref: '#{@ref}',
+ url: "#{escape_javascript(@url)}",
+ commit_url: "#{escape_javascript(@commit_url)}",
+ ref: "#{escape_javascript(@ref)}",
commit_id: '#{@commit.id}'
})
new ShortcutsNetwork(network_graph.branch_graph)
diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md
index fe9091ad9a8..1350c8f693c 100644
--- a/doc/integration/twitter.md
+++ b/doc/integration/twitter.md
@@ -2,9 +2,7 @@
To enable the Twitter OmniAuth provider you must register your application with Twitter. Twitter will generate a client ID and secret key for you to use.
-1. Sign in to [Twitter Developers](https://dev.twitter.com/) area.
-
-1. Hover over the avatar in the top right corner and select "My applications."
+1. Sign in to [Twitter Application Management](https://apps.twitter.com/).
1. Select "Create new app"
@@ -14,18 +12,18 @@ To enable the Twitter OmniAuth provider you must register your application with
- Description: Create a description.
- Website: The URL to your GitLab installation. 'https://gitlab.example.com'
- Callback URL: 'https://gitlab.example.com/users/auth/twitter/callback'
- - Agree to the "Rules of the Road."
+ - Agree to the "Developer Agreement".
![Twitter App Details](twitter_app_details.png)
1. Select "Create your Twitter application."
1. Select the "Settings" tab.
-1. Underneath the Callback URL check the box next to "Allow this application to be used to Sign in the Twitter."
+1. Underneath the Callback URL check the box next to "Allow this application to be used to Sign in with Twitter."
1. Select "Update settings" at the bottom to save changes.
-1. Select the "API Keys" tab.
+1. Select the "Keys and Access Tokens" tab.
1. You should now see an API key and API secret (see screenshot). Keep this page open as you continue configuration.
@@ -78,4 +76,4 @@ To enable the Twitter OmniAuth provider you must register your application with
1. Restart GitLab for the changes to take effect.
-On the sign in page there should now be a Twitter icon below the regular sign in form. Click the icon to begin the authentication process. Twitter will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
+On the sign in page there should now be a Twitter icon below the regular sign in form. Click the icon to begin the authentication process. Twitter will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in. \ No newline at end of file
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index 0e87dc74217..f608674faf6 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -31,7 +31,7 @@ We think there is still room for improvement and will detail a set of practices
## Git flow and its problems
-[![Git Flow timeline by Vincent Driessen, used with permission](gitdashflow.png)
+![Git Flow timeline by Vincent Driessen, used with permission](gitdashflow.png)
Git flow was one of the first proposals to use git branches and it has gotten a lot of attention.
It advocates a master branch and a separate develop branch as well as supporting branches for features, releases and hotfixes.
@@ -54,7 +54,7 @@ And doing releases doesn't automatically mean also doing hotfixes.
![Master branch with feature branches merged in](github_flow.png)
- In reaction to git flow a simpler alternative was detailed, [GitHub flow](https://guides.github.com/introduction/flow/index.html).
+In reaction to git flow a simpler alternative was detailed, [GitHub flow](https://guides.github.com/introduction/flow/index.html).
This flow has only feature branches and a master branch.
This is very simple and clean, many organizations have adopted it with great success.
Atlassian recommends [a similar strategy](http://blogs.atlassian.com/2014/01/simple-git-workflow-simple/) although they rebase feature branches.
@@ -131,7 +131,7 @@ When you feel comfortable with it to be merged you assign it to the person that
There is room for more feedback and after the assigned person feels comfortable with the result the branch is merged.
If the assigned person does not feel comfortable they can close the merge request without merging.
-In GitLab it is common to protect the long-lived branches (e.g. the master branch) so that normal developers [can't modify these protected branches](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/permissions/permissions.md).
+In GitLab it is common to protect the long-lived branches (e.g. the master branch) so that normal developers [can't modify these protected branches](http://doc.gitlab.com/ce/permissions/permissions.html).
So if you want to merge it into a protected branch you assign it to someone with master authorizations.
## Issues with GitLab flow
@@ -216,7 +216,7 @@ This prevents creating a merge commit when merging master into your feature bran
However, just like with squashing you should never rebase commits you have pushed to a remote server.
This makes it impossible to rebase work in progress that you already shared with your team which is something we recommend.
When using rebase to keep your feature branch updated you [need to resolve similar conflicts again and again](http://blogs.atlassian.com/2013/10/git-team-workflows-merge-or-rebase/).
-You can reuse recorded resolutions (rerere) sometimes, but with without rebasing you only have to solve the conflicts one time and you’re set.
+You can reuse recorded resolutions (rerere) sometimes, but without rebasing you only have to solve the conflicts one time and you’re set.
There has to be a better way to avoid many merge commits.
The way to prevent creating many merge commits is to not frequently merge master into the feature branch.
diff --git a/features/project/network_graph.feature b/features/project/network_graph.feature
index 8beb6043aff..6cc89a15a78 100644
--- a/features/project/network_graph.feature
+++ b/features/project/network_graph.feature
@@ -11,6 +11,11 @@ Feature: Project Network Graph
And page should have "master" on graph
@javascript
+ Scenario: I should see project network with 'test' branch
+ When I visit project network page on branch 'test'
+ Then page should have 'test' on graph
+
+ @javascript
Scenario: I should switch "branch" and "tag"
When I switch ref to "feature"
Then page should select "feature" in select box
diff --git a/features/steps/project/network_graph.rb b/features/steps/project/network_graph.rb
index 992cf2734fd..7a83d32a240 100644
--- a/features/steps/project/network_graph.rb
+++ b/features/steps/project/network_graph.rb
@@ -11,8 +11,12 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
# Stub Graph max_size to speed up test (10 commits vs. 650)
Network::Graph.stub(max_count: 10)
- project = Project.find_by(name: "Shop")
- visit namespace_project_network_path(project.namespace, project, "master")
+ @project = Project.find_by(name: "Shop")
+ visit namespace_project_network_path(@project.namespace, @project, "master")
+ end
+
+ step "I visit project network page on branch 'test'" do
+ visit namespace_project_network_path(@project.namespace, @project, "'test'")
end
step 'page should select "master" in select box' do
@@ -29,6 +33,12 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
end
end
+ step "page should have 'test' on graph" do
+ page.within '.network-graph' do
+ expect(page).to have_content "'test'"
+ end
+ end
+
When 'I switch ref to "feature"' do
select 'feature', from: 'ref'
sleep 2
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index ce3d09a32cd..b5556682449 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -6,6 +6,10 @@ module API
class UserBasic < UserSafe
expose :id, :state, :avatar_url
+
+ expose :web_url do |user, options|
+ Rails.application.routes.url_helpers.user_url(user)
+ end
end
class User < UserBasic
@@ -63,6 +67,7 @@ module API
expose :namespace
expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? }
expose :avatar_url
+ expose :star_count, :forks_count
end
class ProjectMember < UserBasic
@@ -73,6 +78,11 @@ module API
class Group < Grape::Entity
expose :id, :name, :path, :description
+ expose :avatar_url
+
+ expose :web_url do |group, options|
+ Rails.application.routes.url_helpers.group_url(group)
+ end
end
class GroupDetail < Group
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index c5a5396cbbf..bbb230a10f0 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -7,7 +7,11 @@ module Backup
def initialize
@config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
@db_dir = File.join(Gitlab.config.backup.path, 'db')
- FileUtils.mkdir_p(@db_dir) unless Dir.exists?(@db_dir)
+ FileUtils.rm_rf(@db_dir)
+ # Ensure the parent dir of @db_dir exists
+ FileUtils.mkdir_p(Gitlab.config.backup.path)
+ # Fail if somebody raced to create @db_dir before us
+ FileUtils.mkdir(@db_dir, mode: 0700)
end
def dump
@@ -25,7 +29,6 @@ module Backup
abort 'Backup failed' unless success
$progress.print 'Compressing database ... '
- FileUtils.rm_f db_file_name_gz
success = system('gzip', db_file_name)
report_success(success)
abort 'Backup failed: compress error' unless success
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 6fa2079d1a8..9ae4b346436 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -16,8 +16,6 @@ module Backup
file << s.to_yaml.gsub(/^---\n/,'')
end
- FileUtils.chmod(0700, folders_to_backup)
-
# create archive
$progress.print "Creating backup archive: #{tar_file} ... "
orig_umask = File.umask(0077)
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index dfb2da9f84e..4d70f7883dd 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -130,7 +130,10 @@ module Backup
def prepare
FileUtils.rm_rf(backup_repos_path)
- FileUtils.mkdir_p(backup_repos_path)
+ # Ensure the parent dir of backup_repos_path exists
+ FileUtils.mkdir_p(Gitlab.config.backup.path)
+ # Fail if somebody raced to create backup_repos_path before us
+ FileUtils.mkdir(backup_repos_path, mode: 0700)
end
def silent
diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb
index bf43610acf6..1f9626644e6 100644
--- a/lib/backup/uploads.rb
+++ b/lib/backup/uploads.rb
@@ -10,7 +10,11 @@ module Backup
# Copy uploads from public/uploads to backup/uploads
def dump
- FileUtils.mkdir_p(backup_uploads_dir)
+ FileUtils.rm_rf(backup_uploads_dir)
+ # Ensure the parent dir of backup_uploads_dir exists
+ FileUtils.mkdir_p(Gitlab.config.backup.path)
+ # Fail if somebody raced to create backup_uploads_dir before us
+ FileUtils.mkdir(backup_uploads_dir, mode: 0700)
FileUtils.cp_r(app_uploads_dir, backup_dir)
end
diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb
index 9de2b24a9da..30f50b82996 100644
--- a/lib/gitlab/markdown/relative_link_filter.rb
+++ b/lib/gitlab/markdown/relative_link_filter.rb
@@ -98,15 +98,25 @@ module Gitlab
#
# Returns a String
def path_type(path)
- if repository.tree(current_sha, path).entries.any?
+ unescaped_path = Addressable::URI.unescape(path)
+
+ if tree?(unescaped_path)
'tree'
- elsif repository.blob_at(current_sha, path).try(:image?)
+ elsif image?(unescaped_path)
'raw'
else
'blob'
end
end
+ def tree?(path)
+ repository.tree(current_sha, path).entries.any?
+ end
+
+ def image?(path)
+ repository.blob_at(current_sha, path).try(:image?)
+ end
+
def current_sha
context[:commit].try(:id) ||
ref ? repository.commit(ref).try(:sha) : repository.head_commit.sha
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index aed84226a2f..badb47c6779 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -485,7 +485,8 @@ namespace :gitlab do
if project.empty_repo?
puts "repository is empty".magenta
- elsif File.realpath(project_hook_directory) == File.realpath(gitlab_shell_hooks_path)
+ elsif File.directory?(project_hook_directory) && File.directory?(gitlab_shell_hooks_path) &&
+ (File.realpath(project_hook_directory) == File.realpath(gitlab_shell_hooks_path))
puts 'ok'.green
else
puts "wrong or missing hooks".red
@@ -754,7 +755,7 @@ namespace :gitlab do
print "Ruby version >= #{required_version} ? ... "
if current_version.valid? && required_version <= current_version
- puts "yes (#{current_version})".green
+ puts "yes (#{current_version})".green
else
puts "no".red
try_fixing_it(
@@ -772,7 +773,7 @@ namespace :gitlab do
print "Git version >= #{required_version} ? ... "
if current_version.valid? && required_version <= current_version
- puts "yes (#{current_version})".green
+ puts "yes (#{current_version})".green
else
puts "no".red
try_fixing_it(
@@ -806,4 +807,3 @@ namespace :gitlab do
end
end
end
-
diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake
index 5f83e5e8e7f..c1ee271ae2b 100644
--- a/lib/tasks/gitlab/import.rake
+++ b/lib/tasks/gitlab/import.rake
@@ -62,11 +62,11 @@ namespace :gitlab do
project = Projects::CreateService.new(user, project_params).execute
- if project.valid?
+ if project.persisted?
puts " * Created #{project.name} (#{repo_path})".green
else
puts " * Failed trying to create #{project.name} (#{repo_path})".red
- puts " Validation Errors: #{project.errors.messages}".red
+ puts " Errors: #{project.errors.messages}".red
end
end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index b8199aa5e61..859a62f740f 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -17,410 +17,215 @@ require 'erb'
# -> Post-process HTML
# -> `gfm_with_options` helper
# -> HTML::Pipeline
-# -> Sanitize
-# -> RelativeLink
-# -> Emoji
-# -> Table of Contents
-# -> Autolinks
-# -> Rinku (http, https, ftp)
-# -> Other schemes
-# -> ExternalLink
-# -> References
-# -> TaskList
+# -> SanitizationFilter
+# -> Other filters, depending on pipeline
# -> `html_safe`
# -> Template
#
# See the MarkdownFeature class for setup details.
describe 'GitLab Markdown', feature: true do
- include ActionView::Helpers::TagHelper
- include ActionView::Helpers::UrlHelper
include Capybara::Node::Matchers
include GitlabMarkdownHelper
+ include MarkdownMatchers
- # `markdown` calls these two methods
- def current_user
- @feat.user
- end
-
- def user_color_scheme_class
- :white
- end
-
- # Let's only parse this thing once
- before(:all) do
- @feat = MarkdownFeature.new
-
- # `markdown` expects a `@project` variable
- @project = @feat.project
-
- @md = markdown(@feat.raw_markdown)
- @doc = Nokogiri::HTML::DocumentFragment.parse(@md)
- end
-
- after(:all) do
- @feat.teardown
+ # Sometimes it can be useful to see the parsed output of the Markdown document
+ # for debugging. Call this method to write the output to
+ # `tmp/capybara/<filename>.html`.
+ def write_markdown(filename = 'markdown_spec')
+ File.open(Rails.root.join("tmp/capybara/#{filename}.html"), 'w') do |file|
+ file.puts @html
+ end
end
- # Given a header ID, goes to that element's parent (the header itself), then
- # its next sibling element (the body).
- def get_section(id)
- @doc.at_css("##{id}").parent.next_element
+ def doc(html = @html)
+ Nokogiri::HTML::DocumentFragment.parse(html)
end
- # Sometimes it can be useful to see the parsed output of the Markdown document
- # for debugging. Uncomment this block to write the output to
- # tmp/capybara/markdown_spec.html.
- #
- # it 'writes to a file' do
- # File.open(Rails.root.join('tmp/capybara/markdown_spec.html'), 'w') do |file|
- # file.puts @md
- # end
- # end
-
- describe 'Markdown' do
- describe 'No Intra Emphasis' do
+ # Shared behavior that all pipelines should exhibit
+ shared_examples 'all pipelines' do
+ describe 'Redcarpet extensions' do
it 'does not parse emphasis inside of words' do
- body = get_section('no-intra-emphasis')
- expect(body.to_html).not_to match('foo<em>bar</em>baz')
+ expect(doc.to_html).not_to match('foo<em>bar</em>baz')
end
- end
- describe 'Tables' do
it 'parses table Markdown' do
- body = get_section('tables')
- expect(body).to have_selector('th:contains("Header")')
- expect(body).to have_selector('th:contains("Row")')
- expect(body).to have_selector('th:contains("Example")')
+ aggregate_failures do
+ expect(doc).to have_selector('th:contains("Header")')
+ expect(doc).to have_selector('th:contains("Row")')
+ expect(doc).to have_selector('th:contains("Example")')
+ end
end
it 'allows Markdown in tables' do
- expect(@doc.at_css('td:contains("Baz")').children.to_html).
+ expect(doc.at_css('td:contains("Baz")').children.to_html).
to eq '<strong>Baz</strong>'
end
- end
- describe 'Fenced Code Blocks' do
it 'parses fenced code blocks' do
- expect(@doc).to have_selector('pre.code.highlight.white.c')
- expect(@doc).to have_selector('pre.code.highlight.white.python')
+ aggregate_failures do
+ expect(doc).to have_selector('pre.code.highlight.white.c')
+ expect(doc).to have_selector('pre.code.highlight.white.python')
+ end
end
- end
- describe 'Strikethrough' do
it 'parses strikethroughs' do
- expect(@doc).to have_selector(%{del:contains("and this text doesn't")})
+ expect(doc).to have_selector(%{del:contains("and this text doesn't")})
end
- end
- describe 'Superscript' do
it 'parses superscript' do
- body = get_section('superscript')
- expect(body.to_html).to match('1<sup>st</sup>')
- expect(body.to_html).to match('2<sup>nd</sup>')
+ expect(doc).to have_selector('sup', count: 2)
end
end
- end
- describe 'HTML::Pipeline' do
describe 'SanitizationFilter' do
- it 'uses a permissive whitelist' do
- expect(@doc).to have_selector('b:contains("b tag")')
- expect(@doc).to have_selector('em:contains("em tag")')
- expect(@doc).to have_selector('code:contains("code tag")')
- expect(@doc).to have_selector('kbd:contains("s")')
- expect(@doc).to have_selector('strike:contains(Emoji)')
- expect(@doc).to have_selector('img[src*="smile.png"]')
- expect(@doc).to have_selector('br')
- expect(@doc).to have_selector('hr')
+ it 'permits b elements' do
+ expect(doc).to have_selector('b:contains("b tag")')
end
- it 'permits span elements' do
- expect(@doc).to have_selector('span:contains("span tag")')
+ it 'permits em elements' do
+ expect(doc).to have_selector('em:contains("em tag")')
end
- it 'permits table alignment' do
- expect(@doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
- expect(@doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
- expect(@doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
-
- expect(@doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
- expect(@doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
- expect(@doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
+ it 'permits code elements' do
+ expect(doc).to have_selector('code:contains("code tag")')
end
- it 'removes `rel` attribute from links' do
- body = get_section('sanitizationfilter')
- expect(body).not_to have_selector('a[rel="bookmark"]')
+ it 'permits kbd elements' do
+ expect(doc).to have_selector('kbd:contains("s")')
end
- it "removes `href` from `a` elements if it's fishy" do
- expect(@doc).not_to have_selector('a[href*="javascript"]')
+ it 'permits strike elements' do
+ expect(doc).to have_selector('strike:contains(Emoji)')
end
- end
- describe 'Escaping' do
- let(:table) { @doc.css('table').last.at_css('tbody') }
-
- it 'escapes non-tag angle brackets' do
- expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 &lt; 3 &amp; 5'
+ it 'permits img elements' do
+ expect(doc).to have_selector('img[src*="smile.png"]')
end
- end
-
- describe 'Edge Cases' do
- it 'allows markup inside link elements' do
- expect(@doc.at_css('a[href="#link-emphasis"]').to_html).
- to eq %{<a href="#link-emphasis"><em>text</em></a>}
-
- expect(@doc.at_css('a[href="#link-strong"]').to_html).
- to eq %{<a href="#link-strong"><strong>text</strong></a>}
- expect(@doc.at_css('a[href="#link-code"]').to_html).
- to eq %{<a href="#link-code"><code>text</code></a>}
+ it 'permits br elements' do
+ expect(doc).to have_selector('br')
end
- end
- describe 'EmojiFilter' do
- it 'parses Emoji' do
- expect(@doc).to have_selector('img.emoji', count: 10)
+ it 'permits hr elements' do
+ expect(doc).to have_selector('hr')
end
- end
- describe 'TableOfContentsFilter' do
- it 'creates anchors inside header elements' do
- expect(@doc).to have_selector('h1 a#gitlab-markdown')
- expect(@doc).to have_selector('h2 a#markdown')
- expect(@doc).to have_selector('h3 a#autolinkfilter')
+ it 'permits span elements' do
+ expect(doc).to have_selector('span:contains("span tag")')
end
- end
- describe 'AutolinkFilter' do
- let(:list) { get_section('autolinkfilter').next_element }
-
- def item(index)
- list.at_css("li:nth-child(#{index})")
+ it 'permits style attribute in th elements' do
+ aggregate_failures do
+ expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
+ expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
+ expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
+ end
end
- it 'autolinks http://' do
- expect(item(1).children.first.name).to eq 'a'
- expect(item(1).children.first['href']).to eq 'http://about.gitlab.com/'
+ it 'permits style attribute in td elements' do
+ aggregate_failures do
+ expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
+ expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
+ expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
+ end
end
- it 'autolinks https://' do
- expect(item(2).children.first.name).to eq 'a'
- expect(item(2).children.first['href']).to eq 'https://google.com/'
+ it 'removes `rel` attribute from links' do
+ expect(doc).not_to have_selector('a[rel="bookmark"]')
end
- it 'autolinks ftp://' do
- expect(item(3).children.first.name).to eq 'a'
- expect(item(3).children.first['href']).to eq 'ftp://ftp.us.debian.org/debian/'
+ it "removes `href` from `a` elements if it's fishy" do
+ expect(doc).not_to have_selector('a[href*="javascript"]')
end
+ end
- it 'autolinks smb://' do
- expect(item(4).children.first.name).to eq 'a'
- expect(item(4).children.first['href']).to eq 'smb://foo/bar/baz'
+ describe 'Escaping' do
+ it 'escapes non-tag angle brackets' do
+ table = doc.css('table').last.at_css('tbody')
+ expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 &lt; 3 &amp; 5'
end
+ end
- it 'autolinks irc://' do
- expect(item(5).children.first.name).to eq 'a'
- expect(item(5).children.first['href']).to eq 'irc://irc.freenode.net/git'
- end
+ describe 'Edge Cases' do
+ it 'allows markup inside link elements' do
+ aggregate_failures do
+ expect(doc.at_css('a[href="#link-emphasis"]').to_html).
+ to eq %{<a href="#link-emphasis"><em>text</em></a>}
- it 'autolinks short, invalid URLs' do
- expect(item(6).children.first.name).to eq 'a'
- expect(item(6).children.first['href']).to eq 'http://localhost:3000'
- end
+ expect(doc.at_css('a[href="#link-strong"]').to_html).
+ to eq %{<a href="#link-strong"><strong>text</strong></a>}
- %w(code a kbd).each do |elem|
- it "ignores links inside '#{elem}' element" do
- body = get_section('autolinkfilter')
- expect(body).not_to have_selector("#{elem} a")
+ expect(doc.at_css('a[href="#link-code"]').to_html).
+ to eq %{<a href="#link-code"><code>text</code></a>}
end
end
end
describe 'ExternalLinkFilter' do
- let(:links) { get_section('externallinkfilter').next_element }
-
it 'adds nofollow to external link' do
- expect(links.css('a').first.to_html).to match 'nofollow'
+ link = doc.at_css('a:contains("Google")')
+ expect(link.attr('rel')).to match 'nofollow'
end
it 'ignores internal link' do
- expect(links.css('a').last.to_html).not_to match 'nofollow'
+ link = doc.at_css('a:contains("GitLab Root")')
+ expect(link.attr('rel')).not_to match 'nofollow'
end
end
+ end
- describe 'ReferenceFilter' do
- it 'handles references in headers' do
- header = @doc.at_css('#reference-filters-eg-1').parent
-
- expect(header.css('a').size).to eq 2
- end
-
- it "handles references in Markdown" do
- body = get_section('reference-filters-eg-1')
- expect(body).to have_selector('em a.gfm-merge_request', count: 1)
- end
-
- it 'parses user references' do
- body = get_section('userreferencefilter')
- expect(body).to have_selector('a.gfm.gfm-project_member', count: 3)
- end
-
- it 'parses issue references' do
- body = get_section('issuereferencefilter')
- expect(body).to have_selector('a.gfm.gfm-issue', count: 2)
- end
-
- it 'parses merge request references' do
- body = get_section('mergerequestreferencefilter')
- expect(body).to have_selector('a.gfm.gfm-merge_request', count: 2)
- end
+ context 'default pipeline' do
+ before(:all) do
+ @feat = MarkdownFeature.new
- it 'parses snippet references' do
- body = get_section('snippetreferencefilter')
- expect(body).to have_selector('a.gfm.gfm-snippet', count: 2)
- end
+ # `gfm_with_options` depends on a `@project` variable
+ @project = @feat.project
- it 'parses commit range references' do
- body = get_section('commitrangereferencefilter')
- expect(body).to have_selector('a.gfm.gfm-commit_range', count: 2)
- end
+ @html = markdown(@feat.raw_markdown)
+ end
- it 'parses commit references' do
- body = get_section('commitreferencefilter')
- expect(body).to have_selector('a.gfm.gfm-commit', count: 2)
- end
+ it_behaves_like 'all pipelines'
- it 'parses label references' do
- body = get_section('labelreferencefilter')
- expect(body).to have_selector('a.gfm.gfm-label', count: 3)
- end
+ it 'includes RelativeLinkFilter' do
+ expect(doc).to parse_relative_links
end
- describe 'Task Lists' do
- it 'generates task lists' do
- body = get_section('task-lists')
- expect(body).to have_selector('ul.task-list', count: 2)
- expect(body).to have_selector('li.task-list-item', count: 7)
- expect(body).to have_selector('input[checked]', count: 3)
- end
+ it 'includes EmojiFilter' do
+ expect(doc).to parse_emoji
end
- end
-end
-
-# This is a helper class used by the GitLab Markdown feature spec
-#
-# Because the feature spec only cares about the output of the Markdown, and the
-# test setup and teardown and parsing is fairly expensive, we only want to do it
-# once. Unfortunately RSpec will not let you access `let`s in a `before(:all)`
-# block, so we fake it by encapsulating all the shared setup in this class.
-#
-# The class renders `spec/fixtures/markdown.md.erb` using ERB, allowing for
-# reference to the factory-created objects.
-class MarkdownFeature
- include FactoryGirl::Syntax::Methods
-
- def initialize
- DatabaseCleaner.start
- end
-
- def teardown
- DatabaseCleaner.clean
- end
-
- def user
- @user ||= create(:user)
- end
- def group
- unless @group
- @group = create(:group)
- @group.add_user(user, Gitlab::Access::DEVELOPER)
+ it 'includes TableOfContentsFilter' do
+ expect(doc).to create_header_links
end
- @group
- end
-
- # Direct references ----------------------------------------------------------
-
- def project
- @project ||= create(:project)
- end
-
- def issue
- @issue ||= create(:issue, project: project)
- end
-
- def merge_request
- @merge_request ||= create(:merge_request, :simple, source_project: project)
- end
-
- def snippet
- @snippet ||= create(:project_snippet, project: project)
- end
-
- def commit
- @commit ||= project.commit
- end
-
- def commit_range
- unless @commit_range
- commit2 = project.commit('HEAD~3')
- @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project)
+ it 'includes AutolinkFilter' do
+ expect(doc).to create_autolinks
end
- @commit_range
- end
-
- def simple_label
- @simple_label ||= create(:label, name: 'gfm', project: project)
- end
-
- def label
- @label ||= create(:label, name: 'awaiting feedback', project: project)
- end
-
- # Cross-references -----------------------------------------------------------
-
- def xproject
- unless @xproject
- namespace = create(:namespace, name: 'cross-reference')
- @xproject = create(:project, namespace: namespace)
- @xproject.team << [user, :developer]
+ it 'includes all reference filters' do
+ aggregate_failures do
+ expect(doc).to reference_users
+ expect(doc).to reference_issues
+ expect(doc).to reference_merge_requests
+ expect(doc).to reference_snippets
+ expect(doc).to reference_commit_ranges
+ expect(doc).to reference_commits
+ expect(doc).to reference_labels
+ end
end
- @xproject
- end
-
- def xissue
- @xissue ||= create(:issue, project: xproject)
- end
-
- def xmerge_request
- @xmerge_request ||= create(:merge_request, :simple, source_project: xproject)
- end
-
- def xsnippet
- @xsnippet ||= create(:project_snippet, project: xproject)
- end
-
- def xcommit
- @xcommit ||= xproject.commit
- end
-
- def xcommit_range
- unless @xcommit_range
- xcommit2 = xproject.commit('HEAD~2')
- @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject)
+ it 'includes TaskListFilter' do
+ expect(doc).to parse_task_lists
end
+ end
- @xcommit_range
+ # `markdown` calls these two methods
+ def current_user
+ @feat.user
end
- def raw_markdown
- fixture = Rails.root.join('spec/fixtures/markdown.md.erb')
- ERB.new(File.read(fixture)).result(binding)
+ def user_color_scheme_class
+ :white
end
end
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 02ab46c905a..41d12afa9ce 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -100,6 +100,13 @@ Markdown should be usable inside a link. Let's try!
- [**text**](#link-strong)
- [`text`](#link-code)
+### RelativeLinkFilter
+
+Linking to a file relative to this project's repository should work.
+
+[Relative Link](doc/README.md)
+![Relative Image](app/assets/images/touch-icon-ipad.png)
+
### EmojiFilter
Because life would be :zzz: without Emoji, right? :rocket:
@@ -123,9 +130,9 @@ These are all plain text that should get turned into links:
But it shouldn't autolink text inside certain tags:
-- <code>http://about.gitlab.com/</code>
-- <a>http://about.gitlab.com/</a>
-- <kbd>http://about.gitlab.com/</kbd>
+- <code>http://code.gitlab.com/</code>
+- <a>http://a.gitlab.com/</a>
+- <kbd>http://kbd.gitlab.com/</kbd>
### ExternalLinkFilter
diff --git a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb
index 5ee5310825d..7f4d67e403f 100644
--- a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb
@@ -1,3 +1,5 @@
+# encoding: UTF-8
+
require 'spec_helper'
module Gitlab::Markdown
@@ -101,6 +103,20 @@ module Gitlab::Markdown
expect(doc.at_css('a')['href']).to eq 'http://example.com'
end
+ it 'supports Unicode filenames' do
+ path = 'files/images/한글.png'
+ escaped = Addressable::URI.escape(path)
+
+ # Stub these methods so the file doesn't actually need to be in the repo
+ allow_any_instance_of(described_class).to receive(:file_exists?).
+ and_return(true)
+ allow_any_instance_of(described_class).
+ to receive(:image?).with(path).and_return(true)
+
+ doc = filter(image(escaped))
+ expect(doc.at_css('img')['src']).to match '/raw/'
+ end
+
context 'when requested path is a file in the repo' do
let(:requested_path) { 'doc/api/README.md' }
include_examples :relative_to_requested
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 63091e913ff..1ffd92b9bd9 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -111,14 +111,20 @@ describe Project do
expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
end
- it 'returns the full web URL for this repo' do
- project = Project.new(path: 'somewhere')
- expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/somewhere")
+ describe "#web_url" do
+ let(:project) { create(:empty_project, path: "somewhere") }
+
+ it 'returns the full web URL for this repo' do
+ expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.path}/somewhere")
+ end
end
- it 'returns the web URL without the protocol for this repo' do
- project = Project.new(path: 'somewhere')
- expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/somewhere")
+ describe "#web_url_without_protocol" do
+ let(:project) { create(:empty_project, path: "somewhere") }
+
+ it 'returns the web URL without the protocol for this repo' do
+ expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/#{project.namespace.path}/somewhere")
+ end
end
describe 'last_activity methods' do
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 439a492cea9..c04e842c67e 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -29,7 +29,7 @@ describe Projects::ForkService do
it "fails due to transaction failure" do
@to_project = fork_project(@from_project, @to_user, false)
expect(@to_project.errors).not_to be_empty
- expect(@to_project.errors[:base]).to include("Failed to fork repository")
+ expect(@to_project.errors[:base]).to include("Failed to fork repository via gitlab-shell")
end
end
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
new file mode 100644
index 00000000000..2a868aed73b
--- /dev/null
+++ b/spec/support/markdown_feature.rb
@@ -0,0 +1,106 @@
+# This is a helper class used by the GitLab Markdown feature spec
+#
+# Because the feature spec only cares about the output of the Markdown, and the
+# test setup and teardown and parsing is fairly expensive, we only want to do it
+# once. Unfortunately RSpec will not let you access `let`s in a `before(:all)`
+# block, so we fake it by encapsulating all the shared setup in this class.
+#
+# The class renders `spec/fixtures/markdown.md.erb` using ERB, allowing for
+# reference to the factory-created objects.
+class MarkdownFeature
+ include FactoryGirl::Syntax::Methods
+
+ def user
+ @user ||= create(:user)
+ end
+
+ def group
+ unless @group
+ @group = create(:group)
+ @group.add_user(user, Gitlab::Access::DEVELOPER)
+ end
+
+ @group
+ end
+
+ # Direct references ----------------------------------------------------------
+
+ def project
+ @project ||= create(:project)
+ end
+
+ def issue
+ @issue ||= create(:issue, project: project)
+ end
+
+ def merge_request
+ @merge_request ||= create(:merge_request, :simple, source_project: project)
+ end
+
+ def snippet
+ @snippet ||= create(:project_snippet, project: project)
+ end
+
+ def commit
+ @commit ||= project.commit
+ end
+
+ def commit_range
+ unless @commit_range
+ commit2 = project.commit('HEAD~3')
+ @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project)
+ end
+
+ @commit_range
+ end
+
+ def simple_label
+ @simple_label ||= create(:label, name: 'gfm', project: project)
+ end
+
+ def label
+ @label ||= create(:label, name: 'awaiting feedback', project: project)
+ end
+
+ # Cross-references -----------------------------------------------------------
+
+ def xproject
+ unless @xproject
+ namespace = create(:namespace, name: 'cross-reference')
+ @xproject = create(:project, namespace: namespace)
+ @xproject.team << [user, :developer]
+ end
+
+ @xproject
+ end
+
+ def xissue
+ @xissue ||= create(:issue, project: xproject)
+ end
+
+ def xmerge_request
+ @xmerge_request ||= create(:merge_request, :simple, source_project: xproject)
+ end
+
+ def xsnippet
+ @xsnippet ||= create(:project_snippet, project: xproject)
+ end
+
+ def xcommit
+ @xcommit ||= xproject.commit
+ end
+
+ def xcommit_range
+ unless @xcommit_range
+ xcommit2 = xproject.commit('HEAD~2')
+ @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject)
+ end
+
+ @xcommit_range
+ end
+
+ def raw_markdown
+ fixture = Rails.root.join('spec/fixtures/markdown.md.erb')
+ ERB.new(File.read(fixture)).result(binding)
+ end
+end
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
new file mode 100644
index 00000000000..9df226c3af8
--- /dev/null
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -0,0 +1,156 @@
+# MarkdownMatchers
+#
+# Custom matchers for our custom HTML::Pipeline filters. These are used to test
+# that specific filters are or are not used by our defined pipelines.
+#
+# Must be included manually.
+module MarkdownMatchers
+ extend RSpec::Matchers::DSL
+ include Capybara::Node::Matchers
+
+ # RelativeLinkFilter
+ matcher :parse_relative_links do
+ set_default_markdown_messages
+
+ match do |actual|
+ link = actual.at_css('a:contains("Relative Link")')
+ image = actual.at_css('img[alt="Relative Image"]')
+
+ expect(link['href']).to end_with('master/doc/README.md')
+ expect(image['src']).to end_with('master/app/assets/images/touch-icon-ipad.png')
+ end
+ end
+
+ # EmojiFilter
+ matcher :parse_emoji do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('img.emoji', count: 10)
+ end
+ end
+
+ # TableOfContentsFilter
+ matcher :create_header_links do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('h1 a#gitlab-markdown')
+ expect(actual).to have_selector('h2 a#markdown')
+ expect(actual).to have_selector('h3 a#autolinkfilter')
+ end
+ end
+
+ # AutolinkFilter
+ matcher :create_autolinks do
+ def have_autolink(link)
+ have_link(link, href: link)
+ end
+
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_autolink('http://about.gitlab.com/')
+ expect(actual).to have_autolink('https://google.com/')
+ expect(actual).to have_autolink('ftp://ftp.us.debian.org/debian/')
+ expect(actual).to have_autolink('smb://foo/bar/baz')
+ expect(actual).to have_autolink('irc://irc.freenode.net/git')
+ expect(actual).to have_autolink('http://localhost:3000')
+
+ %w(code a kbd).each do |elem|
+ expect(body).not_to have_selector("#{elem} a")
+ end
+ end
+ end
+
+ # UserReferenceFilter
+ matcher :reference_users do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('a.gfm.gfm-project_member', count: 3)
+ end
+ end
+
+ # IssueReferenceFilter
+ matcher :reference_issues do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('a.gfm.gfm-issue', count: 3)
+ end
+ end
+
+ # MergeRequestReferenceFilter
+ matcher :reference_merge_requests do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 3)
+ expect(actual).to have_selector('em a.gfm-merge_request')
+ end
+ end
+
+ # SnippetReferenceFilter
+ matcher :reference_snippets do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('a.gfm.gfm-snippet', count: 2)
+ end
+ end
+
+ # CommitRangeReferenceFilter
+ matcher :reference_commit_ranges do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 2)
+ end
+ end
+
+ # CommitReferenceFilter
+ matcher :reference_commits do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('a.gfm.gfm-commit', count: 2)
+ end
+ end
+
+ # LabelReferenceFilter
+ matcher :reference_labels do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('a.gfm.gfm-label', count: 3)
+ end
+ end
+
+ # TaskListFilter
+ matcher :parse_task_lists do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('ul.task-list', count: 2)
+ expect(actual).to have_selector('li.task-list-item', count: 7)
+ expect(actual).to have_selector('input[checked]', count: 3)
+ end
+ end
+end
+
+# Monkeypatch the matcher DSL so that we can reduce some noisy duplication for
+# setting the failure messages for these matchers
+module RSpec::Matchers::DSL::Macros
+ def set_default_markdown_messages
+ failure_message do
+ # expected to parse emoji, but didn't
+ "expected to #{description}, but didn't"
+ end
+
+ failure_message_when_negated do
+ # expected not to parse task lists, but did
+ "expected not to #{description}, but did"
+ end
+ end
+end