summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2015-05-27 10:03:14 +0000
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2015-05-27 10:03:14 +0000
commit701c2e9a3b4b60642eba0ee00c3e11e1e43fb131 (patch)
treec5bca23181bc9320cab36619b2287f1cc405623d
parent3ed05b2191a7ede4734e2b7952f3810e5a1ee728 (diff)
parent3cb6a338466ca9b8e2a831cce306fc6d650231ed (diff)
downloadgitlab-ce-701c2e9a3b4b60642eba0ee00c3e11e1e43fb131.tar.gz
Merge branch 'rs-to_reference' into 'master'
Add to_reference method to referable models Now there is a single source of information for which attribute a model uses to be referenced, and its special character. See merge request !641
-rw-r--r--app/helpers/gitlab_markdown_helper.rb24
-rw-r--r--app/models/commit.rb39
-rw-r--r--app/models/commit_range.rb30
-rw-r--r--app/models/concerns/mentionable.rb13
-rw-r--r--app/models/concerns/referable.rb61
-rw-r--r--app/models/external_issue.rb11
-rw-r--r--app/models/group.rb14
-rw-r--r--app/models/issue.rb29
-rw-r--r--app/models/label.rb41
-rw-r--r--app/models/merge_request.rb34
-rw-r--r--app/models/note.rb4
-rw-r--r--app/models/project.rb14
-rw-r--r--app/models/snippet.rb32
-rw-r--r--app/models/user.rb24
-rw-r--r--app/services/system_note_service.rb60
-rw-r--r--app/views/shared/snippets/_form.html.haml2
-rw-r--r--lib/gitlab/closing_issue_extractor.rb2
-rw-r--r--lib/gitlab/markdown/commit_range_reference_filter.rb9
-rw-r--r--lib/gitlab/markdown/commit_reference_filter.rb11
-rw-r--r--lib/gitlab/markdown/cross_project_reference.rb3
-rw-r--r--lib/gitlab/markdown/external_issue_reference_filter.rb9
-rw-r--r--lib/gitlab/markdown/issue_reference_filter.rb11
-rw-r--r--lib/gitlab/markdown/label_reference_filter.rb20
-rw-r--r--lib/gitlab/markdown/merge_request_reference_filter.rb11
-rw-r--r--lib/gitlab/markdown/reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/snippet_reference_filter.rb11
-rw-r--r--lib/gitlab/markdown/user_reference_filter.rb16
-rw-r--r--spec/features/gitlab_flavored_markdown_spec.rb34
-rw-r--r--spec/features/markdown_spec.rb21
-rw-r--r--spec/fixtures/markdown.md.erb64
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb10
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb70
-rw-r--r--spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb36
-rw-r--r--spec/lib/gitlab/markdown/commit_reference_filter_spec.rb16
-rw-r--r--spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb16
-rw-r--r--spec/lib/gitlab/markdown/issue_reference_filter_spec.rb19
-rw-r--r--spec/lib/gitlab/markdown/label_reference_filter_spec.rb67
-rw-r--r--spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb13
-rw-r--r--spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb11
-rw-r--r--spec/lib/gitlab/markdown/user_reference_filter_spec.rb45
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb4
-rw-r--r--spec/models/commit_range_spec.rb23
-rw-r--r--spec/models/commit_spec.rb24
-rw-r--r--spec/models/external_issue_spec.rb24
-rw-r--r--spec/models/group_spec.rb26
-rw-r--r--spec/models/issue_spec.rb28
-rw-r--r--spec/models/label_spec.rb67
-rw-r--r--spec/models/merge_request_spec.rb41
-rw-r--r--spec/models/project_spec.rb21
-rw-r--r--spec/models/snippet_spec.rb36
-rw-r--r--spec/models/user_spec.rb23
-rw-r--r--spec/services/system_note_service_spec.rb10
-rw-r--r--spec/support/mentionable_shared_examples.rb32
-rw-r--r--spec/support/reference_filter_spec_helper.rb20
54 files changed, 866 insertions, 472 deletions
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 7bcc011fd5f..d89f7b4a28d 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -135,15 +135,25 @@ module GitlabMarkdownHelper
end
end
+ # Returns the text necessary to reference `entity` across projects
+ #
+ # project - Project to reference
+ # entity - Object that responds to `to_reference`
+ #
+ # Examples:
+ #
+ # cross_project_reference(project, project.issues.first)
+ # # => 'namespace1/project1#123'
+ #
+ # cross_project_reference(project, project.merge_requests.first)
+ # # => 'namespace1/project1!345'
+ #
+ # Returns a String
def cross_project_reference(project, entity)
- path = project.path_with_namespace
-
- if entity.kind_of?(Issue)
- [path, entity.iid].join('#')
- elsif entity.kind_of?(MergeRequest)
- [path, entity.iid].join('!')
+ if entity.respond_to?(:to_reference)
+ "#{project.to_reference}#{entity.to_reference}"
else
- raise 'Not supported type'
+ ''
end
end
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index be5a118bfec..f02fe240540 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -1,9 +1,11 @@
class Commit
- include ActiveModel::Conversion
- include StaticModel
extend ActiveModel::Naming
+
+ include ActiveModel::Conversion
include Mentionable
include Participable
+ include Referable
+ include StaticModel
attr_mentionable :safe_message
participant :author, :committer, :notes, :mentioned_users
@@ -56,6 +58,34 @@ class Commit
@raw.id
end
+ def ==(other)
+ (self.class === other) && (raw == other.raw)
+ end
+
+ def self.reference_prefix
+ '@'
+ end
+
+ # Pattern used to extract commit references from text
+ #
+ # The SHA can be between 6 and 40 hex characters.
+ #
+ # This pattern supports cross-project references.
+ def self.reference_pattern
+ %r{
+ (?:#{Project.reference_pattern}#{reference_prefix})?
+ (?<commit>\h{6,40})
+ }x
+ end
+
+ def to_reference(from_project = nil)
+ if cross_project_reference?(from_project)
+ "#{project.to_reference}@#{id}"
+ else
+ id
+ end
+ end
+
def diff_line_count
@diff_line_count ||= Commit::diff_line_count(self.diffs)
@diff_line_count
@@ -126,11 +156,6 @@ class Commit
Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message)
end
- # Mentionable override.
- def gfm_reference
- "commit #{id}"
- end
-
def author
User.find_for_commit(author_email, author_name)
end
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
index e6456198264..86fc9eb01a3 100644
--- a/app/models/commit_range.rb
+++ b/app/models/commit_range.rb
@@ -19,6 +19,7 @@
#
class CommitRange
include ActiveModel::Conversion
+ include Referable
attr_reader :sha_from, :notation, :sha_to
@@ -28,10 +29,24 @@ class CommitRange
# See `exclude_start?`
attr_reader :exclude_start
- # The beginning and ending SHA sums can be between 6 and 40 hex characters,
- # and the range selection can be double- or triple-dot.
+ # The beginning and ending SHAs can be between 6 and 40 hex characters, and
+ # the range notation can be double- or triple-dot.
PATTERN = /\h{6,40}\.{2,3}\h{6,40}/
+ def self.reference_prefix
+ '@'
+ end
+
+ # Pattern used to extract commit range references from text
+ #
+ # This pattern supports cross-project references.
+ def self.reference_pattern
+ %r{
+ (?:#{Project.reference_pattern}#{reference_prefix})?
+ (?<commit_range>#{PATTERN})
+ }x
+ end
+
# Initialize a CommitRange
#
# range_string - The String commit range.
@@ -59,6 +74,17 @@ class CommitRange
"#{sha_from[0..7]}#{notation}#{sha_to[0..7]}"
end
+ def to_reference(from_project = nil)
+ # Not using to_s because we want the full SHAs
+ reference = sha_from + notation + sha_to
+
+ if cross_project_reference?(from_project)
+ reference = project.to_reference + '@' + reference
+ end
+
+ reference
+ end
+
# Returns a String for use in a link's title attribute
def reference_title
"Commits #{suffixed_sha_from} through #{sha_to}"
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index b7c39df885d..6f9f54d08cc 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -20,10 +20,15 @@ module Mentionable
end
end
- # Generate a GFM back-reference that will construct a link back to this Mentionable when rendered. Must
- # be overridden if this model object can be referenced directly by GFM notation.
- def gfm_reference
- raise NotImplementedError.new("#{self.class} does not implement #gfm_reference")
+ # Returns the text used as the body of a Note when this object is referenced
+ #
+ # By default this will be the class name and the result of calling
+ # `to_reference` on the object.
+ def gfm_reference(from_project = nil)
+ # "MergeRequest" > "merge_request" > "Merge request" > "merge request"
+ friendly_name = self.class.to_s.underscore.humanize.downcase
+
+ "#{friendly_name} #{to_reference(from_project)}"
end
# Construct a String that contains possible GFM references.
diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb
new file mode 100644
index 00000000000..cced66cc1e4
--- /dev/null
+++ b/app/models/concerns/referable.rb
@@ -0,0 +1,61 @@
+# == Referable concern
+#
+# Contains functionality related to making a model referable in Markdown, such
+# as "#1", "!2", "~3", etc.
+module Referable
+ extend ActiveSupport::Concern
+
+ # Returns the String necessary to reference this object in Markdown
+ #
+ # from_project - Refering Project object
+ #
+ # This should be overridden by the including class.
+ #
+ # Examples:
+ #
+ # Issue.first.to_reference # => "#1"
+ # Issue.last.to_reference(other_project) # => "cross-project#1"
+ #
+ # Returns a String
+ def to_reference(_from_project = nil)
+ ''
+ end
+
+ module ClassMethods
+ # The character that prefixes the actual reference identifier
+ #
+ # This should be overridden by the including class.
+ #
+ # Examples:
+ #
+ # Issue.reference_prefix # => '#'
+ # MergeRequest.reference_prefix # => '!'
+ #
+ # Returns a String
+ def reference_prefix
+ ''
+ end
+
+ # Regexp pattern used to match references to this object
+ #
+ # This must be overridden by the including class.
+ #
+ # Returns a Regexp
+ def reference_pattern
+ raise NotImplementedError, "#{self} does not implement #{__method__}"
+ end
+ end
+
+ private
+
+ # Check if a reference is being done cross-project
+ #
+ # from_project - Refering Project object
+ def cross_project_reference?(from_project)
+ if self.is_a?(Project)
+ self != from_project
+ else
+ from_project && self.project && self.project != from_project
+ end
+ end
+end
diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb
index 85fdb12bfdc..49f6c95e045 100644
--- a/app/models/external_issue.rb
+++ b/app/models/external_issue.rb
@@ -1,4 +1,6 @@
class ExternalIssue
+ include Referable
+
def initialize(issue_identifier, project)
@issue_identifier, @project = issue_identifier, project
end
@@ -26,4 +28,13 @@ class ExternalIssue
def project
@project
end
+
+ # Pattern used to extract `JIRA-123` issue references from text
+ def self.reference_pattern
+ %r{(?<issue>([A-Z\-]+-)\d+)}
+ end
+
+ def to_reference(_from_project = nil)
+ id
+ end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 687458adac4..b4e908c5602 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -17,6 +17,8 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Group < Namespace
+ include Referable
+
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
has_many :users, through: :group_members
@@ -36,6 +38,18 @@ class Group < Namespace
def sort(method)
order_by(method)
end
+
+ def reference_prefix
+ User.reference_prefix
+ end
+
+ def reference_pattern
+ User.reference_pattern
+ end
+ end
+
+ def to_reference(_from_project = nil)
+ "#{self.class.reference_prefix}#{name}"
end
def human_name
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 6e102051387..2456b7d0dc1 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -21,10 +21,11 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Issue < ActiveRecord::Base
- include Issuable
include InternalId
- include Taskable
+ include Issuable
+ include Referable
include Sortable
+ include Taskable
ActsAsTaggableOn.strict_case_match = true
@@ -53,10 +54,28 @@ class Issue < ActiveRecord::Base
attributes
end
- # Mentionable overrides.
+ def self.reference_prefix
+ '#'
+ end
+
+ # Pattern used to extract `#123` issue references from text
+ #
+ # This pattern supports cross-project references.
+ def self.reference_pattern
+ %r{
+ (#{Project.reference_pattern})?
+ #{Regexp.escape(reference_prefix)}(?<issue>\d+)
+ }x
+ end
+
+ def to_reference(from_project = nil)
+ reference = "#{self.class.reference_prefix}#{iid}"
+
+ if cross_project_reference?(from_project)
+ reference = project.to_reference + reference
+ end
- def gfm_reference
- "issue ##{iid}"
+ reference
end
# Reset issue events cache
diff --git a/app/models/label.rb b/app/models/label.rb
index eee28acefc1..230631b5180 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -11,6 +11,8 @@
#
class Label < ActiveRecord::Base
+ include Referable
+
DEFAULT_COLOR = '#428BCA'
default_value_for :color, DEFAULT_COLOR
@@ -34,6 +36,45 @@ class Label < ActiveRecord::Base
alias_attribute :name, :title
+ def self.reference_prefix
+ '~'
+ end
+
+ # Pattern used to extract label references from text
+ def self.reference_pattern
+ %r{
+ #{reference_prefix}
+ (?:
+ (?<label_id>\d+) | # Integer-based label ID, or
+ (?<label_name>
+ [A-Za-z0-9_-]+ | # String-based single-word label title, or
+ "[^&\?,]+" # String-based multi-word label surrounded in quotes
+ )
+ )
+ }x
+ end
+
+ # Returns the String necessary to reference this Label in Markdown
+ #
+ # format - Symbol format to use (default: :id, optional: :name)
+ #
+ # Note that its argument differs from other objects implementing Referable. If
+ # a non-Symbol argument is given (such as a Project), it will default to :id.
+ #
+ # Examples:
+ #
+ # Label.first.to_reference # => "~1"
+ # Label.first.to_reference(:name) # => "~\"bug\""
+ #
+ # Returns a String
+ def to_reference(format = :id)
+ if format == :name && !name.include?('"')
+ %(#{self.class.reference_prefix}"#{name}")
+ else
+ "#{self.class.reference_prefix}#{id}"
+ end
+ end
+
def open_issues_count
issues.opened.count
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 64f3c39f131..c57016dd6a2 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -25,10 +25,11 @@ require Rails.root.join("app/models/commit")
require Rails.root.join("lib/static_model")
class MergeRequest < ActiveRecord::Base
- include Issuable
- include Taskable
include InternalId
+ include Issuable
+ include Referable
include Sortable
+ include Taskable
belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
@@ -135,6 +136,30 @@ class MergeRequest < ActiveRecord::Base
scope :closed, -> { with_states(:closed, :merged) }
scope :declined, -> { with_states(:closed) }
+ def self.reference_prefix
+ '!'
+ end
+
+ # Pattern used to extract `!123` merge request references from text
+ #
+ # This pattern supports cross-project references.
+ def self.reference_pattern
+ %r{
+ (#{Project.reference_pattern})?
+ #{Regexp.escape(reference_prefix)}(?<merge_request>\d+)
+ }x
+ end
+
+ def to_reference(from_project = nil)
+ reference = "#{self.class.reference_prefix}#{iid}"
+
+ if cross_project_reference?(from_project)
+ reference = project.to_reference + reference
+ end
+
+ reference
+ end
+
def validate_branches
if target_project == source_project && target_branch == source_branch
errors.add :branch_conflict, "You can not use same project/branch for source and target"
@@ -289,11 +314,6 @@ class MergeRequest < ActiveRecord::Base
end
end
- # Mentionable override.
- def gfm_reference
- "merge request !#{iid}"
- end
-
def target_project_path
if target_project
target_project.path_with_namespace
diff --git a/app/models/note.rb b/app/models/note.rb
index 6939a7e73a0..d5f716b3de0 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -326,8 +326,8 @@ class Note < ActiveRecord::Base
end
# Mentionable override.
- def gfm_reference
- noteable.gfm_reference
+ def gfm_reference(from_project = nil)
+ noteable.gfm_reference(from_project)
end
# Mentionable override.
diff --git a/app/models/project.rb b/app/models/project.rb
index 09d3ffd22fe..3c9f0dad28b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -33,11 +33,12 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Project < ActiveRecord::Base
- include Sortable
+ include Gitlab::ConfigHelper
include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel
- include Gitlab::ConfigHelper
include Rails.application.routes.url_helpers
+ include Referable
+ include Sortable
extend Gitlab::ConfigHelper
extend Enumerize
@@ -247,6 +248,11 @@ class Project < ActiveRecord::Base
order_by(method)
end
end
+
+ def reference_pattern
+ name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR
+ %r{(?<project>#{name_pattern}/#{name_pattern})}
+ end
end
def team
@@ -305,6 +311,10 @@ class Project < ActiveRecord::Base
path
end
+ def to_reference(_from_project = nil)
+ path_with_namespace
+ end
+
def web_url
[gitlab_config.url, path_with_namespace].join('/')
end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index d2af26539b6..3ab9e834c63 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -16,14 +16,16 @@
#
class Snippet < ActiveRecord::Base
- include Sortable
- include Linguist::BlobHelper
include Gitlab::VisibilityLevel
+ include Linguist::BlobHelper
include Participable
+ include Referable
+ include Sortable
default_value_for :visibility_level, Snippet::PRIVATE
- belongs_to :author, class_name: "User"
+ belongs_to :author, class_name: 'User'
+ belongs_to :project
has_many :notes, as: :noteable, dependent: :destroy
@@ -50,6 +52,30 @@ class Snippet < ActiveRecord::Base
participant :author, :notes
+ def self.reference_prefix
+ '$'
+ end
+
+ # Pattern used to extract `$123` snippet references from text
+ #
+ # This pattern supports cross-project references.
+ def self.reference_pattern
+ %r{
+ (#{Project.reference_pattern})?
+ #{Regexp.escape(reference_prefix)}(?<snippet>\d+)
+ }x
+ end
+
+ def to_reference(from_project = nil)
+ reference = "#{self.class.reference_prefix}#{id}"
+
+ if cross_project_reference?(from_project)
+ reference = project.to_reference + reference
+ end
+
+ reference
+ end
+
def self.content_types
[
".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
diff --git a/app/models/user.rb b/app/models/user.rb
index 4dd37e73564..50ca4bc5acc 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -62,11 +62,13 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class User < ActiveRecord::Base
- include Sortable
- include Gitlab::ConfigHelper
- include TokenAuthenticatable
extend Gitlab::ConfigHelper
+
+ include Gitlab::ConfigHelper
include Gitlab::CurrentSettings
+ include Referable
+ include Sortable
+ include TokenAuthenticatable
default_value_for :admin, false
default_value_for :can_create_group, gitlab_config.default_can_create_group
@@ -247,6 +249,18 @@ class User < ActiveRecord::Base
def build_user(attrs = {})
User.new(attrs)
end
+
+ def reference_prefix
+ '@'
+ end
+
+ # Pattern used to extract `@user` user references from text
+ def reference_pattern
+ %r{
+ #{Regexp.escape(reference_prefix)}
+ (?<user>#{Gitlab::Regex::NAMESPACE_REGEX_STR})
+ }x
+ end
end
#
@@ -257,6 +271,10 @@ class User < ActiveRecord::Base
username
end
+ def to_reference(_from_project = nil)
+ "#{self.class.reference_prefix}#{username}"
+ end
+
def notification
@notification ||= Notification.new(self)
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 1909ae0d6f1..1527ae0486d 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -10,7 +10,7 @@ class SystemNoteService
# author - User performing the change
# new_commits - Array of Commits added since last push
# existing_commits - Array of Commits added in a previous push
- # oldrev - TODO (rspeicher): I have no idea what this actually does
+ # oldrev - Optional String SHA of a previous Commit
#
# See new_commit_summary and existing_commit_summary.
#
@@ -157,11 +157,11 @@ class SystemNoteService
#
# Example Note text:
#
- # "Mentioned in #1"
+ # "mentioned in #1"
#
- # "Mentioned in !2"
+ # "mentioned in !2"
#
- # "Mentioned in 54f7727c"
+ # "mentioned in 54f7727c"
#
# See cross_reference_note_content.
#
@@ -169,7 +169,7 @@ class SystemNoteService
def self.cross_reference(noteable, mentioner, author)
return if cross_reference_disallowed?(noteable, mentioner)
- gfm_reference = mentioner_gfm_ref(noteable, mentioner)
+ gfm_reference = mentioner.gfm_reference(noteable.project)
note_options = {
project: noteable.project,
@@ -200,12 +200,21 @@ class SystemNoteService
#
# Returns Boolean
def self.cross_reference_disallowed?(noteable, mentioner)
- return false unless MergeRequest === mentioner
- return false unless Commit === noteable
+ return false unless mentioner.is_a?(MergeRequest)
+ return false unless noteable.is_a?(Commit)
mentioner.commits.include?(noteable)
end
+ # Check if a cross reference to a noteable from a mentioner already exists
+ #
+ # This method is used to prevent multiple notes being created for a mention
+ # when a issue is updated, for example.
+ #
+ # noteable - Noteable object being referenced
+ # mentioner - Mentionable object
+ #
+ # Returns Boolean
def self.cross_reference_exists?(noteable, mentioner)
# Initial scope should be system notes of this noteable type
notes = Note.system.where(noteable_type: noteable.class)
@@ -217,7 +226,7 @@ class SystemNoteService
notes = notes.where(noteable_id: noteable.id)
end
- gfm_reference = mentioner_gfm_ref(noteable, mentioner, true)
+ gfm_reference = mentioner.gfm_reference(noteable.project)
notes = notes.where(note: cross_reference_note_content(gfm_reference))
notes.count > 0
@@ -229,39 +238,6 @@ class SystemNoteService
Note.create(args.merge(system: true))
end
- # Prepend the mentioner's namespaced project path to the GFM reference for
- # cross-project references. For same-project references, return the
- # unmodified GFM reference.
- def self.mentioner_gfm_ref(noteable, mentioner, cross_reference = false)
- # FIXME (rspeicher): This was breaking things.
- # if mentioner.is_a?(Commit) && cross_reference
- # return mentioner.gfm_reference.sub('commit ', 'commit %')
- # end
-
- full_gfm_reference(mentioner.project, noteable.project, mentioner)
- end
-
- # Return the +mentioner+ GFM reference. If the mentioner and noteable
- # projects are not the same, add the mentioning project's path to the
- # returned value.
- def self.full_gfm_reference(mentioning_project, noteable_project, mentioner)
- if mentioning_project == noteable_project
- mentioner.gfm_reference
- else
- if mentioner.is_a?(Commit)
- mentioner.gfm_reference.sub(
- /(commit )/,
- "\\1#{mentioning_project.path_with_namespace}@"
- )
- else
- mentioner.gfm_reference.sub(
- /(issue |merge request )/,
- "\\1#{mentioning_project.path_with_namespace}"
- )
- end
- end
- end
-
def self.cross_reference_note_prefix
'mentioned in '
end
@@ -286,7 +262,7 @@ class SystemNoteService
#
# noteable - MergeRequest object
# existing_commits - Array of existing Commit objects
- # oldrev - Optional String SHA of ... TODO (rspeicher): I have no idea what this actually does.
+ # oldrev - Optional String SHA of a previous Commit
#
# Examples:
#
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 9610f9ce414..2feeeecc48b 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -29,7 +29,7 @@
- else
= f.submit 'Save', class: "btn-save btn"
- - if @snippet.respond_to?(:project)
+ - if @snippet.project_id
= link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel"
- else
= link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index ab184d95c05..aeec595782c 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -8,7 +8,7 @@ module Gitlab
def closed_by_message(message)
return [] if message.nil?
-
+
closing_statements = message.scan(ISSUE_CLOSING_REGEX).
map { |ref| ref[0] }.join(" ")
diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb
index 8764f7e474f..61591a9914b 100644
--- a/lib/gitlab/markdown/commit_range_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_range_reference_filter.rb
@@ -19,7 +19,7 @@ module Gitlab
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
- text.gsub(COMMIT_RANGE_PATTERN) do |match|
+ text.gsub(CommitRange.reference_pattern) do |match|
yield match, $~[:commit_range], $~[:project]
end
end
@@ -30,13 +30,8 @@ module Gitlab
@commit_map = {}
end
- # Pattern used to extract commit range references from text
- #
- # This pattern supports cross-project references.
- COMMIT_RANGE_PATTERN = /(#{PROJECT_PATTERN}@)?(?<commit_range>#{CommitRange::PATTERN})/
-
def call
- replace_text_nodes_matching(COMMIT_RANGE_PATTERN) do |content|
+ replace_text_nodes_matching(CommitRange.reference_pattern) do |content|
commit_range_link_filter(content)
end
end
diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb
index b20b29f5d0c..f6932e76e70 100644
--- a/lib/gitlab/markdown/commit_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_reference_filter.rb
@@ -19,20 +19,13 @@ module Gitlab
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
- text.gsub(COMMIT_PATTERN) do |match|
+ text.gsub(Commit.reference_pattern) do |match|
yield match, $~[:commit], $~[:project]
end
end
- # Pattern used to extract commit references from text
- #
- # The SHA1 sum can be between 6 and 40 hex characters.
- #
- # This pattern supports cross-project references.
- COMMIT_PATTERN = /(#{PROJECT_PATTERN}@)?(?<commit>\h{6,40})/
-
def call
- replace_text_nodes_matching(COMMIT_PATTERN) do |content|
+ replace_text_nodes_matching(Commit.reference_pattern) do |content|
commit_link_filter(content)
end
end
diff --git a/lib/gitlab/markdown/cross_project_reference.rb b/lib/gitlab/markdown/cross_project_reference.rb
index c436fabd658..66c256c5104 100644
--- a/lib/gitlab/markdown/cross_project_reference.rb
+++ b/lib/gitlab/markdown/cross_project_reference.rb
@@ -3,9 +3,6 @@ module Gitlab
# Common methods for ReferenceFilters that support an optional cross-project
# reference.
module CrossProjectReference
- NAMING_PATTERN = Gitlab::Regex::NAMESPACE_REGEX_STR
- PROJECT_PATTERN = "(?<project>#{NAMING_PATTERN}/#{NAMING_PATTERN})"
-
# Given a cross-project reference string, get the Project record
#
# Defaults to value of `context[:project]` if:
diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/gitlab/markdown/external_issue_reference_filter.rb
index 0fc3f4cca06..afd28dd8cf3 100644
--- a/lib/gitlab/markdown/external_issue_reference_filter.rb
+++ b/lib/gitlab/markdown/external_issue_reference_filter.rb
@@ -16,19 +16,16 @@ module Gitlab
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
- text.gsub(ISSUE_PATTERN) do |match|
+ text.gsub(ExternalIssue.reference_pattern) do |match|
yield match, $~[:issue]
end
end
- # Pattern used to extract `JIRA-123` issue references from text
- ISSUE_PATTERN = /(?<issue>([A-Z\-]+-)\d+)/
-
def call
# Early return if the project isn't using an external tracker
return doc if project.nil? || project.default_issues_tracker?
- replace_text_nodes_matching(ISSUE_PATTERN) do |content|
+ replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content|
issue_link_filter(content)
end
end
@@ -51,7 +48,7 @@ module Gitlab
%(<a href="#{url}"
title="#{title}"
- class="#{klass}">#{issue}</a>)
+ class="#{klass}">#{match}</a>)
end
end
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb
index 1e885615163..dea04761ead 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/gitlab/markdown/issue_reference_filter.rb
@@ -20,18 +20,13 @@ module Gitlab
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
- text.gsub(ISSUE_PATTERN) do |match|
+ text.gsub(Issue.reference_pattern) do |match|
yield match, $~[:issue].to_i, $~[:project]
end
end
- # Pattern used to extract `#123` issue references from text
- #
- # This pattern supports cross-project references.
- ISSUE_PATTERN = /#{PROJECT_PATTERN}?\#(?<issue>([a-zA-Z\-]+-)?\d+)/
-
def call
- replace_text_nodes_matching(ISSUE_PATTERN) do |content|
+ replace_text_nodes_matching(Issue.reference_pattern) do |content|
issue_link_filter(content)
end
end
@@ -57,7 +52,7 @@ module Gitlab
%(<a href="#{url}"
title="#{title}"
- class="#{klass}">#{project_ref}##{id}</a>)
+ class="#{klass}">#{match}</a>)
else
match
end
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb
index 1a77becee89..e022ca69c91 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/gitlab/markdown/label_reference_filter.rb
@@ -15,26 +15,13 @@ module Gitlab
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
- text.gsub(LABEL_PATTERN) do |match|
+ text.gsub(Label.reference_pattern) do |match|
yield match, $~[:label_id].to_i, $~[:label_name]
end
end
- # Pattern used to extract label references from text
- #
- # TODO (rspeicher): Limit to double quotes (meh) or disallow single quotes in label names (bad).
- LABEL_PATTERN = %r{
- ~(
- (?<label_id>\d+) | # Integer-based label ID, or
- (?<label_name>
- [A-Za-z0-9_-]+ | # String-based single-word label title
- ['"][^&\?,]+['"] # String-based multi-word label surrounded in quotes
- )
- )
- }x
-
def call
- replace_text_nodes_matching(LABEL_PATTERN) do |content|
+ replace_text_nodes_matching(Label.reference_pattern) do |content|
label_link_filter(content)
end
end
@@ -85,8 +72,7 @@ module Gitlab
# Returns a Hash.
def label_params(id, name)
if name
- # TODO (rspeicher): Don't strip single quotes if we decide to only use double quotes for surrounding.
- { name: name.tr('\'"', '') }
+ { name: name.tr('"', '') }
else
{ id: id }
end
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index 740d72abb36..80779819485 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -20,18 +20,13 @@ module Gitlab
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
- text.gsub(MERGE_REQUEST_PATTERN) do |match|
+ text.gsub(MergeRequest.reference_pattern) do |match|
yield match, $~[:merge_request].to_i, $~[:project]
end
end
- # Pattern used to extract `!123` merge request references from text
- #
- # This pattern supports cross-project references.
- MERGE_REQUEST_PATTERN = /#{PROJECT_PATTERN}?!(?<merge_request>\d+)/
-
def call
- replace_text_nodes_matching(MERGE_REQUEST_PATTERN) do |content|
+ replace_text_nodes_matching(MergeRequest.reference_pattern) do |content|
merge_request_link_filter(content)
end
end
@@ -57,7 +52,7 @@ module Gitlab
%(<a href="#{url}"
title="#{title}"
- class="#{klass}">#{project_ref}!#{id}</a>)
+ class="#{klass}">#{match}</a>)
else
match
end
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index a4303d96bef..be4d26af0fc 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -1,5 +1,5 @@
require 'active_support/core_ext/string/output_safety'
-require 'html/pipeline'
+require 'html/pipeline/filter'
module Gitlab
module Markdown
diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb
index 64a0a2696f7..174ba58af6c 100644
--- a/lib/gitlab/markdown/snippet_reference_filter.rb
+++ b/lib/gitlab/markdown/snippet_reference_filter.rb
@@ -20,18 +20,13 @@ module Gitlab
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
- text.gsub(SNIPPET_PATTERN) do |match|
+ text.gsub(Snippet.reference_pattern) do |match|
yield match, $~[:snippet].to_i, $~[:project]
end
end
- # Pattern used to extract `$123` snippet references from text
- #
- # This pattern supports cross-project references.
- SNIPPET_PATTERN = /#{PROJECT_PATTERN}?\$(?<snippet>\d+)/
-
def call
- replace_text_nodes_matching(SNIPPET_PATTERN) do |content|
+ replace_text_nodes_matching(Snippet.reference_pattern) do |content|
snippet_link_filter(content)
end
end
@@ -57,7 +52,7 @@ module Gitlab
%(<a href="#{url}"
title="#{title}"
- class="#{klass}">#{project_ref}$#{id}</a>)
+ class="#{klass}">#{match}</a>)
else
match
end
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb
index 28ec041b1d4..c9972957182 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/gitlab/markdown/user_reference_filter.rb
@@ -16,16 +16,13 @@ module Gitlab
#
# Returns a String replaced with the return of the block.
def self.references_in(text)
- text.gsub(USER_PATTERN) do |match|
+ text.gsub(User.reference_pattern) do |match|
yield match, $~[:user]
end
end
- # Pattern used to extract `@user` user references from text
- USER_PATTERN = /@(?<user>#{Gitlab::Regex::NAMESPACE_REGEX_STR})/
-
def call
- replace_text_nodes_matching(USER_PATTERN) do |content|
+ replace_text_nodes_matching(User.reference_pattern) do |content|
user_link_filter(content)
end
end
@@ -68,7 +65,8 @@ module Gitlab
url = urls.namespace_project_url(project.namespace, project,
only_path: context[:only_path])
- %(<a href="#{url}" class="#{link_class}">@all</a>)
+ text = User.reference_prefix + 'all'
+ %(<a href="#{url}" class="#{link_class}">#{text}</a>)
end
def link_to_namespace(namespace)
@@ -86,7 +84,8 @@ module Gitlab
url = urls.group_url(group, only_path: context[:only_path])
- %(<a href="#{url}" class="#{link_class}">@#{group}</a>)
+ text = Group.reference_prefix + group
+ %(<a href="#{url}" class="#{link_class}">#{text}</a>)
end
def link_to_user(user, namespace)
@@ -94,7 +93,8 @@ module Gitlab
url = urls.user_url(user, only_path: context[:only_path])
- %(<a href="#{url}" class="#{link_class}">@#{user}</a>)
+ text = User.reference_prefix + user
+ %(<a href="#{url}" class="#{link_class}">#{text}</a>)
end
def user_can_reference_group?(group)
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index 133beba7b98..16d1ca55f8d 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -11,7 +11,7 @@ describe "GitLab Flavored Markdown", feature: true do
end
before do
- Commit.any_instance.stub(title: "fix ##{issue.iid}\n\nask @#{fred.username} for details")
+ Commit.any_instance.stub(title: "fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
end
let(:commit) { project.commit }
@@ -25,25 +25,25 @@ describe "GitLab Flavored Markdown", feature: true do
it "should render title in commits#index" do
visit namespace_project_commits_path(project.namespace, project, 'master', limit: 1)
- expect(page).to have_link("##{issue.iid}")
+ expect(page).to have_link(issue.to_reference)
end
it "should render title in commits#show" do
visit namespace_project_commit_path(project.namespace, project, commit)
- expect(page).to have_link("##{issue.iid}")
+ expect(page).to have_link(issue.to_reference)
end
it "should render description in commits#show" do
visit namespace_project_commit_path(project.namespace, project, commit)
- expect(page).to have_link("@#{fred.username}")
+ expect(page).to have_link(fred.to_reference)
end
it "should render title in repositories#branches" do
visit namespace_project_branches_path(project.namespace, project)
- expect(page).to have_link("##{issue.iid}")
+ expect(page).to have_link(issue.to_reference)
end
end
@@ -57,20 +57,20 @@ describe "GitLab Flavored Markdown", feature: true do
author: @user,
assignee: @user,
project: project,
- title: "fix ##{@other_issue.iid}",
- description: "ask @#{fred.username} for details")
+ title: "fix #{@other_issue.to_reference}",
+ description: "ask #{fred.to_reference} for details")
end
it "should render subject in issues#index" do
visit namespace_project_issues_path(project.namespace, project)
- expect(page).to have_link("##{@other_issue.iid}")
+ expect(page).to have_link(@other_issue.to_reference)
end
it "should render subject in issues#show" do
visit namespace_project_issue_path(project.namespace, project, @issue)
- expect(page).to have_link("##{@other_issue.iid}")
+ expect(page).to have_link(@other_issue.to_reference)
end
it "should render details in issues#show" do
@@ -83,19 +83,19 @@ describe "GitLab Flavored Markdown", feature: true do
describe "for merge requests" do
before do
- @merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix ##{issue.iid}")
+ @merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix #{issue.to_reference}")
end
it "should render title in merge_requests#index" do
visit namespace_project_merge_requests_path(project.namespace, project)
- expect(page).to have_link("##{issue.iid}")
+ expect(page).to have_link(issue.to_reference)
end
it "should render title in merge_requests#show" do
visit namespace_project_merge_request_path(project.namespace, project, @merge_request)
- expect(page).to have_link("##{issue.iid}")
+ expect(page).to have_link(issue.to_reference)
end
end
@@ -104,26 +104,26 @@ describe "GitLab Flavored Markdown", feature: true do
before do
@milestone = create(:milestone,
project: project,
- title: "fix ##{issue.iid}",
- description: "ask @#{fred.username} for details")
+ title: "fix #{issue.to_reference}",
+ description: "ask #{fred.to_reference} for details")
end
it "should render title in milestones#index" do
visit namespace_project_milestones_path(project.namespace, project)
- expect(page).to have_link("##{issue.iid}")
+ expect(page).to have_link(issue.to_reference)
end
it "should render title in milestones#show" do
visit namespace_project_milestone_path(project.namespace, project, @milestone)
- expect(page).to have_link("##{issue.iid}")
+ expect(page).to have_link(issue.to_reference)
end
it "should render description in milestones#show" do
visit namespace_project_milestone_path(project.namespace, project, @milestone)
- expect(page).to have_link("@#{fred.username}")
+ expect(page).to have_link(fred.to_reference)
end
end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index 8f3dfc8d5a9..d6954174660 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -66,6 +66,10 @@ describe 'GitLab Markdown' do
@doc.at_css("##{id}").parent.next_element
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
@@ -344,13 +348,13 @@ class MarkdownFeature
end
def commit
- @commit ||= project.repository.commit
+ @commit ||= project.commit
end
def commit_range
unless @commit_range
- commit2 = project.repository.commit('HEAD~3')
- @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}")
+ commit2 = project.commit('HEAD~3')
+ @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project)
end
@commit_range
@@ -376,11 +380,6 @@ class MarkdownFeature
@xproject
end
- # Shortcut to "cross-reference/project"
- def xref
- xproject.path_with_namespace
- end
-
def xissue
@xissue ||= create(:issue, project: xproject)
end
@@ -394,13 +393,13 @@ class MarkdownFeature
end
def xcommit
- @xcommit ||= xproject.repository.commit
+ @xcommit ||= xproject.commit
end
def xcommit_range
unless @xcommit_range
- xcommit2 = xproject.repository.commit('HEAD~2')
- @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}")
+ xcommit2 = xproject.commit('HEAD~2')
+ @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject)
end
@xcommit_range
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 64817ec6700..26fc4e38e5a 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -127,61 +127,61 @@ But it shouldn't autolink text inside certain tags:
- <a>http://about.gitlab.com/</a>
- <kbd>http://about.gitlab.com/</kbd>
-### Reference Filters (e.g., #<%= issue.iid %>)
+### Reference Filters (e.g., <%= issue.to_reference %>)
-References should be parseable even inside _!<%= merge_request.iid %>_ emphasis.
+References should be parseable even inside _<%= merge_request.to_reference %>_ emphasis.
#### UserReferenceFilter
- All: @all
-- User: @<%= user.username %>
-- Group: @<%= group.name %>
-- Ignores invalid: @fake_user
-- Ignored in code: `@<%= user.username %>`
-- Ignored in links: [Link to @<%= user.username %>](#user-link)
+- User: <%= user.to_reference %>
+- Group: <%= group.to_reference %>
+- Ignores invalid: <%= User.reference_prefix %>fake_user
+- Ignored in code: `<%= user.to_reference %>`
+- Ignored in links: [Link to <%= user.to_reference %>](#user-link)
#### IssueReferenceFilter
-- Issue: #<%= issue.iid %>
-- Issue in another project: <%= xref %>#<%= xissue.iid %>
-- Ignored in code: `#<%= issue.iid %>`
-- Ignored in links: [Link to #<%= issue.iid %>](#issue-link)
+- Issue: <%= issue.to_reference %>
+- Issue in another project: <%= xissue.to_reference(project) %>
+- Ignored in code: `<%= issue.to_reference %>`
+- Ignored in links: [Link to <%= issue.to_reference %>](#issue-link)
#### MergeRequestReferenceFilter
-- Merge request: !<%= merge_request.iid %>
-- Merge request in another project: <%= xref %>!<%= xmerge_request.iid %>
-- Ignored in code: `!<%= merge_request.iid %>`
-- Ignored in links: [Link to !<%= merge_request.iid %>](#merge-request-link)
+- Merge request: <%= merge_request.to_reference %>
+- Merge request in another project: <%= xmerge_request.to_reference(project) %>
+- Ignored in code: `<%= merge_request.to_reference %>`
+- Ignored in links: [Link to <%= merge_request.to_reference %>](#merge-request-link)
#### SnippetReferenceFilter
-- Snippet: $<%= snippet.id %>
-- Snippet in another project: <%= xref %>$<%= xsnippet.id %>
-- Ignored in code: `$<%= snippet.id %>`
-- Ignored in links: [Link to $<%= snippet.id %>](#snippet-link)
+- Snippet: <%= snippet.to_reference %>
+- Snippet in another project: <%= xsnippet.to_reference(project) %>
+- Ignored in code: `<%= snippet.to_reference %>`
+- Ignored in links: [Link to <%= snippet.to_reference %>](#snippet-link)
#### CommitRangeReferenceFilter
-- Range: <%= commit_range %>
-- Range in another project: <%= xref %>@<%= xcommit_range %>
-- Ignored in code: `<%= commit_range %>`
-- Ignored in links: [Link to <%= commit_range %>](#commit-range-link)
+- Range: <%= commit_range.to_reference %>
+- Range in another project: <%= xcommit_range.to_reference(project) %>
+- Ignored in code: `<%= commit_range.to_reference %>`
+- Ignored in links: [Link to <%= commit_range.to_reference %>](#commit-range-link)
#### CommitReferenceFilter
-- Commit: <%= commit.id %>
-- Commit in another project: <%= xref %>@<%= xcommit.id %>
-- Ignored in code: `<%= commit.id %>`
-- Ignored in links: [Link to <%= commit.id %>](#commit-link)
+- Commit: <%= commit.to_reference %>
+- Commit in another project: <%= xcommit.to_reference(project) %>
+- Ignored in code: `<%= commit.to_reference %>`
+- Ignored in links: [Link to <%= commit.to_reference %>](#commit-link)
#### LabelReferenceFilter
-- Label by ID: ~<%= simple_label.id %>
-- Label by name: ~<%= simple_label.name %>
-- Label by name in quotes: ~"<%= label.name %>"
-- Ignored in code: `~<%= simple_label.name %>`
-- Ignored in links: [Link to ~<%= simple_label.id %>](#label-link)
+- Label by ID: <%= simple_label.to_reference %>
+- Label by name: <%= Label.reference_prefix %><%= simple_label.name %>
+- Label by name in quotes: <%= label.to_reference(:name) %>
+- Ignored in code: `<%= simple_label.to_reference %>`
+- Ignored in links: [Link to <%= simple_label.to_reference %>](#label-link)
### Task Lists
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 0d0418f84a7..d0b200a9ff8 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -26,7 +26,7 @@ describe GitlabMarkdownHelper do
end
describe "referencing multiple objects" do
- let(:actual) { "!#{merge_request.iid} -> #{commit.id} -> ##{issue.iid}" }
+ let(:actual) { "#{merge_request.to_reference} -> #{commit.to_reference} -> #{issue.to_reference}" }
it "should link to the merge request" do
expected = namespace_project_merge_request_path(project.namespace, project, merge_request)
@@ -50,7 +50,7 @@ describe GitlabMarkdownHelper do
let(:issues) { create_list(:issue, 2, project: project) }
it 'should handle references nested in links with all the text' do
- actual = link_to_gfm("This should finally fix ##{issues[0].iid} and ##{issues[1].iid} for real", commit_path)
+ actual = link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", commit_path)
doc = Nokogiri::HTML.parse(actual)
# Make sure we didn't create invalid markup
@@ -63,7 +63,7 @@ describe GitlabMarkdownHelper do
# First issue link
expect(doc.css('a')[1].attr('href')).
to eq namespace_project_issue_path(project.namespace, project, issues[0])
- expect(doc.css('a')[1].text).to eq "##{issues[0].iid}"
+ expect(doc.css('a')[1].text).to eq issues[0].to_reference
# Internal commit link
expect(doc.css('a')[2].attr('href')).to eq commit_path
@@ -72,7 +72,7 @@ describe GitlabMarkdownHelper do
# Second issue link
expect(doc.css('a')[3].attr('href')).
to eq namespace_project_issue_path(project.namespace, project, issues[1])
- expect(doc.css('a')[3].text).to eq "##{issues[1].iid}"
+ expect(doc.css('a')[3].text).to eq issues[1].to_reference
# Trailing commit link
expect(doc.css('a')[4].attr('href')).to eq commit_path
@@ -90,7 +90,7 @@ describe GitlabMarkdownHelper do
end
it "escapes HTML passed in as the body" do
- actual = "This is a <h1>test</h1> - see ##{issues[0].iid}"
+ actual = "This is a <h1>test</h1> - see #{issues[0].to_reference}"
expect(link_to_gfm(actual, commit_path)).
to match('&lt;h1&gt;test&lt;/h1&gt;')
end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index cb7b0fbb890..5d7ff4f6122 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -1,131 +1,131 @@
require 'spec_helper'
describe Gitlab::ClosingIssueExtractor do
- let(:project) { create(:project) }
- let(:issue) { create(:issue, project: project) }
- let(:iid1) { issue.iid }
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project) }
+ let(:reference) { issue.to_reference }
subject { described_class.new(project, project.creator) }
describe "#closed_by_message" do
context 'with a single reference' do
it do
- message = "Awesome commit (Closes ##{iid1})"
+ message = "Awesome commit (Closes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Awesome commit (closes ##{iid1})"
+ message = "Awesome commit (closes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Closed ##{iid1}"
+ message = "Closed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "closed ##{iid1}"
+ message = "closed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Closing ##{iid1}"
+ message = "Closing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "closing ##{iid1}"
+ message = "closing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Close ##{iid1}"
+ message = "Close #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "close ##{iid1}"
+ message = "close #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Awesome commit (Fixes ##{iid1})"
+ message = "Awesome commit (Fixes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Awesome commit (fixes ##{iid1})"
+ message = "Awesome commit (fixes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Fixed ##{iid1}"
+ message = "Fixed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "fixed ##{iid1}"
+ message = "fixed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Fixing ##{iid1}"
+ message = "Fixing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "fixing ##{iid1}"
+ message = "fixing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Fix ##{iid1}"
+ message = "Fix #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "fix ##{iid1}"
+ message = "fix #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Awesome commit (Resolves ##{iid1})"
+ message = "Awesome commit (Resolves #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Awesome commit (resolves ##{iid1})"
+ message = "Awesome commit (resolves #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Resolved ##{iid1}"
+ message = "Resolved #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "resolved ##{iid1}"
+ message = "resolved #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Resolving ##{iid1}"
+ message = "Resolving #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "resolving ##{iid1}"
+ message = "resolving #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "Resolve ##{iid1}"
+ message = "Resolve #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
- message = "resolve ##{iid1}"
+ message = "resolve #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
end
@@ -133,40 +133,40 @@ describe Gitlab::ClosingIssueExtractor do
context 'with multiple references' do
let(:other_issue) { create(:issue, project: project) }
let(:third_issue) { create(:issue, project: project) }
- let(:iid2) { other_issue.iid }
- let(:iid3) { third_issue.iid }
+ let(:reference2) { other_issue.to_reference }
+ let(:reference3) { third_issue.to_reference }
it 'fetches issues in single line message' do
- message = "Closes ##{iid1} and fix ##{iid2}"
+ message = "Closes #{reference} and fix #{reference2}"
expect(subject.closed_by_message(message)).
to eq([issue, other_issue])
end
it 'fetches comma-separated issues references in single line message' do
- message = "Closes ##{iid1}, closes ##{iid2}"
+ message = "Closes #{reference}, closes #{reference2}"
expect(subject.closed_by_message(message)).
to eq([issue, other_issue])
end
it 'fetches comma-separated issues numbers in single line message' do
- message = "Closes ##{iid1}, ##{iid2} and ##{iid3}"
+ message = "Closes #{reference}, #{reference2} and #{reference3}"
expect(subject.closed_by_message(message)).
to eq([issue, other_issue, third_issue])
end
it 'fetches issues in multi-line message' do
- message = "Awesome commit (closes ##{iid1})\nAlso fixes ##{iid2}"
+ message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
expect(subject.closed_by_message(message)).
to eq([issue, other_issue])
end
it 'fetches issues in hybrid message' do
- message = "Awesome commit (closes ##{iid1})\n"\
- "Also fixing issues ##{iid2}, ##{iid3} and #4"
+ message = "Awesome commit (closes #{reference})\n"\
+ "Also fixing issues #{reference2}, #{reference3} and #4"
expect(subject.closed_by_message(message)).
to eq([issue, other_issue, third_issue])
diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
index 7274cb309a0..d3695ee46d0 100644
--- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
@@ -8,34 +8,36 @@ module Gitlab::Markdown
let(:commit1) { project.commit }
let(:commit2) { project.commit("HEAD~2") }
+ let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}") }
+ let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}") }
+
it 'requires project context' do
- expect { described_class.call('Commit Range 1c002d..d200c1', {}) }.
- to raise_error(ArgumentError, /:project/)
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
- exp = act = "<#{elem}>Commit Range #{commit1.id}..#{commit2.id}</#{elem}>"
+ exp = act = "<#{elem}>Commit Range #{range.to_reference}</#{elem}>"
expect(filter(act).to_html).to eq exp
end
end
context 'internal reference' do
- let(:reference) { "#{commit1.id}...#{commit2.id}" }
- let(:reference2) { "#{commit1.id}..#{commit2.id}" }
+ let(:reference) { range.to_reference }
+ let(:reference2) { range2.to_reference }
it 'links to a valid two-dot reference' do
doc = filter("See #{reference2}")
expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_compare_url(project.namespace, project, from: "#{commit1.id}^", to: commit2.id)
+ to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param)
end
it 'links to a valid three-dot reference' do
doc = filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_compare_url(project.namespace, project, from: commit1.id, to: commit2.id)
+ to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param)
end
it 'links to a valid short ID' do
@@ -51,7 +53,7 @@ module Gitlab::Markdown
it 'links with adjacent text' do
doc = filter("See (#{reference}.)")
- exp = Regexp.escape("#{commit1.short_id}...#{commit2.short_id}")
+ exp = Regexp.escape(range.to_s)
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
end
@@ -65,7 +67,7 @@ module Gitlab::Markdown
it 'includes a title attribute' do
doc = filter("See #{reference}")
- expect(doc.css('a').first.attr('title')).to eq "Commits #{commit1.id} through #{commit2.id}"
+ expect(doc.css('a').first.attr('title')).to eq range.reference_title
end
it 'includes default classes' do
@@ -95,9 +97,11 @@ module Gitlab::Markdown
context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, namespace: namespace) }
- let(:commit1) { project.commit }
- let(:commit2) { project.commit("HEAD~2") }
- let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" }
+ let(:reference) { range.to_reference(project) }
+
+ before do
+ range.project = project2
+ end
context 'when user can access reference' do
before { allow_cross_reference! }
@@ -106,21 +110,21 @@ module Gitlab::Markdown
doc = filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: commit2.id)
+ to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
it 'links with adjacent text' do
doc = filter("Fixed (#{reference}.)")
- exp = Regexp.escape("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
+ exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}")
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
end
it 'ignores invalid commit IDs on the referenced project' do
- exp = act = "Fixed #{project2.path_with_namespace}##{commit1.id.reverse}...#{commit2.id}"
+ exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
expect(filter(act).to_html).to eq exp
- exp = act = "Fixed #{project2.path_with_namespace}##{commit1.id}...#{commit2.id.reverse}"
+ exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
expect(filter(act).to_html).to eq exp
end
diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
index cc32a4fcf03..a0d2cd7e22b 100644
--- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
@@ -8,8 +8,7 @@ module Gitlab::Markdown
let(:commit) { project.commit }
it 'requires project context' do
- expect { described_class.call('Commit 1c002d', {}) }.
- to raise_error(ArgumentError, /:project/)
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
%w(pre code a style).each do |elem|
@@ -47,10 +46,11 @@ module Gitlab::Markdown
end
it 'ignores invalid commit IDs' do
- exp = act = "See #{reference.reverse}"
+ invalid = invalidate_reference(reference)
+ exp = act = "See #{invalid}"
expect(project).to receive(:valid_repo?).and_return(true)
- expect(project.repository).to receive(:commit).with(reference.reverse)
+ expect(project.repository).to receive(:commit).with(invalid)
expect(filter(act).to_html).to eq exp
end
@@ -93,8 +93,8 @@ module Gitlab::Markdown
context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, namespace: namespace) }
- let(:commit) { project.commit }
- let(:reference) { "#{project2.path_with_namespace}@#{commit.id}" }
+ let(:commit) { project2.commit }
+ let(:reference) { commit.to_reference(project) }
context 'when user can access reference' do
before { allow_cross_reference! }
@@ -109,12 +109,12 @@ module Gitlab::Markdown
it 'links with adjacent text' do
doc = filter("Fixed (#{reference}.)")
- exp = Regexp.escape(project2.path_with_namespace)
+ exp = Regexp.escape(project2.to_reference)
expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
end
it 'ignores invalid commit IDs on the referenced project' do
- exp = act = "Committed #{project2.path_with_namespace}##{commit.id.reverse}"
+ exp = act = "Committed #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp
end
diff --git a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
index b19bc125b92..bf9409589fa 100644
--- a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
@@ -9,19 +9,18 @@ module Gitlab::Markdown
end
let(:project) { create(:jira_project) }
- let(:issue) { double('issue', iid: 123) }
context 'JIRA issue references' do
- let(:reference) { "JIRA-#{issue.iid}" }
+ let(:issue) { ExternalIssue.new('JIRA-123', project) }
+ let(:reference) { issue.to_reference }
it 'requires project context' do
- expect { described_class.call('Issue JIRA-123', {}) }.
- to raise_error(ArgumentError, /:project/)
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
- exp = act = "<#{elem}>Issue JIRA-#{issue.iid}</#{elem}>"
+ exp = act = "<#{elem}>Issue #{reference}</#{elem}>"
expect(filter(act).to_html).to eq exp
end
end
@@ -33,13 +32,6 @@ module Gitlab::Markdown
expect(filter(act).to_html).to eq exp
end
- %w(pre code a style).each do |elem|
- it "ignores references contained inside '#{elem}' element" do
- exp = act = "<#{elem}>Issue #{reference}</#{elem}>"
- expect(filter(act).to_html).to eq exp
- end
- end
-
it 'links to a valid reference' do
doc = filter("Issue #{reference}")
expect(doc.css('a').first.attr('href'))
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index 08382b3e7e8..a838d7570c8 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -12,24 +12,23 @@ module Gitlab::Markdown
let(:issue) { create(:issue, project: project) }
it 'requires project context' do
- expect { described_class.call('Issue #123', {}) }.
- to raise_error(ArgumentError, /:project/)
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
- exp = act = "<#{elem}>Issue ##{issue.iid}</#{elem}>"
+ exp = act = "<#{elem}>Issue #{issue.to_reference}</#{elem}>"
expect(filter(act).to_html).to eq exp
end
end
context 'internal reference' do
- let(:reference) { "##{issue.iid}" }
+ let(:reference) { issue.to_reference }
it 'ignores valid references when using non-default tracker' do
expect(project).to receive(:get_issue).with(issue.iid).and_return(nil)
- exp = act = "Issue ##{issue.iid}"
+ exp = act = "Issue #{reference}"
expect(filter(act).to_html).to eq exp
end
@@ -46,9 +45,9 @@ module Gitlab::Markdown
end
it 'ignores invalid issue IDs' do
- exp = act = "Fixed ##{issue.iid + 1}"
+ invalid = invalidate_reference(reference)
+ exp = act = "Fixed #{invalid}"
- expect(project).to receive(:get_issue).with(issue.iid + 1).and_return(nil)
expect(filter(act).to_html).to eq exp
end
@@ -92,7 +91,7 @@ module Gitlab::Markdown
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, namespace: namespace) }
let(:issue) { create(:issue, project: project2) }
- let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
+ let(:reference) { issue.to_reference(project) }
context 'when user can access reference' do
before { allow_cross_reference! }
@@ -101,7 +100,7 @@ module Gitlab::Markdown
expect_any_instance_of(Project).to receive(:get_issue).
with(issue.iid).and_return(nil)
- exp = act = "Issue ##{issue.iid}"
+ exp = act = "Issue #{reference}"
expect(filter(act).to_html).to eq exp
end
@@ -118,7 +117,7 @@ module Gitlab::Markdown
end
it 'ignores invalid issue IDs on the referenced project' do
- exp = act = "Fixed #{project2.path_with_namespace}##{issue.iid + 1}"
+ exp = act = "Fixed #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp
end
diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
index c4548e7431f..41987f57bca 100644
--- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
@@ -7,11 +7,10 @@ module Gitlab::Markdown
let(:project) { create(:empty_project) }
let(:label) { create(:label, project: project) }
- let(:reference) { "~#{label.id}" }
+ let(:reference) { label.to_reference }
it 'requires project context' do
- expect { described_class.call('Label ~123', {}) }.
- to raise_error(ArgumentError, /:project/)
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
%w(pre code a style).each do |elem|
@@ -36,7 +35,7 @@ module Gitlab::Markdown
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
- expect(link).to eq urls.namespace_project_issues_url(project.namespace, project, label_name: label.name, only_path: true)
+ expect(link).to eq urls.namespace_project_issues_path(project.namespace, project, label_name: label.name)
end
it 'adds to the results hash' do
@@ -70,7 +69,7 @@ module Gitlab::Markdown
end
it 'ignores invalid label IDs' do
- exp = act = "Label ~#{label.id + 1}"
+ exp = act = "Label #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp
end
@@ -78,7 +77,7 @@ module Gitlab::Markdown
context 'String-based single-word references' do
let(:label) { create(:label, name: 'gfm', project: project) }
- let(:reference) { "~#{label.name}" }
+ let(:reference) { "#{Label.reference_prefix}#{label.name}" }
it 'links to a valid reference' do
doc = filter("See #{reference}")
@@ -94,59 +93,33 @@ module Gitlab::Markdown
end
it 'ignores invalid label names' do
- exp = act = "Label ~#{label.name.reverse}"
+ exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}"
expect(filter(act).to_html).to eq exp
end
end
context 'String-based multi-word references in quotes' do
- let(:label) { create(:label, name: 'gfm references', project: project) }
+ let(:label) { create(:label, name: 'gfm references', project: project) }
+ let(:reference) { label.to_reference(:name) }
- context 'in single quotes' do
- let(:reference) { "~'#{label.name}'" }
-
- it 'links to a valid reference' do
- doc = filter("See #{reference}")
-
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
- expect(doc.text).to eq 'See gfm references'
- end
-
- it 'links with adjacent text' do
- doc = filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
- end
-
- it 'ignores invalid label names' do
- exp = act = "Label ~'#{label.name.reverse}'"
+ it 'links to a valid reference' do
+ doc = filter("See #{reference}")
- expect(filter(act).to_html).to eq exp
- end
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.text).to eq 'See gfm references'
end
- context 'in double quotes' do
- let(:reference) { %(~"#{label.name}") }
-
- it 'links to a valid reference' do
- doc = filter("See #{reference}")
-
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
- expect(doc.text).to eq 'See gfm references'
- end
-
- it 'links with adjacent text' do
- doc = filter("Label (#{reference}.)")
- expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
- end
+ it 'links with adjacent text' do
+ doc = filter("Label (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
+ end
- it 'ignores invalid label names' do
- exp = act = %(Label ~"#{label.name.reverse}")
+ it 'ignores invalid label names' do
+ exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}")
- expect(filter(act).to_html).to eq exp
- end
+ expect(filter(act).to_html).to eq exp
end
end
diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
index d6e745114f2..6aeb1093602 100644
--- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
@@ -8,19 +8,18 @@ module Gitlab::Markdown
let(:merge) { create(:merge_request, source_project: project) }
it 'requires project context' do
- expect { described_class.call('MergeRequest !123', {}) }.
- to raise_error(ArgumentError, /:project/)
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
- exp = act = "<#{elem}>Merge !#{merge.iid}</#{elem}>"
+ exp = act = "<#{elem}>Merge #{merge.to_reference}</#{elem}>"
expect(filter(act).to_html).to eq exp
end
end
context 'internal reference' do
- let(:reference) { "!#{merge.iid}" }
+ let(:reference) { merge.to_reference }
it 'links to a valid reference' do
doc = filter("See #{reference}")
@@ -35,7 +34,7 @@ module Gitlab::Markdown
end
it 'ignores invalid merge IDs' do
- exp = act = "Merge !#{merge.iid + 1}"
+ exp = act = "Merge #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp
end
@@ -80,7 +79,7 @@ module Gitlab::Markdown
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, namespace: namespace) }
let(:merge) { create(:merge_request, source_project: project2) }
- let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" }
+ let(:reference) { merge.to_reference(project) }
context 'when user can access reference' do
before { allow_cross_reference! }
@@ -99,7 +98,7 @@ module Gitlab::Markdown
end
it 'ignores invalid merge IDs on the referenced project' do
- exp = act = "Merge #{project2.path_with_namespace}!#{merge.iid + 1}"
+ exp = act = "Merge #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp
end
diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
index a4b331157af..07ece66e903 100644
--- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
@@ -6,11 +6,10 @@ module Gitlab::Markdown
let(:project) { create(:empty_project) }
let(:snippet) { create(:project_snippet, project: project) }
- let(:reference) { "$#{snippet.id}" }
+ let(:reference) { snippet.to_reference }
it 'requires project context' do
- expect { described_class.call('Snippet $123', {}) }.
- to raise_error(ArgumentError, /:project/)
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
%w(pre code a style).each do |elem|
@@ -34,7 +33,7 @@ module Gitlab::Markdown
end
it 'ignores invalid snippet IDs' do
- exp = act = "Snippet $#{snippet.id + 1}"
+ exp = act = "Snippet #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp
end
@@ -79,7 +78,7 @@ module Gitlab::Markdown
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, namespace: namespace) }
let(:snippet) { create(:project_snippet, project: project2) }
- let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" }
+ let(:reference) { snippet.to_reference(project) }
context 'when user can access reference' do
before { allow_cross_reference! }
@@ -97,7 +96,7 @@ module Gitlab::Markdown
end
it 'ignores invalid snippet IDs on the referenced project' do
- exp = act = "See #{project2.path_with_namespace}$#{snippet.id + 1}"
+ exp = act = "See #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp
end
diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
index 922502ada33..0ecbdee9b9e 100644
--- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
@@ -4,65 +4,63 @@ module Gitlab::Markdown
describe UserReferenceFilter do
include ReferenceFilterSpecHelper
- let(:project) { create(:empty_project) }
- let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:reference) { user.to_reference }
it 'requires project context' do
- expect { described_class.call('Example @mention', {}) }.
- to raise_error(ArgumentError, /:project/)
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
it 'ignores invalid users' do
- exp = act = 'Hey @somebody'
+ exp = act = "Hey #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq(exp)
end
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
- exp = act = "<#{elem}>Hey @#{user.username}</#{elem}>"
+ exp = act = "<#{elem}>Hey #{reference}</#{elem}>"
expect(filter(act).to_html).to eq exp
end
end
context 'mentioning @all' do
+ let(:reference) { User.reference_prefix + 'all' }
+
before do
project.team << [project.creator, :developer]
end
it 'supports a special @all mention' do
- doc = filter("Hey @all")
+ doc = filter("Hey #{reference}")
expect(doc.css('a').length).to eq 1
expect(doc.css('a').first.attr('href'))
.to eq urls.namespace_project_url(project.namespace, project)
end
it 'adds to the results hash' do
- result = pipeline_result('Hey @all')
+ result = pipeline_result("Hey #{reference}")
expect(result[:references][:user]).to eq [project.creator]
end
end
context 'mentioning a user' do
- let(:reference) { "@#{user.username}" }
-
it 'links to a User' do
doc = filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
end
- # TODO (rspeicher): This test might be overkill
it 'links to a User with a period' do
user = create(:user, name: 'alphA.Beta')
- doc = filter("Hey @#{user.username}")
+ doc = filter("Hey #{user.to_reference}")
expect(doc.css('a').length).to eq 1
end
- # TODO (rspeicher): This test might be overkill
it 'links to a User with an underscore' do
user = create(:user, name: 'ping_pong_king')
- doc = filter("Hey @#{user.username}")
+ doc = filter("Hey #{user.to_reference}")
expect(doc.css('a').length).to eq 1
end
@@ -73,10 +71,9 @@ module Gitlab::Markdown
end
context 'mentioning a group' do
- let(:group) { create(:group) }
- let(:user) { create(:user) }
-
- let(:reference) { "@#{group.name}" }
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
+ let(:reference) { group.to_reference }
context 'that the current user can read' do
before do
@@ -108,23 +105,23 @@ module Gitlab::Markdown
end
it 'links with adjacent text' do
- skip 'TODO (rspeicher): Re-enable when usernames can\'t end in periods.'
- doc = filter("Mention me (@#{user.username}.)")
- expect(doc.to_html).to match(/\(<a.+>@#{user.username}<\/a>\.\)/)
+ skip "TODO (rspeicher): Re-enable when usernames can't end in periods."
+ doc = filter("Mention me (#{reference}.)")
+ expect(doc.to_html).to match(/\(<a.+>#{reference}<\/a>\.\)/)
end
it 'includes default classes' do
- doc = filter("Hey @#{user.username}")
+ doc = filter("Hey #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member'
end
it 'includes an optional custom class' do
- doc = filter("Hey @#{user.username}", reference_class: 'custom')
+ doc = filter("Hey #{reference}", reference_class: 'custom')
expect(doc.css('a').first.attr('class')).to include 'custom'
end
it 'supports an :only_path context' do
- doc = filter("Hey @#{user.username}", only_path: true)
+ doc = filter("Hey #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 9801dc16554..c14f4ac6bf6 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -20,7 +20,7 @@ describe Gitlab::ReferenceExtractor do
@i0 = create(:issue, project: project)
@i1 = create(:issue, project: project)
- subject.analyze("##{@i0.iid}, ##{@i1.iid}, and #999.")
+ subject.analyze("#{@i0.to_reference}, #{@i1.to_reference}, and #{Issue.reference_prefix}999.")
expect(subject.issues).to eq([@i0, @i1])
end
@@ -82,7 +82,7 @@ describe Gitlab::ReferenceExtractor do
end
it 'handles project issue references' do
- subject.analyze("this refers issue #{other_project.path_with_namespace}##{issue.iid}")
+ subject.analyze("this refers issue #{issue.to_reference(project)}")
extracted = subject.issues
expect(extracted.size).to eq(1)
expect(extracted).to eq([issue])
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index 31ee3e99cad..e7fb43ff335 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -1,6 +1,12 @@
require 'spec_helper'
describe CommitRange do
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Referable) }
+ end
+
let(:sha_from) { 'f3f85602' }
let(:sha_to) { 'e86e1013' }
@@ -21,6 +27,23 @@ describe CommitRange do
end
end
+ describe '#to_reference' do
+ let(:project) { double('project', to_reference: 'namespace1/project') }
+
+ before do
+ range.project = project
+ end
+
+ it 'returns a String reference to the object' do
+ expect(range.to_reference).to eq range.to_s
+ end
+
+ it 'supports a cross-project reference' do
+ cross = double('project')
+ expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{range.to_s}"
+ end
+ end
+
describe '#reference_title' do
it 'returns the correct String for three-dot ranges' do
expect(range.reference_title).to eq "Commits #{sha_from} through #{sha_to}"
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index ad2ac143d97..27eb02a870b 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -1,8 +1,28 @@
require 'spec_helper'
describe Commit do
- let(:project) { create :project }
- let(:commit) { project.commit }
+ let(:project) { create(:project) }
+ let(:commit) { project.commit }
+
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Mentionable) }
+ it { is_expected.to include_module(Participable) }
+ it { is_expected.to include_module(Referable) }
+ it { is_expected.to include_module(StaticModel) }
+ end
+
+ describe '#to_reference' do
+ it 'returns a String reference to the object' do
+ expect(commit.to_reference).to eq commit.id
+ end
+
+ it 'supports a cross-project reference' do
+ cross = double('project')
+ expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.id}"
+ end
+ end
describe '#title' do
it "returns no_commit_message when safe_message is blank" do
diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb
new file mode 100644
index 00000000000..7744610db78
--- /dev/null
+++ b/spec/models/external_issue_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe ExternalIssue do
+ let(:project) { double('project', to_reference: 'namespace1/project1') }
+ let(:issue) { described_class.new('EXT-1234', project) }
+
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Referable) }
+ end
+
+ describe '#to_reference' do
+ it 'returns a String reference to the object' do
+ expect(issue.to_reference).to eq issue.id
+ end
+ end
+
+ describe '#title' do
+ it 'returns a title' do
+ expect(issue.title).to eq "External Issue #{issue}"
+ end
+ end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 9428224a64f..80638fc8db2 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -18,16 +18,30 @@ require 'spec_helper'
describe Group do
let!(:group) { create(:group) }
- describe "Associations" do
+ describe 'associations' do
it { is_expected.to have_many :projects }
it { is_expected.to have_many :group_members }
end
- it { is_expected.to validate_presence_of :name }
- it { is_expected.to validate_uniqueness_of(:name) }
- it { is_expected.to validate_presence_of :path }
- it { is_expected.to validate_uniqueness_of(:path) }
- it { is_expected.not_to validate_presence_of :owner }
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Referable) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of :name }
+ it { is_expected.to validate_uniqueness_of(:name) }
+ it { is_expected.to validate_presence_of :path }
+ it { is_expected.to validate_uniqueness_of(:path) }
+ it { is_expected.not_to validate_presence_of :owner }
+ end
+
+ describe '#to_reference' do
+ it 'returns a String reference to the object' do
+ expect(group.to_reference).to eq "@#{group.name}"
+ end
+ end
describe :users do
it { expect(group.users).to eq(group.owners) }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 20d823b40e5..614b648bb52 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -24,15 +24,30 @@ describe Issue do
it { is_expected.to belong_to(:milestone) }
end
- describe "Mass assignment" do
- end
-
describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(InternalId) }
it { is_expected.to include_module(Issuable) }
+ it { is_expected.to include_module(Referable) }
+ it { is_expected.to include_module(Sortable) }
+ it { is_expected.to include_module(Taskable) }
end
subject { create(:issue) }
+ describe '#to_reference' do
+ it 'returns a String reference to the object' do
+ expect(subject.to_reference).to eq "##{subject.iid}"
+ end
+
+ it 'supports a cross-project reference' do
+ cross = double('project')
+ expect(subject.to_reference(cross)).
+ to eq "#{subject.project.to_reference}##{subject.iid}"
+ end
+ end
+
describe '#is_being_reassigned?' do
it 'returns true if the issue assignee has changed' do
subject.assignee = create(:user)
@@ -45,11 +60,8 @@ describe Issue do
describe '#is_being_reassigned?' do
it 'returns issues assigned to user' do
- user = create :user
-
- 2.times do
- issue = create :issue, assignee: user
- end
+ user = create(:user)
+ create_list(:issue, 2, assignee: user)
expect(Issue.open_for(user).count).to eq 2
end
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 8644ac46605..6518213d71c 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -14,30 +14,63 @@ require 'spec_helper'
describe Label do
let(:label) { create(:label) }
- it { expect(label).to be_valid }
- it { is_expected.to belong_to(:project) }
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_many(:label_links).dependent(:destroy) }
+ it { is_expected.to have_many(:issues).through(:label_links).source(:target) }
+ end
+
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Referable) }
+ end
+
+ describe 'validation' do
+ it { is_expected.to validate_presence_of(:project) }
- describe 'Validation' do
it 'should validate color code' do
- expect(build(:label, color: 'G-ITLAB')).not_to be_valid
- expect(build(:label, color: 'AABBCC')).not_to be_valid
- expect(build(:label, color: '#AABBCCEE')).not_to be_valid
- expect(build(:label, color: '#GGHHII')).not_to be_valid
- expect(build(:label, color: '#')).not_to be_valid
- expect(build(:label, color: '')).not_to be_valid
-
- expect(build(:label, color: '#AABBCC')).to be_valid
+ expect(label).not_to allow_value('G-ITLAB').for(:color)
+ expect(label).not_to allow_value('AABBCC').for(:color)
+ expect(label).not_to allow_value('#AABBCCEE').for(:color)
+ expect(label).not_to allow_value('GGHHII').for(:color)
+ expect(label).not_to allow_value('#').for(:color)
+ expect(label).not_to allow_value('').for(:color)
+
+ expect(label).to allow_value('#AABBCC').for(:color)
+ expect(label).to allow_value('#abcdef').for(:color)
end
it 'should validate title' do
- expect(build(:label, title: 'G,ITLAB')).not_to be_valid
- expect(build(:label, title: 'G?ITLAB')).not_to be_valid
- expect(build(:label, title: 'G&ITLAB')).not_to be_valid
- expect(build(:label, title: '')).not_to be_valid
+ expect(label).not_to allow_value('G,ITLAB').for(:title)
+ expect(label).not_to allow_value('G?ITLAB').for(:title)
+ expect(label).not_to allow_value('G&ITLAB').for(:title)
+ expect(label).not_to allow_value('').for(:title)
+
+ expect(label).to allow_value('GITLAB').for(:title)
+ expect(label).to allow_value('gitlab').for(:title)
+ expect(label).to allow_value("customer's request").for(:title)
+ end
+ end
+
+ describe '#to_reference' do
+ context 'using id' do
+ it 'returns a String reference to the object' do
+ expect(label.to_reference).to eq "~#{label.id}"
+ expect(label.to_reference(double('project'))).to eq "~#{label.id}"
+ end
+ end
+
+ context 'using name' do
+ it 'returns a String reference to the object' do
+ expect(label.to_reference(:name)).to eq %(~"#{label.name}")
+ end
- expect(build(:label, title: 'GITLAB')).to be_valid
- expect(build(:label, title: 'gitlab')).to be_valid
+ it 'uses id when name contains double quote' do
+ label = create(:label, name: %q{"irony"})
+ expect(label.to_reference(:name)).to eq "~#{label.id}"
+ end
end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 97b8abc49dd..0465aa34843 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -24,22 +24,45 @@
require 'spec_helper'
describe MergeRequest do
- describe "Validation" do
- it { is_expected.to validate_presence_of(:target_branch) }
- it { is_expected.to validate_presence_of(:source_branch) }
+ subject { create(:merge_request) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') }
+ it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') }
+
+ it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) }
end
- describe "Mass assignment" do
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(InternalId) }
+ it { is_expected.to include_module(Issuable) }
+ it { is_expected.to include_module(Referable) }
+ it { is_expected.to include_module(Sortable) }
+ it { is_expected.to include_module(Taskable) }
end
- describe "Respond to" do
+ describe 'validation' do
+ it { is_expected.to validate_presence_of(:target_branch) }
+ it { is_expected.to validate_presence_of(:source_branch) }
+ end
+
+ describe 'respond to' do
it { is_expected.to respond_to(:unchecked?) }
it { is_expected.to respond_to(:can_be_merged?) }
it { is_expected.to respond_to(:cannot_be_merged?) }
end
- describe 'modules' do
- it { is_expected.to include_module(Issuable) }
+ describe '#to_reference' do
+ it 'returns a String reference to the object' do
+ expect(subject.to_reference).to eq "!#{subject.iid}"
+ end
+
+ it 'supports a cross-project reference' do
+ cross = double('project')
+ expect(subject.to_reference(cross)).to eq "#{subject.source_project.to_reference}!#{subject.iid}"
+ end
end
describe "#mr_and_commit_notes" do
@@ -57,8 +80,6 @@ describe MergeRequest do
end
end
- subject { create(:merge_request) }
-
describe '#is_being_reassigned?' do
it 'returns true if the merge_request assignee has changed' do
subject.assignee = create(:user)
@@ -108,7 +129,7 @@ describe MergeRequest do
it 'detects issues mentioned in the description' do
issue2 = create(:issue, project: subject.project)
- subject.description = "Closes ##{issue2.iid}"
+ subject.description = "Closes #{issue2.to_reference}"
subject.project.stub(default_branch: subject.target_branch)
expect(subject.closes_issues).to include(issue2)
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 37e21a90818..48568e2a3ff 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -32,7 +32,7 @@
require 'spec_helper'
describe Project do
- describe 'Associations' do
+ describe 'associations' do
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:namespace) }
it { is_expected.to belong_to(:creator).class_name('User') }
@@ -54,10 +54,17 @@ describe Project do
it { is_expected.to have_one(:asana_service).dependent(:destroy) }
end
- describe 'Mass assignment' do
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Gitlab::ConfigHelper) }
+ it { is_expected.to include_module(Gitlab::ShellAdapter) }
+ it { is_expected.to include_module(Gitlab::VisibilityLevel) }
+ it { is_expected.to include_module(Referable) }
+ it { is_expected.to include_module(Sortable) }
end
- describe 'Validation' do
+ describe 'validation' do
let!(:project) { create(:project) }
it { is_expected.to validate_presence_of(:name) }
@@ -91,6 +98,14 @@ describe Project do
it { is_expected.to respond_to(:path_with_namespace) }
end
+ describe '#to_reference' do
+ let(:project) { create(:empty_project) }
+
+ it 'returns a String reference to the object' do
+ expect(project.to_reference).to eq project.path_with_namespace
+ end
+ end
+
it 'should return valid url to repo' do
project = Project.new(path: 'somewhere')
expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index e37dcc75230..c81dd36ef4b 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -18,23 +18,47 @@
require 'spec_helper'
describe Snippet do
- describe "Associations" do
- it { is_expected.to belong_to(:author).class_name('User') }
- it { is_expected.to have_many(:notes).dependent(:destroy) }
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Gitlab::VisibilityLevel) }
+ it { is_expected.to include_module(Linguist::BlobHelper) }
+ it { is_expected.to include_module(Participable) }
+ it { is_expected.to include_module(Referable) }
+ it { is_expected.to include_module(Sortable) }
end
- describe "Mass assignment" do
+ describe 'associations' do
+ it { is_expected.to belong_to(:author).class_name('User') }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_many(:notes).dependent(:destroy) }
end
- describe "Validation" do
+ describe 'validation' do
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to ensure_length_of(:title).is_within(0..255) }
it { is_expected.to validate_presence_of(:file_name) }
- it { is_expected.to ensure_length_of(:title).is_within(0..255) }
+ it { is_expected.to ensure_length_of(:file_name).is_within(0..255) }
it { is_expected.to validate_presence_of(:content) }
+
+ it { is_expected.to validate_inclusion_of(:visibility_level).in_array(Gitlab::VisibilityLevel.values) }
+ end
+
+ describe '#to_reference' do
+ let(:project) { create(:empty_project) }
+ let(:snippet) { create(:snippet, project: project) }
+
+ it 'returns a String reference to the object' do
+ expect(snippet.to_reference).to eq "$#{snippet.id}"
+ end
+
+ it 'supports a cross-project reference' do
+ cross = double('project')
+ expect(snippet.to_reference(cross)).to eq "#{project.to_reference}$#{snippet.id}"
+ end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 0dddcd5bda2..e1205c18a85 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -63,7 +63,17 @@ require 'spec_helper'
describe User do
include Gitlab::CurrentSettings
- describe "Associations" do
+ describe 'modules' do
+ subject { described_class }
+
+ it { is_expected.to include_module(Gitlab::ConfigHelper) }
+ it { is_expected.to include_module(Gitlab::CurrentSettings) }
+ it { is_expected.to include_module(Referable) }
+ it { is_expected.to include_module(Sortable) }
+ it { is_expected.to include_module(TokenAuthenticatable) }
+ end
+
+ describe 'associations' do
it { is_expected.to have_one(:namespace) }
it { is_expected.to have_many(:snippets).class_name('Snippet').dependent(:destroy) }
it { is_expected.to have_many(:project_members).dependent(:destroy) }
@@ -79,9 +89,6 @@ describe User do
it { is_expected.to have_many(:identities).dependent(:destroy) }
end
- describe "Mass assignment" do
- end
-
describe 'validations' do
it { is_expected.to validate_presence_of(:username) }
it { is_expected.to validate_presence_of(:projects_limit) }
@@ -175,6 +182,14 @@ describe User do
it { is_expected.to respond_to(:private_token) }
end
+ describe '#to_reference' do
+ let(:user) { create(:user) }
+
+ it 'returns a String reference to the object' do
+ expect(user.to_reference).to eq "@#{user.username}"
+ end
+ end
+
describe '#generate_password' do
it "should execute callback when force_random_password specified" do
user = build(:user, force_random_password: true)
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 6d8c71f94f9..0dcc94e8bd4 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -259,13 +259,13 @@ describe SystemNoteService do
let(:mentioner) { project2.repository.commit }
it 'references the mentioning commit' do
- expect(subject.note).to eq "mentioned in commit #{project2.path_with_namespace}@#{mentioner.id}"
+ expect(subject.note).to eq "mentioned in commit #{mentioner.to_reference(project)}"
end
end
context 'from non-Commit' do
it 'references the mentioning object' do
- expect(subject.note).to eq "mentioned in issue #{project2.path_with_namespace}##{mentioner.iid}"
+ expect(subject.note).to eq "mentioned in issue #{mentioner.to_reference(project)}"
end
end
end
@@ -275,13 +275,13 @@ describe SystemNoteService do
let(:mentioner) { project.repository.commit }
it 'references the mentioning commit' do
- expect(subject.note).to eq "mentioned in commit #{mentioner.id}"
+ expect(subject.note).to eq "mentioned in commit #{mentioner.to_reference}"
end
end
context 'from non-Commit' do
it 'references the mentioning object' do
- expect(subject.note).to eq "mentioned in issue ##{mentioner.iid}"
+ expect(subject.note).to eq "mentioned in issue #{mentioner.to_reference}"
end
end
end
@@ -291,7 +291,7 @@ describe SystemNoteService do
describe '.cross_reference?' do
it 'is truthy when text begins with expected text' do
- expect(described_class.cross_reference?('mentioned in issue #1')).to be_truthy
+ expect(described_class.cross_reference?('mentioned in something')).to be_truthy
end
it 'is falsey when text does not begin with expected text' do
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index 53fb6545553..ede62e8f37a 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -10,12 +10,12 @@ def common_mentionable_setup
let(:mentioned_issue) { create(:issue, project: project) }
let(:mentioned_mr) { create(:merge_request, :simple, source_project: project) }
- let(:mentioned_commit) { project.repository.commit }
+ let(:mentioned_commit) { project.commit }
let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let(:ext_mr) { create(:merge_request, :simple, source_project: ext_proj) }
- let(:ext_commit) { ext_proj.repository.commit }
+ let(:ext_commit) { ext_proj.commit }
# Override to add known commits to the repository stub.
let(:extra_commits) { [] }
@@ -23,21 +23,19 @@ def common_mentionable_setup
# A string that mentions each of the +mentioned_.*+ objects above. Mentionables should add a self-reference
# to this string and place it in their +mentionable_text+.
let(:ref_string) do
- cross = ext_proj.path_with_namespace
-
<<-MSG.strip_heredoc
These references are new:
- Issue: ##{mentioned_issue.iid}
- Merge: !#{mentioned_mr.iid}
- Commit: #{mentioned_commit.id}
+ Issue: #{mentioned_issue.to_reference}
+ Merge: #{mentioned_mr.to_reference}
+ Commit: #{mentioned_commit.to_reference}
This reference is a repeat and should only be mentioned once:
- Repeat: ##{mentioned_issue.iid}
+ Repeat: #{mentioned_issue.to_reference}
These references are cross-referenced:
- Issue: #{cross}##{ext_issue.iid}
- Merge: #{cross}!#{ext_mr.iid}
- Commit: #{cross}@#{ext_commit.short_id}
+ Issue: #{ext_issue.to_reference(project)}
+ Merge: #{ext_mr.to_reference(project)}
+ Commit: #{ext_commit.to_reference(project)}
This is a self-reference and should not be mentioned at all:
Self: #{backref_text}
@@ -109,19 +107,17 @@ shared_examples 'an editable mentionable' do
it 'creates new cross-reference notes when the mentionable text is edited' do
subject.save
- cross = ext_proj.path_with_namespace
-
new_text = <<-MSG
These references already existed:
- Issue: ##{mentioned_issue.iid}
- Commit: #{mentioned_commit.id}
+ Issue: #{mentioned_issue.to_reference}
+ Commit: #{mentioned_commit.to_reference}
This cross-project reference already existed:
- Issue: #{cross}##{ext_issue.iid}
+ Issue: #{ext_issue.to_reference(project)}
These two references are introduced in an edit:
- Issue: ##{new_issues[0].iid}
- Cross: #{cross}##{new_issues[1].iid}
+ Issue: #{new_issues[0].to_reference}
+ Cross: #{new_issues[1].to_reference(project)}
MSG
# These three objects were already referenced, and should not receive new
diff --git a/spec/support/reference_filter_spec_helper.rb b/spec/support/reference_filter_spec_helper.rb
index 06c39e1ada5..afbea55ab99 100644
--- a/spec/support/reference_filter_spec_helper.rb
+++ b/spec/support/reference_filter_spec_helper.rb
@@ -10,6 +10,26 @@ module ReferenceFilterSpecHelper
Rails.application.routes.url_helpers
end
+ # Modify a String reference to make it invalid
+ #
+ # Commit SHAs get reversed, IDs get incremented by 1, all other Strings get
+ # their word characters reversed.
+ #
+ # reference - String reference to modify
+ #
+ # Returns a String
+ def invalidate_reference(reference)
+ if reference =~ /\A(.+)?.\d+\z/
+ # Integer-based reference with optional project prefix
+ reference.gsub(/\d+\z/) { |i| i.to_i + 1 }
+ elsif reference =~ /\A(.+@)?(\h{6,40}\z)/
+ # SHA-based reference with optional prefix
+ reference.gsub(/\h{6,40}\z/) { |v| v.reverse }
+ else
+ reference.gsub(/\w+\z/) { |v| v.reverse }
+ end
+ end
+
# Perform `call` on the described class
#
# Automatically passes the current `project` value to the context if none is